1 Commits

Author SHA1 Message Date
infinitefusion
ba536c0b02 compatibility items trainers 2021-06-24 12:10:44 -04:00
926 changed files with 231775 additions and 437366 deletions

6
.gitignore vendored
View File

@@ -2,16 +2,13 @@
Audio/ Audio/
Graphics/ Graphics/
Plugins/ Plugins/
Screenshots/
# Data folder, but not Data/Scripts folder or messages_core.dat # Data folder, but not Data/Scripts folder
Data/* Data/*
!Data/Scripts.rxdata !Data/Scripts.rxdata
!Data/Scripts/ !Data/Scripts/
!Data/messages_core.dat
# Files in the main project folder # Files in the main project folder
errorlog.txt
Game.ini Game.ini
Game.rxproj Game.rxproj
RGSS*.dll RGSS*.dll
@@ -20,7 +17,6 @@ RGSS*.dll
.vscode/ .vscode/
*.code-workspace *.code-workspace
.idea/ .idea/
*.mkproj
# Operating system generated files & folders # Operating system generated files & folders
.DS_Store .DS_Store

View File

@@ -1,225 +0,0 @@
AllCops:
TargetRubyVersion: "3.0"
NewCops: enable
#===============================================================================
# Layout
#===============================================================================
# We don't need empty lines in methods to separate "return if"s from later code.
Layout/EmptyLineAfterGuardClause:
Enabled: false
# Extra whitespace often helps to make code more presentable.
Layout/ExtraSpacing:
AllowForAlignment: true
AllowBeforeTrailingComments: true
# Looks better than having hash elements shifted way to the right just to line
# up with the hash's opening bracket.
Layout/FirstHashElementIndentation:
EnforcedStyle: consistent
# In a hash with multiple values (one per line), prefer the => to be lined up
# and text to otherwise be left-aligned.
Layout/HashAlignment:
EnforcedHashRocketStyle: table
EnforcedColonStyle: table
# This interferes with the presentation of some code, notably registered procs.
Layout/MultilineMethodCallBraceLayout:
Enabled: false
# This means hashes and arrays are written the same way, rather than hashes
# needing to be written like { foo => bar } while arrays are like [foo, bar].
Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space
#===============================================================================
# Lint
#===============================================================================
# Some methods and blocks will have unused arguments. That's fine.
Lint/UnusedBlockArgument:
Enabled: false
Lint/UnusedMethodArgument:
Enabled: false
#===============================================================================
# Metrics
#===============================================================================
# Yes, Essentials has classes/modules/methods that are too big and complex.
# That's just how it is.
Metrics:
Enabled: false
#===============================================================================
# Naming
#===============================================================================
# This cop forbids class/module names with underscores in them. Having
# underscores isn't the end of the world.
Naming/ClassAndModuleCamelCase:
Enabled: false
# Script files are given names that look reasonable in the list of script
# sections in RMXP, and are all numbered. They won't be camel_case.
Naming/FileName:
Enabled: false
# Disabled for sanity's sake. While this is a cop we want to obey, fixing all
# this is a gargantuan task that may never be completed, and we don't need
# rubocop telling us about the 4000+ instances of camelCase method names.
Naming/MethodName:
Enabled: false
# Disabled for sanity's sake. While this is a cop we want to obey, fixing all
# this is a gargantuan task that may never be completed, and we don't need
# rubocop telling us about the 1500+ instances of camelCase parameter names.
Naming/MethodParameterName:
Enabled: false
# Disabled for sanity's sake. While this is a cop we want to obey, fixing all
# this is a gargantuan task that may never be completed, and we don't need
# rubocop telling us about the 10000+ instances of camelCase variable names.
Naming/VariableName:
Enabled: false
#===============================================================================
# Security
#===============================================================================
# Script event conditions and script switches are eval'd, amongst other things.
Security/Eval:
Enabled: false
# Plenty of things are loaded via Marshal.
Security/MarshalLoad:
Enabled: false
#===============================================================================
# Style
#===============================================================================
# List the attr_reader/writer/accessor variables however you want.
Style/AccessorGrouping:
Enabled: false
# The assign_to_condition style looks awful, indenting loads of lines and
# increasing the separation between variable and value being assigned to it.
# Having said that, using "assign_inside_condition" flags every instance of
# conditional assignment using a one-line ternary operator, so this cop has been
# disabled because such assignment is fine.
Style/ConditionalAssignment:
Enabled: false
EnforcedStyle: assign_inside_condition
# Check with yard instead.
Style/Documentation:
Enabled: false
# This is just shorthand that looks bad due to the lack of an "end" to a "def".
Style/EndlessMethod:
EnforcedStyle: disallow
# It's a choice between format and sprintf. We already make use of sprintf and
# the translatable _ISPRINTF, so...
Style/FormatString:
EnforcedStyle: sprintf
# Prefer sprintf("%s", "Hello") over sprintf("%<greeting>s", greeting: "Hello")
# because it should be easy enough to see which token is which, and it saves
# space.
Style/FormatStringToken:
EnforcedStyle: unannotated
# String literals are not frozen by default, which makes this comment a
# pointless bit of boilerplate that we neither need nor want.
Style/FrozenStringLiteralComment:
Enabled: false
# RMXP and Essentials use lots of global variables.
Style/GlobalVars:
Enabled: false
# Mixing the styles within a hash just looks silly.
Style/HashSyntax:
EnforcedStyle: no_mixed_keys
# Sometimes you want to clearly separate sets of code, one per "paradigm".
Style/IfInsideElse:
Enabled: false
AllowIfModifier: true
# The alernative is ->(x) { x } which is less English than "lambda". This style
# makes lambda definitions require the word "lambda".
Style/Lambda:
EnforcedStyle: lambda
# unless just adds mental gymnastics trying to figure out what it actually
# means. I much prefer if !something.
Style/NegatedIf:
Enabled: false
# .zero?, .positive? and .negative? are more wordy than == 0, > 0 and < 0. They
# also aren't consistent with other value comparisons, e.g. x > 42.
Style/NumericPredicate:
EnforcedStyle: comparison
# Following this just means that calls to an affected method need to know what
# that method calls its parameters, which is ridiculous. Keep things short and
# simple.
Style/OptionalBooleanParameter:
Enabled: false
# has_key? and has_value? are far more readable than key? and value?
Style/PreferredHashMethods:
Enabled: false
# Explicit returns help to show whether a method returns a value.
Style/RedundantReturn:
Enabled: false
# Enforcing the names of variables? To single letter ones? Just no.
Style/SingleLineBlockParams:
Enabled: false
# Single line methods use up less space, and they're easier to list next to each
# other and see that they behave similarly.
Style/SingleLineMethods:
Enabled: false
# This requires writing array[n..] instead of array[n..-1], and I think endless
# ranges look bad.
Style/SlicingWithRange:
Enabled: false
# Single quotes being faster is hardly measurable and only affects parse time.
# Enforcing double quotes reduces the times where you need to change them
# when introducing an interpolation or an apostrophe. Use single quotes only if
# their semantics are needed.
Style/StringLiterals:
EnforcedStyle: double_quotes
# This cop requires arrays of symbols/text to be written like %i[a b c]. We
# don't need that nonsense. ["a", "b", "c"] is clearer and introduces no
# additional syntax to confuse people.
Style/SymbolArray:
EnforcedStyle: brackets
Style/WordArray:
EnforcedStyle: brackets
# Allows procs to be written like { |obj| obj.something } which is clearer.
Style/SymbolProc:
AllowMethodsWithArguments: true
# Parentheses around the condition in a ternary operator helps to differentiate
# it from the true/false results.
Style/TernaryParentheses:
EnforcedStyle: require_parentheses
# This prefers "x += 1 while x < 10" and "x += 1 until x == 10". This hides
# loops, which is not good.
Style/WhileUntilModifier:
Enabled: false

Binary file not shown.

View File

@@ -1,12 +1,12 @@
#==============================================================================# #==============================================================================#
# Pokémon Essentials # # Pokémon Essentials #
# Version 21.1 # # Version 19.1.dev #
# https://github.com/Maruno17/pokemon-essentials # # https://github.com/Maruno17/pokemon-essentials #
#==============================================================================# #==============================================================================#
module Settings module Settings
# The version of your game. It has to adhere to the MAJOR.MINOR.PATCH format. # The version of your game. It has to adhere to the MAJOR.MINOR.PATCH format.
GAME_VERSION = "1.0.0" GAME_VERSION = '1.0.0'
# The generation that the battle system follows. Used throughout the battle # The generation that the battle system follows. Used throughout the battle
# scripts, and also by some other settings which are used in and out of battle # scripts, and also by some other settings which are used in and out of battle
@@ -14,39 +14,40 @@ module Settings
# Note that this isn't perfect. Essentials doesn't accurately replicate every # Note that this isn't perfect. Essentials doesn't accurately replicate every
# single generation's mechanics. It's considered to be good enough. Only # single generation's mechanics. It's considered to be good enough. Only
# generations 5 and later are reasonably supported. # generations 5 and later are reasonably supported.
MECHANICS_GENERATION = 9 MECHANICS_GENERATION = 7
#----------------------------------------------------------------------------- #=============================================================================
# Credits
#-----------------------------------------------------------------------------
# Your game's credits, in an array. You can allow certain lines to be # The default screen width (at a scale of 1.0).
# translated by wrapping them in _INTL() as shown. Blank lines are just "". SCREEN_WIDTH = 512
# To split a line into two columns, put "<s>" in it. Plugin credits and # The default screen height (at a scale of 1.0).
# Essentials engine credits are added to the end of these credits SCREEN_HEIGHT = 384
# automatically. # The default screen scale factor. Possible values are 0.5, 1.0, 1.5 and 2.0.
def self.game_credits SCREEN_SCALE = 1.0
return [
_INTL("My Game by:"),
"Maruno",
"",
_INTL("Also involved were:"),
"A. Lee Uss<s>Anne O'Nymus",
"Ecksam Pell<s>Jane Doe",
"Joe Dan<s>Nick Nayme",
"Sue Donnim<s>",
"",
_INTL("Special thanks to:"),
"Pizza"
]
end
#----------------------------------------------------------------------------- #=============================================================================
# The player and NPCs
#-----------------------------------------------------------------------------
# The maximum level Pokémon can reach.
MAXIMUM_LEVEL = 100
# The level of newly hatched Pokémon.
EGG_LEVEL = 1
# The odds of a newly generated Pokémon being shiny (out of 65536).
SHINY_POKEMON_CHANCE = (MECHANICS_GENERATION >= 6) ? 16 : 8
# The odds of a wild Pokémon/bred egg having Pokérus (out of 65536).
POKERUS_CHANCE = 3
# Whether a bred baby Pokémon can inherit any TM/HM moves from its father. It
# can never inherit TM/HM moves from its mother.
BREEDING_CAN_INHERIT_MACHINE_MOVES = (MECHANICS_GENERATION <= 5)
# Whether a bred baby Pokémon can inherit egg moves from its mother. It can
# always inherit egg moves from its father.
BREEDING_CAN_INHERIT_EGG_MOVES_FROM_MOTHER = (MECHANICS_GENERATION >= 6)
#=============================================================================
# The amount of money the player starts the game with.
INITIAL_MONEY = 3000
# The maximum amount of money the player can have. # The maximum amount of money the player can have.
MAX_MONEY = 9_999_999 MAX_MONEY = 999_999
# The maximum number of Game Corner coins the player can have. # The maximum number of Game Corner coins the player can have.
MAX_COINS = 99_999 MAX_COINS = 99_999
# The maximum number of Battle Points the player can have. # The maximum number of Battle Points the player can have.
@@ -54,24 +55,33 @@ module Settings
# The maximum amount of soot the player can have. # The maximum amount of soot the player can have.
MAX_SOOT = 9_999 MAX_SOOT = 9_999
# The maximum length, in characters, that the player's name can be. # The maximum length, in characters, that the player's name can be.
MAX_PLAYER_NAME_SIZE = 12 MAX_PLAYER_NAME_SIZE = 10
# A set of arrays each containing a trainer type followed by a Game Variable # The maximum number of Pokémon that can be in the party.
# number. If the Variable isn't set to 0, then all trainers with the MAX_PARTY_SIZE = 6
# associated trainer type will be named as whatever is in that Variable.
#=============================================================================
# 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 = [ RIVAL_NAMES = [
[:RIVAL1, 12], [:RIVAL1, 12],
[:RIVAL2, 12], [:RIVAL2, 12],
[:CHAMPION, 12] [:CHAMPION, 12]
] ]
#----------------------------------------------------------------------------- #=============================================================================
# Overworld
#-----------------------------------------------------------------------------
# Whether outdoor maps should be shaded according to the time of day. # Whether outdoor maps should be shaded according to the time of day.
TIME_SHADING = true TIME_SHADING = true
# Whether the reflections of the player/events will ripple horizontally.
ANIMATE_REFLECTIONS = true #=============================================================================
# Whether poisoned Pokémon will lose HP while walking around in the field.
POISON_IN_FIELD = (MECHANICS_GENERATION <= 4)
# Whether poisoned Pokémon will faint while walking around in the field
# (true), or survive the poisoning with 1 HP (false).
POISON_FAINT_IN_FIELD = (MECHANICS_GENERATION <= 3)
# Whether planted berries grow according to Gen 4 mechanics (true) or Gen 3 # Whether planted berries grow according to Gen 4 mechanics (true) or Gen 3
# mechanics (false). # mechanics (false).
NEW_BERRY_PLANTS = (MECHANICS_GENERATION >= 4) NEW_BERRY_PLANTS = (MECHANICS_GENERATION >= 4)
@@ -84,26 +94,26 @@ module Settings
# The ID of the common event that runs when the player stops fishing (runs # The ID of the common event that runs when the player stops fishing (runs
# instead of showing the reeling in animation). # instead of showing the reeling in animation).
FISHING_END_COMMON_EVENT = -1 FISHING_END_COMMON_EVENT = -1
#=============================================================================
# The number of steps allowed before a Safari Zone game is over (0=infinite). # The number of steps allowed before a Safari Zone game is over (0=infinite).
SAFARI_STEPS = 600 SAFARI_STEPS = 600
# The number of seconds a Bug-Catching Contest lasts for (0=infinite). # The number of seconds a Bug Catching Contest lasts for (0=infinite).
BUG_CONTEST_TIME = 20 * 60 # 20 minutes BUG_CONTEST_TIME = 20 * 60 # 20 minutes
# Pairs of map IDs, where the location sign 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.
# 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. # 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 # Moving between two maps that have the exact same name won't show the
# location sign anyway, so you don't need to list those maps here. # location signpost anyway, so you don't need to list those maps here.
NO_LOCATION_SIGNS = [] NO_SIGNPOSTS = []
# Whether poisoned Pokémon will lose HP while walking around in the field.
POISON_IN_FIELD = (MECHANICS_GENERATION <= 4) #=============================================================================
# Whether poisoned Pokémon will faint while walking around in the field
# (true), or survive the poisoning with 1 HP (false).
POISON_FAINT_IN_FIELD = (MECHANICS_GENERATION <= 3)
#-----------------------------------------------------------------------------
# Using moves in the overworld
#-----------------------------------------------------------------------------
# Whether you need at least a certain number of badges to use some hidden # Whether you need at least a certain number of badges to use some hidden
# moves in the field (true), or whether you need one specific badge to use # moves in the field (true), or whether you need one specific badge to use
# them (false). The amounts/specific badges are defined below. # them (false). The amounts/specific badges are defined below.
@@ -123,54 +133,105 @@ module Settings
BADGE_FOR_DIVE = 7 BADGE_FOR_DIVE = 7
BADGE_FOR_WATERFALL = 8 BADGE_FOR_WATERFALL = 8
#----------------------------------------------------------------------------- #=============================================================================
# Pokémon
#-----------------------------------------------------------------------------
# The maximum level Pokémon can reach. # If a move taught by a TM/HM/TR replaces another move, this setting is
MAXIMUM_LEVEL = 100 # whether the machine's move retains the replaced move's PP (true), or whether
# The level of newly hatched Pokémon. # the machine's move has full PP (false).
EGG_LEVEL = 1 TAUGHT_MACHINES_KEEP_OLD_PP = (MECHANICS_GENERATION == 5)
# The odds of a newly generated Pokémon being shiny (out of 65536). # Whether the Black/White Flutes will raise/lower the levels of wild Pokémon
SHINY_POKEMON_CHANCE = (MECHANICS_GENERATION >= 6) ? 16 : 8 # respectively (true), or will lower/raise the wild encounter rate
# Whether super shininess is enabled (uses a different shiny animation). # respectively (false).
SUPER_SHINY = (MECHANICS_GENERATION == 8) FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS = (MECHANICS_GENERATION >= 6)
# Whether Pokémon with the "Legendary", "Mythical" or "Ultra Beast" flags will # Whether Repel uses the level of the first Pokémon in the party regardless of
# have at least 3 perfect IVs. # its HP (true), or it uses the level of the first unfainted Pokémon (false).
LEGENDARIES_HAVE_SOME_PERFECT_IVS = (MECHANICS_GENERATION >= 6) REPEL_COUNTS_FAINTED_POKEMON = (MECHANICS_GENERATION >= 6)
# The odds of a wild Pokémon/bred egg having Pokérus (out of 65536). # Whether Rage Candy Bar acts as a Full Heal (true) or a Potion (false).
POKERUS_CHANCE = 3 RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS = (MECHANICS_GENERATION >= 7)
# Whether IVs and EVs are treated as 0 when calculating a Pokémon's stats.
# IVs and EVs still exist, and are used by Hidden Power and some cosmetic
# things as normal.
DISABLE_IVS_AND_EVS = false
# Whether the Move Relearner can also teach egg moves that the Pokémon knew
# when it hatched and moves that the Pokémon was once taught by a TR. Moves
# from the Pokémon's level-up moveset of the same or a lower level than the
# Pokémon can always be relearned.
MOVE_RELEARNER_CAN_TEACH_MORE_MOVES = (MECHANICS_GENERATION >= 6)
#----------------------------------------------------------------------------- #=============================================================================
# Breeding Pokémon and Day Care
#-----------------------------------------------------------------------------
# Whether Pokémon in the Day Care gain Exp for each step the player takes. # The name of the person who created the Pokémon storage system.
# This should be true for the Day Care and false for the Pokémon Nursery, both def self.storage_creator_name
# of which use the same code in Essentials. return _INTL("Bill")
DAY_CARE_POKEMON_GAIN_EXP_FROM_WALKING = (MECHANICS_GENERATION <= 6) end
# Whether two Pokémon in the Day Care can learn egg moves from each other if # The number of boxes in Pokémon storage.
# they are the same species. NUM_STORAGE_BOXES = 30
DAY_CARE_POKEMON_CAN_SHARE_EGG_MOVES = (MECHANICS_GENERATION >= 8)
# Whether a bred baby Pokémon can inherit any TM/TR/HM moves from its father.
# It can never inherit TM/TR/HM moves from its mother.
BREEDING_CAN_INHERIT_MACHINE_MOVES = (MECHANICS_GENERATION <= 5)
# Whether a bred baby Pokémon can inherit egg moves from its mother. It can
# always inherit egg moves from its father.
BREEDING_CAN_INHERIT_EGG_MOVES_FROM_MOTHER = (MECHANICS_GENERATION >= 6)
#----------------------------------------------------------------------------- #=============================================================================
# Roaming Pokémon
#----------------------------------------------------------------------------- # The names of each pocket of the Bag. Ignore the first entry ("").
def self.bag_pocket_names
return ["",
_INTL("Items"),
_INTL("Medicine"),
_INTL("Poké Balls"),
_INTL("TMs & HMs"),
_INTL("Berries"),
_INTL("Mail"),
_INTL("Battle Items"),
_INTL("Key Items")
]
end
# The maximum number of slots per pocket (-1 means infinite number). Ignore
# the first number (0).
BAG_MAX_POCKET_SIZE = [0, -1, -1, -1, -1, -1, -1, -1, -1]
# The maximum number of items each slot in the Bag can hold.
BAG_MAX_PER_SLOT = 999
# Whether each pocket in turn auto-sorts itself by item ID number. Ignore the
# first entry (the 0).
BAG_POCKET_AUTO_SORT = [0, false, false, false, true, true, false, false, false]
#=============================================================================
# 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).
USE_CURRENT_REGION_DEX = false
# The names of the Pokédex lists, in the order they are defined in the PBS
# file "regionaldexes.txt". The last name is for the National Dex and is added
# onto the end of this array (remember that you don't need to use it). This
# array's order is also the order of $Trainer.pokedex.unlocked_dexes, which
# records which Dexes have been unlocked (the first is unlocked by default).
# If an entry is just a name, then the region map shown in the Area page while
# viewing that Dex list will be the region map of the region the player is
# currently in. The National Dex entry should always behave like this.
# If an entry is of the form [name, number], then the number is a region
# number. That region's map will appear in the Area page while viewing that
# Dex list, no matter which region the player is currently in.
def self.pokedex_names
return [
[_INTL("Kanto Pokédex"), 0],
[_INTL("Johto Pokédex"), 1],
_INTL("National Pokédex")
]
end
# 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).
DEX_SHOWS_ALL_FORMS = false
# An array of numbers, where each number is that of a Dex list (in the same
# order as above, except the National Dex is -1). All Dex lists included here
# will begin their numbering at 0 rather than 1 (e.g. Victini in Unova's Dex).
DEXES_WITH_OFFSETS = []
#=============================================================================
# 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.
# * Game 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]
]
#=============================================================================
# A list of maps used by roaming Pokémon. Each map has an array of other maps # A list of maps used by roaming Pokémon. Each map has an array of other maps
# it can lead to. # it can lead to.
@@ -186,198 +247,45 @@ module Settings
66 => [5, 21, 28, 31, 39, 41, 44, 47, 69], 66 => [5, 21, 28, 31, 39, 41, 44, 47, 69],
69 => [5, 21, 28, 31, 39, 41, 44, 47, 66 ] 69 => [5, 21, 28, 31, 39, 41, 44, 47, 66 ]
} }
# A set of hashes, each containing the details of a roaming Pokémon. The # A set of arrays, each containing the details of a roaming Pokémon. The
# information within each hash is as follows: # information within each array is as follows:
# * :species # * Species.
# * :level # * Level.
# * :icon - Filename in Graphics/UI/Town Map/ of the roamer's Town Map icon. # * Game Switch; the Pokémon roams while this is ON.
# * :game_switch - The Pokémon roams if this is nil or <=0 or if that Game # * Encounter type (0=any, 1=grass/walking in cave, 2=surfing, 3=fishing,
# Switch is ON. Optional. # 4=surfing/fishing). See the bottom of PField_RoamingPokemon for lists.
# * :encounter_type - One of: # * Name of BGM to play for that encounter (optional).
# :all = grass, walking in cave, surfing (default) # * Roaming areas specifically for this Pokémon (optional).
# :land = grass, walking in cave
# :water = surfing, fishing
# :surfing = surfing
# :fishing = fishing
# * :bgm - The BGM to play for the encounter. Optional.
# * :areas - A hash of map IDs that determine where this Pokémon roams. Used
# instead of ROAMING_AREAS above. Optional.
ROAMING_SPECIES = [ ROAMING_SPECIES = [
# { [:LATIAS, 30, 53, 0, "Battle roaming"],
# :species => :LATIAS, [:LATIOS, 30, 53, 0, "Battle roaming"],
# :level => 30, [:KYOGRE, 40, 54, 2, nil, {
# :icon => "pin_latias", 2 => [ 21, 31 ],
# :game_switch => 53, 21 => [2, 31, 69],
# :encounter_type => :all, 31 => [2, 21, 69],
# :bgm => "Battle roaming" 69 => [ 21, 31 ]
# }, }],
# { [:ENTEI, 40, 55, 1, nil]
# :species => :LATIOS,
# :level => 30,
# :icon => "pin_latios",
# :game_switch => 53,
# :encounter_type => :all,
# :bgm => "Battle roaming"
# },
# {
# :species => :KYOGRE,
# :level => 40,
# :game_switch => 54,
# :encounter_type => :surfing,
# :areas => {
# 2 => [ 21, 31 ],
# 21 => [2, 31, 69],
# 31 => [2, 21, 69],
# 69 => [ 21, 31 ]
# }
# },
# {
# :species => :ENTEI,
# :level => 40,
# :icon => "pin_entei",
# :game_switch => 55,
# :encounter_type => :land
# }
] ]
#----------------------------------------------------------------------------- #=============================================================================
# Party and Pokémon storage
#-----------------------------------------------------------------------------
# The maximum number of Pokémon that can be in the party. # A set of arrays, each containing the details of a wild encounter that can
MAX_PARTY_SIZE = 6 # only occur via using the Poké Radar. The information within each array is as
# The number of boxes in Pokémon storage. # follows:
NUM_STORAGE_BOXES = 40 # * Map ID on which this encounter can occur.
# Whether putting a Pokémon into Pokémon storage will heal it. If false, they # * Probability that this encounter will occur (as a percentage).
# are healed by the Recover All: Entire Party event command (at Poké Centers). # * Species.
HEAL_STORED_POKEMON = (MECHANICS_GENERATION <= 7) # * Minimum possible level.
# * Maximum possible level (optional).
#----------------------------------------------------------------------------- POKE_RADAR_ENCOUNTERS = [
# Items [5, 20, :STARLY, 12, 15],
#----------------------------------------------------------------------------- [21, 10, :STANTLER, 14],
[28, 20, :BUTTERFREE, 15, 18],
# Whether various HP-healing items heal the amounts they do in Gen 7+ (true) [28, 20, :BEEDRILL, 15, 18]
# or in earlier Generations (false).
REBALANCED_HEALING_ITEM_AMOUNTS = (MECHANICS_GENERATION >= 7)
# Whether vitamins can add EVs no matter how many that stat already has in it
# (true), or whether they can't make that stat's EVs greater than 100 (false).
NO_VITAMIN_EV_CAP = (MECHANICS_GENERATION >= 8)
# Whether Rage Candy Bar acts as a Full Heal (true) or a Potion (false).
RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS = (MECHANICS_GENERATION >= 7)
# Whether the Black/White Flutes will raise/lower the levels of wild Pokémon
# respectively (true), or will lower/raise the wild encounter rate
# respectively (false).
FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS = (MECHANICS_GENERATION >= 6)
# Whether Rare Candy can be used on a Pokémon that is already at its maximum
# level if it is able to evolve by level-up (if so, triggers that evolution).
RARE_CANDY_USABLE_AT_MAX_LEVEL = (MECHANICS_GENERATION >= 8)
# Whether the player can choose how many of an item to use at once on a
# Pokémon. This applies to Exp-changing items (Rare Candy, Exp Candies) and
# EV-changing items (vitamins, feathers, EV-lowering berries).
USE_MULTIPLE_STAT_ITEMS_AT_ONCE = (MECHANICS_GENERATION >= 8)
# If a move taught by a TM/HM/TR replaces another move, this setting is
# whether the machine's move retains the replaced move's PP (true), or whether
# the machine's move has full PP (false).
TAUGHT_MACHINES_KEEP_OLD_PP = (MECHANICS_GENERATION == 5)
# Whether you get 1 Premier Ball for every 10 of any kind of Poké Ball bought
# from a Mart at once (true), or 1 Premier Ball for buying 10+ regular Poké
# Balls (false).
MORE_BONUS_PREMIER_BALLS = (MECHANICS_GENERATION >= 8)
# The default sell price of an item to a Poké Mart is its buy price divided by
# this number.
ITEM_SELL_PRICE_DIVISOR = (MECHANICS_GENERATION >= 9) ? 4 : 2
#-----------------------------------------------------------------------------
# Pokédex
#-----------------------------------------------------------------------------
# The names of the Pokédex lists, in the order they are defined in the PBS
# file "regional_dexes.txt". The last name is for the National Dex and is
# added onto the end of this array.
# Each entry is either just a name, or is an array containing a name and a
# number. If there is a number, it is a region number as defined in
# town_map.txt. If there is no number, the number of the region the player is
# currently in will be used. The region number determines which Town Map is
# shown in the Area page when viewing that Pokédex list.
def self.pokedex_names
return [
[_INTL("Kanto Pokédex"), 0],
[_INTL("Johto Pokédex"), 1],
_INTL("National Pokédex")
] ]
end
# 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).
USE_CURRENT_REGION_DEX = false
# 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).
DEX_SHOWS_ALL_FORMS = false
# An array of numbers, where each number is that of a Dex list (in the same
# order as above, except the National Dex is -1). All Dex lists included here
# will begin their numbering at 0 rather than 1 (e.g. Victini in Unova's Dex).
DEXES_WITH_OFFSETS = []
# Whether the Pokédex entry of a newly owned species will be shown after it
# hatches from an egg, after it evolves and after obtaining it from a trade,
# in addition to after catching it in battle.
SHOW_NEW_SPECIES_POKEDEX_ENTRY_MORE_OFTEN = (MECHANICS_GENERATION >= 7)
#----------------------------------------------------------------------------- #=============================================================================
# Town Map
#-----------------------------------------------------------------------------
# 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.
# * Game 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/UI/Town Map folder.
# * The graphic will always (true) or never (false) be shown on a wall map.
REGION_MAP_EXTRAS = [
[0, 51, 16, 15, "hidden_Berth", false],
[0, 52, 20, 14, "hidden_Faraday", false]
]
# Whether the player can use Fly while looking at the Town Map. This is only
# allowed if the player can use Fly normally.
CAN_FLY_FROM_TOWN_MAP = true
#-----------------------------------------------------------------------------
# Phone
#-----------------------------------------------------------------------------
# The default setting for Phone.rematches_enabled, which determines whether
# trainers registered in the Phone can become ready for a rematch. If false,
# Phone.rematches_enabled = true will enable rematches at any point you want.
PHONE_REMATCHES_POSSIBLE_FROM_BEGINNING = false
# Whether the messages in a phone call with a trainer are colored blue or red
# depending on that trainer's gender. Note that this doesn't apply to contacts
# whose phone calls are in a Common Event; they will need to be colored
# manually in their Common Events.
COLOR_PHONE_CALL_MESSAGES_BY_CONTACT_GENDER = true
#-----------------------------------------------------------------------------
# Battle starting
#-----------------------------------------------------------------------------
# Whether Repel uses the level of the first Pokémon in the party regardless of
# its HP (true), or it uses the level of the first unfainted Pokémon (false).
REPEL_COUNTS_FAINTED_POKEMON = (MECHANICS_GENERATION >= 6)
# Whether more abilities affect whether wild Pokémon appear, which Pokémon
# they are, etc.
MORE_ABILITIES_AFFECT_WILD_ENCOUNTERS = (MECHANICS_GENERATION >= 8)
# Whether shiny wild Pokémon are more likely to appear if the player has
# previously defeated/caught lots of other Pokémon of the same species.
HIGHER_SHINY_CHANCES_WITH_NUMBER_BATTLED = (MECHANICS_GENERATION >= 8)
# Whether overworld weather can set the default terrain effect in battle.
# Storm weather sets Electric Terrain, and fog weather sets Misty Terrain.
OVERWORLD_WEATHER_SETS_BATTLE_TERRAIN = (MECHANICS_GENERATION >= 8)
#-----------------------------------------------------------------------------
# Game Switches
#-----------------------------------------------------------------------------
# The Game Switch that is set to ON when the player blacks out. # The Game Switch that is set to ON when the player blacks out.
STARTING_OVER_SWITCH = 1 STARTING_OVER_SWITCH = 1
@@ -389,23 +297,14 @@ module Settings
# The Game Switch which, while ON, makes all Pokémon created considered to be # The Game Switch which, while ON, makes all Pokémon created considered to be
# met via a fateful encounter. # met via a fateful encounter.
FATEFUL_ENCOUNTER_SWITCH = 32 FATEFUL_ENCOUNTER_SWITCH = 32
# The Game Switch which, while ON, disables the effect of the Pokémon Box Link
# and prevents the player from accessing Pokémon storage via the party screen
# with it.
DISABLE_BOX_LINK_SWITCH = 35
#----------------------------------------------------------------------------- #=============================================================================
# Overworld animation IDs
#-----------------------------------------------------------------------------
# ID of the animation played when the player steps on grass (grass rustling). # ID of the animation played when the player steps on grass (grass rustling).
GRASS_ANIMATION_ID = 1 GRASS_ANIMATION_ID = 1
# ID of the animation played when the player lands on the ground after hopping # ID of the animation played when the player lands on the ground after hopping
# over a ledge (shows a dust impact). # over a ledge (shows a dust impact).
DUST_ANIMATION_ID = 2 DUST_ANIMATION_ID = 2
# ID of the animation played when the player finishes taking a step onto still
# water (shows a water ripple).
WATER_RIPPLE_ANIMATION_ID = 8
# ID of the animation played when a trainer notices the player (an exclamation # ID of the animation played when a trainer notices the player (an exclamation
# bubble). # bubble).
EXCLAMATION_ANIMATION_ID = 3 EXCLAMATION_ANIMATION_ID = 3
@@ -422,46 +321,17 @@ module Settings
# is on the map (for new plant growth mechanics only). # is on the map (for new plant growth mechanics only).
PLANT_SPARKLE_ANIMATION_ID = 7 PLANT_SPARKLE_ANIMATION_ID = 7
#----------------------------------------------------------------------------- #=============================================================================
# Files
#-----------------------------------------------------------------------------
DEFAULT_WILD_BATTLE_BGM = "Battle wild" # An array of available languages in the game, and their corresponding message
DEFAULT_WILD_VICTORY_BGM = "Battle victory" # file in the Data folder. Edit only if you have 2 or more languages to choose
DEFAULT_WILD_CAPTURE_ME = "Battle capture success" # from.
DEFAULT_TRAINER_BATTLE_BGM = "Battle trainer"
DEFAULT_TRAINER_VICTORY_BGM = "Battle victory"
#-----------------------------------------------------------------------------
# Languages
#-----------------------------------------------------------------------------
# An array of available languages in the game. Each one is an array containing
# the display name of the language in-game, and that language's filename
# fragment. A language will use the language data files from the Data folder
# called messages_FRAGMENT_core.dat and messages_FRAGMENT_game.dat (if they
# exist).
LANGUAGES = [ LANGUAGES = [
# ["English", "english"], # ["English", "english.dat"],
# ["Deutsch", "deutsch"] # ["Deutsch", "deutsch.dat"]
] ]
#----------------------------------------------------------------------------- #=============================================================================
# Screen size and zoom
#-----------------------------------------------------------------------------
# The default screen width (at a scale of 1.0). You should also edit the
# property "defScreenW" in mkxp.json to match.
SCREEN_WIDTH = 512
# The default screen height (at a scale of 1.0). You should also edit the
# property "defScreenH" in mkxp.json to match.
SCREEN_HEIGHT = 384
# The default screen scale factor. Possible values are 0.5, 1.0, 1.5 and 2.0.
SCREEN_SCALE = 1.0
#-----------------------------------------------------------------------------
# Messages
#-----------------------------------------------------------------------------
# Available speech frames. These are graphic files in "Graphics/Windowskins/". # Available speech frames. These are graphic files in "Graphics/Windowskins/".
SPEECH_WINDOWSKINS = [ SPEECH_WINDOWSKINS = [
@@ -487,6 +357,7 @@ module Settings
"speech hgss 20", "speech hgss 20",
"speech pl 18" "speech pl 18"
] ]
# Available menu frames. These are graphic files in "Graphics/Windowskins/". # Available menu frames. These are graphic files in "Graphics/Windowskins/".
MENU_WINDOWSKINS = [ MENU_WINDOWSKINS = [
"choice 1", "choice 1",
@@ -518,30 +389,10 @@ module Settings
"choice 27", "choice 27",
"choice 28" "choice 28"
] ]
#-----------------------------------------------------------------------------
# Debug helpers
#-----------------------------------------------------------------------------
# Whether the game will ask you if you want to fully compile every time you
# start the game (in Debug mode). You will not need to hold Ctrl/Shift to
# compile anything.
PROMPT_TO_COMPILE = false
# Whether the game will skip the intro splash screens and title screen, and go
# straight to the Continue/New Game screen. Only applies to playing in Debug
# mode.
SKIP_TITLE_SCREEN = true
# Whether the game will skip the Continue/New Game screen and go straight into
# a saved game (if there is one) or start a new game (if there isn't). Only
# applies to playing in Debug mode.
SKIP_CONTINUE_SCREEN = false
end end
#===============================================================================
# DO NOT EDIT THESE! # DO NOT EDIT THESE!
#===============================================================================
module Essentials module Essentials
VERSION = "21.1" VERSION = "19.1.dev"
ERROR_TEXT = "" ERROR_TEXT = ""
MKXPZ_VERSION = "2.4.2/c9378cf"
end end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module PBDebug module PBDebug
@@log = [] @@log = []
@@ -10,65 +7,28 @@ module PBDebug
rescue rescue
PBDebug.log("") PBDebug.log("")
PBDebug.log("**Exception: #{$!.message}") PBDebug.log("**Exception: #{$!.message}")
backtrace = "" PBDebug.log("#{$!.backtrace.inspect}")
$!.backtrace.each { |line| backtrace += line + "\r\n" }
PBDebug.log(backtrace)
PBDebug.log("") PBDebug.log("")
pbPrintException($!) # if $INTERNAL # if $INTERNAL
pbPrintException($!)
# end
PBDebug.flush PBDebug.flush
end end
end end
def self.flush def self.flush
if $DEBUG && $INTERNAL && @@log.length > 0 if $DEBUG && $INTERNAL && @@log.length>0
File.open("Data/debuglog.txt", "a+b") { |f| f.write(@@log.join) } File.open("Data/debuglog.txt", "a+b") { |f| f.write("#{@@log}") }
end end
@@log.clear @@log.clear
end end
def self.log(msg) def self.log(msg)
if $DEBUG && $INTERNAL if $DEBUG && $INTERNAL
echoln msg.gsub("%", "%%") @@log.push("#{msg}\r\n")
@@log.push(msg + "\r\n") # if @@log.length>1024
PBDebug.flush # if @@log.length > 1024 PBDebug.flush
end # end
end
def self.log_header(msg)
if $DEBUG && $INTERNAL
echoln Console.markup_style(msg.gsub("%", "%%"), text: :light_purple)
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_message(msg)
if $DEBUG && $INTERNAL
msg = "\"" + msg + "\""
echoln Console.markup_style(msg.gsub("%", "%%"), text: :dark_gray)
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_ai(msg)
if $DEBUG && $INTERNAL
msg = "[AI] " + msg
echoln msg.gsub("%", "%%")
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_score_change(amt, msg)
return if amt == 0
if $DEBUG && $INTERNAL
sign = (amt > 0) ? "+" : "-"
amt_text = sprintf("%3d", amt.abs)
msg = " #{sign}#{amt_text}: #{msg}"
echoln msg.gsub("%", "%%")
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end end
end end

View File

@@ -1,24 +1,25 @@
#=============================================================================== # To use the console, use the executable explicitly built
# To use the console, use the executable explicitly built with the console # with the console enabled on Windows. On Linux and macOS,
# enabled on Windows. On Linux and macOS, just launch the executable directly # just launch the executable directly from a terminal.
# from a terminal.
#===============================================================================
module Console module Console
def self.setup_console def self.setup_console
return unless $DEBUG return unless $DEBUG
echoln "GPU Cache Max: #{Bitmap.max_size}" echoln "--------------------------------"
echoln "-------------------------------------------------------------------------------"
echoln "#{System.game_title} Output Window" echoln "#{System.game_title} Output Window"
echoln "-------------------------------------------------------------------------------" echoln "--------------------------------"
echoln "If you can see this window, you are running the game in Debug Mode. This means" echoln "If you are seeing this window, you are running"
echoln "that you're either playing a debug version of the game, or you're playing from" echoln "#{System.game_title} in Debug Mode. This means"
echoln "within RPG Maker XP." echoln "that you're either playing a Debug Version, or"
echoln "you are playing from within RPG Maker XP."
echoln "" echoln ""
echoln "Closing this window will close the game. If you want to get rid of this window," echoln "Closing this window will close the game. If"
echoln "run the program from the Shell, or download a release version of the game." echoln "you want to get rid of this window, run the"
echoln "-------------------------------------------------------------------------------" echoln "program from the Shell, or download a Release"
echoln "version."
echoln ""
echoln "--------------------------------"
echoln "Debug Output:" echoln "Debug Output:"
echoln "-------------------------------------------------------------------------------" echoln "--------------------------------"
echoln "" echoln ""
end end
@@ -35,9 +36,6 @@ module Console
end end
end end
#===============================================================================
#
#===============================================================================
module Kernel module Kernel
def echo(string) def echo(string)
return unless $DEBUG return unless $DEBUG
@@ -45,199 +43,9 @@ module Kernel
end end
def echoln(string) def echoln(string)
caller_info = caller(1..1).first
file, line, method = caller_info.split(":")
echo "#{file}, #{line}:\t"
echo(string) echo(string)
echo("\r\n") echo("\r\n")
end end
end end
Console.setup_console Console.setup_console
#===============================================================================
# Console message formatting
#===============================================================================
module Console
module_function
#-----------------------------------------------------------------------------
# echo string into console (example shorthand for common options)
#-----------------------------------------------------------------------------
# heading 1
def echo_h1(msg)
echoln markup_style("*** #{msg} ***", text: :brown)
echoln ""
end
# heading 2
def echo_h2(msg, **options)
echoln markup_style(msg, **options)
echoln ""
end
# heading 3
def echo_h3(msg)
echoln markup(msg)
echoln ""
end
# list item
def echo_li(msg, pad = 0, color = :brown)
echo markup_style(" -> ", text: color)
pad = (pad - msg.length) > 0 ? "." * (pad - msg.length) : ""
echo markup(msg + pad)
end
# list item with line break after
def echoln_li(msg, pad = 0, color = :brown)
self.echo_li(msg, pad, color)
echoln ""
end
# Same as echoln_li but text is in green
def echoln_li_done(msg)
self.echo_li(markup_style(msg, text: :green), 0, :green)
echoln ""
echoln ""
end
# paragraph with markup
def echo_p(msg)
echoln markup(msg)
end
# warning message
def echo_warn(msg)
echoln markup_style("WARNING: #{msg}", text: :yellow)
end
# error message
def echo_error(msg)
echoln markup_style("ERROR: #{msg}", text: :light_red)
end
# status output
def echo_status(status)
if status
echoln markup_style("OK", text: :green)
else
echoln markup_style("FAIL", text: :red)
end
end
# completion output
def echo_done(status)
if status
echoln markup_style("done", text: :green)
else
echoln markup_style("error", text: :red)
end
end
#-----------------------------------------------------------------------------
# Markup options
#-----------------------------------------------------------------------------
def string_colors
{
default: "38", black: "30", red: "31", green: "32", brown: "33",
blue: "34", purple: "35", cyan: "36", gray: "37",
dark_gray: "1;30", light_red: "1;31", light_green: "1;32", yellow: "1;33",
light_blue: "1;34", light_purple: "1;35", light_cyan: "1;36", white: "1;37"
}
end
def background_colors
{
default: "0", black: "40", red: "41", green: "42", brown: "43",
blue: "44", purple: "45", cyan: "46", gray: "47",
dark_gray: "100", light_red: "101", light_green: "102", yellow: "103",
light_blue: "104", light_purple: "105", light_cyan: "106", white: "107"
}
end
def font_options
{
bold: "1", dim: "2", italic: "3", underline: "4", reverse: "7",
hidden: "8"
}
end
# Text markup that turns text between them a certain color
def markup_colors
{
"`" => :cyan, '"' => :purple, "==" => :purple, "$" => :green, "~" => :red
}
end
def markup_options
{
"__" => :underline, "*" => :bold, "|" => :italic
}
end
# apply console coloring
def markup_style(string, text: :default, bg: :default, **options)
# get colors
code_text = string_colors[text]
code_bg = background_colors[bg]
# get options
options_pool = options.select { |key, val| font_options.key?(key) && val }
markup_pool = options_pool.keys.map { |opt| font_options[opt] }.join(";").squeeze
# return formatted string
return "\e[#{code_bg};#{markup_pool};#{code_text}m#{string}\e[0m".squeeze(";")
end
#-----------------------------------------------------------------------------
# Perform markup on text
#-----------------------------------------------------------------------------
def markup_all_options
@markup_all_options ||= markup_colors.merge(markup_options)
end
def markup_component(string, component, key, options)
# trim inner markup content
l = key.length
trimmed = component[l...-l]
# merge markup options
options[trimmed] = {} unless options[trimmed]
options[trimmed].deep_merge!({}.tap do |new_opt|
new_opt[:text] = markup_colors[key] if markup_colors.key?(key)
new_opt[markup_options[key]] = true if markup_options.key?(key)
end)
# remove markup from input string
string.gsub!(component, trimmed)
# return output
return string, options
end
def markup_breakdown(string, options = {})
# iterate through all options
markup_all_options.each_key do |key|
# ensure escape
key_char = key.chars.map { |c| "\\#{c}" }.join
# define regex
regex = "#{key_char}.*?#{key_char}"
# go through matches
string.scan(/#{regex}/).each do |component|
return *markup_breakdown(*markup_component(string, component, key, options))
end
end
# return output
return string, options
end
def markup(string)
# get a breakdown of all markup options
string, options = markup_breakdown(string)
# iterate through each option and apply
options.each do |key, opt|
string.gsub!(key, markup_style(key, **opt))
end
# return string
return string
end
end

View File

@@ -4,29 +4,12 @@
class Reset < Exception class Reset < Exception
end end
#=============================================================================== def pbGetExceptionMessage(e,_script="")
#
#===============================================================================
class EventScriptError < Exception
attr_accessor :event_message
def initialize(message)
super(nil)
@event_message = message
end
end
#===============================================================================
#
#===============================================================================
def pbGetExceptionMessage(e, _script = "")
return e.event_message.dup if e.is_a?(EventScriptError) # Message with map/event ID generated elsewhere
emessage = e.message.dup emessage = e.message.dup
emessage.force_encoding(Encoding::UTF_8) emessage.force_encoding(Encoding::UTF_8)
case e if e.is_a?(Hangup)
when Hangup
emessage = "The script is taking too long. The game will restart." emessage = "The script is taking too long. The game will restart."
when Errno::ENOENT elsif e.is_a?(Errno::ENOENT)
filename = emessage.sub("No such file or directory - ", "") filename = emessage.sub("No such file or directory - ", "")
emessage = "File #{filename} not found." emessage = "File #{filename} not found."
end end
@@ -35,28 +18,30 @@ def pbGetExceptionMessage(e, _script = "")
end end
def pbPrintException(e) def pbPrintException(e)
emessage = ""
if $EVENTHANGUPMSG && $EVENTHANGUPMSG!=""
emessage = $EVENTHANGUPMSG # Message with map/event ID generated elsewhere
$EVENTHANGUPMSG = nil
else
emessage = pbGetExceptionMessage(e) emessage = pbGetExceptionMessage(e)
end
# begin message formatting # begin message formatting
message = "[Pokémon Essentials version #{Essentials::VERSION}]\r\n" message = "[Pokémon Essentials version #{Essentials::VERSION}]\r\n"
message += "#{Essentials::ERROR_TEXT}\r\n" # For third party scripts to add to message += "#{Essentials::ERROR_TEXT}\r\n" # For third party scripts to add to
if !e.is_a?(EventScriptError)
message += "Exception: #{e.class}\r\n" message += "Exception: #{e.class}\r\n"
message += "Message: " message += "Message: #{emessage}\r\n"
end
message += emessage
# show last 10/25 lines of backtrace # show last 10/25 lines of backtrace
if !e.is_a?(EventScriptError) message += "\r\nBacktrace:\r\n"
message += "\r\n\r\nBacktrace:\r\n" btrace = ""
backtrace_text = ""
if e.backtrace if e.backtrace
maxlength = ($INTERNAL) ? 25 : 10 maxlength = ($INTERNAL) ? 25 : 10
e.backtrace[0, maxlength].each { |i| backtrace_text += "#{i}\r\n" } e.backtrace[0, maxlength].each { |i| btrace += "#{i}\r\n" }
end
backtrace_text.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] } rescue nil
message += backtrace_text
end end
btrace.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] } rescue nil
message += btrace
# output to log # output to log
errorlog = "errorlog.txt" errorlog = "errorlog.txt"
errorlog = RTP.getSaveFileName("errorlog.txt") if (Object.const_defined?(:RTP) rescue false)
File.open(errorlog, "ab") do |f| File.open(errorlog, "ab") do |f|
f.write("\r\n=================\r\n\r\n[#{Time.now}]\r\n") f.write("\r\n=================\r\n\r\n[#{Time.now}]\r\n")
f.write(message) f.write(message)
@@ -69,8 +54,8 @@ def pbPrintException(e)
# output message # output message
print("#{message}\r\nThis exception was logged in #{errorlogline}.\r\nHold Ctrl when closing this message to copy it to the clipboard.") print("#{message}\r\nThis exception was logged in #{errorlogline}.\r\nHold Ctrl when closing this message to copy it to the clipboard.")
# Give a ~500ms coyote time to start holding Control # Give a ~500ms coyote time to start holding Control
t = System.uptime t = System.delta
until System.uptime - t >= 0.5 until (System.delta - t) >= 500000
Input.update Input.update
if Input.press?(Input::CTRL) if Input.press?(Input::CTRL)
Input.clipboard = message Input.clipboard = message

View File

@@ -1,6 +1,4 @@
#===============================================================================
# The Kernel module is extended to include the validate method. # The Kernel module is extended to include the validate method.
#===============================================================================
module Kernel module Kernel
private private

View File

@@ -1,7 +1,5 @@
#===============================================================================
# The Deprecation module is used to warn game & plugin creators of deprecated # The Deprecation module is used to warn game & plugin creators of deprecated
# methods. # methods.
#===============================================================================
module Deprecation module Deprecation
module_function module_function
@@ -10,21 +8,19 @@ module Deprecation
# @param removal_version [String] version the method is removed in # @param removal_version [String] version the method is removed in
# @param alternative [String] preferred alternative method # @param alternative [String] preferred alternative method
def warn_method(method_name, removal_version = nil, alternative = nil) def warn_method(method_name, removal_version = nil, alternative = nil)
text = _INTL('Usage of deprecated method "{1}" or its alias.', method_name) text = _INTL('WARN: usage of deprecated method "{1}" or its alias.', method_name)
unless removal_version.nil? unless removal_version.nil?
text += "\n" + _INTL("The method is slated to be removed in Essentials {1}.", removal_version) text += _INTL("\nThe method is slated to be"\
" removed in Essentials {1}.", removal_version)
end end
unless alternative.nil? unless alternative.nil?
text += "\n" + _INTL("Use \"{1}\" instead.", alternative) text += _INTL("\nUse \"{1}\" instead.", alternative)
end end
Console.echo_warn text echoln text
end end
end end
#=============================================================================== # The Module class is extended to allow easy deprecation of instance and class methods.
# The Module class is extended to allow easy deprecation of instance and class
# methods.
#===============================================================================
class Module class Module
private private
@@ -45,11 +41,11 @@ class Module
raise ArgumentError, "#{class_name} does not have method #{aliased_method} defined" raise ArgumentError, "#{class_name} does not have method #{aliased_method} defined"
end end
delimiter = class_method ? "." : "#" delimiter = class_method ? '.' : '#'
target.define_method(name) do |*args, **kvargs| target.define_method(name) do |*args, **kvargs|
alias_name = sprintf("%s%s%s", class_name, delimiter, name) alias_name = format('%s%s%s', class_name, delimiter, name)
aliased_method_name = sprintf("%s%s%s", class_name, delimiter, aliased_method) aliased_method_name = format('%s%s%s', class_name, delimiter, aliased_method)
Deprecation.warn_method(alias_name, removal_in, aliased_method_name) Deprecation.warn_method(alias_name, removal_in, aliased_method_name)
method(aliased_method).call(*args, **kvargs) method(aliased_method).call(*args, **kvargs)
end end

View File

@@ -1,16 +1,27 @@
#=============================================================================== # Using mkxp-z v2.2.0 - https://gitlab.com/mkxp-z/mkxp-z/-/releases/v2.2.0
# Using mkxp-z v2.4.2/c9378cf - built 2023-07-07
# https://github.com/mkxp-z/mkxp-z/actions/runs/5482601942
#===============================================================================
$VERBOSE = nil $VERBOSE = nil
Font.default_shadow = false if Font.respond_to?(:default_shadow) Font.default_shadow = false if Font.respond_to?(:default_shadow)
Encoding.default_internal = Encoding::UTF_8 Graphics.frame_rate = 40
Encoding.default_external = Encoding::UTF_8
def pbSetWindowText(string) def pbSetWindowText(string)
System.set_window_title(string || System.game_title) System.set_window_title(string || System.game_title)
end end
class Bitmap
alias mkxp_draw_text draw_text unless method_defined?(:mkxp_draw_text)
def draw_text(x, y, width, height, text, align = 0)
height = text_size(text).height
mkxp_draw_text(x, y, width, height, text, align)
end
end
module Graphics
def self.delta_s
return self.delta.to_f / 1_000_000
end
end
def pbSetResizeFactor(factor) def pbSetResizeFactor(factor)
if !$ResizeInitialized if !$ResizeInitialized
Graphics.resize_screen(Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) Graphics.resize_screen(Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT)
@@ -24,33 +35,3 @@ def pbSetResizeFactor(factor)
Graphics.center Graphics.center
end end
end end
#===============================================================================
#
#===============================================================================
class Bitmap
attr_accessor :text_offset_y
alias mkxp_draw_text draw_text unless method_defined?(:mkxp_draw_text)
def draw_text(x, y, width, height = nil, text = "", align = 0)
if x.is_a?(Rect)
x.y -= (@text_offset_y || 0)
# rect, string & alignment
mkxp_draw_text(x, y, width)
else
y -= (@text_offset_y || 0)
height = text_size(text).height
mkxp_draw_text(x, y, width, height, text, align)
end
end
end
#===============================================================================
#
#===============================================================================
if System::VERSION != Essentials::MKXPZ_VERSION
printf(sprintf("\e[1;33mWARNING: mkxp-z version %s detected, but this version of Pokémon Essentials was designed for mkxp-z version %s.\e[0m\r\n",
System::VERSION, Essentials::MKXPZ_VERSION))
printf("\e[1;33mWARNING: Pokémon Essentials may not work properly.\e[0m\r\n")
end

View File

@@ -2,24 +2,27 @@
# Reads files of certain format from a directory # Reads files of certain format from a directory
#=============================================================================== #===============================================================================
class Dir class Dir
#-----------------------------------------------------------------------------
# Reads all files in a directory # Reads all files in a directory
#-----------------------------------------------------------------------------
def self.get(dir, filters = "*", full = true) def self.get(dir, filters = "*", full = true)
files = [] files = []
filters = [filters] if !filters.is_a?(Array) filters = [filters] if !filters.is_a?(Array)
self.chdir(dir) do self.chdir(dir) do
filters.each do |filter| for filter in filters
self.glob(filter) { |f| files.push(full ? (dir + "/" + f) : f) } self.glob(filter){ |f| files.push(full ? (dir + "/" + f) : f) }
end end
end end
return files.sort return files.sort
end end
#-----------------------------------------------------------------------------
# Generates entire file/folder tree from a certain directory # Generates entire file/folder tree from a certain directory
#-----------------------------------------------------------------------------
def self.all(dir, filters = "*", full = true) def self.all(dir, filters = "*", full = true)
# sets variables for starting # sets variables for starting
files = [] files = []
subfolders = [] subfolders = []
self.get(dir, filters, full).each do |file| for file in self.get(dir, filters, full)
# engages in recursion to read the entire file tree # engages in recursion to read the entire file tree
if self.safe?(file) # Is a directory if self.safe?(file) # Is a directory
subfolders += self.all(file, filters, full) subfolders += self.all(file, filters, full)
@@ -30,60 +33,70 @@ class Dir
# returns all found files # returns all found files
return files + subfolders return files + subfolders
end end
#-----------------------------------------------------------------------------
# Checks for existing directory # Checks for existing directory, gets around accents
#-----------------------------------------------------------------------------
def self.safe?(dir) def self.safe?(dir)
return FileTest.directory?(dir) return false if !FileTest.directory?(dir)
end ret = false
self.chdir(dir) { ret = true } rescue nil
# Creates all the required directories for filename path return ret
def self.create(path)
path.gsub!("\\", "/") # Windows compatibility
# get path tree
dirs = path.split("/")
full = ""
dirs.each do |dir|
full += dir + "/"
# creates directories
self.mkdir(full) if !self.safe?(full)
end
end
# Generates entire folder tree from a certain directory
def self.all_dirs(dir)
# sets variables for starting
dirs = []
self.get(dir, "*", true).each do |file|
# engages in recursion to read the entire folder tree
dirs += self.all_dirs(file) if self.safe?(file)
end
# returns all found directories
return dirs.length > 0 ? (dirs + [dir]) : [dir]
end
# Deletes all the files in a directory and all the sub directories (allows for non-empty dirs)
def self.delete_all(dir)
# delete all files in dir
self.all(dir).each { |f| File.delete(f) }
# delete all dirs in dir
self.all_dirs(dir).each { |f| Dir.delete(f) }
end end
#-----------------------------------------------------------------------------
end end
#===============================================================================
# extensions for file class
#===============================================================================
class File
#-----------------------------------------------------------------------------
# Checks for existing file, gets around accents
#-----------------------------------------------------------------------------
def self.safe?(file)
ret = false
self.open(file, 'rb') { ret = true } rescue nil
return ret
end
#-----------------------------------------------------------------------------
end
#=============================================================================== #===============================================================================
# Checking for files and directories # 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 # Similar to "Dir.glob", but designed to work around a problem with accessing
# files if a path contains accent marks. # files if a path contains accent marks.
# "dir" is the directory path, "wildcard" is the filename pattern to match. # "dir" is the directory path, "wildcard" is the filename pattern to match.
def safeGlob(dir, wildcard) def safeGlob(dir,wildcard)
ret = [] ret = []
afterChdir = false afterChdir = false
begin begin
Dir.chdir(dir) do Dir.chdir(dir) {
afterChdir = true afterChdir = true
Dir.glob(wildcard) { |f| ret.push(dir + "/" + f) } Dir.glob(wildcard) { |f| ret.push(dir+"/"+f) }
end }
rescue Errno::ENOENT rescue Errno::ENOENT
raise if afterChdir raise if afterChdir
end end
@@ -95,8 +108,8 @@ end
def pbResolveAudioSE(file) def pbResolveAudioSE(file)
return nil if !file return nil if !file
if RTP.exists?("Audio/SE/" + file, ["", ".wav", ".ogg", ".mp3", ".wma"]) if RTP.exists?("Audio/SE/"+file,["",".wav",".mp3",".ogg"])
return RTP.getPath("Audio/SE/" + file, ["", ".wav", ".ogg", ".mp3", ".wma"]) return RTP.getPath("Audio/SE/"+file,["",".wav",".mp3",".ogg"])
end end
return nil return nil
end end
@@ -105,19 +118,19 @@ end
# archives. Returns nil if the path can't be found. # archives. Returns nil if the path can't be found.
def pbResolveBitmap(x) def pbResolveBitmap(x)
return nil if !x return nil if !x
noext = x.gsub(/\.(bmp|png|gif|jpg|jpeg)$/, "") noext = x.gsub(/\.(bmp|png|gif|jpg|jpeg)$/,"")
filename = nil filename = nil
# RTP.eachPathFor(x) { |path| # RTP.eachPathFor(x) { |path|
# filename = pbTryString(path) if !filename # filename = pbTryString(path) if !filename
# filename = pbTryString(path + ".gif") if !filename # filename = pbTryString(path+".gif") if !filename
# } # }
RTP.eachPathFor(noext) do |path| RTP.eachPathFor(noext) { |path|
filename = pbTryString(path + ".png") if !filename filename = pbTryString(path+".png") if !filename
filename = pbTryString(path + ".gif") if !filename filename = pbTryString(path+".gif") if !filename
# filename = pbTryString(path + ".jpg") if !filename # filename = pbTryString(path+".jpg") if !filename
# filename = pbTryString(path + ".jpeg") if !filename # filename = pbTryString(path+".jpeg") if !filename
# filename = pbTryString(path + ".bmp") if !filename # filename = pbTryString(path+".bmp") if !filename
end }
return filename return filename
end end
@@ -144,7 +157,7 @@ def canonicalize(c)
pos = -1 pos = -1
ret = [] ret = []
retstr = "" retstr = ""
csplit.each do |x| for x in csplit
if x == ".." if x == ".."
if pos >= 0 if pos >= 0
ret.delete_at(pos) ret.delete_at(pos)
@@ -155,47 +168,46 @@ def canonicalize(c)
pos += 1 pos += 1
end end
end end
ret.length.times do |i| for i in 0...ret.length
retstr += "/" if i > 0 retstr += "/" if i > 0
retstr += ret[i] retstr += ret[i]
end end
return retstr return retstr
end end
#===============================================================================
#
#===============================================================================
module RTP module RTP
@rtpPaths = nil @rtpPaths = nil
def self.exists?(filename, extensions = []) def self.exists?(filename,extensions=[])
return false if nil_or_empty?(filename) return false if nil_or_empty?(filename)
eachPathFor(filename) do |path| eachPathFor(filename) { |path|
return true if FileTest.exist?(path) return true if safeExists?(path)
extensions.each do |ext| for ext in extensions
return true if FileTest.exist?(path + ext) return true if safeExists?(path+ext)
end
end end
}
return false return false
end end
def self.getImagePath(filename) def self.getImagePath(filename)
return self.getPath(filename, ["", ".png", ".gif"]) # ".jpg", ".jpeg", ".bmp" return self.getPath(filename,["",".png",".gif"]) # ".jpg", ".jpeg", ".bmp"
end end
def self.getAudioPath(filename) def self.getAudioPath(filename)
return self.getPath(filename, ["", ".wav", ".ogg", ".mp3", ".midi", ".mid", ".wma"]) return self.getPath(filename,["",".mp3",".wav",".wma",".mid",".ogg",".midi"])
end end
def self.getPath(filename, extensions = []) def self.getPath(filename,extensions=[])
return filename if nil_or_empty?(filename) return filename if nil_or_empty?(filename)
eachPathFor(filename) do |path| eachPathFor(filename) { |path|
return path if FileTest.exist?(path) return path if safeExists?(path)
extensions.each do |ext| for ext in extensions
file = path + ext file = path+ext
return file if FileTest.exist?(file) return file if safeExists?(file)
end
end end
}
return filename return filename
end end
@@ -207,13 +219,13 @@ module RTP
yield filename yield filename
else else
# relative path # relative path
RTP.eachPath do |path| RTP.eachPath { |path|
if path == "./" if path=="./"
yield filename yield filename
else else
yield path + filename yield path+filename
end
end end
}
end end
end end
@@ -225,9 +237,11 @@ module RTP
def self.eachPath def self.eachPath
# XXX: Use "." instead of Dir.pwd because of problems retrieving files if # XXX: Use "." instead of Dir.pwd because of problems retrieving files if
# the current directory contains an accent mark # the current directory contains an accent mark
yield ".".gsub(/[\/\\]/, "/").gsub(/[\/\\]$/, "") + "/" yield ".".gsub(/[\/\\]/,"/").gsub(/[\/\\]$/,"")+"/"
end end
private
def self.getSaveFileName(fileName) def self.getSaveFileName(fileName)
File.join(getSaveFolder, fileName) File.join(getSaveFolder, fileName)
end end
@@ -243,47 +257,50 @@ module RTP
end end
end end
#===============================================================================
#
#===============================================================================
module FileTest module FileTest
IMAGE_EXTENSIONS = [".png", ".gif"] # ".jpg", ".jpeg", ".bmp", Image_ext = ['.png', '.gif'] # '.jpg', '.jpeg', '.bmp',
AUDIO_EXTENSIONS = [".wav", ".ogg", ".mp3", ".midi", ".mid", ".wma"] Audio_ext = ['.mp3', '.mid', '.midi', '.ogg', '.wav', '.wma']
def self.audio_exist?(filename) def self.audio_exist?(filename)
return RTP.exists?(filename, AUDIO_EXTENSIONS) return RTP.exists?(filename,Audio_ext)
end end
def self.image_exist?(filename) def self.image_exist?(filename)
return RTP.exists?(filename, IMAGE_EXTENSIONS) return RTP.exists?(filename,Image_ext)
end end
end end
#===============================================================================
#
#===============================================================================
# Used to determine whether a data file exists (rather than a graphics or # Used to determine whether a data file exists (rather than a graphics or
# audio file). Doesn't check RTP, but does check encrypted archives. # audio file). Doesn't check RTP, but does check encrypted archives.
# NOTE: pbGetFileChar checks anything added in MKXP's RTP setting, and matching
# mount points added through System.mount. # Note: pbGetFileChar checks anything added in MKXP's RTP setting,
# and matching mount points added through System.mount
def pbRgssExists?(filename) def pbRgssExists?(filename)
return !pbGetFileChar(filename).nil? if FileTest.exist?("./Game.rgssad") if safeExists?("./Game.rgssad")
return pbGetFileChar(filename)!=nil
else
filename = canonicalize(filename) filename = canonicalize(filename)
return FileTest.exist?(filename) return safeExists?(filename)
end
end end
# Opens an IO, even if the file is in an encrypted archive. # Opens an IO, even if the file is in an encrypted archive.
# Doesn't check RTP for the file. # Doesn't check RTP for the file.
# NOTE: load_data checks anything added in MKXP's RTP setting, and matching
# mount points added through System.mount. # Note: load_data checks anything added in MKXP's RTP setting,
def pbRgssOpen(file, mode = nil) # and matching mount points added through System.mount
# File.open("debug.txt", "ab") { |fw| fw.write([file, mode, Time.now.to_f].inspect + "\r\n") } def pbRgssOpen(file,mode=nil)
if !FileTest.exist?("./Game.rgssad") #File.open("debug.txt","ab") { |fw| fw.write([file,mode,Time.now.to_f].inspect+"\r\n") }
if !safeExists?("./Game.rgssad")
if block_given? if block_given?
File.open(file, mode) { |f| yield f } File.open(file,mode) { |f| yield f }
return nil return nil
else else
return File.open(file, mode) return File.open(file,mode)
end end
end end
file = canonicalize(file) file = canonicalize(file)
@@ -301,9 +318,9 @@ end
# encrypted archives. # encrypted archives.
def pbGetFileChar(file) def pbGetFileChar(file)
canon_file = canonicalize(file) canon_file = canonicalize(file)
if !FileTest.exist?("./Game.rgssad") if !safeExists?("./Game.rgssad")
return nil if !FileTest.exist?(canon_file) return nil if !safeExists?(canon_file)
return nil if file.last == "/" # Is a directory return nil if file.last == '/' # Is a directory
begin begin
File.open(canon_file, "rb") { |f| return f.read(1) } # read one byte File.open(canon_file, "rb") { |f| return f.read(1) } # read one byte
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, Errno::EISDIR rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, Errno::EISDIR
@@ -321,19 +338,20 @@ end
def pbTryString(x) def pbTryString(x)
ret = pbGetFileChar(x) ret = pbGetFileChar(x)
return nil_or_empty?(ret) ? nil : x return (ret!=nil && ret!="") ? x : nil
end end
# Gets the contents of a file. Doesn't check RTP, but does check # Gets the contents of a file. Doesn't check RTP, but does check
# encrypted archives. # encrypted archives.
# NOTE: load_data will check anything added in MKXP's RTP setting, and matching
# mount points added through System.mount. # Note: load_data will check anything added in MKXP's RTP setting,
# and matching mount points added through System.mount
def pbGetFileString(file) def pbGetFileString(file)
file = canonicalize(file) file = canonicalize(file)
if !FileTest.exist?("./Game.rgssad") if !safeExists?("./Game.rgssad")
return nil if !FileTest.exist?(file) return nil if !safeExists?(file)
begin begin
File.open(file, "rb") { |f| return f.read } # read all data File.open(file,"rb") { |f| return f.read } # read all data
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
return nil return nil
end end
@@ -347,22 +365,22 @@ def pbGetFileString(file)
return str return str
end end
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
class StringInput class StringInput
include Enumerable include Enumerable
attr_reader :lineno, :string
class << self class << self
def new(str) def new( str )
if block_given? if block_given?
begin begin
f = super f = super
yield f yield f
ensure ensure
f&.close f.close if f
end end
else else
super super
@@ -371,19 +389,21 @@ class StringInput
alias open new alias open new
end end
def initialize(str) def initialize( str )
@string = str @string = str
@pos = 0 @pos = 0
@closed = false @closed = false
@lineno = 0 @lineno = 0
end end
attr_reader :lineno,:string
def inspect def inspect
return "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@string[0, 30].inspect}>" return "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@string[0,30].inspect}>"
end end
def close def close
raise IOError, "closed stream" if @closed raise IOError, 'closed stream' if @closed
@pos = nil @pos = nil
@closed = true @closed = true
end end
@@ -391,7 +411,7 @@ class StringInput
def closed?; @closed; end def closed?; @closed; end
def pos def pos
raise IOError, "closed stream" if @closed raise IOError, 'closed stream' if @closed
[@pos, @string.size].min [@pos, @string.size].min
end end
@@ -401,8 +421,8 @@ class StringInput
def pos=(value); seek(value); end def pos=(value); seek(value); end
def seek(offset, whence = IO::SEEK_SET) def seek(offset, whence=IO::SEEK_SET)
raise IOError, "closed stream" if @closed raise IOError, 'closed stream' if @closed
case whence case whence
when IO::SEEK_SET then @pos = offset when IO::SEEK_SET then @pos = offset
when IO::SEEK_CUR then @pos += offset when IO::SEEK_CUR then @pos += offset
@@ -416,12 +436,12 @@ class StringInput
end end
def eof? def eof?
raise IOError, "closed stream" if @closed raise IOError, 'closed stream' if @closed
@pos > @string.size @pos > @string.size
end end
def each(&block) def each( &block )
raise IOError, "closed stream" if @closed raise IOError, 'closed stream' if @closed
begin begin
@string.each(&block) @string.each(&block)
ensure ensure
@@ -430,15 +450,14 @@ class StringInput
end end
def gets def gets
raise IOError, "closed stream" if @closed raise IOError, 'closed stream' if @closed
idx = @string.index("\n", @pos) if idx = @string.index(?\n, @pos)
if idx
idx += 1 # "\n".size idx += 1 # "\n".size
line = @string[@pos...idx] line = @string[ @pos ... idx ]
@pos = idx @pos = idx
@pos += 1 if @pos == @string.size @pos += 1 if @pos == @string.size
else else
line = @string[@pos..-1] line = @string[ @pos .. -1 ]
@pos = @string.size + 1 @pos = @string.size + 1
end end
@lineno += 1 @lineno += 1
@@ -446,18 +465,18 @@ class StringInput
end end
def getc def getc
raise IOError, "closed stream" if @closed raise IOError, 'closed stream' if @closed
ch = @string[@pos] ch = @string[@pos]
@pos += 1 @pos += 1
@pos += 1 if @pos == @string.size @pos += 1 if @pos == @string.size
ch ch
end end
def read(len = nil) def read( len = nil )
raise IOError, "closed stream" if @closed raise IOError, 'closed stream' if @closed
if !len if !len
return nil if eof? return nil if eof?
rest = @string[@pos...@string.size] rest = @string[@pos ... @string.size]
@pos = @string.size + 1 @pos = @string.size + 1
return rest return rest
end end
@@ -466,6 +485,8 @@ class StringInput
@pos += 1 if @pos == @string.size @pos += 1 if @pos == @string.size
str str
end end
alias read_all read
def read_all; read(); end
alias sysread read alias sysread read
end end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module FileInputMixin module FileInputMixin
def fgetb def fgetb
ret = 0 ret = 0
@@ -70,7 +67,7 @@ module FileInputMixin
self.pos = 0 self.pos = 0
offset = fgetdw >> 3 offset = fgetdw >> 3
return 0 if index >= offset return 0 if index >= offset
self.pos = (index * 8) + 4 self.pos = index * 8 + 4
return fgetdw return fgetdw
end end
@@ -88,9 +85,6 @@ module FileInputMixin
end end
end end
#===============================================================================
#
#===============================================================================
module FileOutputMixin module FileOutputMixin
def fputb(b) def fputb(b)
b &= 0xFF b &= 0xFF
@@ -114,32 +108,27 @@ module FileOutputMixin
end end
end end
#===============================================================================
#
#===============================================================================
class File < IO class File < IO
# unless defined?(debugopen) =begin
# class << self unless defined?(debugopen)
# alias debugopen open class << self
# end alias debugopen open
# end 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
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 FileInputMixin
include FileOutputMixin include FileOutputMixin
end end
#===============================================================================
#
#===============================================================================
class StringInput class StringInput
include FileInputMixin include FileInputMixin
@@ -148,7 +137,7 @@ class StringInput
end end
def each_byte def each_byte
until eof? while !eof?
yield getc yield getc
end end
end end
@@ -156,9 +145,6 @@ class StringInput
def binmode; end def binmode; end
end end
#===============================================================================
#
#===============================================================================
class StringOutput class StringOutput
include FileOutputMixin include FileOutputMixin
end end

View File

@@ -1,19 +1,21 @@
#=============================================================================== #############################
#
# HTTP utility functions # HTTP utility functions
#=============================================================================== #
def pbPostData(url, postdata, filename = nil, depth = 0) #############################
def pbPostData(url, postdata, filename=nil, depth=0)
if url[/^http:\/\/([^\/]+)(.*)$/] if url[/^http:\/\/([^\/]+)(.*)$/]
host = $1 host = $1
# path = $2 path = $2
# path = "/" if path.length == 0 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" 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 do |key, value| body = postdata.map { |key, value|
keyString = key.to_s keyString = key.to_s
valueString = value.to_s valueString = value.to_s
keyString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf("%%%02x", s[0]) } keyString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
valueString.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}" next "#{keyString}=#{valueString}"
end.join("&") }.join('&')
ret = HTTPLite.post_body( ret = HTTPLite.post_body(
url, url,
body, body,
@@ -29,7 +31,7 @@ def pbPostData(url, postdata, filename = nil, depth = 0)
return ret if !ret.is_a?(Hash) return ret if !ret.is_a?(Hash)
return "" if ret[:status] != 200 return "" if ret[:status] != 200
return ret[:body] if !filename return ret[:body] if !filename
File.open(filename, "wb") { |f| f.write(ret[:body]) } File.open(filename, "wb"){|f|f.write(ret[:body])}
return "" return ""
end end
return "" return ""
@@ -61,7 +63,7 @@ end
def pbDownloadToFile(url, file) def pbDownloadToFile(url, file)
begin begin
pbDownloadData(url, file) pbDownloadData(url,file)
rescue rescue
end end
end end
@@ -77,7 +79,7 @@ end
def pbPostToFile(url, postdata, file) def pbPostToFile(url, postdata, file)
begin begin
pbPostData(url, postdata, file) pbPostData(url, postdata,file)
rescue rescue
end end
end end

View File

@@ -2,7 +2,7 @@
# class Object # class Object
#=============================================================================== #===============================================================================
class Object class Object
alias full_inspect inspect unless method_defined?(:full_inspect) alias full_inspect inspect
def inspect def inspect
return "#<#{self.class}>" return "#<#{self.class}>"
@@ -23,21 +23,32 @@ end
#=============================================================================== #===============================================================================
class String class String
def starts_with_vowel? def starts_with_vowel?
return ["a", "e", "i", "o", "u"].include?(self[0].downcase) return ['a', 'e', 'i', 'o', 'u'].include?(self[0, 1].downcase)
end end
def first(n = 1); return self[0...n]; end def first(n = 1)
return self[0...n]
end
def last(n = 1); return self[-n..-1] || self; end def last(n = 1)
return self[-n..-1] || self
end
def blank?; return self.strip.empty?; end def blank?
blank = true
s = self.scan(/./)
for l in s
blank = false if l != ""
end
return blank
end
def cut(bitmap, width) def cut(bitmap, width)
string = self string = self
width -= bitmap.text_size("...").width width -= bitmap.text_size("...").width
string_width = 0 string_width = 0
text = [] text = []
string.scan(/./).each do |char| for char in string.scan(/./)
wdh = bitmap.text_size(char).width wdh = bitmap.text_size(char).width
next if (wdh + string_width) > width next if (wdh + string_width) > width
string_width += wdh string_width += wdh
@@ -45,30 +56,19 @@ class String
end end
text.push("...") if text.length < string.length text.push("...") if text.length < string.length
new_string = "" new_string = ""
text.each do |char| for char in text
new_string += char new_string += char
end end
return new_string return new_string
end end
def numeric?
return !self[/\A[+-]?\d+(?:\.\d+)?\Z/].nil?
end
end end
#=============================================================================== #===============================================================================
# class Numeric # class Numeric
#=============================================================================== #===============================================================================
class Numeric class Numeric
# Turns a number into a string formatted like 12,345,678. Some languages use # Turns a number into a string formatted like 12,345,678.
# different characters as the thousands separator.
def to_s_formatted def to_s_formatted
case System.user_language[0..1]
when "fr", "es"
return self.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1 ').reverse
when "it", "de"
return self.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1.').reverse
end
return self.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse return self.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse
end end
@@ -79,47 +79,17 @@ class Numeric
_INTL("twelve"), _INTL("thirteen"), _INTL("fourteen"), _INTL("fifteen"), _INTL("twelve"), _INTL("thirteen"), _INTL("fourteen"), _INTL("fifteen"),
_INTL("sixteen"), _INTL("seventeen"), _INTL("eighteen"), _INTL("nineteen"), _INTL("sixteen"), _INTL("seventeen"), _INTL("eighteen"), _INTL("nineteen"),
_INTL("twenty")] _INTL("twenty")]
return ret[self] if self.is_a?(Integer) && self >= 0 && self <= ret.length - 1 return ret[self] if self.is_a?(Integer) && self >= 0 && self <= ret.length
return self.to_s return self.to_s
end end
def to_ordinal
ret = [_INTL("zeroth"), _INTL("first"), _INTL("second"), _INTL("third"),
_INTL("fourth"), _INTL("fifth"), _INTL("sixth"), _INTL("seventh"),
_INTL("eighth"), _INTL("ninth"), _INTL("tenth"), _INTL("eleventh"),
_INTL("twelfth"), _INTL("thirteenth"), _INTL("fourteenth"), _INTL("fifteenth"),
_INTL("sixteenth"), _INTL("seventeenth"), _INTL("eighteenth"), _INTL("nineteenth"),
_INTL("twentieth")]
return ret[self] if self.is_a?(Integer) && self >= 0 && self <= ret.length - 1
return self.to_ord
end
# Returns "1st", "2nd", "3rd", etc.
def to_ord
return self.to_s if !self.is_a?(Integer)
ret = self.to_s
if ((self % 100) / 10) == 1 # 10-19
ret += "th"
elsif (self % 10) == 1
ret += "st"
elsif (self % 10) == 2
ret += "nd"
elsif (self % 10) == 3
ret += "rd"
else
ret += "th"
end
return ret
end
end end
#=============================================================================== #===============================================================================
# class Array # class Array
#=============================================================================== #===============================================================================
class Array class Array
# xor of two arrays def ^(other) # xor of two arrays
def ^(other) return (self|other) - (self&other)
return (self | other) - (self & other)
end end
def swap(val1, val2) def swap(val1, val2)
@@ -130,29 +100,6 @@ class Array
end end
end end
#===============================================================================
# class Hash
#===============================================================================
class Hash
def deep_merge(hash)
merged_hash = self.clone
merged_hash.deep_merge!(hash) if hash.is_a?(Hash)
return merged_hash
end
def deep_merge!(hash)
# failsafe
return unless hash.is_a?(Hash)
hash.each do |key, val|
if self[key].is_a?(Hash)
self[key].deep_merge!(val)
else
self[key] = val
end
end
end
end
#=============================================================================== #===============================================================================
# module Enumerable # module Enumerable
#=============================================================================== #===============================================================================
@@ -164,238 +111,6 @@ module Enumerable
end end
end end
#===============================================================================
# Collision testing
#===============================================================================
class Rect < Object
def contains?(cx, cy)
return cx >= self.x && cx < self.x + self.width &&
cy >= self.y && cy < self.y + self.height
end
end
#===============================================================================
# class File
#===============================================================================
class File
# Copies the source file to the destination path.
def self.copy(source, destination)
data = ""
t = System.uptime
File.open(source, "rb") do |f|
loop do
r = f.read(4096)
break if !r
if System.uptime - t >= 5
t += 5
Graphics.update
end
data += r
end
end
File.delete(destination) if File.file?(destination)
f = File.new(destination, "wb")
f.write data
f.close
end
# Copies the source to the destination and deletes the source.
def self.move(source, destination)
File.copy(source, destination)
File.delete(source)
end
end
#===============================================================================
# class Color
#===============================================================================
class Color
# alias for old constructor
alias init_original initialize unless self.private_method_defined?(:init_original)
# New constructor, accepts RGB values as well as a hex number or string value.
def initialize(*args)
pbPrintException("Wrong number of arguments! At least 1 is needed!") if args.length < 1
case args.length
when 1
case args.first
when Integer
hex = args.first.to_s(16)
when String
try_rgb_format = args.first.split(",")
init_original(*try_rgb_format.map(&:to_i)) if try_rgb_format.length.between?(3, 4)
hex = args.first.delete("#")
end
pbPrintException("Wrong type of argument given!") if !hex
r = hex[0...2].to_i(16)
g = hex[2...4].to_i(16)
b = hex[4...6].to_i(16)
when 3
r, g, b = *args
end
init_original(r, g, b) if r && g && b
init_original(*args)
end
def self.new_from_rgb(param)
return Font.default_color if !param
base_int = param.to_i(16)
case param.length
when 8 # 32-bit hex
return Color.new(
(base_int >> 24) & 0xFF,
(base_int >> 16) & 0xFF,
(base_int >> 8) & 0xFF,
(base_int) & 0xFF
)
when 6 # 24-bit hex
return Color.new(
(base_int >> 16) & 0xFF,
(base_int >> 8) & 0xFF,
(base_int) & 0xFF
)
when 4 # 15-bit hex
return Color.new(
((base_int) & 0x1F) << 3,
((base_int >> 5) & 0x1F) << 3,
((base_int >> 10) & 0x1F) << 3
)
when 1, 2 # Color number
case base_int
when 0 then return Color.white
when 1 then return Color.blue
when 2 then return Color.red
when 3 then return Color.green
when 4 then return Color.cyan
when 5 then return Color.pink
when 6 then return Color.yellow
when 7 then return Color.gray
else return Font.default_color
end
end
return Font.default_color
end
# @return [String] the 15-bit representation of this color in a string, ignoring its alpha
def to_rgb15
ret = (self.red.to_i >> 3)
ret |= ((self.green.to_i >> 3) << 5)
ret |= ((self.blue.to_i >> 3) << 10)
return sprintf("%04X", ret)
end
# @return [String] this color in the format "RRGGBB", ignoring its alpha
def to_rgb24
return sprintf("%02X%02X%02X", self.red.to_i, self.green.to_i, self.blue.to_i)
end
# @return [String] this color in the format "RRGGBBAA" (or "RRGGBB" if this color's alpha is 255)
def to_rgb32(always_include_alpha = false)
if self.alpha.to_i == 255 && !always_include_alpha
return sprintf("%02X%02X%02X", self.red.to_i, self.green.to_i, self.blue.to_i)
end
return sprintf("%02X%02X%02X%02X", self.red.to_i, self.green.to_i, self.blue.to_i, self.alpha.to_i)
end
# @return [String] this color in the format "#RRGGBB", ignoring its alpha
def to_hex
return "#" + to_rgb24
end
# @return [Integer] this color in RGB format converted to an integer
def to_i
return self.to_rgb24.to_i(16)
end
# @return [Color] the contrasting color to this one
def get_contrast_color
r = self.red
g = self.green
b = self.blue
yuv = [
(r * 0.299) + (g * 0.587) + (b * 0.114),
(r * -0.1687) + (g * -0.3313) + (b * 0.500) + 0.5,
(r * 0.500) + (g * -0.4187) + (b * -0.0813) + 0.5
]
if yuv[0] < 127.5
yuv[0] += (255 - yuv[0]) / 2
else
yuv[0] = yuv[0] / 2
end
return Color.new(
yuv[0] + (1.4075 * (yuv[2] - 0.5)),
yuv[0] - (0.3455 * (yuv[1] - 0.5)) - (0.7169 * (yuv[2] - 0.5)),
yuv[0] + (1.7790 * (yuv[1] - 0.5)),
self.alpha
)
end
# Converts the provided hex string/24-bit integer to RGB values.
def self.hex_to_rgb(hex)
hex = hex.delete("#") if hex.is_a?(String)
hex = hex.to_s(16) if hex.is_a?(Numeric)
r = hex[0...2].to_i(16)
g = hex[2...4].to_i(16)
b = hex[4...6].to_i(16)
return r, g, b
end
# Parses the input as a Color and returns a Color object made from it.
def self.parse(color)
case color
when Color
return color
when String, Numeric
return Color.new(color)
end
# returns nothing if wrong input
return nil
end
# Returns color object for some commonly used colors.
def self.red; return Color.new(255, 128, 128); end
def self.green; return Color.new(128, 255, 128); end
def self.blue; return Color.new(128, 128, 255); end
def self.yellow; return Color.new(255, 255, 128); end
def self.magenta; return Color.new(255, 0, 255); end
def self.cyan; return Color.new(128, 255, 255); end
def self.white; return Color.new(255, 255, 255); end
def self.gray; return Color.new(192, 192, 192); end
def self.black; return Color.new( 0, 0, 0); end
def self.pink; return Color.new(255, 128, 255); end
def self.orange; return Color.new(255, 155, 0); end
def self.purple; return Color.new(155, 0, 255); end
def self.brown; return Color.new(112, 72, 32); end
end
#===============================================================================
# Wrap code blocks in a class which passes data accessible as instance variables
# within the code block.
#
# wrapper = CallbackWrapper.new { puts @test }
# wrapper.set(test: "Hi")
# wrapper.execute #=> "Hi"
#===============================================================================
class CallbackWrapper
@params = {}
def initialize(&block)
@code_block = block
end
def execute(given_block = nil, *args)
execute_block = given_block || @code_block
@params.each do |key, value|
args.instance_variable_set("@#{key}", value)
end
args.instance_eval(&execute_block)
end
def set(params = {})
@params = params
end
end
#=============================================================================== #===============================================================================
# Kernel methods # Kernel methods
#=============================================================================== #===============================================================================
@@ -417,26 +132,11 @@ class << Kernel
return oldRand(a) return oldRand(a)
end end
elsif a.nil? elsif a.nil?
return oldRand(b) return (b) ? oldRand(b) : oldRand(2)
end end
return oldRand
end end
end end
def nil_or_empty?(string) def nil_or_empty?(string)
return string.nil? || !string.is_a?(String) || string.size == 0 return string.nil? || !string.is_a?(String) || string.size == 0
end end
#===============================================================================
# Linear interpolation between two values, given the duration of the change and
# either:
# - the time passed since the start of the change (delta), or
# - the start time of the change (delta) and the current time (now)
#===============================================================================
def lerp(start_val, end_val, duration, delta, now = nil)
return end_val if duration <= 0
delta = now - delta if now
return start_val if delta <= 0
return end_val if delta >= duration
return start_val + ((end_val - start_val) * delta / duration.to_f)
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module Input module Input
USE = C USE = C
BACK = B BACK = B
@@ -19,17 +16,18 @@ module Input
def self.update def self.update
update_KGC_ScreenCapture update_KGC_ScreenCapture
pbScreenCapture if trigger?(Input::F8) if trigger?(Input::F8)
pbScreenCapture
end
end end
end end
#===============================================================================
#
#===============================================================================
module Mouse module Mouse
module_function
# Returns the position of the mouse relative to the game window. # Returns the position of the mouse relative to the game window.
def self.getMousePos(catch_anywhere = false) def getMousePos(catch_anywhere = false)
return nil unless Input.mouse_in_window || catch_anywhere return nil unless System.mouse_in_window || catch_anywhere
return Input.mouse_x, Input.mouse_y return Input.mouse_x, Input.mouse_y
end end
end end

View File

@@ -1,8 +1,8 @@
#==============================================================================# #==============================================================================#
# Plugin Manager # # Plugin Manager #
# by Marin # # by Marin #
# Support for external plugin scripts by Luka S.J. # # support for external plugin scripts by Luka S.J. #
# Tweaked by Maruno # # tweaked by Maruno #
#------------------------------------------------------------------------------# #------------------------------------------------------------------------------#
# Provides a simple interface that allows plugins to require dependencies # # Provides a simple interface that allows plugins to require dependencies #
# at specific versions, and to specify incompatibilities between plugins. # # at specific versions, and to specify incompatibilities between plugins. #
@@ -12,118 +12,181 @@
#------------------------------------------------------------------------------# #------------------------------------------------------------------------------#
# Usage: # # Usage: #
# # # #
# Each plugin should have its own folder in the "Plugins" folder found in the # # A Pokémon Essentials plugin should register itself using the PluginManager. #
# main directory. The "Plugins" folder is similar in concept to the "PBS" # # The simplest way to do so, for a plugin without dependencies, is as follows: #
# folder, in that its contents are compiled and recorded as existing. The #
# plugin's script file(s) are placed in its folder - they must be .rb files. #
# # # #
# A plugin's folder must also contain a "meta.txt" file. This file is what # # PluginManager.register({ #
# makes Essentials recognise that the plugin exists, and contains important # # :name => "Basic Plugin", #
# information about the plugin; if this file does not exist, the folder's # # :version => "1.0", #
# contents are ignored. Each line in this file is a property. # # :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => "Marin" #
# }) #
# # # #
# Required lines: # # 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. #
# # # #
# Name = Simple Extension The plugin's name # # A plugin's version should be in the format X.Y.Z, but the number of digits #
# Version = 1.0 The plugin's version # # you use does not matter. You can also use Xa, Xb, Xc, Ya, etc. #
# Essentials = 19.1,20 Compatible version(s) of Essentials # # What matters is that you use it consistently, so that it can be compared. #
# Link = https://reliccastle.com/link-to-the-plugin/ #
# Credits = Luka S.J.,Maruno,Marin One or more names #
# # # #
# A plugin's version should be in the format X or X.Y or X.Y.Z, where X/Y/Z # # IF there are multiple people to credit, their names should be in an array. #
# are numbers. You can also use Xa, Xb, Xc, Ya, etc. What matters is that you # # If there is only one credit, it does not need an array: #
# use version numbers consistently for your plugin. A later version will be #
# alphanumerically higher than an older version. #
# # # #
# Plugins can interact with each other in several ways, such as requiring # # :credits => "Marin" #
# another one to exist or by clashing with each other. These interactions are # # :credits => ["Marin", "Maruno"], #
# known as dependencies and conflicts. The lines below are all optional, and #
# go in "meta.txt" to define how your plugin works (or doesn't work) with #
# others. You can have multiples of each of these lines. #
# # # #
# Requires = Basic Plugin Must have this plugin (any version) #
# Requires = Useful Utils,1.1 Must have this plugin/min. version #
# Exact = Scene Tweaks,2 Must have this plugin/version #
# Optional = Extended Windows,1.2 If this plugin exists, load it first #
# Conflicts = Complex Extension Incompatible plugin #
# # # #
# A plugin that depends on another one ("Requires"/"Exact"/"Optional") will #
# make that other plugin be loaded first. The "Optional" line is for a plugin #
# which isn't necessary, but if it does exist in the same project, it must be #
# at the given version or higher. #
# # # #
# When plugins are compiled, their scripts are stored in the file # # Dependency: #
# "PluginScripts.rxdata" in the "Data" folder. Dependencies defined above will #
# ensure that they are loaded in a suitable order. Scripts within a plugin are #
# loaded alphanumerically, going through subfolders depth-first. #
# # # #
# The "Plugins" folder should be deleted when the game is released. Scripts in # # A plugin can require another plugin to be installed in order to work. For #
# there are compiled, but any other files used by a plugin (graphics/audio) # # example, the "Simple Extension" plugin depends on the above "Basic Plugin" #
# should go into other folders and not the plugin's folder. # # like so: #
# #
# PluginManager.register({ #
# :name => "Simple Extension", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => ["Marin", "Maruno"], #
# :dependencies => ["Basic Plugin"] #
# }) #
# #
# If there are multiple dependencies, they should be listed in an array. If #
# there is only one dependency, it does not need an array: #
# #
# :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: #
# #
# :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: #
# #
# :dependencies => [ #
# [:exact, "Basic Plugin", "1.2"] #
# ] #
# #
# If your plugin can work without another plugin, but it is 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 check whether 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. #
# #
# :dependencies => [ #
# [:optional, "QoL Improvements", "1.1"] #
# ] #
# #
# The :optional_exact flag is a combination of :optional and :exact. #
# #
# #
# #
# Incompatibility: #
# #
# 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" #
# ] #
# }) #
# # # #
#------------------------------------------------------------------------------# #------------------------------------------------------------------------------#
# The code behind plugins: # # Plugin folder: #
# # # #
# When a plugin's "meta.txt" file is read, its contents are registered in the # # The Plugin folder is treated like the PBS folder, but for script files for #
# PluginManager. A simple example of registering a plugin is as follows: # # plugins. Each plugin has its own folder within the Plugin folder. Each #
# plugin must have a meta.txt file in its folder, which contains information #
# about that plugin. Folders without this meta.txt file are ignored. #
# # # #
# PluginManager.register({ # # Scripts must be in .rb files. You should not put any other files into a #
# :name => "Basic Plugin", # # plugin's folder except for script files and meta.txt. #
# :version => "1.0", #
# :essentials => "20", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => ["Marin"] #
# }) #
# # # #
# The :link value is optional, but recommended. This will be shown in the # # When the game is compiled, scripts in these folders are read and converted #
# message if the PluginManager detects that this plugin needs to be updated. # # into a usable format, and saved in the file Data/PluginScripts.rxdata. #
# Script files are loaded in order of their name and subfolder, so it is wise #
# to name script files "001_first script.rb", "002_second script.rb", etc. to #
# ensure they are loaded in the correct order. #
# # # #
# Here is the same example but also with dependencies and conflicts: # # When the game is compressed for distribution, the Plugin folder and all its #
# contents should be deleted (like the PBS folder), because its contents will #
# be unused (they will have been compiled into the PluginScripts.rxdata file). #
# # # #
# PluginManager.register({ # # The contents of meta.txt are as follows: #
# :name => "Basic Plugin", #
# :version => "1.0", #
# :essentials => "20", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => ["Marin"], #
# :dependencies => ["Basic Plugin", #
# ["Useful Utils", "1.1"], #
# [:exact, "Scene Tweaks", "2"], #
# [:optional, "Extended Windows", "1.2"], #
# ], #
# :incompatibilities => ["Simple Extension"] #
# }) #
# # # #
# The example dependencies/conflict are the same as the examples shown above # # Name = Simple Extension #
# for lines in "meta.txt". :optional_exact is a combination of :exact and # # Version = 1.0 #
# :optional, and there is no way to make use of its combined functionality via # # Requires = Basic Plugin #
# "meta.txt". # # Requires = Useful Utilities,1.1 #
# Conflicts = Complex Extension #
# Conflicts = Extended Windows #
# Link = https://reliccastle.com/link-to-the-plugin/ #
# Credits = Luka S.J.,Maruno,Marin #
# #
# These lines are related to what is described above. You can have multiple #
# "Requires" and "Conflicts" lines, each listing a single other plugin that is #
# either a dependency or a conflict respectively. #
# #
# Examples of the "Requires" line: #
# #
# Requires = Basic Plugin #
# Requires = Basic Plugin,1.1 #
# Requires = Basic Plugin,1.1,exact #
# Requires = Basic Plugin,1.1,optional #
# Exact = Basic Plugin,1.1 #
# Optional = Basic Plugin,1.1 #
# #
# The "Exact" and "Optional" lines are equivalent to the "Requires" lines #
# that contain those keywords. #
# #
# There is also a "Scripts" line, which lists one or more script files that #
# should be loaded first. You can have multiple "Scripts" lines. However, you #
# can achieve the same effect by simply naming your script files in #
# alphanumeric order to make them load in a particular order, so the "Scripts" #
# line should not be necessary. #
# # # #
#------------------------------------------------------------------------------# #------------------------------------------------------------------------------#
# Please give credit when using this. # # Please give credit when using this. #
#==============================================================================# #==============================================================================#
module PluginManager module PluginManager
# Holds all registered plugin data. # Holds all registered plugin data.
@@Plugins = {} @@Plugins = {}
#-----------------------------------------------------------------------------
# Registers a plugin and tests its dependencies and incompatibilities. # Registers a plugin and tests its dependencies and incompatibilities.
#-----------------------------------------------------------------------------
def self.register(options) def self.register(options)
name = nil name = nil
version = nil version = nil
essentials = nil
link = nil link = nil
dependencies = nil dependencies = nil
incompats = nil incompats = nil
credits = [] credits = []
order = [:name, :version, :essentials, :link, :dependencies, :incompatibilities, :credits] order = [:name, :version, :link, :dependencies, :incompatibilities, :credits]
# Ensure it first reads the plugin's name, which is used in error reporting, # Ensure it first reads the plugin's name, which is used in error reporting,
# by sorting the keys # by sorting the keys
keys = options.keys.sort do |a, b| keys = options.keys.sort do |a, b|
idx_a = order.index(a) || order.size idx_a = order.index(a)
idx_b = order.index(b) || order.size 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 next idx_a <=> idx_b
end end
keys.each do |key| for key in keys
value = options[key] value = options[key]
case key case key
when :name # Plugin name when :name # Plugin name
@@ -135,10 +198,10 @@ module PluginManager
end end
name = value name = value
when :version # Plugin version when :version # Plugin version
self.error("Plugin version must be a string.") if nil_or_empty?(value) if nil_or_empty?(value)
self.error("Plugin version must be a string.")
end
version = value version = value
when :essentials
essentials = value
when :link # Plugin website when :link # Plugin website
if nil_or_empty?(value) if nil_or_empty?(value)
self.error("Plugin link must be a non-empty string.") self.error("Plugin link must be a non-empty string.")
@@ -147,13 +210,12 @@ module PluginManager
when :dependencies # Plugin dependencies when :dependencies # Plugin dependencies
dependencies = value dependencies = value
dependencies = [dependencies] if !dependencies.is_a?(Array) || !dependencies[0].is_a?(Array) dependencies = [dependencies] if !dependencies.is_a?(Array) || !dependencies[0].is_a?(Array)
value.each do |dep| for dep in value
case dep if dep.is_a?(String) # "plugin name"
when String # "plugin name"
if !self.installed?(dep) if !self.installed?(dep)
self.error("Plugin '#{name}' requires plugin '#{dep}' to be installed above it.") self.error("Plugin '#{name}' requires plugin '#{dep}' to be installed above it.")
end end
when Array elsif dep.is_a?(Array)
case dep.size case dep.size
when 1 # ["plugin name"] when 1 # ["plugin name"]
if dep[0].is_a?(String) if dep[0].is_a?(String)
@@ -174,8 +236,7 @@ module PluginManager
if self.installed?(dep_name) # Have plugin but lower version if self.installed?(dep_name) # Have plugin but lower version
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} or higher, " + msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} or higher, " +
"but the installed version is #{self.version(dep_name)}." "but the installed version is #{self.version(dep_name)}."
dep_link = self.link(dep_name) if dep_link = self.link(dep_name)
if dep_link
msg += "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'." msg += "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end end
self.error(msg) self.error(msg)
@@ -217,8 +278,7 @@ module PluginManager
msg = "Plugin '#{name}' requires plugin '#{dep_name}', if installed, to be version #{dep_version}" msg = "Plugin '#{name}' requires plugin '#{dep_name}', if installed, to be version #{dep_version}"
msg << " or higher" if !exact msg << " or higher" if !exact
msg << ", but the installed version was #{self.version(dep_name)}." msg << ", but the installed version was #{self.version(dep_name)}."
dep_link = self.link(dep_name) if dep_link = self.link(dep_name)
if dep_link
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'." msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end end
self.error(msg) self.error(msg)
@@ -228,36 +288,37 @@ module PluginManager
msg = "Plugin '#{name}' requires plugin '#{dep_name}' to be version #{dep_version}" msg = "Plugin '#{name}' requires plugin '#{dep_name}' to be version #{dep_version}"
msg << " or later" if !exact msg << " or later" if !exact
msg << ", but the installed version was #{self.version(dep_name)}." msg << ", but the installed version was #{self.version(dep_name)}."
dep_link = self.link(dep_name) if dep_link = self.link(dep_name)
if dep_link
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'." msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end end
self.error(msg)
else # Don't have plugin else # Don't have plugin
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} " msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} "
msg << "or later " if !exact msg << "or later" if !exact
msg << "to be installed above it." msg << "to be installed above it."
end
self.error(msg) self.error(msg)
end end
end end
end end
end end
end
when :incompatibilities # Plugin incompatibilities when :incompatibilities # Plugin incompatibilities
incompats = value incompats = value
incompats = [incompats] if !incompats.is_a?(Array) incompats = [incompats] if !incompats.is_a?(Array)
incompats.each do |incompat| for incompat in incompats
if self.installed?(incompat) if self.installed?(incompat)
self.error("Plugin '#{name}' is incompatible with '#{incompat}'. They cannot both be used at the same time.") self.error("Plugin '#{name}' is incompatible with '#{incompat}'. " +
"They cannot both be used at the same time.")
end end
end end
when :credits # Plugin credits when :credits # Plugin credits
value = [value] if value.is_a?(String) value = [value] if value.is_a?(String)
if value.is_a?(Array) if value.is_a?(Array)
value.each do |entry| for entry in value
if entry.is_a?(String) if !entry.is_a?(String)
credits << entry
else
self.error("Plugin '#{name}'s credits array contains a non-string value.") self.error("Plugin '#{name}'s credits array contains a non-string value.")
else
credits << entry
end end
end end
else else
@@ -267,29 +328,30 @@ module PluginManager
self.error("Invalid plugin registry key '#{key}'.") self.error("Invalid plugin registry key '#{key}'.")
end end
end end
@@Plugins.each_value do |plugin| for plugin in @@Plugins.values
if plugin[:incompatibilities]&.include?(name) 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.") self.error("Plugin '#{plugin[:name]}' is incompatible with '#{name}'. " +
"They cannot both be used at the same time.")
end end
end end
# Add plugin to class variable # Add plugin to class variable
@@Plugins[name] = { @@Plugins[name] = {
:name => name, :name => name,
:version => version, :version => version,
:essentials => essentials,
:link => link, :link => link,
:dependencies => dependencies, :dependencies => dependencies,
:incompatibilities => incompats, :incompatibilities => incompats,
:credits => credits :credits => credits
} }
end end
#-----------------------------------------------------------------------------
# Throws a pure error message without stack trace or any other useless info. # Throws a pure error message without stack trace or any other useless info.
#-----------------------------------------------------------------------------
def self.error(msg) def self.error(msg)
Graphics.update Graphics.update
t = Thread.new do t = Thread.new do
Console.echo_error("Plugin Error:\r\n#{msg}") echoln "Plugin Error:\r\n#{msg}"
print("Plugin Error:\r\n#{msg}") p "Plugin Error: #{msg}"
Thread.exit Thread.exit
end end
while t.status while t.status
@@ -297,10 +359,11 @@ module PluginManager
end end
Kernel.exit! true Kernel.exit! true
end end
#-----------------------------------------------------------------------------
# Returns true if the specified plugin is installed. # Returns true if the specified plugin is installed.
# If the version is specified, this version is taken into account. # 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. # If mustequal is true, the version must be a match with the specified version.
#-----------------------------------------------------------------------------
def self.installed?(plugin_name, plugin_version = nil, mustequal = false) def self.installed?(plugin_name, plugin_version = nil, mustequal = false)
plugin = @@Plugins[plugin_name] plugin = @@Plugins[plugin_name]
return false if plugin.nil? return false if plugin.nil?
@@ -309,97 +372,87 @@ module PluginManager
return true if !mustequal && comparison >= 0 return true if !mustequal && comparison >= 0
return true if mustequal && comparison == 0 return true if mustequal && comparison == 0
end end
#-----------------------------------------------------------------------------
# Returns the string names of all installed plugins. # Returns the string names of all installed plugins.
#-----------------------------------------------------------------------------
def self.plugins def self.plugins
return @@Plugins.keys return @@Plugins.keys
end end
#-----------------------------------------------------------------------------
# Returns the installed version of the specified plugin. # Returns the installed version of the specified plugin.
#-----------------------------------------------------------------------------
def self.version(plugin_name) def self.version(plugin_name)
return if !installed?(plugin_name) return if !installed?(plugin_name)
return @@Plugins[plugin_name][:version] return @@Plugins[plugin_name][:version]
end end
#-----------------------------------------------------------------------------
# Returns the link of the specified plugin. # Returns the link of the specified plugin.
#-----------------------------------------------------------------------------
def self.link(plugin_name) def self.link(plugin_name)
return if !installed?(plugin_name) return if !installed?(plugin_name)
return @@Plugins[plugin_name][:link] return @@Plugins[plugin_name][:link]
end end
#-----------------------------------------------------------------------------
# Returns the credits of the specified plugin. # Returns the credits of the specified plugin.
#-----------------------------------------------------------------------------
def self.credits(plugin_name) def self.credits(plugin_name)
return if !installed?(plugin_name) return if !installed?(plugin_name)
return @@Plugins[plugin_name][:credits] return @@Plugins[plugin_name][:credits]
end end
#-----------------------------------------------------------------------------
# Compares two versions given in string form. v1 should be the plugin version # 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. # you actually have, and v2 should be the minimum/desired plugin version.
# Return values: # Return values:
# 1 if v1 is higher than v2 # 1 if v1 is higher than v2
# 0 if v1 is equal to v2 # 0 if v1 is equal to v2
# -1 if v1 is lower than v2 # -1 if v1 is lower than v2
#-----------------------------------------------------------------------------
def self.compare_versions(v1, v2) def self.compare_versions(v1, v2)
version_chunks1 = v1.split(".") d1 = v1.split("")
version_chunks1.each_with_index do |val, i| d1.insert(0, "0") if d1[0] == "." # Turn ".123" into "0.123"
next if val != "" while d1[-1] == "."; d1 = d1[0..-2]; end # Turn "123." into "123"
version_chunks1[i] = (i == 0) ? "0" : nil 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
version_chunks1.compact!
version_chunks2 = v2.split(".")
version_chunks2.each_with_index do |val, i|
next if val != ""
version_chunks2[i] = (i == 0) ? "0" : nil
end
version_chunks2.compact!
# Compare each chunk in turn
decision = :equal # Could be :higher or :lower
[version_chunks1.length, version_chunks2.length].max.times do |i|
chunk1 = version_chunks1[i]
chunk2 = version_chunks2[i]
if !chunk1
decision = :lower if decision == :equal
break
elsif !chunk2
decision = :higher if decision == :equal
break
end
# Make both chunks the same left by pre-padding with "0"
chars_count = [chunk1.length, chunk2.length].max
chunk1 = chunk1.rjust(chars_count, "0").chars
chunk2 = chunk2.rjust(chars_count, "0").chars
chunk1.length.times do |j|
c1 = chunk1[j]
c2 = chunk2[j]
next if c1 == c2
decision = (c1.to_i(16) > c2.to_i(16)) ? :higher : :lower
break
end
break if decision != :equal
end
case decision
when :equal then return 0
when :higher then return 1
when :lower then return -1
end end
return 0 return 0
end end
#-----------------------------------------------------------------------------
# Formats the error message # formats the error message
#-----------------------------------------------------------------------------
def self.pluginErrorMsg(name, script) def self.pluginErrorMsg(name, script)
e = $!
# begin message formatting # begin message formatting
message = "[Pokémon Essentials version #{Essentials::VERSION}]\r\n" message = "[Pokémon Essentials version #{Essentials::VERSION}]\r\n"
message += "#{Essentials::ERROR_TEXT}\r\n" # For third party scripts to add to message += "#{Essentials::ERROR_TEXT}\r\n" # For third party scripts to add to
message += "Error in Plugin: [#{name}]\r\n" message += "Error in Plugin [#{name}]:\r\n"
message += "Exception: #{e.class}\r\n" message += "#{$!.class} occurred.\r\n"
message += "Message: " # go through message content
message += e.message for line in $!.message.split("\r\n")
next if nil_or_empty?(line)
n = line[/\d+/]
err = line.split(":")[-1].strip
lms = line.split(":")[0].strip
err.gsub!(n, "") if n
err = err.capitalize if err.is_a?(String) && !err.empty?
linum = n ? "Line #{n}: " : ""
message += "#{linum}#{err}: #{lms}\r\n"
end
# show last 10 lines of backtrace # show last 10 lines of backtrace
message += "\r\n\r\nBacktrace:\r\n" message += "\r\nBacktrace:\r\n"
e.backtrace[0, 10].each { |i| message += "#{i}\r\n" } $!.backtrace[0, 10].each { |i| message += "#{i}\r\n" }
# output to log # output to log
errorlog = "errorlog.txt" errorlog = "errorlog.txt"
errorlog = RTP.getSaveFileName("errorlog.txt") if (Object.const_defined?(:RTP) rescue false)
File.open(errorlog, "ab") do |f| File.open(errorlog, "ab") do |f|
f.write("\r\n=================\r\n\r\n[#{Time.now}]\r\n") f.write("\r\n=================\r\n\r\n[#{Time.now}]\r\n")
f.write(message) f.write(message)
@@ -412,8 +465,8 @@ module PluginManager
# output message # output message
print("#{message}\r\nThis exception was logged in #{errorlogline}.\r\nHold Ctrl when closing this message to copy it to the clipboard.") print("#{message}\r\nThis exception was logged in #{errorlogline}.\r\nHold Ctrl when closing this message to copy it to the clipboard.")
# Give a ~500ms coyote time to start holding Control # Give a ~500ms coyote time to start holding Control
t = System.uptime t = System.delta
until System.uptime - t >= 0.5 until (System.delta - t) >= 500000
Input.update Input.update
if Input.press?(Input::CTRL) if Input.press?(Input::CTRL)
Input.clipboard = message Input.clipboard = message
@@ -421,26 +474,24 @@ module PluginManager
end end
end end
end end
#-----------------------------------------------------------------------------
# Used to read the metadata file # Used to read the metadata file
#-----------------------------------------------------------------------------
def self.readMeta(dir, file) def self.readMeta(dir, file)
filename = "#{dir}/#{file}" filename = "#{dir}/#{file}"
meta = {} meta = {}
# read file # read file
Compiler.pbCompilerEachPreppedLine(filename) do |line, line_no| Compiler.pbCompilerEachPreppedLine(filename) { |line, line_no|
# split line up into property name and values # split line up into property name and values
if !line[/^\s*(\w+)\s*=\s*(.*)$/] if !line[/^\s*(\w+)\s*=\s*(.*)$/]
raise _INTL("Bad line syntax (expected syntax like XXX=YYY).") + "\n" + FileLineData.linereport raise _INTL("Bad line syntax (expected syntax like XXX=YYY)\r\n{1}", FileLineData.linereport)
end end
property = $~[1].upcase property = $~[1].upcase
data = $~[2].split(",") data = $~[2].split(',')
data.each_with_index { |value, i| data[i] = value.strip } data.each_with_index { |value, i| data[i] = value.strip }
# begin formatting data hash # begin formatting data hash
case property case property
when "ESSENTIALS" when 'REQUIRES'
meta[:essentials] = [] if !meta[:essentials]
data.each { |ver| meta[:essentials].push(ver) }
when "REQUIRES"
meta[:dependencies] = [] if !meta[:dependencies] meta[:dependencies] = [] if !meta[:dependencies]
if data.length < 2 # No version given, just push name of plugin dependency if data.length < 2 # No version given, just push name of plugin dependency
meta[:dependencies].push(data[0]) meta[:dependencies].push(data[0])
@@ -450,33 +501,33 @@ module PluginManager
else # Push dependency type, name and version of plugin dependency else # Push dependency type, name and version of plugin dependency
meta[:dependencies].push([data[2].downcase.to_sym, data[0], data[1]]) meta[:dependencies].push([data[2].downcase.to_sym, data[0], data[1]])
end end
when "EXACT" when 'EXACT'
next if data.length < 2 # Exact dependencies must have a version given; ignore if not next if data.length < 2 # Exact dependencies must have a version given; ignore if not
meta[:dependencies] = [] if !meta[:dependencies] meta[:dependencies] = [] if !meta[:dependencies]
meta[:dependencies].push([:exact, data[0], data[1]]) meta[:dependencies].push([:exact, data[0], data[1]])
when "OPTIONAL" when 'OPTIONAL'
next if data.length < 2 # Optional dependencies must have a version given; ignore if not next if data.length < 2 # Optional dependencies must have a version given; ignore if not
meta[:dependencies] = [] if !meta[:dependencies] meta[:dependencies] = [] if !meta[:dependencies]
meta[:dependencies].push([:optional, data[0], data[1]]) meta[:dependencies].push([:optional, data[0], data[1]])
when "CONFLICTS" when 'CONFLICTS'
meta[:incompatibilities] = [] if !meta[:incompatibilities] meta[:incompatibilities] = [] if !meta[:incompatibilities]
data.each { |value| meta[:incompatibilities].push(value) if value && !value.empty? } data.each { |value| meta[:incompatibilities].push(value) if value && !value.empty? }
when "SCRIPTS" when 'SCRIPTS'
meta[:scripts] = [] if !meta[:scripts] meta[:scripts] = [] if !meta[:scripts]
data.each { |scr| meta[:scripts].push(scr) } data.each { |scr| meta[:scripts].push(scr) }
when "CREDITS" when 'CREDITS'
meta[:credits] = data meta[:credits] = data
when "LINK", "WEBSITE" when 'LINK', 'WEBSITE'
meta[:link] = data[0] meta[:link] = data[0]
else else
meta[property.downcase.to_sym] = data[0] meta[property.downcase.to_sym] = data[0]
end end
end }
# generate a list of all script files to be loaded, in the order they are to # generate a list of all script files to be loaded, in the order they are to
# be loaded (files listed in the meta file are loaded first) # be loaded (files listed in the meta file are loaded first)
meta[:scripts] = [] if !meta[:scripts] meta[:scripts] = [] if !meta[:scripts]
# get all script files from plugin Dir # get all script files from plugin Dir
Dir.all(dir).each do |fl| for fl in Dir.all(dir)
next if !fl.include?(".rb") next if !fl.include?(".rb")
meta[:scripts].push(fl.gsub("#{dir}/", "")) meta[:scripts].push(fl.gsub("#{dir}/", ""))
end end
@@ -485,24 +536,26 @@ module PluginManager
# return meta hash # return meta hash
return meta return meta
end end
#-----------------------------------------------------------------------------
# Get a list of all the plugin directories to inspect # Get a list of all the plugin directories to inspect
#-----------------------------------------------------------------------------
def self.listAll def self.listAll
return [] if !$DEBUG || FileTest.exist?("Game.rgssad") || !Dir.safe?("Plugins") return [] if !$DEBUG || safeExists?("Game.rgssad")
# get a list of all directories in the `Plugins/` folder # get a list of all directories in the `Plugins/` folder
dirs = [] dirs = []
Dir.get("Plugins").each { |d| dirs.push(d) if Dir.safe?(d) } Dir.get("Plugins").each { |d| dirs.push(d) if Dir.safe?(d) }
# return all plugins # return all plugins
return dirs return dirs
end end
#-----------------------------------------------------------------------------
# Catch any potential loop with dependencies and raise an error # Catch any potential loop with dependencies and raise an error
#-----------------------------------------------------------------------------
def self.validateDependencies(name, meta, og = nil) def self.validateDependencies(name, meta, og = nil)
# exit if no registered dependency # exit if no registered dependency
return nil if !meta[name] || !meta[name][:dependencies] return nil if !meta[name] || !meta[name][:dependencies]
og = [name] if !og og = [name] if !og
# go through all dependencies # go through all dependencies
meta[name][:dependencies].each do |dname| for dname in meta[name][:dependencies]
# clean the name to a simple string # clean the name to a simple string
dname = dname[0] if dname.is_a?(Array) && dname.length == 2 dname = dname[0] if dname.is_a?(Array) && dname.length == 2
dname = dname[1] if dname.is_a?(Array) && dname.length == 3 dname = dname[1] if dname.is_a?(Array) && dname.length == 3
@@ -514,25 +567,20 @@ module PluginManager
end end
return name return name
end end
#-----------------------------------------------------------------------------
# Sort load order based on dependencies (this ends up in reverse order) # Sort load order based on dependencies (this ends up in reverse order)
#-----------------------------------------------------------------------------
def self.sortLoadOrder(order, plugins) def self.sortLoadOrder(order, plugins)
# go through the load order # go through the load order
order.each do |o| for o in order
next if !plugins[o] || !plugins[o][:dependencies] next if !plugins[o] || !plugins[o][:dependencies]
# go through all dependencies # go through all dependencies
plugins[o][:dependencies].each do |dname| for dname in plugins[o][:dependencies]
optional = false
# clean the name to a simple string # clean the name to a simple string
if dname.is_a?(Array) dname = dname[0] if dname.is_a?(Array) && dname.length == 2
optional = [:optional, :optional_exact].include?(dname[0]) dname = dname[1] if dname.is_a?(Array) && dname.length == 3
dname = dname[dname.length - 2]
end
# catch missing dependency # catch missing dependency
if !order.include?(dname) self.error("Plugin '#{o}' requires plugin '#{dname}' to work properly.") if !order.include?(dname)
next if optional
self.error("Plugin '#{o}' requires plugin '#{dname}' to work properly.")
end
# skip if already sorted # skip if already sorted
next if order.index(dname) > order.index(o) next if order.index(dname) > order.index(o)
# catch looping dependency issue # catch looping dependency issue
@@ -542,16 +590,17 @@ module PluginManager
end end
return order return order
end end
#-----------------------------------------------------------------------------
# Get the order in which to load plugins # Get the order in which to load plugins
#-----------------------------------------------------------------------------
def self.getPluginOrder def self.getPluginOrder
plugins = {} plugins = {}
order = [] order = []
# Find all plugin folders that have a meta.txt and add them to the list of # Find all plugin folders that have a meta.txt and add them to the list of
# plugins. # plugins.
self.listAll.each do |dir| for dir in self.listAll
# skip if there is no meta file # skip if there is no meta file
next if !FileTest.exist?(dir + "/meta.txt") next if !safeExists?(dir + "/meta.txt")
ndx = order.length ndx = order.length
meta = self.readMeta(dir, "meta.txt") meta = self.readMeta(dir, "meta.txt")
meta[:dir] = dir meta[:dir] = dir
@@ -569,53 +618,44 @@ module PluginManager
# sort the load order # sort the load order
return self.sortLoadOrder(order, plugins).reverse, plugins return self.sortLoadOrder(order, plugins).reverse, plugins
end end
#-----------------------------------------------------------------------------
# Check if plugins need compiling # Check if plugins need compiling
#-----------------------------------------------------------------------------
def self.needCompiling?(order, plugins) def self.needCompiling?(order, plugins)
# fixed actions # fixed actions
return false if !$DEBUG || FileTest.exist?("Game.rgssad") return false if !$DEBUG || safeExists?("Game.rgssad")
return true if $full_compile return true if !safeExists?("Data/PluginScripts.rxdata")
return true if !FileTest.exist?("Data/PluginScripts.rxdata")
Input.update Input.update
# Force compiling if holding Shift or Ctrl return true if Input.press?(Input::CTRL)
return true if Input.press?(Input::SHIFT) || Input.press?(Input::CTRL) # analyze whether or not to push recompile
# Should compile if the number of plugins has changed
scripts = load_data("Data/PluginScripts.rxdata")
return true if scripts.length != plugins.length
# Should compile if any plugins have changed version or been replaced
found_plugins = []
plugins.each_pair { |name, meta| found_plugins.push([meta[:name], meta[:version]]) }
existing_plugins = []
scripts.each { |plugin| existing_plugins.push([plugin[1][:name], plugin[1][:version]]) }
return true if found_plugins != existing_plugins
# Should compile if any plugin files have been recently modified
mtime = File.mtime("Data/PluginScripts.rxdata") mtime = File.mtime("Data/PluginScripts.rxdata")
order.each do |o| for o in order
# go through all the registered plugin scripts # go through all the registered plugin scripts
scr = plugins[o][:scripts] scr = plugins[o][:scripts]
dir = plugins[o][:dir] dir = plugins[o][:dir]
scr.each do |sc| for sc in scr
return true if File.mtime("#{dir}/#{sc}") > mtime return true if File.mtime("#{dir}/#{sc}") > mtime
end end
return true if File.mtime("#{dir}/meta.txt") > mtime return true if File.mtime("#{dir}/meta.txt") > mtime
end end
return false return false
end end
#-----------------------------------------------------------------------------
# Check if plugins need compiling # Check if plugins need compiling
#-----------------------------------------------------------------------------
def self.compilePlugins(order, plugins) def self.compilePlugins(order, plugins)
Console.echo_li("Compiling plugin scripts...") echo 'Compiling plugin scripts...'
scripts = [] scripts = []
# go through the entire order one by one # go through the entire order one by one
order.each do |o| for o in order
# save name, metadata and scripts array # save name, metadata and scripts array
meta = plugins[o].clone meta = plugins[o].clone
meta.delete(:scripts) meta.delete(:scripts)
meta.delete(:dir) meta.delete(:dir)
dat = [o, meta, []] dat = [o, meta, []]
# iterate through each file to deflate # iterate through each file to deflate
plugins[o][:scripts].each do |file| for file in plugins[o][:scripts]
File.open("#{plugins[o][:dir]}/#{file}", "rb") do |f| File.open("#{plugins[o][:dir]}/#{file}", 'rb') do |f|
dat[2].push([file, Zlib::Deflate.deflate(f.read)]) dat[2].push([file, Zlib::Deflate.deflate(f.read)])
end end
end end
@@ -623,47 +663,41 @@ module PluginManager
scripts.push(dat) scripts.push(dat)
end end
# save to main `PluginScripts.rxdata` file # save to main `PluginScripts.rxdata` file
File.open("Data/PluginScripts.rxdata", "wb") { |f| Marshal.dump(scripts, f) } File.open("Data/PluginScripts.rxdata", 'wb') { |f| Marshal.dump(scripts, f) }
# collect garbage # collect garbage
GC.start GC.start
Console.echo_done(true) echoln ' done.'
echoln ''
end end
#-----------------------------------------------------------------------------
# Check if plugins need compiling # Check if plugins need compiling
#-----------------------------------------------------------------------------
def self.runPlugins def self.runPlugins
Console.echo_h1("Checking plugins")
# get the order of plugins to interpret # get the order of plugins to interpret
order, plugins = self.getPluginOrder order, plugins = self.getPluginOrder
# compile if necessary # compile if necessary
if self.needCompiling?(order, plugins) self.compilePlugins(order, plugins) if self.needCompiling?(order, plugins)
self.compilePlugins(order, plugins)
else
Console.echoln_li("Plugins were not compiled")
end
# load plugins # load plugins
scripts = load_data("Data/PluginScripts.rxdata") scripts = load_data("Data/PluginScripts.rxdata")
echoed_plugins = [] echoed_plugins = []
scripts.each do |plugin| for plugin in scripts
# get the required data # get the required data
name, meta, script = plugin name, meta, script = plugin
if !meta[:essentials] || !meta[:essentials].include?(Essentials::VERSION)
Console.echo_warn("Plugin '#{name}' may not be compatible with Essentials v#{Essentials::VERSION}. Trying to load anyway.")
end
# register plugin # register plugin
self.register(meta) self.register(meta)
# go through each script and interpret # go through each script and interpret
script.each do |scr| for scr in script
# turn code into plaintext # turn code into plaintext
code = Zlib::Inflate.inflate(scr[1]).force_encoding(Encoding::UTF_8) code = Zlib::Inflate.inflate(scr[1]).force_encoding(Encoding::UTF_8)
# get rid of tabs # get rid of tabs
code.gsub!("\t", " ") code.gsub!("\t", " ")
# construct filename # construct filename
sname = scr[0].gsub("\\", "/").split("/")[-1] sname = scr[0].gsub("\\","/").split("/")[-1]
fname = "[#{name}] #{sname}" fname = "[#{name}] #{sname}"
# try to run the code # try to run the code
begin begin
eval(code, TOPLEVEL_BINDING, fname) eval(code, TOPLEVEL_BINDING, fname)
Console.echoln_li("Loaded plugin: ==#{name}== (ver. #{meta[:version]})") if !echoed_plugins.include?(name) echoln "Loaded plugin: #{name}" if !echoed_plugins.include?(name)
echoed_plugins.push(name) echoed_plugins.push(name)
rescue Exception # format error message to display rescue Exception # format error message to display
self.pluginErrorMsg(name, sname) self.pluginErrorMsg(name, sname)
@@ -671,24 +705,7 @@ module PluginManager
end end
end end
end end
if scripts.length > 0 echoln '' if !echoed_plugins.empty?
Console.echoln_li_done("Successfully loaded #{scripts.length} plugin(s)")
else
Console.echoln_li_done("No plugins found")
end
end
# Get plugin dir from name based on meta entries
def self.findDirectory(name)
# go through the plugins folder
Dir.get("Plugins").each do |dir|
next if !Dir.safe?(dir)
next if !FileTest.exist?(dir + "/meta.txt")
# read meta
meta = self.readMeta(dir, "meta.txt")
return dir if meta[:name] == name
end
# return nil if no plugin dir found
return nil
end end
#-----------------------------------------------------------------------------
end end

View File

@@ -1,103 +1,534 @@
#=============================================================================== class SpriteAnimation
# Additions to class Sprite that allows class AnimationContainerSprite to attach @@_animations = []
# overworld animations to itself. @@_reference_count = {}
#===============================================================================
module RPG def initialize(sprite)
class Sprite < ::Sprite @sprite = sprite
def initialize(viewport = nil) end
super(viewport)
@_animation_duration = 0 %w[
@_animation_frame = 0 x y ox oy viewport flash src_rect opacity tone
@animations = [] ].each_with_index do |s, _i|
@loopAnimations = [] eval <<-__END__
def #{s}(*arg)
@sprite.#{s}(*arg)
end
__END__
end
def self.clear
@@_animations.clear
end end
def dispose def dispose
dispose_animation dispose_animation
dispose_loop_animation dispose_loop_animation
super 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 || !@@_animations.include?(animation)
16.times do
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 = []
16.times do
sprite = ::Sprite.new(self.viewport)
sprite.bitmap = bitmap
sprite.visible = false
@_loop_animation_sprites.push(sprite)
end
update_loop_animation
end end
def dispose_animation def dispose_animation
@animations.each { |a| a&.dispose_animation } return if @_animation_sprites == nil
@animations.clear 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 end
def dispose_loop_animation def dispose_loop_animation
@loopAnimations.each { |a| a&.dispose_loop_animation } return if @_loop_animation_sprites == nil
@loopAnimations.clear 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
sprite_y += self.src_rect.height / 2 if position == 1
sprite_y += self.src_rect.height if position == 2
end
for i in 0..15
sprite = sprites[i]
pattern = cell_data[i, 0]
if sprite == nil || pattern == nil || 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 then sprite.z = 1
when 1 then sprite.z = sprite.y+32+15
when 2 then 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 ||
(timing.condition == 1 && hit == true) ||
(timing.condition == 2 && 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 end
def x=(x) def x=(x)
@animations.each { |a| a.x = x if a } sx = x - self.x
@loopAnimations.each { |a| a.x = x if a } return if sx == 0
super 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 end
def y=(y) def y=(y)
@animations.each { |a| a.y = y if a } sy = y - self.y
@loopAnimations.each { |a| a.y = y if a } 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 super
end 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) && 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) def pushAnimation(array, anim)
array.length.times do |i| for i in 0...array.length
next if array[i]&.active? next if array[i] && array[i].active?
array[i] = anim array[i] = anim
return return
end end
array.push(anim) array.push(anim)
end end
def animation(animation, hit, height = 3, no_tone = false) def animation(animation, hit, height = 3)
anim = SpriteAnimation.new(self) anim = SpriteAnimation.new(self)
anim.animation(animation, hit, height, no_tone) anim.animation(animation,hit,height)
pushAnimation(@animations, anim) pushAnimation(@animations,anim)
end end
def loop_animation(animation) def loop_animation(animation)
anim = SpriteAnimation.new(self) anim = SpriteAnimation.new(self)
anim.loop_animation(animation) anim.loop_animation(animation)
pushAnimation(@loopAnimations, anim) 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 end
def effect? def effect?
@animations.each { |a| return true if a.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 return false
end end
def update_animation
@animations.each { |a| a.update_animation if a&.active? }
end
def update_loop_animation
@loopAnimations.each { |a| a.update_loop_animation if a&.active? }
end
def update def update
super super
@animations.each { |a| a.update } if @_whiten_duration > 0
@loopAnimations.each { |a| a.update } @_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 SpriteAnimation.clear
end end
def update_animation
for a in @animations
a.update_animation if a && a.active?
end
end end
end
#=============================================================================== def update_loop_animation
# A version of class Sprite that allows its coordinates to be floats rather than for a in @loopAnimations
# integers. a.update_loop_animation if a && a.active?
#=============================================================================== end
class FloatSprite < Sprite end
def x; return @float_x; end
def y; return @float_y; end
def x=(value) def x=(x)
@float_x = value for a in @animations
a.x = x if a
end
for a in @loopAnimations
a.x = x if a
end
super super
end end
def y=(value) def y=(y)
@float_y = value for a in @animations
a.y = y if a
end
for a in @loopAnimations
a.y = y if a
end
super super
end end
end
end end

View File

@@ -1,38 +1,11 @@
#===============================================================================
#
#===============================================================================
module Settings module Settings
#----------------------------------------------------------------------------- # Whether a move's physical/special category depends on the move itself as in
# Turn order and disobedience # newer Gens (true), or on its type as in older Gens (false).
#----------------------------------------------------------------------------- MOVE_CATEGORY_PER_MOVE = (MECHANICS_GENERATION >= 4)
# Whether turn order is recalculated after a Pokémon Mega Evolves. # Whether turn order is recalculated after a Pokémon Mega Evolves.
RECALCULATE_TURN_ORDER_AFTER_MEGA_EVOLUTION = (MECHANICS_GENERATION >= 7) RECALCULATE_TURN_ORDER_AFTER_MEGA_EVOLUTION = (MECHANICS_GENERATION >= 7)
# Whether turn order is recalculated after a Pokémon's Speed stat changes. # Whether turn order is recalculated after a Pokémon's Speed stat changes.
RECALCULATE_TURN_ORDER_AFTER_SPEED_CHANGES = (MECHANICS_GENERATION >= 8) RECALCULATE_TURN_ORDER_AFTER_SPEED_CHANGES = (MECHANICS_GENERATION >= 8)
# Whether any Pokémon (originally owned by the player or foreign) can disobey
# the player's commands if the Pokémon is too high a level compared to the
# number of Gym Badges the player has.
ANY_HIGH_LEVEL_POKEMON_CAN_DISOBEY = false
# Whether foreign Pokémon can disobey the player's commands if the Pokémon is
# too high a level compared to the number of Gym Badges the player has.
FOREIGN_HIGH_LEVEL_POKEMON_CAN_DISOBEY = true
#-----------------------------------------------------------------------------
# Mega Evolution
#-----------------------------------------------------------------------------
# The Game Switch which, while ON, prevents all Pokémon in battle from Mega
# Evolving even if they otherwise could.
NO_MEGA_EVOLUTION = 34
#-----------------------------------------------------------------------------
# Move usage calculations
#-----------------------------------------------------------------------------
# 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).
MOVE_CATEGORY_PER_MOVE = (MECHANICS_GENERATION >= 4)
# Whether critical hits do 1.5x damage and have 4 stages (true), or they do 2x # Whether critical hits do 1.5x damage and have 4 stages (true), or they do 2x
# damage and have 5 stages as in Gen 5 (false). Also determines whether # damage and have 5 stages as in Gen 5 (false). Also determines whether
# critical hit rate can be copied by Transform/Psych Up. # critical hit rate can be copied by Transform/Psych Up.
@@ -43,23 +16,11 @@ module Settings
# * Grass-type immunity to powder moves and Effect Spore # * Grass-type immunity to powder moves and Effect Spore
# * Poison-type Pokémon can't miss when using Toxic # * Poison-type Pokémon can't miss when using Toxic
MORE_TYPE_EFFECTS = (MECHANICS_GENERATION >= 6) MORE_TYPE_EFFECTS = (MECHANICS_GENERATION >= 6)
# The minimum number of Gym Badges required to boost each stat of a player's
# Pokémon by 1.1x, in battle only.
NUM_BADGES_BOOST_ATTACK = (MECHANICS_GENERATION >= 4) ? 999 : 1
NUM_BADGES_BOOST_DEFENSE = (MECHANICS_GENERATION >= 4) ? 999 : 5
NUM_BADGES_BOOST_SPATK = (MECHANICS_GENERATION >= 4) ? 999 : 7
NUM_BADGES_BOOST_SPDEF = (MECHANICS_GENERATION >= 4) ? 999 : 7
NUM_BADGES_BOOST_SPEED = (MECHANICS_GENERATION >= 4) ? 999 : 3
#-----------------------------------------------------------------------------
# Move, ability and item effects
#-----------------------------------------------------------------------------
# Whether the in-battle hail weather is replaced by Snowstorm (from Gen 9+)
# instead.
USE_SNOWSTORM_WEATHER_INSTEAD_OF_HAIL = (MECHANICS_GENERATION >= 9)
# Whether weather caused by an ability lasts 5 rounds (true) or forever (false). # Whether weather caused by an ability lasts 5 rounds (true) or forever (false).
FIXED_DURATION_WEATHER_FROM_ABILITY = (MECHANICS_GENERATION >= 6) FIXED_DURATION_WEATHER_FROM_ABILITY = (MECHANICS_GENERATION >= 6)
#=============================================================================
# Whether X items (X Attack, etc.) raise their stat by 2 stages (true) or 1 # Whether X items (X Attack, etc.) raise their stat by 2 stages (true) or 1
# (false). # (false).
X_STAT_ITEMS_RAISE_BY_TWO_STAGES = (MECHANICS_GENERATION >= 7) X_STAT_ITEMS_RAISE_BY_TWO_STAGES = (MECHANICS_GENERATION >= 7)
@@ -70,37 +31,26 @@ module Settings
# raises the holder's Special Attack and Special Defense by 50% (false). # raises the holder's Special Attack and Special Defense by 50% (false).
SOUL_DEW_POWERS_UP_TYPES = (MECHANICS_GENERATION >= 7) SOUL_DEW_POWERS_UP_TYPES = (MECHANICS_GENERATION >= 7)
#----------------------------------------------------------------------------- #=============================================================================
# Affection
#-----------------------------------------------------------------------------
# Whether Pokémon with high happiness will gain more Exp from battles, have a # The minimum number of badges required to boost each stat of a player's
# chance of avoiding/curing negative effects by themselves, resisting # Pokémon by 1.1x, in battle only.
# fainting, etc. NUM_BADGES_BOOST_ATTACK = (MECHANICS_GENERATION >= 4) ? 999 : 1
AFFECTION_EFFECTS = false NUM_BADGES_BOOST_DEFENSE = (MECHANICS_GENERATION >= 4) ? 999 : 5
# Whether a Pokémon's happiness is limited to 179, and can only be increased NUM_BADGES_BOOST_SPATK = (MECHANICS_GENERATION >= 4) ? 999 : 7
# further with friendship-raising berries. Related to AFFECTION_EFFECTS by NUM_BADGES_BOOST_SPDEF = (MECHANICS_GENERATION >= 4) ? 999 : 7
# default because affection effects only start applying above a happiness of NUM_BADGES_BOOST_SPEED = (MECHANICS_GENERATION >= 4) ? 999 : 3
# 179. Also lowers the happiness evolution threshold to 160.
APPLY_HAPPINESS_SOFT_CAP = AFFECTION_EFFECTS
#----------------------------------------------------------------------------- #=============================================================================
# Capturing Pokémon
#-----------------------------------------------------------------------------
# Whether the critical capture mechanic applies. Note that its calculation is # An array of items which act as Mega Rings for the player (NPCs don't need a
# based on a total of 600+ species (i.e. that many species need to be caught # Mega Ring item, just a Mega Stone held by their Pokémon).
# to provide the greatest critical capture chance of 2.5x), and there may be MEGA_RINGS = [:MEGARING, :MEGABRACELET, :MEGACUFF, :MEGACHARM]
# fewer species in your game. # The Game Switch which, while ON, prevents all Pokémon in battle from Mega
ENABLE_CRITICAL_CAPTURES = (MECHANICS_GENERATION >= 5) # Evolving even if they otherwise could.
# Whether the player is asked what to do with a newly caught Pokémon if their NO_MEGA_EVOLUTION = 34
# party is full. If true, the player can toggle whether they are asked this in
# the Options screen.
NEW_CAPTURE_CAN_REPLACE_PARTY_MEMBER = (MECHANICS_GENERATION >= 7)
#----------------------------------------------------------------------------- #=============================================================================
# Exp and EV gain
#-----------------------------------------------------------------------------
# Whether the Exp gained from beating a Pokémon should be scaled depending on # Whether the Exp gained from beating a Pokémon should be scaled depending on
# the gainer's level. # the gainer's level.
@@ -110,21 +60,14 @@ module Settings
# that much Exp (false). This also applies to Exp gained via the Exp Share # that much Exp (false). This also applies to Exp gained via the Exp Share
# (held item version) being distributed to all Exp Share holders. # (held item version) being distributed to all Exp Share holders.
SPLIT_EXP_BETWEEN_GAINERS = (MECHANICS_GENERATION <= 5) SPLIT_EXP_BETWEEN_GAINERS = (MECHANICS_GENERATION <= 5)
# Whether the Exp gained from beating a Pokémon is multiplied by 1.5 if that # Whether the critical capture mechanic applies. Note that its calculation is
# Pokémon is owned by another trainer. # based on a total of 600+ species (i.e. that many species need to be caught
MORE_EXP_FROM_TRAINER_POKEMON = (MECHANICS_GENERATION <= 6) # to provide the greatest critical capture chance of 2.5x), and there may be
# Whether a Pokémon holding a Power item gains 8 (true) or 4 (false) EVs in # fewer species in your game.
# the relevant stat. ENABLE_CRITICAL_CAPTURES = (MECHANICS_GENERATION >= 5)
MORE_EVS_FROM_POWER_ITEMS = (MECHANICS_GENERATION >= 7)
# Whether Pokémon gain Exp for capturing a Pokémon. # Whether Pokémon gain Exp for capturing a Pokémon.
GAIN_EXP_FOR_CAPTURE = (MECHANICS_GENERATION >= 6) GAIN_EXP_FOR_CAPTURE = (MECHANICS_GENERATION >= 6)
# The Game Switch which, whie ON, prevents the player from losing money if
#-----------------------------------------------------------------------------
# End of battle
#-----------------------------------------------------------------------------
CAN_FORFEIT_TRAINER_BATTLES = (MECHANICS_GENERATION >= 9)
# The Game Switch which, while ON, prevents the player from losing money if
# they lose a battle (they can still gain money from trainers for winning). # they lose a battle (they can still gain money from trainers for winning).
NO_MONEY_LOSS = 33 NO_MONEY_LOSS = 33
# Whether party Pokémon check whether they can evolve after all battles # Whether party Pokémon check whether they can evolve after all battles
@@ -132,13 +75,4 @@ module Settings
CHECK_EVOLUTION_AFTER_ALL_BATTLES = (MECHANICS_GENERATION >= 6) CHECK_EVOLUTION_AFTER_ALL_BATTLES = (MECHANICS_GENERATION >= 6)
# Whether fainted Pokémon can try to evolve after a battle. # Whether fainted Pokémon can try to evolve after a battle.
CHECK_EVOLUTION_FOR_FAINTED_POKEMON = true CHECK_EVOLUTION_FOR_FAINTED_POKEMON = true
#-----------------------------------------------------------------------------
# AI
#-----------------------------------------------------------------------------
# Whether wild Pokémon with the "Legendary", "Mythical" or "UltraBeast" flag
# (as defined in pokemon.txt) have a smarter AI. Their skill level is set to
# 32, which is a medium skill level.
SMARTER_WILD_LEGENDARY_POKEMON = true
end end

View File

@@ -1,30 +1,19 @@
#===============================================================================
# The SaveData module is used to manipulate save data. It contains the {Value}s # The SaveData module is used to manipulate save data. It contains the {Value}s
# that make up the save data and {Conversion}s for resolving incompatibilities # that make up the save data and {Conversion}s for resolving incompatibilities
# between Essentials and game versions. # between Essentials and game versions.
# @see SaveData.register # @see SaveData.register
# @see SaveData.register_conversion # @see SaveData.register_conversion
#===============================================================================
module SaveData module SaveData
DIRECTORY = (File.directory?(System.data_directory)) ? System.data_directory : "./" # Contains the file path of the save file.
FILENAME_REGEX = /Game(\d*)\.rxdata$/ FILE_PATH = if File.directory?(System.data_directory)
System.data_directory + '/Game.rxdata'
else
'./Game.rxdata'
end
# @return [Boolean] whether any save files exist # @return [Boolean] whether the save file exists
def self.exists? def self.exists?
return !all_save_files.empty? return File.file?(FILE_PATH)
end
# @return[Array] array of filenames in the save folder that are save files
def self.all_save_files
files = Dir.get(DIRECTORY, "*", false)
ret = []
files.each do |file|
next if !file[FILENAME_REGEX]
ret.push([$~[1].to_i, file])
end
ret.sort! { |a, b| a[0] <=> b[0] }
ret.map! { |val| val[1] }
return ret
end end
# Fetches the save data from the given file. # Fetches the save data from the given file.
@@ -54,9 +43,9 @@ module SaveData
def self.read_from_file(file_path) def self.read_from_file(file_path)
validate file_path => String validate file_path => String
save_data = get_data_from_file(file_path) save_data = get_data_from_file(file_path)
save_data = to_hash_format(save_data) if save_data.is_a?(Array) # Pre-v19 save file support save_data = to_hash_format(save_data) if save_data.is_a?(Array)
if !save_data.empty? && run_conversions(save_data) if !save_data.empty? && run_conversions(save_data)
File.open(file_path, "wb") { |file| Marshal.dump(save_data, file) } File.open(file_path, 'wb') { |file| Marshal.dump(save_data, file) }
end end
return save_data return save_data
end end
@@ -68,19 +57,14 @@ module SaveData
def self.save_to_file(file_path) def self.save_to_file(file_path)
validate file_path => String validate file_path => String
save_data = self.compile_save_hash save_data = self.compile_save_hash
File.open(file_path, "wb") { |file| Marshal.dump(save_data, file) } File.open(file_path, 'wb') { |file| Marshal.dump(save_data, file) }
end end
# Deletes the save file (and a possible .bak backup file if one exists) # Deletes the save file (and a possible .bak backup file if one exists)
# @raise [Error::ENOENT] # @raise [Error::ENOENT]
def self.delete_file(filename) def self.delete_file
File.delete(DIRECTORY + filename) File.delete(FILE_PATH)
File.delete(DIRECTORY + filename + ".bak") if File.file?(DIRECTORY + filename + ".bak") File.delete(FILE_PATH + '.bak') if File.file?(FILE_PATH + '.bak')
end
def self.filename_from_index(index = 0)
return "Game.rxdata" if index <= 0
return "Game#{index}.rxdata"
end end
# Converts the pre-v19 format data to the new format. # Converts the pre-v19 format data to the new format.
@@ -95,4 +79,19 @@ module SaveData
end end
return hash return hash
end end
# Moves a save file from the old Saved Games folder to the new
# location specified by {FILE_PATH}. Does nothing if a save file
# already exists in {FILE_PATH}.
def self.move_old_windows_save
return if File.file?(FILE_PATH)
game_title = System.game_title.gsub(/[^\w ]/, '_')
home = ENV['HOME'] || ENV['HOMEPATH']
return if home.nil?
old_location = File.join(home, 'Saved Games', game_title)
return unless File.directory?(old_location)
old_file = File.join(old_location, 'Game.rxdata')
return unless File.file?(old_file)
File.move(old_file, FILE_PATH)
end
end end

View File

@@ -1,21 +1,15 @@
#===============================================================================
#
#===============================================================================
module SaveData module SaveData
# Contains Value objects for each save element. # Contains Value objects for each save element.
# Populated during runtime by SaveData.register calls. # Populated during runtime by SaveData.register calls.
# @type [Array<Value>] # @type [Array<Value>]
@values = [] @values = []
#=============================================================================
# An error raised if an invalid save value is being saved or loaded. # An error raised if an invalid save value is being saved or loaded.
#=============================================================================
class InvalidValueError < RuntimeError; end class InvalidValueError < RuntimeError; end
#============================================================================= #=============================================================================
# Represents a single value in save data. # Represents a single value in save data.
# New values are added using {SaveData.register}. # New values are added using {SaveData.register}.
#=============================================================================
class Value class Value
# @return [Symbol] the value id # @return [Symbol] the value id
attr_reader :id attr_reader :id
@@ -26,7 +20,6 @@ module SaveData
@id = id @id = id
@loaded = false @loaded = false
@load_in_bootup = false @load_in_bootup = false
@reset_on_new_game = false
instance_eval(&block) instance_eval(&block)
raise "No save_value defined for save value #{id.inspect}" if @save_proc.nil? raise "No save_value defined for save value #{id.inspect}" if @save_proc.nil?
raise "No load_value defined for save value #{id.inspect}" if @load_proc.nil? raise "No load_value defined for save value #{id.inspect}" if @load_proc.nil?
@@ -77,24 +70,11 @@ module SaveData
return @load_in_bootup return @load_in_bootup
end end
def reset_on_new_game
@reset_on_new_game = true
end
def reset_on_new_game?
return @reset_on_new_game
end
# @return [Boolean] whether the value has been loaded # @return [Boolean] whether the value has been loaded
def loaded? def loaded?
return @loaded return @loaded
end end
# Marks value as unloaded.
def mark_as_unloaded
@loaded = false
end
# Uses the {#from_old_format} proc to select the correct data from # Uses the {#from_old_format} proc to select the correct data from
# +old_format+ and return it. # +old_format+ and return it.
# Returns nil if the proc is undefined. # Returns nil if the proc is undefined.
@@ -105,8 +85,6 @@ module SaveData
return @old_format_get_proc.call(old_format) return @old_format_get_proc.call(old_format)
end end
#---------------------------------------------------------------------------
private private
# Raises an {InvalidValueError} if the given value is invalid. # Raises an {InvalidValueError} if the given value is invalid.
@@ -131,21 +109,21 @@ module SaveData
# Requires a block with the loaded value as its parameter. # Requires a block with the loaded value as its parameter.
# @see SaveData.register # @see SaveData.register
def load_value(&block) def load_value(&block)
raise ArgumentError, "No block given to load_value" unless block_given? raise ArgumentError, 'No block given to load_value' unless block_given?
@load_proc = block @load_proc = block
end end
# Defines what is saved into save data. Requires a block. # Defines what is saved into save data. Requires a block.
# @see SaveData.register # @see SaveData.register
def save_value(&block) def save_value(&block)
raise ArgumentError, "No block given to save_value" unless block_given? raise ArgumentError, 'No block given to save_value' unless block_given?
@save_proc = block @save_proc = block
end end
# If present, defines what the value is set to at the start of a new game. # If present, defines what the value is set to at the start of a new game.
# @see SaveData.register # @see SaveData.register
def new_game_value(&block) def new_game_value(&block)
raise ArgumentError, "No block given to new_game_value" unless block_given? raise ArgumentError, 'No block given to new_game_value' unless block_given?
@new_game_value_proc = block @new_game_value_proc = block
end end
@@ -159,15 +137,14 @@ module SaveData
# save format. Requires a block with the old format array as its parameter. # save format. Requires a block with the old format array as its parameter.
# @see SaveData.register # @see SaveData.register
def from_old_format(&block) def from_old_format(&block)
raise ArgumentError, "No block given to from_old_format" unless block_given? raise ArgumentError, 'No block given to from_old_format' unless block_given?
@old_format_get_proc = block @old_format_get_proc = block
end end
# @!endgroup # @!endgroup
end end
#--------------------------------------------------------------------------- #=============================================================================
# Registers a {Value} to be saved into save data. # Registers a {Value} to be saved into save data.
# Takes a block which defines the value's saving ({Value#save_value}) # Takes a block which defines the value's saving ({Value#save_value})
# and loading ({Value#load_value}) procedures. # and loading ({Value#load_value}) procedures.
@@ -189,6 +166,7 @@ module SaveData
# save_value { $foo } # save_value { $foo }
# load_value { |value| $foo = value } # load_value { |value| $foo = value }
# new_game_value { Foo.new } # new_game_value { Foo.new }
# from_old_format { |old_format| old_format[16] if old_format[16].is_a?(Foo) }
# end # end
# @example Registering a value to be loaded on bootup # @example Registering a value to be loaded on bootup
# SaveData.register(:bar) do # SaveData.register(:bar) do
@@ -198,20 +176,15 @@ module SaveData
# new_game_value { Bar.new } # new_game_value { Bar.new }
# end # end
# @param id [Symbol] value id # @param id [Symbol] value id
# @yield the block of code to be saved as a Value # @yieldself [Value]
def self.register(id, &block) def self.register(id, &block)
validate id => Symbol validate id => Symbol
unless block_given? unless block_given?
raise ArgumentError, "No block given to SaveData.register" raise ArgumentError, 'No block given to SaveData.register'
end end
@values << Value.new(id, &block) @values << Value.new(id, &block)
end end
def self.unregister(id)
validate id => Symbol
@values.delete_if { |value| value.id == id }
end
# @param save_data [Hash] save data to validate # @param save_data [Hash] save data to validate
# @return [Boolean] whether the given save data is valid # @return [Boolean] whether the given save data is valid
def self.valid?(save_data) def self.valid?(save_data)
@@ -248,20 +221,13 @@ module SaveData
load_values(save_data) { |value| !value.loaded? } load_values(save_data) { |value| !value.loaded? }
end end
# Marks all values that aren't loaded on bootup as unloaded.
def self.mark_values_as_unloaded
@values.each do |value|
value.mark_as_unloaded if !value.load_in_bootup? || value.reset_on_new_game?
end
end
# Loads each value from the given save data that has # Loads each value from the given save data that has
# been set to be loaded during bootup. Done when a save file exists. # been set to be loaded during bootup. Done when a save file exists.
# @param save_data [Hash] save data to load # @param save_data [Hash] save data to load
# @raise [InvalidValueError] if an invalid value is being loaded # @raise [InvalidValueError] if an invalid value is being loaded
def self.load_bootup_values(save_data, reload = false) def self.load_bootup_values(save_data)
validate save_data => Hash validate save_data => Hash
load_values(save_data) { |value| (reload || !value.loaded?) && value.load_in_bootup? } load_values(save_data) { |value| !value.loaded? && value.load_in_bootup? }
end end
# Goes through each value with {Value#load_in_bootup} enabled and loads their # Goes through each value with {Value#load_in_bootup} enabled and loads their
@@ -277,7 +243,7 @@ module SaveData
# new game. # new game.
def self.load_new_game_values def self.load_new_game_values
@values.each do |value| @values.each do |value|
value.load_new_game_value if value.has_new_game_proc? && (!value.loaded? || value.reset_on_new_game?) value.load_new_game_value if value.has_new_game_proc? && !value.loaded?
end end
end end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module SaveData module SaveData
# Contains Conversion objects for each defined conversion: # Contains Conversion objects for each defined conversion:
# { # {
@@ -24,7 +21,6 @@ module SaveData
#============================================================================= #=============================================================================
# Represents a conversion made to save data. # Represents a conversion made to save data.
# New conversions are added using {SaveData.register_conversion}. # New conversions are added using {SaveData.register_conversion}.
#=============================================================================
class Conversion class Conversion
# @return [Symbol] conversion ID # @return [Symbol] conversion ID
attr_reader :id attr_reader :id
@@ -75,8 +71,6 @@ module SaveData
@value_procs[key].call(object) if @value_procs[key].is_a?(Proc) @value_procs[key].call(object) if @value_procs[key].is_a?(Proc)
end end
#---------------------------------------------------------------------------
private private
# @!group Configuration # @!group Configuration
@@ -118,7 +112,7 @@ module SaveData
# @see SaveData.register_conversion # @see SaveData.register_conversion
def to_value(value_id, &block) def to_value(value_id, &block)
validate value_id => Symbol validate value_id => Symbol
raise ArgumentError, "No block given to to_value" unless block_given? raise ArgumentError, 'No block given to to_value' unless block_given?
if @value_procs[value_id].is_a?(Proc) if @value_procs[value_id].is_a?(Proc)
raise "Multiple to_value definitions in conversion #{@id} for #{value_id}" raise "Multiple to_value definitions in conversion #{@id} for #{value_id}"
end end
@@ -128,7 +122,7 @@ module SaveData
# Defines a conversion to the entire save data. # Defines a conversion to the entire save data.
# @see SaveData.register_conversion # @see SaveData.register_conversion
def to_all(&block) def to_all(&block)
raise ArgumentError, "No block given to to_all" unless block_given? raise ArgumentError, 'No block given to to_all' unless block_given?
if @all_proc.is_a?(Proc) if @all_proc.is_a?(Proc)
raise "Multiple to_all definitions in conversion #{@id}" raise "Multiple to_all definitions in conversion #{@id}"
end end
@@ -138,8 +132,7 @@ module SaveData
# @!endgroup # @!endgroup
end end
#--------------------------------------------------------------------------- #=============================================================================
# Registers a {Conversion} to occur for save data that meets the given criteria. # Registers a {Conversion} to occur for save data that meets the given criteria.
# Two types of criteria can be defined: {Conversion#essentials_version} and # Two types of criteria can be defined: {Conversion#essentials_version} and
# {Conversion#game_version}. The conversion is automatically run on save data # {Conversion#game_version}. The conversion is automatically run on save data
@@ -159,11 +152,11 @@ module SaveData
# save_data[:new_value] = Foo.new # save_data[:new_value] = Foo.new
# end # end
# end # end
# @yield the block of code to be saved as a Conversion # @yield self [Conversion]
def self.register_conversion(id, &block) def self.register_conversion(id, &block)
validate id => Symbol validate id => Symbol
unless block_given? unless block_given?
raise ArgumentError, "No block given to SaveData.register_conversion" raise ArgumentError, 'No block given to SaveData.register_conversion'
end end
conversion = Conversion.new(id, &block) conversion = Conversion.new(id, &block)
@conversions[conversion.trigger_type][conversion.version] ||= [] @conversions[conversion.trigger_type][conversion.version] ||= []
@@ -175,8 +168,8 @@ module SaveData
def self.get_conversions(save_data) def self.get_conversions(save_data)
conversions_to_run = [] conversions_to_run = []
versions = { versions = {
essentials: save_data[:essentials_version] || "18.1", essentials: save_data[:essentials_version] || '18.1',
game: save_data[:game_version] || "0.0.0" game: save_data[:game_version] || '0.0.0'
} }
[:essentials, :game].each do |trigger_type| [:essentials, :game].each do |trigger_type|
# Ensure the versions are sorted from lowest to highest # Ensure the versions are sorted from lowest to highest
@@ -201,15 +194,14 @@ module SaveData
validate save_data => Hash validate save_data => Hash
conversions_to_run = self.get_conversions(save_data) conversions_to_run = self.get_conversions(save_data)
return false if conversions_to_run.none? return false if conversions_to_run.none?
filepath = SaveData::DIRECTORY + SaveData.filename_from_index(save_data[:stats].save_filename_number || 0) File.open(SaveData::FILE_PATH + '.bak', 'wb') { |f| Marshal.dump(save_data, f) }
File.open(filepath + ".bak", "wb") { |f| Marshal.dump(save_data, f) } echoln "Running #{conversions_to_run.length} conversions..."
Console.echo_h1(_INTL("Converting save file"))
conversions_to_run.each do |conversion| conversions_to_run.each do |conversion|
Console.echo_li("#{conversion.title}...") echo "#{conversion.title}..."
conversion.run(save_data) conversion.run(save_data)
Console.echo_done(true) echoln ' done.'
end end
Console.echoln_li_done(_INTL("Successfully applied {1} save file conversion(s)", conversions_to_run.length)) echoln '' if conversions_to_run.length > 0
save_data[:essentials_version] = Essentials::VERSION save_data[:essentials_version] = Essentials::VERSION
save_data[:game_version] = Settings::GAME_VERSION save_data[:game_version] = Settings::GAME_VERSION
return true return true

View File

@@ -1,27 +1,41 @@
#===============================================================================
# Contains the save values defined in Essentials by default. # Contains the save values defined in Essentials by default.
#===============================================================================
SaveData.register(:player) do SaveData.register(:player) do
ensure_class :Player ensure_class :Player
save_value { $player } save_value { $Trainer }
load_value { |value| $player = value } load_value { |value| $Trainer = value }
new_game_value { Player.new("Unnamed", GameData::TrainerType.keys.first) } new_game_value {
trainer_type = nil # Get the first defined trainer type as a placeholder
GameData::TrainerType.each { |t| trainer_type = t.id; break }
Player.new("Unnamed", trainer_type)
}
from_old_format { |old_format| old_format[0] }
end
SaveData.register(:frame_count) do
ensure_class :Integer
save_value { Graphics.frame_count }
load_value { |value| Graphics.frame_count = value }
new_game_value { 0 }
from_old_format { |old_format| old_format[1] }
end end
SaveData.register(:game_system) do SaveData.register(:game_system) do
load_in_bootup
ensure_class :Game_System ensure_class :Game_System
save_value { $game_system } save_value { $game_system }
load_value { |value| $game_system = value } load_value { |value| $game_system = value }
new_game_value { Game_System.new } new_game_value { Game_System.new }
from_old_format { |old_format| old_format[2] }
end end
SaveData.register(:pokemon_system) do SaveData.register(:pokemon_system) do
load_in_bootup # Because this contains values for the Options screen load_in_bootup
ensure_class :PokemonSystem ensure_class :PokemonSystem
save_value { $PokemonSystem } save_value { $PokemonSystem }
load_value { |value| $PokemonSystem = value } load_value { |value| $PokemonSystem = value }
new_game_value { PokemonSystem.new } new_game_value { PokemonSystem.new }
from_old_format { |old_format| old_format[3] }
end end
SaveData.register(:switches) do SaveData.register(:switches) do
@@ -29,6 +43,7 @@ SaveData.register(:switches) do
save_value { $game_switches } save_value { $game_switches }
load_value { |value| $game_switches = value } load_value { |value| $game_switches = value }
new_game_value { Game_Switches.new } new_game_value { Game_Switches.new }
from_old_format { |old_format| old_format[5] }
end end
SaveData.register(:variables) do SaveData.register(:variables) do
@@ -36,6 +51,7 @@ SaveData.register(:variables) do
save_value { $game_variables } save_value { $game_variables }
load_value { |value| $game_variables = value } load_value { |value| $game_variables = value }
new_game_value { Game_Variables.new } new_game_value { Game_Variables.new }
from_old_format { |old_format| old_format[6] }
end end
SaveData.register(:self_switches) do SaveData.register(:self_switches) do
@@ -43,6 +59,7 @@ SaveData.register(:self_switches) do
save_value { $game_self_switches } save_value { $game_self_switches }
load_value { |value| $game_self_switches = value } load_value { |value| $game_self_switches = value }
new_game_value { Game_SelfSwitches.new } new_game_value { Game_SelfSwitches.new }
from_old_format { |old_format| old_format[7] }
end end
SaveData.register(:game_screen) do SaveData.register(:game_screen) do
@@ -50,12 +67,14 @@ SaveData.register(:game_screen) do
save_value { $game_screen } save_value { $game_screen }
load_value { |value| $game_screen = value } load_value { |value| $game_screen = value }
new_game_value { Game_Screen.new } new_game_value { Game_Screen.new }
from_old_format { |old_format| old_format[8] }
end end
SaveData.register(:map_factory) do SaveData.register(:map_factory) do
ensure_class :PokemonMapFactory ensure_class :PokemonMapFactory
save_value { $map_factory } save_value { $MapFactory }
load_value { |value| $map_factory = value } load_value { |value| $MapFactory = value }
from_old_format { |old_format| old_format[9] }
end end
SaveData.register(:game_player) do SaveData.register(:game_player) do
@@ -63,6 +82,7 @@ SaveData.register(:game_player) do
save_value { $game_player } save_value { $game_player }
load_value { |value| $game_player = value } load_value { |value| $game_player = value }
new_game_value { Game_Player.new } new_game_value { Game_Player.new }
from_old_format { |old_format| old_format[10] }
end end
SaveData.register(:global_metadata) do SaveData.register(:global_metadata) do
@@ -70,6 +90,7 @@ SaveData.register(:global_metadata) do
save_value { $PokemonGlobal } save_value { $PokemonGlobal }
load_value { |value| $PokemonGlobal = value } load_value { |value| $PokemonGlobal = value }
new_game_value { PokemonGlobalMetadata.new } new_game_value { PokemonGlobalMetadata.new }
from_old_format { |old_format| old_format[11] }
end end
SaveData.register(:map_metadata) do SaveData.register(:map_metadata) do
@@ -77,13 +98,15 @@ SaveData.register(:map_metadata) do
save_value { $PokemonMap } save_value { $PokemonMap }
load_value { |value| $PokemonMap = value } load_value { |value| $PokemonMap = value }
new_game_value { PokemonMapMetadata.new } new_game_value { PokemonMapMetadata.new }
from_old_format { |old_format| old_format[12] }
end end
SaveData.register(:bag) do SaveData.register(:bag) do
ensure_class :PokemonBag ensure_class :PokemonBag
save_value { $bag } save_value { $PokemonBag }
load_value { |value| $bag = value } load_value { |value| $PokemonBag = value }
new_game_value { PokemonBag.new } new_game_value { PokemonBag.new }
from_old_format { |old_format| old_format[13] }
end end
SaveData.register(:storage_system) do SaveData.register(:storage_system) do
@@ -91,25 +114,22 @@ SaveData.register(:storage_system) do
save_value { $PokemonStorage } save_value { $PokemonStorage }
load_value { |value| $PokemonStorage = value } load_value { |value| $PokemonStorage = value }
new_game_value { PokemonStorage.new } new_game_value { PokemonStorage.new }
from_old_format { |old_format| old_format[14] }
end end
SaveData.register(:essentials_version) do SaveData.register(:essentials_version) do
load_in_bootup
ensure_class :String ensure_class :String
save_value { Essentials::VERSION } save_value { Essentials::VERSION }
load_value { |value| $save_engine_version = value } load_value { |value| $SaveVersion = value }
new_game_value { Essentials::VERSION } new_game_value { Essentials::VERSION }
from_old_format { |old_format| old_format[15] }
end end
SaveData.register(:game_version) do SaveData.register(:game_version) do
load_in_bootup
ensure_class :String ensure_class :String
save_value { Settings::GAME_VERSION } save_value { Settings::GAME_VERSION }
load_value { |value| $save_game_version = value } load_value { |value| $game_version = value }
new_game_value { Settings::GAME_VERSION } new_game_value { Settings::GAME_VERSION }
end end
SaveData.register(:stats) do
ensure_class :GameStats
save_value { $stats }
load_value { |value| $stats = value }
new_game_value { GameStats.new }
end

View File

@@ -1,134 +1,242 @@
#=============================================================================== # Contains conversions defined in Essentials by default.
# Conversions required to support backwards compatibility with old save files
# (within reason).
#===============================================================================
SaveData.register_conversion(:v21_replace_phone_data) do SaveData.register_conversion(:v19_define_versions) do
essentials_version 21 essentials_version 19
display_title "Updating Phone data format" display_title 'Adding game version and Essentials version to save data'
to_all do |save_data|
unless save_data.has_key?(:essentials_version)
save_data[:essentials_version] = Essentials::VERSION
end
unless save_data.has_key?(:game_version)
save_data[:game_version] = Settings::GAME_VERSION
end
end
end
SaveData.register_conversion(:v19_convert_PokemonSystem) do
essentials_version 19
display_title 'Updating PokemonSystem class'
to_all do |save_data|
new_system = PokemonSystem.new
new_system.textspeed = save_data[:pokemon_system].textspeed || new_system.textspeed
new_system.battlescene = save_data[:pokemon_system].battlescene || new_system.battlescene
new_system.battlestyle = save_data[:pokemon_system].battlestyle || new_system.battlestyle
new_system.frame = save_data[:pokemon_system].frame || new_system.frame
new_system.textskin = save_data[:pokemon_system].textskin || new_system.textskin
new_system.screensize = save_data[:pokemon_system].screensize || new_system.screensize
new_system.language = save_data[:pokemon_system].language || new_system.language
new_system.runstyle = save_data[:pokemon_system].runstyle || new_system.runstyle
new_system.bgmvolume = save_data[:pokemon_system].bgmvolume || new_system.bgmvolume
new_system.sevolume = save_data[:pokemon_system].sevolume || new_system.sevolume
new_system.textinput = save_data[:pokemon_system].textinput || new_system.textinput
save_data[:pokemon_system] = new_system
end
end
SaveData.register_conversion(:v19_convert_player) do
essentials_version 19
display_title 'Converting player trainer class'
to_all do |save_data|
next if save_data[:player].is_a?(Player)
# Conversion of the party is handled in PokeBattle_Trainer.convert
save_data[:player] = PokeBattle_Trainer.convert(save_data[:player])
end
end
SaveData.register_conversion(:v19_move_global_data_to_player) do
essentials_version 19
display_title 'Moving some global metadata data to player'
to_all do |save_data|
global = save_data[:global_metadata]
player = save_data[:player]
player.character_ID = global.playerID
global.playerID = nil
global.pokedexUnlocked.each_with_index do |value, i|
if value
player.pokedex.unlock(i)
else
player.pokedex.lock(i)
end
end
player.coins = global.coins
global.coins = nil
player.soot = global.sootsack
global.sootsack = nil
player.has_running_shoes = global.runningShoes
global.runningShoes = nil
player.seen_storage_creator = global.seenStorageCreator
global.seenStorageCreator = nil
player.has_snag_machine = global.snagMachine
global.snagMachine = nil
player.seen_purify_chamber = global.seenPurifyChamber
global.seenPurifyChamber = nil
end
end
SaveData.register_conversion(:v19_convert_global_metadata) do
essentials_version 19
display_title 'Adding encounter version variable to global metadata'
to_value :global_metadata do |global| to_value :global_metadata do |global|
if !global.phone global.bridge ||= 0
global.instance_eval do global.encounter_version ||= 0
@phone = Phone.new if global.pcItemStorage
@phoneTime = nil # Don't bother using this global.pcItemStorage.items.each_with_index do |slot, i|
if @phoneNumbers item_data = GameData::Item.try_get(slot[0])
@phoneNumbers.each do |contact| if item_data
if contact.length > 4 slot[0] = item_data.id
# Trainer
@phone.add(contact[6], contact[7], contact[1], contact[2], contact[5], 0)
new_contact = @phone.get(contact[1], contact[2], 0)
new_contact.visible = contact[0]
new_contact.rematch_flag = [contact[4] - 1, 0].max
else else
# Non-trainer global.pcItemStorage.items[i] = nil
@phone.add(contact[3], contact[2], contact[1])
end end
end end
@phoneNumbers = nil global.pcItemStorage.items.compact!
end
if global.mailbox
global.mailbox.each_with_index do |mail, i|
global.mailbox[i] = PokemonMail.convert(mail) if mail
end
end
global.phoneNumbers.each do |contact|
contact[1] = GameData::TrainerType.get(contact[1]).id if contact && contact.length == 8
end
if global.partner
global.partner[0] = GameData::TrainerType.get(global.partner[0]).id
global.partner[3].each_with_index do |pkmn, i|
global.partner[3][i] = PokeBattle_Pokemon.convert(pkmn) if pkmn
end
end
if global.daycare
global.daycare.each do |slot|
slot[0] = PokeBattle_Pokemon.convert(slot[0]) if slot && slot[0]
end
end
if global.roamPokemon
global.roamPokemon.each_with_index do |pkmn, i|
global.roamPokemon[i] = PokeBattle_Pokemon.convert(pkmn) if pkmn && pkmn != true
end
end
global.purifyChamber.sets.each do |set|
set.shadow = PokeBattle_Pokemon.convert(set.shadow) if set.shadow
set.list.each_with_index do |pkmn, i|
set.list[i] = PokeBattle_Pokemon.convert(pkmn) if pkmn
end
end
if global.hallOfFame
global.hallOfFame.each do |team|
next if !team
team.each_with_index do |pkmn, i|
team[i] = PokeBattle_Pokemon.convert(pkmn) if pkmn
end end
end end
end end
end if global.triads
end global.triads.items.each do |card|
card[0] = GameData::Species.get(card[0]).id if card && card[0] && card[0] != 0
#===============================================================================
SaveData.register_conversion(:v21_replace_flute_booleans) do
essentials_version 21
display_title "Updating Black/White Flute variables"
to_value :map_metadata do |metadata|
metadata.instance_eval do
if !@blackFluteUsed.nil?
if Settings::FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS
@higher_level_wild_pokemon = @blackFluteUsed
else
@lower_encounter_rate = @blackFluteUsed
end
@blackFluteUsed = nil
end
if !@whiteFluteUsed.nil?
if Settings::FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS
@lower_level_wild_pokemon = @whiteFluteUsed
else
@higher_encounter_rate = @whiteFluteUsed
end
@whiteFluteUsed = nil
end end
end end
end end
end end
#=============================================================================== SaveData.register_conversion(:v19_1_fix_phone_contacts) do
essentials_version 19.1
SaveData.register_conversion(:v21_add_bump_stat) do display_title 'Fixing phone contacts data'
essentials_version 21 to_value :global_metadata do |global|
display_title "Adding a bump stat" global.phoneNumbers.each do |contact|
to_value :stats do |stats| contact[1] = GameData::TrainerType.get(contact[1]).id if contact && contact.length == 8
stats.instance_eval do
@bump_count = 0 if !@bump_count
end end
end end
end end
#=============================================================================== SaveData.register_conversion(:v19_convert_bag) do
essentials_version 19
SaveData.register_conversion(:v22_add_adventure_magic_number) do display_title 'Converting item IDs in Bag'
essentials_version 22
display_title "Adding adventure ID"
to_value :game_system do |game_system|
game_system.instance_eval do
@adventure_magic_number ||= rand(2**32)
end
end
end
#===============================================================================
SaveData.register_conversion(:v22_add_new_stats) do
essentials_version 22
display_title "Adding some more stats"
to_value :stats do |stats|
stats.instance_eval do
@wild_battles_fled = 0 if !@wild_battles_fled
@pokemon_release_count = 0 if !@pokemon_release_count
@primal_reversion_count = 0 if !@primal_reversion_count
end
end
end
#===============================================================================
SaveData.register_conversion(:v22_convert_bag_object) do
essentials_version 22
display_title "Converting Bag's pockets"
to_value :bag do |bag| to_value :bag do |bag|
bag.instance_eval do bag.instance_eval do
all_pockets = GameData::BagPocket.all_pockets for pocket in self.pockets
if @pockets.is_a?(Array) pocket.each_with_index do |item, i|
new_pockets = {} next if !item || !item[0] || item[0] == 0
all_pockets.each { |pckt| new_pockets[pckt] = [] } item_data = GameData::Item.try_get(item[0])
@pockets.each_with_index do |value, i| if item_data
next if i == 0 item[0] = item_data.id
value.each do |item| else
pckt = GameData::Item.get(item[0]).bag_pocket pocket[i] = nil
new_pockets[pckt].push(item)
end end
end end
@pockets = new_pockets pocket.compact!
end end
if @last_viewed_pocket.is_a?(Integer) self.registeredIndex # Just to ensure this data exists
@last_viewed_pocket = all_pockets[@last_viewed_pocket - 1] || all_pockets.first self.registeredItems.each_with_index do |item, i|
next if !item
if item == 0
self.registeredItems[i] = nil
else
item_data = GameData::Item.try_get(item)
if item_data
self.registeredItems[i] = item_data.id
else
self.registeredItems[i] = nil
end end
if @last_pocket_selections.is_a?(Array)
new_sels = {}
all_pockets.each { |pckt| new_sels[pckt] = 0 }
@last_pocket_selections.each_with_index do |value, i|
next if i == 0
pckt = all_pockets[i - 1]
new_sels[pckt] = value if pckt && value <= @pockets[pckt].length - 1
end end
@last_pocket_selections = new_sels end
self.registeredItems.compact!
end # bag.instance_eval
end # to_value
end
SaveData.register_conversion(:v19_convert_game_variables) do
essentials_version 19
display_title 'Converting classes of things in Game Variables'
to_all do |save_data|
variables = save_data[:variables]
for i in 0..5000
value = variables[i]
next if value.nil?
if value.is_a?(Array)
value.each_with_index do |value2, j|
if value2.is_a?(PokeBattle_Pokemon)
value[j] = PokeBattle_Pokemon.convert(value2)
end
end
elsif value.is_a?(PokeBattle_Pokemon)
variables[i] = PokeBattle_Pokemon.convert(value)
elsif value.is_a?(PokemonBag)
SaveData.run_single_conversions(value, :bag, save_data)
end end
end end
end end
end end
SaveData.register_conversion(:v19_convert_storage) do
essentials_version 19
display_title 'Converting classes of Pokémon in storage'
to_value :storage_system do |storage|
storage.instance_eval do
for box in 0...self.maxBoxes
for i in 0...self.maxPokemon(box)
self[box, i] = PokeBattle_Pokemon.convert(self[box, i]) if self[box, i]
end
end
self.unlockedWallpapers # Just to ensure this data exists
end # storage.instance_eval
end # to_value
end
SaveData.register_conversion(:v19_convert_game_player) do
essentials_version 19
display_title 'Converting game player character'
to_value :game_player do |game_player|
game_player.width = 1
game_player.height = 1
game_player.sprite_size = [Game_Map::TILE_WIDTH, Game_Map::TILE_HEIGHT]
game_player.pattern_surf ||= 0
game_player.lock_pattern ||= false
game_player.move_speed = game_player.move_speed
end
end
SaveData.register_conversion(:v19_convert_game_screen) do
essentials_version 19
display_title 'Converting game screen'
to_value :game_screen do |game_screen|
game_screen.weather(game_screen.weather_type, game_screen.weather_max, 0)
end
end

View File

@@ -1,53 +1,52 @@
#===============================================================================
# The Game module contains methods for saving and loading the game. # The Game module contains methods for saving and loading the game.
#===============================================================================
module Game module Game
module_function
# Initializes various global variables and loads the game data. # Initializes various global variables and loads the game data.
def initialize def self.initialize
$PokemonTemp = PokemonTemp.new
$game_temp = Game_Temp.new $game_temp = Game_Temp.new
$game_system = Game_System.new $game_system = Game_System.new
$data_animations = load_data("Data/Animations.rxdata") $data_animations = load_data('Data/Animations.rxdata')
$data_tilesets = load_data("Data/Tilesets.rxdata") $data_tilesets = load_data('Data/Tilesets.rxdata')
$data_common_events = load_data("Data/CommonEvents.rxdata") $data_common_events = load_data('Data/CommonEvents.rxdata')
$data_system = load_data("Data/System.rxdata") $data_system = load_data('Data/System.rxdata')
pbLoadBattleAnimations pbLoadBattleAnimations
GameData.load_all GameData.load_all
map_file = sprintf("Data/Map%03d.rxdata", $data_system.start_map_id) map_file = format('Data/Map%03d.rxdata', $data_system.start_map_id)
if $data_system.start_map_id == 0 || !pbRgssExists?(map_file) if $data_system.start_map_id == 0 || !pbRgssExists?(map_file)
raise _INTL("No starting position was set in the map editor.") raise _INTL('No starting position was set in the map editor.')
end end
end end
# Loads bootup data from save file (if it exists) or creates bootup data (if # Loads bootup data from save file (if it exists) or creates bootup data (if
# it doesn't). # it doesn't).
def set_up_system def self.set_up_system
SaveData.move_old_windows_save if System.platform[/Windows/]
save_data = (SaveData.exists?) ? SaveData.read_from_file(SaveData::FILE_PATH) : {}
if save_data.empty?
SaveData.initialize_bootup_values SaveData.initialize_bootup_values
else
SaveData.load_bootup_values(save_data)
end
# Set resize factor
pbSetResizeFactor([$PokemonSystem.screensize, 4].min) pbSetResizeFactor([$PokemonSystem.screensize, 4].min)
# Set language (and choose language if there is no save file) # Set language (and choose language if there is no save file)
if !Settings::LANGUAGES.empty? if Settings::LANGUAGES.length >= 2
$PokemonSystem.language = pbChooseLanguage if !SaveData.exists? && Settings::LANGUAGES.length >= 2 $PokemonSystem.language = pbChooseLanguage if save_data.empty?
MessageTypes.load_message_files(Settings::LANGUAGES[$PokemonSystem.language][1]) pbLoadMessages('Data/' + Settings::LANGUAGES[$PokemonSystem.language][1])
end end
end end
# Called when starting a new game. Initializes global variables # Called when starting a new game. Initializes global variables
# and transfers the player into the map scene. # and transfers the player into the map scene.
def start_new def self.start_new
# Essentials 21 renamed the global variable $Trainer if $game_map && $game_map.events
# It's still used everywhere in events, global events so this makes things simpler
if $game_map&.events
$game_map.events.each_value { |event| event.clear_starting } $game_map.events.each_value { |event| event.clear_starting }
end end
$game_temp.common_event_id = 0 if $game_temp $game_temp.common_event_id = 0 if $game_temp
pbMapInterpreter&.clear $PokemonTemp.begunNewGame = true
pbMapInterpreter&.setup(nil, 0, 0)
$scene = Scene_Map.new $scene = Scene_Map.new
SaveData.load_new_game_values SaveData.load_new_game_values
$game_temp.last_uptime_refreshed_play_time = System.uptime $MapFactory = PokemonMapFactory.new($data_system.start_map_id)
$stats.play_sessions += 1
$map_factory = PokemonMapFactory.new($data_system.start_map_id)
$game_player.moveto($data_system.start_x, $data_system.start_y) $game_player.moveto($data_system.start_x, $data_system.start_y)
$game_player.refresh $game_player.refresh
$PokemonEncounters = PokemonEncounters.new $PokemonEncounters = PokemonEncounters.new
@@ -59,17 +58,10 @@ module Game
# Loads the game from the given save data and starts the map scene. # Loads the game from the given save data and starts the map scene.
# @param save_data [Hash] hash containing the save data # @param save_data [Hash] hash containing the save data
# @raise [SaveData::InvalidValueError] if an invalid value is being loaded # @raise [SaveData::InvalidValueError] if an invalid value is being loaded
def load(save_data) def self.load(save_data)
# Essentials 21 renamed the global variable $Trainer
# It's still used everywhere in events, global events so this makes things simpler
$Trainer = $player
validate save_data => Hash validate save_data => Hash
SaveData.load_all_values(save_data) SaveData.load_all_values(save_data)
$game_temp.last_uptime_refreshed_play_time = System.uptime self.load_map
$stats.play_sessions += 1
load_map
pbAutoplayOnSave pbAutoplayOnSave
$game_map.update $game_map.update
$PokemonMap.updateMap $PokemonMap.updateMap
@@ -77,30 +69,32 @@ module Game
end end
# Loads and validates the map. Called when loading a saved game. # Loads and validates the map. Called when loading a saved game.
def load_map def self.load_map
$game_map = $map_factory.map $game_map = $MapFactory.map
magic_number_matches = ($game_system.magic_number == $data_system.magic_number) magic_number_matches = ($game_system.magic_number == $data_system.magic_number)
if !magic_number_matches || $PokemonGlobal.safesave if !magic_number_matches || $PokemonGlobal.safesave
pbMapInterpreter.setup(nil, 0) if pbMapInterpreterRunning? if pbMapInterpreterRunning?
pbMapInterpreter.setup(nil, 0)
end
begin begin
$map_factory.setup($game_map.map_id) $MapFactory.setup($game_map.map_id)
rescue Errno::ENOENT rescue Errno::ENOENT
if $DEBUG if $DEBUG
pbMessage(_INTL("Map {1} was not found.", $game_map.map_id)) pbMessage(_INTL('Map {1} was not found.', $game_map.map_id))
map = pbWarpToMap map = pbWarpToMap
exit unless map exit unless map
$map_factory.setup(map[0]) $MapFactory.setup(map[0])
$game_player.moveto(map[1], map[2]) $game_player.moveto(map[1], map[2])
else else
raise _INTL("The map was not found. The game cannot continue.") raise _INTL('The map was not found. The game cannot continue.')
end end
end end
$game_player.center($game_player.x, $game_player.y) $game_player.center($game_player.x, $game_player.y)
else else
$map_factory.setMapChanged($game_map.map_id) $MapFactory.setMapChanged($game_map.map_id)
end end
if $game_map.events.nil? if $game_map.events.nil?
raise _INTL("The map is corrupt. The game cannot continue.") raise _INTL('The map is corrupt. The game cannot continue.')
end end
$PokemonEncounters = PokemonEncounters.new $PokemonEncounters = PokemonEncounters.new
$PokemonEncounters.setup($game_map.map_id) $PokemonEncounters.setup($game_map.map_id)
@@ -108,21 +102,17 @@ module Game
end end
# Saves the game. Returns whether the operation was successful. # Saves the game. Returns whether the operation was successful.
# @param index [Integer] the number to put in the save file's name Game#.rzdata # @param save_file [String] the save file path
# @param directory [String] the folder to put the save file in
# @param safe [Boolean] whether $PokemonGlobal.safesave should be set to true # @param safe [Boolean] whether $PokemonGlobal.safesave should be set to true
# @return [Boolean] whether the operation was successful # @return [Boolean] whether the operation was successful
# @raise [SaveData::InvalidValueError] if an invalid value is being saved # @raise [SaveData::InvalidValueError] if an invalid value is being saved
def save(index, directory = SaveData::DIRECTORY, safe: false) def self.save(save_file = SaveData::FILE_PATH, safe: false)
validate index => Integer, directory => String, safe => [TrueClass, FalseClass] validate save_file => String, safe => [TrueClass, FalseClass]
filename = SaveData.filename_from_index(index)
$PokemonGlobal.safesave = safe $PokemonGlobal.safesave = safe
$game_system.save_count += 1 $game_system.save_count += 1
$game_system.magic_number = $data_system.magic_number $game_system.magic_number = $data_system.magic_number
$stats.set_time_last_saved
$stats.save_filename_number = index
begin begin
SaveData.save_to_file(directory + filename) SaveData.save_to_file(save_file)
Graphics.frame_reset Graphics.frame_reset
rescue IOError, SystemCallError rescue IOError, SystemCallError
$game_system.save_count -= 1 $game_system.save_count -= 1

View File

@@ -1,52 +1,45 @@
#=============================================================================== #===============================================================================
# Modified Scene_Map class for Pokémon. # ** Modified Scene_Map class for Pokémon.
#-------------------------------------------------------------------------------
#
#=============================================================================== #===============================================================================
class Scene_Map class Scene_Map
attr_reader :spritesetGlobal attr_reader :spritesetGlobal
attr_reader :map_renderer
def spriteset(map_id = -1) def spriteset
return @spritesets[map_id] if map_id > 0 && @spritesets[map_id] for i in @spritesets.values
@spritesets.each_value do |i| return i if i.map==$game_map
return i if i.map == $game_map
end end
return @spritesets.values[0] return @spritesets.values[0]
end end
def createSpritesets def createSpritesets
@map_renderer = TilemapRenderer.new(Spriteset_Map.viewport) if !@map_renderer || @map_renderer.disposed? @spritesetGlobal = Spriteset_Global.new
@spritesetGlobal = Spriteset_Global.new if !@spritesetGlobal
@spritesets = {} @spritesets = {}
$map_factory.maps.each do |map| for map in $MapFactory.maps
@spritesets[map.map_id] = Spriteset_Map.new(map) @spritesets[map.map_id] = Spriteset_Map.new(map)
end end
$map_factory.setSceneStarted(self) $MapFactory.setSceneStarted(self)
updateSpritesets(true) updateSpritesets
end end
def createSingleSpriteset(map) def createSingleSpriteset(map)
temp = $scene.spriteset.getAnimations temp = $scene.spriteset.getAnimations
@spritesets[map] = Spriteset_Map.new($map_factory.maps[map]) @spritesets[map] = Spriteset_Map.new($MapFactory.maps[map])
$scene.spriteset.restoreAnimations(temp) $scene.spriteset.restoreAnimations(temp)
$map_factory.setSceneStarted(self) $MapFactory.setSceneStarted(self)
updateSpritesets(true) updateSpritesets
end end
def disposeSpritesets def disposeSpritesets
return if !@spritesets return if !@spritesets
@spritesets.each_key do |i| for i in @spritesets.keys
next if !@spritesets[i] next if !@spritesets[i]
@spritesets[i].dispose @spritesets[i].dispose
@spritesets[i] = nil @spritesets[i] = nil
end end
@spritesets.clear @spritesets.clear
@spritesets = {} @spritesets = {}
end
def dispose
disposeSpritesets
@map_renderer.dispose
@map_renderer = nil
@spritesetGlobal.dispose @spritesetGlobal.dispose
@spritesetGlobal = nil @spritesetGlobal = nil
end end
@@ -57,24 +50,26 @@ class Scene_Map
return if !playingBGM && !playingBGS return if !playingBGM && !playingBGS
map = load_data(sprintf("Data/Map%03d.rxdata", mapid)) map = load_data(sprintf("Data/Map%03d.rxdata", mapid))
if playingBGM && map.autoplay_bgm if playingBGM && map.autoplay_bgm
test_filename = map.bgm.name if (PBDayNight.isNight? rescue false)
test_filename += "_n" if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/" + test_filename + "_n") pbBGMFade(0.8) if playingBGM.name!=map.bgm.name && playingBGM.name!=map.bgm.name+"_n"
pbBGMFade(0.8) if playingBGM.name != test_filename else
pbBGMFade(0.8) if playingBGM.name!=map.bgm.name
end end
if playingBGS && map.autoplay_bgs && playingBGS.name != map.bgs.name end
pbBGMFade(0.8) if playingBGS && map.autoplay_bgs
pbBGMFade(0.8) if playingBGS.name!=map.bgs.name
end end
Graphics.frame_reset Graphics.frame_reset
end end
def transfer_player(cancel_swimming = true) def transfer_player(cancelVehicles=true)
$game_temp.player_transferring = false $game_temp.player_transferring = false
pbCancelVehicles($game_temp.player_new_map_id, cancel_swimming) pbCancelVehicles($game_temp.player_new_map_id) if cancelVehicles
autofade($game_temp.player_new_map_id) autofade($game_temp.player_new_map_id)
pbBridgeOff pbBridgeOff
@spritesetGlobal.playersprite.clearShadows @spritesetGlobal.playersprite.clearShadows
if $game_map.map_id != $game_temp.player_new_map_id if $game_map.map_id!=$game_temp.player_new_map_id
$map_factory.setup($game_temp.player_new_map_id) $MapFactory.setup($game_temp.player_new_map_id)
end end
$game_player.moveto($game_temp.player_new_x, $game_temp.player_new_y) $game_player.moveto($game_temp.player_new_x, $game_temp.player_new_y)
case $game_temp.player_new_direction case $game_temp.player_new_direction
@@ -84,14 +79,13 @@ class Scene_Map
when 8 then $game_player.turn_up when 8 then $game_player.turn_up
end end
$game_player.straighten $game_player.straighten
$game_temp.followers.map_transfer_followers
$game_map.update $game_map.update
disposeSpritesets disposeSpritesets
RPG::Cache.clear RPG::Cache.clear
createSpritesets createSpritesets
if $game_temp.transition_processing if $game_temp.transition_processing
$game_temp.transition_processing = false $game_temp.transition_processing = false
Graphics.transition Graphics.transition(20)
end end
$game_map.autoplay $game_map.autoplay
Graphics.frame_reset Graphics.frame_reset
@@ -103,7 +97,9 @@ class Scene_Map
$game_temp.in_menu = true $game_temp.in_menu = true
$game_player.straighten $game_player.straighten
$game_map.update $game_map.update
UI::PauseMenu.new.main sscene = PokemonPauseMenu_Scene.new
sscreen = PokemonPauseMenu.new(sscene)
sscreen.pbStartPokemonMenu
$game_temp.in_menu = false $game_temp.in_menu = false
end end
@@ -115,119 +111,107 @@ class Scene_Map
end end
def miniupdate def miniupdate
$game_temp.in_mini_update = true $PokemonTemp.miniupdate = true
loop do loop do
$game_player.update
updateMaps updateMaps
$game_player.update
$game_system.update $game_system.update
$game_screen.update $game_screen.update
break if !$game_temp.player_transferring break unless $game_temp.player_transferring
transfer_player(false) transfer_player
break if $game_temp.transition_processing break if $game_temp.transition_processing
end end
updateSpritesets updateSpritesets
$game_temp.in_mini_update = false $PokemonTemp.miniupdate = false
end end
def updateMaps def updateMaps
$map_factory.maps.each do |map| for map in $MapFactory.maps
map.update map.update
end end
$map_factory.updateMaps(self) $MapFactory.updateMaps(self)
end end
def updateSpritesets(refresh = false) def updateSpritesets
@spritesets = {} if !@spritesets @spritesets = {} if !@spritesets
$map_factory.maps.each do |map|
@spritesets[map.map_id] = Spriteset_Map.new(map) if !@spritesets[map.map_id]
end
keys = @spritesets.keys.clone keys = @spritesets.keys.clone
keys.each do |i| for i in keys
if $map_factory.hasMap?(i) if !$MapFactory.hasMap?(i)
@spritesets[i].update @spritesets[i].dispose if @spritesets[i]
else
@spritesets[i]&.dispose
@spritesets[i] = nil @spritesets[i] = nil
@spritesets.delete(i) @spritesets.delete(i)
else
@spritesets[i].update
end end
end end
@spritesetGlobal.update @spritesetGlobal.update
pbDayNightTint(@map_renderer) for map in $MapFactory.maps
@map_renderer.refresh if refresh @spritesets[map.map_id] = Spriteset_Map.new(map) if !@spritesets[map.map_id]
@map_renderer.update end
EventHandlers.trigger(:on_frame_update) Events.onMapUpdate.trigger(self)
end end
def update def update
loop do loop do
updateMaps
pbMapInterpreter.update pbMapInterpreter.update
$game_player.update $game_player.update
updateMaps
$game_system.update $game_system.update
$game_screen.update $game_screen.update
break if !$game_temp.player_transferring break unless $game_temp.player_transferring
transfer_player(false) transfer_player
break if $game_temp.transition_processing break if $game_temp.transition_processing
end end
updateSpritesets updateSpritesets
if $game_temp.title_screen_calling if $game_temp.to_title
SaveData.mark_values_as_unloaded
$scene = pbCallTitle $scene = pbCallTitle
return return
end end
if $game_temp.transition_processing if $game_temp.transition_processing
$game_temp.transition_processing = false $game_temp.transition_processing = false
if $game_temp.transition_name == "" if $game_temp.transition_name == ""
Graphics.transition Graphics.transition(20)
else else
Graphics.transition(40, "Graphics/Transitions/" + $game_temp.transition_name) Graphics.transition(40, "Graphics/Transitions/" + $game_temp.transition_name)
end end
end end
return if $game_temp.message_window_showing return if $game_temp.message_window_showing
if !pbMapInterpreterRunning? && !$PokemonGlobal.forced_movement? if !pbMapInterpreterRunning?
if Input.trigger?(Input::USE) if Input.trigger?(Input::USE)
$game_temp.interact_calling = true $PokemonTemp.hiddenMoveEventCalling = true
elsif Input.trigger?(Input::ACTION) elsif Input.trigger?(Input::BACK)
if !$game_system.menu_disabled && !$game_player.moving? unless $game_system.menu_disabled || $game_player.moving?
$game_temp.menu_calling = true $game_temp.menu_calling = true
$game_temp.menu_beep = true $game_temp.menu_beep = true
end end
elsif Input.trigger?(Input::SPECIAL) elsif Input.trigger?(Input::SPECIAL)
$game_temp.ready_menu_calling = true if !$game_player.moving? unless $game_player.moving?
$PokemonTemp.keyItemCalling = true
end
elsif Input.press?(Input::F9) elsif Input.press?(Input::F9)
$game_temp.debug_calling = true if $DEBUG $game_temp.debug_calling = true if $DEBUG
end end
end end
if !$game_player.moving? unless $game_player.moving?
if $game_temp.menu_calling if $game_temp.menu_calling
call_menu call_menu
elsif $game_temp.debug_calling elsif $game_temp.debug_calling
call_debug call_debug
elsif $game_temp.ready_menu_calling elsif $PokemonTemp.keyItemCalling
$game_temp.ready_menu_calling = false $PokemonTemp.keyItemCalling = false
$game_player.straighten $game_player.straighten
pbUseKeyItem pbUseKeyItem
elsif $game_temp.interact_calling elsif $PokemonTemp.hiddenMoveEventCalling
$game_temp.interact_calling = false $PokemonTemp.hiddenMoveEventCalling = false
triggered = false
# Try to trigger an event the player is standing on, and one in front of
# the player
if !$game_temp.in_mini_update
triggered ||= $game_player.check_event_trigger_here([0])
triggered ||= $game_player.check_event_trigger_there([0, 2]) if !triggered
end
# Try to trigger an interaction with a tile
if !triggered
$game_player.straighten $game_player.straighten
EventHandlers.trigger(:on_player_interact) Events.onAction.trigger(self)
end
end end
end end
end end
def main def main
createSpritesets createSpritesets
Graphics.transition Graphics.transition(20)
loop do loop do
Graphics.update Graphics.update
Input.update Input.update
@@ -235,13 +219,9 @@ class Scene_Map
break if $scene != self break if $scene != self
end end
Graphics.freeze Graphics.freeze
dispose disposeSpritesets
if $game_temp.title_screen_calling if $game_temp.to_title
pbMapInterpreter.command_end if pbMapInterpreterRunning? Graphics.transition(20)
$game_temp.last_uptime_refreshed_play_time = nil
$game_temp.title_screen_calling = false
pbBGMFade(1.0)
Graphics.transition
Graphics.freeze Graphics.freeze
end end
end end

View File

@@ -5,9 +5,11 @@
# Game_System class and the Game_Event class. # Game_System class and the Game_Event class.
#=============================================================================== #===============================================================================
class Interpreter class Interpreter
# Object Initialization #-----------------------------------------------------------------------------
# * Object Initialization
# depth : nest depth # depth : nest depth
# main : main flag # main : main flag
#-----------------------------------------------------------------------------
def initialize(depth = 0, main = false) def initialize(depth = 0, main = false)
@depth = depth @depth = depth
@main = main @main = main
@@ -20,28 +22,25 @@ class Interpreter
def inspect def inspect
str = super.chop str = super.chop
str << sprintf(" @event_id: %d>", @event_id) str << format(' @event_id: %d>', @event_id)
return str return str
end end
def clear def clear
@map_id = 0 # map ID when starting up @map_id = 0 # map ID when starting up
@event_id = 0 @event_id = 0 # event ID
@message_waiting = false # waiting for message to end @message_waiting = false # waiting for message to end
@move_route_waiting = false # waiting for move completion @move_route_waiting = false # waiting for move completion
@wait_count = 0 @wait_count = 0 # wait count
@wait_start = nil @child_interpreter = nil # child interpreter
@child_interpreter = nil @branch = {} # branch data
@branch = {}
@buttonInput = false @buttonInput = false
@hidden_choices = []
@renamed_choices = []
end_follower_overrides
end end
#-----------------------------------------------------------------------------
# Event Setup # * Event Setup
# list : list of event commands # list : list of event commands
# event_id : event ID # event_id : event ID
#-----------------------------------------------------------------------------
def setup(list, event_id, map_id = nil) def setup(list, event_id, map_id = nil)
clear clear
@map_id = map_id || $game_map.map_id @map_id = map_id || $game_map.map_id
@@ -60,7 +59,7 @@ class Interpreter
return return
end end
# Check all map events for one that wants to start, and set it up # Check all map events for one that wants to start, and set it up
$game_map.events.each_value do |event| for event in $game_map.events.values
next if !event.starting next if !event.starting
if event.trigger < 3 # Isn't autorun or parallel processing if event.trigger < 3 # Isn't autorun or parallel processing
event.lock event.lock
@@ -70,7 +69,7 @@ class Interpreter
return return
end end
# Check all common events for one that is autorun, and set it up # Check all common events for one that is autorun, and set it up
$data_common_events.compact.each do |common_event| for common_event in $data_common_events.compact
next if common_event.trigger != 1 || !$game_switches[common_event.switch_id] next if common_event.trigger != 1 || !$game_switches[common_event.switch_id]
setup(common_event.list, 0) setup(common_event.list, 0)
return return
@@ -78,9 +77,11 @@ class Interpreter
end end
def running? def running?
return !@list.nil? return @list != nil
end end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update def update
@loop_count = 0 @loop_count = 0
loop do loop do
@@ -91,7 +92,7 @@ class Interpreter
end end
# If this interpreter's map isn't the current map or connected to it, # If this interpreter's map isn't the current map or connected to it,
# forget this interpreter's event ID # forget this interpreter's event ID
if $game_map.map_id != @map_id && !$map_factory.areConnected?($game_map.map_id, @map_id) if $game_map.map_id != @map_id && !$MapFactory.areConnected?($game_map.map_id, @map_id)
@event_id = 0 @event_id = 0
end end
# Update child interpreter if one exists # Update child interpreter if one exists
@@ -105,21 +106,15 @@ class Interpreter
# Do nothing if any event or the player is in the middle of a move route # Do nothing if any event or the player is in the middle of a move route
if @move_route_waiting if @move_route_waiting
return if $game_player.move_route_forcing return if $game_player.move_route_forcing
$game_map.events.each_value do |event| for event in $game_map.events.values
return if event.move_route_forcing
end
$game_temp.followers.each_follower do |event, follower|
return if event.move_route_forcing return if event.move_route_forcing
end end
@move_route_waiting = false @move_route_waiting = false
end end
# Do nothing if the player is jumping out of surfing
return if $game_temp.ending_surf
# Do nothing while waiting # Do nothing while waiting
if @wait_count > 0 if @wait_count > 0
return if System.uptime - @wait_start < @wait_count @wait_count -= 1
@wait_count = 0 return
@wait_start = nil
end end
# Do nothing if the pause menu is going to open # Do nothing if the pause menu is going to open
return if $game_temp.menu_calling return if $game_temp.menu_calling
@@ -134,52 +129,70 @@ class Interpreter
@index += 1 @index += 1
end end
end end
#-----------------------------------------------------------------------------
# * Execute script
#-----------------------------------------------------------------------------
def execute_script(script) def execute_script(script)
begin begin
result = eval(script) result = eval(script)
return result return result
rescue Exception rescue Exception
e = $! e = $!
raise if e.is_a?(SystemExit) || e.class.to_s == "Reset" raise if e.is_a?(SystemExit) || "#{e.class}" == "Reset"
event = get_self event = get_self
# Gather text for error message s = "Backtrace:\r\n"
message = pbGetExceptionMessage(e) message = pbGetExceptionMessage(e)
backtrace_text = ""
if e.is_a?(SyntaxError) if e.is_a?(SyntaxError)
script.each_line do |line| script.each_line { |line|
line.gsub!(/\s+$/, "") line.gsub!(/\s+$/, "")
if line[/^\s*\(/] if line[/^\s*\(/]
message += "\r\n***Line '#{line}' shouldn't begin with '('. Try putting the '('\r\n" message += "\r\n***Line '#{line}' shouldn't begin with '('. Try\r\n"
message += "at the end of the previous line instead, or using 'extendtext.exe'." message += "putting the '(' at the end of the previous line instead,\r\n"
message += "or using 'extendtext.exe'."
end end
if line[/\:\:\s*$/]
message += "\r\n***Line '#{line}' can't end with '::'. Try putting\r\n"
message += "the next word on the same line, e.g. 'PBSpecies:" + ":MEW'"
end
}
else
for bt in e.backtrace[0, 10]
s += bt + "\r\n"
end
s.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] }
end
message = "Exception: #{e.class}\r\nMessage: " + message + "\r\n"
message += "\r\n***Full script:\r\n#{script}\r\n"
if event && $game_map
map_name = ($game_map.name rescue nil) || "???"
err = "Script error in event #{event.id} (coords #{event.x},#{event.y}), map #{$game_map.map_id} (#{map_name}):\r\n"
err += "#{message}\r\n#{s}"
if e.is_a?(Hangup)
$EVENTHANGUPMSG = err
raise
end
elsif $game_map
map_name = ($game_map.name rescue nil) || "???"
err = "Script error in map #{$game_map.map_id} (#{map_name}):\r\n"
err += "#{message}\r\n#{s}"
if e.is_a?(Hangup)
$EVENTHANGUPMSG = err
raise
end end
else else
backtrace_text += "\r\n" err = "Script error in interpreter:\r\n#{message}\r\n#{s}"
backtrace_text += "Backtrace:" if e.is_a?(Hangup)
e.backtrace[0, 10].each { |i| backtrace_text += "\r\n#{i}" } $EVENTHANGUPMSG = err
backtrace_text.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] } rescue nil raise
backtrace_text += "\r\n"
end
# Assemble error message
err = "Script error in Interpreter\r\n"
if $game_map
map_name = (pbGetBasicMapNameFromId($game_map.map_id) rescue nil) || "???"
if event
err = "Script error in event #{event.id} (coords #{event.x},#{event.y}), map #{$game_map.map_id} (#{map_name})\r\n"
else
err = "Script error in Common Event, map #{$game_map.map_id} (#{map_name})\r\n"
end end
end end
err += "Exception: #{e.class}\r\n" raise err
err += "Message: #{message}\r\n\r\n"
err += "***Full script:\r\n#{script}" # \r\n"
err += backtrace_text
# Raise error
raise EventScriptError.new(err)
end end
end end
#-----------------------------------------------------------------------------
# * Get Character
# parameter : parameter
#-----------------------------------------------------------------------------
def get_character(parameter = 0) def get_character(parameter = 0)
case parameter case parameter
when -1 # player when -1 # player
@@ -204,18 +217,21 @@ class Interpreter
def get_event(parameter) def get_event(parameter)
return get_character(parameter) return get_character(parameter)
end end
#-----------------------------------------------------------------------------
# Freezes all events on the map (for use at the beginning of common events) # * Freezes all events on the map (for use at the beginning of common events)
#-----------------------------------------------------------------------------
def pbGlobalLock def pbGlobalLock
$game_map.events.each_value { |event| event.minilock } $game_map.events.values.each { |event| event.minilock }
end end
#-----------------------------------------------------------------------------
# Unfreezes all events on the map (for use at the end of common events) # * Unfreezes all events on the map (for use at the end of common events)
#-----------------------------------------------------------------------------
def pbGlobalUnlock def pbGlobalUnlock
$game_map.events.each_value { |event| event.unlock } $game_map.events.values.each { |event| event.unlock }
end end
#-----------------------------------------------------------------------------
# Gets the next index in the interpreter, ignoring certain commands between messages # * Gets the next index in the interpreter, ignoring certain commands between messages
#-----------------------------------------------------------------------------
def pbNextIndex(index) def pbNextIndex(index)
return -1 if !@list || @list.length == 0 return -1 if !@list || @list.length == 0
i = index + 1 i = index + 1
@@ -265,27 +281,12 @@ class Interpreter
temp_index += 1 temp_index += 1
end end
end end
#-----------------------------------------------------------------------------
def follower_move_route(id = nil) # * Various methods to be used in a script event command.
@follower_move_route = true #-----------------------------------------------------------------------------
@follower_move_route_id = id
end
def follower_animation(id = nil)
@follower_animation = true
@follower_animation_id = id
end
def end_follower_overrides
@follower_move_route = false
@follower_move_route_id = nil
@follower_animation = false
@follower_animation_id = nil
end
# Helper function that shows a picture in a script. # Helper function that shows a picture in a script.
def pbShowPicture(number, name, origin, x, y, zoomX = 100, zoomY = 100, opacity = 255, blendType = 0) def pbShowPicture(number, name, origin, x, y, zoomX = 100, zoomY = 100, opacity = 255, blendType = 0)
number += ($game_temp.in_battle ? 50 : 0) number = number + ($game_temp.in_battle ? 50 : 0)
$game_screen.pictures[number].show(name, origin, x, y, zoomX, zoomY, opacity, blendType) $game_screen.pictures[number].show(name, origin, x, y, zoomX, zoomY, opacity, blendType)
end end
@@ -294,7 +295,7 @@ class Interpreter
def pbEraseThisEvent def pbEraseThisEvent
if $game_map.events[@event_id] if $game_map.events[@event_id]
$game_map.events[@event_id].erase $game_map.events[@event_id].erase
$PokemonMap&.addErasedEvent(@event_id) $PokemonMap.addErasedEvent(@event_id) if $PokemonMap
end end
@index += 1 @index += 1
return true return true
@@ -324,8 +325,8 @@ class Interpreter
mapid = @map_id if mapid < 0 mapid = @map_id if mapid < 0
old_value = $game_self_switches[[mapid, eventid, switch_name]] old_value = $game_self_switches[[mapid, eventid, switch_name]]
$game_self_switches[[mapid, eventid, switch_name]] = value $game_self_switches[[mapid, eventid, switch_name]] = value
if value != old_value && $map_factory.hasMap?(mapid) if value != old_value && $MapFactory.hasMap?(mapid)
$map_factory.getMap(mapid, false).need_refresh = true $MapFactory.getMap(mapid, false).need_refresh = true
end end
end end
@@ -367,7 +368,7 @@ class Interpreter
end end
def pbGetPokemon(id) def pbGetPokemon(id)
return $player.party[pbGet(id)] return $Trainer.party[pbGet(id)]
end end
def pbSetEventTime(*arg) def pbSetEventTime(*arg)
@@ -376,30 +377,28 @@ class Interpreter
time = time.to_i time = time.to_i
pbSetSelfSwitch(@event_id, "A", true) pbSetSelfSwitch(@event_id, "A", true)
$PokemonGlobal.eventvars[[@map_id, @event_id]] = time $PokemonGlobal.eventvars[[@map_id, @event_id]] = time
arg.each do |otherevt| for otherevt in arg
pbSetSelfSwitch(otherevt, "A", true) pbSetSelfSwitch(otherevt, "A", true)
$PokemonGlobal.eventvars[[@map_id, otherevt]] = time $PokemonGlobal.eventvars[[@map_id, otherevt]] = time
end end
end end
# Used in boulder events. Allows an event to be pushed. # Used in boulder events. Allows an event to be pushed.
def pbPushThisEvent(strength = false) def pbPushThisEvent
event = get_self event = get_self
old_x = event.x old_x = event.x
old_y = event.y old_y = event.y
# Apply strict version of passable, which treats tiles that are passable # Apply strict version of passable, which treats tiles that are passable
# only from certain directions as fully impassible # only from certain directions as fully impassible
return if !event.can_move_in_direction?($game_player.direction, true) return if !event.can_move_in_direction?($game_player.direction, true)
$stats.strength_push_count += 1
case $game_player.direction case $game_player.direction
when 2 then event.move_down when 2 then event.move_down
when 4 then event.move_left when 4 then event.move_left
when 6 then event.move_right when 6 then event.move_right
when 8 then event.move_up when 8 then event.move_up
end end
$PokemonMap&.addMovedEvent(@event_id) $PokemonMap.addMovedEvent(@event_id) if $PokemonMap
if old_x != event.x || old_y != event.y if old_x != event.x || old_y != event.y
pbSEPlay("Strength push") if strength
$game_player.lock $game_player.lock
loop do loop do
Graphics.update Graphics.update
@@ -412,7 +411,7 @@ class Interpreter
end end
def pbPushThisBoulder def pbPushThisBoulder
pbPushThisEvent(true) if $PokemonMap.strengthUsed pbPushThisEvent if $PokemonMap.strengthUsed
return true return true
end end
@@ -427,14 +426,14 @@ class Interpreter
return true if $DEBUG && !GameData::TrainerType.exists?(symbol) return true if $DEBUG && !GameData::TrainerType.exists?(symbol)
tr_type = GameData::TrainerType.get(symbol).id tr_type = GameData::TrainerType.get(symbol).id
pbGlobalLock pbGlobalLock
pbPlayTrainerIntroBGM(tr_type) pbPlayTrainerIntroME(tr_type)
return true return true
end end
def pbTrainerEnd def pbTrainerEnd
pbGlobalUnlock pbGlobalUnlock
event = get_self event = get_self
event&.erase_route event.erase_route if event
end end
def setPrice(item, buy_price = -1, sell_price = -1) def setPrice(item, buy_price = -1, sell_price = -1)
@@ -442,9 +441,9 @@ class Interpreter
$game_temp.mart_prices[item] = [-1, -1] if !$game_temp.mart_prices[item] $game_temp.mart_prices[item] = [-1, -1] if !$game_temp.mart_prices[item]
$game_temp.mart_prices[item][0] = buy_price if buy_price > 0 $game_temp.mart_prices[item][0] = buy_price if buy_price > 0
if sell_price >= 0 # 0=can't sell if sell_price >= 0 # 0=can't sell
$game_temp.mart_prices[item][1] = sell_price $game_temp.mart_prices[item][1] = sell_price * 2
elsif buy_price > 0 else
$game_temp.mart_prices[item][1] = buy_price / Settings::ITEM_SELL_PRICE_DIVISOR $game_temp.mart_prices[item][1] = buy_price if buy_price > 0
end end
end end

View File

@@ -51,7 +51,7 @@ class Interpreter
when 134 then return command_134 # Change Save Access when 134 then return command_134 # Change Save Access
when 135 then return command_135 # Change Menu Access when 135 then return command_135 # Change Menu Access
when 136 then return command_136 # Change Encounter when 136 then return command_136 # Change Encounter
when 201 then return command_201 # Transfer Overrides when 201 then return command_201 # Transfer Player
when 202 then return command_202 # Set Event Location when 202 then return command_202 # Set Event Location
when 203 then return command_203 # Scroll Map when 203 then return command_203 # Scroll Map
when 204 then return command_204 # Change Map Settings when 204 then return command_204 # Change Map Settings
@@ -121,19 +121,16 @@ class Interpreter
def command_dummy def command_dummy
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * End Event # * End Event
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_end def command_end
@list = nil @list = nil
end_follower_overrides
# If main map event and event ID are valid, unlock event # If main map event and event ID are valid, unlock event
if @main && @event_id > 0 && $game_map.events[@event_id] if @main && @event_id > 0 && $game_map.events[@event_id]
$game_map.events[@event_id].unlock $game_map.events[@event_id].unlock
end end
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Command Skip # * Command Skip
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -144,7 +141,6 @@ class Interpreter
@index += 1 @index += 1
end end
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Command If # * Command If
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -155,7 +151,6 @@ class Interpreter
end end
return command_skip return command_skip
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Show Text # * Show Text
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -163,7 +158,7 @@ class Interpreter
return false if $game_temp.message_window_showing return false if $game_temp.message_window_showing
message = @list[@index].parameters[0] message = @list[@index].parameters[0]
message_end = "" message_end = ""
choices = nil commands = nil
number_input_variable = nil number_input_variable = nil
number_input_max_digits = nil number_input_max_digits = nil
# Check the next command(s) for things to add on to this text # Check the next command(s) for things to add on to this text
@@ -179,8 +174,8 @@ class Interpreter
when 101 # Show Text when 101 # Show Text
message_end = "\1" message_end = "\1"
when 102 # Show Choices when 102 # Show Choices
commands = @list[next_index].parameters
@index = next_index @index = next_index
choices = setup_choices(@list[@index].parameters)
when 103 # Input Number when 103 # Input Number
number_input_variable = @list[next_index].parameters[0] number_input_variable = @list[next_index].parameters[0]
number_input_max_digits = @list[next_index].parameters[1] number_input_max_digits = @list[next_index].parameters[1]
@@ -190,11 +185,15 @@ class Interpreter
end end
# Translate the text # Translate the text
message = _MAPINTL($game_map.map_id, message) message = _MAPINTL($game_map.map_id, message)
# Display the text, with choices/number choosing if appropriate # Display the text, with commands/number choosing if appropriate
@message_waiting = true # Lets parallel process events work while a message is displayed @message_waiting = true # Lets parallel process events work while a message is displayed
if choices if commands
command = pbMessage(message + message_end, choices[0], choices[1]) cmd_texts = []
@branch[@list[@index].indent] = choices[2][command] || command for cmd in commands[0]
cmd_texts.push(_MAPINTL($game_map.map_id, cmd))
end
command = pbMessage(message + message_end, cmd_texts, commands[1])
@branch[@list[@index].indent] = command
elsif number_input_variable elsif number_input_variable
params = ChooseNumberParams.new params = ChooseNumberParams.new
params.setMaxDigits(number_input_max_digits) params.setMaxDigits(number_input_max_digits)
@@ -207,118 +206,37 @@ class Interpreter
@message_waiting = false @message_waiting = false
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Show Choices # * Show Choices
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_102 def command_102
choices = setup_choices(@list[@index].parameters)
@message_waiting = true @message_waiting = true
command = pbShowCommands(nil, choices[0], choices[1]) command = pbShowCommands(nil, @list[@index].parameters[0], @list[@index].parameters[1])
@message_waiting = false @message_waiting = false
@branch[@list[@index].indent] = choices[2][command] || command @branch[@list[@index].indent] = command
Input.update # Must call Input.update again to avoid extra triggers Input.update # Must call Input.update again to avoid extra triggers
return true return true
end end
def setup_choices(params)
# Get initial options
choices = params[0].clone
cancel_index = params[1]
# Clone @list so the original isn't modified
@list = Marshal.load(Marshal.dump(@list))
# Get more choices
@choice_branch_index = 4
ret = add_more_choices(choices, cancel_index, @index + 1, @list[@index].indent)
# Rename choices
ret[0].each_with_index { |choice, i| ret[0][i] = @renamed_choices[i] if @renamed_choices[i] }
@renamed_choices.clear
# Remove hidden choices
ret[2] = Array.new(ret[0].length) { |i| i }
@hidden_choices.each_with_index do |condition, i|
next if !condition
ret[0][i] = nil
ret[2][i] = nil
end
ret[0].compact!
ret[2].compact!
@hidden_choices.clear
# Translate choices
ret[0].map! { |ch| _MAPINTL($game_map.map_id, ch) }
return ret
end
def add_more_choices(choices, cancel_index, choice_index, indent)
# Find index of next command after the current Show Choices command
loop do
break if @list[choice_index].indent == indent && ![402, 403, 404].include?(@list[choice_index].code)
choice_index += 1
end
next_cmd = @list[choice_index]
# If the next command isn't another Show Choices, we're done
return [choices, cancel_index] if next_cmd.code != 102
# Add more choices
old_length = choices.length
choices += next_cmd.parameters[0]
# Update cancel option
if next_cmd.parameters[1] == 5 # Branch
cancel_index = choices.length + 1
@choice_branch_index = cancel_index - 1
elsif next_cmd.parameters[1] > 0 # A choice
cancel_index = old_length + next_cmd.parameters[1]
@choice_branch_index = -1
end
# Update first Show Choices command to include all options and result of cancelling
@list[@index].parameters[0] = choices
@list[@index].parameters[1] = cancel_index
# Find the "When" lines for this Show Choices command and update their index parameter
temp_index = choice_index + 1
loop do
break if @list[temp_index].indent == indent && ![402, 403, 404].include?(@list[temp_index].code)
if @list[temp_index].code == 402 && @list[temp_index].indent == indent
@list[temp_index].parameters[0] += old_length
end
temp_index += 1
end
# Delete the "Show Choices" line
@list.delete(next_cmd)
# Find more choices to add
return add_more_choices(choices, cancel_index, choice_index + 1, indent)
end
def hide_choice(number, condition = true)
@hidden_choices[number - 1] = condition
end
def rename_choice(number, new_name, condition = true)
return if !condition || nil_or_empty?(new_name)
@renamed_choices[number - 1] = new_name
end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * When [**] # * When [**]
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_402 def command_402
# @parameters[0] is 0/1/2/3 for Choice 1/2/3/4 respectively
if @branch[@list[@index].indent] == @parameters[0] if @branch[@list[@index].indent] == @parameters[0]
@branch.delete(@list[@index].indent) @branch.delete(@list[@index].indent)
return true return true
end end
return command_skip return command_skip
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * When Cancel # * When Cancel
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_403 def command_403
# @parameters[0] is 4 for "Branch" if @branch[@list[@index].indent] == 4
if @branch[@list[@index].indent] == @choice_branch_index
@branch.delete(@list[@index].indent) @branch.delete(@list[@index].indent)
return true return true
end end
return command_skip return command_skip
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Input Number # * Input Number
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -333,7 +251,6 @@ class Interpreter
@message_waiting = false @message_waiting = false
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Text Options # * Change Text Options
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -343,22 +260,26 @@ class Interpreter
$game_system.message_frame = @parameters[1] $game_system.message_frame = @parameters[1]
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Button Input Processing # * Button Input Processing
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def pbButtonInputProcessing(variable_number = 0, timeout_frames = 0) def pbButtonInputProcessing(variable_number = 0, timeout_frames = 0)
ret = 0 ret = 0
timer_start = System.uptime timer = timeout_frames * Graphics.frame_rate / 20
loop do loop do
Graphics.update Graphics.update
Input.update Input.update
pbUpdateSceneMap pbUpdateSceneMap
# Check for input and break if there is one # Check for input and break if there is one
(1..18).each { |i| ret = i if Input.trigger?(i) } for i in 1..18
ret = i if Input.trigger?(i)
end
break if ret != 0 break if ret != 0
# Break if the timer runs out # Count down the timer and break if it runs out
break if timeout_frames > 0 && System.uptime - timer_start >= timeout_frames / 20.0 if timeout_frames > 0
timer -= 1
break if timer <= 0
end
end end
Input.update Input.update
if variable_number && variable_number > 0 if variable_number && variable_number > 0
@@ -376,16 +297,13 @@ class Interpreter
@index += 1 @index += 1
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Wait # * Wait
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_106 def command_106
@wait_count = @parameters[0] / 20.0 @wait_count = @parameters[0] * Graphics.frame_rate / 20
@wait_start = System.uptime
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Conditional Branch # * Conditional Branch
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -400,22 +318,8 @@ class Interpreter
result = ($game_switches[@parameters[1]] == (@parameters[2] == 0)) result = ($game_switches[@parameters[1]] == (@parameters[2] == 0))
end end
when 1 # variable when 1 # variable
variable1_name = $data_system.variables[@parameters[1]]
if variable1_name && variable1_name[/^s\:/]
value1 = eval($~.post_match)
else
value1 = $game_variables[@parameters[1]] value1 = $game_variables[@parameters[1]]
end value2 = (@parameters[2] == 0) ? @parameters[3] : $game_variables[@parameters[3]]
if @parameters[2] == 0
value2 = @parameters[3]
else
variable2_name = $data_system.variables[@parameters[3]]
if variable2_name && variable2_name[/^s\:/]
value2 = eval($~.post_match)
else
value2 = $game_variables[@parameters[3]]
end
end
case @parameters[4] case @parameters[4]
when 0 then result = (value1 == value2) when 0 then result = (value1 == value2)
when 1 then result = (value1 >= value2) when 1 then result = (value1 >= value2)
@@ -430,8 +334,8 @@ class Interpreter
result = ($game_self_switches[key] == (@parameters[2] == 0)) result = ($game_self_switches[key] == (@parameters[2] == 0))
end end
when 3 # timer when 3 # timer
if $game_system.timer_start if $game_system.timer_working
sec = $game_system.timer sec = $game_system.timer / Graphics.frame_rate
result = (@parameters[2] == 0) ? (sec >= @parameters[1]) : (sec <= @parameters[1]) result = (@parameters[2] == 0) ? (sec >= @parameters[1]) : (sec <= @parameters[1])
end end
# when 4, 5 # actor, enemy # when 4, 5 # actor, enemy
@@ -439,7 +343,7 @@ class Interpreter
character = get_character(@parameters[1]) character = get_character(@parameters[1])
result = (character.direction == @parameters[2]) if character result = (character.direction == @parameters[2]) if character
when 7 # gold when 7 # gold
gold = $player.money gold = $Trainer.money
result = (@parameters[2] == 0) ? (gold >= @parameters[1]) : (gold <= @parameters[1]) result = (@parameters[2] == 0) ? (gold >= @parameters[1]) : (gold <= @parameters[1])
# when 8, 9, 10 # item, weapon, armor # when 8, 9, 10 # item, weapon, armor
when 11 # button when 11 # button
@@ -455,7 +359,6 @@ class Interpreter
end end
return command_skip return command_skip
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Else # * Else
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -466,14 +369,12 @@ class Interpreter
end end
return command_skip return command_skip
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Loop # * Loop
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_112 def command_112
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Repeat Above # * Repeat Above
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -484,7 +385,6 @@ class Interpreter
return true if @list[@index].indent == indent return true if @list[@index].indent == indent
end end
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Break Loop # * Break Loop
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -501,7 +401,6 @@ class Interpreter
end end
end end
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Exit Event Processing # * Exit Event Processing
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -509,19 +408,17 @@ class Interpreter
command_end command_end
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Erase Event # * Erase Event
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_116 def command_116
if @event_id > 0 if @event_id > 0
$game_map.events[@event_id]&.erase $game_map.events[@event_id].erase if $game_map.events[@event_id]
$PokemonMap&.addErasedEvent(@event_id) $PokemonMap.addErasedEvent(@event_id) if $PokemonMap
end end
@index += 1 @index += 1
return false return false
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Call Common Event # * Call Common Event
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -533,14 +430,12 @@ class Interpreter
end end
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Label # * Label
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_118 def command_118
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Jump to Label # * Jump to Label
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -559,13 +454,12 @@ class Interpreter
temp_index += 1 temp_index += 1
end end
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Control Switches # * Control Switches
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_121 def command_121
should_refresh = false should_refresh = false
(@parameters[0]..@parameters[1]).each do |i| for i in @parameters[0]..@parameters[1]
next if $game_switches[i] == (@parameters[2] == 0) next if $game_switches[i] == (@parameters[2] == 0)
$game_switches[i] = (@parameters[2] == 0) $game_switches[i] = (@parameters[2] == 0)
should_refresh = true should_refresh = true
@@ -574,7 +468,6 @@ class Interpreter
$game_map.need_refresh = true if should_refresh $game_map.need_refresh = true if should_refresh
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Control Variables # * Control Variables
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -603,43 +496,42 @@ class Interpreter
when 7 # other when 7 # other
case @parameters[4] case @parameters[4]
when 0 then value = $game_map.map_id # map ID when 0 then value = $game_map.map_id # map ID
when 1 then value = $player.pokemon_count # party members when 1 then value = $Trainer.pokemon_party.length # party members
when 2 then value = $player.money # gold when 2 then value = $Trainer.money # gold
when 3 then value = $stats.distance_moved # steps # when 3 # steps
when 4 then value = $stats.play_time # play time when 4 then value = Graphics.frame_count / Graphics.frame_rate # play time
when 5 then value = $game_system.timer # timer when 5 then value = $game_system.timer / Graphics.frame_rate # timer
when 6 then value = $game_system.save_count # save count when 6 then value = $game_system.save_count # save count
end end
end end
# Apply value and operation to all specified game variables # Apply value and operation to all specified game variables
(@parameters[0]..@parameters[1]).each do |i| for i in @parameters[0]..@parameters[1]
case @parameters[2] case @parameters[2]
when 0 # set when 0 # set
next if $game_variables[i] == value next if $game_variables[i] == value
$game_variables[i] = value $game_variables[i] = value
when 1 # add when 1 # add
next if $game_variables[i] >= 99_999_999 next if $game_variables[i] >= 99999999
$game_variables[i] += value $game_variables[i] += value
when 2 # subtract when 2 # subtract
next if $game_variables[i] <= -99_999_999 next if $game_variables[i] <= -99999999
$game_variables[i] -= value $game_variables[i] -= value
when 3 # multiply when 3 # multiply
next if value == 1 next if value == 1
$game_variables[i] *= value $game_variables[i] *= value
when 4 # divide when 4 # divide
next if [0, 1].include?(value) next if value == 1 || value == 0
$game_variables[i] /= value $game_variables[i] /= value
when 5 # remainder when 5 # remainder
next if [0, 1].include?(value) next if value == 1 || value == 0
$game_variables[i] %= value $game_variables[i] %= value
end end
$game_variables[i] = 99_999_999 if $game_variables[i] > 99_999_999 $game_variables[i] = 99999999 if $game_variables[i] > 99999999
$game_variables[i] = -99_999_999 if $game_variables[i] < -99_999_999 $game_variables[i] = -99999999 if $game_variables[i] < -99999999
$game_map.need_refresh = true $game_map.need_refresh = true
end end
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Control Self Switch # * Control Self Switch
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -654,23 +546,21 @@ class Interpreter
end end
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Control Timer # * Control Timer
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_124 def command_124
$game_system.timer_start = (@parameters[0] == 0) ? $stats.play_time : nil $game_system.timer_working = (@parameters[0] == 0)
$game_system.timer_duration = @parameters[1] if @parameters[0] == 0 $game_system.timer = @parameters[1] * Graphics.frame_rate if @parameters[0] == 0
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Gold # * Change Gold
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_125 def command_125
value = (@parameters[1] == 0) ? @parameters[2] : $game_variables[@parameters[2]] value = (@parameters[1] == 0) ? @parameters[2] : $game_variables[@parameters[2]]
value = -value if @parameters[0] == 1 # Decrease value = -value if @parameters[0] == 1 # Decrease
$player.money += value $Trainer.money += value
return true return true
end end
@@ -678,12 +568,11 @@ class Interpreter
def command_127; command_dummy; end # Change Weapons def command_127; command_dummy; end # Change Weapons
def command_128; command_dummy; end # Change Armor def command_128; command_dummy; end # Change Armor
def command_129; command_dummy; end # Change Party Member def command_129; command_dummy; end # Change Party Member
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Windowskin # * Change Windowskin
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_131 def command_131
Settings::SPEECH_WINDOWSKINS.length.times do |i| for i in 0...Settings::SPEECH_WINDOWSKINS.length
next if Settings::SPEECH_WINDOWSKINS[i] != @parameters[0] next if Settings::SPEECH_WINDOWSKINS[i] != @parameters[0]
$PokemonSystem.textskin = i $PokemonSystem.textskin = i
MessageConfig.pbSetSpeechFrame("Graphics/Windowskins/" + Settings::SPEECH_WINDOWSKINS[i]) MessageConfig.pbSetSpeechFrame("Graphics/Windowskins/" + Settings::SPEECH_WINDOWSKINS[i])
@@ -691,7 +580,6 @@ class Interpreter
end end
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Battle BGM # * Change Battle BGM
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -699,12 +587,13 @@ class Interpreter
($PokemonGlobal.nextBattleBGM = @parameters[0]) ? @parameters[0].clone : nil ($PokemonGlobal.nextBattleBGM = @parameters[0]) ? @parameters[0].clone : nil
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Battle End ME # * Change Battle End ME
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_133; command_dummy; end def command_133
($PokemonGlobal.nextBattleME = @parameters[0]) ? @parameters[0].clone : nil
return true
end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Save Access # * Change Save Access
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -712,7 +601,6 @@ class Interpreter
$game_system.save_disabled = (@parameters[0] == 0) $game_system.save_disabled = (@parameters[0] == 0)
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Menu Access # * Change Menu Access
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -720,7 +608,6 @@ class Interpreter
$game_system.menu_disabled = (@parameters[0] == 0) $game_system.menu_disabled = (@parameters[0] == 0)
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Encounter # * Change Encounter
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -729,9 +616,8 @@ class Interpreter
$game_player.make_encounter_count $game_player.make_encounter_count
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Transfer Overrides # * Transfer Player
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_201 def command_201
return true if $game_temp.in_battle return true if $game_temp.in_battle
@@ -744,12 +630,13 @@ class Interpreter
$game_temp.player_new_map_id = @parameters[1] $game_temp.player_new_map_id = @parameters[1]
$game_temp.player_new_x = @parameters[2] $game_temp.player_new_x = @parameters[2]
$game_temp.player_new_y = @parameters[3] $game_temp.player_new_y = @parameters[3]
$game_temp.player_new_direction = @parameters[4]
else # Appoint with variables else # Appoint with variables
$game_temp.player_new_map_id = $game_variables[@parameters[1]] $game_temp.player_new_map_id = $game_variables[@parameters[1]]
$game_temp.player_new_x = $game_variables[@parameters[2]] $game_temp.player_new_x = $game_variables[@parameters[2]]
$game_temp.player_new_y = $game_variables[@parameters[3]] $game_temp.player_new_y = $game_variables[@parameters[3]]
end
$game_temp.player_new_direction = @parameters[4] $game_temp.player_new_direction = @parameters[4]
end
@index += 1 @index += 1
# If transition happens with a fade, do the fade # If transition happens with a fade, do the fade
if @parameters[5] == 0 if @parameters[5] == 0
@@ -759,7 +646,6 @@ class Interpreter
end end
return false return false
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Set Event Location # * Set Event Location
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -768,10 +654,9 @@ class Interpreter
character = get_character(@parameters[0]) character = get_character(@parameters[0])
return true if character.nil? return true if character.nil?
# Move the character # Move the character
case @parameters[1] if @parameters[1] == 0 # Direct appointment
when 0 # Direct appointment
character.moveto(@parameters[2], @parameters[3]) character.moveto(@parameters[2], @parameters[3])
when 1 # Appoint with variables elsif @parameters[1] == 1 # Appoint with variables
character.moveto($game_variables[@parameters[2]], $game_variables[@parameters[3]]) character.moveto($game_variables[@parameters[2]], $game_variables[@parameters[3]])
else # Exchange with another event else # Exchange with another event
character2 = get_character(@parameters[2]) character2 = get_character(@parameters[2])
@@ -791,7 +676,6 @@ class Interpreter
end end
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Scroll Map # * Scroll Map
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -801,7 +685,6 @@ class Interpreter
$game_map.start_scroll(@parameters[0], @parameters[1], @parameters[2]) $game_map.start_scroll(@parameters[0], @parameters[1], @parameters[2])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Map Settings # * Change Map Settings
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -824,38 +707,29 @@ class Interpreter
end end
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Fog Color Tone # * Change Fog Color Tone
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_205 def command_205
$game_map.start_fog_tone_change(@parameters[0], @parameters[1]) $game_map.start_fog_tone_change(@parameters[0], @parameters[1] * Graphics.frame_rate / 20)
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Fog Opacity # * Change Fog Opacity
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_206 def command_206
$game_map.start_fog_opacity_change(@parameters[0], @parameters[1]) $game_map.start_fog_opacity_change(@parameters[0], @parameters[1] * Graphics.frame_rate / 20)
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Show Animation # * Show Animation
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_207 def command_207
character = get_character(@parameters[0]) character = get_character(@parameters[0])
if @follower_animation
character = Followers.get(@follower_animation_id)
@follower_animation = false
@follower_animation_id = nil
end
return true if character.nil? return true if character.nil?
character.animation_id = @parameters[1] character.animation_id = @parameters[1]
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Transparent Flag # * Change Transparent Flag
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -863,22 +737,15 @@ class Interpreter
$game_player.transparent = (@parameters[0] == 0) $game_player.transparent = (@parameters[0] == 0)
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Set Move Route # * Set Move Route
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_209 def command_209
character = get_character(@parameters[0]) character = get_character(@parameters[0])
if @follower_move_route
character = Followers.get(@follower_move_route_id)
@follower_move_route = false
@follower_move_route_id = nil
end
return true if character.nil? return true if character.nil?
character.force_move_route(@parameters[1]) character.force_move_route(@parameters[1])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Wait for Move's Completion # * Wait for Move's Completion
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -886,7 +753,6 @@ class Interpreter
@move_route_waiting = true if !$game_temp.in_battle @move_route_waiting = true if !$game_temp.in_battle
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Prepare for Transition # * Prepare for Transition
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -895,7 +761,6 @@ class Interpreter
Graphics.freeze Graphics.freeze
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Execute Transition # * Execute Transition
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -906,31 +771,27 @@ class Interpreter
@index += 1 @index += 1
return false return false
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Screen Color Tone # * Change Screen Color Tone
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_223 def command_223
$game_screen.start_tone_change(@parameters[0], @parameters[1]) $game_screen.start_tone_change(@parameters[0], @parameters[1] * Graphics.frame_rate / 20)
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Screen Flash # * Screen Flash
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_224 def command_224
$game_screen.start_flash(@parameters[0], @parameters[1]) $game_screen.start_flash(@parameters[0], @parameters[1] * Graphics.frame_rate / 20)
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Screen Shake # * Screen Shake
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_225 def command_225
$game_screen.start_shake(@parameters[0], @parameters[1], @parameters[2]) $game_screen.start_shake(@parameters[0], @parameters[1], @parameters[2] * Graphics.frame_rate / 20)
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Show Picture # * Show Picture
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -947,7 +808,6 @@ class Interpreter
x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9]) x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Move Picture # * Move Picture
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -960,12 +820,10 @@ class Interpreter
x = $game_variables[@parameters[4]] x = $game_variables[@parameters[4]]
y = $game_variables[@parameters[5]] y = $game_variables[@parameters[5]]
end end
$game_screen.pictures[number].move(@parameters[1], @parameters[2], x, y, $game_screen.pictures[number].move(@parameters[1] * Graphics.frame_rate / 20,
@parameters[6], @parameters[7], @parameters[2], x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9])
@parameters[8], @parameters[9])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Rotate Picture # * Rotate Picture
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -974,16 +832,15 @@ class Interpreter
$game_screen.pictures[number].rotate(@parameters[1]) $game_screen.pictures[number].rotate(@parameters[1])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Change Picture Color Tone # * Change Picture Color Tone
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_234 def command_234
number = @parameters[0] + ($game_temp.in_battle ? 50 : 0) number = @parameters[0] + ($game_temp.in_battle ? 50 : 0)
$game_screen.pictures[number].start_tone_change(@parameters[1], @parameters[2]) $game_screen.pictures[number].start_tone_change(@parameters[1],
@parameters[2] * Graphics.frame_rate / 20)
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Erase Picture # * Erase Picture
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -992,7 +849,6 @@ class Interpreter
$game_screen.pictures[number].erase $game_screen.pictures[number].erase
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Set Weather Effects # * Set Weather Effects
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1000,7 +856,6 @@ class Interpreter
$game_screen.weather(@parameters[0], @parameters[1], @parameters[2]) $game_screen.weather(@parameters[0], @parameters[1], @parameters[2])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Play BGM # * Play BGM
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1008,7 +863,6 @@ class Interpreter
pbBGMPlay(@parameters[0]) pbBGMPlay(@parameters[0])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Fade Out BGM # * Fade Out BGM
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1016,7 +870,6 @@ class Interpreter
pbBGMFade(@parameters[0]) pbBGMFade(@parameters[0])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Play BGS # * Play BGS
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1024,7 +877,6 @@ class Interpreter
pbBGSPlay(@parameters[0]) pbBGSPlay(@parameters[0])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Fade Out BGS # * Fade Out BGS
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1032,7 +884,6 @@ class Interpreter
pbBGSFade(@parameters[0]) pbBGSFade(@parameters[0])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Memorize BGM/BGS # * Memorize BGM/BGS
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1041,7 +892,6 @@ class Interpreter
$game_system.bgs_memorize $game_system.bgs_memorize
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Restore BGM/BGS # * Restore BGM/BGS
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1050,7 +900,6 @@ class Interpreter
$game_system.bgs_restore $game_system.bgs_restore
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Play ME # * Play ME
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1058,7 +907,6 @@ class Interpreter
pbMEPlay(@parameters[0]) pbMEPlay(@parameters[0])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Play SE # * Play SE
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1066,7 +914,6 @@ class Interpreter
pbSEPlay(@parameters[0]) pbSEPlay(@parameters[0])
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Stop SE # * Stop SE
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1080,25 +927,23 @@ class Interpreter
def command_602; command_if(1); end # If Escape def command_602; command_if(1); end # If Escape
def command_603; command_if(2); end # If Lose def command_603; command_if(2); end # If Lose
def command_302; command_dummy; end # Shop Processing def command_302; command_dummy; end # Shop Processing
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Name Input Processing # * Name Input Processing
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_303 def command_303
if $player if $Trainer
$player.name = pbEnterPlayerName(_INTL("Your name?"), 1, @parameters[1], $player.name) $Trainer.name = pbEnterPlayerName(_INTL("Your name?"), 1, @parameters[1], $Trainer.name)
return true return true
end end
if $game_actors && $data_actors && $data_actors[@parameters[0]] if $game_actors && $data_actors && $data_actors[@parameters[0]] != nil
$game_temp.battle_abort = true $game_temp.battle_abort = true
pbFadeOutIn do pbFadeOutIn {
sscene = PokemonEntryScene.new sscene = PokemonEntryScene.new
sscreen = PokemonEntry.new(sscene) sscreen = PokemonEntry.new(sscene)
$game_actors[@parameters[0]].name = sscreen.pbStartScreen( $game_actors[@parameters[0]].name = sscreen.pbStartScreen(
_INTL("Enter {1}'s name.", $game_actors[@parameters[0]].name), _INTL("Enter {1}'s name.", $game_actors[@parameters[0]].name),
1, @parameters[1], $game_actors[@parameters[0]].name 1, @parameters[1], $game_actors[@parameters[0]].name)
) }
end
end end
return true return true
end end
@@ -1106,18 +951,11 @@ class Interpreter
def command_311; command_dummy; end # Change HP def command_311; command_dummy; end # Change HP
def command_312; command_dummy; end # Change SP def command_312; command_dummy; end # Change SP
def command_313; command_dummy; end # Change State def command_313; command_dummy; end # Change State
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Recover All # * Recover All
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_314 def command_314
if @parameters[0] == 0 $Trainer.heal_party if @parameters[0] == 0
if Settings::HEAL_STORED_POKEMON # No need to heal stored Pokémon
$player.heal_party
else
pbEachPokemon { |pkmn, box| pkmn.heal } # Includes party Pokémon
end
end
return true return true
end end
@@ -1139,7 +977,6 @@ class Interpreter
def command_338; command_dummy; end # Deal Damage def command_338; command_dummy; end # Deal Damage
def command_339; command_dummy; end # Force Action def command_339; command_dummy; end # Force Action
def command_340; command_dummy; end # Abort Battle def command_340; command_dummy; end # Abort Battle
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Call Menu Screen # * Call Menu Screen
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1148,15 +985,15 @@ class Interpreter
@index += 1 @index += 1
return false return false
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Call Save Screen # * Call Save Screen
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_352 def command_352
pbFadeOutIn { UI::Save.new.main } scene = PokemonSave_Scene.new
screen = PokemonSaveScreen.new(scene)
screen.pbSaveScreen
return true return true
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Game Over # * Game Over
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1165,15 +1002,13 @@ class Interpreter
pbBGSFade(1.0) pbBGSFade(1.0)
pbFadeOutIn { pbStartOver(true) } pbFadeOutIn { pbStartOver(true) }
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Return to Title Screen # * Return to Title Screen
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def command_354 def command_354
$game_temp.title_screen_calling = true $game_temp.to_title = true
return false return false
end end
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# * Script # * Script
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@@ -1182,7 +1017,7 @@ class Interpreter
# Look for more script commands or a continuation of one, and add them to script # Look for more script commands or a continuation of one, and add them to script
loop do loop do
break if ![355, 655].include?(@list[@index + 1].code) break if ![355, 655].include?(@list[@index + 1].code)
script += @list[@index + 1].parameters[0] + "\n" script += @list[@index+1].parameters[0] + "\n"
@index += 1 @index += 1
end end
# Run the script # Run the script

View File

@@ -13,15 +13,21 @@ class Event
end end
# Removes an event handler procedure from the event. # Removes an event handler procedure from the event.
def -(other) def -(method)
@callbacks.delete(other) for i in 0...@callbacks.length
next if @callbacks[i]!=method
@callbacks.delete_at(i)
break
end
return self return self
end end
# Adds an event handler procedure from the event. # Adds an event handler procedure from the event.
def +(other) def +(method)
return self if @callbacks.include?(other) for i in 0...@callbacks.length
@callbacks.push(other) return self if @callbacks[i]==method
end
@callbacks.push(method)
return self return self
end end
@@ -38,13 +44,13 @@ class Event
# proc { |sender,params| } where params is an array of the other parameters, and # proc { |sender,params| } where params is an array of the other parameters, and
# proc { |sender,arg0,arg1,...| } # proc { |sender,arg0,arg1,...| }
def trigger(*arg) def trigger(*arg)
arglist = arg[1, arg.length] arglist = arg[1,arg.length]
@callbacks.each do |callback| for callback in @callbacks
if callback.arity > 2 && arg.length == callback.arity if callback.arity>2 && arg.length==callback.arity
# Retrofitted for callbacks that take three or more arguments # Retrofitted for callbacks that take three or more arguments
callback.call(*arg) callback.call(*arg)
else else
callback.call(arg[0], arglist) callback.call(arg[0],arglist)
end end
end end
end end
@@ -53,164 +59,16 @@ class Event
# by the code where the event occurred. The first argument is the sender of # by the code where the event occurred. The first argument is the sender of
# the event, the other arguments are the event's parameters. # the event, the other arguments are the event's parameters.
def trigger2(*arg) def trigger2(*arg)
@callbacks.each do |callback| for callback in @callbacks
callback.call(*arg) callback.call(*arg)
end end
end end
end end
#=============================================================================== #===============================================================================
# Same as class Event, but each registered proc has a name (a symbol) so it can #
# be referenced individually.
#===============================================================================
class NamedEvent
def initialize
@callbacks = {}
end
# Adds an event handler procedure from the event.
def add(key, proc)
@callbacks[key] = proc
end
# Removes an event handler procedure from the event.
def remove(key)
@callbacks.delete(key)
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.
def trigger(*args)
@callbacks.each_value { |callback| callback.call(*args) }
end
end
#===============================================================================
# A class that stores code that can be triggered. Each piece of code has an
# associated ID, which can be anything that can be used as a key in a hash.
#=============================================================================== #===============================================================================
class HandlerHash class HandlerHash
def initialize
@hash = {}
end
def [](id)
return @hash[id] if id && @hash[id]
return nil
end
def keys
return @hash.keys
end
def add(id, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{id.inspect} has no valid handler (#{handler.inspect} was given)"
end
@hash[id] = handler || handlerBlock if id && !id.empty?
end
def copy(src, *dests)
handler = self[src]
return if !handler
dests.each { |dest| add(dest, handler) }
end
def remove(key)
@hash.delete(key)
end
def clear
@hash.clear
end
def each
@hash.each_pair { |key, value| yield key, value }
end
def keys
return @hash.keys.clone
end
# NOTE: The call does not pass id as a parameter to the proc/block.
def trigger(id, *args)
handler = self[id]
return handler&.call(*args)
end
end
#===============================================================================
# A stripped-down version of class HandlerHash which only deals with IDs that
# are symbols. Also contains an add_ifs hash for code that applies to multiple
# IDs (determined by its condition proc).
#===============================================================================
class HandlerHashSymbol
def initialize
@hash = {}
@add_ifs = {}
end
def [](sym)
sym = sym.id if !sym.is_a?(Symbol) && sym.respond_to?("id")
return @hash[sym] if sym && @hash[sym]
@add_ifs.each_value do |add_if|
return add_if[1] if add_if[0].call(sym)
end
return nil
end
def keys
return @hash.keys
end
def add(sym, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
end
@hash[sym] = handler || handlerBlock if sym
end
def addIf(sym, conditionProc, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "addIf call for #{sym} in #{self.class.name} has no valid handler (#{handler.inspect} was given)"
end
@add_ifs[sym] = [conditionProc, handler || handlerBlock]
end
def copy(src, *dests)
handler = self[src]
return if !handler
dests.each { |dest| add(dest, handler) }
end
def remove(key)
@hash.delete(key)
end
def clear
@hash.clear
@add_ifs.clear
end
def trigger(sym, *args)
sym = sym.id if !sym.is_a?(Symbol) && sym.respond_to?("id")
handler = self[sym]
return handler&.call(sym, *args)
end
end
#===============================================================================
# A specialised version of class HandlerHash which only deals with IDs that are
# constants in a particular class or module. That class or module must be
# defined when creating an instance of this class.
# Unused.
#===============================================================================
class HandlerHashEnum
def initialize(mod) def initialize(mod)
@mod = mod @mod = mod
@hash = {} @hash = {}
@@ -218,25 +76,6 @@ class HandlerHashEnum
@symbolCache = {} @symbolCache = {}
end end
# 'sym' can be an ID or symbol.
def [](sym)
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
@addIfs.each do |addif|
return addif[1] if addif[0].call(id)
end
end
return ret
end
def keys
return @hash.keys
end
def fromSymbol(sym) def fromSymbol(sym)
return sym unless sym.is_a?(Symbol) || sym.is_a?(String) return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
mod = Object.const_get(@mod) rescue nil mod = Object.const_get(@mod) rescue nil
@@ -250,8 +89,8 @@ class HandlerHashEnum
return ret if ret return ret if ret
mod = Object.const_get(@mod) rescue nil mod = Object.const_get(@mod) rescue nil
return nil if !mod return nil if !mod
mod.constants.each do |key| for key in mod.constants
next if mod.const_get(key) != sym next if mod.const_get(key)!=sym
ret = key.to_sym ret = key.to_sym
@symbolCache[sym] = ret @symbolCache[sym] = ret
break break
@@ -259,9 +98,15 @@ class HandlerHashEnum
return ret return ret
end end
# 'sym' can be an ID or symbol. def addIf(conditionProc,handler=nil,&handlerBlock)
def add(sym, handler = nil, &handlerBlock) if ![Proc,Hash].include?(handler.class) && !block_given?
if ![Proc, Hash].include?(handler.class) && !block_given? raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
end
@addIfs.push([conditionProc,handler || handlerBlock])
end
def add(sym,handler=nil,&handlerBlock) # 'sym' can be an ID or symbol
if ![Proc,Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)" raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
end end
id = fromSymbol(sym) id = fromSymbol(sym)
@@ -270,6 +115,126 @@ class HandlerHashEnum
@hash[symbol] = handler || handlerBlock if symbol @hash[symbol] = handler || handlerBlock if symbol
end 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
#===============================================================================
# A stripped-down version of class HandlerHash which only deals with symbols and
# doesn't care about whether those symbols actually relate to a defined thing.
#===============================================================================
class HandlerHash2
def initialize
@hash = {}
@add_ifs = []
end
def [](sym)
sym = sym.id if !sym.is_a?(Symbol) && sym.respond_to?("id")
return @hash[sym] if sym && @hash[sym]
for add_if in @add_ifs
return add_if[1] if add_if[0].call(sym)
end
return nil
end
def addIf(conditionProc, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
end
@add_ifs.push([conditionProc, handler || handlerBlock])
end
def add(sym, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
end
@hash[sym] = handler || handlerBlock if sym
end
def copy(src, *dests)
handler = self[src]
return if !handler
for dest in dests
self.add(dest, handler)
end
end
def clear
@hash.clear
end
def trigger(sym, *args)
sym = sym.id if !sym.is_a?(Symbol) && sym.respond_to?("id")
handler = self[sym]
return (handler) ? handler.call(sym, *args) : nil
end
end
#===============================================================================
# An even more stripped down version of class HandlerHash which just takes
# hashes with keys, no matter what the keys are.
#===============================================================================
class HandlerHashBasic
def initialize
@ordered_keys = []
@hash = {}
@addIfs = []
end
def [](entry)
ret = nil
ret = @hash[entry] if entry && @hash[entry]
unless ret
for addif in @addIfs
return addif[1] if addif[0].call(entry)
end
end
return ret
end
def each
@ordered_keys.each { |key| yield key, @hash[key] }
end
def add(entry, handler = nil, &handlerBlock)
if ![Proc,Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{entry.inspect} has no valid handler (#{handler.inspect} was given)"
end
return if !entry || entry.empty?
@ordered_keys.push(entry) if !@ordered_keys.include?(entry)
@hash[entry] = handler || handlerBlock
end
def addIf(conditionProc, handler = nil, &handlerBlock) def addIf(conditionProc, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given? if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)" raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
@@ -285,26 +250,26 @@ class HandlerHashEnum
def clear def clear
@hash.clear @hash.clear
@addIfs.clear @ordered_keys.clear
end end
def trigger(sym, *args) def trigger(entry, *args)
handler = self[sym] handler = self[entry]
return (handler) ? handler.call(fromSymbol(sym), *args) : nil return (handler) ? handler.call(*args) : nil
end end
end end
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
class SpeciesHandlerHash < HandlerHashSymbol class SpeciesHandlerHash < HandlerHash2
end end
class AbilityHandlerHash < HandlerHashSymbol class AbilityHandlerHash < HandlerHash2
end end
class ItemHandlerHash < HandlerHashSymbol class ItemHandlerHash < HandlerHash2
end end
class MoveHandlerHash < HandlerHashSymbol class MoveHandlerHash < HandlerHash2
end end

View File

@@ -1,173 +0,0 @@
#===============================================================================
# This module stores events that can happen during the game. A procedure can
# subscribe to an event by adding itself to the event. It will then be called
# whenever the event occurs. Existing events are:
#-------------------------------------------------------------------------------
# :on_game_map_setup - When a Game_Map is set up. Typically changes map data.
# :on_new_spriteset_map - When a Spriteset_Map is created. Adds more things to
# show in the overworld.
# :on_frame_update - Once per frame. Various frame/time counters.
# :on_leave_map - When leaving a map. End weather/expired effects.
# :on_enter_map - Upon entering a new map. Set up new effects, end expired
# effects.
# :on_map_or_spriteset_change - Upon entering a new map or when spriteset was
# made. Show things on-screen.
#-------------------------------------------------------------------------------
# :on_player_change_direction - When the player turns in a different direction.
# :on_leave_tile - When any event or the player starts to move from a tile.
# :on_step_taken - When any event or the player finishes a step.
# :on_player_step_taken - When the player finishes a step/ends surfing, except
# as part of a move route. Step-based counters.
# :on_player_step_taken_can_transfer - When the player finishes a step/ends
# surfing, except as part of a move route. Step-based effects that can
# transfer the player elsewhere.
# :on_player_interact - When the player presses the Use button in the
# overworld.
#-------------------------------------------------------------------------------
# :on_trainer_load - When an NPCTrainer is generated (to battle against or as
# a registered partner). Various modifications to that trainer and their
# Pokémon.
# :on_wild_species_chosen - When a species/level have been chosen for a wild
# encounter. Changes the species/level (e.g. roamer, Poké Radar chain).
# :on_wild_pokemon_created - When a Pokemon object has been created for a wild
# encounter. Various modifications to that Pokémon.
# :on_calling_wild_battle - When a wild battle is called. Prevents that wild
# battle and instead starts a different kind of battle (e.g. Safari Zone).
# :on_start_battle - Just before a battle starts. Memorize/reset information
# about party Pokémon, which is used after battle for evolution checks.
# :on_end_battle - Just after a battle ends. Evolution checks, Pickup/Honey
# Gather, blacking out.
# :on_wild_battle_end - After a wild battle. Updates Poké Radar chain info.
#===============================================================================
module EventHandlers
@@events = {}
module_function
# Add a named callback for the given event.
def add(event, key, proc)
@@events[event] = NamedEvent.new if !@@events.has_key?(event)
@@events[event].add(key, proc)
end
# Remove a named callback from the given event.
def remove(event, key)
@@events[event]&.remove(key)
end
# Clear all callbacks for the given event.
def clear(key)
@@events[key]&.clear
end
# Trigger all callbacks from an Event if it has been defined.
def trigger(event, *args)
return @@events[event]&.trigger(*args)
end
end
#===============================================================================
# This module stores the contents of various menus. Each command in a menu is a
# hash of data (containing its name, relative order, code to run when chosen,
# etc.).
# Menus that use this module are:
#-------------------------------------------------------------------------------
# Pause menu
# Party screen main interact menu
# Pokégear main menu
# Options screen
# PC main menu
# Various debug menus (main, Pokémon, battle, battle Pokémon)
#===============================================================================
module MenuHandlers
@@handlers = {}
module_function
def add(menu, option, hash)
@@handlers[menu] = HandlerHash.new if !@@handlers.has_key?(menu)
@@handlers[menu].add(option, hash)
end
def remove(menu, option)
@@handlers[menu]&.remove(option)
end
def clear(menu)
@@handlers[menu]&.clear
end
def get(menu, option)
return @@handlers[menu][option]
end
def each(menu)
return if !@@handlers.has_key?(menu)
@@handlers[menu].each { |option, hash| yield option, hash }
end
def each_available(menu, *args)
return if !@@handlers.has_key?(menu)
options = @@handlers[menu]
keys = options.keys
sorted_keys = keys.sort_by { |option| options[option]["order"] || keys.index(option) }
sorted_keys.each do |option|
hash = options[option]
next if hash["condition"] && !hash["condition"].call(*args)
if hash["multi_options"]
extra_options = hash["multi_options"].call(*args)
if extra_options && extra_options.length > 0
if extra_options[0].is_a?(Array)
extra_options.each { |opt| yield *opt }
else
yield *extra_options
end
end
next
end
if hash["name"].is_a?(Proc)
name = hash["name"].call(*args)
else
name = _INTL(hash["name"])
end
yield option, hash, name
end
end
def call(menu, option, function, *args)
option_hash = @@handlers[menu][option]
return nil if !option_hash || !option_hash[function]
return option_hash[function].call(*args)
end
end
#===============================================================================
#
#===============================================================================
module UIActionHandlers
@@handlers = {}
module_function
def add(menu, action, hash)
@@handlers[menu] = HandlerHash.new if !@@handlers.has_key?(menu)
@@handlers[menu].add(action, hash)
end
def remove(menu, action)
@@handlers[menu]&.remove(action)
end
def clear(menu)
@@handlers[menu]&.clear
end
def get(menu, action)
return @@handlers[menu][action]
end
def each(menu)
return if !@@handlers.has_key?(menu)
@@handlers[menu].each { |action, hash| yield action, hash }
end
end

View File

@@ -0,0 +1,172 @@
#===============================================================================
# This module stores events that can happen during the game. A procedure can
# subscribe to an event by adding itself to the event. It will then be called
# whenever the event occurs.
#===============================================================================
module Events
@@OnMapCreate = Event.new
@@OnMapUpdate = Event.new
@@OnMapChange = Event.new
@@OnMapChanging = Event.new
@@OnMapSceneChange = Event.new
@@OnSpritesetCreate = Event.new
@@OnAction = Event.new
@@OnStepTaken = Event.new
@@OnLeaveTile = Event.new
@@OnStepTakenFieldMovement = Event.new
@@OnStepTakenTransferPossible = Event.new
@@OnStartBattle = Event.new
@@OnEndBattle = Event.new
@@OnWildPokemonCreate = Event.new
@@OnWildBattleOverride = Event.new
@@OnWildBattleEnd = Event.new
@@OnTrainerPartyLoad = Event.new
@@OnChangeDirection = Event.new
# Fires whenever a map is created. Event handler receives two parameters: the
# map (RPG::Map) and the tileset (RPG::Tileset)
def self.onMapCreate; @@OnMapCreate; end
def self.onMapCreate=(v); @@OnMapCreate = v; end
# Fires each frame during a map update.
def self.onMapUpdate; @@OnMapUpdate; end
def self.onMapUpdate=(v); @@OnMapUpdate = v; end
# Fires whenever one map is about to change to a different one. Event handler
# receives the new map ID and the Game_Map object representing the new map.
# When the event handler is called, $game_map still refers to the old map.
def self.onMapChanging; @@OnMapChanging; end
def self.onMapChanging=(v); @@OnMapChanging = v; end
# Fires whenever the player moves to a new map. Event handler receives the old
# map ID or 0 if none. Also fires when the first map of the game is loaded
def self.onMapChange; @@OnMapChange; end
def self.onMapChange=(v); @@OnMapChange = v; end
# Fires whenever the map scene is regenerated and soon after the player moves
# to a new map.
# Parameters:
# e[0] - Scene_Map object.
# e[1] - Whether the player just moved to a new map (either true or false). If
# false, some other code had called $scene.createSpritesets to
# regenerate the map scene without transferring the player elsewhere
def self.onMapSceneChange; @@OnMapSceneChange; end
def self.onMapSceneChange=(v); @@OnMapSceneChange = v; end
# Fires whenever a spriteset is created.
# Parameters:
# e[0] - Spriteset being created. e[0].map is the map associated with the
# spriteset (not necessarily the current map).
# e[1] - Viewport used for tilemap and characters
def self.onSpritesetCreate; @@OnSpritesetCreate; end
def self.onSpritesetCreate=(v); @@OnSpritesetCreate = v; end
# Triggers when the player presses the Action button on the map.
def self.onAction; @@OnAction; end
def self.onAction=(v); @@OnAction = v; end
# Fires whenever the player takes a step.
def self.onStepTaken; @@OnStepTaken; end
def self.onStepTaken=(v); @@OnStepTaken = v; end
# Fires whenever the player or another event leaves a tile.
# Parameters:
# e[0] - Event that just left the tile.
# e[1] - Map ID where the tile is located (not necessarily
# the current map). Use "$MapFactory.getMap(e[1])" to
# get the Game_Map object corresponding to that map.
# e[2] - X-coordinate of the tile
# e[3] - Y-coordinate of the tile
def self.onLeaveTile; @@OnLeaveTile; end
def self.onLeaveTile=(v); @@OnLeaveTile = v; end
# Fires whenever the player or another event enters a tile.
# Parameters:
# e[0] - Event that just entered a tile.
def self.onStepTakenFieldMovement; @@OnStepTakenFieldMovement; end
def self.onStepTakenFieldMovement=(v); @@OnStepTakenFieldMovement = v; end
# Fires whenever the player takes a step. The event handler may possibly move
# the player elsewhere.
# Parameters:
# e[0] - Array that contains a single boolean value. If an event handler moves
# the player to a new map, it should set this value to true. Other
# event handlers should check this parameter's value.
def self.onStepTakenTransferPossible; @@OnStepTakenTransferPossible; end
def self.onStepTakenTransferPossible=(v); @@OnStepTakenTransferPossible = v; end
def self.onStartBattle; @@OnStartBattle; end
def self.onStartBattle=(v); @@OnStartBattle = v; end
def self.onEndBattle; @@OnEndBattle; end
def self.onEndBattle=(v); @@OnEndBattle = v; end
# Triggers whenever a wild Pokémon is created
# Parameters:
# e[0] - Pokémon being created
def self.onWildPokemonCreate; @@OnWildPokemonCreate; end
def self.onWildPokemonCreate=(v); @@OnWildPokemonCreate = v; end
# Triggers at the start of a wild battle. Event handlers can provide their
# own wild battle routines to override the default behavior.
def self.onWildBattleOverride; @@OnWildBattleOverride; end
def self.onWildBattleOverride=(v); @@OnWildBattleOverride = v; end
# Triggers whenever a wild Pokémon battle ends
# Parameters:
# e[0] - Pokémon species
# e[1] - Pokémon level
# e[2] - Battle result (1-win, 2-loss, 3-escaped, 4-caught, 5-draw)
def self.onWildBattleEnd; @@OnWildBattleEnd; end
def self.onWildBattleEnd=(v); @@OnWildBattleEnd = v; end
# Triggers whenever an NPC trainer's Pokémon party is loaded
# Parameters:
# e[0] - Trainer
# e[1] - Items possessed by the trainer
# e[2] - Party
def self.onTrainerPartyLoad; @@OnTrainerPartyLoad; end
def self.onTrainerPartyLoad=(v); @@OnTrainerPartyLoad = v; end
# Fires whenever the player changes direction.
def self.onChangeDirection; @@OnChangeDirection; end
def self.onChangeDirection=(v); @@OnChangeDirection = v; end
end
#===============================================================================
#
#===============================================================================
def pbOnSpritesetCreate(spriteset,viewport)
Events.onSpritesetCreate.trigger(nil,spriteset,viewport)
end
#===============================================================================
# This module stores encounter-modifying events that can happen during the game.
# A procedure can subscribe to an event by adding itself to the event. It will
# then be called whenever the event occurs.
#===============================================================================
module EncounterModifier
@@procs = []
@@procsEnd = []
def self.register(p)
@@procs.push(p)
end
def self.registerEncounterEnd(p)
@@procsEnd.push(p)
end
def self.trigger(encounter)
for prc in @@procs
encounter = prc.call(encounter)
end
return encounter
end
def self.triggerEncounterEnd()
for prc in @@procsEnd
prc.call()
end
end
end

View File

@@ -1,62 +0,0 @@
#===============================================================================
# NOTE: Some Settings in here will be moved elsewhere eventually. They're all
# just gathered here while the new UI is being written.
#===============================================================================
module Settings
# :one, :adventure, :multiple
SAVE_SLOTS = :multiple
# Whether the main color of a move's name in the Fight menu in battle matches
# the pixel at coordinate (10,34) in cursor_fight.png for that move's type
# (true), or whether the move name's color is the default black (false).
BATTLE_MOVE_NAME_COLOR_FROM_GRAPHIC = true
# Whether "Town Map" will show as an option in the pause menu if the player
# has that item in the Bag and doesn't have a Pokégear.
SHOW_TOWN_MAP_IN_PAUSE_MENU = true
# The filename of a location sign graphic to be used if the map metadata for a
# map doesn't define one. Make this nil to use the default menu windowskin.
DEFAULT_LOCATION_SIGN_GRAPHIC = "Pt default"
# Assigns location sign graphics to text styles (numbers). These are used in
# class LocationWindow to display the text appropriately for the graphic being
# used. Style :none is reserved for the "no graphic" style. A filename may
# instead be an array of [filename, text base color, text shadow color].
LOCATION_SIGN_GRAPHIC_STYLES = {
:dp => [["DP", Color.new(72, 80, 72), Color.new(144, 160, 160)]],
:hgss => [["HGSS cave", Color.new(232, 232, 232), Color.new(120, 144, 160)],
["HGSS city", Color.new(56, 64, 72), Color.new(152, 152, 144)],
["HGSS default", Color.new(48, 64, 72), Color.new(144, 144, 96)],
["HGSS forest", Color.new(232, 232, 232), Color.new(120, 176, 144)],
["HGSS lake", Color.new(40, 48, 56), Color.new(104, 144, 192)],
["HGSS park", Color.new(40, 48, 56), Color.new(120, 136, 152)],
["HGSS route", Color.new(48, 64, 72), Color.new(136, 136, 104)],
["HGSS sea", Color.new(216, 240, 248), Color.new(24, 96, 144)],
["HGSS town", Color.new(48, 56, 64), Color.new(144, 120, 80)]],
:platinum => ["Pt cave", "Pt city", "Pt default", "Pt forest", "Pt lake",
"Pt park", "Pt route", "Pt sea", "Pt town"]
}
# Whether a move's power/type/category/etc. as shown in battle, the summary
# screen and the Move Reminder screen will appear as their calculated values
# (true) or their values from the PBS file moves.txt (false). For example, if
# this is true, Judgment's displayed type will depend on the Plate being held
# by the Pokémon that knows it.
SHOW_MODIFIED_MOVE_PROPERTIES = false
# Whether pressing Use in the Town Map will zoom it in to 200% and show a text
# pane on the right showing the selected point's description. The cursor can
# still be moved while zoomed in.
ENABLE_TOWN_MAP_ZOOM_IN_FOR_DETAILS = true
# Whether points in the Town Map can be marked.
ENABLE_TOWN_MAP_MARKING = true
# TODO: Allow renaming a Pokémon from the party screen/summary screen (not
# sure which). Gen 9 feature.
# TODO: Allow forgetting/remembering moves from the summary screen. Gen 9
# feature.
# TODO: Show usability party balls in the Bag. Maybe?
# TODO: Replace Run with Call in battle; don't have this depend on the Shadow
# type existing?
# TODO: Whether new items go at the top or bottom of its Bag pocket?
end

View File

@@ -4,7 +4,11 @@
# This class handles screen maintenance data, such as change in color tone, # This class handles screen maintenance data, such as change in color tone,
# flashing, etc. Refer to "$game_screen" for the instance of this class. # flashing, etc. Refer to "$game_screen" for the instance of this class.
#=============================================================================== #===============================================================================
class Game_Screen class Game_Screen
#-----------------------------------------------------------------------------
# * Public Instance Variables
#-----------------------------------------------------------------------------
attr_reader :brightness # brightness attr_reader :brightness # brightness
attr_reader :tone # color tone attr_reader :tone # color tone
attr_reader :flash_color # flash color attr_reader :flash_color # flash color
@@ -13,106 +17,120 @@ class Game_Screen
attr_reader :weather_type # weather type attr_reader :weather_type # weather type
attr_reader :weather_max # max number of weather sprites attr_reader :weather_max # max number of weather sprites
attr_accessor :weather_duration # ticks in which the weather should fade in attr_accessor :weather_duration # ticks in which the weather should fade in
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize def initialize
@brightness = 255 @brightness = 255
@fadeout_duration = 0
@fadein_duration = 0
@tone = Tone.new(0, 0, 0, 0) @tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0) @tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0 @tone_duration = 0
@tone_timer_start = nil
@flash_color = Color.new(0, 0, 0, 0) @flash_color = Color.new(0, 0, 0, 0)
@flash_duration = 0 @flash_duration = 0
@flash_timer_start = nil
@shake_power = 0 @shake_power = 0
@shake_speed = 0 @shake_speed = 0
@shake_duration = 0 @shake_duration = 0
@shake_direction = 1
@shake = 0 @shake = 0
@pictures = [nil] @pictures = [nil]
(1..100).each { |i| @pictures.push(Game_Picture.new(i)) } for i in 1..100
@weather_type = :None @pictures.push(Game_Picture.new(i))
end
@weather_type = 0
@weather_max = 0.0 @weather_max = 0.0
@weather_duration = 0 @weather_duration = 0
end end
#-----------------------------------------------------------------------------
# duration is time in 1/20ths of a second. # * Start Changing Color Tone
# tone : color tone
# duration : time
#-----------------------------------------------------------------------------
def start_tone_change(tone, duration) def start_tone_change(tone, duration)
if duration == 0
@tone = tone.clone
return
end
@tone_initial = @tone.clone
@tone_target = tone.clone @tone_target = tone.clone
@tone_duration = duration / 20.0 @tone_duration = duration
@tone_timer_start = $stats.play_time if @tone_duration == 0
@tone = @tone_target.clone
end end
end
# duration is time in 1/20ths of a second. #-----------------------------------------------------------------------------
# * Start Flashing
# color : color
# duration : time
#-----------------------------------------------------------------------------
def start_flash(color, duration) def start_flash(color, duration)
@flash_color = color.clone @flash_color = color.clone
@flash_initial_alpha = @flash_color.alpha @flash_duration = duration
@flash_duration = duration / 20.0
@flash_timer_start = $stats.play_time
end end
#-----------------------------------------------------------------------------
# duration is time in 1/20ths of a second. # * Start Shaking
# power : strength
# speed : speed
# duration : time
#-----------------------------------------------------------------------------
def start_shake(power, speed, duration) def start_shake(power, speed, duration)
@shake_power = power @shake_power = power
@shake_speed = speed @shake_speed = speed
@shake_duration = duration / 20.0 @shake_duration = duration
@shake_timer_start = $stats.play_time
end end
#-----------------------------------------------------------------------------
# duration is time in 1/20ths of a second. # * Set Weather
# type : type
# power : strength
# duration : time
#-----------------------------------------------------------------------------
def weather(type, power, duration) def weather(type, power, duration)
@weather_type = GameData::Weather.get(type).id @weather_type = GameData::Weather.get(type).id
@weather_max = (power + 1) * RPG::Weather::MAX_SPRITES / 10 @weather_max = (power + 1) * RPG::Weather::MAX_SPRITES / 10
@weather_duration = duration @weather_duration = duration # In 1/20ths of a seconds
end end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update def update
now = $stats.play_time if @fadeout_duration && @fadeout_duration>=1
if @tone_timer_start d = @fadeout_duration
@tone.red = lerp(@tone_initial.red, @tone_target.red, @tone_duration, @tone_timer_start, now) @brightness = (@brightness*(d-1))/d
@tone.green = lerp(@tone_initial.green, @tone_target.green, @tone_duration, @tone_timer_start, now) @fadeout_duration -= 1
@tone.blue = lerp(@tone_initial.blue, @tone_target.blue, @tone_duration, @tone_timer_start, now)
@tone.gray = lerp(@tone_initial.gray, @tone_target.gray, @tone_duration, @tone_timer_start, now)
if now - @tone_timer_start >= @tone_duration
@tone_initial = nil
@tone_timer_start = nil
end end
if @fadein_duration && @fadein_duration>=1
d = @fadein_duration
@brightness = (@brightness*(d-1)+255)/d
@fadein_duration -= 1
end end
if @flash_timer_start if @tone_duration>=1
@flash_color.alpha = lerp(@flash_initial_alpha, 0, @flash_duration, @flash_timer_start, now) d = @tone_duration
if now - @flash_timer_start >= @flash_duration @tone.red = (@tone.red*(d-1)+@tone_target.red)/d
@flash_initial_alpha = nil @tone.green = (@tone.green*(d-1)+@tone_target.green)/d
@flash_timer_start = nil @tone.blue = (@tone.blue*(d-1)+@tone_target.blue)/d
@tone.gray = (@tone.gray*(d-1)+@tone_target.gray)/d
@tone_duration -= 1
end end
if @flash_duration>=1
d = @flash_duration
@flash_color.alpha = @flash_color.alpha*(d-1)/d
@flash_duration -= 1
end end
if @shake_timer_start if @shake_duration>=1 || @shake!=0
delta_t = now - @shake_timer_start delta = (@shake_power*@shake_speed*@shake_direction)/10.0
movement_per_second = @shake_power * @shake_speed * 4 if @shake_duration<=1 && @shake*(@shake+delta)<0
limit = @shake_power * 2.5 # Maximum pixel displacement
phase = (delta_t * movement_per_second / limit).to_i % 4
case phase
when 0, 2
@shake = (movement_per_second * delta_t) % limit
@shake *= -1 if phase == 2
else
@shake = limit - ((movement_per_second * delta_t) % limit)
@shake *= -1 if phase == 3
end
if delta_t >= @shake_duration
@shake_phase = phase if !@shake_phase || phase == 1 || phase == 3
if phase != @shake_phase || @shake < 2
@shake_timer_start = nil
@shake = 0 @shake = 0
else
@shake += delta
end end
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 end
if $game_temp.in_battle if $game_temp.in_battle
(51..100).each { |i| @pictures[i].update } for i in 51..100
@pictures[i].update
end
else else
(1..50).each { |i| @pictures[i].update } for i in 1..50
@pictures[i].update
end
end end
end end
end end
@@ -120,15 +138,17 @@ end
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
def pbToneChangeAll(tone, duration) def pbToneChangeAll(tone,duration)
$game_screen.start_tone_change(tone, duration) $game_screen.start_tone_change(tone,duration*Graphics.frame_rate/20)
$game_screen.pictures.each { |picture| picture&.start_tone_change(tone, duration) } for picture in $game_screen.pictures
picture.start_tone_change(tone,duration*Graphics.frame_rate/20) if picture
end
end end
def pbFlash(color, frames) def pbShake(power,speed,frames)
$game_screen.start_flash(color, frames) $game_screen.start_shake(power,speed,frames*Graphics.frame_rate/20)
end end
def pbShake(power, speed, frames) def pbFlash(color,frames)
$game_screen.start_shake(power, speed, frames) $game_screen.start_flash(color,frames*Graphics.frame_rate/20)
end end

View File

@@ -5,83 +5,51 @@
# Refer to "$game_temp" for the instance of this class. # Refer to "$game_temp" for the instance of this class.
#=============================================================================== #===============================================================================
class Game_Temp class Game_Temp
# Flags requesting something to happen
attr_accessor :menu_calling # menu calling flag
attr_accessor :ready_menu_calling # ready menu calling flag
attr_accessor :debug_calling # debug calling flag
attr_accessor :interact_calling # EventHandlers.trigger(:on_player_interact) flag
attr_accessor :battle_abort # battle flag: interrupt (unused)
attr_accessor :title_screen_calling # return to title screen flag
attr_accessor :common_event_id # common event ID to start
attr_accessor :field_move_to_use
attr_accessor :field_move_user
# Flags indicating something is happening
attr_accessor :in_menu # menu is open
attr_accessor :in_storage # in-Pokémon storage flag
attr_accessor :in_battle # in-battle flag
attr_accessor :message_window_showing # message window showing attr_accessor :message_window_showing # message window showing
attr_accessor :ending_surf # jumping off surf base flag attr_accessor :common_event_id # common event ID
attr_accessor :surf_base_coords # [x, y] while jumping on/off, or nil attr_accessor :in_battle # in-battle flag
attr_accessor :in_mini_update # performing mini update flag attr_accessor :battle_abort # battle flag: interrupt
# Battle
attr_accessor :battleback_name # battleback file name attr_accessor :battleback_name # battleback file name
attr_accessor :force_single_battle # force next battle to be 1v1 flag attr_accessor :in_menu # menu is open
attr_accessor :waiting_trainer # [trainer, event ID] or nil attr_accessor :menu_beep # menu: play sound effect flag
attr_accessor :last_battle_record # record of actions in last recorded battle attr_accessor :menu_calling # menu calling flag
# Overrides transfers attr_accessor :debug_calling # debug calling flag
attr_accessor :player_transferring # player place movement flag attr_accessor :player_transferring # player place movement flag
attr_accessor :player_new_map_id # player destination: map ID attr_accessor :player_new_map_id # player destination: map ID
attr_accessor :player_new_x # player destination: x-coordinate attr_accessor :player_new_x # player destination: x-coordinate
attr_accessor :player_new_y # player destination: y-coordinate attr_accessor :player_new_y # player destination: y-coordinate
attr_accessor :player_new_direction # player destination: direction attr_accessor :player_new_direction # player destination: direction
attr_accessor :fly_destination # [map ID, x, y] or nil
# Transitions
attr_accessor :transition_processing # transition processing flag attr_accessor :transition_processing # transition processing flag
attr_accessor :transition_name # transition file name attr_accessor :transition_name # transition file name
attr_accessor :background_bitmap attr_accessor :to_title # return to title screen flag
attr_accessor :fadestate # for sprite hashes attr_accessor :fadestate # for sprite hashes
# Other attr_accessor :background_bitmap
attr_accessor :menu_beep # menu: play sound effect flag
attr_accessor :menu_last_choice # pause menu: index of last selection
attr_accessor :memorized_bgm # set when trainer intro BGM is played
attr_accessor :memorized_bgm_position # set when trainer intro BGM is played
attr_accessor :darkness_sprite # DarknessSprite or nil
attr_accessor :mart_prices attr_accessor :mart_prices
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize def initialize
# Flags requesting something to happen
@menu_calling = false
@ready_menu_calling = false
@debug_calling = false
@interact_calling = false
@battle_abort = false
@title_screen_calling = false
@common_event_id = 0
# Flags indicating something is happening
@in_menu = false
@in_storage = false
@in_battle = false
@message_window_showing = false @message_window_showing = false
@ending_surf = false @common_event_id = 0
@in_mini_update = false @in_battle = false
# Battle @battle_abort = false
@battleback_name = "" @battleback_name = ''
@force_single_battle = false @in_menu = false
# Overrides transfers @menu_beep = false
@menu_calling = false
@debug_calling = false
@player_transferring = false @player_transferring = false
@player_new_map_id = 0 @player_new_map_id = 0
@player_new_x = 0 @player_new_x = 0
@player_new_y = 0 @player_new_y = 0
@player_new_direction = 0 @player_new_direction = 0
# Transitions
@transition_processing = false @transition_processing = false
@transition_name = "" @transition_name = ""
@to_title = false
@fadestate = 0 @fadestate = 0
# Other @background_bitmap = nil
@menu_beep = false @message_window_showing = false
@memorized_bgm = nil @transition_processing = false
@memorized_bgm_position = 0
@menu_last_choice = 0
@mart_prices = {} @mart_prices = {}
end end

View File

@@ -5,20 +5,25 @@
# Refer to "$game_switches" for the instance of this class. # Refer to "$game_switches" for the instance of this class.
#=============================================================================== #===============================================================================
class Game_Switches class Game_Switches
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize def initialize
@data = [] @data = []
end end
#-----------------------------------------------------------------------------
# Get Switch # * Get Switch
# switch_id : switch ID # switch_id : switch ID
#-----------------------------------------------------------------------------
def [](switch_id) def [](switch_id)
return @data[switch_id] if switch_id <= 5000 && @data[switch_id] return @data[switch_id] if switch_id <= 5000 && @data[switch_id] != nil
return false return false
end end
#-----------------------------------------------------------------------------
# Set Switch # * Set Switch
# switch_id : switch ID # switch_id : switch ID
# value : ON (true) / OFF (false) # value : ON (true) / OFF (false)
#-----------------------------------------------------------------------------
def []=(switch_id, value) def []=(switch_id, value)
@data[switch_id] = value if switch_id <= 5000 @data[switch_id] = value if switch_id <= 5000
end end

View File

@@ -5,20 +5,25 @@
# Refer to "$game_variables" for the instance of this class. # Refer to "$game_variables" for the instance of this class.
#=============================================================================== #===============================================================================
class Game_Variables class Game_Variables
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize def initialize
@data = [] @data = []
end end
#-----------------------------------------------------------------------------
# Get Variable # * Get Variable
# variable_id : variable ID # variable_id : variable ID
#-----------------------------------------------------------------------------
def [](variable_id) def [](variable_id)
return @data[variable_id] if variable_id <= 5000 && !@data[variable_id].nil? return @data[variable_id] if variable_id <= 5000 && !@data[variable_id].nil?
return 0 return 0
end end
#-----------------------------------------------------------------------------
# Set Variable # * Set Variable
# variable_id : variable ID # variable_id : variable ID
# value : the variable's value # value : the variable's value
#-----------------------------------------------------------------------------
def []=(variable_id, value) def []=(variable_id, value)
@data[variable_id] = value if variable_id <= 5000 @data[variable_id] = value if variable_id <= 5000
end end

View File

@@ -5,19 +5,24 @@
# "Hash." Refer to "$game_self_switches" for the instance of this class. # "Hash." Refer to "$game_self_switches" for the instance of this class.
#=============================================================================== #===============================================================================
class Game_SelfSwitches class Game_SelfSwitches
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize def initialize
@data = {} @data = {}
end end
#-----------------------------------------------------------------------------
# Get Self Switch # * Get Self Switch
# key : key # key : key
#-----------------------------------------------------------------------------
def [](key) def [](key)
return @data[key] == true return (@data[key]==true) ? true : false
end end
#-----------------------------------------------------------------------------
# Set Self Switch # * Set Self Switch
# key : key # key : key
# value : ON (true) / OFF (false) # value : ON (true) / OFF (false)
#-----------------------------------------------------------------------------
def []=(key, value) def []=(key, value)
@data[key] = value @data[key] = value
end end

View File

@@ -1,15 +1,15 @@
#=============================================================================== #==============================================================================
# ** Game_System # ** Game_System
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# This class handles data surrounding the system. Backround music, etc. # This class handles data surrounding the system. Backround music, etc.
# is managed here as well. Refer to "$game_system" for the instance of # is managed here as well. Refer to "$game_system" for the instance of
# this class. # this class.
#=============================================================================== #==============================================================================
class Game_System class Game_System
attr_reader :map_interpreter # map event interpreter attr_reader :map_interpreter # map event interpreter
attr_reader :battle_interpreter # battle event interpreter attr_reader :battle_interpreter # battle event interpreter
attr_accessor :timer_start # $stats.play_time when timer was started, or nil attr_accessor :timer # timer
attr_accessor :timer_duration # Time (in seconds) the timer is initially set to attr_accessor :timer_working # timer working flag
attr_accessor :save_disabled # save forbidden attr_accessor :save_disabled # save forbidden
attr_accessor :menu_disabled # menu forbidden attr_accessor :menu_disabled # menu forbidden
attr_accessor :encounter_disabled # encounter forbidden attr_accessor :encounter_disabled # encounter forbidden
@@ -24,8 +24,8 @@ class Game_System
def initialize def initialize
@map_interpreter = Interpreter.new(0, true) @map_interpreter = Interpreter.new(0, true)
@battle_interpreter = Interpreter.new(0, false) @battle_interpreter = Interpreter.new(0, false)
@timer_start = nil @timer = 0
@timer_duration = 0 @timer_working = false
@save_disabled = false @save_disabled = false
@menu_disabled = false @menu_disabled = false
@encounter_disabled = false @encounter_disabled = false
@@ -33,88 +33,55 @@ class Game_System
@message_frame = 0 @message_frame = 0
@save_count = 0 @save_count = 0
@magic_number = 0 @magic_number = 0
@adventure_magic_number = rand(2**32)
@autoscroll_x_speed = 0 @autoscroll_x_speed = 0
@autoscroll_y_speed = 0 @autoscroll_y_speed = 0
@bgm_position = 0 @bgm_position = 0
@bgs_position = 0 @bgs_position = 0
end end
def adventure_magic_number ################################################################################
@adventure_magic_number ||= rand(2**32)
return @adventure_magic_number
end
def battle_bgm def bgm_play(bgm)
return (@battle_bgm) ? @battle_bgm : $data_system.battle_bgm
end
attr_writer :battle_bgm
def battle_end_me
return (@battle_end_me) ? @battle_end_me : $data_system.battle_end_me
end
attr_writer :battle_end_me
def windowskin_name
return $data_system.windowskin_name if @windowskin_name.nil?
return @windowskin_name
end
attr_writer :windowskin_name
def timer
return 0 if !@timer_start || !$stats
return @timer_duration - $stats.play_time + @timer_start
end
#-----------------------------------------------------------------------------
def bgm_play(bgm, track = nil)
old_pos = @bgm_position old_pos = @bgm_position
@bgm_position = 0 @bgm_position = 0
bgm_play_internal(bgm, 0, track) bgm_play_internal(bgm,0)
@bgm_position = old_pos @bgm_position = old_pos
end end
def bgm_play_internal2(name, volume, pitch, position, track = nil) # :nodoc: def bgm_play_internal2(name,volume,pitch,position) # :nodoc:
vol = volume vol = volume
vol *= $PokemonSystem.bgmvolume / 100.0 vol *= $PokemonSystem.bgmvolume/100.0
vol = vol.to_i vol = vol.to_i
begin begin
Audio.bgm_play(name, vol, pitch, position, track) Audio.bgm_play(name,vol,pitch,position)
rescue ArgumentError rescue ArgumentError
Audio.bgm_play(name, vol, pitch, 0, track) Audio.bgm_play(name,vol,pitch)
end end
end end
def bgm_play_internal(bgm, position, track = nil) # :nodoc: def bgm_play_internal(bgm,position) # :nodoc:
if !track || track == 0
@bgm_position = position if !@bgm_paused @bgm_position = position if !@bgm_paused
@playing_bgm = bgm&.clone @playing_bgm = (bgm==nil) ? nil : bgm.clone
end if bgm!=nil && bgm.name!=""
if bgm && bgm.name != "" if FileTest.audio_exist?("Audio/BGM/"+bgm.name)
if !@defaultBGM && FileTest.audio_exist?("Audio/BGM/" + bgm.name) bgm_play_internal2("Audio/BGM/"+bgm.name,
bgm_play_internal2("Audio/BGM/" + bgm.name, bgm.volume, bgm.pitch, @bgm_position, track) bgm.volume,bgm.pitch,@bgm_position) if !@defaultBGM
end end
else else
if !track || track == 0
@bgm_position = position if !@bgm_paused @bgm_position = position if !@bgm_paused
@playing_bgm = nil @playing_bgm = nil
end Audio.bgm_stop if !@defaultBGM
Audio.bgm_stop(track) if !@defaultBGM
end end
if @defaultBGM if @defaultBGM
bgm_play_internal2("Audio/BGM/" + @defaultBGM.name, bgm_play_internal2("Audio/BGM/"+@defaultBGM.name,
@defaultBGM.volume, @defaultBGM.pitch, @bgm_position, track) @defaultBGM.volume,@defaultBGM.pitch,@bgm_position)
end end
Graphics.frame_reset Graphics.frame_reset
end end
def bgm_pause(fadetime = 0.0) # :nodoc: def bgm_pause(fadetime=0.0) # :nodoc:
pos = Audio.bgm_pos rescue 0 pos = Audio.bgm_pos rescue 0
self.bgm_fade(fadetime) if fadetime > 0.0 self.bgm_fade(fadetime) if fadetime>0.0
@bgm_position = pos @bgm_position = pos
@bgm_paused = true @bgm_paused = true
end end
@@ -126,26 +93,22 @@ class Game_System
def bgm_resume(bgm) # :nodoc: def bgm_resume(bgm) # :nodoc:
if @bgm_paused if @bgm_paused
self.bgm_play_internal(bgm, @bgm_position) self.bgm_play_internal(bgm,@bgm_position)
@bgm_position = 0 @bgm_position = 0
@bgm_paused = false @bgm_paused = false
end end
end end
def bgm_stop(track = nil) # :nodoc: def bgm_stop # :nodoc:
if !track || track == 0
@bgm_position = 0 if !@bgm_paused @bgm_position = 0 if !@bgm_paused
@playing_bgm = nil @playing_bgm = nil
end Audio.bgm_stop if !@defaultBGM
Audio.bgm_stop(track) if !@defaultBGM
end end
def bgm_fade(time, track = nil) # :nodoc: def bgm_fade(time) # :nodoc:
if !track || track == 0
@bgm_position = 0 if !@bgm_paused @bgm_position = 0 if !@bgm_paused
@playing_bgm = nil @playing_bgm = nil
end Audio.bgm_fade((time*1000).floor) if !@defaultBGM
Audio.bgm_fade((time * 1000).floor, track) if !@defaultBGM
end end
def playing_bgm def playing_bgm
@@ -167,27 +130,28 @@ class Game_System
return (@playing_bgm) ? @playing_bgm.clone : nil return (@playing_bgm) ? @playing_bgm.clone : nil
end end
def setDefaultBGM(bgm, volume = 80, pitch = 100) def setDefaultBGM(bgm,volume=80,pitch=100)
bgm = RPG::AudioFile.new(bgm, volume, pitch) if bgm.is_a?(String) bgm = RPG::AudioFile.new(bgm,volume,pitch) if bgm.is_a?(String)
if bgm!=nil && bgm.name!=""
@defaultBGM = nil @defaultBGM = nil
if bgm && bgm.name != ""
self.bgm_play(bgm) self.bgm_play(bgm)
@defaultBGM = bgm.clone @defaultBGM = bgm.clone
else else
@defaultBGM = nil
self.bgm_play(@playing_bgm) self.bgm_play(@playing_bgm)
end end
end end
#----------------------------------------------------------------------------- ################################################################################
def me_play(me) def me_play(me)
me = RPG::AudioFile.new(me) if me.is_a?(String) me = RPG::AudioFile.new(me) if me.is_a?(String)
if me && me.name != "" if me!=nil && me.name!=""
if FileTest.audio_exist?("Audio/ME/" + me.name) if FileTest.audio_exist?("Audio/ME/"+me.name)
vol = me.volume vol = me.volume
vol *= $PokemonSystem.bgmvolume / 100.0 vol *= $PokemonSystem.bgmvolume/100.0
vol = vol.to_i vol = vol.to_i
Audio.me_play("Audio/ME/" + me.name, vol, me.pitch) Audio.me_play("Audio/ME/"+me.name,vol,me.pitch)
end end
else else
Audio.me_stop Audio.me_stop
@@ -195,16 +159,16 @@ class Game_System
Graphics.frame_reset Graphics.frame_reset
end end
#----------------------------------------------------------------------------- ################################################################################
def bgs_play(bgs) def bgs_play(bgs)
@playing_bgs = (bgs.nil?) ? nil : bgs.clone @playing_bgs = (bgs==nil) ? nil : bgs.clone
if bgs && bgs.name != "" if bgs!=nil && bgs.name!=""
if FileTest.audio_exist?("Audio/BGS/" + bgs.name) if FileTest.audio_exist?("Audio/BGS/"+bgs.name)
vol = bgs.volume vol = bgs.volume
vol *= $PokemonSystem.sevolume / 100.0 vol *= $PokemonSystem.sevolume/100.0
vol = vol.to_i vol = vol.to_i
Audio.bgs_play("Audio/BGS/" + bgs.name, vol, bgs.pitch) Audio.bgs_play("Audio/BGS/"+bgs.name,vol,bgs.pitch)
end end
else else
@bgs_position = 0 @bgs_position = 0
@@ -214,8 +178,8 @@ class Game_System
Graphics.frame_reset Graphics.frame_reset
end end
def bgs_pause(fadetime = 0.0) # :nodoc: def bgs_pause(fadetime=0.0) # :nodoc:
if fadetime > 0.0 if fadetime>0.0
self.bgs_fade(fadetime) self.bgs_fade(fadetime)
else else
self.bgs_stop self.bgs_stop
@@ -243,7 +207,7 @@ class Game_System
def bgs_fade(time) def bgs_fade(time)
@bgs_position = 0 @bgs_position = 0
@playing_bgs = nil @playing_bgs = nil
Audio.bgs_fade((time * 1000).floor) Audio.bgs_fade((time*1000).floor)
end end
def playing_bgs def playing_bgs
@@ -262,15 +226,15 @@ class Game_System
return (@playing_bgs) ? @playing_bgs.clone : nil return (@playing_bgs) ? @playing_bgs.clone : nil
end end
#----------------------------------------------------------------------------- ################################################################################
def se_play(se) def se_play(se)
se = RPG::AudioFile.new(se) if se.is_a?(String) se = RPG::AudioFile.new(se) if se.is_a?(String)
if se && se.name != "" && FileTest.audio_exist?("Audio/SE/" + se.name) if se!=nil && se.name!="" && FileTest.audio_exist?("Audio/SE/"+se.name)
vol = se.volume vol = se.volume
vol *= $PokemonSystem.sevolume / 100.0 vol *= $PokemonSystem.sevolume/100.0
vol = vol.to_i vol = vol.to_i
Audio.se_play("Audio/SE/" + se.name, vol, se.pitch) Audio.se_play("Audio/SE/"+se.name,vol,se.pitch)
end end
end end
@@ -278,12 +242,43 @@ class Game_System
Audio.se_stop Audio.se_stop
end 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 def update
if Input.trigger?(Input::SPECIAL) && pbCurrentEventCommentInput(1, "Cut Scene") @timer -= 1 if @timer_working && @timer>0
event = @map_interpreter.get_self if Input.trigger?(Input::SPECIAL) && pbCurrentEventCommentInput(1,"Cut Scene")
@map_interpreter.pbSetSelfSwitch(event.id, "A", true) event = @map_interpreter.get_character(0)
@map_interpreter.pbSetSelfSwitch(event.id,"A",true)
@map_interpreter.command_end @map_interpreter.command_end
event.start event.start
end end

View File

@@ -4,7 +4,11 @@
# This class handles the picture. It's used within the Game_Screen class # This class handles the picture. It's used within the Game_Screen class
# ($game_screen). # ($game_screen).
#=============================================================================== #===============================================================================
class Game_Picture class Game_Picture
#-----------------------------------------------------------------------------
# * Public Instance Variables
#-----------------------------------------------------------------------------
attr_reader :number # picture number attr_reader :number # picture number
attr_reader :name # file name attr_reader :name # file name
attr_reader :origin # starting point attr_reader :origin # starting point
@@ -16,7 +20,10 @@ class Game_Picture
attr_reader :blend_type # blend method attr_reader :blend_type # blend method
attr_reader :tone # color tone attr_reader :tone # color tone
attr_reader :angle # rotation angle attr_reader :angle # rotation angle
#-----------------------------------------------------------------------------
# * Object Initialization
# number : picture number
#-----------------------------------------------------------------------------
def initialize(number) def initialize(number)
@number = number @number = number
@name = "" @name = ""
@@ -28,7 +35,6 @@ class Game_Picture
@opacity = 255.0 @opacity = 255.0
@blend_type = 1 @blend_type = 1
@duration = 0 @duration = 0
@move_timer_start = nil
@target_x = @x @target_x = @x
@target_y = @y @target_y = @y
@target_zoom_x = @zoom_x @target_zoom_x = @zoom_x
@@ -37,12 +43,11 @@ class Game_Picture
@tone = Tone.new(0, 0, 0, 0) @tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0) @tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0 @tone_duration = 0
@tone_timer_start = nil
@angle = 0 @angle = 0
@rotate_speed = 0 @rotate_speed = 0
end end
#-----------------------------------------------------------------------------
# Show Picture # * Show Picture
# name : file name # name : file name
# origin : starting point # origin : starting point
# x : x-coordinate # x : x-coordinate
@@ -51,6 +56,7 @@ class Game_Picture
# zoom_y : y directional zoom rate # zoom_y : y directional zoom rate
# opacity : opacity level # opacity : opacity level
# blend_type : blend method # blend_type : blend method
#-----------------------------------------------------------------------------
def show(name, origin, x, y, zoom_x, zoom_y, opacity, blend_type) def show(name, origin, x, y, zoom_x, zoom_y, opacity, blend_type)
@name = name @name = name
@origin = origin @origin = origin
@@ -59,7 +65,7 @@ class Game_Picture
@zoom_x = zoom_x.to_f @zoom_x = zoom_x.to_f
@zoom_y = zoom_y.to_f @zoom_y = zoom_y.to_f
@opacity = opacity.to_f @opacity = opacity.to_f
@blend_type = blend_type || 0 @blend_type = blend_type ? blend_type : 0
@duration = 0 @duration = 0
@target_x = @x @target_x = @x
@target_y = @y @target_y = @y
@@ -69,13 +75,12 @@ class Game_Picture
@tone = Tone.new(0, 0, 0, 0) @tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0) @tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0 @tone_duration = 0
@tone_timer_start = nil
@angle = 0 @angle = 0
@rotate_speed = 0 @rotate_speed = 0
end end
#-----------------------------------------------------------------------------
# Move Picture # * Move Picture
# duration : time in 1/20ths of a second # duration : time
# origin : starting point # origin : starting point
# x : x-coordinate # x : x-coordinate
# y : y-coordinate # y : y-coordinate
@@ -83,80 +88,65 @@ class Game_Picture
# zoom_y : y directional zoom rate # zoom_y : y directional zoom rate
# opacity : opacity level # opacity : opacity level
# blend_type : blend method # blend_type : blend method
#-----------------------------------------------------------------------------
def move(duration, origin, x, y, zoom_x, zoom_y, opacity, blend_type) def move(duration, origin, x, y, zoom_x, zoom_y, opacity, blend_type)
@duration = duration / 20.0 @duration = duration
@origin = origin @origin = origin
@initial_x = @x
@initial_y = @y
@target_x = x.to_f @target_x = x.to_f
@target_y = y.to_f @target_y = y.to_f
@initial_zoom_x = @zoom_x
@initial_zoom_y = @zoom_y
@target_zoom_x = zoom_x.to_f @target_zoom_x = zoom_x.to_f
@target_zoom_y = zoom_y.to_f @target_zoom_y = zoom_y.to_f
@initial_opacity = @opacity
@target_opacity = opacity.to_f @target_opacity = opacity.to_f
@blend_type = blend_type || 0 @blend_type = blend_type ? blend_type : 0
@move_timer_start = $stats.play_time
end end
#-----------------------------------------------------------------------------
# Change Rotation Speed # * Change Rotation Speed
# speed : rotation speed (degrees to change per 1/20th of a second) # speed : rotation speed
#-----------------------------------------------------------------------------
def rotate(speed) def rotate(speed)
@rotate_timer = (speed == 0) ? nil : System.uptime # Time since last frame
@rotate_speed = speed @rotate_speed = speed
end end
#-----------------------------------------------------------------------------
# Start Change of Color Tone # * Start Change of Color Tone
# tone : color tone # tone : color tone
# duration : time in 1/20ths of a second # duration : time
#-----------------------------------------------------------------------------
def start_tone_change(tone, duration) def start_tone_change(tone, duration)
if duration == 0
@tone = tone.clone
return
end
@tone_initial = @tone.clone
@tone_target = tone.clone @tone_target = tone.clone
@tone_duration = duration / 20.0 @tone_duration = duration
@tone_timer_start = $stats.play_time if @tone_duration == 0
@tone = @tone_target.clone
end end
end
#-----------------------------------------------------------------------------
# * Erase Picture
#-----------------------------------------------------------------------------
def erase def erase
@name = "" @name = ""
end end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update def update
return if @name == "" if @duration >= 1
now = $stats.play_time d = @duration
if @move_timer_start @x = (@x * (d - 1) + @target_x) / d
@x = lerp(@initial_x, @target_x, @duration, @move_timer_start, now) @y = (@y * (d - 1) + @target_y) / d
@y = lerp(@initial_y, @target_y, @duration, @move_timer_start, now) @zoom_x = (@zoom_x * (d - 1) + @target_zoom_x) / d
@zoom_x = lerp(@initial_zoom_x, @target_zoom_x, @duration, @move_timer_start, now) @zoom_y = (@zoom_y * (d - 1) + @target_zoom_y) / d
@zoom_y = lerp(@initial_zoom_y, @target_zoom_y, @duration, @move_timer_start, now) @opacity = (@opacity * (d - 1) + @target_opacity) / d
@opacity = lerp(@initial_opacity, @target_opacity, @duration, @move_timer_start, now) @duration -= 1
if now - @move_timer_start >= @duration
@initial_x = nil
@initial_y = nil
@initial_zoom_x = nil
@initial_zoom_y = nil
@initial_opacity = nil
@move_timer_start = nil
end
end
if @tone_timer_start
@tone.red = lerp(@tone_initial.red, @tone_target.red, @tone_duration, @tone_timer_start, now)
@tone.green = lerp(@tone_initial.green, @tone_target.green, @tone_duration, @tone_timer_start, now)
@tone.blue = lerp(@tone_initial.blue, @tone_target.blue, @tone_duration, @tone_timer_start, now)
@tone.gray = lerp(@tone_initial.gray, @tone_target.gray, @tone_duration, @tone_timer_start, now)
if now - @tone_timer_start >= @tone_duration
@tone_initial = nil
@tone_timer_start = nil
end 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 end
if @rotate_speed != 0 if @rotate_speed != 0
@rotate_timer = System.uptime if !@rotate_timer @angle += @rotate_speed / 2.0
@angle += @rotate_speed * (System.uptime - @rotate_timer) * 20.0
@rotate_timer = System.uptime
while @angle < 0 while @angle < 0
@angle += 360 @angle += 360
end end

View File

@@ -1,9 +1,9 @@
#=============================================================================== #==============================================================================
# ** Game_Map # ** Game_Map
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# This class handles the map. It includes scrolling and passable determining # This class handles the map. It includes scrolling and passable determining
# functions. Refer to "$game_map" for the instance of this class. # functions. Refer to "$game_map" for the instance of this class.
#=============================================================================== #==============================================================================
class Game_Map class Game_Map
attr_accessor :map_id attr_accessor :map_id
attr_accessor :tileset_name # tileset file name attr_accessor :tileset_name # tileset file name
@@ -44,7 +44,7 @@ class Game_Map
def setup(map_id) def setup(map_id)
@map_id = map_id @map_id = map_id
@map = load_data(sprintf("Data/Map%03d.rxdata", map_id)) @map = load_data(sprintf("Data/Map%03d.rxdata",map_id))
tileset = $data_tilesets[@map.tileset_id] tileset = $data_tilesets[@map.tileset_id]
updateTileset updateTileset
@fog_ox = 0 @fog_ox = 0
@@ -52,24 +52,22 @@ class Game_Map
@fog_tone = Tone.new(0, 0, 0, 0) @fog_tone = Tone.new(0, 0, 0, 0)
@fog_tone_target = Tone.new(0, 0, 0, 0) @fog_tone_target = Tone.new(0, 0, 0, 0)
@fog_tone_duration = 0 @fog_tone_duration = 0
@fog_tone_timer_start = nil
@fog_opacity_duration = 0 @fog_opacity_duration = 0
@fog_opacity_target = 0 @fog_opacity_target = 0
@fog_opacity_timer_start = nil
self.display_x = 0 self.display_x = 0
self.display_y = 0 self.display_y = 0
@need_refresh = false @need_refresh = false
EventHandlers.trigger(:on_game_map_setup, map_id, @map, tileset) Events.onMapCreate.trigger(self,map_id,@map,tileset)
@events = {} @events = {}
@map.events.each_key do |i| for i in @map.events.keys
@events[i] = Game_Event.new(@map_id, @map.events[i], self) @events[i] = Game_Event.new(@map_id, @map.events[i],self)
end end
@common_events = {} @common_events = {}
(1...$data_common_events.size).each do |i| for i in 1...$data_common_events.size
@common_events[i] = Game_CommonEvent.new(i) @common_events[i] = Game_CommonEvent.new(i)
end end
@scroll_distance_x = 0 @scroll_direction = 2
@scroll_distance_y = 0 @scroll_rest = 0
@scroll_speed = 4 @scroll_speed = 4
end end
@@ -97,52 +95,57 @@ class Game_Map
def encounter_list; return @map.encounter_list; end def encounter_list; return @map.encounter_list; end
def encounter_step; return @map.encounter_step; end def encounter_step; return @map.encounter_step; end
def data; return @map.data; end def data; return @map.data; end
def tileset_id; return @map.tileset_id; end
def bgm; return @map.bgm; end
def name def name
return pbGetMapNameFromId(@map_id) ret = pbGetMessage(MessageTypes::MapNames,@map_id)
ret.gsub!(/\\PN/,$Trainer.name) if $Trainer
return ret
end end
#-----------------------------------------------------------------------------
def metadata # * Autoplays background music
return GameData::MapMetadata.try_get(@map_id)
end
# Returns the name of this map's BGM. If it's night time, returns the night
# version of the BGM (if it exists).
def bgm_name
if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/" + @map.bgm.name + "_n")
return @map.bgm.name + "_n"
end
return @map.bgm.name
end
# Autoplays background music
# Plays music called "[normal BGM]_n" if it's night time and it exists # Plays music called "[normal BGM]_n" if it's night time and it exists
#-----------------------------------------------------------------------------
def autoplayAsCue def autoplayAsCue
pbCueBGM(bgm_name, 1.0, @map.bgm.volume, @map.bgm.pitch) if @map.autoplay_bgm if @map.autoplay_bgm
pbBGSPlay(@map.bgs) if @map.autoplay_bgs 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
end
# Plays background music 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 # Plays music called "[normal BGM]_n" if it's night time and it exists
#-----------------------------------------------------------------------------
def autoplay def autoplay
pbBGMPlay(bgm_name, @map.bgm.volume, @map.bgm.pitch) if @map.autoplay_bgm if @map.autoplay_bgm
pbBGSPlay(@map.bgs) if @map.autoplay_bgs 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 end
def valid?(x, y) def valid?(x, y)
return x >= 0 && x < width && y >= 0 && y < height return x>=0 && x<width && y>=0 && y<height
end end
def validLax?(x, y) def validLax?(x, y)
return x >= -10 && x <= width + 10 && y >= -10 && y <= height + 10 return x>=-10 && x<=width+10 && y>=-10 && y<=height+10
end end
def passable?(x, y, dir, self_event = nil) def passable?(x, y, d, self_event = nil)
return false if !valid?(x, y) return false if !valid?(x, y)
bit = (1 << ((dir / 2) - 1)) & 0x0f bit = (1 << (d / 2 - 1)) & 0x0f
events.each_value do |event| for event in events.values
next if event.tile_id <= 0 next if event.tile_id <= 0
next if event == self_event next if event == self_event
next if !event.at_coordinate?(x, y) next if !event.at_coordinate?(x, y)
@@ -153,11 +156,11 @@ class Game_Map
return false if passage & 0x0f == 0x0f return false if passage & 0x0f == 0x0f
return true if @priorities[event.tile_id] == 0 return true if @priorities[event.tile_id] == 0
end end
return playerPassable?(x, y, dir, self_event) if self_event == $game_player return playerPassable?(x, y, d, self_event) if self_event==$game_player
# All other events # All other events
newx = x newx = x
newy = y newy = y
case dir case d
when 1 when 1
newx -= 1 newx -= 1
newy += 1 newy += 1
@@ -180,15 +183,14 @@ class Game_Map
newy -= 1 newy -= 1
end end
return false if !valid?(newx, newy) return false if !valid?(newx, newy)
[2, 1, 0].each do |i| for i in [2, 1, 0]
tile_id = data[x, y, i] tile_id = data[x, y, i]
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
# If already on water, only allow movement to another water tile # If already on water, only allow movement to another water tile
if self_event && terrain.can_surf_freely if self_event != nil && terrain.can_surf_freely
[2, 1, 0].each do |j| for j in [2, 1, 0]
facing_tile_id = data[newx, newy, j] facing_tile_id = data[newx, newy, j]
next if facing_tile_id == 0 return false if facing_tile_id == nil
return false if facing_tile_id.nil?
facing_terrain = GameData::TerrainTag.try_get(@terrain_tags[facing_tile_id]) facing_terrain = GameData::TerrainTag.try_get(@terrain_tags[facing_tile_id])
if facing_terrain.id != :None && !facing_terrain.ignore_passability if facing_terrain.id != :None && !facing_terrain.ignore_passability
return facing_terrain.can_surf_freely return facing_terrain.can_surf_freely
@@ -198,32 +200,30 @@ class Game_Map
# Can't walk onto ice # Can't walk onto ice
elsif terrain.ice elsif terrain.ice
return false return false
elsif self_event && self_event.x == x && self_event.y == y elsif self_event != nil && self_event.x == x && self_event.y == y
# Can't walk onto ledges # Can't walk onto ledges
[2, 1, 0].each do |j| for j in [2, 1, 0]
facing_tile_id = data[newx, newy, j] facing_tile_id = data[newx, newy, j]
next if facing_tile_id == 0 return false if facing_tile_id == nil
return false if facing_tile_id.nil?
facing_terrain = GameData::TerrainTag.try_get(@terrain_tags[facing_tile_id]) facing_terrain = GameData::TerrainTag.try_get(@terrain_tags[facing_tile_id])
return false if facing_terrain.ledge return false if facing_terrain.ledge
break if facing_terrain.id != :None && !facing_terrain.ignore_passability break if facing_terrain.id != :None && !facing_terrain.ignore_passability
end end
end end
next if terrain&.ignore_passability
next if tile_id == 0
# Regular passability checks # Regular passability checks
if !terrain || !terrain.ignore_passability
passage = @passages[tile_id] passage = @passages[tile_id]
return false if passage & bit != 0 || passage & 0x0f == 0x0f return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0 return true if @priorities[tile_id] == 0
end end
end
return true return true
end end
def playerPassable?(x, y, dir, self_event = nil) def playerPassable?(x, y, d, self_event = nil)
bit = (1 << ((dir / 2) - 1)) & 0x0f bit = (1 << (d / 2 - 1)) & 0x0f
[2, 1, 0].each do |i| for i in [2, 1, 0]
tile_id = data[x, y, i] tile_id = data[x, y, i]
next if tile_id == 0
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
passage = @passages[tile_id] passage = @passages[tile_id]
if terrain if terrain
@@ -232,34 +232,34 @@ class Game_Map
# Make water tiles passable if player is surfing # Make water tiles passable if player is surfing
return true if $PokemonGlobal.surfing && terrain.can_surf && !terrain.waterfall return true if $PokemonGlobal.surfing && terrain.can_surf && !terrain.waterfall
# Prevent cycling in really tall grass/on ice # Prevent cycling in really tall grass/on ice
return false if $PokemonGlobal.bicycle && (terrain.must_walk || terrain.must_walk_or_run) return false if $PokemonGlobal.bicycle && terrain.must_walk
# Depend on passability of bridge tile if on bridge # Depend on passability of bridge tile if on bridge
if terrain.bridge && $PokemonGlobal.bridge > 0 if terrain.bridge && $PokemonGlobal.bridge > 0
return (passage & bit == 0 && passage & 0x0f != 0x0f) return (passage & bit == 0 && passage & 0x0f != 0x0f)
end end
end end
next if terrain&.ignore_passability
# Regular passability checks # Regular passability checks
if !terrain || !terrain.ignore_passability
return false if passage & bit != 0 || passage & 0x0f == 0x0f return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0 return true if @priorities[tile_id] == 0
end end
end
return true return true
end end
# Returns whether the position x,y is fully passable (there is no blocking # Returns whether the position x,y is fully passable (there is no blocking
# event there, and the tile is fully passable in all directions). # event there, and the tile is fully passable in all directions)
def passableStrict?(x, y, d, self_event = nil) def passableStrict?(x, y, d, self_event = nil)
return false if !valid?(x, y) return false if !valid?(x, y)
events.each_value do |event| for event in events.values
next if event == self_event || event.tile_id < 0 || event.through next if event == self_event || event.tile_id < 0 || event.through
next if !event.at_coordinate?(x, y) next if !event.at_coordinate?(x, y)
next if GameData::TerrainTag.try_get(@terrain_tags[event.tile_id]).ignore_passability next if GameData::TerrainTag.try_get(@terrain_tags[event.tile_id]).ignore_passability
return false if @passages[event.tile_id] & 0x0f != 0 return false if @passages[event.tile_id] & 0x0f != 0
return true if @priorities[event.tile_id] == 0 return true if @priorities[event.tile_id] == 0
end end
[2, 1, 0].each do |i| for i in [2, 1, 0]
tile_id = data[x, y, i] tile_id = data[x, y, i]
next if tile_id == 0
next if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).ignore_passability next if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).ignore_passability
return false if @passages[tile_id] & 0x0f != 0 return false if @passages[tile_id] & 0x0f != 0
return true if @priorities[tile_id] == 0 return true if @priorities[tile_id] == 0
@@ -267,10 +267,9 @@ class Game_Map
return true return true
end end
def bush?(x, y) def bush?(x,y)
[2, 1, 0].each do |i| for i in [2, 1, 0]
tile_id = data[x, y, i] tile_id = data[x, y, i]
next if tile_id == 0
return false if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).bridge && return false if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).bridge &&
$PokemonGlobal.bridge > 0 $PokemonGlobal.bridge > 0
return true if @passages[tile_id] & 0x40 == 0x40 return true if @passages[tile_id] & 0x40 == 0x40
@@ -278,10 +277,9 @@ class Game_Map
return false return false
end end
def deepBush?(x, y) def deepBush?(x,y)
[2, 1, 0].each do |i| for i in [2, 1, 0]
tile_id = data[x, y, i] tile_id = data[x, y, i]
next if tile_id == 0
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
return false if terrain.bridge && $PokemonGlobal.bridge > 0 return false if terrain.bridge && $PokemonGlobal.bridge > 0
return true if terrain.deep_bush && @passages[tile_id] & 0x40 == 0x40 return true if terrain.deep_bush && @passages[tile_id] & 0x40 == 0x40
@@ -289,21 +287,19 @@ class Game_Map
return false return false
end end
def counter?(x, y) def counter?(x,y)
[2, 1, 0].each do |i| for i in [2, 1, 0]
tile_id = data[x, y, i] tile_id = data[x, y, i]
next if tile_id == 0
passage = @passages[tile_id] passage = @passages[tile_id]
return true if passage & 0x80 == 0x80 return true if passage & 0x80 == 0x80
end end
return false return false
end end
def terrain_tag(x, y, countBridge = false) def terrain_tag(x,y,countBridge=false)
if valid?(x, y) if valid?(x, y)
[2, 1, 0].each do |i| for i in [2, 1, 0]
tile_id = data[x, y, i] tile_id = data[x, y, i]
next if tile_id == 0
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
next if terrain.id == :None || terrain.ignore_passability next if terrain.id == :None || terrain.ignore_passability
next if !countBridge && terrain.bridge && $PokemonGlobal.bridge == 0 next if !countBridge && terrain.bridge && $PokemonGlobal.bridge == 0
@@ -314,8 +310,8 @@ class Game_Map
end end
# Unused. # Unused.
def check_event(x, y) def check_event(x,y)
self.events.each_value do |event| for event in self.events.values
return event.id if event.at_coordinate?(x, y) return event.id if event.at_coordinate?(x, y)
end end
end end
@@ -323,21 +319,21 @@ class Game_Map
def display_x=(value) def display_x=(value)
return if @display_x == value return if @display_x == value
@display_x = value @display_x = value
if metadata&.snap_edges if GameData::MapMetadata.exists?(self.map_id) && GameData::MapMetadata.get(self.map_id).snap_edges
max_x = (self.width - (Graphics.width.to_f / TILE_WIDTH)) * REAL_RES_X max_x = (self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X
@display_x = [0, [@display_x, max_x].min].max @display_x = [0, [@display_x, max_x].min].max
end end
$map_factory&.setMapsInRange $MapFactory.setMapsInRange if $MapFactory
end end
def display_y=(value) def display_y=(value)
return if @display_y == value return if @display_y == value
@display_y = value @display_y = value
if metadata&.snap_edges if GameData::MapMetadata.exists?(self.map_id) && GameData::MapMetadata.get(self.map_id).snap_edges
max_y = (self.height - (Graphics.height.to_f / TILE_HEIGHT)) * REAL_RES_Y max_y = (self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y
@display_y = [0, [@display_y, max_y].min].max @display_y = [0, [@display_y, max_y].min].max
end end
$map_factory&.setMapsInRange $MapFactory.setMapsInRange if $MapFactory
end end
def scroll_up(distance) def scroll_up(distance)
@@ -356,135 +352,90 @@ class Game_Map
self.display_x += distance self.display_x += distance
end end
# speed is: def start_scroll(direction, distance, speed)
# 1: moves 1 tile in 1.6 seconds @scroll_direction = direction
# 2: moves 1 tile in 0.8 seconds if direction==2 || direction==8 # down or up
# 3: moves 1 tile in 0.4 seconds @scroll_rest = distance * REAL_RES_Y
# 4: moves 1 tile in 0.2 seconds else
# 5: moves 1 tile in 0.1 seconds @scroll_rest = distance * REAL_RES_X
# 6: moves 1 tile in 0.05 seconds
def start_scroll(direction, distance, speed = 4)
return if direction <= 0 || direction == 5 || direction >= 10
if [1, 3, 4, 6, 7, 9].include?(direction) # horizontal
@scroll_distance_x = distance
@scroll_distance_x *= -1 if [1, 4, 7].include?(direction)
end
if [1, 2, 3, 7, 8, 9].include?(direction) # vertical
@scroll_distance_y = distance
@scroll_distance_y *= -1 if [7, 8, 9].include?(direction)
end end
@scroll_speed = speed @scroll_speed = speed
@scroll_start_x = display_x
@scroll_start_y = display_y
@scroll_timer_start = System.uptime
end
# The two distances can be positive or negative.
def start_scroll_custom(distance_x, distance_y, speed = 4)
return if distance_x == 0 && distance_y == 0
@scroll_distance_x = distance_x
@scroll_distance_y = distance_y
@scroll_speed = speed
@scroll_start_x = display_x
@scroll_start_y = display_y
@scroll_timer_start = System.uptime
end end
def scrolling? def scrolling?
return (@scroll_distance_x || 0) != 0 || (@scroll_distance_y || 0) != 0 return @scroll_rest > 0
end end
# duration is time in 1/20ths of a second. def start_fog_tone_change(tone,duration)
def start_fog_tone_change(tone, duration)
if duration == 0
@fog_tone = tone.clone
return
end
@fog_tone_initial = @fog_tone.clone
@fog_tone_target = tone.clone @fog_tone_target = tone.clone
@fog_tone_duration = duration / 20.0 @fog_tone_duration = duration
@fog_tone_timer_start = $stats.play_time if @fog_tone_duration == 0
@fog_tone = @fog_tone_target.clone
end
end end
# duration is time in 1/20ths of a second. def start_fog_opacity_change(opacity,duration)
def start_fog_opacity_change(opacity, duration) @fog_opacity_target = opacity*1.0
if duration == 0 @fog_opacity_duration = duration
@fog_opacity = opacity.to_f if @fog_opacity_duration==0
return @fog_opacity = @fog_opacity_target
end end
@fog_opacity_initial = @fog_opacity
@fog_opacity_target = opacity.to_f
@fog_opacity_duration = duration / 20.0
@fog_opacity_timer_start = $stats.play_time
end
def set_tile(x, y, layer, id = 0)
self.data[x, y, layer] = id
end
def erase_tile(x, y, layer)
set_tile(x, y, layer, 0)
end end
def refresh def refresh
@events.each_value do |event| for event in @events.values
event.refresh event.refresh
end end
@common_events.each_value do |common_event| for common_event in @common_events.values
common_event.refresh common_event.refresh
end end
@need_refresh = false @need_refresh = false
end end
def update def update
uptime_now = System.uptime # refresh maps if necessary
play_now = $stats.play_time if $MapFactory
# Refresh maps if necessary for i in $MapFactory.maps
if $map_factory i.refresh if i.need_refresh
$map_factory.maps.each { |i| i.refresh if i.need_refresh } end
$map_factory.setCurrentMap $MapFactory.setCurrentMap
end end
# If scrolling # If scrolling
if (@scroll_distance_x || 0) != 0 if @scroll_rest>0
duration = @scroll_distance_x.abs * TILE_WIDTH.to_f / (10 * (2**@scroll_speed)) distance = (1<<@scroll_speed)*40.0/Graphics.frame_rate
scroll_offset = lerp(0, @scroll_distance_x, duration, @scroll_timer_start, uptime_now) distance = @scroll_rest if distance>@scroll_rest
self.display_x = @scroll_start_x + (scroll_offset * REAL_RES_X) case @scroll_direction
@scroll_distance_x = 0 if scroll_offset == @scroll_distance_x when 2 then scroll_down(distance)
when 4 then scroll_left(distance)
when 6 then scroll_right(distance)
when 8 then scroll_up(distance)
end end
if (@scroll_distance_y || 0) != 0 @scroll_rest -= distance
duration = @scroll_distance_y.abs * TILE_HEIGHT.to_f / (10 * (2**@scroll_speed))
scroll_offset = lerp(0, @scroll_distance_y, duration, @scroll_timer_start, uptime_now)
self.display_y = @scroll_start_y + (scroll_offset * REAL_RES_Y)
@scroll_distance_y = 0 if scroll_offset == @scroll_distance_y
end end
# Only update events that are on-screen # Only update events that are on-screen
if !$game_temp.in_menu for event in @events.values
@events.each_value { |event| event.update } event.update
end end
# Update common events # Update common events
@common_events.each_value { |common_event| common_event.update } for common_event in @common_events.values
common_event.update
end
# Update fog # Update fog
@fog_scroll_last_update_timer = uptime_now if !@fog_scroll_last_update_timer @fog_ox -= @fog_sx/8.0
scroll_mult = (uptime_now - @fog_scroll_last_update_timer) * 5 @fog_oy -= @fog_sy/8.0
@fog_ox -= @fog_sx * scroll_mult if @fog_tone_duration>=1
@fog_oy -= @fog_sy * scroll_mult d = @fog_tone_duration
@fog_scroll_last_update_timer = uptime_now target = @fog_tone_target
if @fog_tone_timer_start @fog_tone.red = (@fog_tone.red * (d - 1) + target.red) / d
@fog_tone.red = lerp(@fog_tone_initial.red, @fog_tone_target.red, @fog_tone_duration, @fog_tone_timer_start, play_now) @fog_tone.green = (@fog_tone.green * (d - 1) + target.green) / d
@fog_tone.green = lerp(@fog_tone_initial.green, @fog_tone_target.green, @fog_tone_duration, @fog_tone_timer_start, play_now) @fog_tone.blue = (@fog_tone.blue * (d - 1) + target.blue) / d
@fog_tone.blue = lerp(@fog_tone_initial.blue, @fog_tone_target.blue, @fog_tone_duration, @fog_tone_timer_start, play_now) @fog_tone.gray = (@fog_tone.gray * (d - 1) + target.gray) / d
@fog_tone.gray = lerp(@fog_tone_initial.gray, @fog_tone_target.gray, @fog_tone_duration, @fog_tone_timer_start, play_now) @fog_tone_duration -= 1
if play_now - @fog_tone_timer_start >= @fog_tone_duration
@fog_tone_initial = nil
@fog_tone_timer_start = nil
end
end
if @fog_opacity_timer_start
@fog_opacity = lerp(@fog_opacity_initial, @fog_opacity_target, @fog_opacity_duration, @fog_opacity_timer_start, play_now)
if play_now - @fog_opacity_timer_start >= @fog_opacity_duration
@fog_opacity_initial = nil
@fog_opacity_timer_start = nil
end 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 end
end end
@@ -492,74 +443,26 @@ end
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
# Scroll the map in the given direction by the given distance at the (optional) def pbScrollMap(direction,distance,speed)
# given speed. if speed==0
def pbScrollMap(direction, distance, speed = 4) case direction
if speed == 0 when 2 then $game_map.scroll_down(distance * Game_Map::REAL_RES_Y)
if [1, 2, 3].include?(direction) when 4 then $game_map.scroll_left(distance * Game_Map::REAL_RES_X)
$game_map.scroll_down(distance * Game_Map::REAL_RES_Y) when 6 then $game_map.scroll_right(distance * Game_Map::REAL_RES_X)
elsif [7, 8, 9].include?(direction) when 8 then $game_map.scroll_up(distance * Game_Map::REAL_RES_Y)
$game_map.scroll_up(distance * Game_Map::REAL_RES_Y)
end
if [3, 6, 9].include?(direction)
$game_map.scroll_right(distance * Game_Map::REAL_RES_X)
elsif [1, 4, 7].include?(direction)
$game_map.scroll_left(distance * Game_Map::REAL_RES_X)
end end
else else
$game_map.start_scroll(direction, distance, speed) $game_map.start_scroll(direction, distance, speed)
oldx = $game_map.display_x
oldy = $game_map.display_y
loop do loop do
Graphics.update Graphics.update
Input.update Input.update
pbUpdateSceneMap
break if !$game_map.scrolling? break if !$game_map.scrolling?
end
end
end
# Scroll the map to center on the given coordinates at the (optional) given
# speed. The scroll can happen in up to two parts, depending on where the target
# is relative to the current location: an initial diagonal movement and a
# following cardinal (vertical/horizontal) movement.
def pbScrollMapTo(x, y, speed = 4)
if !$game_map.valid?(x, y)
print "pbScrollMapTo: given x,y is invalid"
return
elsif !(0..6).include?(speed)
print "pbScrollMapTo: invalid speed (0-6 only)"
return
end
# Get tile coordinates that the screen is currently scrolled to
screen_offset_x = (Graphics.width - Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS / 2
screen_offset_y = (Graphics.height - Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS / 2
current_tile_x = ($game_map.display_x + screen_offset_x) / Game_Map::REAL_RES_X
current_tile_y = ($game_map.display_y + screen_offset_y) / Game_Map::REAL_RES_Y
offset_x = x - current_tile_x
offset_y = y - current_tile_y
return if offset_x == 0 && offset_y == 0
if speed == 0
if offset_y > 0
$game_map.scroll_down(offset_y.abs * Game_Map::REAL_RES_Y)
elsif offset_y < 0
$game_map.scroll_up(offset_y.abs * Game_Map::REAL_RES_Y)
end
if offset_x > 0
$game_map.scroll_right(offset_x.abs * Game_Map::REAL_RES_X)
elsif offset_x < 0
$game_map.scroll_left(offset_x.abs * Game_Map::REAL_RES_X)
end
else
$game_map.start_scroll_custom(offset_x, offset_y, speed)
loop do
Graphics.update
Input.update
pbUpdateSceneMap pbUpdateSceneMap
break if !$game_map.scrolling? break if $game_map.display_x==oldx && $game_map.display_y==oldy
oldx = $game_map.display_x
oldy = $game_map.display_y
end end
end end
end end
# Scroll the map to center on the player at the (optional) given speed.
def pbScrollMapToPlayer(speed = 4)
pbScrollMapTo($game_player.x, $game_player.y, speed)
end

View File

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

View File

@@ -1,5 +1,5 @@
#=============================================================================== #===============================================================================
# Map Factory (allows multiple maps to be loaded at once and connected). # Map Factory (allows multiple maps to be loaded at once and connected)
#=============================================================================== #===============================================================================
class PokemonMapFactory class PokemonMapFactory
attr_reader :maps attr_reader :maps
@@ -19,7 +19,7 @@ class PokemonMapFactory
@maps[0] = Game_Map.new @maps[0] = Game_Map.new
@mapIndex = 0 @mapIndex = 0
oldID = ($game_map) ? $game_map.map_id : 0 oldID = ($game_map) ? $game_map.map_id : 0
setMapChanging(id, @maps[0]) if oldID != 0 && oldID != @maps[0].map_id setMapChanging(id,@maps[0]) if oldID!=0 && oldID!=@maps[0].map_id
$game_map = @maps[0] $game_map = @maps[0]
@maps[0].setup(id) @maps[0].setup(id)
setMapsInRange setMapsInRange
@@ -27,33 +27,33 @@ class PokemonMapFactory
end end
def map def map
@mapIndex = 0 if !@mapIndex || @mapIndex < 0 @mapIndex = 0 if !@mapIndex || @mapIndex<0
return @maps[@mapIndex] if @maps[@mapIndex] return @maps[@mapIndex] if @maps[@mapIndex]
raise "No maps in save file... (mapIndex=#{@mapIndex})" if @maps.length == 0 raise "No maps in save file... (mapIndex=#{@mapIndex})" if @maps.length==0
if @maps[0] if @maps[0]
echoln "Using next map, may be incorrect (mapIndex=#{@mapIndex}, length=#{@maps.length})" echoln("Using next map, may be incorrect (mapIndex=#{@mapIndex}, length=#{@maps.length})")
return @maps[0] return @maps[0]
end end
raise "No maps in save file... (all maps empty; mapIndex=#{@mapIndex})" raise "No maps in save file... (all maps empty; mapIndex=#{@mapIndex})"
end end
def hasMap?(id) def hasMap?(id)
@maps.each do |map| for map in @maps
return true if map.map_id == id return true if map.map_id==id
end end
return false return false
end end
def getMapIndex(id) def getMapIndex(id)
@maps.length.times do |i| for i in 0...@maps.length
return i if @maps[i].map_id == id return i if @maps[i].map_id==id
end end
return -1 return -1
end end
def getMap(id, add = true) def getMap(id,add=true)
@maps.each do |map| for map in @maps
return map if map.map_id == id return map if map.map_id==id
end end
map = Game_Map.new map = Game_Map.new
map.setup(id) map.setup(id)
@@ -62,12 +62,14 @@ class PokemonMapFactory
end end
def getMapNoAdd(id) def getMapNoAdd(id)
return getMap(id, false) return getMap(id,false)
end end
def getNewMap(playerX, playerY, map_id = nil) def getNewMap(playerX,playerY)
id = map_id || $game_map.map_id id = $game_map.map_id
MapFactoryHelper.eachConnectionForMap(id) do |conn| conns = MapFactoryHelper.getMapConnections
if conns[id]
for conn in conns[id]
mapidB = nil mapidB = nil
newx = 0 newx = 0
newy = 0 newy = 0
@@ -83,27 +85,27 @@ class PokemonMapFactory
newy = conn[2] - conn[5] + playerY newy = conn[2] - conn[5] + playerY
end end
if newx >= 0 && newx < mapB[0] && newy >= 0 && newy < mapB[1] if newx >= 0 && newx < mapB[0] && newy >= 0 && newy < mapB[1]
return [getMapNoAdd(mapidB), newx, newy] if map_id
return [getMap(mapidB), newx, newy] return [getMap(mapidB), newx, newy]
end end
end end
end
return nil return nil
end end
# Detects whether the player has moved onto a connected map, and if so, causes # Detects whether the player has moved onto a connected map, and if so, causes
# their transfer to that map. # their transfer to that map.
def setCurrentMap def setCurrentMap
return if $game_player.moving? || $game_player.jumping? return if $game_player.moving?
return if $game_map.valid?($game_player.x, $game_player.y) return if $game_map.valid?($game_player.x,$game_player.y)
newmap = getNewMap($game_player.x, $game_player.y) newmap = getNewMap($game_player.x,$game_player.y)
return if !newmap return if !newmap
oldmap = $game_map.map_id oldmap=$game_map.map_id
if oldmap != 0 && oldmap != newmap[0].map_id if oldmap!=0 && oldmap!=newmap[0].map_id
setMapChanging(newmap[0].map_id, newmap[0]) setMapChanging(newmap[0].map_id,newmap[0])
end end
$game_map = newmap[0] $game_map = newmap[0]
@mapIndex = getMapIndex($game_map.map_id) @mapIndex = getMapIndex($game_map.map_id)
$game_player.moveto(newmap[1], newmap[2]) $game_player.moveto(newmap[1],newmap[2])
$game_map.update $game_map.update
pbAutoplayOnTransition pbAutoplayOnTransition
$game_map.refresh $game_map.refresh
@@ -115,11 +117,13 @@ class PokemonMapFactory
return if @fixup return if @fixup
@fixup = true @fixup = true
id = $game_map.map_id id = $game_map.map_id
MapFactoryHelper.eachConnectionForMap(id) do |conn| conns = MapFactoryHelper.getMapConnections
if conns[id]
for conn in conns[id]
if conn[0] == id if conn[0] == id
mapA = getMap(conn[0]) mapA = getMap(conn[0])
newdispx = ((conn[4] - conn[1]) * Game_Map::REAL_RES_X) + mapA.display_x 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 newdispy = (conn[5] - conn[2]) * Game_Map::REAL_RES_Y + mapA.display_y
if hasMap?(conn[3]) || MapFactoryHelper.mapInRangeById?(conn[3], newdispx, newdispy) if hasMap?(conn[3]) || MapFactoryHelper.mapInRangeById?(conn[3], newdispx, newdispy)
mapB = getMap(conn[3]) mapB = getMap(conn[3])
mapB.display_x = newdispx if mapB.display_x != newdispx mapB.display_x = newdispx if mapB.display_x != newdispx
@@ -127,8 +131,8 @@ class PokemonMapFactory
end end
else else
mapA = getMap(conn[3]) mapA = getMap(conn[3])
newdispx = ((conn[1] - conn[4]) * Game_Map::REAL_RES_X) + mapA.display_x 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 newdispy = (conn[2] - conn[5]) * Game_Map::REAL_RES_Y + mapA.display_y
if hasMap?(conn[0]) || MapFactoryHelper.mapInRangeById?(conn[0], newdispx, newdispy) if hasMap?(conn[0]) || MapFactoryHelper.mapInRangeById?(conn[0], newdispx, newdispy)
mapB = getMap(conn[0]) mapB = getMap(conn[0])
mapB.display_x = newdispx if mapB.display_x != newdispx mapB.display_x = newdispx if mapB.display_x != newdispx
@@ -136,146 +140,165 @@ class PokemonMapFactory
end end
end end
end end
end
@fixup = false @fixup = false
end end
def setMapChanging(newID, newMap) def setMapChanging(newID,newMap)
EventHandlers.trigger(:on_leave_map, newID, newMap) Events.onMapChanging.trigger(self,newID,newMap)
end end
def setMapChanged(prevMap) def setMapChanged(prevMap)
EventHandlers.trigger(:on_enter_map, prevMap) Events.onMapChange.trigger(self,prevMap)
@mapChanged = true @mapChanged = true
end end
def setSceneStarted(scene) def setSceneStarted(scene)
EventHandlers.trigger(:on_map_or_spriteset_change, scene, @mapChanged) Events.onMapSceneChange.trigger(self,scene,@mapChanged)
@mapChanged = false @mapChanged = false
end end
# Similar to Game_Player#passable?, but supports map connections # Similar to Game_Player#passable?, but supports map connections
def isPassableFromEdge?(x, y, dir = 0) def isPassableFromEdge?(x, y)
return true if $game_map.valid?(x, y) return true if $game_map.valid?(x, y)
newmap = getNewMap(x, y, $game_map.map_id) newmap = getNewMap(x, y)
return false if !newmap return false if !newmap
return isPassable?(newmap[0].map_id, newmap[1], newmap[2], dir) return isPassable?(newmap[0].map_id, newmap[1], newmap[2])
end end
def isPassable?(mapID, x, y, dir = 0, thisEvent = nil) def isPassable?(mapID, x, y, thisEvent = nil)
thisEvent = $game_player if !thisEvent thisEvent = $game_player if !thisEvent
map = getMapNoAdd(mapID) map = getMapNoAdd(mapID)
return false if !map return false if !map
return false if !map.valid?(x, y) return false if !map.valid?(x, y)
return true if thisEvent.through return true if thisEvent.through
# Check passability of tile # Check passability of tile
return true if $DEBUG && Input.press?(Input::CTRL) && thisEvent.is_a?(Game_Player) if thisEvent.is_a?(Game_Player)
return false if !map.passable?(x, y, dir, thisEvent) 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
# Check passability of event(s) in that spot # Check passability of event(s) in that spot
map.events.each_value do |event| for event in map.events.values
next if event == thisEvent || !event.at_coordinate?(x, y) next if event == thisEvent || !event.at_coordinate?(x, y)
return false if !event.through && event.character_name != "" return false if !event.through && event.character_name != ""
end end
# Check passability of player # Check passability of player
if !thisEvent.is_a?(Game_Player) && if !thisEvent.is_a?(Game_Player)
$game_map.map_id == mapID && $game_player.x == x && $game_player.y == y && if $game_map.map_id == mapID && $game_player.x == x && $game_player.y == y
!$game_player.through && $game_player.character_name != "" return false if !$game_player.through && $game_player.character_name != ""
return false end
end end
return true return true
end end
# Only used by follower events # Only used by dependent events
def isPassableStrict?(mapID, x, y, thisEvent = nil) def isPassableStrict?(mapID,x,y,thisEvent=nil)
thisEvent = $game_player if !thisEvent thisEvent = $game_player if !thisEvent
map = getMapNoAdd(mapID) map = getMapNoAdd(mapID)
return false if !map return false if !map
return false if !map.valid?(x, y) return false if !map.valid?(x,y)
return true if thisEvent.through return true if thisEvent.through
return true if $DEBUG && Input.press?(Input::CTRL) && thisEvent.is_a?(Game_Player) if thisEvent==$game_player
return false if !map.passableStrict?(x, y, 0, thisEvent) if !($DEBUG && Input.press?(Input::CTRL))
map.events.each_value do |event| 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.at_coordinate?(x, y) next if event == thisEvent || !event.at_coordinate?(x, y)
return false if !event.through && event.character_name != "" return false if !event.through && event.character_name!=""
end end
return true return true
end end
def getTerrainTag(mapid, x, y, countBridge = false) def getTerrainTag(mapid,x,y,countBridge=false)
map = getMapNoAdd(mapid) map = getMapNoAdd(mapid)
return map.terrain_tag(x, y, countBridge) return map.terrain_tag(x,y,countBridge)
end end
# NOTE: Assumes the event is 1x1 tile in size. Only returns one terrain tag. # NOTE: Assumes the event is 1x1 tile in size. Only returns one terrain tag.
def getFacingTerrainTag(dir = nil, event = nil) def getFacingTerrainTag(dir=nil,event=nil)
tile = getFacingTile(dir, event) tile = getFacingTile(dir,event)
return GameData::TerrainTag.get(:None) if !tile return GameData::TerrainTag.get(:None) if !tile
return getTerrainTag(tile[0], tile[1], tile[2]) return getTerrainTag(tile[0],tile[1],tile[2])
end end
def getTerrainTagFromCoords(mapid, x, y, countBridge = false) def getTerrainTagFromCoords(mapid,x,y,countBridge=false)
tile = getRealTilePos(mapid, x, y) tile = getRealTilePos(mapid,x,y)
return GameData::TerrainTag.get(:None) if !tile return GameData::TerrainTag.get(:None) if !tile
return getTerrainTag(tile[0], tile[1], tile[2]) return getTerrainTag(tile[0],tile[1],tile[2])
end end
def areConnected?(mapID1, mapID2) def areConnected?(mapID1, mapID2)
return true if mapID1 == mapID2 return true if mapID1 == mapID2
return MapFactoryHelper.mapsConnected?(mapID1, mapID2) conns = MapFactoryHelper.getMapConnections
if conns[mapID1]
for conn in conns[mapID1]
return true if conn[0] == mapID2 || conn[3] == mapID2
end
end
return false
end end
# Returns the coordinate change to go from this position to other position
def getRelativePos(thisMapID, thisX, thisY, otherMapID, otherX, otherY) def getRelativePos(thisMapID, thisX, thisY, otherMapID, otherX, otherY)
if thisMapID == otherMapID # Both events share the same map if thisMapID == otherMapID # Both events share the same map
return [otherX - thisX, otherY - thisY] return [otherX - thisX, otherY - thisY]
end end
MapFactoryHelper.eachConnectionForMap(thisMapID) do |conn| conns = MapFactoryHelper.getMapConnections
if conns[thisMapID]
for conn in conns[thisMapID]
if conn[0] == otherMapID if conn[0] == otherMapID
posX = conn[4] - conn[1] + otherX - thisX posX = thisX + conn[1] - conn[4] + otherX
posY = conn[5] - conn[2] + otherY - thisY posY = thisY + conn[2] - conn[5] + otherY
return [posX, posY] return [posX, posY]
elsif conn[3] == otherMapID elsif conn[1] == otherMapID
posX = conn[1] - conn[4] + otherX - thisX posX = thisX + conn[4] - conn[1] + otherX
posY = conn[2] - conn[5] + otherY - thisY posY = thisY + conn[5] - conn[2] + otherY
return [posX, posY] return [posX, posY]
end end
end end
end
return [0, 0] return [0, 0]
end end
# Gets the distance from this event to another event. Example: If this event's # 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 # 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). # the array (3,-4), because (5-2=3) and (1-5=-4).
def getThisAndOtherEventRelativePos(thisEvent, otherEvent) def getThisAndOtherEventRelativePos(thisEvent,otherEvent)
return [0, 0] if !thisEvent || !otherEvent return [0,0] if !thisEvent || !otherEvent
return getRelativePos(thisEvent.map.map_id, thisEvent.x, thisEvent.y, return getRelativePos(
otherEvent.map.map_id, otherEvent.x, otherEvent.y) thisEvent.map.map_id,thisEvent.x,thisEvent.y,
otherEvent.map.map_id,otherEvent.x,otherEvent.y)
end end
def getThisAndOtherPosRelativePos(thisEvent, otherMapID, otherX, otherY) def getThisAndOtherPosRelativePos(thisEvent,otherMapID,otherX,otherY)
return [0, 0] if !thisEvent return [0,0] if !thisEvent
return getRelativePos(thisEvent.map.map_id, thisEvent.x, thisEvent.y, return getRelativePos(
otherMapID, otherX, otherY) thisEvent.map.map_id,thisEvent.x,thisEvent.y,otherMapID,otherX,otherY)
end end
# Unused # Unused
def getOffsetEventPos(event, xOffset, yOffset) def getOffsetEventPos(event,xOffset,yOffset)
event = $game_player if !event event = $game_player if !event
return nil if !event return nil if !event
return getRealTilePos(event.map.map_id, event.x + xOffset, event.y + yOffset) return getRealTilePos(event.map.map_id,event.x+xOffset,event.y+yOffset)
end end
# NOTE: Assumes the event is 1x1 tile in size. Only returns one tile. # NOTE: Assumes the event is 1x1 tile in size. Only returns one tile.
def getFacingTile(direction = nil, event = nil, steps = 1) def getFacingTile(direction=nil,event=nil,steps=1)
event = $game_player if event.nil? event = $game_player if event==nil
return [0, 0, 0] if !event return [0,0,0] if !event
x = event.x x = event.x
y = event.y y = event.y
id = event.map.map_id id = event.map.map_id
direction = event.direction if direction.nil? direction = event.direction if direction==nil
return getFacingTileFromPos(id, x, y, direction, steps) return getFacingTileFromPos(id,x,y,direction,steps)
end end
def getFacingTileFromPos(mapID, x, y, direction = 0, steps = 1) def getFacingTileFromPos(mapID,x,y,direction=0,steps=1)
id = mapID id = mapID
case direction case direction
when 1 when 1
@@ -299,15 +322,17 @@ class PokemonMapFactory
x += steps x += steps
y -= steps y -= steps
else else
return [id, x, y] return [id,x,y]
end end
return getRealTilePos(mapID, x, y) return getRealTilePos(mapID,x,y)
end end
def getRealTilePos(mapID, x, y) def getRealTilePos(mapID, x, y)
id = mapID id = mapID
return [id, x, y] if getMapNoAdd(id).valid?(x, y) return [id, x, y] if getMapNoAdd(id).valid?(x, y)
MapFactoryHelper.eachConnectionForMap(id) do |conn| conns = MapFactoryHelper.getMapConnections
if conns[id]
for conn in conns[id]
if conn[0] == id if conn[0] == id
newX = x + conn[4] - conn[1] newX = x + conn[4] - conn[1]
newY = y + conn[5] - conn[2] newY = y + conn[5] - conn[2]
@@ -324,10 +349,11 @@ class PokemonMapFactory
return [conn[0], newX, newY] return [conn[0], newX, newY]
end end
end end
end
return nil return nil
end end
def getFacingCoords(x, y, direction = 0, steps = 1) def getFacingCoords(x,y,direction=0,steps=1)
case direction case direction
when 1 when 1
x -= steps x -= steps
@@ -350,29 +376,36 @@ class PokemonMapFactory
x += steps x += steps
y -= steps y -= steps
end end
return [x, y] return [x,y]
end end
def updateMaps(scene) def updateMaps(scene)
updateMapsInternal updateMapsInternal
setSceneStarted(scene) if @mapChanged $MapFactory.setSceneStarted(scene) if @mapChanged
end end
def updateMapsInternal def updateMapsInternal
return if $game_player.moving? return if $game_player.moving?
if !MapFactoryHelper.hasConnections?($game_map.map_id) if !MapFactoryHelper.hasConnections?($game_map.map_id)
return if @maps.length == 1 return if @maps.length==1
@maps.delete_if { |map| map.map_id != $game_map.map_id } 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) @mapIndex = getMapIndex($game_map.map_id)
return return
end end
old_num_maps = @maps.length
@maps.delete_if { |map| !MapFactoryHelper.mapsConnected?($game_map.map_id, map.map_id) }
@mapIndex = getMapIndex($game_map.map_id) if @maps.length != old_num_maps
setMapsInRange setMapsInRange
old_num_maps = @maps.length deleted = false
@maps.delete_if { |map| !MapFactoryHelper.mapInRange?(map) } for i in 0...@maps.length
@mapIndex = getMapIndex($game_map.map_id) if @maps.length != old_num_maps 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
end end
@@ -384,14 +417,12 @@ module MapFactoryHelper
@@MapConnections = nil @@MapConnections = nil
@@MapDims = nil @@MapDims = nil
module_function def self.clear
def clear
@@MapConnections = nil @@MapConnections = nil
@@MapDims = nil @@MapDims = nil
end end
def getMapConnections def self.getMapConnections
if !@@MapConnections if !@@MapConnections
@@MapConnections = [] @@MapConnections = []
conns = load_data("Data/map_connections.dat") conns = load_data("Data/map_connections.dat")
@@ -429,35 +460,22 @@ module MapFactoryHelper
return @@MapConnections return @@MapConnections
end end
def hasConnections?(id) def self.hasConnections?(id)
conns = MapFactoryHelper.getMapConnections conns = MapFactoryHelper.getMapConnections
return conns[id] ? true : false return conns[id] ? true : false
end end
def mapsConnected?(id1, id2) # Gets the height and width of the map with id
MapFactoryHelper.eachConnectionForMap(id1) do |conn| def self.getMapDims(id)
return true if conn[0] == id2 || conn[3] == id2
end
return false
end
def eachConnectionForMap(id)
conns = MapFactoryHelper.getMapConnections
return if !conns[id]
conns[id].each { |conn| yield conn }
end
# Gets the height and width of the map with id.
def getMapDims(id)
# Create cache if doesn't exist # Create cache if doesn't exist
@@MapDims = [] if !@@MapDims @@MapDims = [] if !@@MapDims
# Add map to cache if can't be found # Add map to cache if can't be found
if !@@MapDims[id] if !@@MapDims[id]
begin begin
map = load_data(sprintf("Data/Map%03d.rxdata", id)) map = load_data(sprintf("Data/Map%03d.rxdata", id))
@@MapDims[id] = [map.width, map.height] @@MapDims[id] = [map.width,map.height]
rescue rescue
@@MapDims[id] = [0, 0] @@MapDims[id] = [0,0]
end end
end end
# Return map in cache # Return map in cache
@@ -466,32 +484,32 @@ module MapFactoryHelper
# Returns the X or Y coordinate of an edge on the map with id. # Returns the X or Y coordinate of an edge on the map with id.
# Considers the special strings "N","W","E","S" # Considers the special strings "N","W","E","S"
def getMapEdge(id, edge) def self.getMapEdge(id,edge)
return 0 if ["N", "W"].include?(edge) return 0 if edge=="N" || edge=="W"
dims = getMapDims(id) # Get dimensions dims = getMapDims(id) # Get dimensions
return dims[0] if edge == "E" return dims[0] if edge=="E"
return dims[1] if edge == "S" return dims[1] if edge=="S"
return dims[0] # real dimension (use width) return dims[0] # real dimension (use width)
end end
def mapInRange?(map) def self.mapInRange?(map)
range = 6 # Number of tiles range = 6 # Number of tiles
dispx = map.display_x dispx = map.display_x
dispy = map.display_y dispy = map.display_y
return false if dispx >= (map.width + range) * Game_Map::REAL_RES_X 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 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 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 false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
return true return true
end end
def mapInRangeById?(id, dispx, dispy) def self.mapInRangeById?(id,dispx,dispy)
range = 6 # Number of tiles range = 6 # Number of tiles
dims = MapFactoryHelper.getMapDims(id) dims = MapFactoryHelper.getMapDims(id)
return false if dispx >= (dims[0] + range) * Game_Map::REAL_RES_X 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 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 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 false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
return true return true
end end
end end
@@ -501,8 +519,8 @@ end
#=============================================================================== #===============================================================================
# Unused # Unused
def updateTilesets def updateTilesets
maps = $map_factory.maps maps = $MapFactory.maps
maps.each do |map| for map in maps
map&.updateTileset map.updateTileset if map
end end
end end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
class Game_Character class Game_Character
attr_reader :id attr_reader :id
attr_reader :original_x attr_reader :original_x
@@ -9,15 +6,13 @@ class Game_Character
attr_reader :y attr_reader :y
attr_reader :real_x attr_reader :real_x
attr_reader :real_y attr_reader :real_y
attr_writer :x_offset # In pixels, positive shifts sprite to the right
attr_writer :y_offset # In pixels, positive shifts sprite down
attr_accessor :width attr_accessor :width
attr_accessor :height attr_accessor :height
attr_accessor :sprite_size attr_accessor :sprite_size
attr_reader :tile_id attr_reader :tile_id
attr_accessor :character_name attr_accessor :character_name
attr_accessor :character_hue attr_accessor :character_hue
attr_accessor :opacity attr_reader :opacity
attr_reader :blend_type attr_reader :blend_type
attr_accessor :direction attr_accessor :direction
attr_accessor :pattern attr_accessor :pattern
@@ -25,16 +20,13 @@ class Game_Character
attr_accessor :lock_pattern attr_accessor :lock_pattern
attr_reader :move_route_forcing attr_reader :move_route_forcing
attr_accessor :through attr_accessor :through
attr_reader :animation_id attr_accessor :animation_id
attr_accessor :animation_height
attr_accessor :animation_regular_tone
attr_accessor :transparent attr_accessor :transparent
attr_reader :move_speed attr_reader :move_speed
attr_reader :jump_speed
attr_accessor :walk_anime attr_accessor :walk_anime
attr_writer :bob_height attr_writer :bob_height
def initialize(map = nil) def initialize(map=nil)
@map = map @map = map
@id = 0 @id = 0
@original_x = 0 @original_x = 0
@@ -43,8 +35,6 @@ class Game_Character
@y = 0 @y = 0
@real_x = 0 @real_x = 0
@real_y = 0 @real_y = 0
@x_offset = 0
@y_offset = 0
@width = 1 @width = 1
@height = 1 @height = 1
@sprite_size = [Game_Map::TILE_WIDTH, Game_Map::TILE_HEIGHT] @sprite_size = [Game_Map::TILE_WIDTH, Game_Map::TILE_HEIGHT]
@@ -59,14 +49,13 @@ class Game_Character
@lock_pattern = false @lock_pattern = false
@move_route_forcing = false @move_route_forcing = false
@through = false @through = false
animation_id = 0 @animation_id = 0
@transparent = false @transparent = false
@original_direction = 2 @original_direction = 2
@original_pattern = 0 @original_pattern = 0
@move_type = 0 @move_type = 0
self.move_speed = 3 self.move_speed = 3
self.move_frequency = 6 self.move_frequency = 6
self.jump_speed = 3
@move_route = nil @move_route = nil
@move_route_index = 0 @move_route_index = 0
@original_move_route = nil @original_move_route = nil
@@ -75,32 +64,19 @@ class Game_Character
@step_anime = false # Whether character should animate while still @step_anime = false # Whether character should animate while still
@direction_fix = false @direction_fix = false
@always_on_top = false @always_on_top = false
@anime_count = 0 # Time since pattern was last changed @anime_count = 0
@stop_count = 0 # Time since character last finished moving @stop_count = 0
@jump_peak = 0 # Max height while jumping @jump_peak = 0 # Max height while jumping
@jump_distance = 0 # Total distance of jump @jump_distance = 0 # Total distance of jump
@jump_fraction = 0 # How far through a jump we currently are (0-1) @jump_distance_left = 0 # Distance left to travel
@jumping_on_spot = false @jump_count = 0 # Frames left in a stationary jump
@bob_height = 0 @bob_height = 0
@wait_count = 0 @wait_count = 0
@wait_start = nil
@moved_this_frame = false @moved_this_frame = false
@moveto_happened = false
@locked = false @locked = false
@prelock_direction = 0 @prelock_direction = 0
end end
def animation_id=(value)
@animation_id = value
if value == 0
@animation_height = 3
@animation_regular_tone = false
end
end
def x_offset; return @x_offset || 0; end
def y_offset; return @y_offset || 0; end
def at_coordinate?(check_x, check_y) def at_coordinate?(check_x, check_y)
return check_x >= @x && check_x < @x + @width && return check_x >= @x && check_x < @x + @width &&
check_y > @y - @height && check_y <= @y check_y > @y - @height && check_y <= @y
@@ -112,61 +88,70 @@ class Game_Character
end end
def each_occupied_tile def each_occupied_tile
(@x...(@x + @width)).each do |i| for i in @x...(@x + @width)
((@y - @height + 1)..@y).each do |j| for j in (@y - @height + 1)..@y
yield i, j yield i, j
end end
end end
end end
def move_speed=(val) def move_speed=(val)
return if val==@move_speed
@move_speed = val @move_speed = val
# Time taken to traverse one tile (in seconds) for each speed: # @move_speed_real is the number of quarter-pixels to move each frame. There
# 1 => 1.0 # are 128 quarter-pixels per tile. By default, it is calculated from
# 2 => 0.5 # @move_speed and has these values (assuming 40 fps):
# 3 => 0.25 # Walking speed # 1 => 3.2 # 40 frames per tile
# 4 => 0.125 # Running speed (2x walking speed) # 2 => 6.4 # 20 frames per tile
# 5 => 0.1 # Cycling speed (1.25x running speed) # 3 => 12.8 # 10 frames per tile - walking speed
# 6 => 0.05 # 4 => 25.6 # 5 frames per tile - running speed (2x walking speed)
case val # 5 => 32 # 4 frames per tile - cycling speed (1.25x running speed)
when 6 then @move_time = 0.05 # 6 => 64 # 2 frames per tile
when 5 then @move_time = 0.1 self.move_speed_real = (val == 6) ? 64 : (val == 5) ? 32 : (2 ** (val + 1)) * 0.8
else @move_time = 2.0 / (2**val)
end
end end
# Takes the same values as move_speed above. def move_speed_real
def jump_speed=(val) self.move_speed = @move_speed if !@move_speed_real
@jump_speed = val return @move_speed_real
case val
when 6 then @jump_time = 0.05
when 5 then @jump_time = 0.1
else @jump_time = 2.0 / (2**val)
end
end end
# Returns time in seconds for one full cycle (4 frames) of an animating def move_speed_real=(val)
# charset to show. Two frames are shown per movement across one tile. @move_speed_real = val * 40.0 / Graphics.frame_rate
def pattern_update_speed end
return @jump_time * 2 if jumping?
ret = @move_time * 2 def jump_speed_real
ret *= 2 if @move_speed >= 5 # Cycling speed or faster; slower animation self.jump_speed_real = (2 ** (3 + 1)) * 0.8 if !@jump_speed_real # 3 is walking speed
return ret return @jump_speed_real
end
def jump_speed_real=(val)
@jump_speed_real = val * 40.0 / Graphics.frame_rate
end end
def move_frequency=(val) def move_frequency=(val)
return if val == @move_frequency return if val==@move_frequency
@move_frequency = val @move_frequency = val
# Time in seconds to wait between each action in a move route (not forced). # @move_frequency_real is the number of frames to wait between each action
# Specifically, this is the time to wait after the character stops moving # in a move route (not forced). Specifically, this is the number of frames
# because of the previous action. # to wait after the character stops moving because of the previous action.
# 1 => 4.75 seconds # By default, it is calculated from @move_frequency and has these values
# 2 => 3.6 seconds # (assuming 40 fps):
# 3 => 2.55 seconds # 1 => 190 # 4.75 seconds
# 4 => 1.6 seconds # 2 => 144 # 3.6 seconds
# 5 => 0.75 seconds # 3 => 102 # 2.55 seconds
# 6 => 0 seconds, i.e. continuous movement # 4 => 64 # 1.6 seconds
@command_delay = (40 - (val * 2)) * (6 - val) / 40.0 # 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 end
def bob_height def bob_height
@@ -196,10 +181,9 @@ class Game_Character
@direction = @prelock_direction if !@direction_fix && @prelock_direction != 0 @direction = @prelock_direction if !@direction_fix && @prelock_direction != 0
end end
#----------------------------------------------------------------------------- #=============================================================================
# Information from map data # Information from map data
#----------------------------------------------------------------------------- #=============================================================================
def map def map
return (@map) ? @map : $game_map return (@map) ? @map : $game_map
end end
@@ -209,65 +193,48 @@ class Game_Character
end end
def bush_depth def bush_depth
return 0 if respond_to?("name") && name[/airborne/i]
return @bush_depth || 0 return @bush_depth || 0
end end
def calculate_bush_depth def calculate_bush_depth
if @tile_id > 0 || @always_on_top || jumping? || (respond_to?("name") && name[/airborne/i]) if @tile_id > 0 || @always_on_top || jumping?
@bush_depth = 0 @bush_depth = 0
return else
end deep_bush = regular_bush = false
this_map = (self.map.valid?(@x, @y)) ? [self.map, @x, @y] : $map_factory&.getNewMap(@x, @y, self.map.map_id)
if this_map && this_map[0].deepBush?(this_map[1], this_map[2])
xbehind = @x + (@direction == 4 ? 1 : @direction == 6 ? -1 : 0) xbehind = @x + (@direction == 4 ? 1 : @direction == 6 ? -1 : 0)
ybehind = @y + (@direction == 8 ? 1 : @direction == 2 ? -1 : 0) ybehind = @y + (@direction == 8 ? 1 : @direction == 2 ? -1 : 0)
if moving? this_map = (self.map.valid?(@x, @y)) ? [self.map, @x, @y] : $MapFactory.getNewMap(@x, @y)
behind_map = (self.map.valid?(xbehind, ybehind)) ? [self.map, xbehind, ybehind] : $map_factory&.getNewMap(xbehind, ybehind, self.map.map_id) if this_map[0].deepBush?(this_map[1], this_map[2]) && self.map.deepBush?(xbehind, ybehind)
@bush_depth = Game_Map::TILE_HEIGHT if behind_map[0].deepBush?(behind_map[1], behind_map[2])
else
@bush_depth = Game_Map::TILE_HEIGHT @bush_depth = Game_Map::TILE_HEIGHT
end elsif !moving? && this_map[0].bush?(this_map[1], this_map[2])
elsif this_map && this_map[0].bush?(this_map[1], this_map[2]) && !moving?
@bush_depth = 12 @bush_depth = 12
else else
@bush_depth = 0 @bush_depth = 0
end end
end end
def fullPattern
case self.direction
when 2 then return self.pattern
when 4 then return self.pattern + 4
when 6 then return self.pattern + 8
when 8 then return self.pattern + 12
end
return 0
end end
#----------------------------------------------------------------------------- #=============================================================================
# Passability # Passability
#----------------------------------------------------------------------------- #=============================================================================
def passable?(x, y, d, strict = false)
def passable?(x, y, dir, strict = false) new_x = x + (d == 6 ? 1 : d == 4 ? -1 : 0)
new_x = x + (dir == 6 ? 1 : dir == 4 ? -1 : 0) new_y = y + (d == 2 ? 1 : d == 8 ? -1 : 0)
new_y = y + (dir == 2 ? 1 : dir == 8 ? -1 : 0)
return false unless self.map.valid?(new_x, new_y) return false unless self.map.valid?(new_x, new_y)
return true if @through return true if @through
if strict if strict
return false unless self.map.passableStrict?(x, y, dir, self) return false unless self.map.passableStrict?(x, y, d, self)
return false unless self.map.passableStrict?(new_x, new_y, 10 - dir, self) return false unless self.map.passableStrict?(new_x, new_y, 10 - d, self)
else else
return false unless self.map.passable?(x, y, dir, self) return false unless self.map.passable?(x, y, d, self)
return false unless self.map.passable?(new_x, new_y, 10 - dir, self) return false unless self.map.passable?(new_x, new_y, 10 - d, self)
end end
self.map.events.each_value do |event| for event in self.map.events.values
next if self == event || !event.at_coordinate?(new_x, new_y) || event.through next if self == event || !event.at_coordinate?(new_x, new_y) || event.through
return false if self != $game_player || event.character_name != "" return false if self != $game_player || event.character_name != ""
end end
if $game_player.x == new_x && $game_player.y == new_y && if $game_player.x == new_x && $game_player.y == new_y
!$game_player.through && @character_name != "" return false if !$game_player.through && @character_name != ""
return false
end end
return true return true
end end
@@ -276,24 +243,24 @@ class Game_Character
case dir case dir
when 2, 8 # Down, up when 2, 8 # Down, up
y_diff = (dir == 8) ? @height - 1 : 0 y_diff = (dir == 8) ? @height - 1 : 0
(start_x...(start_x + @width)).each do |i| for i in start_x...(start_x + @width)
return false if !passable?(i, start_y - y_diff, dir, strict) return false if !passable?(i, start_y - y_diff, dir, strict)
end end
return true return true
when 4, 6 # Left, right when 4, 6 # Left, right
x_diff = (dir == 6) ? @width - 1 : 0 x_diff = (dir == 6) ? @width - 1 : 0
((start_y - @height + 1)..start_y).each do |i| for i in (start_y - @height + 1)..start_y
return false if !passable?(start_x + x_diff, i, dir, strict) return false if !passable?(start_x + x_diff, i, dir, strict)
end end
return true return true
when 1, 3 # Down diagonals when 1, 3 # Down diagonals
# Treated as moving down first and then horizontally, because that # Treated as moving down first and then horizontally, because that
# describes which tiles the character's feet touch # describes which tiles the character's feet touch
(start_x...(start_x + @width)).each do |i| for i in start_x...(start_x + @width)
return false if !passable?(i, start_y, 2, strict) return false if !passable?(i, start_y, 2, strict)
end end
x_diff = (dir == 3) ? @width - 1 : 0 x_diff = (dir == 3) ? @width - 1 : 0
((start_y - @height + 1)..start_y).each do |i| for i in (start_y - @height + 1)..start_y
return false if !passable?(start_x + x_diff, i + 1, dir + 3, strict) return false if !passable?(start_x + x_diff, i + 1, dir + 3, strict)
end end
return true return true
@@ -301,12 +268,12 @@ class Game_Character
# Treated as moving horizontally first and then up, because that describes # Treated as moving horizontally first and then up, because that describes
# which tiles the character's feet touch # which tiles the character's feet touch
x_diff = (dir == 9) ? @width - 1 : 0 x_diff = (dir == 9) ? @width - 1 : 0
((start_y - @height + 1)..start_y).each do |i| for i in (start_y - @height + 1)..start_y
return false if !passable?(start_x + x_diff, i, dir - 3, strict) return false if !passable?(start_x + x_diff, i, dir - 3, strict)
end end
x_tile_offset = (dir == 9) ? 1 : -1 x_offset = (dir == 9) ? 1 : -1
(start_x...(start_x + @width)).each do |i| for i in start_x...(start_x + @width)
return false if !passable?(i + x_tile_offset, start_y - @height + 1, 8, strict) return false if !passable?(i + x_offset, start_y - @height + 1, 8, strict)
end end
return true return true
end end
@@ -317,19 +284,17 @@ class Game_Character
return can_move_from_coordinate?(@x, @y, dir, strict) return can_move_from_coordinate?(@x, @y, dir, strict)
end end
#----------------------------------------------------------------------------- #=============================================================================
# Screen position of the character # Screen position of the character
#----------------------------------------------------------------------------- #=============================================================================
def screen_x def screen_x
ret = ((@real_x.to_f - self.map.display_x) / Game_Map::X_SUBPIXELS).round ret = ((@real_x - self.map.display_x) / Game_Map::X_SUBPIXELS).round
ret += @width * Game_Map::TILE_WIDTH / 2 ret += @width * Game_Map::TILE_WIDTH / 2
ret += self.x_offset
return ret return ret
end end
def screen_y_ground def screen_y_ground
ret = ((@real_y.to_f - self.map.display_y) / Game_Map::Y_SUBPIXELS).round ret = ((@real_y - self.map.display_y) / Game_Map::Y_SUBPIXELS).round
ret += Game_Map::TILE_HEIGHT ret += Game_Map::TILE_HEIGHT
return ret return ret
end end
@@ -337,10 +302,13 @@ class Game_Character
def screen_y def screen_y
ret = screen_y_ground ret = screen_y_ground
if jumping? if jumping?
jump_progress = (@jump_fraction - 0.5).abs # 0.5 to 0 to 0.5 if @jump_count > 0
ret += @jump_peak * ((4 * (jump_progress**2)) - 1) jump_fraction = ((@jump_count * jump_speed_real / Game_Map::REAL_RES_X) - 0.5).abs # 0.5 to 0 to 0.5
else
jump_fraction = ((@jump_distance_left / @jump_distance) - 0.5).abs # 0.5 to 0 to 0.5
end
ret += @jump_peak * (4 * jump_fraction**2 - 1)
end end
ret += self.y_offset
return ret return ret
end end
@@ -349,7 +317,7 @@ class Game_Character
z = screen_y_ground z = screen_y_ground
if @tile_id > 0 if @tile_id > 0
begin begin
return z + (self.map.priorities[@tile_id] * 32) return z + self.map.priorities[@tile_id] * 32
rescue rescue
raise "Event's graphic is an out-of-range tile (event #{@id}, map #{self.map.map_id})" raise "Event's graphic is an out-of-range tile (event #{@id}, map #{self.map.map_id})"
end end
@@ -358,16 +326,16 @@ class Game_Character
return z + ((height > Game_Map::TILE_HEIGHT) ? Game_Map::TILE_HEIGHT - 1 : 0) return z + ((height > Game_Map::TILE_HEIGHT) ? Game_Map::TILE_HEIGHT - 1 : 0)
end end
#----------------------------------------------------------------------------- #=============================================================================
# Movement # Movement
#----------------------------------------------------------------------------- #=============================================================================
def moving? def moving?
return !@move_timer.nil? return @real_x != @x * Game_Map::REAL_RES_X ||
@real_y != @y * Game_Map::REAL_RES_Y
end end
def jumping? def jumping?
return !@jump_timer.nil? return (@jump_distance_left || 0) > 0 || @jump_count > 0
end end
def straighten def straighten
@@ -377,7 +345,7 @@ class Game_Character
end end
def force_move_route(move_route) def force_move_route(move_route)
if @original_move_route.nil? if @original_move_route == nil
@original_move_route = @move_route @original_move_route = @move_route
@original_move_route_index = @move_route_index @original_move_route_index = @move_route_index
end end
@@ -386,7 +354,6 @@ class Game_Character
@move_route_forcing = true @move_route_forcing = true
@prelock_direction = 0 @prelock_direction = 0
@wait_count = 0 @wait_count = 0
@wait_start = nil
move_type_custom move_type_custom
end end
@@ -396,15 +363,14 @@ class Game_Character
@real_x = @x * Game_Map::REAL_RES_X @real_x = @x * Game_Map::REAL_RES_X
@real_y = @y * Game_Map::REAL_RES_Y @real_y = @y * Game_Map::REAL_RES_Y
@prelock_direction = 0 @prelock_direction = 0
@moveto_happened = true
calculate_bush_depth calculate_bush_depth
triggerLeaveTile triggerLeaveTile
end end
def triggerLeaveTile def triggerLeaveTile
if @oldX && @oldY && @oldMap && if @oldX && @oldY && @oldMap &&
(@oldX != self.x || @oldY != self.y || @oldMap != self.map.map_id) (@oldX!=self.x || @oldY!=self.y || @oldMap!=self.map.map_id)
EventHandlers.trigger(:on_leave_tile, self, @oldMap, @oldX, @oldY) Events.onLeaveTile.trigger(self,self,@oldMap,@oldX,@oldY)
end end
@oldX = self.x @oldX = self.x
@oldY = self.y @oldY = self.y
@@ -416,10 +382,9 @@ class Game_Character
triggerLeaveTile triggerLeaveTile
end end
#----------------------------------------------------------------------------- #=============================================================================
# Movement commands # Movement commands
#----------------------------------------------------------------------------- #=============================================================================
def move_type_random def move_type_random
case rand(6) case rand(6)
when 0..3 then move_random when 0..3 then move_random
@@ -429,8 +394,8 @@ class Game_Character
end end
def move_type_toward_player def move_type_toward_player
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0)) sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0)) sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
if sx.abs + sy.abs >= 20 if sx.abs + sy.abs >= 20
move_random move_random
return return
@@ -444,14 +409,11 @@ class Game_Character
def move_type_custom def move_type_custom
return if jumping? || moving? return if jumping? || moving?
return if @move_route.list.size <= 1 # Empty move route while @move_route_index < @move_route.list.size
start_index = @move_route_index
(@move_route.list.size - 1).times do
command = @move_route.list[@move_route_index] command = @move_route.list[@move_route_index]
if command.code == 0 if command.code == 0
if @move_route.repeat if @move_route.repeat
@move_route_index = 0 @move_route_index = 0
command = @move_route.list[@move_route_index]
else else
if @move_route_forcing if @move_route_forcing
@move_route_forcing = false @move_route_forcing = false
@@ -460,12 +422,9 @@ class Game_Character
@original_move_route = nil @original_move_route = nil
end end
@stop_count = 0 @stop_count = 0
end
return return
end end
end
done_one_command = true
# The below move route commands wait for a frame (i.e. return) after
# executing them
if command.code <= 14 if command.code <= 14
case command.code case command.code
when 1 then move_down when 1 then move_down
@@ -486,13 +445,13 @@ class Game_Character
@move_route_index += 1 if @move_route.skippable || moving? || jumping? @move_route_index += 1 if @move_route.skippable || moving? || jumping?
return return
end end
# The below move route commands wait for a frame (i.e. return) after if command.code == 15 # Wait
# executing them @wait_count = (command.parameters[0] * Graphics.frame_rate / 20) - 1
if command.code >= 15 && command.code <= 26 @move_route_index += 1
return
end
if command.code >= 16 && command.code <= 26
case command.code case command.code
when 15 # Wait
@wait_count = command.parameters[0] / 20.0
@wait_start = System.uptime
when 16 then turn_down when 16 then turn_down
when 17 then turn_left when 17 then turn_left
when 18 then turn_right when 18 then turn_right
@@ -508,8 +467,6 @@ class Game_Character
@move_route_index += 1 @move_route_index += 1
return return
end end
# The below move route commands don't wait for a frame (i.e. return) after
# executing them
if command.code >= 27 if command.code >= 27
case command.code case command.code
when 27 when 27
@@ -554,14 +511,7 @@ class Game_Character
when 42 then @opacity = command.parameters[0] when 42 then @opacity = command.parameters[0]
when 43 then @blend_type = command.parameters[0] when 43 then @blend_type = command.parameters[0]
when 44 then pbSEPlay(command.parameters[0]) when 44 then pbSEPlay(command.parameters[0])
when 45 when 45 then eval(command.parameters[0])
eval(command.parameters[0])
if command.parameters[0][/^move_random_range/] ||
command.parameters[0][/^move_random_UD/] ||
command.parameters[0][/^move_random_LR/]
@move_route_index += 1
return
end
end end
@move_route_index += 1 @move_route_index += 1
end end
@@ -572,11 +522,8 @@ class Game_Character
turn_generic(dir) if turn_enabled turn_generic(dir) if turn_enabled
if can_move_in_direction?(dir) if can_move_in_direction?(dir)
turn_generic(dir) turn_generic(dir)
@move_initial_x = @x
@move_initial_y = @y
@x += (dir == 4) ? -1 : (dir == 6) ? 1 : 0 @x += (dir == 4) ? -1 : (dir == 6) ? 1 : 0
@y += (dir == 8) ? -1 : (dir == 2) ? 1 : 0 @y += (dir == 8) ? -1 : (dir == 2) ? 1 : 0
@move_timer = 0.0
increase_steps increase_steps
else else
check_event_trigger_touch(dir) check_event_trigger_touch(dir)
@@ -604,11 +551,8 @@ class Game_Character
@direction = (@direction == 6 ? 4 : @direction == 2 ? 8 : @direction) @direction = (@direction == 6 ? 4 : @direction == 2 ? 8 : @direction)
end end
if can_move_in_direction?(7) if can_move_in_direction?(7)
@move_initial_x = @x
@move_initial_y = @y
@x -= 1 @x -= 1
@y -= 1 @y -= 1
@move_timer = 0.0
increase_steps increase_steps
end end
end end
@@ -618,11 +562,8 @@ class Game_Character
@direction = (@direction == 4 ? 6 : @direction == 2 ? 8 : @direction) @direction = (@direction == 4 ? 6 : @direction == 2 ? 8 : @direction)
end end
if can_move_in_direction?(9) if can_move_in_direction?(9)
@move_initial_x = @x
@move_initial_y = @y
@x += 1 @x += 1
@y -= 1 @y -= 1
@move_timer = 0.0
increase_steps increase_steps
end end
end end
@@ -632,11 +573,8 @@ class Game_Character
@direction = (@direction == 6 ? 4 : @direction == 8 ? 2 : @direction) @direction = (@direction == 6 ? 4 : @direction == 8 ? 2 : @direction)
end end
if can_move_in_direction?(1) if can_move_in_direction?(1)
@move_initial_x = @x
@move_initial_y = @y
@x -= 1 @x -= 1
@y += 1 @y += 1
@move_timer = 0.0
increase_steps increase_steps
end end
end end
@@ -646,17 +584,13 @@ class Game_Character
@direction = (@direction == 4 ? 6 : @direction == 8 ? 2 : @direction) @direction = (@direction == 4 ? 6 : @direction == 8 ? 2 : @direction)
end end
if can_move_in_direction?(3) if can_move_in_direction?(3)
@move_initial_x = @x
@move_initial_y = @y
@x += 1 @x += 1
@y += 1 @y += 1
@move_timer = 0.0
increase_steps increase_steps
end end
end end
# Anticlockwise. def moveLeft90 # anticlockwise
def moveLeft90
case self.direction case self.direction
when 2 then move_right # down when 2 then move_right # down
when 4 then move_down # left when 4 then move_down # left
@@ -665,8 +599,7 @@ class Game_Character
end end
end end
# Clockwise. def moveRight90 # clockwise
def moveRight90
case self.direction case self.direction
when 2 then move_left # down when 2 then move_left # down
when 4 then move_up # left when 4 then move_up # left
@@ -684,23 +617,21 @@ class Game_Character
end end
end end
def move_random_range(xrange = -1, yrange = -1) def move_random_range(xrange=-1,yrange=-1)
dirs = [] # 0=down, 1=left, 2=right, 3=up dirs = [] # 0=down, 1=left, 2=right, 3=up
if xrange < 0 if xrange<0
dirs.push(1) dirs.push(1); dirs.push(2)
dirs.push(2) elsif xrange>0
elsif xrange > 0
dirs.push(1) if @x > @original_x - xrange dirs.push(1) if @x > @original_x - xrange
dirs.push(2) if @x < @original_x + xrange dirs.push(2) if @x < @original_x + xrange
end end
if yrange < 0 if yrange<0
dirs.push(0) dirs.push(0); dirs.push(3)
dirs.push(3) elsif yrange>0
elsif yrange > 0
dirs.push(0) if @y < @original_y + yrange dirs.push(0) if @y < @original_y + yrange
dirs.push(3) if @y > @original_y - yrange dirs.push(3) if @y > @original_y - yrange
end end
return if dirs.length == 0 return if dirs.length==0
case dirs[rand(dirs.length)] case dirs[rand(dirs.length)]
when 0 then move_down(false) when 0 then move_down(false)
when 1 then move_left(false) when 1 then move_left(false)
@@ -709,17 +640,17 @@ class Game_Character
end end
end end
def move_random_UD(range = -1) def move_random_UD(range=-1)
move_random_range(0, range) move_random_range(0,range)
end end
def move_random_LR(range = -1) def move_random_LR(range=-1)
move_random_range(range, 0) move_random_range(range,0)
end end
def move_toward_player def move_toward_player
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0)) sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0)) sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
return if sx == 0 && sy == 0 return if sx == 0 && sy == 0
abs_sx = sx.abs abs_sx = sx.abs
abs_sy = sy.abs abs_sy = sy.abs
@@ -727,29 +658,21 @@ class Game_Character
(rand(2) == 0) ? abs_sx += 1 : abs_sy += 1 (rand(2) == 0) ? abs_sx += 1 : abs_sy += 1
end end
if abs_sx > abs_sy if abs_sx > abs_sy
if abs_sx >= 1
(sx > 0) ? move_left : move_right (sx > 0) ? move_left : move_right
end
if !moving? && sy != 0 if !moving? && sy != 0
if abs_sy >= 1
(sy > 0) ? move_up : move_down (sy > 0) ? move_up : move_down
end end
end
else else
if abs_sy >= 1
(sy > 0) ? move_up : move_down (sy > 0) ? move_up : move_down
end
if !moving? && sx != 0 if !moving? && sx != 0
if abs_sx >= 1
(sx > 0) ? move_left : move_right (sx > 0) ? move_left : move_right
end end
end end
end end
end
def move_away_from_player def move_away_from_player
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0)) sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0)) sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
return if sx == 0 && sy == 0 return if sx == 0 && sy == 0
abs_sx = sx.abs abs_sx = sx.abs
abs_sy = sy.abs abs_sy = sy.abs
@@ -799,43 +722,42 @@ class Game_Character
end end
each_occupied_tile { |i, j| return if !passable?(i + x_plus, j + y_plus, 0) } each_occupied_tile { |i, j| return if !passable?(i + x_plus, j + y_plus, 0) }
end end
@jump_initial_x = @x @x = @x + x_plus
@jump_initial_y = @y @y = @y + y_plus
@x += x_plus real_distance = Math::sqrt(x_plus * x_plus + y_plus * y_plus)
@y += y_plus
@jump_timer = 0.0
real_distance = Math.sqrt((x_plus**2) + (y_plus**2))
distance = [1, real_distance].max distance = [1, real_distance].max
@jump_peak = distance * Game_Map::TILE_HEIGHT * 3 / 8 # 3/4 of tile for ledge jumping @jump_peak = distance * Game_Map::TILE_HEIGHT * 3 / 8 # 3/4 of tile for ledge jumping
@jump_distance = [x_plus.abs * Game_Map::REAL_RES_X, y_plus.abs * Game_Map::REAL_RES_Y].max @jump_distance = [x_plus.abs * Game_Map::REAL_RES_X, y_plus.abs * Game_Map::REAL_RES_Y].max
@jumping_on_spot = (real_distance == 0) @jump_distance_left = 1 # Just needs to be non-zero
increase_steps if real_distance > 0 # Jumping to somewhere else
@jump_count = 0
else # Jumping on the spot
@jump_speed_real = nil # Reset jump speed
@jump_count = Game_Map::REAL_RES_X / jump_speed_real # Number of frames to jump one tile
end
@stop_count = 0
if self.is_a?(Game_Player)
$PokemonTemp.dependentEvents.pbMoveDependentEvents
end
triggerLeaveTile
end end
def jumpForward(distance = 1) def jumpForward
return false if distance == 0
old_x = @x
old_y = @y
case self.direction case self.direction
when 2 then jump(0, distance) # down when 2 then jump(0,1) # down
when 4 then jump(-distance, 0) # left when 4 then jump(-1,0) # left
when 6 then jump(distance, 0) # right when 6 then jump(1,0) # right
when 8 then jump(0, -distance) # up when 8 then jump(0,-1) # up
end end
return @x != old_x || @y != old_y
end end
def jumpBackward(distance = 1) def jumpBackward
return false if distance == 0
old_x = @x
old_y = @y
case self.direction case self.direction
when 2 then jump(0, -distance) # down when 2 then jump(0,-1) # down
when 4 then jump(distance, 0) # left when 4 then jump(1,0) # left
when 6 then jump(-distance, 0) # right when 6 then jump(-1,0) # right
when 8 then jump(0, distance) # up when 8 then jump(0,1) # up
end end
return @x != old_x || @y != old_y
end end
def turn_generic(dir) def turn_generic(dir)
@@ -843,7 +765,7 @@ class Game_Character
oldDirection = @direction oldDirection = @direction
@direction = dir @direction = dir
@stop_count = 0 @stop_count = 0
check_event_trigger_after_turning if dir != oldDirection pbCheckEventTriggerAfterTurning if dir != oldDirection
end end
def turn_down; turn_generic(2); end def turn_down; turn_generic(2); end
@@ -892,8 +814,8 @@ class Game_Character
end end
def turn_toward_player def turn_toward_player
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0)) sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0)) sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
return if sx == 0 && sy == 0 return if sx == 0 && sy == 0
if sx.abs > sy.abs if sx.abs > sy.abs
(sx > 0) ? turn_left : turn_right (sx > 0) ? turn_left : turn_right
@@ -903,8 +825,8 @@ class Game_Character
end end
def turn_away_from_player def turn_away_from_player
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0)) sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0)) sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
return if sx == 0 && sy == 0 return if sx == 0 && sy == 0
if sx.abs > sy.abs if sx.abs > sy.abs
(sx > 0) ? turn_right : turn_left (sx > 0) ? turn_right : turn_left
@@ -913,36 +835,26 @@ class Game_Character
end end
end end
#----------------------------------------------------------------------------- #=============================================================================
# Updating # Updating
#----------------------------------------------------------------------------- #=============================================================================
def update def update
return if $game_temp.in_menu
time_now = System.uptime
@last_update_time = time_now if !@last_update_time || @last_update_time > time_now
@delta_t = time_now - @last_update_time
@last_update_time = time_now
return if @delta_t > 0.25 # Was in a menu; delay movement
@moved_last_frame = @moved_this_frame @moved_last_frame = @moved_this_frame
@stopped_last_frame = @stopped_this_frame @stopped_last_frame = @stopped_this_frame
@moved_this_frame = false if !$game_temp.in_menu
@stopped_this_frame = false
# Update command # Update command
update_command update_command
# Update movement # Update movement
(moving? || jumping?) ? update_move : update_stop (moving? || jumping?) ? update_move : update_stop
end
# Update animation # Update animation
update_pattern update_pattern
end end
def update_command def update_command
if @wait_count > 0 if @wait_count > 0
return if System.uptime - @wait_start < @wait_count @wait_count -= 1
@wait_count = 0 elsif @move_route_forcing
@wait_start = nil
end
if @move_route_forcing
move_type_custom move_type_custom
elsif !@starting && !lock? && !moving? && !jumping? elsif !@starting && !lock? && !moving? && !jumping?
update_command_new update_command_new
@@ -950,7 +862,15 @@ class Game_Character
end end
def update_command_new def update_command_new
if @stop_count >= @command_delay # @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 case @move_type
when 1 then move_type_random when 1 then move_type_random
when 2 then move_type_toward_player when 2 then move_type_toward_player
@@ -960,71 +880,53 @@ class Game_Character
end end
def update_move def update_move
if @move_timer # Move the character (the 0.1 catches rounding errors)
@move_timer += @delta_t distance = (jumping?) ? jump_speed_real : move_speed_real
# Move horizontally dest_x = @x * Game_Map::REAL_RES_X
if @x != @move_initial_x dest_y = @y * Game_Map::REAL_RES_Y
dist = (@move_initial_x - @x).abs if @real_x < dest_x
@real_x = lerp(@move_initial_x, @x, @move_time * dist, @move_timer) * Game_Map::REAL_RES_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 end
# Move vertically if @real_y < dest_y
if @y != @move_initial_y @real_y += distance
dist = (@move_initial_y - @y).abs @real_y = dest_y if @real_y > dest_y - 0.1
@real_y = lerp(@move_initial_y, @y, @move_time * dist, @move_timer) * Game_Map::REAL_RES_Y else
@real_y -= distance
@real_y = dest_y if @real_y < dest_y + 0.1
end end
elsif @jump_timer # Refresh how far is left to travel in a jump
self.jump_speed = 3 if !@jump_time if jumping?
@jump_timer += @delta_t @jump_count -= 1 if @jump_count > 0 # For stationary jumps only
dist = [(@x - @jump_initial_x).abs, (@y - @jump_initial_y).abs].max @jump_distance_left = [(dest_x - @real_x).abs, (dest_y - @real_y).abs].max
dist = 1 if dist == 0 # Jumping on spot
# Move horizontally
if @x != @jump_initial_x
@real_x = lerp(@jump_initial_x, @x, @jump_time * dist, @jump_timer) * Game_Map::REAL_RES_X
end
# Move vertically
if @y != @jump_initial_y
@real_y = lerp(@jump_initial_y, @y, @jump_time * dist, @jump_timer) * Game_Map::REAL_RES_Y
end
# Calculate how far through the jump we are (from 0 to 1)
@jump_fraction = @jump_timer / (@jump_time * dist)
end
# Snap to end position if close enough
@real_x = @x * Game_Map::REAL_RES_X if (@real_x - (@x * Game_Map::REAL_RES_X)).abs < Game_Map::X_SUBPIXELS / 2
@real_y = @y * Game_Map::REAL_RES_Y if (@real_y - (@y * Game_Map::REAL_RES_Y)).abs < Game_Map::Y_SUBPIXELS / 2
# End of move
if moving? && @move_timer >= @move_time &&
@real_x == @x * Game_Map::REAL_RES_X && @real_y == @y * Game_Map::REAL_RES_Y
@move_timer = nil
end
# End of jump
if jumping? && @jump_fraction >= 1
@jump_timer = nil
@jump_peak = 0
@jump_distance = 0
@jump_fraction = 0
@jumping_on_spot = false
end end
# End of a step, so perform events that happen at this time # End of a step, so perform events that happen at this time
if !jumping? && !moving? if !jumping? && !moving?
EventHandlers.trigger(:on_step_taken, self) Events.onStepTakenFieldMovement.trigger(self, self)
calculate_bush_depth calculate_bush_depth
@stopped_this_frame = true @stopped_this_frame = true
elsif !@moved_last_frame || @stopped_last_frame # Started a new step elsif !@moved_last_frame || @stopped_last_frame # Started a new step
calculate_bush_depth calculate_bush_depth
@stopped_this_frame = false
end end
# Increment animation counter # Increment animation counter
@anime_count += @delta_t if @walk_anime || @step_anime @anime_count += 1 if @walk_anime || @step_anime
@moved_this_frame = true @moved_this_frame = true
end end
def update_stop def update_stop
@anime_count += @delta_t if @step_anime @anime_count += 1 if @step_anime
@stop_count += @delta_t if !@starting && !lock? @stop_count += 1 if !@starting && !lock?
@moved_this_frame = false
@stopped_this_frame = false
end end
def update_pattern def update_pattern
return if @lock_pattern return if @lock_pattern
# return if @jumping_on_spot # Don't animate if jumping on the spot # return if @jump_count > 0 # Don't animate if jumping on the spot
# Character has stopped moving, return to original pattern # Character has stopped moving, return to original pattern
if @moved_last_frame && !@moved_this_frame && !@step_anime if @moved_last_frame && !@moved_this_frame && !@step_anime
@pattern = @original_pattern @pattern = @original_pattern
@@ -1040,10 +942,12 @@ class Game_Character
# Calculate how many frames each pattern should display for, i.e. the time # 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 # it takes to move half a tile (or a whole tile if cycling). We assume the
# game uses square tiles. # game uses square tiles.
pattern_time = pattern_update_speed / 4 # 4 frames per cycle in a charset real_speed = (jumping?) ? jump_speed_real : move_speed_real
return if @anime_count < pattern_time frames_per_pattern = Game_Map::REAL_RES_X / (real_speed * 2.0)
frames_per_pattern *= 2 if move_speed >= 5 # Cycling speed or faster
return if @anime_count < frames_per_pattern
# Advance to the next animation frame # Advance to the next animation frame
@pattern = (@pattern + 1) % 4 @pattern = (@pattern + 1) % 4
@anime_count -= pattern_time @anime_count -= frames_per_pattern
end end
end end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
class Game_Event < Game_Character class Game_Event < Game_Character
attr_reader :map_id attr_reader :map_id
attr_reader :trigger attr_reader :trigger
@@ -9,7 +6,7 @@ class Game_Event < Game_Character
attr_reader :tempSwitches # Temporary self-switches attr_reader :tempSwitches # Temporary self-switches
attr_accessor :need_refresh attr_accessor :need_refresh
def initialize(map_id, event, map = nil) def initialize(map_id, event, map=nil)
super(map) super(map)
@map_id = map_id @map_id = map_id
@event = event @event = event
@@ -57,7 +54,7 @@ class Game_Event < Game_Character
end end
def tsOn?(c) def tsOn?(c)
return @tempSwitches && @tempSwitches[c] == true return @tempSwitches && @tempSwitches[c]==true
end end
def tsOff?(c) def tsOff?(c)
@@ -65,62 +62,56 @@ class Game_Event < Game_Character
end end
def setTempSwitchOn(c) def setTempSwitchOn(c)
@tempSwitches[c] = true @tempSwitches[c]=true
refresh refresh
end end
def setTempSwitchOff(c) def setTempSwitchOff(c)
@tempSwitches[c] = false @tempSwitches[c]=false
refresh refresh
end end
def isOff?(c) def isOff?(c)
return !$game_self_switches[[@map_id, @event.id, c]] return !$game_self_switches[[@map_id,@event.id,c]]
end end
def switchIsOn?(id) def switchIsOn?(id)
switch_name = $data_system.switches[id] switchname = $data_system.switches[id]
if switch_name && switch_name[/^s\:/] return false if !switchname
if switchname[/^s\:/]
return eval($~.post_match) return eval($~.post_match)
end else
return $game_switches[id] return $game_switches[id]
end end
def variableIsLessThan?(id, value)
variable_name = $data_system.variables[id]
if variable_name && variable_name[/^s\:/]
return eval($~.post_match) < value
end
return $game_variables[id] < value
end end
def variable def variable
return nil if !$PokemonGlobal.eventvars return nil if !$PokemonGlobal.eventvars
return $PokemonGlobal.eventvars[[@map_id, @event.id]] return $PokemonGlobal.eventvars[[@map_id,@event.id]]
end end
def setVariable(variable) def setVariable(variable)
$PokemonGlobal.eventvars[[@map_id, @event.id]] = variable $PokemonGlobal.eventvars[[@map_id,@event.id]]=variable
end end
def varAsInt def varAsInt
return 0 if !$PokemonGlobal.eventvars return 0 if !$PokemonGlobal.eventvars
return $PokemonGlobal.eventvars[[@map_id, @event.id]].to_i return $PokemonGlobal.eventvars[[@map_id,@event.id]].to_i
end end
def expired?(secs = 86_400) def expired?(secs=86400)
ontime = self.variable ontime=self.variable
time = pbGetTimeNow time=pbGetTimeNow
return ontime && (time.to_i > ontime + secs) return ontime && (time.to_i>ontime+secs)
end end
def expiredDays?(days = 1) def expiredDays?(days=1)
ontime = self.variable.to_i ontime=self.variable.to_i
return false if !ontime return false if !ontime
now = pbGetTimeNow now=pbGetTimeNow
elapsed = (now.to_i - ontime) / 86_400 elapsed=(now.to_i-ontime)/86400
elapsed += 1 if (now.to_i - ontime) % 86_400 > ((now.hour * 3600) + (now.min * 60) + now.sec) elapsed+=1 if (now.to_i-ontime)%86400>(now.hour*3600+now.min*60+now.sec)
return elapsed >= days return elapsed>=days
end end
def cooledDown?(seconds) def cooledDown?(seconds)
@@ -136,11 +127,10 @@ class Game_Event < Game_Character
end end
def onEvent? def onEvent?
return @map_id == $game_player.map_id && at_coordinate?($game_player.x, $game_player.y) return @map_id == $game_map.map_id && at_coordinate?($game_player.x, $game_player.y)
end end
def over_trigger? def over_trigger?
return false if @map_id != $game_player.map_id
return false if @character_name != "" && !@through return false if @character_name != "" && !@through
return false if @event.name[/hiddenitem/i] return false if @event.name[/hiddenitem/i]
each_occupied_tile do |i, j| each_occupied_tile do |i, j|
@@ -149,10 +139,19 @@ class Game_Event < Game_Character
return false return false
end 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(dir) def check_event_trigger_touch(dir)
return if @map_id != $game_player.map_id
return if @trigger != 2 # Event touch
return if $game_system.map_interpreter.running? return if $game_system.map_interpreter.running?
return if @trigger != 2 # Event touch
case dir case dir
when 2 when 2
return if $game_player.y != @y + 1 return if $game_player.y != @y + 1
@@ -168,41 +167,12 @@ class Game_Event < Game_Character
start start
end end
def check_event_trigger_after_turning
return if @map_id != $game_player.map_id
return if @trigger != 2 # Not Event Touch
return if $game_system.map_interpreter.running? || @starting
return if !self.name[/(?:sight|trainer)\((\d+)\)/i]
distance = $~[1].to_i
return if !pbEventCanReachPlayer?(self, $game_player, distance)
return if jumping? || over_trigger?
start
end
def check_event_trigger_after_moving
return if @map_id != $game_player.map_id
return if @trigger != 2 # Not Event Touch
return if $game_system.map_interpreter.running? || @starting
if self.name[/(?:sight|trainer)\((\d+)\)/i]
distance = $~[1].to_i
return if !pbEventCanReachPlayer?(self, $game_player, distance)
elsif self.name[/counter\((\d+)\)/i]
distance = $~[1].to_i
return if !pbEventFacesPlayer?(self, $game_player, distance)
else
return
end
return if jumping? || over_trigger?
start
end
def check_event_trigger_auto def check_event_trigger_auto
case @trigger if @trigger == 2 # Event touch
when 2 # Event touch if at_coordinate?($game_player.x, $game_player.y)
if at_coordinate?($game_player.x, $game_player.y) && !jumping? && over_trigger? start if !jumping? && over_trigger?
start
end end
when 3 # Autorun elsif @trigger == 3 # Autorun
start start
end end
end end
@@ -210,11 +180,11 @@ class Game_Event < Game_Character
def refresh def refresh
new_page = nil new_page = nil
unless @erased unless @erased
@event.pages.reverse.each do |page| for page in @event.pages.reverse
c = page.condition c = page.condition
next if c.switch1_valid && !switchIsOn?(c.switch1_id) next if c.switch1_valid && !switchIsOn?(c.switch1_id)
next if c.switch2_valid && !switchIsOn?(c.switch2_id) next if c.switch2_valid && !switchIsOn?(c.switch2_id)
next if c.variable_valid && variableIsLessThan?(c.variable_id, c.variable_value) next if c.variable_valid && $game_variables[c.variable_id] < c.variable_value
if c.self_switch_valid if c.self_switch_valid
key = [@map_id, @event.id, c.self_switch_ch] key = [@map_id, @event.id, c.self_switch_ch]
next if $game_self_switches[key] != true next if $game_self_switches[key] != true
@@ -226,7 +196,7 @@ class Game_Event < Game_Character
return if new_page == @page return if new_page == @page
@page = new_page @page = new_page
clear_starting clear_starting
if @page.nil? if @page == nil
@tile_id = 0 @tile_id = 0
@character_name = "" @character_name = ""
@character_hue = 0 @character_hue = 0
@@ -266,50 +236,43 @@ class Game_Event < Game_Character
@trigger = @page.trigger @trigger = @page.trigger
@list = @page.list @list = @page.list
@interpreter = nil @interpreter = nil
@interpreter = Interpreter.new if @trigger == 4 # Parallel Process if @trigger == 4 # Parallel Process
@interpreter = Interpreter.new
end
check_event_trigger_auto check_event_trigger_auto
end end
def should_update?(recalc = false) def should_update?(recalc=false)
return @to_update if !recalc return @to_update if !recalc
return true if @updated_last_frame
return true if @trigger && (@trigger == 3 || @trigger == 4) return true if @trigger && (@trigger == 3 || @trigger == 4)
return true if @move_route_forcing || @moveto_happened return true if @move_route_forcing
return true if @event.name[/update/i] return true if @event.name[/update/i]
range = 2 # Number of tiles 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 > 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_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 - @sprite_size[1] > Graphics.height + range * Game_Map::TILE_HEIGHT
return false if self.screen_y_ground < -range * Game_Map::TILE_HEIGHT return false if self.screen_y_ground < -range * Game_Map::TILE_HEIGHT
return true return true
end end
def update def update
@to_update = should_update?(true) @to_update = should_update?(true)
@updated_last_frame = false
return if !@to_update return if !@to_update
@updated_last_frame = true
@moveto_happened = false
last_moving = moving? last_moving = moving?
super super
check_event_trigger_after_moving if !moving? && last_moving if !moving? && last_moving
$game_player.pbCheckEventTriggerFromDistance([2])
end
if @need_refresh if @need_refresh
@need_refresh = false @need_refresh = false
refresh refresh
end end
check_event_trigger_auto check_event_trigger_auto
if @interpreter if @interpreter != nil
@interpreter.setup(@list, @event.id, @map_id) if !@interpreter.running? unless @interpreter.running?
@interpreter.setup(@list, @event.id, @map_id)
end
@interpreter.update @interpreter.update
end end
end end
def update_move
was_jumping = jumping?
super
if was_jumping && !jumping? && !@transparent && (@tile_id > 0 || @character_name != "")
spriteset = $scene.spriteset(map_id)
spriteset&.addUserAnimation(Settings::DUST_ANIMATION_ID, self.x, self.y, true, 1)
end
end
end end

View File

@@ -1,616 +0,0 @@
#===============================================================================
# ** 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 :charsetData
attr_accessor :encounter_count
SCREEN_CENTER_X = ((Settings::SCREEN_WIDTH / 2) - (Game_Map::TILE_WIDTH / 2)) * Game_Map::X_SUBPIXELS
SCREEN_CENTER_Y = ((Settings::SCREEN_HEIGHT / 2) - (Game_Map::TILE_HEIGHT / 2)) * Game_Map::Y_SUBPIXELS
# Time in seconds for one cycle of bobbing (playing 4 charset frames) while
# surfing or diving.
SURF_BOB_DURATION = 1.5
def initialize(*arg)
super(*arg)
@lastdir = 0
@lastdirframe = 0
end
def map
@map = nil
return $game_map
end
def map_id
return $game_map.map_id
end
def screen_z(height = 0)
ret = super
return ret + 1
end
def has_follower?
return $PokemonGlobal.followers.length > 0
end
def can_map_transfer_with_follower?
return $PokemonGlobal.followers.length == 0
end
def can_ride_vehicle_with_follower?
return $PokemonGlobal.followers.length == 0
end
#-----------------------------------------------------------------------------
def can_run?
return @move_speed > 3 if @move_route_forcing
return false if $game_temp.in_menu || $game_temp.in_battle ||
$game_temp.message_window_showing || pbMapInterpreterRunning?
return false if !$player.has_running_shoes && !$PokemonGlobal.diving &&
!$PokemonGlobal.surfing && !$PokemonGlobal.bicycle
return false if jumping?
return false if pbTerrainTag.must_walk
return ($PokemonSystem.runstyle == 1) ^ Input.press?(Input::BACK)
end
def set_movement_type(type)
meta = GameData::PlayerMetadata.get($player&.character_ID || 1)
new_charset = nil
case type
when :fishing
new_charset = pbGetPlayerCharset(meta.fish_charset)
when :surf_fishing
new_charset = pbGetPlayerCharset(meta.surf_fish_charset)
when :diving, :diving_fast, :diving_jumping, :diving_stopped
self.move_speed = 3 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.dive_charset)
when :surfing, :surfing_fast, :surfing_jumping, :surfing_stopped
if !@move_route_forcing
self.move_speed = (type == :surfing_jumping) ? 3 : 4
end
new_charset = pbGetPlayerCharset(meta.surf_charset)
when :descending_waterfall, :ascending_waterfall
self.move_speed = 2 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.surf_charset)
when :cycling, :cycling_fast, :cycling_jumping, :cycling_stopped
if !@move_route_forcing
self.move_speed = (type == :cycling_jumping) ? 3 : 5
end
new_charset = pbGetPlayerCharset(meta.cycle_charset)
when :running
self.move_speed = 4 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.run_charset)
when :ice_sliding
self.move_speed = 4 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.walk_charset)
else # :walking, :jumping, :walking_stopped
self.move_speed = 3 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.walk_charset)
end
@character_name = new_charset if new_charset
end
# Called when the player's character or outfit changes. Assumes the player
# isn't moving.
def refresh_charset
meta = GameData::PlayerMetadata.get($player&.character_ID || 1)
new_charset = nil
if $PokemonGlobal&.diving
new_charset = pbGetPlayerCharset(meta.dive_charset)
elsif $PokemonGlobal&.surfing
new_charset = pbGetPlayerCharset(meta.surf_charset)
elsif $PokemonGlobal&.bicycle
new_charset = pbGetPlayerCharset(meta.cycle_charset)
else
new_charset = pbGetPlayerCharset(meta.walk_charset)
end
@character_name = new_charset if new_charset
end
#-----------------------------------------------------------------------------
def bump_into_object
return if @bump_time_start && (System.uptime - @bump_time_start < @move_time)
pbSEPlay("Overrides bump") if !@move_route_forcing
$stats.bump_count += 1
@bump_time_start = System.uptime
end
def add_move_distance_to_stats(distance = 1)
if $PokemonGlobal&.diving || $PokemonGlobal&.surfing
$stats.distance_surfed += distance
elsif $PokemonGlobal&.bicycle
$stats.distance_cycled += distance
else
$stats.distance_walked += distance
end
$stats.distance_slid_on_ice += distance if $PokemonGlobal.ice_sliding
end
def move_generic(dir, turn_enabled = true)
turn_generic(dir, true) if turn_enabled
if !$game_temp.encounter_triggered
if can_move_in_direction?(dir)
x_offset = (dir == 4) ? -1 : (dir == 6) ? 1 : 0
y_offset = (dir == 8) ? -1 : (dir == 2) ? 1 : 0
# Jump over ledges
if pbFacingTerrainTag.ledge
if jumpForward(2)
pbSEPlay("Overrides jump")
increase_steps
end
return
elsif pbFacingTerrainTag.waterfall_crest && dir == 2
$PokemonGlobal.descending_waterfall = true
$game_player.through = true
$stats.waterfalls_descended += 1
end
# Jumping out of surfing back onto land
return if pbEndSurf(x_offset, y_offset)
# General movement
turn_generic(dir, true)
if !$game_temp.encounter_triggered
@move_initial_x = @x
@move_initial_y = @y
@x += x_offset
@y += y_offset
@move_timer = 0.0
add_move_distance_to_stats(x_offset.abs + y_offset.abs)
increase_steps
end
elsif !check_event_trigger_touch(dir)
bump_into_object
end
end
$game_temp.encounter_triggered = false
end
def turn_generic(dir, keep_enc_indicator = false)
old_direction = @direction
super(dir)
if @direction != old_direction && !@move_route_forcing && !pbMapInterpreterRunning?
EventHandlers.trigger(:on_player_change_direction)
$game_temp.encounter_triggered = false if !keep_enc_indicator
end
end
def jump(x_plus, y_plus)
old_x = @x
old_y = @y
super
add_move_distance_to_stats(x_plus.abs + y_plus.abs) if @x != old_x || @y != old_y
end
#-----------------------------------------------------------------------------
def pbTerrainTag(countBridge = false)
return $map_factory.getTerrainTagFromCoords(self.map.map_id, @x, @y, countBridge) if $map_factory
return $game_map.terrain_tag(@x, @y, countBridge)
end
def pbFacingEvent(ignoreInterpreter = false)
return nil if $game_system.map_interpreter.running? && !ignoreInterpreter
# Check the tile in front of the player for events
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)
$game_map.events.each_value do |event|
next if !event.at_coordinate?(new_x, new_y)
next if event.jumping? || event.over_trigger?
return event
end
# If the tile in front is a counter, check one tile beyond that for events
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)
$game_map.events.each_value do |event|
next if !event.at_coordinate?(new_x, new_y)
next if event.jumping? || event.over_trigger?
return event
end
end
return nil
end
def pbFacingTerrainTag(dir = nil)
dir = self.direction if !dir
return $map_factory.getFacingTerrainTag(dir, self) if $map_factory
facing = pbFacingTile(dir, self)
return $game_map.terrain_tag(facing[1], facing[2])
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, dir, strict = false)
# Get new coordinates
new_x = x + (dir == 6 ? 1 : dir == 4 ? -1 : 0)
new_y = y + (dir == 2 ? 1 : dir == 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 !$map_factory
return $map_factory.isPassableFromEdge?(new_x, new_y, 10 - dir)
end
# If debug mode is ON and Ctrl key was pressed
return true if $DEBUG && Input.press?(Input::CTRL)
return super
end
# Set Map Display Position to Center of Screen
def center(x, y)
self.map.display_x = (x * Game_Map::REAL_RES_X) - SCREEN_CENTER_X
self.map.display_y = (y * Game_Map::REAL_RES_Y) - SCREEN_CENTER_Y
end
# Move to Designated Position
# x : x-coordinate
# y : y-coordinate
def moveto(x, y)
super
center(x, y)
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
def refresh
@opacity = 255
@blend_type = 0
end
#-----------------------------------------------------------------------------
def pbTriggeredTrainerEvents(triggers, checkIfRunning = true, trainer_only = false)
result = []
# If event is running
return result if checkIfRunning && $game_system.map_interpreter.running?
# All event loops
$game_map.events.each_value do |event|
next if !triggers.include?(event.trigger)
next if !event.name[/trainer\((\d+)\)/i] && (trainer_only || !event.name[/sight\((\d+)\)/i])
distance = $~[1].to_i
next if !pbEventCanReachPlayer?(event, self, distance)
next if event.jumping? || event.over_trigger?
result.push(event)
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
$game_map.events.each_value do |event|
next if !triggers.include?(event.trigger)
next if !event.name[/counter\((\d+)\)/i]
distance = $~[1].to_i
next if !pbEventFacesPlayer?(event, self, distance)
next if event.jumping? || event.over_trigger?
result.push(event)
end
return result
end
def check_event_trigger_after_turning; end
def pbCheckEventTriggerFromDistance(triggers)
events = pbTriggeredTrainerEvents(triggers)
events.concat(pbTriggeredCounterEvents(triggers))
return false if events.length == 0
ret = false
events.each do |event|
event.start
ret = true if event.starting
end
return ret
end
# Trigger event(s) at the same coordinates as self with the appropriate
# trigger(s) that can be triggered
def check_event_trigger_here(triggers)
result = false
# If event is running
return result if $game_system.map_interpreter.running?
# All event loops
$game_map.events.each_value do |event|
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(@x, @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 if event.starting
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
$game_map.events.each_value do |event|
next if !triggers.include?(event.trigger)
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(new_x, new_y)
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true if event.starting
end
# If fitting event is not found
if result == false && $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
$game_map.events.each_value do |event|
next if !triggers.include?(event.trigger)
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(new_x, new_y)
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true if event.starting
end
end
return result
end
# Touch Event Starting Determinant
def check_event_trigger_touch(dir)
result = false
return result if $game_system.map_interpreter.running?
# All event loops
x_offset = (dir == 4) ? -1 : (dir == 6) ? 1 : 0
y_offset = (dir == 8) ? -1 : (dir == 2) ? 1 : 0
$game_map.events.each_value do |event|
next if ![1, 2].include?(event.trigger) # Overrides touch, event touch
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(@x + x_offset, @y + y_offset)
if event.name[/(?:sight|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
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true if event.starting
end
return result
end
#-----------------------------------------------------------------------------
def update
last_real_x = @real_x
last_real_y = @real_y
@last_terrain_tag = pbTerrainTag
super
update_stop if $game_temp.in_menu && @stopped_last_frame
update_screen_position(last_real_x, last_real_y)
# Update dependent events
if (!@moved_last_frame || @stopped_last_frame) && (moving? || jumping?)
$game_temp.followers.move_followers
end
$game_temp.followers.update
update_event_triggering
end
def update_command_new
dir = Input.dir4
if $PokemonGlobal.forced_movement?
move_forward
@last_input_time = nil
return
elsif dir <= 0
@last_input_time = nil
return
end
return if pbMapInterpreterRunning? || $game_temp.message_window_showing ||
$game_temp.in_mini_update || $game_temp.in_menu
# Move player in the direction the directional button is being pressed
if @moved_last_frame ||
(dir == direction && (!@last_input_time || System.uptime - @last_input_time >= 0.075))
case dir
when 2 then move_down
when 4 then move_left
when 6 then move_right
when 8 then move_up
end
@last_input_time = nil
elsif dir != direction
case dir
when 2 then turn_down
when 4 then turn_left
when 6 then turn_right
when 8 then turn_up
end
@last_input_time = System.uptime
end
end
def update_move
if !@moved_last_frame || @stopped_last_frame # Started a new step
if $PokemonGlobal.ice_sliding || @last_terrain_tag.ice
set_movement_type(:ice_sliding)
elsif $PokemonGlobal.descending_waterfall
set_movement_type(:descending_waterfall)
elsif $PokemonGlobal.ascending_waterfall
set_movement_type(:ascending_waterfall)
else
faster = can_run?
if $PokemonGlobal&.diving
set_movement_type((faster) ? :diving_fast : :diving)
elsif $PokemonGlobal&.surfing
set_movement_type((faster) ? :surfing_fast : :surfing)
elsif $PokemonGlobal&.bicycle
set_movement_type((faster) ? :cycling_fast : :cycling)
else
set_movement_type((faster) ? :running : :walking)
end
end
if jumping?
if $PokemonGlobal&.diving
set_movement_type(:diving_jumping)
elsif $PokemonGlobal&.surfing
set_movement_type(:surfing_jumping)
elsif $PokemonGlobal&.bicycle
set_movement_type(:cycling_jumping)
else
set_movement_type(:jumping) # Walking speed/charset while jumping
end
end
end
was_jumping = jumping?
super
if was_jumping && !jumping? && !@transparent && (@tile_id > 0 || @character_name != "")
if !$PokemonGlobal.surfing || $game_temp.ending_surf
spriteset = $scene.spriteset(map_id)
spriteset&.addUserAnimation(Settings::DUST_ANIMATION_ID, self.x, self.y, true, 1)
end
end
end
def update_stop
if @stopped_last_frame
if $PokemonGlobal&.diving
set_movement_type(:diving_stopped)
elsif $PokemonGlobal&.surfing
set_movement_type(:surfing_stopped)
elsif $PokemonGlobal&.bicycle
set_movement_type(:cycling_stopped)
else
set_movement_type(:walking_stopped)
end
end
super
end
def update_pattern
if $PokemonGlobal&.surfing || $PokemonGlobal&.diving
bob_pattern = (4 * System.uptime / SURF_BOB_DURATION).to_i % 4
@pattern = bob_pattern if !@lock_pattern
@pattern_surf = bob_pattern
@bob_height = (bob_pattern >= 2) ? 2 : 0
@anime_count = 0
else
@bob_height = 0
super
end
end
# Track the player on-screen as they move.
def update_screen_position(last_real_x, last_real_y)
return if self.map.scrolling? || !(@moved_last_frame || @moved_this_frame)
if (@real_x < last_real_x && @real_x < $game_map.display_x + SCREEN_CENTER_X) ||
(@real_x > last_real_x && @real_x > $game_map.display_x + SCREEN_CENTER_X)
self.map.display_x += @real_x - last_real_x
end
if (@real_y < last_real_y && @real_y < $game_map.display_y + SCREEN_CENTER_Y) ||
(@real_y > last_real_y && @real_y > $game_map.display_y + SCREEN_CENTER_Y)
self.map.display_y += @real_y - last_real_y
end
end
def update_event_triggering
return if moving? || jumping? || $PokemonGlobal.forced_movement?
# Try triggering events upon walking into them/in front of them
if @moved_this_frame
$game_temp.followers.turn_followers
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
end
end
#===============================================================================
#
#===============================================================================
def pbGetPlayerCharset(charset, trainer = nil, force = false)
trainer = $player if !trainer
outfit = (trainer) ? trainer.outfit : 0
return nil if !force && $game_player&.charsetData &&
$game_player.charsetData[0] == trainer.character_ID &&
$game_player.charsetData[1] == charset &&
$game_player.charsetData[2] == outfit
$game_player.charsetData = [trainer.character_ID, charset, outfit] if $game_player
ret = charset
if pbResolveBitmap("Graphics/Characters/" + ret + "_" + outfit.to_s)
ret = ret + "_" + outfit.to_s
end
return ret
end
def pbUpdateVehicle
if $PokemonGlobal&.diving
$game_player.set_movement_type(:diving_stopped)
elsif $PokemonGlobal&.surfing
$game_player.set_movement_type(:surfing_stopped)
elsif $PokemonGlobal&.bicycle
$game_player.set_movement_type(:cycling_stopped)
else
$game_player.set_movement_type(:walking_stopped)
end
end
def pbCancelVehicles(destination = nil, cancel_swimming = true)
$PokemonGlobal.surfing = false if cancel_swimming
$PokemonGlobal.diving = false if cancel_swimming
$PokemonGlobal.bicycle = false if !destination || !pbCanUseBike?(destination)
pbUpdateVehicle
end
def pbCanUseBike?(map_id)
map_metadata = GameData::MapMetadata.try_get(map_id)
return false if !map_metadata
return map_metadata.always_bicycle || map_metadata.can_bicycle || map_metadata.outdoor_map
end
def pbMountBike
return if $PokemonGlobal.bicycle
$PokemonGlobal.bicycle = true
$stats.cycle_count += 1
pbUpdateVehicle
bike_bgm = GameData::Metadata.get.bicycle_BGM
pbCueBGM(bike_bgm, 0.4) if bike_bgm
pbSEPlay("Bicycle")
pbPokeRadarCancel
end
def pbDismountBike
return if !$PokemonGlobal.bicycle
$PokemonGlobal.bicycle = false
pbUpdateVehicle
$game_map.autoplayAsCue
end

View File

@@ -1,56 +0,0 @@
#===============================================================================
# ** 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
def initialize(common_event_id)
@common_event_id = common_event_id
@interpreter = nil
refresh
end
def name
return $data_common_events[@common_event_id].name
end
def trigger
return $data_common_events[@common_event_id].trigger
end
def switch_id
return $data_common_events[@common_event_id].switch_id
end
def list
return $data_common_events[@common_event_id].list
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 refresh
# Create an interpreter for parallel process if necessary
if self.trigger == 2 && switchIsOn?(self.switch_id)
@interpreter = Interpreter.new if @interpreter.nil?
else
@interpreter = nil
end
end
def update
return if !@interpreter
# Set up event if interpreter is not running
@interpreter.setup(self.list, 0) if !@interpreter.running?
# Update interpreter
@interpreter.update
end
end

View File

@@ -0,0 +1,442 @@
#===============================================================================
# ** 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
SCREEN_CENTER_X = (Settings::SCREEN_WIDTH / 2 - Game_Map::TILE_WIDTH / 2) * Game_Map::X_SUBPIXELS
SCREEN_CENTER_Y = (Settings::SCREEN_HEIGHT / 2 - Game_Map::TILE_HEIGHT / 2) * Game_Map::Y_SUBPIXELS
def initialize(*arg)
super(*arg)
@lastdir=0
@lastdirframe=0
@bump_se=0
end
def map
@map = nil
return $game_map
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_generic(dir, turn_enabled = true)
turn_generic(dir, true) if turn_enabled
if !$PokemonTemp.encounterTriggered
if can_move_in_direction?(dir)
x_offset = (dir == 4) ? -1 : (dir == 6) ? 1 : 0
y_offset = (dir == 8) ? -1 : (dir == 2) ? 1 : 0
return if pbLedge(x_offset, y_offset)
return if pbEndSurf(x_offset, y_offset)
turn_generic(dir, true)
if !$PokemonTemp.encounterTriggered
@x += x_offset
@y += y_offset
$PokemonTemp.dependentEvents.pbMoveDependentEvents
increase_steps
end
elsif !check_event_trigger_touch(dir)
bump_into_object
end
end
$PokemonTemp.encounterTriggered = false
end
def turn_generic(dir, keep_enc_indicator = false)
old_direction = @direction
super(dir)
if @direction != old_direction && !@move_route_forcing && !pbMapInterpreterRunning?
Events.onChangeDirection.trigger(self, self)
$PokemonTemp.encounterTriggered = false if !keep_enc_indicator
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) && triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
result.push(event) if !event.jumping? && !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) && triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
result.push(event) if !event.jumping? && !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 pbTerrainTag(countBridge = false)
return $MapFactory.getTerrainTag(self.map.map_id, @x, @y, countBridge) if $MapFactory
return $game_map.terrain_tag(@x, @y, countBridge)
end
def pbFacingEvent(ignoreInterpreter=false)
return nil if $game_system.map_interpreter.running? && !ignoreInterpreter
# Check the tile in front of the player for events
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.at_coordinate?(new_x, new_y)
next if event.jumping? || event.over_trigger?
return event
end
# If the tile in front is a counter, check one tile beyond that for events
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.at_coordinate?(new_x, new_y)
next if event.jumping? || event.over_trigger?
return event
end
end
return nil
end
def pbFacingTerrainTag(dir = nil)
dir = self.direction if !dir
return $MapFactory.getFacingTerrainTag(dir, self) if $MapFactory
facing = pbFacingTile(dir, self)
return $game_map.terrain_tag(facing[1], facing[2])
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, strict = false)
# 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 && Input.press?(Input::CTRL)
return super
end
#-----------------------------------------------------------------------------
# * Set Map Display Position to Center of Screen
#-----------------------------------------------------------------------------
def center(x, y)
self.map.display_x = x * Game_Map::REAL_RES_X - SCREEN_CENTER_X
self.map.display_y = y * Game_Map::REAL_RES_Y - SCREEN_CENTER_Y
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
#-----------------------------------------------------------------------------
# * Trigger event(s) at the same coordinates as self with the appropriate
# trigger(s) that can be triggered
#-----------------------------------------------------------------------------
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.at_coordinate?(@x, @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.at_coordinate?(new_x, 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.at_coordinate?(new_x, 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(dir)
result = false
return result if $game_system.map_interpreter.running?
# All event loops
x_offset = (dir == 4) ? -1 : (dir == 6) ? 1 : 0
y_offset = (dir == 8) ? -1 : (dir == 2) ? 1 : 0
for event in $game_map.events.values
next if ![1, 2].include?(event.trigger) # Player touch, event touch
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(@x + x_offset, @y + y_offset)
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
# 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? || $game_temp.message_window_showing ||
$PokemonTemp.miniupdate || $game_temp.in_menu
# Move player in the direction the directional button is being pressed
if @moved_last_frame ||
(dir > 0 && dir == @lastdir && Graphics.frame_count - @lastdirframe > Graphics.frame_rate / 20)
case dir
when 2 then move_down
when 4 then move_left
when 6 then move_right
when 8 then move_up
end
elsif dir != @lastdir
case dir
when 2 then turn_down
when 4 then turn_left
when 6 then turn_right
when 8 then 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 self.map.scrolling? || !(@moved_last_frame || @moved_this_frame)
self.map.display_x = @real_x - SCREEN_CENTER_X
self.map.display_y = @real_y - SCREEN_CENTER_Y
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
# Try to manually interact with events
if Input.trigger?(Input::USE) && !$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] == $Trainer.character_ID &&
$game_player.charsetData[1] == charset &&
$game_player.charsetData[2] == outfit
end
$game_player.charsetData = [$Trainer.character_ID,charset,outfit] if $game_player
ret = meta[charset]
ret = meta[1] if nil_or_empty?(ret)
if pbResolveBitmap("Graphics/Characters/"+ret+"_"+outfit.to_s)
ret = ret+"_"+outfit.to_s
end
return ret
end
def pbUpdateVehicle
meta = GameData::Metadata.get_player($Trainer.character_ID)
if meta
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?(map_id)
map_metadata = GameData::MapMetadata.try_get(map_id)
return false if !map_metadata
return true if map_metadata.always_bicycle
val = map_metadata.can_bicycle || map_metadata.outdoor_map
return (val) ? true : false
end
def pbMountBike
return if $PokemonGlobal.bicycle
$PokemonGlobal.bicycle = true
pbUpdateVehicle
bike_bgm = GameData::Metadata.get.bicycle_BGM
pbCueBGM(bike_bgm, 0.5) if bike_bgm
pbPokeRadarCancel
end
def pbDismountBike
return if !$PokemonGlobal.bicycle
$PokemonGlobal.bicycle = false
pbUpdateVehicle
$game_map.autoplayAsCue
end

View File

@@ -1,215 +0,0 @@
#===============================================================================
# Instances of this are stored in @realEvents.
#===============================================================================
class Game_Follower < Game_Event
attr_writer :map
def initialize(event_data)
# Create RPG::Event to base self on
rpg_event = RPG::Event.new(event_data.x, event_data.y)
rpg_event.id = event_data.event_id
rpg_event.name = event_data.event_name
if event_data.common_event_id
# Must setup common event list here and now
common_event = Game_CommonEvent.new(event_data.common_event_id)
rpg_event.pages[0].list = common_event.list
end
# Create self
super(event_data.original_map_id, rpg_event, $map_factory.getMap(event_data.current_map_id))
# Modify self
self.character_name = event_data.character_name
self.character_hue = event_data.character_hue
case event_data.direction
when 2 then turn_down
when 4 then turn_left
when 6 then turn_right
when 8 then turn_up
end
end
def map_id
return @map.map_id
end
#-----------------------------------------------------------------------------
def move_through(direction)
old_through = @through
@through = true
case direction
when 2 then move_down
when 4 then move_left
when 6 then move_right
when 8 then move_up
end
@through = old_through
end
def move_fancy(direction)
delta_x = (direction == 6) ? 1 : (direction == 4) ? -1 : 0
delta_y = (direction == 2) ? 1 : (direction == 8) ? -1 : 0
new_x = self.x + delta_x
new_y = self.y + delta_y
# Move if new position is the player's, or the new position is passable,
# or self's current position is not passable
if ($game_player.x == new_x && $game_player.y == new_y) ||
location_passable?(new_x, new_y, 10 - direction) ||
!location_passable?(self.x, self.y, direction)
move_through(direction)
end
end
def jump_fancy(direction, leader)
delta_x = (direction == 6) ? 2 : (direction == 4) ? -2 : 0
delta_y = (direction == 2) ? 2 : (direction == 8) ? -2 : 0
half_delta_x = delta_x / 2
half_delta_y = delta_y / 2
if location_passable?(self.x + half_delta_x, self.y + half_delta_y, 10 - direction)
# Can walk over the middle tile normally; just take two steps
move_fancy(direction)
move_fancy(direction)
elsif location_passable?(self.x + delta_x, self.y + delta_y, 10 - direction)
# Can't walk over the middle tile, but can walk over the end tile; jump over
if location_passable?(self.x, self.y, direction)
if leader.jumping?
self.jump_speed = leader.jump_speed || 3
else
self.jump_speed = leader.move_speed || 3
# This is halved because self has to jump 2 tiles in the time it takes
# the leader to move one tile
@jump_time /= 2
end
jump(delta_x, delta_y)
else
# self's current tile isn't passable; just take two steps ignoring passability
move_through(direction)
move_through(direction)
end
end
end
def fancy_moveto(new_x, new_y, leader)
if self.x - new_x == 1 && self.y == new_y
move_fancy(4)
elsif self.x - new_x == -1 && self.y == new_y
move_fancy(6)
elsif self.x == new_x && self.y - new_y == 1
move_fancy(8)
elsif self.x == new_x && self.y - new_y == -1
move_fancy(2)
elsif self.x - new_x == 2 && self.y == new_y
jump_fancy(4, leader)
elsif self.x - new_x == -2 && self.y == new_y
jump_fancy(6, leader)
elsif self.x == new_x && self.y - new_y == 2
jump_fancy(8, leader)
elsif self.x == new_x && self.y - new_y == -2
jump_fancy(2, leader)
elsif self.x != new_x || self.y != new_y
moveto(new_x, new_y)
end
end
# Ceases all movement immediately. Used when the leader wants to move another
# tile but self hasn't quite finished its previous movement yet.
def end_movement
@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
@move_timer = nil
@jump_timer = nil
@jump_peak = 0
@jump_distance = 0
@jump_fraction = 0
@jumping_on_spot = false
end
#-----------------------------------------------------------------------------
def turn_towards_leader(leader)
pbTurnTowardEvent(self, leader)
end
def follow_leader(leader, instant = false, leaderIsTrueLeader = true)
return if @move_route_forcing
end_movement
maps_connected = $map_factory.areConnected?(leader.map.map_id, self.map.map_id)
target = nil
# Get the target tile that self wants to move to
if maps_connected
behind_direction = 10 - leader.direction
target = $map_factory.getFacingTile(behind_direction, leader)
if target && $map_factory.getTerrainTag(target[0], target[1], target[2]).ledge
# Get the tile above the ledge (where the leader jumped from)
target = $map_factory.getFacingTileFromPos(target[0], target[1], target[2], behind_direction)
end
target = [leader.map.map_id, leader.x, leader.y] if !target
else
# Map transfer to an unconnected map
target = [leader.map.map_id, leader.x, leader.y]
end
# Move self to the target
if self.map.map_id != target[0]
vector = $map_factory.getRelativePos(target[0], 0, 0, self.map.map_id, @x, @y)
@map = $map_factory.getMap(target[0])
# NOTE: Can't use moveto because vector is outside the boundaries of the
# map, and moveto doesn't allow setting invalid coordinates.
@x = vector[0]
@y = vector[1]
@real_x = @x * Game_Map::REAL_RES_X
@real_y = @y * Game_Map::REAL_RES_Y
end
if instant || !maps_connected
moveto(target[1], target[2])
else
fancy_moveto(target[1], target[2], leader)
end
end
#-----------------------------------------------------------------------------
private
def location_passable?(x, y, direction)
this_map = self.map
return false if !this_map || !this_map.valid?(x, y)
return true if @through
passed_tile_checks = false
bit = (1 << ((direction / 2) - 1)) & 0x0f
# Check all events for ones using tiles as graphics, and see if they're passable
this_map.events.each_value do |event|
next if event.tile_id < 0 || event.through || !event.at_coordinate?(x, y)
tile_data = GameData::TerrainTag.try_get(this_map.terrain_tags[event.tile_id])
next if tile_data.ignore_passability
next if tile_data.bridge && $PokemonGlobal.bridge == 0
return false if tile_data.ledge
passage = this_map.passages[event.tile_id] || 0
return false if passage & bit != 0
passed_tile_checks = true if (tile_data.bridge && $PokemonGlobal.bridge > 0) ||
(this_map.priorities[event.tile_id] || -1) == 0
break if passed_tile_checks
end
# Check if tiles at (x, y) allow passage for followe
if !passed_tile_checks
[2, 1, 0].each do |i|
tile_id = this_map.data[x, y, i] || 0
next if tile_id == 0
tile_data = GameData::TerrainTag.try_get(this_map.terrain_tags[tile_id])
next if tile_data.ignore_passability
next if tile_data.bridge && $PokemonGlobal.bridge == 0
return false if tile_data.ledge
passage = this_map.passages[tile_id] || 0
return false if passage & bit != 0
break if tile_data.bridge && $PokemonGlobal.bridge > 0
break if (this_map.priorities[tile_id] || -1) == 0
end
end
# Check all events on the map to see if any are in the way
this_map.events.each_value do |event|
next if !event.at_coordinate?(x, y)
return false if !event.through && event.character_name != ""
end
return true
end
end

View File

@@ -0,0 +1,102 @@
class Game_Player < Game_Character
@@bobFrameSpeed = 1.0/15
def fullPattern
case self.direction
when 2 then return self.pattern
when 4 then return self.pattern + 4
when 6 then return self.pattern + 8
when 8 then return self.pattern + 12
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?
input = ($PokemonSystem.runstyle == 1) ^ Input.press?(Input::ACTION)
return input && $Trainer.has_running_shoes && !jumping? &&
!$PokemonGlobal.diving && !$PokemonGlobal.surfing &&
!$PokemonGlobal.bicycle && !$game_player.pbTerrainTag.must_walk
end
def pbIsRunning?
return moving? && !@move_route_forcing && pbCanRun?
end
def character_name
@defaultCharacterName = "" if !@defaultCharacterName
return @defaultCharacterName if @defaultCharacterName!=""
if !@move_route_forcing && $Trainer.character_ID>=0
meta = GameData::Metadata.get_player($Trainer.character_ID)
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 $game_player.pbTerrainTag.ice
self.move_speed = 4 # Sliding on ice
elsif !moving? && !@move_route_forcing && $PokemonGlobal
if $PokemonGlobal.bicycle
self.move_speed = 5 # Cycling
elsif pbCanRun? || $PokemonGlobal.surfing
self.move_speed = 4 # Running, surfing
else
self.move_speed = 3 # Walking, diving
end
end
super
end
def update_pattern
if $PokemonGlobal.surfing || $PokemonGlobal.diving
p = ((Graphics.frame_count%60)*@@bobFrameSpeed).floor
@pattern = p if !@lock_pattern
@pattern_surf = p
@bob_height = (p>=2) ? 2 : 0
else
@bob_height = 0
super
end
end
end
=begin
class Game_Character
alias update_old2 update
def update
if self.is_a?(Game_Event)
if @dependentEvents
for i in 0...@dependentEvents.length
if @dependentEvents[i][0]==$game_map.map_id &&
@dependentEvents[i][1]==self.id
self.move_speed_real = $game_player.move_speed_real
break
end
end
end
end
update_old2
end
end
=end

View File

@@ -0,0 +1,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 && switchIsOn?(self.switch_id)
if @interpreter == nil
@interpreter = Interpreter.new
end
else
@interpreter = nil
end
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
# If parallel process is valid
if @interpreter != nil
# If not running
unless @interpreter.running?
# Set up event
@interpreter.setup(self.list, 0)
end
# Update interpreter
@interpreter.update
end
end
end

View File

@@ -1,389 +0,0 @@
#===============================================================================
# Data saved in $PokemonGlobal.followers.
#===============================================================================
class FollowerData
attr_accessor :original_map_id
attr_accessor :event_id
attr_accessor :event_name
attr_accessor :current_map_id
attr_accessor :x, :y
attr_accessor :direction
attr_accessor :character_name, :character_hue
attr_accessor :name
attr_accessor :common_event_id
attr_accessor :visible
attr_accessor :invisible_after_transfer
def initialize(original_map_id, event_id, event_name, current_map_id, x, y,
direction, character_name, character_hue)
@original_map_id = original_map_id
@event_id = event_id
@event_name = event_name
@current_map_id = current_map_id
@x = x
@y = y
@direction = direction
@character_name = character_name
@character_hue = character_hue
@name = nil
@common_event_id = nil
@visible = true
@invisible_after_transfer = false
end
def visible?
return @visible && !@invisible_after_transfer
end
def interact(event)
return if !event || event.list.size <= 1
return if !@common_event_id
# Start event
$game_map.refresh if $game_map.need_refresh
event.lock
pbMapInterpreter.setup(event.list, event.id, event.map.map_id)
end
end
#===============================================================================
# Permanently stores data of follower events (i.e. in save files).
#===============================================================================
class PokemonGlobalMetadata
attr_writer :followers
def followers
@followers = [] if !@followers
return @followers
end
end
#===============================================================================
# Stores Game_Follower instances just for the current play session.
#===============================================================================
class Game_Temp
attr_writer :followers
def followers
@followers = Game_FollowerFactory.new if !@followers
return @followers
end
end
#===============================================================================
#
#===============================================================================
class Game_FollowerFactory
attr_reader :last_update
def initialize
@events = []
$PokemonGlobal.followers.each do |follower|
@events.push(create_follower_object(follower))
end
@last_update = -1
end
#-----------------------------------------------------------------------------
def add_follower(event, name = nil, common_event_id = nil)
return if !event
followers = $PokemonGlobal.followers
if followers.any? { |data| data.original_map_id == $game_map.map_id && data.event_id == event.id }
return # Event is already dependent
end
eventData = FollowerData.new($game_map.map_id, event.id, event.name,
$game_map.map_id, event.x, event.y, event.direction,
event.character_name.clone, event.character_hue)
eventData.name = name
eventData.common_event_id = common_event_id
newEvent = create_follower_object(eventData)
followers.push(eventData)
@events.push(newEvent)
@last_update += 1
end
def remove_follower_by_event(event)
followers = $PokemonGlobal.followers
map_id = $game_map.map_id
followers.each_with_index do |follower, i|
next if follower.current_map_id != map_id
next if follower.original_map_id != event.map_id
next if follower.event_id != event.id
followers[i] = nil
@events[i] = nil
@last_update += 1
end
followers.compact!
@events.compact!
end
def remove_follower_by_name(name)
followers = $PokemonGlobal.followers
followers.each_with_index do |follower, i|
next if follower.name != name
followers[i] = nil
@events[i] = nil
@last_update += 1
end
followers.compact!
@events.compact!
end
def remove_all_followers
$PokemonGlobal.followers.clear
@events.clear
@last_update += 1
end
def get_follower_by_index(index = 0)
@events.each_with_index { |event, i| return event if i == index }
return nil
end
def get_follower_by_name(name)
each_follower { |event, follower| return event if follower&.name == name }
return nil
end
def each_follower
$PokemonGlobal.followers.each_with_index { |follower, i| yield @events[i], follower }
end
#-----------------------------------------------------------------------------
def turn_followers
leader = $game_player
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.turn_towards_leader(leader)
follower.direction = event.direction
leader = event
end
end
def move_followers
leader = $game_player
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.follow_leader(leader, false, (i == 0))
follower.x = event.x
follower.y = event.y
follower.current_map_id = event.map.map_id
follower.direction = event.direction
leader = event
end
end
def map_transfer_followers
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.map = $game_map
event.moveto($game_player.x, $game_player.y)
event.direction = $game_player.direction
event.opacity = 255
follower.x = event.x
follower.y = event.y
follower.current_map_id = event.map.map_id
follower.direction = event.direction
follower.invisible_after_transfer = true
end
end
def follow_into_door
# Setting an event's move route also makes it start along that move route,
# so we need to record all followers' current positions first before setting
# any move routes
follower_pos = []
follower_pos.push([$game_player.map.map_id, $game_player.x, $game_player.y])
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
follower_pos.push([event.map.map_id, event.x, event.y])
end
# Calculate and set move route from each follower to player
move_route = []
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
leader = follower_pos[i]
vector = $map_factory.getRelativePos(event.map.map_id, event.x, event.y,
leader[0], leader[1], leader[2])
if vector[0] != 0
move_route.prepend((vector[0] > 0) ? PBMoveRoute::RIGHT : PBMoveRoute::LEFT)
elsif vector[1] != 0
move_route.prepend((vector[1] > 0) ? PBMoveRoute::DOWN : PBMoveRoute::UP)
end
pbMoveRoute(event, move_route + [PBMoveRoute::OPACITY, 0])
end
end
# Used when coming out of a door.
def hide_followers
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.opacity = 0
end
end
# Used when coming out of a door. Makes all followers invisible until the
# player starts moving.
def put_followers_on_player
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.moveto($game_player.x, $game_player.y)
event.opacity = 255
follower.x = event.x
follower.y = event.y
follower.invisible_after_transfer = true
end
end
#-----------------------------------------------------------------------------
def update
return if $game_temp.in_menu
followers = $PokemonGlobal.followers
return if followers.length == 0
# Update all followers
leader = $game_player
player_moving = $game_player.moving? || $game_player.jumping?
followers.each_with_index do |follower, i|
event = @events[i]
next if !@events[i]
if follower.invisible_after_transfer && player_moving
follower.invisible_after_transfer = false
event.turn_towards_leader($game_player)
end
event.move_speed = leader.move_speed
event.transparent = !follower.visible?
if $PokemonGlobal.ice_sliding
event.straighten
event.walk_anime = false
else
event.walk_anime = true
end
if event.jumping? || event.moving? || !player_moving
event.update
elsif !event.starting
event.set_starting
event.update
event.clear_starting
end
follower.direction = event.direction
leader = event
end
# Check event triggers
if Input.trigger?(Input::USE) && !$game_temp.in_menu && !$game_temp.in_battle &&
!$game_player.move_route_forcing && !$game_temp.message_window_showing &&
!pbMapInterpreterRunning?
# Get position of tile facing the player
facing_tile = $map_factory.getFacingTile
# Assumes player is 1x1 tile in size
each_follower do |event, follower|
next if !facing_tile || event.map.map_id != facing_tile[0] ||
!event.at_coordinate?(facing_tile[1], facing_tile[2]) # Not on facing tile
next if event.jumping?
follower.interact(event)
end
end
end
#-----------------------------------------------------------------------------
private
def create_follower_object(event_data)
return Game_Follower.new(event_data)
end
end
#===============================================================================
#
#===============================================================================
class FollowerSprites
def initialize(viewport)
@viewport = viewport
@sprites = []
@last_update = nil
@disposed = false
end
def dispose
return if @disposed
@sprites.each { |sprite| sprite.dispose }
@sprites.clear
@disposed = true
end
def disposed?
return @disposed
end
def refresh
@sprites.each { |sprite| sprite.dispose }
@sprites.clear
$game_temp.followers.each_follower do |event, follower|
@sprites.push(Sprite_Character.new(@viewport, event))
end
end
def update
if $game_temp.followers.last_update != @last_update
refresh
@last_update = $game_temp.followers.last_update
end
@sprites.each { |sprite| sprite.update }
end
end
#===============================================================================
# Helper module for adding/removing/getting followers.
#===============================================================================
module Followers
module_function
# @param event_id [Integer] ID of the event on the current map to be added as a follower
# @param name [String] identifier name of the follower to be added
# @param common_event_id [Integer] ID of the Common Event triggered when interacting with this follower
def add(event_id, name, common_event_id)
$game_temp.followers.add_follower($game_map.events[event_id], name, common_event_id)
end
# @param event [Game_Event] map event to be added as a follower
def add_event(event)
$game_temp.followers.add_follower(event)
end
# @param name [String] identifier name of the follower to be removed
def remove(name)
$game_temp.followers.remove_follower_by_name(name)
end
# @param event [Game_Event] map event to be removed as a follower
def remove_event(event)
$game_temp.followers.remove_follower_by_event(event)
end
# Removes all followers.
def clear
$game_temp.followers.remove_all_followers
pbDeregisterPartner rescue nil
end
# @param name [String, nil] name of the follower to get, or nil for the first follower
# @return [Game_Follower, nil] follower object
def get(name = nil)
return $game_temp.followers.get_follower_by_name(name) if name
return $game_temp.followers.get_follower_by_index
end
def follow_into_door
$game_temp.followers.follow_into_door
end
def hide_followers
$game_temp.followers.hide_followers
end
def put_followers_on_player
$game_temp.followers.put_followers_on_player
end
end

View File

@@ -0,0 +1,563 @@
class PokemonTemp
attr_writer :dependentEvents
def dependentEvents
@dependentEvents = DependentEvents.new if !@dependentEvents
return @dependentEvents
end
end
def pbRemoveDependencies()
$PokemonTemp.dependentEvents.removeAllEvents()
pbDeregisterPartner() rescue nil
end
def pbAddDependency(event)
$PokemonTemp.dependentEvents.addEvent(event)
end
def pbRemoveDependency(event)
$PokemonTemp.dependentEvents.removeEvent(event)
end
def pbAddDependency2(eventID, eventName, commonEvent)
$PokemonTemp.dependentEvents.addEvent($game_map.events[eventID],eventName,commonEvent)
end
# Gets the Game_Character object associated with a dependent event.
def pbGetDependency(eventName)
return $PokemonTemp.dependentEvents.getEventByName(eventName)
end
def pbRemoveDependency2(eventName)
$PokemonTemp.dependentEvents.removeEventByName(eventName)
end
class PokemonGlobalMetadata
attr_writer :dependentEvents
def dependentEvents
@dependentEvents = [] if !@dependentEvents
return @dependentEvents
end
end
def pbTestPass(follower,x,y,_direction=nil)
return $MapFactory.isPassableStrict?(follower.map.map_id,x,y,follower)
end
# Same map only
def moveThrough(follower,direction)
oldThrough=follower.through
follower.through=true
case direction
when 2 then follower.move_down
when 4 then follower.move_left
when 6 then follower.move_right
when 8 then follower.move_up
end
follower.through=oldThrough
end
# Same map only
def moveFancy(follower,direction)
deltaX=(direction == 6 ? 1 : (direction == 4 ? -1 : 0))
deltaY=(direction == 2 ? 1 : (direction == 8 ? -1 : 0))
newX = follower.x + deltaX
newY = follower.y + deltaY
# Move if new position is the player's, or the new position is passable,
# or the current position is not passable
if ($game_player.x==newX && $game_player.y==newY) ||
pbTestPass(follower,newX,newY,0) ||
!pbTestPass(follower,follower.x,follower.y,0)
oldThrough=follower.through
follower.through=true
case direction
when 2 then follower.move_down
when 4 then follower.move_left
when 6 then follower.move_right
when 8 then follower.move_up
end
follower.through=oldThrough
end
end
# Same map only
def jumpFancy(follower,direction,leader)
deltaX=(direction == 6 ? 2 : (direction == 4 ? -2 : 0))
deltaY=(direction == 2 ? 2 : (direction == 8 ? -2 : 0))
halfDeltaX=(direction == 6 ? 1 : (direction == 4 ? -1 : 0))
halfDeltaY=(direction == 2 ? 1 : (direction == 8 ? -1 : 0))
middle=pbTestPass(follower,follower.x+halfDeltaX,follower.y+halfDeltaY,0)
ending=pbTestPass(follower,follower.x+deltaX, follower.y+deltaY, 0)
if middle
moveFancy(follower,direction)
moveFancy(follower,direction)
elsif ending
if pbTestPass(follower,follower.x,follower.y,0)
if leader.jumping?
follower.jump_speed_real = leader.jump_speed_real * Graphics.frame_rate / 40.0
else
follower.jump_speed_real = leader.move_speed_real * Graphics.frame_rate / 20.0
end
follower.jump(deltaX,deltaY)
else
moveThrough(follower,direction)
moveThrough(follower,direction)
end
end
end
def pbFancyMoveTo(follower,newX,newY,leader)
if follower.x-newX==-1 && follower.y==newY
moveFancy(follower,6)
elsif follower.x-newX==1 && follower.y==newY
moveFancy(follower,4)
elsif follower.y-newY==-1 && follower.x==newX
moveFancy(follower,2)
elsif follower.y-newY==1 && follower.x==newX
moveFancy(follower,8)
elsif follower.x-newX==-2 && follower.y==newY
jumpFancy(follower,6,leader)
elsif follower.x-newX==2 && follower.y==newY
jumpFancy(follower,4,leader)
elsif follower.y-newY==-2 && follower.x==newX
jumpFancy(follower,2,leader)
elsif follower.y-newY==2 && follower.x==newX
jumpFancy(follower,8,leader)
elsif follower.x!=newX || follower.y!=newY
follower.moveto(newX,newY)
end
end
class DependentEvents
attr_reader :lastUpdate
def createEvent(eventData)
rpgEvent = RPG::Event.new(eventData[3],eventData[4])
rpgEvent.id = eventData[1]
if eventData[9]
# Must setup common event list here and now
commonEvent = Game_CommonEvent.new(eventData[9])
rpgEvent.pages[0].list = commonEvent.list
end
newEvent = Game_Event.new(eventData[0],rpgEvent,$MapFactory.getMap(eventData[2]))
newEvent.character_name = eventData[6]
newEvent.character_hue = eventData[7]
case eventData[5] # direction
when 2 then newEvent.turn_down
when 4 then newEvent.turn_left
when 6 then newEvent.turn_right
when 8 then newEvent.turn_up
end
return newEvent
end
def initialize
# Original map, Event ID, Current map, X, Y, Direction
events=$PokemonGlobal.dependentEvents
@realEvents=[]
@lastUpdate=-1
for event in events
@realEvents.push(createEvent(event))
end
end
def pbEnsureEvent(event, newMapID)
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
# Check original map ID and original event ID
if events[i][0]==event.map_id && events[i][1]==event.id
# Change current map ID
events[i][2]=newMapID
newEvent=createEvent(events[i])
# Replace event
@realEvents[i]=newEvent
@lastUpdate+=1
return i
end
end
return -1
end
def pbFollowEventAcrossMaps(leader,follower,instant=false,leaderIsTrueLeader=true)
d=leader.direction
areConnected=$MapFactory.areConnected?(leader.map.map_id,follower.map.map_id)
# Get the rear facing tile of leader
facingDirection=10-d
if !leaderIsTrueLeader && areConnected
relativePos=$MapFactory.getThisAndOtherEventRelativePos(leader,follower)
# Assumes leader and follower are both 1x1 tile in size
if (relativePos[1]==0 && relativePos[0]==2) # 2 spaces to the right of leader
facingDirection=6
elsif (relativePos[1]==0 && relativePos[0]==-2) # 2 spaces to the left of leader
facingDirection=4
elsif relativePos[1]==-2 && relativePos[0]==0 # 2 spaces above leader
facingDirection=8
elsif relativePos[1]==2 && relativePos[0]==0 # 2 spaces below leader
facingDirection=2
end
end
facings=[facingDirection] # Get facing from behind
# facings.push([0,0,4,0,8,0,2,0,6][d]) # Get right facing
# facings.push([0,0,6,0,2,0,8,0,4][d]) # Get left facing
if !leaderIsTrueLeader
facings.push(d) # Get forward facing
end
mapTile=nil
if areConnected
bestRelativePos=-1
oldthrough=follower.through
follower.through=false
for i in 0...facings.length
facing=facings[i]
tile=$MapFactory.getFacingTile(facing,leader)
# Assumes leader is 1x1 tile in size
passable=tile && $MapFactory.isPassableStrict?(tile[0],tile[1],tile[2],follower)
if i==0 && !passable && tile &&
$MapFactory.getTerrainTag(tile[0],tile[1],tile[2]).ledge
# If the tile isn't passable and the tile is a ledge,
# get tile from further behind
tile=$MapFactory.getFacingTileFromPos(tile[0],tile[1],tile[2],facing)
passable=tile && $MapFactory.isPassableStrict?(tile[0],tile[1],tile[2],follower)
end
if passable
relativePos=$MapFactory.getThisAndOtherPosRelativePos(
follower,tile[0],tile[1],tile[2])
# Assumes follower is 1x1 tile in size
distance=Math.sqrt(relativePos[0]*relativePos[0]+relativePos[1]*relativePos[1])
if bestRelativePos==-1 || bestRelativePos>distance
bestRelativePos=distance
mapTile=tile
end
if i==0 && distance<=1 # Prefer behind if tile can move up to 1 space
break
end
end
end
follower.through=oldthrough
else
tile=$MapFactory.getFacingTile(facings[0],leader)
# Assumes leader is 1x1 tile in size
passable=tile && $MapFactory.isPassableStrict?(tile[0],tile[1],tile[2],follower)
mapTile=passable ? mapTile : nil
end
if mapTile && follower.map.map_id==mapTile[0]
# Follower is on same map
newX=mapTile[1]
newY=mapTile[2]
deltaX=(d == 6 ? -1 : d == 4 ? 1 : 0)
deltaY=(d == 2 ? -1 : d == 8 ? 1 : 0)
posX = newX + deltaX
posY = newY + deltaY
follower.move_speed=leader.move_speed # sync movespeed
if (follower.x-newX==-1 && follower.y==newY) ||
(follower.x-newX==1 && follower.y==newY) ||
(follower.y-newY==-1 && follower.x==newX) ||
(follower.y-newY==1 && follower.x==newX)
if instant
follower.moveto(newX,newY)
else
pbFancyMoveTo(follower,newX,newY,leader)
end
elsif (follower.x-newX==-2 && follower.y==newY) ||
(follower.x-newX==2 && follower.y==newY) ||
(follower.y-newY==-2 && follower.x==newX) ||
(follower.y-newY==2 && follower.x==newX)
if instant
follower.moveto(newX,newY)
else
pbFancyMoveTo(follower,newX,newY,leader)
end
elsif follower.x!=posX || follower.y!=posY
if instant
follower.moveto(newX,newY)
else
pbFancyMoveTo(follower,posX,posY,leader)
pbFancyMoveTo(follower,newX,newY,leader)
end
end
else
if !mapTile
# Make current position into leader's position
mapTile=[leader.map.map_id,leader.x,leader.y]
end
if follower.map.map_id==mapTile[0]
# Follower is on same map as leader
follower.moveto(leader.x,leader.y)
else
# Follower will move to different map
events=$PokemonGlobal.dependentEvents
eventIndex=pbEnsureEvent(follower,mapTile[0])
if eventIndex>=0
newFollower=@realEvents[eventIndex]
newEventData=events[eventIndex]
newFollower.moveto(mapTile[1],mapTile[2])
newEventData[3]=mapTile[1]
newEventData[4]=mapTile[2]
end
end
end
end
def debugEcho
self.eachEvent { |e,d|
echoln d
echoln [e.map_id,e.map.map_id,e.id]
}
end
def pbMapChangeMoveDependentEvents
events=$PokemonGlobal.dependentEvents
updateDependentEvents
leader=$game_player
for i in 0...events.length
event=@realEvents[i]
pbFollowEventAcrossMaps(leader,event,true,i==0)
# Update X and Y for this event
events[i][3]=event.x
events[i][4]=event.y
events[i][5]=event.direction
# Set leader to this event
leader=event
end
end
def pbMoveDependentEvents
events=$PokemonGlobal.dependentEvents
updateDependentEvents
leader=$game_player
for i in 0...events.length
event=@realEvents[i]
pbFollowEventAcrossMaps(leader,event,false,i==0)
# Update X and Y for this event
events[i][3]=event.x
events[i][4]=event.y
events[i][5]=event.direction
# Set leader to this event
leader=event
end
end
def pbTurnDependentEvents
events=$PokemonGlobal.dependentEvents
updateDependentEvents
leader=$game_player
for i in 0...events.length
event=@realEvents[i]
pbTurnTowardEvent(event,leader)
# Update direction for this event
events[i][5]=event.direction
# Set leader to this event
leader=event
end
end
def eachEvent
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
yield @realEvents[i],events[i]
end
end
def updateDependentEvents
events=$PokemonGlobal.dependentEvents
return if events.length==0
for i in 0...events.length
event=@realEvents[i]
next if !@realEvents[i]
event.transparent=$game_player.transparent
if event.jumping? || event.moving? ||
!($game_player.jumping? || $game_player.moving?)
event.update
elsif !event.starting
event.set_starting
event.update
event.clear_starting
end
events[i][3]=event.x
events[i][4]=event.y
events[i][5]=event.direction
end
# Check event triggers
if Input.trigger?(Input::USE) && !$game_temp.in_menu && !$game_temp.in_battle &&
!$game_player.move_route_forcing && !$game_temp.message_window_showing &&
!pbMapInterpreterRunning?
# Get position of tile facing the player
facingTile=$MapFactory.getFacingTile()
# Assumes player is 1x1 tile in size
self.eachEvent { |e,d|
next if !d[9]
if e.at_coordinate?($game_player.x, $game_player.y)
# On same position
if !e.jumping? && (!e.respond_to?("over_trigger") || e.over_trigger?)
if e.list.size>1
# Start event
$game_map.refresh if $game_map.need_refresh
e.lock
pbMapInterpreter.setup(e.list,e.id,e.map.map_id)
end
end
elsif facingTile && e.map.map_id==facingTile[0] &&
e.at_coordinate?(facingTile[1], facingTile[2])
# On facing tile
if !e.jumping? && (!e.respond_to?("over_trigger") || !e.over_trigger?)
if e.list.size>1
# Start event
$game_map.refresh if $game_map.need_refresh
e.lock
pbMapInterpreter.setup(e.list,e.id,e.map.map_id)
end
end
end
}
end
end
def removeEvent(event)
events=$PokemonGlobal.dependentEvents
mapid=$game_map.map_id
for i in 0...events.length
if events[i][2]==mapid && # Refer to current map
events[i][0]==event.map_id && # Event's map ID is original ID
events[i][1]==event.id
events[i]=nil
@realEvents[i]=nil
@lastUpdate+=1
end
end
events.compact!
@realEvents.compact!
end
def getEventByName(name)
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
if events[i] && events[i][8]==name # Arbitrary name given to dependent event
return @realEvents[i]
end
end
return nil
end
def removeAllEvents
events=$PokemonGlobal.dependentEvents
events.clear
@realEvents.clear
@lastUpdate+=1
end
def removeEventByName(name)
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
if events[i] && events[i][8]==name # Arbitrary name given to dependent event
events[i]=nil
@realEvents[i]=nil
@lastUpdate+=1
end
end
events.compact!
@realEvents.compact!
end
def addEvent(event,eventName=nil,commonEvent=nil)
return if !event
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
if events[i] && events[i][0]==$game_map.map_id && events[i][1]==event.id
# Already exists
return
end
end
# Original map ID, original event ID, current map ID,
# event X, event Y, event direction,
# event's filename,
# event's hue, event's name, common event ID
eventData=[
$game_map.map_id,event.id,$game_map.map_id,
event.x,event.y,event.direction,
event.character_name.clone,
event.character_hue,eventName,commonEvent
]
newEvent=createEvent(eventData)
events.push(eventData)
@realEvents.push(newEvent)
@lastUpdate+=1
event.erase
end
end
class DependentEventSprites
def initialize(viewport,map)
@disposed=false
@sprites=[]
@map=map
@viewport=viewport
refresh
@lastUpdate=nil
end
def refresh
for sprite in @sprites
sprite.dispose
end
@sprites.clear
$PokemonTemp.dependentEvents.eachEvent { |event,data|
if data[0]==@map.map_id # Check original map
@map.events[data[1]].erase
end
if data[2]==@map.map_id # Check current map
@sprites.push(Sprite_Character.new(@viewport,event))
end
}
end
def update
if $PokemonTemp.dependentEvents.lastUpdate!=@lastUpdate
refresh
@lastUpdate=$PokemonTemp.dependentEvents.lastUpdate
end
for sprite in @sprites
sprite.update
end
end
def dispose
return if @disposed
for sprite in @sprites
sprite.dispose
end
@sprites.clear
@disposed=true
end
def disposed?
@disposed
end
end
Events.onSpritesetCreate += proc { |_sender,e|
spriteset = e[0] # Spriteset being created
viewport = e[1] # Viewport used for tilemap and characters
map = spriteset.map # Map associated with the spriteset (not necessarily the current map)
spriteset.addUserSprite(DependentEventSprites.new(viewport,map))
}
Events.onMapSceneChange += proc { |_sender,e|
mapChanged = e[1]
if mapChanged
$PokemonTemp.dependentEvents.pbMapChangeMoveDependentEvents
end
}

View File

@@ -1,214 +0,0 @@
#===============================================================================
# Stored in $stats
#===============================================================================
class GameStats
# Travel
attr_accessor :distance_walked, :distance_cycled, :distance_surfed # surfed includes diving
attr_accessor :distance_slid_on_ice # Also counted in distance_walked
attr_accessor :bump_count # Times the player walked into something
attr_accessor :cycle_count, :surf_count, :dive_count
# Field actions
attr_accessor :fly_count, :cut_count, :flash_count
attr_accessor :rock_smash_count, :rock_smash_battles
attr_accessor :headbutt_count, :headbutt_battles
attr_accessor :strength_push_count # Number of shoves, not the times Strength was used
attr_accessor :waterfall_count, :waterfalls_descended
# Items
attr_accessor :repel_count
attr_accessor :itemfinder_count
attr_accessor :fishing_count, :fishing_battles
attr_accessor :poke_radar_count, :poke_radar_longest_chain
attr_accessor :berry_plants_picked, :max_yield_berry_plants
attr_accessor :berries_planted
# NPCs
attr_accessor :poke_center_count
attr_accessor :revived_fossil_count
attr_accessor :lottery_prize_count # Times won any prize at all
# Pokémon
attr_accessor :eggs_hatched
attr_accessor :evolution_count, :evolutions_cancelled
attr_accessor :trade_count
attr_accessor :pokemon_release_count
attr_accessor :moves_taught_by_item, :moves_taught_by_tutor, :moves_taught_by_reminder
attr_accessor :day_care_deposits, :day_care_levels_gained
attr_accessor :pokerus_infections
attr_accessor :shadow_pokemon_purified
# Battles
attr_accessor :wild_battles_won, :wild_battles_lost, :wild_battles_fled # Fled counts both player and wild Pokémon fleeing
attr_accessor :trainer_battles_won, :trainer_battles_lost
attr_accessor :total_exp_gained
attr_accessor :battle_money_gained, :battle_money_lost
attr_accessor :blacked_out_count
attr_accessor :mega_evolution_count, :primal_reversion_count
attr_accessor :failed_poke_ball_count
# Currency
attr_accessor :money_spent_at_marts
attr_accessor :money_earned_at_marts
attr_accessor :mart_items_bought, :premier_balls_earned
attr_accessor :drinks_bought, :drinks_won # From vending machines
attr_accessor :coins_won, :coins_lost # Not bought, not spent
attr_accessor :battle_points_won, :battle_points_spent
attr_accessor :soot_collected
# Special stats
attr_accessor :gym_leader_attempts # An array of integers
attr_accessor :times_to_get_badges # An array of times in seconds
attr_accessor :elite_four_attempts
attr_accessor :hall_of_fame_entry_count # See also Game Variable 13
attr_accessor :time_to_enter_hall_of_fame # In seconds
attr_accessor :safari_pokemon_caught, :most_captures_per_safari_game
attr_accessor :bug_contest_count, :bug_contest_wins
# Play
attr_writer :play_time # In seconds; the reader also updates the value
attr_accessor :play_sessions
attr_accessor :time_last_saved # In seconds
attr_reader :real_time_saved
attr_accessor :save_filename_number # -1 if haven't saved yet
def initialize
# Travel
@distance_walked = 0
@distance_cycled = 0
@distance_surfed = 0
@distance_slid_on_ice = 0
@bump_count = 0
@cycle_count = 0
@surf_count = 0
@dive_count = 0
# Field actions
@fly_count = 0
@cut_count = 0
@flash_count = 0
@rock_smash_count = 0
@rock_smash_battles = 0
@headbutt_count = 0
@headbutt_battles = 0
@strength_push_count = 0
@waterfall_count = 0
@waterfalls_descended = 0
# Items
@repel_count = 0
@itemfinder_count = 0
@fishing_count = 0
@fishing_battles = 0
@poke_radar_count = 0
@poke_radar_longest_chain = 0
@berry_plants_picked = 0
@max_yield_berry_plants = 0
@berries_planted = 0
# NPCs
@poke_center_count = 0 # Incremented in Poké Center nurse events
@revived_fossil_count = 0 # Incremented in fossil reviver events
@lottery_prize_count = 0 # Incremented in lottery NPC events
# Pokémon
@eggs_hatched = 0
@evolution_count = 0
@evolutions_cancelled = 0
@trade_count = 0
@pokemon_release_count = 0
@moves_taught_by_item = 0
@moves_taught_by_tutor = 0
@moves_taught_by_reminder = 0
@day_care_deposits = 0
@day_care_levels_gained = 0
@pokerus_infections = 0
@shadow_pokemon_purified = 0
# Battles
@wild_battles_won = 0
@wild_battles_lost = 0
@wild_battles_fled = 0
@trainer_battles_won = 0
@trainer_battles_lost = 0
@total_exp_gained = 0
@battle_money_gained = 0
@battle_money_lost = 0
@blacked_out_count = 0
@mega_evolution_count = 0
@primal_reversion_count = 0
@failed_poke_ball_count = 0
# Currency
@money_spent_at_marts = 0
@money_earned_at_marts = 0
@mart_items_bought = 0
@premier_balls_earned = 0
@drinks_bought = 0 # Incremented in vending machine events
@drinks_won = 0 # Incremented in vending machine events
@coins_won = 0
@coins_lost = 0
@battle_points_won = 0
@battle_points_spent = 0
@soot_collected = 0
# Special stats
@gym_leader_attempts = [0] * 50 # Incremented in Gym Leader events (50 is arbitrary but suitably large)
@times_to_get_badges = [] # Set with set_time_to_badge(number) in Gym Leader events
@elite_four_attempts = 0 # Incremented in door event leading to the first E4 member
@hall_of_fame_entry_count = 0 # Incremented in Hall of Fame event
@time_to_enter_hall_of_fame = 0 # Set with set_time_to_hall_of_fame in Hall of Fame event
@safari_pokemon_caught = 0
@most_captures_per_safari_game = 0
@bug_contest_count = 0
@bug_contest_wins = 0
# Play
@play_time = 0
@play_sessions = 0
@time_last_saved = 0
@real_time_saved = 0
@save_filename_number = -1
end
def distance_moved
return @distance_walked + @distance_cycled + @distance_surfed
end
def caught_pokemon_count
return 0 if !$player
ret = 0
GameData::Species.each_species { |sp| ret += $player.pokedex.caught_count(sp) }
return ret
end
def save_count
return $game_system&.save_count || 0
end
def set_time_to_badge(number)
@times_to_get_badges[number] = play_time
end
def set_time_to_hall_of_fame
@time_to_enter_hall_of_fame = play_time if @time_to_enter_hall_of_fame == 0
end
def play_time
if $game_temp&.last_uptime_refreshed_play_time
now = System.uptime
@play_time += now - $game_temp.last_uptime_refreshed_play_time
$game_temp.last_uptime_refreshed_play_time = now
end
return @play_time
end
# For looking at a save file's play time.
def real_play_time
return @play_time
end
def play_time_per_session
return play_time / @play_sessions
end
def set_time_last_saved
@time_last_saved = play_time
@real_time_saved = Time.now.to_i
end
def time_since_last_save
return play_time - @time_last_saved
end
end
#===============================================================================
#
#===============================================================================
class Game_Temp
attr_accessor :last_uptime_refreshed_play_time
end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
class Sprite_Picture class Sprite_Picture
def initialize(viewport, picture) def initialize(viewport, picture)
@viewport = viewport @viewport = viewport
@@ -10,11 +7,11 @@ class Sprite_Picture
end end
def dispose def dispose
@sprite&.dispose @sprite.dispose if @sprite
end end
def update def update
@sprite&.update @sprite.update if @sprite
# If picture file name is different from current one # If picture file name is different from current one
if @picture_name != @picture.name if @picture_name != @picture.name
# Remember file name to instance variables # Remember file name to instance variables
@@ -22,16 +19,16 @@ class Sprite_Picture
# If file name is not empty # If file name is not empty
if @picture_name != "" if @picture_name != ""
# Get picture graphic # Get picture graphic
@sprite = IconSprite.new(0, 0, @viewport) if !@sprite @sprite=IconSprite.new(0,0,@viewport) if !@sprite
@sprite.setBitmap("Graphics/Pictures/" + @picture_name) @sprite.setBitmap("Graphics/Pictures/"+@picture_name)
end end
end end
# If file name is empty # If file name is empty
if @picture_name == "" if @picture_name == ""
# Set sprite to invisible # Set sprite to invisible
if @sprite if @sprite
@sprite&.dispose @sprite.dispose if @sprite
@sprite = nil @sprite=nil
end end
return return
end end

View File

@@ -1,18 +1,15 @@
#===============================================================================
#
#===============================================================================
class Sprite_Timer class Sprite_Timer
def initialize(viewport = nil) def initialize(viewport=nil)
@viewport = viewport @viewport=viewport
@timer = nil @timer=nil
@total_sec = nil @total_sec=nil
@disposed = false @disposed=false
end end
def dispose def dispose
@timer&.dispose @timer.dispose if @timer
@timer = nil @timer=nil
@disposed = true @disposed=true
end end
def disposed? def disposed?
@@ -21,17 +18,17 @@ class Sprite_Timer
def update def update
return if disposed? return if disposed?
if $game_system.timer_start if $game_system.timer_working
@timer.visible = true if @timer @timer.visible = true if @timer
if !@timer if !@timer
@timer = Window_AdvancedTextPokemon.newWithSize("", Graphics.width - 120, 0, 120, 64) @timer=Window_AdvancedTextPokemon.newWithSize("",Graphics.width-120,0,120,64)
@timer.width = @timer.borderX + 96 @timer.width=@timer.borderX+96
@timer.x = Graphics.width - @timer.width @timer.x=Graphics.width-@timer.width
@timer.viewport = @viewport @timer.viewport=@viewport
@timer.z = 99998 @timer.z=99998
end end
curtime = $game_system.timer curtime=$game_system.timer / Graphics.frame_rate
curtime = 0 if curtime < 0 curtime=0 if curtime<0
if curtime != @total_sec if curtime != @total_sec
# Calculate total number of seconds # Calculate total number of seconds
@total_sec = curtime @total_sec = curtime
@@ -41,8 +38,8 @@ class Sprite_Timer
@timer.text = _ISPRINTF("<ac>{1:02d}:{2:02d}", min, sec) @timer.text = _ISPRINTF("<ac>{1:02d}:{2:02d}", min, sec)
end end
@timer.update @timer.update
elsif @timer else
@timer.visible = false @timer.visible=false if @timer
end end
end end
end end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
class BushBitmap class BushBitmap
def initialize(bitmap, isTile, depth) def initialize(bitmap, isTile, depth)
@bitmaps = [] @bitmaps = []
@@ -11,7 +8,7 @@ class BushBitmap
end end
def dispose def dispose
@bitmaps.each { |b| b&.dispose } @bitmaps.each { |b| b.dispose if b }
end end
def bitmap def bitmap
@@ -31,7 +28,7 @@ class BushBitmap
ret = Bitmap.new(bitmap.width, bitmap.height) ret = Bitmap.new(bitmap.width, bitmap.height)
charheight = ret.height / 4 charheight = ret.height / 4
cy = charheight - depth - 2 cy = charheight - depth - 2
4.times do |i| for i in 0...4
y = i * charheight y = i * charheight
if cy >= 0 if cy >= 0
ret.blt(0, y, bitmap, Rect.new(0, y, ret.width, cy)) ret.blt(0, y, bitmap, Rect.new(0, y, ret.width, cy))
@@ -56,9 +53,8 @@ class BushBitmap
end end
end end
#===============================================================================
#
#===============================================================================
class Sprite_Character < RPG::Sprite class Sprite_Character < RPG::Sprite
attr_accessor :character attr_accessor :character
@@ -68,11 +64,9 @@ class Sprite_Character < RPG::Sprite
@oldbushdepth = 0 @oldbushdepth = 0
@spriteoffset = false @spriteoffset = false
if !character || character == $game_player || (character.name[/reflection/i] rescue false) if !character || character == $game_player || (character.name[/reflection/i] rescue false)
@reflection = Sprite_Reflection.new(self, viewport) @reflection = Sprite_Reflection.new(self, character, viewport)
end end
@surfbase = Sprite_SurfBase.new(self, viewport) if character == $game_player @surfbase = Sprite_SurfBase.new(self, character, viewport) if character == $game_player
self.zoom_x = TilemapRenderer::ZOOM_X
self.zoom_y = TilemapRenderer::ZOOM_Y
update update
end end
@@ -86,66 +80,57 @@ class Sprite_Character < RPG::Sprite
end end
def dispose def dispose
@bushbitmap&.dispose @bushbitmap.dispose if @bushbitmap
@bushbitmap = nil @bushbitmap = nil
@charbitmap&.dispose @charbitmap.dispose if @charbitmap
@charbitmap = nil @charbitmap = nil
@reflection&.dispose @reflection.dispose if @reflection
@reflection = nil @reflection = nil
@surfbase&.dispose @surfbase.dispose if @surfbase
@surfbase = nil @surfbase = nil
@character = nil
super super
end end
def refresh_graphic def update
return if @tile_id == @character.tile_id && return if @character.is_a?(Game_Event) && !@character.should_update?
@character_name == @character.character_name && super
@character_hue == @character.character_hue && if @tile_id != @character.tile_id ||
@oldbushdepth == @character.bush_depth @character_name != @character.character_name ||
@character_hue != @character.character_hue ||
@oldbushdepth != @character.bush_depth
@tile_id = @character.tile_id @tile_id = @character.tile_id
@character_name = @character.character_name @character_name = @character.character_name
@character_hue = @character.character_hue @character_hue = @character.character_hue
@oldbushdepth = @character.bush_depth @oldbushdepth = @character.bush_depth
@charbitmap&.dispose
@charbitmap = nil
@bushbitmap&.dispose
@bushbitmap = nil
if @tile_id >= 384 if @tile_id >= 384
@charbitmap.dispose if @charbitmap
@charbitmap = pbGetTileBitmap(@character.map.tileset_name, @tile_id, @charbitmap = pbGetTileBitmap(@character.map.tileset_name, @tile_id,
@character_hue, @character.width, @character.height) @character_hue, @character.width, @character.height)
@charbitmapAnimated = false @charbitmapAnimated = false
@bushbitmap.dispose if @bushbitmap
@bushbitmap = nil
@spriteoffset = false @spriteoffset = false
@cw = Game_Map::TILE_WIDTH * @character.width @cw = Game_Map::TILE_WIDTH * @character.width
@ch = Game_Map::TILE_HEIGHT * @character.height @ch = Game_Map::TILE_HEIGHT * @character.height
self.src_rect.set(0, 0, @cw, @ch) self.src_rect.set(0, 0, @cw, @ch)
self.ox = @cw / 2 self.ox = @cw / 2
self.oy = @ch self.oy = @ch
elsif @character_name != "" @character.sprite_size = [@cw, @ch]
else
@charbitmap.dispose if @charbitmap
@charbitmap = AnimatedBitmap.new( @charbitmap = AnimatedBitmap.new(
"Graphics/Characters/" + @character_name, @character_hue 'Graphics/Characters/' + @character_name, @character_hue)
) RPG::Cache.retain('Graphics/Characters/', @character_name, @character_hue) if @character == $game_player
RPG::Cache.retain("Graphics/Characters/", @character_name, @character_hue) if @character == $game_player
@charbitmapAnimated = true @charbitmapAnimated = true
@bushbitmap.dispose if @bushbitmap
@bushbitmap = nil
@spriteoffset = @character_name[/offset/i] @spriteoffset = @character_name[/offset/i]
@cw = @charbitmap.width / 4 @cw = @charbitmap.width / 4
@ch = @charbitmap.height / 4 @ch = @charbitmap.height / 4
self.ox = @cw / 2 self.ox = @cw / 2
else
self.bitmap = nil
@cw = 0
@ch = 0
@reflection&.update
end
@character.sprite_size = [@cw, @ch] @character.sprite_size = [@cw, @ch]
end end
end
def update
return if @character.is_a?(Game_Event) && !@character.should_update?
super
refresh_graphic
return if !@charbitmap
@charbitmap.update if @charbitmapAnimated @charbitmap.update if @charbitmapAnimated
bushdepth = @character.bush_depth bushdepth = @character.bush_depth
if bushdepth == 0 if bushdepth == 0
@@ -169,21 +154,20 @@ class Sprite_Character < RPG::Sprite
pbDayNightTint(self) pbDayNightTint(self)
end end
end end
this_x = @character.screen_x self.x = @character.screen_x
this_x = ((this_x - (Graphics.width / 2)) * TilemapRenderer::ZOOM_X) + (Graphics.width / 2) if TilemapRenderer::ZOOM_X != 1 self.y = @character.screen_y
self.x = this_x
this_y = @character.screen_y
this_y = ((this_y - (Graphics.height / 2)) * TilemapRenderer::ZOOM_Y) + (Graphics.height / 2) if TilemapRenderer::ZOOM_Y != 1
self.y = this_y
self.z = @character.screen_z(@ch) 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.opacity = @character.opacity
self.blend_type = @character.blend_type self.blend_type = @character.blend_type
if @character.animation_id && @character.animation_id != 0 # self.bush_depth = @character.bush_depth
if @character.animation_id != 0
animation = $data_animations[@character.animation_id] animation = $data_animations[@character.animation_id]
animation(animation, true, @character.animation_height || 3, @character.animation_regular_tone || false) animation(animation, true)
@character.animation_id = 0 @character.animation_id = 0
end end
@reflection&.update @reflection.update if @reflection
@surfbase&.update @surfbase.update if @surfbase
end end
end end

View File

@@ -1,38 +1,34 @@
#===============================================================================
#
#===============================================================================
class Sprite_Reflection class Sprite_Reflection
attr_reader :visible attr_reader :visible
attr_accessor :event
def initialize(parent_sprite, viewport = nil) def initialize(sprite,event,viewport=nil)
@parent_sprite = parent_sprite @rsprite = sprite
@sprite = nil @sprite = nil
@event = event
@height = 0 @height = 0
@fixedheight = false @fixedheight = false
if @parent_sprite.character && @parent_sprite.character != $game_player && if @event && @event!=$game_player
@parent_sprite.character.name[/reflection\((\d+)\)/i] if @event.name[/reflection\((\d+)\)/i]
@height = $~[1].to_i || 0 @height = $~[1].to_i || 0
@fixedheight = true @fixedheight = true
end end
end
@viewport = viewport @viewport = viewport
@disposed = false @disposed = false
update update
end end
def dispose def dispose
return if @disposed if !@disposed
@sprite&.dispose @sprite.dispose if @sprite
@sprite = nil @sprite = nil
@parent_sprite = nil
@disposed = true @disposed = true
end end
end
def disposed? def disposed?
return @disposed @disposed
end
def event
return @parent_sprite.character
end end
def visible=(value) def visible=(value)
@@ -42,7 +38,7 @@ class Sprite_Reflection
def update def update
return if disposed? return if disposed?
shouldShow = @parent_sprite.visible shouldShow = @rsprite.visible
if !shouldShow if !shouldShow
# Just-in-time disposal of sprite # Just-in-time disposal of sprite
if @sprite if @sprite
@@ -54,40 +50,37 @@ class Sprite_Reflection
# Just-in-time creation of sprite # Just-in-time creation of sprite
@sprite = Sprite.new(@viewport) if !@sprite @sprite = Sprite.new(@viewport) if !@sprite
if @sprite if @sprite
x = @parent_sprite.x - (@parent_sprite.ox * TilemapRenderer::ZOOM_X) x = @rsprite.x-@rsprite.ox
y = @parent_sprite.y - (@parent_sprite.oy * TilemapRenderer::ZOOM_Y) y = @rsprite.y-@rsprite.oy
y -= Game_Map::TILE_HEIGHT * TilemapRenderer::ZOOM_Y if event.character_name[/offset/i] y -= 32 if @rsprite.character.character_name[/offset/i]
@height = $PokemonGlobal.bridge if !@fixedheight @height = $PokemonGlobal.bridge if !@fixedheight
y += @height * TilemapRenderer::ZOOM_Y * Game_Map::TILE_HEIGHT / 2 y += @height*16
width = @parent_sprite.src_rect.width width = @rsprite.src_rect.width
height = @parent_sprite.src_rect.height height = @rsprite.src_rect.height
@sprite.x = x + ((width / 2) * TilemapRenderer::ZOOM_X) @sprite.x = x+width/2
@sprite.y = y + ((height + (height / 2)) * TilemapRenderer::ZOOM_Y) @sprite.y = y+height+height/2
@sprite.ox = width / 2 @sprite.ox = width/2
@sprite.oy = (height / 2) - 2 # Hard-coded 2 pixel shift up @sprite.oy = height/2-2 # Hard-coded 2 pixel shift up
@sprite.oy -= event.bob_height * 2 @sprite.oy -= @rsprite.character.bob_height*2
@sprite.z = @parent_sprite.groundY - (Graphics.height / 2) @sprite.z = -50 # Still water is -100, map is 0 and above
@sprite.z -= 1000 # Still water is -2000, map is 0 and above @sprite.zoom_x = @rsprite.zoom_x
@sprite.z += 1 if event == $game_player @sprite.zoom_y = @rsprite.zoom_y
@sprite.zoom_x = @parent_sprite.zoom_x frame = (Graphics.frame_count%40)/10
if Settings::ANIMATE_REFLECTIONS && !GameData::MapMetadata.try_get(event.map_id)&.still_reflections @sprite.zoom_x *= [1.0, 0.95, 1.0, 1.05][frame]
@sprite.zoom_x += 0.05 * @sprite.zoom_x * Math.sin(2 * Math::PI * System.uptime)
end
@sprite.zoom_y = @parent_sprite.zoom_y
@sprite.angle = 180.0 @sprite.angle = 180.0
@sprite.mirror = true @sprite.mirror = true
@sprite.bitmap = @parent_sprite.bitmap @sprite.bitmap = @rsprite.bitmap
@sprite.tone = @parent_sprite.tone @sprite.tone = @rsprite.tone
if @height > 0 if @height>0
@sprite.color = Color.new(48, 96, 160, 255) # Dark still water @sprite.color = Color.new(48,96,160,255) # Dark still water
@sprite.opacity = @parent_sprite.opacity @sprite.opacity = @rsprite.opacity
@sprite.visible = !Settings::TIME_SHADING # Can't time-tone a colored sprite @sprite.visible = !Settings::TIME_SHADING # Can't time-tone a colored sprite
else else
@sprite.color = Color.new(224, 224, 224, 96) @sprite.color = Color.new(224,224,224,96)
@sprite.opacity = @parent_sprite.opacity * 3 / 4 @sprite.opacity = @rsprite.opacity*3/4
@sprite.visible = true @sprite.visible = true
end end
@sprite.src_rect = @parent_sprite.src_rect @sprite.src_rect = @rsprite.src_rect
end end
end end
end end

View File

@@ -1,41 +1,35 @@
#===============================================================================
#
#===============================================================================
class Sprite_SurfBase class Sprite_SurfBase
attr_reader :visible attr_reader :visible
attr_accessor :event
def initialize(parent_sprite, viewport = nil) def initialize(sprite,event,viewport=nil)
@parent_sprite = parent_sprite @rsprite = sprite
@sprite = nil @sprite = nil
@event = event
@viewport = viewport @viewport = viewport
@disposed = false @disposed = false
@surfbitmap = AnimatedBitmap.new("Graphics/Characters/base_surf") @surfbitmap = AnimatedBitmap.new("Graphics/Characters/base_surf")
@divebitmap = AnimatedBitmap.new("Graphics/Characters/base_dive") @divebitmap = AnimatedBitmap.new("Graphics/Characters/base_dive")
RPG::Cache.retain("Graphics/Characters/base_surf") RPG::Cache.retain("Graphics/Characters/base_surf")
RPG::Cache.retain("Graphics/Characters/base_dive") RPG::Cache.retain("Graphics/Characters/base_dive")
@cws = @surfbitmap.width / 4 @cws = @surfbitmap.width/4
@chs = @surfbitmap.height / 4 @chs = @surfbitmap.height/4
@cwd = @divebitmap.width / 4 @cwd = @divebitmap.width/4
@chd = @divebitmap.height / 4 @chd = @divebitmap.height/4
update update
end end
def dispose def dispose
return if @disposed return if @disposed
@sprite&.dispose @sprite.dispose if @sprite
@sprite = nil @sprite = nil
@parent_sprite = nil
@surfbitmap.dispose @surfbitmap.dispose
@divebitmap.dispose @divebitmap.dispose
@disposed = true @disposed = true
end end
def disposed? def disposed?
return @disposed @disposed
end
def event
return @parent_sprite.character
end end
def visible=(value) def visible=(value)
@@ -55,7 +49,7 @@ class Sprite_SurfBase
end end
# Just-in-time creation of sprite # Just-in-time creation of sprite
@sprite = Sprite.new(@viewport) if !@sprite @sprite = Sprite.new(@viewport) if !@sprite
return if !@sprite if @sprite
if $PokemonGlobal.surfing if $PokemonGlobal.surfing
@sprite.bitmap = @surfbitmap.bitmap @sprite.bitmap = @surfbitmap.bitmap
cw = @cws cw = @cws
@@ -65,30 +59,25 @@ class Sprite_SurfBase
cw = @cwd cw = @cwd
ch = @chd ch = @chd
end end
sx = event.pattern_surf * cw sx = @event.pattern_surf*cw
sy = ((event.direction - 2) / 2) * ch sy = ((@event.direction-2)/2)*ch
@sprite.src_rect.set(sx, sy, cw, ch) @sprite.src_rect.set(sx,sy,cw,ch)
if $game_temp.surf_base_coords if $PokemonTemp.surfJump
spr_x = ((($game_temp.surf_base_coords[0] * Game_Map::REAL_RES_X) - event.map.display_x).to_f / Game_Map::X_SUBPIXELS).round @sprite.x = ($PokemonTemp.surfJump[0]*Game_Map::REAL_RES_X-@event.map.display_x+3)/4+(Game_Map::TILE_WIDTH/2)
spr_x += (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
spr_x = ((spr_x - (Graphics.width / 2)) * TilemapRenderer::ZOOM_X) + (Graphics.width / 2) if TilemapRenderer::ZOOM_X != 1
@sprite.x = spr_x
spr_y = ((($game_temp.surf_base_coords[1] * Game_Map::REAL_RES_Y) - event.map.display_y).to_f / Game_Map::Y_SUBPIXELS).round
spr_y += (Game_Map::TILE_HEIGHT / 2) + 16
spr_y = ((spr_y - (Graphics.height / 2)) * TilemapRenderer::ZOOM_Y) + (Graphics.height / 2) if TilemapRenderer::ZOOM_Y != 1
@sprite.y = spr_y
else else
@sprite.x = @parent_sprite.x @sprite.x = @rsprite.x
@sprite.y = @parent_sprite.y @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
@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 = @parent_sprite.zoom_x
@sprite.zoom_y = @parent_sprite.zoom_y
@sprite.tone = @parent_sprite.tone
@sprite.color = @parent_sprite.color
@sprite.opacity = @parent_sprite.opacity
end end
end end

View File

@@ -1,19 +1,12 @@
#===============================================================================
#
#===============================================================================
class Spriteset_Global class Spriteset_Global
attr_reader :playersprite attr_reader :playersprite
@@viewport2 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) @@viewport2 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT)
@@viewport2.z = 200 @@viewport2.z = 200
def initialize def initialize
@map_id = $game_map&.map_id || 0
@follower_sprites = FollowerSprites.new(Spriteset_Map.viewport)
@playersprite = Sprite_Character.new(Spriteset_Map.viewport, $game_player) @playersprite = Sprite_Character.new(Spriteset_Map.viewport, $game_player)
@weather = RPG::Weather.new(Spriteset_Map.viewport)
@picture_sprites = [] @picture_sprites = []
(1..100).each do |i| for i in 1..100
@picture_sprites.push(Sprite_Picture.new(@@viewport2, $game_screen.pictures[i])) @picture_sprites.push(Sprite_Picture.new(@@viewport2, $game_screen.pictures[i]))
end end
@timer_sprite = Sprite_Timer.new @timer_sprite = Sprite_Timer.new
@@ -21,38 +14,16 @@ class Spriteset_Global
end end
def dispose def dispose
@follower_sprites.dispose
@follower_sprites = nil
@playersprite.dispose @playersprite.dispose
@playersprite = nil
@weather.dispose
@weather = nil
@picture_sprites.each { |sprite| sprite.dispose } @picture_sprites.each { |sprite| sprite.dispose }
@picture_sprites.clear
@timer_sprite.dispose @timer_sprite.dispose
@playersprite = nil
@picture_sprites.clear
@timer_sprite = nil @timer_sprite = nil
end end
def update def update
@follower_sprites.update
@playersprite.update @playersprite.update
if @weather.type != $game_screen.weather_type
@weather.fade_in($game_screen.weather_type, $game_screen.weather_max, $game_screen.weather_duration)
end
if @map_id != $game_map.map_id
offsets = $map_factory.getRelativePos(@map_id, 0, 0, $game_map.map_id, 0, 0)
if offsets == [0, 0]
@weather.ox_offset = 0
@weather.oy_offset = 0
else
@weather.ox_offset += offsets[0] * Game_Map::TILE_WIDTH
@weather.oy_offset += offsets[1] * Game_Map::TILE_HEIGHT
end
@map_id = $game_map.map_id
end
@weather.ox = ($game_map.display_x / Game_Map::X_SUBPIXELS).round
@weather.oy = ($game_map.display_y / Game_Map::Y_SUBPIXELS).round
@weather.update
@picture_sprites.each { |sprite| sprite.update } @picture_sprites.each { |sprite| sprite.update }
@timer_sprite.update @timer_sprite.update
end end

View File

@@ -1,42 +1,38 @@
#===============================================================================
# Unused.
#===============================================================================
class ClippableSprite < Sprite_Character class ClippableSprite < Sprite_Character
def initialize(viewport, event, tilemap) def initialize(viewport,event,tilemap)
@tilemap = tilemap @tilemap = tilemap
@_src_rect = Rect.new(0, 0, 0, 0) @_src_rect = Rect.new(0,0,0,0)
super(viewport, event) super(viewport,event)
end end
def update def update
super super
@_src_rect = self.src_rect @_src_rect = self.src_rect
tmright = (@tilemap.map_data.xsize * Game_Map::TILE_WIDTH) - @tilemap.ox tmright = @tilemap.map_data.xsize*Game_Map::TILE_WIDTH-@tilemap.ox
echoln "x=#{self.x},ox=#{self.ox},tmright=#{tmright},tmox=#{@tilemap.ox}" echoln("x=#{self.x},ox=#{self.ox},tmright=#{tmright},tmox=#{@tilemap.ox}")
if @tilemap.ox - self.ox < -self.x if @tilemap.ox-self.ox<-self.x
# clipped on left # clipped on left
diff = -self.x - @tilemap.ox + self.ox diff = -self.x-@tilemap.ox+self.ox
self.src_rect = Rect.new(@_src_rect.x + diff, @_src_rect.y, self.src_rect = Rect.new(@_src_rect.x+diff,@_src_rect.y,
@_src_rect.width - diff, @_src_rect.height) @_src_rect.width-diff,@_src_rect.height)
echoln "clipped out left: #{diff} #{@tilemap.ox - self.ox} #{self.x}" echoln("clipped out left: #{diff} #{@tilemap.ox-self.ox} #{self.x}")
elsif tmright - self.ox < self.x elsif tmright-self.ox<self.x
# clipped on right # clipped on right
diff = self.x - tmright + self.ox diff = self.x-tmright+self.ox
self.src_rect = Rect.new(@_src_rect.x, @_src_rect.y, self.src_rect = Rect.new(@_src_rect.x,@_src_rect.y,
@_src_rect.width - diff, @_src_rect.height) @_src_rect.width-diff,@_src_rect.height)
echoln "clipped out right: #{diff} #{tmright + self.ox} #{self.x}" echoln("clipped out right: #{diff} #{tmright+self.ox} #{self.x}")
else else
echoln "-not- clipped out left: #{diff} #{@tilemap.ox - self.ox} #{self.x}" echoln("-not- clipped out left: #{diff} #{@tilemap.ox-self.ox} #{self.x}")
end end
end end
end end
#===============================================================================
#
#===============================================================================
class Spriteset_Map class Spriteset_Map
attr_reader :map attr_reader :map
attr_accessor :tilemap
@@viewport0 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) # Panorama @@viewport0 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) # Panorama
@@viewport0.z = -100 @@viewport0.z = -100
@@viewport1 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) # Map, events, player, fog @@viewport1 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) # Map, events, player, fog
@@ -44,40 +40,51 @@ class Spriteset_Map
@@viewport3 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) # Flashing @@viewport3 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) # Flashing
@@viewport3.z = 500 @@viewport3.z = 500
# For access by Spriteset_Global. def Spriteset_Map.viewport # For access by Spriteset_Global
def self.viewport
return @@viewport1 return @@viewport1
end end
def initialize(map = nil) def initialize(map=nil)
@map = (map) ? map : $game_map @map = (map) ? map : $game_map
$scene.map_renderer.add_tileset(@map.tileset_name) @tilemap = TilemapLoader.new(@@viewport1)
@map.autotile_names.each { |filename| $scene.map_renderer.add_autotile(filename) } @tilemap.tileset = pbGetTileset(@map.tileset_name)
$scene.map_renderer.add_extra_autotiles(@map.tileset_id) 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) @panorama = AnimatedPlane.new(@@viewport0)
@fog = AnimatedPlane.new(@@viewport1) @fog = AnimatedPlane.new(@@viewport1)
@fog.z = 3000 @fog.z = 3000
@character_sprites = [] @character_sprites = []
@map.events.keys.sort.each do |i| for i in @map.events.keys.sort
sprite = Sprite_Character.new(@@viewport1, @map.events[i]) sprite = Sprite_Character.new(@@viewport1,@map.events[i])
@character_sprites.push(sprite) @character_sprites.push(sprite)
end end
EventHandlers.trigger(:on_new_spriteset_map, self, @@viewport1) @weather = RPG::Weather.new(@@viewport1)
pbOnSpritesetCreate(self,@@viewport1)
update update
end end
def dispose def dispose
if $scene.is_a?(Scene_Map) @tilemap.tileset.dispose
$scene.map_renderer.remove_tileset(@map.tileset_name) for i in 0...7
@map.autotile_names.each { |filename| $scene.map_renderer.remove_autotile(filename) } @tilemap.autotiles[i].dispose
$scene.map_renderer.remove_extra_autotiles(@map.tileset_id)
end end
@tilemap.dispose
@panorama.dispose @panorama.dispose
@fog.dispose @fog.dispose
@character_sprites.each { |sprite| sprite.dispose } for sprite in @character_sprites
sprite.dispose
end
@weather.dispose
@tilemap = nil
@panorama = nil @panorama = nil
@fog = nil @fog = nil
@character_sprites.clear @character_sprites.clear
@weather = nil
end end
def getAnimations def getAnimations
@@ -89,40 +96,51 @@ class Spriteset_Map
end end
def update def update
if @panorama_name != @map.panorama_name || @panorama_hue != @map.panorama_hue if @panorama_name!=@map.panorama_name || @panorama_hue!=@map.panorama_hue
@panorama_name = @map.panorama_name @panorama_name = @map.panorama_name
@panorama_hue = @map.panorama_hue @panorama_hue = @map.panorama_hue
@panorama.set_panorama(nil) if !@panorama.bitmap.nil? @panorama.setPanorama(nil) if @panorama.bitmap!=nil
@panorama.set_panorama(@panorama_name, @panorama_hue) if !nil_or_empty?(@panorama_name) @panorama.setPanorama(@panorama_name,@panorama_hue) if @panorama_name!=""
Graphics.frame_reset Graphics.frame_reset
end end
if @fog_name != @map.fog_name || @fog_hue != @map.fog_hue if @fog_name!=@map.fog_name || @fog_hue!=@map.fog_hue
@fog_name = @map.fog_name @fog_name = @map.fog_name
@fog_hue = @map.fog_hue @fog_hue = @map.fog_hue
@fog.set_fog(nil) if !@fog.bitmap.nil? @fog.setFog(nil) if @fog.bitmap!=nil
@fog.set_fog(@fog_name, @fog_hue) if !nil_or_empty?(@fog_name) @fog.setFog(@fog_name,@fog_hue) if @fog_name!=""
Graphics.frame_reset Graphics.frame_reset
end end
tmox = (@map.display_x / Game_Map::X_SUBPIXELS).round tmox = (@map.display_x/Game_Map::X_SUBPIXELS).round
tmoy = (@map.display_y / Game_Map::Y_SUBPIXELS).round tmoy = (@map.display_y/Game_Map::Y_SUBPIXELS).round
@@viewport1.rect.set(0, 0, Graphics.width, Graphics.height) @tilemap.ox = tmox
@tilemap.oy = tmoy
@@viewport1.rect.set(0,0,Graphics.width,Graphics.height)
@@viewport1.ox = 0 @@viewport1.ox = 0
@@viewport1.oy = 0 @@viewport1.oy = 0
@@viewport1.ox += $game_screen.shake @@viewport1.ox += $game_screen.shake
@panorama.ox = tmox / 2 @tilemap.update
@panorama.oy = tmoy / 2 @panorama.ox = tmox/2
@fog.ox = tmox + @map.fog_ox @panorama.oy = tmoy/2
@fog.oy = tmoy + @map.fog_oy @fog.ox = tmox+@map.fog_ox
@fog.zoom_x = @map.fog_zoom / 100.0 @fog.oy = tmoy+@map.fog_oy
@fog.zoom_y = @map.fog_zoom / 100.0 @fog.zoom_x = @map.fog_zoom/100.0
@fog.zoom_y = @map.fog_zoom/100.0
@fog.opacity = @map.fog_opacity @fog.opacity = @map.fog_opacity
@fog.blend_type = @map.fog_blend_type @fog.blend_type = @map.fog_blend_type
@fog.tone = @map.fog_tone @fog.tone = @map.fog_tone
@panorama.update @panorama.update
@fog.update @fog.update
@character_sprites.each do |sprite| for sprite in @character_sprites
sprite.update sprite.update
end end
if self.map!=$game_map
@weather.fade_in(:None, 0, 20)
else
@weather.fade_in($game_screen.weather_type, $game_screen.weather_max, $game_screen.weather_duration)
end
@weather.ox = tmox
@weather.oy = tmoy
@weather.update
@@viewport1.tone = $game_screen.tone @@viewport1.tone = $game_screen.tone
@@viewport3.color = $game_screen.flash_color @@viewport3.color = $game_screen.flash_color
@@viewport1.update @@viewport1.update

View File

@@ -1,336 +1,86 @@
#=============================================================================== =begin
# A sprite whose sole purpose is to display an animation. This sprite
#=============================================================================== can be displayed anywhere on the map and is disposed
class SpriteAnimation automatically when its animation is finished.
@@_animations = [] Used for grass rustling and so forth.
@@_reference_count = {} =end
class AnimationSprite < RPG::Sprite
def initialize(sprite) def initialize(animID,map,tileX,tileY,viewport=nil,tinting=false,height=3)
@sprite = sprite
end
def x(*arg); @sprite.x(*arg); end
def y(*arg); @sprite.y(*arg); end
def ox(*arg); @sprite.ox(*arg); end
def oy(*arg); @sprite.oy(*arg); end
def viewport(*arg); @sprite.viewport(*arg); end
def flash(*arg); @sprite.flash(*arg); end
def src_rect(*arg); @sprite.src_rect(*arg); end
def opacity(*arg); @sprite.opacity(*arg); end
def tone(*arg); @sprite.tone(*arg); end
def self.clear
@@_animations.clear
end
def dispose
dispose_animation
dispose_loop_animation
end
def animation(animation, hit, height = 3, no_tone = false)
dispose_animation
@_animation = animation
return if @_animation.nil?
@_animation_hit = hit
@_animation_height = height
@_animation_no_tone = no_tone
@_animation_duration = @_animation.frame_max
@_animation_index = -1
fr = 20
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
fr = $~[1].to_i
end
@_animation_time_per_frame = 1.0 / fr
@_animation_timer_start = System.uptime
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 || !@@_animations.include?(animation)
16.times do
sprite = ::Sprite.new(self.viewport)
sprite.bitmap = bitmap
sprite.visible = false
@_animation_sprites.push(sprite)
end
@@_animations.push(animation) unless @@_animations.include?(animation)
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_duration = @_animation.frame_max
@_loop_animation_index = -1
fr = 20
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
fr = $~[1].to_i
end
@_loop_animation_time_per_frame = 1.0 / fr
@_loop_animation_timer_start = System.uptime
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 = []
16.times do
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
@@_reference_count[sprite.bitmap] -= 1
sprite.bitmap.dispose if @@_reference_count[sprite.bitmap] == 0
end
@_animation_sprites.each { |s| s.dispose }
@_animation_sprites = nil
@_animation = nil
@_animation_duration = 0
end
def dispose_loop_animation
return if @_loop_animation_sprites.nil?
sprite = @_loop_animation_sprites[0]
if sprite
@@_reference_count[sprite.bitmap] -= 1
sprite.bitmap.dispose if @@_reference_count[sprite.bitmap] == 0
end
@_loop_animation_sprites.each { |s| s.dispose }
@_loop_animation_sprites = nil
@_loop_animation = nil
end
def active?
return @_loop_animation_sprites || @_animation_sprites
end
def effect?
return @_animation_duration > 0
end
def update
update_animation if @_animation
update_loop_animation if @_loop_animation
end
def update_animation
new_index = ((System.uptime - @_animation_timer_start) / @_animation_time_per_frame).to_i
if new_index >= @_animation_duration
dispose_animation
return
end
quick_update = (@_animation_index == new_index)
@_animation_index = new_index
frame_index = @_animation_index
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
@_animation.timings.each do |timing|
next if timing.frame != frame_index
animation_process_timing(timing, @_animation_hit)
end
end
def update_loop_animation
new_index = ((System.uptime - @_loop_animation_timer_start) / @_loop_animation_time_per_frame).to_i
new_index %= @_loop_animation_duration
quick_update = (@_loop_animation_index == new_index)
@_loop_animation_index = new_index
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
@_loop_animation.timings.each do |timing|
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 # Screen
if self.viewport
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
if self.src_rect.height > 1
sprite_y += self.src_rect.height / 2 if position == 1 # Middle
sprite_y += self.src_rect.height if position == 2 # Bottom
end
end
16.times do |i|
sprite = sprites[i]
pattern = cell_data[i, 0]
if sprite.nil? || pattern.nil? || pattern == -1
sprite.visible = false if sprite
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 -1 then sprite.z = -25
when 0 then sprite.z = 1
when 1 then sprite.z = sprite.y + (Game_Map::TILE_HEIGHT * 3 / 2) + 1
when 2 then sprite.z = sprite.y + (Game_Map::TILE_HEIGHT * 3) + 1
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 if !@_animation_no_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 ||
(timing.condition == 1 && hit == true) ||
(timing.condition == 2 && 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
self.viewport.flash(timing.flash_color, timing.flash_duration * 2) if self.viewport
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
16.times { |i| @_animation_sprites[i].x += sx }
end
if @_loop_animation_sprites
16.times { |i| @_loop_animation_sprites[i].x += sx }
end
end
def y=(y)
sy = y - self.y
return if sy == 0
if @_animation_sprites
16.times { |i| @_animation_sprites[i].y += sy }
end
if @_loop_animation_sprites
16.times { |i| @_loop_animation_sprites[i].y += sy }
end
end
end
#===============================================================================
# A sprite whose sole purpose is to display an animation (a SpriteAnimation).
# 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.
#===============================================================================
class AnimationContainerSprite < RPG::Sprite
def initialize(animID, map, tileX, tileY, viewport = nil, tinting = false, height = 3)
super(viewport) super(viewport)
@tileX = tileX @tileX = tileX
@tileY = tileY @tileY = tileY
self.bitmap = Bitmap.new(1, 1)
self.bitmap.clear
@map = map @map = map
setCoords setCoords
pbDayNightTint(self) if tinting pbDayNightTint(self) if tinting
self.animation($data_animations[animID], true, height) self.animation($data_animations[animID],true,height)
end end
def setCoords def setCoords
self.x = (((@tileX * Game_Map::REAL_RES_X) - @map.display_x) / Game_Map::X_SUBPIXELS).ceil self.x = ((@tileX * Game_Map::REAL_RES_X - @map.display_x) / Game_Map::X_SUBPIXELS).ceil
self.x += Game_Map::TILE_WIDTH / 2 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 = ((@tileY * Game_Map::REAL_RES_Y - @map.display_y) / Game_Map::Y_SUBPIXELS).ceil
self.y += Game_Map::TILE_HEIGHT self.y += Game_Map::TILE_HEIGHT
end end
def dispose
self.bitmap.dispose
super
end
def update def update
return if disposed? if !self.disposed?
setCoords setCoords
super super
dispose if !effect? self.dispose if !self.effect?
end
end end
end end
#===============================================================================
#
#===============================================================================
class Spriteset_Map class Spriteset_Map
attr_reader :usersprites alias _animationSprite_initialize initialize
alias _animationSprite_update update
alias _animationSprite_dispose dispose
alias _animationSprite_initialize initialize unless private_method_defined?(:_animationSprite_initialize) def initialize(map=nil)
alias _animationSprite_update update unless method_defined?(:_animationSprite_update) @usersprites=[]
alias _animationSprite_dispose dispose unless method_defined?(:_animationSprite_dispose)
def initialize(map = nil)
@usersprites = []
_animationSprite_initialize(map) _animationSprite_initialize(map)
end end
# Used to display animations that remain in the same location on the map. def addUserAnimation(animID,x,y,tinting=false,height=3)
# Typically for grass rustling and dust clouds, and other animations that sprite=AnimationSprite.new(animID,$game_map,x,y,@@viewport1,tinting,height)
# aren't relative to an event.
def addUserAnimation(animID, x, y, tinting = false, height = 3)
sprite = AnimationContainerSprite.new(animID, self.map, x, y, @@viewport1, tinting, height)
addUserSprite(sprite) addUserSprite(sprite)
return sprite return sprite
end end
def addUserSprite(new_sprite) def addUserSprite(sprite)
@usersprites.each_with_index do |sprite, i| for i in 0...@usersprites.length
next if sprite && !sprite.disposed? if @usersprites[i]==nil || @usersprites[i].disposed?
@usersprites[i] = new_sprite @usersprites[i]=sprite
return return
end end
@usersprites.push(new_sprite) end
@usersprites.push(sprite)
end end
def dispose def dispose
_animationSprite_dispose _animationSprite_dispose
@usersprites.each { |sprite| sprite.dispose } for i in 0...@usersprites.length
@usersprites[i].dispose
end
@usersprites.clear @usersprites.clear
end end
def update def update
@@viewport3.tone.set(0, 0, 0, 0) return if @tilemap.disposed?
pbDayNightTint(@tilemap)
@@viewport3.tone.set(0,0,0,0)
_animationSprite_update _animationSprite_update
@usersprites.each { |sprite| sprite.update if !sprite.disposed? } for i in 0...@usersprites.length
@usersprites.delete_if { |sprite| sprite.disposed? } @usersprites[i].update if !@usersprites[i].disposed?
end
end end
end end

View File

@@ -7,19 +7,19 @@
class Sprite_Shadow < RPG::Sprite class Sprite_Shadow < RPG::Sprite
attr_accessor :character attr_accessor :character
def initialize(viewport, character = nil, params = []) def initialize(viewport, character = nil,params=[])
super(viewport) super(viewport)
@source = params[0] @source = params[0]
@anglemin = (params.size > 1) ? params[1] : 0 @anglemin = (params.size>1) ? params[1] : 0
@anglemax = (params.size > 2) ? params[2] : 0 @anglemax = (params.size>2) ? params[2] : 0
@self_opacity = (params.size > 4) ? params[4] : 100 @self_opacity = (params.size>4) ? params[4] : 100
@distancemax = (params.size > 3) ? params[3] : 350 @distancemax = (params.size>3) ? params[3] : 350
@character = character @character = character
update update
end end
def dispose def dispose
@chbitmap&.dispose @chbitmap.dispose if @chbitmap
super super
end end
@@ -35,8 +35,8 @@ class Sprite_Shadow < RPG::Sprite
@tile_id = @character.tile_id @tile_id = @character.tile_id
@character_name = @character.character_name @character_name = @character.character_name
@character_hue = @character.character_hue @character_hue = @character.character_hue
@chbitmap&.dispose
if @tile_id >= 384 if @tile_id >= 384
@chbitmap.dispose if @chbitmap
@chbitmap = pbGetTileBitmap(@character.map.tileset_name, @chbitmap = pbGetTileBitmap(@character.map.tileset_name,
@tile_id, @character.character_hue) @tile_id, @character.character_hue)
self.src_rect.set(0, 0, 32, 32) self.src_rect.set(0, 0, 32, 32)
@@ -45,8 +45,9 @@ class Sprite_Shadow < RPG::Sprite
self.ox = 16 self.ox = 16
self.oy = 32 self.oy = 32
else else
@chbitmap = AnimatedBitmap.new("Graphics/Characters/" + @character.character_name, @chbitmap.dispose if @chbitmap
@character.character_hue) @chbitmap = AnimatedBitmap.new(
'Graphics/Characters/'+@character.character_name,@character.character_hue)
@cw = @chbitmap.width / 4 @cw = @chbitmap.width / 4
@ch = @chbitmap.height / 4 @ch = @chbitmap.height / 4
self.ox = @cw / 2 self.ox = @cw / 2
@@ -74,8 +75,8 @@ class Sprite_Shadow < RPG::Sprite
self.src_rect.set(sx, sy, @cw, @ch) self.src_rect.set(sx, sy, @cw, @ch)
end end
self.x = ScreenPosHelper.pbScreenX(@character) self.x = ScreenPosHelper.pbScreenX(@character)
self.y = ScreenPosHelper.pbScreenY(@character) - 5 self.y = ScreenPosHelper.pbScreenY(@character)-5
self.z = ScreenPosHelper.pbScreenZ(@character, @ch) - 1 self.z = ScreenPosHelper.pbScreenZ(@character,@ch)-1
self.zoom_x = ScreenPosHelper.pbScreenZoomX(@character) self.zoom_x = ScreenPosHelper.pbScreenZoomX(@character)
self.zoom_y = ScreenPosHelper.pbScreenZoomY(@character) self.zoom_y = ScreenPosHelper.pbScreenZoomY(@character)
self.blend_type = @character.blend_type self.blend_type = @character.blend_type
@@ -87,11 +88,11 @@ class Sprite_Shadow < RPG::Sprite
end end
@deltax = ScreenPosHelper.pbScreenX(@source) - self.x @deltax = ScreenPosHelper.pbScreenX(@source) - self.x
@deltay = ScreenPosHelper.pbScreenY(@source) - self.y @deltay = ScreenPosHelper.pbScreenY(@source) - self.y
self.color = Color.black self.color = Color.new(0, 0, 0)
@distance = ((@deltax**2) + (@deltay**2)) @distance = ((@deltax ** 2) + (@deltay ** 2))
self.opacity = @self_opacity * 13_000 / ((@distance * 370 / @distancemax) + 6000) self.opacity = @self_opacity * 13000 / ((@distance * 370 / @distancemax) + 6000)
self.angle = 57.3 * Math.atan2(@deltax, @deltay) self.angle = 57.3 * Math.atan2(@deltax, @deltay)
@angle_trigo = self.angle + 90 @angle_trigo = self.angle+90
@angle_trigo += 360 if @angle_trigo < 0 @angle_trigo += 360 if @angle_trigo < 0
if @anglemin != 0 || @anglemax != 0 if @anglemin != 0 || @anglemax != 0
if (@angle_trigo < @anglemin || @angle_trigo > @anglemax) && @anglemin < @anglemax if (@angle_trigo < @anglemin || @angle_trigo > @anglemax) && @anglemin < @anglemax
@@ -105,8 +106,7 @@ class Sprite_Shadow < RPG::Sprite
end end
end end
# From Near's Anti Lag Script, edited. def in_range?(element, object, range) # From Near's Anti Lag Script, edited
def in_range?(element, object, range)
elemScreenX = ScreenPosHelper.pbScreenX(element) elemScreenX = ScreenPosHelper.pbScreenX(element)
elemScreenY = ScreenPosHelper.pbScreenY(element) elemScreenY = ScreenPosHelper.pbScreenY(element)
objScreenX = ScreenPosHelper.pbScreenX(object) objScreenX = ScreenPosHelper.pbScreenX(object)
@@ -118,11 +118,13 @@ class Sprite_Shadow < RPG::Sprite
end end
end end
#===============================================================================
#===================================================
# ? CLASS Sprite_Character edit # ? CLASS Sprite_Character edit
#=============================================================================== #===================================================
class Sprite_Character < RPG::Sprite class Sprite_Character < RPG::Sprite
alias shadow_initialize initialize unless private_method_defined?(:shadow_initialize) alias :shadow_initialize :initialize
def initialize(viewport, character = nil) def initialize(viewport, character = nil)
@ombrelist = [] @ombrelist = []
@@ -130,74 +132,84 @@ class Sprite_Character < RPG::Sprite
shadow_initialize(viewport, @character) shadow_initialize(viewport, @character)
end end
def setShadows(map, shadows) def setShadows(map,shadows)
if character.is_a?(Game_Event) && shadows.length > 0 if character.is_a?(Game_Event) && shadows.length > 0
params = XPML_read(map, "Shadow", @character, 4) params = XPML_read(map,"Shadow",@character,4)
if params if params != nil
shadows.each do |shadow| for i in 0...shadows.size
@ombrelist.push(Sprite_Shadow.new(viewport, @character, shadows)) @ombrelist.push(Sprite_Shadow.new(viewport, @character, shadows[i]))
end end
end end
end end
if character.is_a?(Game_Player) && shadows.length > 0 if character.is_a?(Game_Player) && shadows.length > 0
shadows.each do |shadow| for i in 0...shadows.size
@ombrelist.push(Sprite_Shadow.new(viewport, $game_player, shadow)) @ombrelist.push(Sprite_Shadow.new(viewport, $game_player, shadows[i]))
end end
end end
update update
end end
def clearShadows def clearShadows
@ombrelist.each { |s| s&.dispose } @ombrelist.each { |s| s.dispose if s }
@ombrelist.clear @ombrelist.clear
end end
alias shadow_update update unless method_defined?(:shadow_update) alias shadow_update update
def update def update
shadow_update shadow_update
@ombrelist.each { |ombre| ombre.update } if @ombrelist.length>0
for i in 0...@ombrelist.size
@ombrelist[i].update
end
end
end end
end end
#===============================================================================
#===================================================
# ? CLASS Game_Event edit # ? CLASS Game_Event edit
#=============================================================================== #===================================================
class Game_Event class Game_Event
attr_accessor :id attr_accessor :id
end end
#===============================================================================
#===================================================
# ? CLASS Spriteset_Map edit # ? CLASS Spriteset_Map edit
#=============================================================================== #===================================================
class Spriteset_Map class Spriteset_Map
attr_accessor :shadows attr_accessor :shadows
alias shadow_initialize initialize unless private_method_defined?(:shadow_initialize) alias shadow_initialize initialize
def initialize(map=nil)
def initialize(map = nil)
@shadows = [] @shadows = []
warn = false warn = false
map = $game_map if !map map = $game_map if !map
map.events.keys.sort.each do |k| for k in map.events.keys.sort
ev = map.events[k] ev = map.events[k]
warn = true if ev.list && ev.list.length > 0 && ev.list[0].code == 108 && warn = true if (ev.list != nil && ev.list.length > 0 &&
(ev.list[0].parameters == ["s"] || ev.list[0].parameters == ["o"]) ev.list[0].code == 108 &&
params = XPML_read(map, "Shadow Source", ev, 4) (ev.list[0].parameters == ["s"] || ev.list[0].parameters == ["o"]))
@shadows.push([ev] + params) if params params = XPML_read(map,"Shadow Source", ev, 4)
@shadows.push([ev] + params) if params != nil
end end
if warn == true if warn == true
p "Warning : At least one event on this map uses the obsolete way to add shadows" p "Warning : At least one event on this map uses the obsolete way to add shadows"
end end
shadow_initialize(map) shadow_initialize(map)
@character_sprites.each do |sprite| for sprite in @character_sprites
sprite.setShadows(map, @shadows) sprite.setShadows(map, @shadows)
end end
$scene.spritesetGlobal.playersprite.setShadows(map, @shadows) $scene.spritesetGlobal.playersprite.setShadows(map, @shadows)
end end
end end
#===============================================================================
#===================================================
# ? XPML Definition, by Rataime, using ideas from Near Fantastica # ? XPML Definition, by Rataime, using ideas from Near Fantastica
# #
# Returns nil if the markup wasn't present at all, # Returns nil if the markup wasn't present at all,
@@ -215,25 +227,32 @@ end
# p XPML_read("second", event_id) -> [1, "two"] # p XPML_read("second", event_id) -> [1, "two"]
# p XPML_read("third", event_id) -> [3] # p XPML_read("third", event_id) -> [3]
# p XPML_read("forth", event_id) -> nil # p XPML_read("forth", event_id) -> nil
#=============================================================================== #===================================================
def XPML_read(map, markup, event, max_param_number = 0) def XPML_read(map,markup,event,max_param_number=0)
parameter_list = nil parameter_list = nil
return nil if !event || event.list.nil? return nil if !event || event.list == nil
event.list.size.times do |i| for i in 0...event.list.size
next unless event.list[i].code == 108 && if event.list[i].code == 108 &&
event.list[i].parameters[0].downcase == "begin " + markup.downcase event.list[i].parameters[0].downcase == "begin " + markup.downcase
parameter_list = [] if parameter_list.nil? parameter_list = [] if parameter_list == nil
((i + 1)...event.list.size).each do |j| for j in i+1...event.list.size
return parameter_list if event.list[j].code != 108 if event.list[j].code == 108
parts = event.list[j].parameters[0].split parts = event.list[j].parameters[0].split
return parameter_list if parts.size == 1 || parts[0].downcase == "begin" if parts.size != 1 && parts[0].downcase != "begin"
if parts[1].to_i != 0 || parts[1] == "0" if parts[1].to_i != 0 || parts[1] == "0"
parameter_list.push(parts[1].to_i) parameter_list.push(parts[1].to_i)
else else
parameter_list.push(parts[1]) parameter_list.push(parts[1])
end end
else
return parameter_list
end
else
return parameter_list
end
return parameter_list if max_param_number != 0 && j == i + max_param_number return parameter_list if max_param_number != 0 && j == i + max_param_number
end end
end end
end
return parameter_list return parameter_list
end end

View File

@@ -0,0 +1,587 @@
# 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, :blend_type
attr_reader :bitmap
def initialize(viewport)
@viewport = viewport
@sprite = nil
@x = 0
@y = 0
@z = 0
@ox = 0
@oy = 0
@opacity = 255
@bitmap = nil
@blend_type = 0
@minleft = 0
@mintop = 0
end
def dispose
@sprite.dispose if @sprite
end
def bitmap=(value)
@bitmap = value
if value
@minleft = -value.width
@mintop = -value.height
else
@minleft = 0
@mintop = 0
end
end
def update
w = Graphics.width
h = Graphics.height
if !@sprite && @x>=@minleft && @y>=@mintop && @x<w && @y<h
@sprite = Sprite.new(@viewport)
elsif @sprite && (@x<@minleft || @y<@mintop || @x>=w || @y>=h)
@sprite.dispose
@sprite = nil
end
if @sprite
@sprite.x = @x if @sprite.x!=@x
@sprite.x -= @ox
@sprite.y = @y if @sprite.y!=@y
@sprite.y -= @oy
@sprite.z = @z if @sprite.z!=@z
@sprite.opacity = @opacity if @sprite.opacity!=@opacity
@sprite.blend_type = @blend_type if @sprite.blend_type!=@blend_type
@sprite.bitmap = @bitmap if @sprite.bitmap!=@bitmap
end
end
end
class ParticleEffect_Event < ParticleEffect
attr_accessor :event
def initialize(event,viewport=nil)
@event = event
@viewport = viewport
@particles = []
@bitmaps = {}
end
def setParameters(params)
@randomhue,@leftright,@fade,
@maxparticless,@hue,@slowdown,
@ytop,@ybottom,@xleft,@xright,
@xgravity,@ygravity,@xoffset,@yoffset,
@opacityvar,@originalopacity = params
end
def loadBitmap(filename,hue)
key = [filename,hue]
bitmap = @bitmaps[key]
if !bitmap || bitmap.disposed?
bitmap = AnimatedBitmap.new("Graphics/Fogs/"+filename,hue).deanimate
@bitmaps[key] = bitmap
end
return bitmap
end
def initParticles(filename,opacity,zOffset=0,blendtype=1)
@particles = []
@particlex = []
@particley = []
@opacity = []
@startingx = self.x + @xoffset
@startingy = self.y + @yoffset
@screen_x = self.x
@screen_y = self.y
@real_x = @event.real_x
@real_y = @event.real_y
@filename = filename
@zoffset = zOffset
@bmwidth = 32
@bmheight = 32
for i in 0...@maxparticless
@particlex[i] = -@xoffset
@particley[i] = -@yoffset
@particles[i] = ParticleSprite.new(@viewport)
@particles[i].bitmap = loadBitmap(filename, @hue) if filename
if i==0 && @particles[i].bitmap
@bmwidth = @particles[i].bitmap.width
@bmheight = @particles[i].bitmap.height
end
@particles[i].blend_type = blendtype
@particles[i].y = @startingy
@particles[i].x = @startingx
@particles[i].z = self.z+zOffset
@opacity[i] = rand(opacity/4)
@particles[i].opacity = @opacity[i]
@particles[i].update
end
end
def x; return ScreenPosHelper.pbScreenX(@event); end
def y; return ScreenPosHelper.pbScreenY(@event); end
def z; return ScreenPosHelper.pbScreenZ(@event); end
def update
if @viewport &&
(@viewport.rect.x >= Graphics.width ||
@viewport.rect.y >= Graphics.height)
return
end
selfX = self.x
selfY = self.y
selfZ = self.z
newRealX = @event.real_x
newRealY = @event.real_y
@startingx = selfX + @xoffset
@startingy = selfY + @yoffset
@__offsetx = (@real_x==newRealX) ? 0 : selfX-@screen_x
@__offsety = (@real_y==newRealY) ? 0 : selfY-@screen_y
@screen_x = selfX
@screen_y = selfY
@real_x = newRealX
@real_y = newRealY
if @opacityvar>0 && @viewport
opac = 255.0/@opacityvar
minX = opac*(-@xgravity*1.0 / @slowdown).floor + @startingx
maxX = opac*(@xgravity*1.0 / @slowdown).floor + @startingx
minY = opac*(-@ygravity*1.0 / @slowdown).floor + @startingy
maxY = @startingy
minX -= @bmwidth
minY -= @bmheight
maxX += @bmwidth
maxY += @bmheight
if maxX<0 || maxY<0 || minX>=Graphics.width || minY>=Graphics.height
# echo "skipped"
return
end
end
particleZ = selfZ+@zoffset
for i in 0...@maxparticless
@particles[i].z = particleZ
if @particles[i].y <= @ytop
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @particles[i].x <= @xleft
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @particles[i].y >= @ybottom
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @particles[i].x >= @xright
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @fade == 0
if @opacity[i] <= 0
@opacity[i] = @originalopacity
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
else
if @opacity[i] <= 0
@opacity[i] = 250
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
end
calcParticlePos(i)
if @randomhue == 1
@hue += 0.5
@hue = 0 if @hue >= 360
@particles[i].bitmap = loadBitmap(@filename, @hue) if @filename
end
@opacity[i] = @opacity[i] - rand(@opacityvar)
@particles[i].opacity = @opacity[i]
@particles[i].update
end
end
def calcParticlePos(i)
@leftright = rand(2)
if @leftright == 1
xo = -@xgravity*1.0 / @slowdown
else
xo = @xgravity*1.0 / @slowdown
end
yo = -@ygravity*1.0 / @slowdown
@particlex[i] += xo
@particley[i] += yo
@particlex[i] -= @__offsetx
@particley[i] -= @__offsety
@particlex[i] = @particlex[i].floor
@particley[i] = @particley[i].floor
@particles[i].x = @particlex[i]+@startingx+@xoffset
@particles[i].y = @particley[i]+@startingy+@yoffset
end
def dispose
for particle in @particles
particle.dispose
end
for bitmap in @bitmaps.values
bitmap.dispose
end
@particles.clear
@bitmaps.clear
end
end
class Particle_Engine::Fire < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,20,40,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
initParticles("particle",250)
end
end
class Particle_Engine::Smoke < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,80,20,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
initParticles("smoke",250)
end
end
class Particle_Engine::Teleport < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([1,1,1,10,rand(360),1,-64,
Graphics.height,-64,Graphics.width,0,3,-8,-15,20,0])
initParticles("wideportal",250)
for i in 0...@maxparticless
@particles[i].ox = 16
@particles[i].oy = 16
end
end
end
class Particle_Engine::Spirit < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([1,0,1,20,rand(360),0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
initParticles("particle",250)
end
end
class Particle_Engine::Explosion < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,20,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
initParticles("explosion",250)
end
end
class Particle_Engine::Aura < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,20,0,1,-64,
Graphics.height,-64,Graphics.width,2,2,-5,-13,30,0])
initParticles("particle",250)
end
end
class Particle_Engine::Soot < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,20,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
initParticles("smoke",100,0,2)
end
end
class Particle_Engine::SootSmoke < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,30,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
initParticles("smoke",100,0)
for i in 0...@maxparticless
@particles[i].blend_type = rand(6) < 3 ? 1 : 2
end
end
end
class Particle_Engine::Rocket < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,60,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0,-5,-15,5,80])
initParticles("smoke",100,-1)
end
end
class Particle_Engine::FixedTeleport < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([1,0,1,10,rand(360),1,
-Graphics.height,Graphics.height,0,Graphics.width,0,3,-8,-15,20,0])
initParticles("wideportal",250)
for i in 0...@maxparticless
@particles[i].ox = 16
@particles[i].oy = 16
end
end
end
# By Peter O.
class Particle_Engine::StarTeleport < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,10,0,1,
-Graphics.height,Graphics.height,0,Graphics.width,0,3,-8,-15,10,0])
initParticles("star",250)
for i in 0...@maxparticless
@particles[i].ox = 48
@particles[i].oy = 48
end
end
end
class Particle_Engine::Smokescreen < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,250,0,0.2,-64,
Graphics.height,-64,Graphics.width,0.8,0.8,-5,-15,5,80])
initParticles(nil,100)
for i in 0...@maxparticless
rnd = rand(3)
@opacity[i] = (rnd==0) ? 1 : 100
filename = (rnd==0) ? "explosionsmoke" : "smoke"
@particles[i].bitmap = loadBitmap(filename, @hue)
end
end
def calcParticlePos(i)
if @randomhue==1
filename = (rand(3)==0) ? "explosionsmoke" : "smoke"
@particles[i].bitmap = loadBitmap(filename, @hue)
end
multiple = 1.7
xgrav = @xgravity*multiple/@slowdown
xgrav = -xgrav if (rand(2)==1)
ygrav = @ygravity*multiple/@slowdown
ygrav = -ygrav if (rand(2)==1)
@particlex[i] += xgrav
@particley[i] += ygrav
@particlex[i] -= @__offsetx
@particley[i] -= @__offsety
@particlex[i] = @particlex[i].floor
@particley[i] = @particley[i].floor
@particles[i].x = @particlex[i]+@startingx+@xoffset
@particles[i].y = @particley[i]+@startingy+@yoffset
end
end
class Particle_Engine::Flare < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,30,10,1,-64,
Graphics.height,-64,Graphics.width,2,2,-5,-12,30,0])
initParticles("particle",255)
end
end
class Particle_Engine::Splash < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,30,255,1,-64,
Graphics.height,-64,Graphics.width,4,2,-5,-12,30,0])
initParticles("smoke",50)
end
def update
super
for i in 0...@maxparticless
@particles[i].opacity = 50
@particles[i].update
end
end
end
class Game_Event < Game_Character
attr_accessor :pe_refresh
alias nf_particles_game_map_initialize initialize
def initialize(map_id,event,map=nil)
@pe_refresh = false
begin
nf_particles_game_map_initialize(map_id, event, map)
rescue ArgumentError
nf_particles_game_map_initialize(map_id, event)
end
end
alias nf_particles_game_map_refresh refresh
def refresh
nf_particles_game_map_refresh
@pe_refresh = true
end
end

View File

@@ -1,548 +0,0 @@
#===============================================================================
#
#===============================================================================
class PictureOrigin
TOP_LEFT = 0
CENTER = 1
TOP_RIGHT = 2
BOTTOM_LEFT = 3
LOWER_LEFT = 3
BOTTOM_RIGHT = 4
LOWER_RIGHT = 4
TOP = 5
BOTTOM = 6
LEFT = 7
RIGHT = 8
end
#===============================================================================
#
#===============================================================================
class Processes
XY = 0
DELTA_XY = 1
Z = 2
CURVE = 3
ZOOM = 4
ANGLE = 5
TONE = 6
COLOR = 7
HUE = 8
OPACITY = 9
VISIBLE = 10
BLEND_TYPE = 11
SE = 12
NAME = 13
ORIGIN = 14
SRC = 15
SRC_SIZE = 16
CROP_BOTTOM = 17
end
#===============================================================================
#
#===============================================================================
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 += ((cx0 - x0) * t)
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 += ((cy0 - y0) * t)
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
#===============================================================================
# PictureEx
#===============================================================================
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 move_processes
ret = []
@processes.each do |p|
next if ![Processes::XY, Processes::DELTA_XY].include?(p[0])
pro = []
pro.push(p[0] == Processes::XY ? "XY" : "DELTA")
if p[1] == 0 && p[2] == 0
pro.push("start " + p[7].to_i.to_s + ", " + p[8].to_i.to_s)
else
pro.push("for " + p[2].to_s) if p[2] > 0
if p[0] == Processes::XY
pro.push("go to " + p[7].to_i.to_s + ", " + p[8].to_i.to_s)
else
pro.push("move by " + p[7].to_i.to_s + ", " + p[8].to_i.to_s)
end
end
ret.push(pro)
end
return ret
end
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
@auto_angle = 0 # Cumulative angle change caused by @rotate_speed
@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::TOP_LEFT
@src_rect = Rect.new(0, 0, -1, -1)
@cropBottom = -1
@frameUpdates = []
end
def callback(cb)
case cb
when Proc
cb.call(self)
when Array
cb[0].method(cb[1]).call(self, *cb[2])
when Method
cb.call(self)
end
end
def setCallback(delay, cb = nil)
delay = ensureDelayAndDuration(delay)
@processes.push([nil, delay, 0, false, cb])
end
def running?
return @processes.length > 0
end
def totalDuration
ret = 0
@processes.each do |process|
dur = process[1] + process[2]
ret = dur if dur > ret
end
return ret
end
def ensureDelayAndDuration(delay, duration = nil)
delay = self.totalDuration if delay < 0
return delay, duration if !duration.nil?
return delay
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
end
def erase
self.name = ""
end
def clearProcesses
@processes = []
@timer_start = nil
end
def adjustPosition(xOffset, yOffset)
@processes.each do |process|
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, false, 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, false, 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::DELTA_XY, delay, duration, false, 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, false, 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, false, 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, false, 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, false, 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, false, 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, false, 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, false, 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, false, 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::BLEND_TYPE, delay, 0, false, cb, blend])
end
def setSE(delay, seFile, volume = nil, pitch = nil, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::SE, delay, 0, false, cb, seFile, volume, pitch])
end
def setName(delay, name, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::NAME, delay, 0, false, cb, name])
end
def setOrigin(delay, origin, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::ORIGIN, delay, 0, false, cb, origin])
end
def setSrc(delay, srcX, srcY, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::SRC, delay, 0, false, cb, srcX, srcY])
end
def setSrcSize(delay, srcWidth, srcHeight, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::SRC_SIZE, delay, 0, false, 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::CROP_BOTTOM, delay, 0, false, cb, y])
end
def update
time_now = System.uptime
@timer_start = time_now if !@timer_start
this_frame = ((time_now - @timer_start) * 20).to_i # 20 frames per second
procEnded = false
@frameUpdates.clear
@processes.each_with_index do |process, i|
# Skip processes that aren't due to start yet
next if process[1] > this_frame
# Set initial values if the process has just started
if !process[3] # Not started yet
process[3] = true # Running
case process[0]
when Processes::XY
process[5] = @x
process[6] = @y
when Processes::DELTA_XY
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
# Update process
@frameUpdates.push(process[0]) if !@frameUpdates.include?(process[0])
start_time = @timer_start + (process[1] / 20.0)
duration = process[2] / 20.0
case process[0]
when Processes::XY, Processes::DELTA_XY
@x = lerp(process[5], process[7], duration, start_time, time_now)
@y = lerp(process[6], process[8], duration, start_time, time_now)
when Processes::CURVE
@x, @y = getCubicPoint2(process[5], (time_now - start_time) / duration)
when Processes::Z
@z = lerp(process[5], process[6], duration, start_time, time_now)
when Processes::ZOOM
@zoom_x = lerp(process[5], process[7], duration, start_time, time_now)
@zoom_y = lerp(process[6], process[8], duration, start_time, time_now)
when Processes::ANGLE
@angle = lerp(process[5], process[6], duration, start_time, time_now)
when Processes::TONE
@tone.red = lerp(process[5].red, process[6].red, duration, start_time, time_now)
@tone.green = lerp(process[5].green, process[6].green, duration, start_time, time_now)
@tone.blue = lerp(process[5].blue, process[6].blue, duration, start_time, time_now)
@tone.gray = lerp(process[5].gray, process[6].gray, duration, start_time, time_now)
when Processes::COLOR
@color.red = lerp(process[5].red, process[6].red, duration, start_time, time_now)
@color.green = lerp(process[5].green, process[6].green, duration, start_time, time_now)
@color.blue = lerp(process[5].blue, process[6].blue, duration, start_time, time_now)
@color.alpha = lerp(process[5].alpha, process[6].alpha, duration, start_time, time_now)
when Processes::HUE
@hue = lerp(process[5], process[6], duration, start_time, time_now)
when Processes::OPACITY
@opacity = lerp(process[5], process[6], duration, start_time, time_now)
when Processes::VISIBLE
@visible = process[5]
when Processes::BLEND_TYPE
@blend_type = process[5]
when Processes::SE
pbSEPlay(process[5], process[6], process[7])
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::SRC_SIZE
@src_rect.width = process[5]
@src_rect.height = process[6]
when Processes::CROP_BOTTOM
@cropBottom = process[5]
end
# Erase process if its duration has elapsed
if process[1] + process[2] <= this_frame
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
@timer_start = nil if @processes.empty? && @rotate_speed == 0
# Add the constant rotation speed
if @rotate_speed != 0
@frameUpdates.push(Processes::ANGLE) if !@frameUpdates.include?(Processes::ANGLE)
@auto_angle = @rotate_speed * (time_now - @timer_start)
while @auto_angle < 0
@auto_angle += 360
end
@auto_angle %= 360
@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
picture.frameUpdates.each do |type|
case type
when Processes::XY, Processes::DELTA_XY
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::BLEND_TYPE
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::TOP_LEFT, PictureOrigin::LEFT, PictureOrigin::BOTTOM_LEFT
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::TOP_RIGHT, PictureOrigin::RIGHT, PictureOrigin::BOTTOM_RIGHT
sprite.ox = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.width : 0
end
case picture.origin
when PictureOrigin::TOP_LEFT, PictureOrigin::TOP, PictureOrigin::TOP_RIGHT
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::BOTTOM_LEFT, PictureOrigin::BOTTOM, PictureOrigin::BOTTOM_RIGHT
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::SRC_SIZE
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

View File

@@ -0,0 +1,519 @@
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
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
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
#===============================================================================
# PictureEx
#===============================================================================
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); cb.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, pitch=nil, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::SE,delay,0,0,cb,seFile,volume,pitch])
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],process[7])
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

View File

@@ -1,48 +0,0 @@
#===============================================================================
#
#===============================================================================
module ScreenPosHelper
@heightcache = {}
module_function
def pbScreenZoomX(ch)
return Game_Map::TILE_WIDTH / 32.0
end
def pbScreenZoomY(ch)
return Game_Map::TILE_HEIGHT / 32.0
end
def pbScreenX(ch)
return ch.screen_x
end
def pbScreenY(ch)
return ch.screen_y
end
def 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 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)
return ret
end
end

View File

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

View File

@@ -0,0 +1,43 @@
module ScreenPosHelper
def self.pbScreenZoomX(ch)
return Game_Map::TILE_WIDTH/32.0
end
def self.pbScreenZoomY(ch)
return Game_Map::TILE_HEIGHT/32.0
end
def self.pbScreenX(ch)
return ch.screen_x
end
def self.pbScreenY(ch)
return ch.screen_y
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)
return ret
end
end

View File

@@ -0,0 +1,59 @@
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
setClass(CustomTilemap)
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
else
@tilemap = newtilemap
end
end
def dispose; @tilemap.dispose; end
def disposed?; @tilemap && @tilemap.disposed?; end
def update; @tilemap.update; end
def viewport; @tilemap.viewport; end
def autotiles; @tilemap.autotiles; end
def tileset; @tilemap.tileset; end
def tileset=(v); @tilemap.tileset = v; end
def map_data; @tilemap.map_data; end
def map_data=(v); @tilemap.map_data = v; end
def flash_data; @tilemap.flash_data; end
def flash_data=(v); @tilemap.flash_data = v; end
def priorities; @tilemap.priorities; end
def priorities=(v); @tilemap.priorities = v; end
def terrain_tags; (@tilemap.terrain_tags rescue nil); end
def terrain_tags=(v); (@tilemap.terrain_tags = v rescue nil); end
def visible; @tilemap.visible; end
def visible=(v); @tilemap.visible = v; end
def tone; (@tilemap.tone rescue @tone); end
def tone=(value); (@tilemap.tone = value rescue nil); end
def ox; @tilemap.ox; end
def ox=(v); @tilemap.ox = v; end
def oy; @tilemap.oy; end
def oy=(v); @tilemap.oy = v; end
end

View File

@@ -1,619 +0,0 @@
#===============================================================================
#
#===============================================================================
class TilemapRenderer
attr_reader :tilesets
attr_reader :autotiles
attr_accessor :tone
attr_accessor :color
attr_reader :viewport
attr_accessor :ox # Does nothing
attr_accessor :oy # Does nothing
attr_accessor :visible # Does nothing
DISPLAY_TILE_WIDTH = Game_Map::TILE_WIDTH rescue 32
DISPLAY_TILE_HEIGHT = Game_Map::TILE_HEIGHT rescue 32
SOURCE_TILE_WIDTH = 32
SOURCE_TILE_HEIGHT = 32
ZOOM_X = DISPLAY_TILE_WIDTH / SOURCE_TILE_WIDTH
ZOOM_Y = DISPLAY_TILE_HEIGHT / SOURCE_TILE_HEIGHT
TILESET_TILES_PER_ROW = 8
AUTOTILES_COUNT = 8 # Counting the blank tile as an autotile
TILES_PER_AUTOTILE = 48
TILESET_START_ID = AUTOTILES_COUNT * TILES_PER_AUTOTILE
# If an autotile's filename ends with "[x]", its frame duration will be x/20
# seconds instead.
AUTOTILE_FRAME_DURATION = 5 # In 1/20ths of a second
# Filenames of extra autotiles for each tileset. Each tileset's entry is an
# array containing two other arrays (you can leave either of those empty, but
# they must be defined):
# - The first sub-array is for large autotiles, i.e. ones with 48 different
# tile layouts. For example, "Brick path" and "Sea".
# - The second is for single tile autotiles. For example, "Flowers1" and
# "Waterfall"
# The top tiles of the tileset will instead use these autotiles. Large
# autotiles come first, in the same 8x6 layout as you see when you double-
# click on a real autotile in RMXP. After that are the single tile autotiles.
# Extra autotiles are only useful if the tiles are animated, because otherwise
# you just have some tiles which belong in the tileset instead.
EXTRA_AUTOTILES = {
# Examples:
# 1 => [["Sand shore"], ["Flowers2"]],
# 2 => [[], ["Flowers2", "Waterfall", "Waterfall crest", "Waterfall bottom"]],
# 6 => [["Water rock", "Sea deep"], []]
}
#=============================================================================
#
#=============================================================================
class TilesetBitmaps
attr_accessor :changed
attr_accessor :bitmaps
def initialize
@bitmaps = {}
@bitmap_wraps = {} # Whether each tileset is a mega texture and has multiple columns
@load_counts = {}
@bridge = 0
@changed = true
end
def [](filename)
return @bitmaps[filename]
end
def []=(filename, bitmap)
return if nil_or_empty?(filename)
@bitmaps[filename] = bitmap
@bitmap_wraps[filename] = false
@changed = true
end
def add(filename)
return if nil_or_empty?(filename)
if @bitmaps[filename]
@load_counts[filename] += 1
return
end
bitmap = pbGetTileset(filename)
@bitmap_wraps[filename] = false
if bitmap.mega?
self[filename] = TilemapRenderer::TilesetWrapper.wrapTileset(bitmap)
@bitmap_wraps[filename] = true
bitmap.dispose
else
self[filename] = bitmap
end
@load_counts[filename] = 1
end
def remove(filename)
return if nil_or_empty?(filename) || !@bitmaps[filename]
if @load_counts[filename] > 1
@load_counts[filename] -= 1
return
end
@bitmaps[filename].dispose
@bitmaps.delete(filename)
@bitmap_wraps.delete(filename)
@load_counts.delete(filename)
end
def set_src_rect(tile, tile_id)
return if nil_or_empty?(tile.filename)
return if !@bitmaps[tile.filename]
tile.src_rect.x = ((tile_id - TILESET_START_ID) % TILESET_TILES_PER_ROW) * SOURCE_TILE_WIDTH
tile.src_rect.y = ((tile_id - TILESET_START_ID) / TILESET_TILES_PER_ROW) * SOURCE_TILE_HEIGHT
if @bitmap_wraps[tile.filename]
height = @bitmaps[tile.filename].height
col = (tile_id - TILESET_START_ID) * SOURCE_TILE_HEIGHT / (TILESET_TILES_PER_ROW * height)
tile.src_rect.x += col * TILESET_TILES_PER_ROW * SOURCE_TILE_WIDTH
tile.src_rect.y -= col * height
end
end
def update; end
end
#=============================================================================
#
#=============================================================================
class AutotileBitmaps < TilesetBitmaps
attr_reader :current_frames
def initialize
super
@frame_counts = {} # Number of frames in each autotile
@frame_durations = {} # How long each frame lasts per autotile
@current_frames = {} # Which frame each autotile is currently showing
@timer_start = System.uptime
end
def []=(filename, value)
super
return if nil_or_empty?(filename)
frame_count(filename, true)
set_current_frame(filename)
end
def add(filename)
return if nil_or_empty?(filename)
if @bitmaps[filename]
@load_counts[filename] += 1
return
end
orig_bitmap = pbGetAutotile(filename)
@bitmap_wraps[filename] = false
duration = AUTOTILE_FRAME_DURATION
if filename[/\[\s*(\d+?)\s*\]\s*$/]
duration = $~[1].to_i
end
@frame_durations[filename] = duration.to_f / 20
bitmap = AutotileExpander.expand(orig_bitmap)
self[filename] = bitmap
if bitmap.height > SOURCE_TILE_HEIGHT && bitmap.height < TILES_PER_AUTOTILE * SOURCE_TILE_HEIGHT
@bitmap_wraps[filename] = true
end
orig_bitmap.dispose if orig_bitmap != bitmap
@load_counts[filename] = 1
end
def remove(filename)
super
return if @load_counts[filename] && @load_counts[filename] > 0
@frame_counts.delete(filename)
@current_frames.delete(filename)
@frame_durations.delete(filename)
end
def frame_count(filename, force_recalc = false)
if !@frame_counts[filename] || force_recalc
return 0 if !@bitmaps[filename]
bitmap = @bitmaps[filename]
@frame_counts[filename] = [bitmap.width / SOURCE_TILE_WIDTH, 1].max
if bitmap.height > SOURCE_TILE_HEIGHT && @bitmap_wraps[filename]
@frame_counts[filename] /= 2
end
end
return @frame_counts[filename]
end
def animated?(filename)
return frame_count(filename) > 1
end
def current_frame(filename)
set_current_frame(filename) if !@current_frames[filename]
return @current_frames[filename]
end
def set_current_frame(filename)
frames = frame_count(filename)
if frames < 2
@current_frames[filename] = 0
else
@current_frames[filename] = ((System.uptime - @timer_start) / @frame_durations[filename]).floor % frames
end
end
def set_src_rect(tile, tile_id)
return if nil_or_empty?(tile.filename)
return if !@bitmaps[tile.filename]
frame = current_frame(tile.filename)
if @bitmaps[tile.filename].height == SOURCE_TILE_HEIGHT
tile.src_rect.x = frame * SOURCE_TILE_WIDTH
tile.src_rect.y = 0
return
end
wraps = @bitmap_wraps[tile.filename]
high_id = ((tile_id % TILES_PER_AUTOTILE) >= TILES_PER_AUTOTILE / 2)
tile.src_rect.x = 0
tile.src_rect.y = (tile_id % TILES_PER_AUTOTILE) * SOURCE_TILE_HEIGHT
if wraps && high_id
tile.src_rect.x = SOURCE_TILE_WIDTH
tile.src_rect.y -= SOURCE_TILE_HEIGHT * TILES_PER_AUTOTILE / 2
end
tile.src_rect.x += frame * SOURCE_TILE_WIDTH * (wraps ? 2 : 1)
end
def update
super
# Update the current frame for each autotile
@bitmaps.each_key do |filename|
next if !@bitmaps[filename] || @bitmaps[filename].disposed?
old_frame = @current_frames[filename]
set_current_frame(filename)
@changed = true if @current_frames[filename] != old_frame
end
end
end
#=============================================================================
#
#=============================================================================
class TileSprite < Sprite
attr_accessor :filename
attr_accessor :tile_id
attr_accessor :is_autotile
attr_accessor :animated
attr_accessor :priority
attr_accessor :shows_reflection
attr_accessor :bridge
attr_accessor :need_refresh
def set_bitmap(filename, tile_id, autotile, animated, priority, bitmap)
self.bitmap = bitmap
self.src_rect = Rect.new(0, 0, SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT)
self.zoom_x = ZOOM_X
self.zoom_y = ZOOM_Y
@filename = filename
@tile_id = tile_id
@is_autotile = autotile
@animated = animated
@priority = priority
@shows_reflection = false
@bridge = false
self.visible = !bitmap.nil?
@need_refresh = true
end
end
#-----------------------------------------------------------------------------
def initialize(viewport)
@tilesets = TilesetBitmaps.new
@autotiles = AutotileBitmaps.new
@tiles_horizontal_count = (Graphics.width.to_f / DISPLAY_TILE_WIDTH).ceil + 1
@tiles_vertical_count = (Graphics.height.to_f / DISPLAY_TILE_HEIGHT).ceil + 1
@tone = Tone.new(0, 0, 0, 0)
@old_tone = Tone.new(0, 0, 0, 0)
@color = Color.new(0, 0, 0, 0)
@old_color = Color.new(0, 0, 0, 0)
@self_viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
@viewport = (viewport) ? viewport : @self_viewport
@old_viewport_ox = 0
@old_viewport_oy = 0
# NOTE: The extra tiles horizontally/vertically hang off the left and top
# edges of the screen, because the pixel_offset values are positive
# and are added to the tile sprite coordinates.
@tiles = []
@tiles_horizontal_count.times do |i|
@tiles[i] = []
@tiles_vertical_count.times do |j|
@tiles[i][j] = Array.new(3) { TileSprite.new(@viewport) }
end
end
@current_map_id = 0
@tile_offset_x = 0
@tile_offset_y = 0
@pixel_offset_x = 0
@pixel_offset_y = 0
@ox = 0
@oy = 0
@visible = true
@need_refresh = true
@disposed = false
end
def dispose
return if disposed?
@tiles.each do |col|
col.each do |coord|
coord.each { |tile| tile.dispose }
coord.clear
end
end
@tiles.clear
@tilesets.bitmaps.each_value { |bitmap| bitmap.dispose }
@tilesets.bitmaps.clear
@autotiles.bitmaps.each_value { |bitmap| bitmap.dispose }
@autotiles.bitmaps.clear
@self_viewport.dispose
@self_viewport = nil
@disposed = true
end
def disposed?
return @disposed
end
#-----------------------------------------------------------------------------
def add_tileset(filename)
@tilesets.add(filename)
end
def remove_tileset(filename)
@tilesets.remove(filename)
end
def add_autotile(filename)
@autotiles.add(filename)
end
def remove_autotile(filename)
@autotiles.remove(filename)
end
def add_extra_autotiles(tileset_id)
return if !EXTRA_AUTOTILES[tileset_id]
EXTRA_AUTOTILES[tileset_id].each do |arr|
arr.each { |filename| add_autotile(filename) }
end
end
def remove_extra_autotiles(tileset_id)
return if !EXTRA_AUTOTILES[tileset_id]
EXTRA_AUTOTILES[tileset_id].each do |arr|
arr.each { |filename| remove_autotile(filename) }
end
end
#-----------------------------------------------------------------------------
def refresh
@need_refresh = true
end
def refresh_tile_bitmap(tile, map, tile_id)
tile.tile_id = tile_id
if tile_id < TILES_PER_AUTOTILE
tile.set_bitmap("", tile_id, false, false, 0, nil)
tile.shows_reflection = false
tile.bridge = false
else
terrain_tag = map.terrain_tags[tile_id] || 0
terrain_tag_data = GameData::TerrainTag.try_get(terrain_tag)
priority = map.priorities[tile_id] || 0
single_autotile_start_id = TILESET_START_ID
true_tileset_start_id = TILESET_START_ID
extra_autotile_arrays = EXTRA_AUTOTILES[map.tileset_id]
if extra_autotile_arrays
large_autotile_count = extra_autotile_arrays[0].length
single_autotile_count = extra_autotile_arrays[1].length
single_autotile_start_id += large_autotile_count * TILES_PER_AUTOTILE
true_tileset_start_id += large_autotile_count * TILES_PER_AUTOTILE
true_tileset_start_id += single_autotile_count
end
if tile_id < true_tileset_start_id
filename = ""
if tile_id < TILESET_START_ID # Real autotiles
filename = map.autotile_names[(tile_id / TILES_PER_AUTOTILE) - 1]
elsif tile_id < single_autotile_start_id # Large extra autotiles
filename = extra_autotile_arrays[0][(tile_id - TILESET_START_ID) / TILES_PER_AUTOTILE]
else # Single extra autotiles
filename = extra_autotile_arrays[1][tile_id - single_autotile_start_id]
end
tile.set_bitmap(filename, tile_id, true, @autotiles.animated?(filename),
priority, @autotiles[filename])
else
filename = map.tileset_name
tile.set_bitmap(filename, tile_id, false, false, priority, @tilesets[filename])
end
tile.shows_reflection = terrain_tag_data&.shows_reflections
tile.bridge = terrain_tag_data&.bridge
end
refresh_tile_src_rect(tile, tile_id)
end
def refresh_tile_src_rect(tile, tile_id)
if tile.is_autotile
@autotiles.set_src_rect(tile, tile_id)
else
@tilesets.set_src_rect(tile, tile_id)
end
end
# For animated autotiles only
def refresh_tile_frame(tile, tile_id)
return if !tile.animated
@autotiles.set_src_rect(tile, tile_id)
end
# x and y are the positions of tile within @tiles, not a map x/y
def refresh_tile_coordinates(tile, x, y)
tile.x = (x * DISPLAY_TILE_WIDTH) - @pixel_offset_x
tile.y = (y * DISPLAY_TILE_HEIGHT) - @pixel_offset_y
end
def refresh_tile_z(tile, map, y, layer, tile_id)
if tile.shows_reflection
tile.z = -2000
elsif tile.bridge && $PokemonGlobal.bridge > 0
tile.z = 0
else
priority = tile.priority
tile.z = (priority == 0) ? 0 : (y * SOURCE_TILE_HEIGHT) + (priority * SOURCE_TILE_HEIGHT) + SOURCE_TILE_HEIGHT + 1
end
end
def refresh_tile(tile, x, y, map, layer, tile_id)
refresh_tile_bitmap(tile, map, tile_id)
refresh_tile_coordinates(tile, x, y)
refresh_tile_z(tile, map, y, layer, tile_id)
tile.need_refresh = false
end
#-----------------------------------------------------------------------------
def check_if_screen_moved
ret = false
# Check for map change
if @current_map_id != $game_map.map_id
if MapFactoryHelper.hasConnections?(@current_map_id)
offsets = $map_factory.getRelativePos(@current_map_id, 0, 0, $game_map.map_id, 0, 0)
if offsets
@tile_offset_x -= offsets[0]
@tile_offset_y -= offsets[1]
else
ret = true # Need a full refresh
end
else
ret = true
end
@current_map_id = $game_map.map_id
end
# Check for tile movement
current_map_display_x = ($game_map.display_x.to_f / Game_Map::X_SUBPIXELS).round
current_map_display_y = ($game_map.display_y.to_f / Game_Map::Y_SUBPIXELS).round
new_tile_offset_x = (current_map_display_x / SOURCE_TILE_WIDTH) * ZOOM_X
new_tile_offset_y = (current_map_display_y / SOURCE_TILE_HEIGHT) * ZOOM_Y
if new_tile_offset_x != @tile_offset_x
if new_tile_offset_x > @tile_offset_x
# Take tile stacks off the right and insert them at the beginning (left)
(new_tile_offset_x - @tile_offset_x).times do
c = @tiles.shift
@tiles.push(c)
c.each do |coord|
coord.each { |tile| tile.need_refresh = true }
end
end
else
# Take tile stacks off the beginning (left) and push them onto the end (right)
(@tile_offset_x - new_tile_offset_x).times do
c = @tiles.pop
@tiles.prepend(c)
c.each do |coord|
coord.each { |tile| tile.need_refresh = true }
end
end
end
@screen_moved = true
@tile_offset_x = new_tile_offset_x
end
if new_tile_offset_y != @tile_offset_y
if new_tile_offset_y > @tile_offset_y
# Take tile stacks off the bottom and insert them at the beginning (top)
@tiles.each do |col|
(new_tile_offset_y - @tile_offset_y).times do
c = col.shift
col.push(c)
c.each { |tile| tile.need_refresh = true }
end
end
else
# Take tile stacks off the beginning (top) and push them onto the end (bottom)
@tiles.each do |col|
(@tile_offset_y - new_tile_offset_y).times do
c = col.pop
col.prepend(c)
c.each { |tile| tile.need_refresh = true }
end
end
end
@screen_moved = true
@screen_moved_vertically = true
@tile_offset_y = new_tile_offset_y
end
# Check for pixel movement
new_pixel_offset_x = (current_map_display_x % SOURCE_TILE_WIDTH) * ZOOM_X
new_pixel_offset_y = (current_map_display_y % SOURCE_TILE_HEIGHT) * ZOOM_Y
if new_pixel_offset_x != @pixel_offset_x
@screen_moved = true
@pixel_offset_x = new_pixel_offset_x
end
if new_pixel_offset_y != @pixel_offset_y
@screen_moved = true
@screen_moved_vertically = true
@pixel_offset_y = new_pixel_offset_y
end
return ret
end
#-----------------------------------------------------------------------------
def update
# Update tone
if @old_tone != @tone
@tiles.each do |col|
col.each do |coord|
coord.each { |tile| tile.tone = @tone }
end
end
@old_tone = @tone.clone
end
# Update color
if @old_color != @color
@tiles.each do |col|
col.each do |coord|
coord.each { |tile| tile.color = @color }
end
end
@old_color = @color.clone
end
# Recalculate autotile frames
@tilesets.update
@autotiles.update
do_full_refresh = @need_refresh
if @viewport.ox != @old_viewport_ox || @viewport.oy != @old_viewport_oy
@old_viewport_ox = @viewport.ox
@old_viewport_oy = @viewport.oy
do_full_refresh = true
end
# Check whether the screen has moved since the last update
@screen_moved = false
@screen_moved_vertically = false
if $PokemonGlobal.bridge != @bridge
@bridge = $PokemonGlobal.bridge
@screen_moved_vertically = true # To update bridge tiles' z values
end
do_full_refresh = true if check_if_screen_moved
# Update all tile sprites
visited = []
@tiles_horizontal_count.times do |i|
visited[i] = []
@tiles_vertical_count.times { |j| visited[i][j] = false }
end
$map_factory.maps.each do |map|
# Calculate x/y ranges of tile sprites that represent them
map_display_x = (map.display_x.to_f / Game_Map::X_SUBPIXELS).round
map_display_x = ((map_display_x + (Graphics.width / 2)) * ZOOM_X) - (Graphics.width / 2) if ZOOM_X != 1
map_display_y = (map.display_y.to_f / Game_Map::Y_SUBPIXELS).round
map_display_y = ((map_display_y + (Graphics.height / 2)) * ZOOM_Y) - (Graphics.height / 2) if ZOOM_Y != 1
map_display_x_tile = map_display_x / DISPLAY_TILE_WIDTH
map_display_y_tile = map_display_y / DISPLAY_TILE_HEIGHT
start_x = [-map_display_x_tile, 0].max
start_y = [-map_display_y_tile, 0].max
end_x = @tiles_horizontal_count - 1
end_x = [end_x, map.width - map_display_x_tile - 1].min
end_y = @tiles_vertical_count - 1
end_y = [end_y, map.height - map_display_y_tile - 1].min
next if start_x > end_x || start_y > end_y || end_x < 0 || end_y < 0
# Update all tile sprites representing this map
(start_x..end_x).each do |i|
tile_x = i + map_display_x_tile
(start_y..end_y).each do |j|
tile_y = j + map_display_y_tile
@tiles[i][j].each_with_index do |tile, layer|
tile_id = map.data[tile_x, tile_y, layer]
if do_full_refresh || tile.need_refresh || tile.tile_id != tile_id
refresh_tile(tile, i, j, map, layer, tile_id)
else
refresh_tile_frame(tile, tile_id) if tile.animated && @autotiles.changed
# Update tile's x/y coordinates
refresh_tile_coordinates(tile, i, j) if @screen_moved
# Update tile's z value
refresh_tile_z(tile, map, j, layer, tile_id) if @screen_moved_vertically
end
end
# Record x/y as visited
visited[i][j] = true
end
end
end
# Clear all unvisited tile sprites
@tiles.each_with_index do |col, i|
col.each_with_index do |coord, j|
next if visited[i][j]
coord.each do |tile|
tile.set_bitmap("", 0, false, false, 0, nil)
tile.shows_reflection = false
tile.bridge = false
end
end
end
@need_refresh = false
@autotiles.changed = false
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,97 +0,0 @@
#===============================================================================
# This module is a little fix that works around PC hardware limitations. Since
# Essentials isn't working with software rendering anymore, it now has to deal
# with the limits of the GPU. For the most part this is no big deal, but people
# do have some really big tilesets.
#
# The fix is simple enough: If your tileset is too big, a new bitmap will be
# constructed with all the excess pixels sent to the image's right side. This
# basically means that you now have a limit far higher than you should ever
# actually need.
#
# Hardware limit -> max tileset length:
# 1024px -> 4096px
# 2048px -> 16384px (enough to get the normal limit)
# 4096px -> 65536px (enough to load pretty much any tileset)
# 8192px -> 262144px
# 16384px -> 1048576px (what most people have at this point)
#===============================================================================
class TilemapRenderer
module TilesetWrapper
TILESET_WIDTH = SOURCE_TILE_WIDTH * TILESET_TILES_PER_ROW
# Looks useless, but covers weird numbers given to mkxp.json or a funky driver
MAX_TEX_SIZE = (Bitmap.max_size / 1024) * 1024
MAX_TEX_SIZE_BOOSTED = (MAX_TEX_SIZE**2) / TILESET_WIDTH
module_function
def wrapTileset(originalbmp)
width = originalbmp.width
height = originalbmp.height
if width == TILESET_WIDTH && originalbmp.mega?
columns = (height / MAX_TEX_SIZE.to_f).ceil
if columns * TILESET_WIDTH > MAX_TEX_SIZE
raise "Tileset is too long!\n\nSIZE: #{originalbmp.height}px\nHARDWARE LIMIT: #{MAX_TEX_SIZE}px\nBOOSTED LIMIT: #{MAX_TEX_SIZE_BOOSTED}px"
end
bmp = Bitmap.new(TILESET_WIDTH * columns, MAX_TEX_SIZE)
remainder = height % MAX_TEX_SIZE
remainder = MAX_TEX_SIZE if remainder == 0
columns.times do |col|
srcrect = Rect.new(0, col * MAX_TEX_SIZE, width, (col + 1 == columns) ? remainder : MAX_TEX_SIZE)
bmp.blt(col * TILESET_WIDTH, 0, originalbmp, srcrect)
end
return bmp
end
return originalbmp
end
def getWrappedRect(src_rect)
ret = Rect.new(0, 0, 0, 0)
col = (src_rect.y / MAX_TEX_SIZE.to_f).floor
ret.x = (col * TILESET_WIDTH) + src_rect.x.clamp(0, TILESET_WIDTH)
ret.y = src_rect.y % MAX_TEX_SIZE
ret.width = src_rect.width.clamp(0, TILESET_WIDTH - src_rect.x)
ret.height = src_rect.height.clamp(0, MAX_TEX_SIZE)
return ret
end
#---------------------------------------------------------------------------
private
def blitWrappedPixels(destX, destY, dest, src, srcrect)
if srcrect.y + srcrect.width < MAX_TEX_SIZE
# Save the processing power
dest.blt(destX, destY, src, srcrect)
return
end
merge = (srcrect.y % MAX_TEX_SIZE) > ((srcrect.y + srcrect.height) % MAX_TEX_SIZE)
srcrect_mod = getWrappedRect(srcrect)
if merge
# FIXME: won't work on heights longer than two columns, but nobody should need
# more than 32k pixels high at once anyway
side = {
:a => MAX_TEX_SIZE - srcrect_mod.y,
:b => srcrect_mod.height - MAX_TEX_SIZE + srcrect_mod.y
}
dest.blt(destX, destY, src, Rect.new(srcrect_mod.x, srcrect_mod.y, srcrect_mod.width, side[:a]))
dest.blt(destX, destY + side[:a], src, Rect.new(srcrect_mod.x + TILESET_WIDTH, 0, srcrect_mod.width, side[:b]))
else
dest.blt(destX, destY, src, srcrect_mod)
end
end
def stretchBlitWrappedPixels(destrect, dest, src, srcrect)
if srcrect.y + srcrect.width < MAX_TEX_SIZE
# Save the processing power
dest.stretch_blt(destrect, src, srcrect)
return
end
# Does a regular blit to a non-megasurface, then stretch_blts that to
# the destination. Yes it is slow
tmp = Bitmap.new(srcrect.width, srcrect.height)
blitWrappedPixels(0, 0, tmp, src, srcrect)
dest.stretch_blt(destrect, tmp, Rect.new(0, 0, srcrect.width, srcrect.height))
end
end
end

View File

@@ -1,75 +0,0 @@
#===============================================================================
#
#===============================================================================
class TilemapRenderer
module AutotileExpander
MAX_TEXTURE_SIZE = (Bitmap.max_size / 1024) * 1024
module_function
# This doesn't allow for cache sizes smaller than 768, but if that applies
# to you, you've got bigger problems.
def expand(bitmap)
return bitmap if bitmap.height == SOURCE_TILE_HEIGHT
expanded_format = (bitmap.height == SOURCE_TILE_HEIGHT * 6)
wrap = false
if MAX_TEXTURE_SIZE < TILES_PER_AUTOTILE * SOURCE_TILE_HEIGHT
wrap = true # Each autotile will occupy two columns instead of one
end
frames_count = [bitmap.width / (3 * SOURCE_TILE_WIDTH), 1].max
new_bitmap = Bitmap.new(frames_count * (wrap ? 2 : 1) * SOURCE_TILE_WIDTH,
TILES_PER_AUTOTILE * SOURCE_TILE_HEIGHT / (wrap ? 2 : 1))
rect = Rect.new(0, 0, SOURCE_TILE_WIDTH / 2, SOURCE_TILE_HEIGHT / 2)
TILES_PER_AUTOTILE.times do |id|
pattern = TileDrawingHelper::AUTOTILE_PATTERNS[id >> 3][id % TILESET_TILES_PER_ROW]
wrap_offset_x = (wrap && id >= TILES_PER_AUTOTILE / 2) ? SOURCE_TILE_WIDTH : 0
wrap_offset_y = (wrap && id >= TILES_PER_AUTOTILE / 2) ? (TILES_PER_AUTOTILE / 2) * SOURCE_TILE_HEIGHT : 0
frames_count.times do |frame|
if expanded_format && [1, 2, 4, 8].include?(id)
dest_x = frame * SOURCE_TILE_WIDTH * (wrap ? 2 : 1)
dest_x += wrap_offset_x
next if dest_x > MAX_TEXTURE_SIZE
dest_y = id * SOURCE_TILE_HEIGHT
dest_y -= wrap_offset_y
next if dest_y > MAX_TEXTURE_SIZE
case id
when 1 # Top left corner
new_bitmap.blt(dest_x, dest_y, bitmap,
Rect.new(frame * SOURCE_TILE_WIDTH * 3, SOURCE_TILE_HEIGHT * 4,
SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT))
when 2 # Top right corner
new_bitmap.blt(dest_x, dest_y, bitmap,
Rect.new(SOURCE_TILE_WIDTH + (frame * SOURCE_TILE_WIDTH * 3), SOURCE_TILE_HEIGHT * 4,
SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT))
when 4 # Bottom right corner
new_bitmap.blt(dest_x, dest_y, bitmap,
Rect.new(SOURCE_TILE_WIDTH + (frame * SOURCE_TILE_WIDTH * 3), SOURCE_TILE_HEIGHT * 5,
SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT))
when 8 # Bottom left corner
new_bitmap.blt(dest_x, dest_y, bitmap,
Rect.new(frame * SOURCE_TILE_WIDTH * 3, SOURCE_TILE_HEIGHT * 5,
SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT))
end
next
end
pattern.each_with_index do |src_chunk, i|
real_src_chunk = src_chunk - 1
dest_x = (i % 2) * SOURCE_TILE_WIDTH / 2
dest_x += frame * SOURCE_TILE_WIDTH * (wrap ? 2 : 1)
dest_x += wrap_offset_x
next if dest_x > MAX_TEXTURE_SIZE
dest_y = (i / 2) * SOURCE_TILE_HEIGHT / 2
dest_y += id * SOURCE_TILE_HEIGHT
dest_y -= wrap_offset_y
next if dest_y > MAX_TEXTURE_SIZE
rect.x = (real_src_chunk % 6) * SOURCE_TILE_WIDTH / 2
rect.x += SOURCE_TILE_WIDTH * 3 * frame
rect.y = (real_src_chunk / 6) * SOURCE_TILE_HEIGHT / 2
new_bitmap.blt(dest_x, dest_y, bitmap, rect)
end
end
end
return new_bitmap
end
end
end

View File

@@ -0,0 +1,228 @@
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)
if tileset.mega?
@tileset = TileWrap::wrapTileset(tileset)
tileset.dispose
@shouldWrap = true
else
@tileset = tileset
@shouldWrap = false
end
@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)
rect = TileWrap::getWrappedRect(rect) if @shouldWrap
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
#===============================================================================
#
#===============================================================================
def createMinimap(mapid)
map=load_data(sprintf("Data/Map%03d.rxdata",mapid)) rescue nil
return BitmapWrapper.new(32,32) if !map
bitmap=BitmapWrapper.new(map.width*4,map.height*4)
black=Color.new(0,0,0)
tilesets=$data_tilesets
tileset=tilesets[map.tileset_id]
return bitmap if !tileset
helper=TileDrawingHelper.fromTileset(tileset)
for y in 0...map.height
for x in 0...map.width
for z in 0..2
id=map.data[x,y,z]
id=0 if !id
helper.bltSmallTile(bitmap,x*4,y*4,4,4,id)
end
end
end
bitmap.fill_rect(0,0,bitmap.width,1,black)
bitmap.fill_rect(0,bitmap.height-1,bitmap.width,1,black)
bitmap.fill_rect(0,0,1,bitmap.height,black)
bitmap.fill_rect(bitmap.width-1,0,1,bitmap.height,black)
return bitmap
end
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
# Unused
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

View File

@@ -1,246 +0,0 @@
#===============================================================================
#
#===============================================================================
class TileDrawingHelper
attr_accessor :tileset
attr_accessor :autotiles
AUTOTILE_PATTERNS = [
[[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
NEIGHBORS_TO_AUTOTILE_INDEX = [
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, layer = nil)
return 0 if x < 0 || x >= data.xsize
return 0 if y < 0 || y >= data.ysize
if layer.nil?
t = data[x, y]
else
t = data[x, y, layer]
end
xp1 = [x + 1, data.xsize - 1].min
yp1 = [y + 1, data.ysize - 1].min
xm1 = [x - 1, 0].max
ym1 = [y - 1, 0].max
i = 0
if layer.nil?
i |= 0x01 if data[ x, ym1] == t # N
i |= 0x02 if data[xp1, ym1] == t # NE
i |= 0x04 if data[xp1, y] == t # E
i |= 0x08 if data[xp1, yp1] == t # SE
i |= 0x10 if data[ x, yp1] == t # S
i |= 0x20 if data[xm1, yp1] == t # SW
i |= 0x40 if data[xm1, y] == t # W
i |= 0x80 if data[xm1, ym1] == t # NW
else
i |= 0x01 if data[ x, ym1, layer] == t # N
i |= 0x02 if data[xp1, ym1, layer] == t # NE
i |= 0x04 if data[xp1, y, layer] == t # E
i |= 0x08 if data[xp1, yp1, layer] == t # SE
i |= 0x10 if data[ x, yp1, layer] == t # S
i |= 0x20 if data[xm1, yp1, layer] == t # SW
i |= 0x40 if data[xm1, y, layer] == t # W
i |= 0x80 if data[xm1, ym1, layer] == t # NW
end
return i
end
def self.fromTileset(tileset)
bmtileset = pbGetTileset(tileset.tileset_name)
bmautotiles = []
7.times do |i|
bmautotiles.push(pbGetAutotile(tileset.autotile_names[i]))
end
return self.new(bmtileset, bmautotiles)
end
#-----------------------------------------------------------------------------
def initialize(tileset, autotiles)
if tileset.mega?
@tileset = TilemapRenderer::TilesetWrapper.wrapTileset(tileset)
tileset.dispose
@shouldWrap = true
else
@tileset = tileset
@shouldWrap = false
end
@autotiles = autotiles
end
def dispose
@tileset&.dispose
@tileset = nil
@autotiles.each_with_index do |autotile, i|
autotile.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 = AUTOTILE_PATTERNS[id >> 3][id & 7]
src = Rect.new(0, 0, 0, 0)
4.times do |i|
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)
rect = TilemapRenderer::TilesetWrapper.getWrappedRect(rect) if @shouldWrap
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
#===============================================================================
#
#===============================================================================
def createMinimap(mapid)
map = load_data(sprintf("Data/Map%03d.rxdata", mapid)) rescue nil
return Bitmap.new(32, 32) if !map
bitmap = Bitmap.new(map.width * 4, map.height * 4)
black = Color.black
tilesets = $data_tilesets
tileset = tilesets[map.tileset_id]
return bitmap if !tileset
helper = TileDrawingHelper.fromTileset(tileset)
map.height.times do |y|
map.width.times do |x|
3.times do |z|
id = map.data[x, y, z]
id = 0 if !id
helper.bltSmallTile(bitmap, x * 4, y * 4, 4, 4, id)
end
end
end
bitmap.fill_rect(0, 0, bitmap.width, 1, black)
bitmap.fill_rect(0, bitmap.height - 1, bitmap.width, 1, black)
bitmap.fill_rect(0, 0, 1, bitmap.height, black)
bitmap.fill_rect(bitmap.width - 1, 0, 1, bitmap.height, black)
return bitmap
end
def bltMinimapAutotile(dstBitmap, x, y, srcBitmap, id)
return if id >= 48 || !srcBitmap || srcBitmap.disposed?
anim = 0
cxTile = 3
cyTile = 3
tiles = TileDrawingHelper::AUTOTILE_PATTERNS[id >> 3][id & 7]
src = Rect.new(0, 0, 0, 0)
4.times do |i|
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
# Unused
def getPassabilityMinimap(mapid)
map = load_data(sprintf("Data/Map%03d.rxdata", mapid))
tileset = $data_tilesets[map.tileset_id]
minimap = AnimatedBitmap.new("Graphics/UI/minimap_tiles")
ret = Bitmap.new(map.width * 6, map.height * 6)
passtable = Table.new(map.width, map.height)
passages = tileset.passages
map.width.times do |i|
map.height.times do |j|
pass = true
[2, 1, 0].each do |z|
if !passable?(passages, map.data[i, j, z])
pass = false
break
end
end
passtable[i, j] = pass ? 1 : 0
end
end
neighbors = TileDrawingHelper::NEIGHBORS_TO_AUTOTILE_INDEX
map.width.times do |i|
map.height.times do |j|
next 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
minimap.dispose
return ret
end

View File

@@ -1,17 +1,13 @@
#===============================================================================
#
#===============================================================================
class Hangup < Exception; end class Hangup < Exception; end
#===============================================================================
#
#===============================================================================
module RPG module RPG
module Cache module Cache
def self.debug def self.debug
t = Time.now t = Time.now
filename = t.strftime("%H %M %S.%L.txt") filename = t.strftime("%H %M %S.%L.txt")
File.open("cache_" + filename, "wb") do |f| File.open("cache_" + filename, "wb") { |f|
@cache.each do |key, value| @cache.each do |key, value|
if !value if !value
f.write("#{key} (nil)\r\n") f.write("#{key} (nil)\r\n")
@@ -21,7 +17,7 @@ module RPG
f.write("#{key} (#{value.refcount}, #{value.width}x#{value.height})\r\n") f.write("#{key} (#{value.refcount}, #{value.width}x#{value.height})\r\n")
end end
end end
end }
end end
def self.setKey(key, obj) def self.setKey(key, obj)
@@ -31,7 +27,7 @@ module RPG
def self.fromCache(i) def self.fromCache(i)
return nil if !@cache.include?(i) return nil if !@cache.include?(i)
obj = @cache[i] obj = @cache[i]
return nil if obj&.disposed? return nil if obj && obj.disposed?
return obj return obj
end end
@@ -71,7 +67,7 @@ module RPG
ret.addRef ret.addRef
else else
ret = BitmapWrapper.new(32 * width, 32 * height) ret = BitmapWrapper.new(32 * width, 32 * height)
x = ((tile_id - 384) % 8) * 32 x = (tile_id - 384) % 8 * 32
y = (((tile_id - 384) / 8) - height + 1) * 32 y = (((tile_id - 384) / 8) - height + 1) * 32
tileset = yield(filename) tileset = yield(filename)
ret.blt(0, 0, tileset, Rect.new(x, y, 32 * width, 32 * height)) ret.blt(0, 0, tileset, Rect.new(x, y, 32 * width, 32 * height))
@@ -90,10 +86,6 @@ module RPG
self.load_bitmap("Graphics/Transitions/", filename) self.load_bitmap("Graphics/Transitions/", filename)
end end
def self.ui(filename)
self.load_bitmap("Graphics/UI/", filename)
end
def self.retain(folder_name, filename = "", hue = 0) def self.retain(folder_name, filename = "", hue = 0)
path = folder_name + filename path = folder_name + filename
ret = fromCache(path) ret = fromCache(path)
@@ -110,9 +102,8 @@ module RPG
end end
end end
#===============================================================================
#
#===============================================================================
class BitmapWrapper < Bitmap class BitmapWrapper < Bitmap
attr_reader :refcount attr_reader :refcount
attr_accessor :never_dispose attr_accessor :never_dispose

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,16 +4,16 @@
class IconWindow < SpriteWindow_Base class IconWindow < SpriteWindow_Base
attr_reader :name attr_reader :name
def initialize(x, y, width, height, viewport = nil) def initialize(x,y,width,height,viewport=nil)
super(x, y, width, height) super(x,y,width,height)
self.viewport = viewport self.viewport=viewport
self.contents = nil self.contents=nil
@name = "" @name=""
@_iconbitmap = nil @_iconbitmap=nil
end end
def dispose def dispose
clearBitmaps clearBitmaps()
super super
end end
@@ -21,14 +21,14 @@ class IconWindow < SpriteWindow_Base
super super
if @_iconbitmap if @_iconbitmap
@_iconbitmap.update @_iconbitmap.update
self.contents = @_iconbitmap.bitmap self.contents=@_iconbitmap.bitmap
end end
end end
def clearBitmaps def clearBitmaps
@_iconbitmap&.dispose @_iconbitmap.dispose if @_iconbitmap
@_iconbitmap = nil @_iconbitmap=nil
self.contents = nil if !self.disposed? self.contents=nil if !self.disposed?
end end
# Sets the icon's filename. Alias for setBitmap. # Sets the icon's filename. Alias for setBitmap.
@@ -37,35 +37,37 @@ class IconWindow < SpriteWindow_Base
end end
# Sets the icon's filename. # Sets the icon's filename.
def setBitmap(file, hue = 0) def setBitmap(file,hue=0)
clearBitmaps clearBitmaps()
@name = file @name=file
return if file.nil? return if file==nil
if file == "" if file!=""
@_iconbitmap = nil @_iconbitmap=AnimatedBitmap.new(file,hue)
else
@_iconbitmap = AnimatedBitmap.new(file, hue)
# for compatibility # for compatibility
self.contents = @_iconbitmap ? @_iconbitmap.bitmap : nil self.contents=@_iconbitmap ? @_iconbitmap.bitmap : nil
else
@_iconbitmap=nil
end end
end end
end end
#=============================================================================== #===============================================================================
# Displays an icon bitmap in a window. Supports animated images. # Displays an icon bitmap in a window. Supports animated images.
# Accepts bitmaps and paths to bitmap files in its constructor. # Accepts bitmaps and paths to bitmap files in its constructor.
#=============================================================================== #===============================================================================
class PictureWindow < SpriteWindow_Base class PictureWindow < SpriteWindow_Base
def initialize(pathOrBitmap) def initialize(pathOrBitmap)
super(0, 0, 32, 32) super(0,0,32,32)
self.viewport = viewport self.viewport=viewport
self.contents = nil self.contents=nil
@_iconbitmap = nil @_iconbitmap=nil
setBitmap(pathOrBitmap) setBitmap(pathOrBitmap)
end end
def dispose def dispose
clearBitmaps clearBitmaps()
super super
end end
@@ -73,46 +75,47 @@ class PictureWindow < SpriteWindow_Base
super super
if @_iconbitmap if @_iconbitmap
if @_iconbitmap.is_a?(Bitmap) if @_iconbitmap.is_a?(Bitmap)
self.contents = @_iconbitmap self.contents=@_iconbitmap
else else
@_iconbitmap.update @_iconbitmap.update
self.contents = @_iconbitmap.bitmap self.contents=@_iconbitmap.bitmap
end end
end end
end end
def clearBitmaps def clearBitmaps
@_iconbitmap&.dispose @_iconbitmap.dispose if @_iconbitmap
@_iconbitmap = nil @_iconbitmap=nil
self.contents = nil if !self.disposed? self.contents=nil if !self.disposed?
end end
# Sets the icon's bitmap or filename. (hue parameter # Sets the icon's bitmap or filename. (hue parameter
# is ignored unless pathOrBitmap is a filename) # is ignored unless pathOrBitmap is a filename)
def setBitmap(pathOrBitmap, hue = 0) def setBitmap(pathOrBitmap,hue=0)
clearBitmaps clearBitmaps()
if pathOrBitmap && pathOrBitmap != "" if pathOrBitmap!=nil && pathOrBitmap!=""
case pathOrBitmap if pathOrBitmap.is_a?(Bitmap)
when Bitmap @_iconbitmap=pathOrBitmap
@_iconbitmap = pathOrBitmap self.contents=@_iconbitmap
self.contents = @_iconbitmap self.width=@_iconbitmap.width+self.borderX
self.width = @_iconbitmap.width + self.borderX self.height=@_iconbitmap.height+self.borderY
self.height = @_iconbitmap.height + self.borderY elsif pathOrBitmap.is_a?(AnimatedBitmap)
when AnimatedBitmap @_iconbitmap=pathOrBitmap
@_iconbitmap = pathOrBitmap self.contents=@_iconbitmap.bitmap
self.contents = @_iconbitmap.bitmap self.width=@_iconbitmap.bitmap.width+self.borderX
self.width = @_iconbitmap.bitmap.width + self.borderX self.height=@_iconbitmap.bitmap.height+self.borderY
self.height = @_iconbitmap.bitmap.height + self.borderY
else else
@_iconbitmap = AnimatedBitmap.new(pathOrBitmap, hue) @_iconbitmap=AnimatedBitmap.new(pathOrBitmap,hue)
self.contents = @_iconbitmap&.bitmap self.contents=@_iconbitmap ? @_iconbitmap.bitmap : nil
self.width = self.borderX + (@_iconbitmap&.bitmap&.width || 32) self.width=@_iconbitmap ? @_iconbitmap.bitmap.width+self.borderX :
self.height = self.borderY + (@_iconbitmap&.bitmap&.height || 32) 32+self.borderX
self.height=@_iconbitmap ? @_iconbitmap.bitmap.height+self.borderY :
32+self.borderY
end end
else else
@_iconbitmap = nil @_iconbitmap=nil
self.width = 32 + self.borderX self.width=32+self.borderX
self.height = 32 + self.borderY self.height=32+self.borderY
end end
end end
end end

View File

@@ -1,371 +0,0 @@
#===============================================================================
# Sprite class that maintains a bitmap of its own.
# This bitmap can't be changed to a different one.
#===============================================================================
class BitmapSprite < Sprite
attr_reader :text_themes
def initialize(width, height, viewport = nil)
super(viewport)
self.bitmap = Bitmap.new(width, height)
@text_themes = {}
@initialized = true
end
def dispose
self.bitmap.dispose if !self.disposed?
super
end
def bitmap=(value)
super(value) if !@initialized
end
#-----------------------------------------------------------------------------
def add_text_theme(id, base_color, shadow_color = nil)
@text_themes[id] = [base_color, shadow_color]
end
# TODO: Replaces def pbDrawTextPositions.
def draw_themed_text(string, text_x, text_y, align = :left, theme = :default, outline = :shadow)
string_size = self.bitmap.text_size(string)
case align
when :right
text_x -= string_size.width
when :center
text_x -= (string_size.width / 2)
end
if !@text_themes[theme]
theme = (@text_themes[:default]) ? :default : @text_themes.keys.first
end
case outline || :shadow
when :shadow
draw_shadowed_text(string, text_x, text_y, theme)
when :outline
draw_outlined_text(string, text_x, text_y, theme)
when :none
draw_plain_text(string, text_x, text_y, theme)
end
end
# TODO: Replaces def pbDrawShadowText.
def draw_shadowed_text(string, text_x, text_y, theme)
return if !@text_themes[theme]
base_color, shadow_color = @text_themes[theme]
string_size = self.bitmap.text_size(string)
string_width = string_size.width + 1
string_height = string_size.height + 1
if shadow_color && shadow_color.alpha > 0
self.bitmap.font.color = shadow_color
self.bitmap.draw_text(text_x + 2, text_y, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x, text_y + 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y + 2, string_width, string_height, string, 0)
end
if base_color && base_color.alpha > 0
self.bitmap.font.color = base_color
self.bitmap.draw_text(text_x, text_y, string_width, string_height, string, 0)
end
end
# TODO: Replaces def pbDrawOutlineText.
def draw_outlined_text(string, text_x, text_y, theme)
return if !@text_themes[theme]
base_color, shadow_color = @text_themes[theme]
string_size = self.bitmap.text_size(string)
string_width = string_size.width + 1
string_height = string_size.height + 1
if shadow_color && shadow_color.alpha > 0
self.bitmap.font.color = shadow_color
self.bitmap.draw_text(text_x - 2, text_y - 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x, text_y - 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y - 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x - 2, text_y, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x - 2, text_y + 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x, text_y + 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y + 2, string_width, string_height, string, 0)
end
if base_color && base_color.alpha > 0
self.bitmap.font.color = base_color
self.bitmap.draw_text(text_x, text_y, string_width, string_height, string, 0)
end
end
# TODO: Replaces def pbDrawPlainText.
def draw_plain_text(string, text_x, text_y, theme)
return if !@text_themes[theme]
base_color = @text_themes[theme][0]
return if !base_color || base_color.alpha == 0
string_size = self.bitmap.text_size(string)
string_width = string_size.width + 1
string_height = string_size.height + 1
self.bitmap.font.color = base_color
self.bitmap.draw_text(text_x, text_y, string_width, string_height, string, 0)
end
#-----------------------------------------------------------------------------
# TODO: Replaces def pbDrawImagePositions.
def draw_image(filename, image_x, image_y, src_x = 0, src_y = 0, src_width = -1, src_height = -1)
src_bitmap = (filename.is_a?(AnimatedBitmap)) ? filename : AnimatedBitmap.new(pbBitmapName(filename))
src_width = (src_width >= 0) ? src_width : src_bitmap.width
src_height = (src_height >= 0) ? src_height : src_bitmap.height
src_rect = Rect.new(src_x, src_y, src_width, src_height)
self.bitmap.blt(image_x, image_y, src_bitmap.bitmap, src_rect)
src_bitmap.dispose if !filename.is_a?(AnimatedBitmap)
end
end
#===============================================================================
#
#===============================================================================
class AnimatedSprite < Sprite
attr_reader :frame
attr_reader :framewidth
attr_reader :frameheight
attr_reader :framecount
attr_reader :animname
# frameskip is in 1/20ths of a second, and is the time between frame changes.
def initializeLong(animname, framecount, framewidth, frameheight, frameskip)
@animname = pbBitmapName(animname)
@time_per_frame = [1, frameskip].max / 20.0
raise _INTL("Frame width is 0") if framewidth == 0
raise _INTL("Frame height is 0") if frameheight == 0
begin
@animbitmap = AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap = Bitmap.new(framewidth, frameheight)
end
if @animbitmap.width % framewidth != 0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame width ({2}) [Bitmap={3}]",
@animbitmap.width, framewidth, animname)
end
if @animbitmap.height % frameheight != 0
raise _INTL("Bitmap's height ({1}) is not a multiple of frame height ({2}) [Bitmap={3}]",
@animbitmap.height, frameheight, animname)
end
@framecount = framecount
@framewidth = framewidth
@frameheight = frameheight
@framesperrow = @animbitmap.width / @framewidth
@playing = false
self.bitmap = @animbitmap
self.src_rect.width = @framewidth
self.src_rect.height = @frameheight
self.frame = 0
end
# Shorter version of AnimatedSprite. All frames are placed on a single row
# of the bitmap, so that the width and height need not be defined beforehand.
# frameskip is in 1/20ths of a second, and is the time between frame changes.
def initializeShort(animname, framecount, frameskip)
@animname = pbBitmapName(animname)
@time_per_frame = [1, frameskip].max / 20.0
begin
@animbitmap = AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap = Bitmap.new(framecount * 4, 32)
end
if @animbitmap.width % framecount != 0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame count ({2}) [Bitmap={3}]",
@animbitmap.width, framewidth, animname)
end
@framecount = framecount
@framewidth = @animbitmap.width / @framecount
@frameheight = @animbitmap.height
@framesperrow = framecount
@playing = false
self.bitmap = @animbitmap
self.src_rect.width = @framewidth
self.src_rect.height = @frameheight
self.frame = 0
end
def initialize(*args)
if args.length == 1
super(args[0][3])
initializeShort(args[0][0], args[0][1], args[0][2])
else
super(args[5])
initializeLong(args[0], args[1], args[2], args[3], args[4])
end
end
def self.create(animname, framecount, frameskip, viewport = nil)
return self.new([animname, framecount, frameskip, viewport])
end
def dispose
return if disposed?
@animbitmap.dispose
@animbitmap = nil
super
end
def playing?
return @playing
end
def frame=(value)
@frame = value
self.src_rect.x = @frame % @framesperrow * @framewidth
self.src_rect.y = @frame / @framesperrow * @frameheight
end
def start
@playing = true
end
alias play start
def stop
@playing = false
end
def update
super
if @playing
new_frame = (System.uptime / @time_per_frame).to_i % self.framecount
self.frame = new_frame if self.frame != new_frame
end
end
end
#===============================================================================
# Displays an icon bitmap in a sprite. Supports animated images.
#===============================================================================
class IconSprite < Sprite
attr_reader :name
def initialize(*args)
case args.length
when 0
super(nil)
self.bitmap = nil
when 1
super(args[0])
self.bitmap = nil
when 2
super(nil)
self.x = args[0]
self.y = args[1]
else
super(args[2])
self.x = args[0]
self.y = args[1]
end
@name = ""
@_iconbitmap = nil
end
def dispose
clearBitmaps
super
end
# Sets the icon's filename. Alias for setBitmap.
def name=(value)
setBitmap(value)
end
# Sets the icon's filename.
def setBitmap(file, hue = 0)
oldrc = self.src_rect
clearBitmaps
@name = file
return if file.nil?
if file == ""
@_iconbitmap = nil
else
@_iconbitmap = AnimatedBitmap.new(file, hue)
# for compatibility
self.bitmap = @_iconbitmap ? @_iconbitmap.bitmap : nil
self.src_rect = oldrc
end
end
def clearBitmaps
@_iconbitmap&.dispose
@_iconbitmap = nil
self.bitmap = nil if !self.disposed?
end
def update
super
return if !@_iconbitmap
@_iconbitmap.update
if self.bitmap != @_iconbitmap.bitmap
oldrc = self.src_rect
self.bitmap = @_iconbitmap.bitmap
self.src_rect = oldrc
end
end
end
#===============================================================================
# Sprite class that stores multiple bitmaps, and displays only one at once.
#===============================================================================
class ChangelingSprite < Sprite
# Key is the mode (a symbol).
# Value is one of:
# filepath
# [filepath, src_x, src_y, src_width, src_height]
BITMAPS = {}
def initialize(x = 0, y = 0, viewport = nil)
super(viewport)
self.x = x
self.y = y
@bitmaps = {}
@changeling_data = {}
@current_bitmap = nil
initialize_changeling_data
end
def initialize_changeling_data
self.class::BITMAPS.each_pair { |mode, data| add_bitmap(mode, data) }
end
def dispose
return if disposed?
@bitmaps.each_value { |bm| bm.dispose }
@bitmaps.clear
super
end
#-----------------------------------------------------------------------------
def add_bitmap(mode, *data)
raise ArgumentError.new(_INTL("wrong number of arguments (given {1}, expected 2 or 6)", data.length + 1)) if ![1, 5].include?(data.length)
filepath = (data[0].is_a?(Array)) ? data[0][0] : data[0]
@bitmaps[filepath] = AnimatedBitmap.new(filepath) if !@bitmaps[filepath]
@changeling_data[mode] = (data[0].is_a?(Array) ? data[0].clone : [data[0]])
end
def change_bitmap(mode)
@current_mode = mode
if @current_mode && @changeling_data[@current_mode]
data = @changeling_data[@current_mode]
@current_bitmap = @bitmaps[data[0]]
self.bitmap = @current_bitmap.bitmap
if data.length > 1
self.src_rect.set(data[1], data[2], data[3], data[4])
else
self.src_rect.set(0, 0, self.bitmap.width, self.bitmap.height)
end
else
@current_bitmap = nil
self.bitmap = nil
end
end
#-----------------------------------------------------------------------------
def update
return if disposed?
@bitmaps.each_value { |bm| bm.update }
self.bitmap = @current_bitmap.bitmap if @current_bitmap
end
end

View File

@@ -0,0 +1,359 @@
#===============================================================================
# SpriteWrapper is a class which wraps (most of) Sprite's properties.
#===============================================================================
class SpriteWrapper
def initialize(viewport=nil)
@sprite = Sprite.new(viewport)
end
def dispose; @sprite.dispose; end
def disposed?; return @sprite.disposed?; end
def viewport; return @sprite.viewport; end
def flash(color,duration); return @sprite.flash(color,duration); end
def update; return @sprite.update; end
def x; @sprite.x; end
def x=(value); @sprite.x = value; end
def y; @sprite.y; end
def y=(value); @sprite.y = value; end
def bitmap; @sprite.bitmap; end
def bitmap=(value); @sprite.bitmap = value; end
def src_rect; @sprite.src_rect; end
def src_rect=(value); @sprite.src_rect = value; end
def visible; @sprite.visible; end
def visible=(value); @sprite.visible = value; end
def z; @sprite.z; end
def z=(value); @sprite.z = value; end
def ox; @sprite.ox; end
def ox=(value); @sprite.ox = value; end
def oy; @sprite.oy; end
def oy=(value); @sprite.oy = value; end
def zoom_x; @sprite.zoom_x; end
def zoom_x=(value); @sprite.zoom_x = value; end
def zoom_y; @sprite.zoom_y; end
def zoom_y=(value); @sprite.zoom_y = value; end
def angle; @sprite.angle; end
def angle=(value); @sprite.angle = value; end
def mirror; @sprite.mirror; end
def mirror=(value); @sprite.mirror = value; end
def bush_depth; @sprite.bush_depth; end
def bush_depth=(value); @sprite.bush_depth = value; end
def opacity; @sprite.opacity; end
def opacity=(value); @sprite.opacity = value; end
def blend_type; @sprite.blend_type; end
def blend_type=(value); @sprite.blend_type = value; end
def color; @sprite.color; end
def color=(value); @sprite.color = value; end
def tone; @sprite.tone; end
def tone=(value); @sprite.tone = value; end
def viewport=(value)
return if self.viewport==value
bitmap = @sprite.bitmap
src_rect = @sprite.src_rect
visible = @sprite.visible
x = @sprite.x
y = @sprite.y
z = @sprite.z
ox = @sprite.ox
oy = @sprite.oy
zoom_x = @sprite.zoom_x
zoom_y = @sprite.zoom_y
angle = @sprite.angle
mirror = @sprite.mirror
bush_depth = @sprite.bush_depth
opacity = @sprite.opacity
blend_type = @sprite.blend_type
color = @sprite.color
tone = @sprite.tone
@sprite.dispose
@sprite = Sprite.new(value)
@sprite.bitmap = bitmap
@sprite.src_rect = src_rect
@sprite.visible = visible
@sprite.x = x
@sprite.y = y
@sprite.z = z
@sprite.ox = ox
@sprite.oy = oy
@sprite.zoom_x = zoom_x
@sprite.zoom_y = zoom_y
@sprite.angle = angle
@sprite.mirror = mirror
@sprite.bush_depth = bush_depth
@sprite.opacity = opacity
@sprite.blend_type = blend_type
@sprite.color = color
@sprite.tone = tone
end
end
#===============================================================================
# Sprite class that maintains a bitmap of its own.
# This bitmap can't be changed to a different one.
#===============================================================================
class BitmapSprite < SpriteWrapper
def initialize(width,height,viewport=nil)
super(viewport)
self.bitmap=Bitmap.new(width,height)
@initialized=true
end
def bitmap=(value)
super(value) if !@initialized
end
def dispose
self.bitmap.dispose if !self.disposed?
super
end
end
#===============================================================================
#
#===============================================================================
class AnimatedSprite < SpriteWrapper
attr_reader :frame
attr_reader :framewidth
attr_reader :frameheight
attr_reader :framecount
attr_reader :animname
def initializeLong(animname,framecount,framewidth,frameheight,frameskip)
@animname=pbBitmapName(animname)
@realframes=0
@frameskip=[1,frameskip].max
@frameskip *= Graphics.frame_rate/20
raise _INTL("Frame width is 0") if framewidth==0
raise _INTL("Frame height is 0") if frameheight==0
begin
@animbitmap=AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap=Bitmap.new(framewidth,frameheight)
end
if @animbitmap.width%framewidth!=0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame width ({2}) [Bitmap={3}]",
@animbitmap.width,framewidth,animname)
end
if @animbitmap.height%frameheight!=0
raise _INTL("Bitmap's height ({1}) is not a multiple of frame height ({2}) [Bitmap={3}]",
@animbitmap.height,frameheight,animname)
end
@framecount=framecount
@framewidth=framewidth
@frameheight=frameheight
@framesperrow=@animbitmap.width/@framewidth
@playing=false
self.bitmap=@animbitmap
self.src_rect.width=@framewidth
self.src_rect.height=@frameheight
self.frame=0
end
# Shorter version of AnimationSprite. All frames are placed on a single row
# of the bitmap, so that the width and height need not be defined beforehand
def initializeShort(animname,framecount,frameskip)
@animname=pbBitmapName(animname)
@realframes=0
@frameskip=[1,frameskip].max
@frameskip *= Graphics.frame_rate/20
begin
@animbitmap=AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap=Bitmap.new(framecount*4,32)
end
if @animbitmap.width%framecount!=0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame count ({2}) [Bitmap={3}]",
@animbitmap.width,framewidth,animname)
end
@framecount=framecount
@framewidth=@animbitmap.width/@framecount
@frameheight=@animbitmap.height
@framesperrow=framecount
@playing=false
self.bitmap=@animbitmap
self.src_rect.width=@framewidth
self.src_rect.height=@frameheight
self.frame=0
end
def initialize(*args)
if args.length==1
super(args[0][3])
initializeShort(args[0][0],args[0][1],args[0][2])
else
super(args[5])
initializeLong(args[0],args[1],args[2],args[3],args[4])
end
end
def self.create(animname,framecount,frameskip,viewport=nil)
return self.new([animname,framecount,frameskip,viewport])
end
def dispose
return if disposed?
@animbitmap.dispose
@animbitmap=nil
super
end
def playing?
return @playing
end
def frame=(value)
@frame=value
@realframes=0
self.src_rect.x=@frame%@framesperrow*@framewidth
self.src_rect.y=@frame/@framesperrow*@frameheight
end
def start
@playing=true
@realframes=0
end
alias play start
def stop
@playing=false
end
def update
super
if @playing
@realframes+=1
if @realframes==@frameskip
@realframes=0
self.frame+=1
self.frame%=self.framecount
end
end
end
end
#===============================================================================
# Displays an icon bitmap in a sprite. Supports animated images.
#===============================================================================
class IconSprite < SpriteWrapper
attr_reader :name
def initialize(*args)
if args.length==0
super(nil)
self.bitmap=nil
elsif args.length==1
super(args[0])
self.bitmap=nil
elsif args.length==2
super(nil)
self.x=args[0]
self.y=args[1]
else
super(args[2])
self.x=args[0]
self.y=args[1]
end
@name=""
@_iconbitmap=nil
end
def dispose
clearBitmaps()
super
end
# Sets the icon's filename. Alias for setBitmap.
def name=(value)
setBitmap(value)
end
# Sets the icon's filename.
def setBitmap(file,hue=0)
oldrc=self.src_rect
clearBitmaps()
@name=file
return if file==nil
if file!=""
@_iconbitmap=AnimatedBitmap.new(file,hue)
# for compatibility
self.bitmap=@_iconbitmap ? @_iconbitmap.bitmap : nil
self.src_rect=oldrc
else
@_iconbitmap=nil
end
end
def clearBitmaps
@_iconbitmap.dispose if @_iconbitmap
@_iconbitmap=nil
self.bitmap=nil if !self.disposed?
end
def update
super
return if !@_iconbitmap
@_iconbitmap.update
if self.bitmap!=@_iconbitmap.bitmap
oldrc=self.src_rect
self.bitmap=@_iconbitmap.bitmap
self.src_rect=oldrc
end
end
end
#===============================================================================
# Old GifSprite class, retained for compatibility
#===============================================================================
class GifSprite < IconSprite
def initialize(path)
super(0,0)
setBitmap(path)
end
end
#===============================================================================
# SpriteWrapper that stores multiple bitmaps, and displays only one at once.
#===============================================================================
class ChangelingSprite < SpriteWrapper
def initialize(x=0,y=0,viewport=nil)
super(viewport)
self.x = x
self.y = y
@bitmaps = {}
@currentBitmap = nil
end
def addBitmap(key,path)
@bitmaps[key].dispose if @bitmaps[key]
@bitmaps[key] = AnimatedBitmap.new(path)
end
def changeBitmap(key)
@currentBitmap = @bitmaps[key]
self.bitmap = (@currentBitmap) ? @currentBitmap.bitmap : nil
end
def dispose
return if disposed?
for bm in @bitmaps.values; bm.dispose; end
@bitmaps.clear
super
end
def update
return if disposed?
for bm in @bitmaps.values; bm.update; end
self.bitmap = (@currentBitmap) ? @currentBitmap.bitmap : nil
end
end

View File

@@ -6,10 +6,10 @@ class AnimatedBitmap
raise "Filename is nil (missing graphic)." if file.nil? raise "Filename is nil (missing graphic)." if file.nil?
path = file path = file
filename = "" filename = ""
if file.last != "/" # Isn't just a directory if file.last != '/' # Isn't just a directory
split_file = file.split(/[\\\/]/) split_file = file.split(/[\\\/]/)
filename = split_file.pop filename = split_file.pop
path = split_file.join("/") + "/" path = split_file.join('/') + '/'
end end
if filename[/^\[\d+(?:,\d+)?\]/] # Starts with 1 or 2 numbers in square brackets if filename[/^\[\d+(?:,\d+)?\]/] # Starts with 1 or 2 numbers in square brackets
@bitmap = PngAnimatedBitmap.new(path, filename, hue) @bitmap = PngAnimatedBitmap.new(path, filename, hue)
@@ -43,22 +43,22 @@ class PngAnimatedBitmap
def initialize(dir, filename, hue = 0) def initialize(dir, filename, hue = 0)
@frames = [] @frames = []
@currentFrame = 0 @currentFrame = 0
@timer_start = System.uptime @framecount = 0
panorama = RPG::Cache.load_bitmap(dir, filename, hue) panorama = RPG::Cache.load_bitmap(dir, filename, hue)
if filename[/^\[(\d+)(?:,(\d+))?\]/] # Starts with 1 or 2 numbers in brackets if filename[/^\[(\d+)(?:,(\d+))?\]/] # Starts with 1 or 2 numbers in brackets
# File has a frame count # File has a frame count
numFrames = $1.to_i numFrames = $1.to_i
duration = $2.to_i # In 1/20ths of a second delay = $2.to_i
duration = 5 if duration == 0 delay = 10 if delay == 0
raise "Invalid frame count in #{filename}" if numFrames <= 0 raise "Invalid frame count in #{filename}" if numFrames <= 0
raise "Invalid frame duration in #{filename}" if duration <= 0 raise "Invalid frame delay in #{filename}" if delay <= 0
if panorama.width % numFrames != 0 if panorama.width % numFrames != 0
raise "Bitmap's width (#{panorama.width}) is not divisible by frame count: #{filename}" raise "Bitmap's width (#{panorama.width}) is not divisible by frame count: #{filename}"
end end
@frame_duration = duration / 20.0 @frameDelay = delay
subWidth = panorama.width / numFrames subWidth = panorama.width / numFrames
numFrames.times do |i| for i in 0...numFrames
subBitmap = Bitmap.new(subWidth, panorama.height) subBitmap = BitmapWrapper.new(subWidth, panorama.height)
subBitmap.blt(0, 0, panorama, Rect.new(subWidth * i, 0, subWidth, panorama.height)) subBitmap.blt(0, 0, panorama, Rect.new(subWidth * i, 0, subWidth, panorama.height))
@frames.push(subBitmap) @frames.push(subBitmap)
end end
@@ -68,16 +68,6 @@ class PngAnimatedBitmap
end end
end end
def dispose
return if @disposed
@frames.each { |f| f.dispose }
@disposed = true
end
def disposed?
return @disposed
end
def [](index) def [](index)
return @frames[index] return @frames[index]
end end
@@ -85,6 +75,15 @@ class PngAnimatedBitmap
def width; self.bitmap.width; end def width; self.bitmap.width; end
def height; self.bitmap.height; end def height; self.bitmap.height; end
def deanimate
for i in 1...@frames.length
@frames[i].dispose
end
@frames = [@frames[0]]
@currentFrame = 0
return @frames[0]
end
def bitmap def bitmap
return @frames[@currentFrame] return @frames[@currentFrame]
end end
@@ -93,42 +92,53 @@ class PngAnimatedBitmap
return @currentFrame return @currentFrame
end end
def length def frameDelay(_index)
return @frames.length return @frameDelay
end end
# Actually returns the total number of 1/20ths of a second this animation lasts. def length
def totalFrames return @frames.length
return (@frame_duration * @frames.length * 20).to_i
end end
def each def each
@frames.each { |item| yield item } @frames.each { |item| yield item }
end end
def deanimate def totalFrames
(1...@frames.length).each do |i| return @frameDelay * @frames.length
@frames[i].dispose
end
@frames = [@frames[0]]
@currentFrame = 0
@frame_duration = 0
return @frames[0]
end end
def copy def disposed?
x = self.clone return @disposed
x.frames = x.frames.clone
x.frames.each_with_index { |frame, i| x.frames[i] = frame.copy }
return x
end end
def update def update
return if disposed? return if disposed?
if @frames.length > 1 if @frames.length > 1
@currentFrame = ((System.uptime - @timer_start) / @frame_duration).to_i % @frames.length @framecount += 1
if @framecount >= @frameDelay
@framecount = 0
@currentFrame += 1
@currentFrame %= @frames.length
end end
end end
end
def dispose
if !@disposed
@frames.each { |f| f.dispose }
end
@disposed = true
end
def copy
x = self.clone
x.frames = x.frames.clone
for i in 0...x.frames.length
x.frames[i] = x.frames[i].copy
end
return x
end
end end
#=============================================================================== #===============================================================================
@@ -147,7 +157,7 @@ class GifBitmap
rescue rescue
@bitmap = nil @bitmap = nil
end end
@bitmap = Bitmap.new(32, 32) if @bitmap.nil? @bitmap = BitmapWrapper.new(32, 32) if @bitmap.nil?
@bitmap.play if @bitmap&.animated? @bitmap.play if @bitmap&.animated?
end end
@@ -210,19 +220,19 @@ end
# #
#=============================================================================== #===============================================================================
def pbGetTileBitmap(filename, tile_id, hue, width = 1, height = 1) def pbGetTileBitmap(filename, tile_id, hue, width = 1, height = 1)
return RPG::Cache.tileEx(filename, tile_id, hue, width, height) do |f| return RPG::Cache.tileEx(filename, tile_id, hue, width, height) { |f|
AnimatedBitmap.new("Graphics/Tilesets/" + filename).deanimate AnimatedBitmap.new("Graphics/Tilesets/" + filename).deanimate
end }
end end
def pbGetTileset(name, hue = 0) def pbGetTileset(name,hue=0)
return AnimatedBitmap.new("Graphics/Tilesets/" + name, hue).deanimate return AnimatedBitmap.new("Graphics/Tilesets/" + name, hue).deanimate
end end
def pbGetAutotile(name, hue = 0) def pbGetAutotile(name,hue=0)
return AnimatedBitmap.new("Graphics/Autotiles/" + name, hue).deanimate return AnimatedBitmap.new("Graphics/Autotiles/" + name, hue).deanimate
end end
def pbGetAnimation(name, hue = 0) def pbGetAnimation(name,hue=0)
return AnimatedBitmap.new("Graphics/Animations/" + name, hue).deanimate return AnimatedBitmap.new("Graphics/Animations/" + name, hue).deanimate
end end

View File

@@ -6,71 +6,225 @@ class Plane
def refresh; end def refresh; end
end end
#=============================================================================== #===============================================================================
# A plane class that displays a single color. # This class works around a limitation that planes are always
# 640 by 480 pixels in size regardless of the window's size.
#=============================================================================== #===============================================================================
class ColoredPlane < Plane class LargePlane < Plane
def initialize(color, viewport = nil) attr_accessor :borderX
super(viewport) attr_accessor :borderY
self.bitmap = Bitmap.new(32, 32)
set_plane_color(color) def initialize(viewport=nil)
@__sprite=Sprite.new(viewport)
@__disposed=false
@__ox=0
@__oy=0
@__bitmap=nil
@__visible=true
@__sprite.visible=false
@borderX=0
@borderY=0
end
def disposed?
return @__disposed
end end
def dispose def dispose
self.bitmap&.dispose if !@__disposed
@__sprite.bitmap.dispose if @__sprite.bitmap
@__sprite.dispose
@__sprite=nil
@__bitmap=nil
@__disposed=true
end
#super
end
def ox; @__ox; end
def oy; @__oy; end
def ox=(value);
return if @__ox==value
@__ox = value
refresh
end
def oy=(value);
return if @__oy==value
@__oy = value
refresh
end
def bitmap
return @__bitmap
end
def bitmap=(value)
if value==nil
if @__bitmap!=nil
@__bitmap=nil
@__sprite.visible=(@__visible && !@__bitmap.nil?)
end
elsif @__bitmap!=value && !value.disposed?
@__bitmap=value
refresh
elsif value.disposed?
if @__bitmap!=nil
@__bitmap=nil
@__sprite.visible=(@__visible && !@__bitmap.nil?)
end
end
end
def viewport; @__sprite.viewport; end
def zoom_x; @__sprite.zoom_x; end
def zoom_y; @__sprite.zoom_y; end
def opacity; @__sprite.opacity; end
def blend_type; @__sprite.blend_type; end
def visible; @__visible; end
def z; @__sprite.z; end
def color; @__sprite.color; end
def tone; @__sprite.tone; end
def zoom_x=(v);
return if @__sprite.zoom_x==v
@__sprite.zoom_x = v
refresh
end
def zoom_y=(v);
return if @__sprite.zoom_y==v
@__sprite.zoom_y = v
refresh
end
def opacity=(v); @__sprite.opacity=(v); end
def blend_type=(v); @__sprite.blend_type=(v); end
def visible=(v); @__visible=v; @__sprite.visible=(@__visible && !@__bitmap.nil?); end
def z=(v); @__sprite.z=(v); end
def color=(v); @__sprite.color=(v); end
def tone=(v); @__sprite.tone=(v); end
def update; ;end
def refresh
@__sprite.visible = (@__visible && !@__bitmap.nil?)
if @__bitmap
if !@__bitmap.disposed?
@__ox += @__bitmap.width*@__sprite.zoom_x if @__ox<0
@__oy += @__bitmap.height*@__sprite.zoom_y if @__oy<0
@__ox -= @__bitmap.width*@__sprite.zoom_x if @__ox>@__bitmap.width
@__oy -= @__bitmap.height*@__sprite.zoom_y if @__oy>@__bitmap.height
dwidth = (Graphics.width/@__sprite.zoom_x+@borderX).to_i # +2
dheight = (Graphics.height/@__sprite.zoom_y+@borderY).to_i # +2
@__sprite.bitmap = ensureBitmap(@__sprite.bitmap,dwidth,dheight)
@__sprite.bitmap.clear
tileBitmap(@__sprite.bitmap,@__bitmap,@__bitmap.rect)
else
@__sprite.visible = false
end
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,srcbitmap,srcrect)
return if !srcbitmap || srcbitmap.disposed?
dstrect = dstbitmap.rect
left = (dstrect.x-@__ox/@__sprite.zoom_x).to_i
top = (dstrect.y-@__oy/@__sprite.zoom_y).to_i
while left>0; left -= srcbitmap.width; end
while top>0; top -= srcbitmap.height; end
y = top
while y<dstrect.height
x = left
while x<dstrect.width
dstbitmap.blt(x+@borderX,y+@borderY,srcbitmap,srcrect)
x += srcrect.width
end
y += srcrect.height
end
end
end
#===============================================================================
# A plane class that displays a single color.
#===============================================================================
class ColoredPlane < LargePlane
def initialize(color,viewport=nil)
super(viewport)
self.bitmap=Bitmap.new(32,32)
setPlaneColor(color)
end
def dispose
self.bitmap.dispose if self.bitmap
super super
end end
def set_plane_color(value) def setPlaneColor(value)
self.bitmap.fill_rect(0, 0, self.bitmap.width, self.bitmap.height, value) self.bitmap.fill_rect(0,0,self.bitmap.width,self.bitmap.height,value)
refresh self.refresh
end end
end end
#=============================================================================== #===============================================================================
# A plane class that supports animated images. # A plane class that supports animated images.
#=============================================================================== #===============================================================================
class AnimatedPlane < Plane class AnimatedPlane < LargePlane
def initialize(viewport) def initialize(viewport)
super(viewport) super(viewport)
@bitmap = nil @bitmap=nil
end end
def dispose def dispose
clear_bitmap clearBitmaps()
super super
end end
def setBitmap(file, hue = 0) def update
clear_bitmap super
return if file.nil? if @bitmap
@bitmap = AnimatedBitmap.new(file, hue) @bitmap.update
self.bitmap = @bitmap.bitmap if @bitmap self.bitmap=@bitmap.bitmap
end
def set_panorama(file, hue = 0)
if file.is_a?(String) && file.length > 0
setBitmap("Graphics/Panoramas/" + file, hue)
else
clear_bitmap
end end
end end
def set_fog(file, hue = 0) def clearBitmaps
if file.is_a?(String) && file.length > 0 @bitmap.dispose if @bitmap
setBitmap("Graphics/Fogs/" + file, hue) @bitmap=nil
else self.bitmap=nil if !self.disposed?
clear_bitmap
end
end end
#----------------------------------------------------------------------------- def setPanorama(file, hue=0)
clearBitmaps()
return if file==nil
@bitmap=AnimatedBitmap.new("Graphics/Panoramas/"+file,hue)
end
private def setFog(file, hue=0)
clearBitmaps()
return if file==nil
@bitmap=AnimatedBitmap.new("Graphics/Fogs/"+file,hue)
end
def clear_bitmap def setBitmap(file, hue=0)
@bitmap&.dispose clearBitmaps()
@bitmap = nil return if file==nil
self.bitmap = nil if !self.disposed? @bitmap=AnimatedBitmap.new(file,hue)
end end
end end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,28 +2,32 @@
# #
#=============================================================================== #===============================================================================
class CharacterEntryHelper class CharacterEntryHelper
attr_accessor :text attr_reader :text
attr_accessor :maxlength attr_accessor :maxlength
attr_reader :passwordChar attr_reader :passwordChar
attr_accessor :cursor attr_accessor :cursor
def initialize(text) def initialize(text)
@maxlength = -1 @maxlength=-1
@text = text @text=text
@passwordChar = "" @passwordChar=""
@cursor = text.scan(/./m).length @cursor=text.scan(/./m).length
end
def text=(value)
@text=value
end end
def textChars def textChars
chars = text.scan(/./m) chars=text.scan(/./m)
if @passwordChar != "" if @passwordChar!=""
chars.length.times { |i| chars[i] = @passwordChar } chars.length.times { |i| chars[i] = @passwordChar }
end end
return chars return chars
end end
def passwordChar=(value) def passwordChar=(value)
@passwordChar = value || "" @passwordChar=value ? value : ""
end end
def length def length
@@ -31,70 +35,75 @@ class CharacterEntryHelper
end end
def canInsert? def canInsert?
chars = self.text.scan(/./m) chars=self.text.scan(/./m)
return false if @maxlength >= 0 && chars.length >= @maxlength return false if @maxlength>=0 && chars.length>=@maxlength
return true return true
end end
def insert(ch) def insert(ch)
chars = self.text.scan(/./m) chars=self.text.scan(/./m)
return false if @maxlength >= 0 && chars.length >= @maxlength return false if @maxlength>=0 && chars.length>=@maxlength
chars.insert(@cursor, ch) chars.insert(@cursor,ch)
@text = "" @text=""
chars.each { |char| @text += char if char } for ch in chars
@cursor += 1 @text+=ch if ch
end
@cursor+=1
return true return true
end end
def canDelete? def canDelete?
chars = self.text.scan(/./m) chars=self.text.scan(/./m)
return false if chars.length <= 0 || @cursor <= 0 return false if chars.length<=0 || @cursor<=0
return true return true
end end
def delete def delete
chars = self.text.scan(/./m) chars=self.text.scan(/./m)
return false if chars.length <= 0 || @cursor <= 0 return false if chars.length<=0 || @cursor<=0
chars.delete_at(@cursor - 1) chars.delete_at(@cursor-1)
@text = "" @text=""
chars.each do |ch| for ch in chars
@text += ch if ch @text+=ch if ch
end end
@cursor -= 1 @cursor-=1
return true return true
end end
#-----------------------------------------------------------------------------
private private
def ensure def ensure
return if @maxlength < 0 return if @maxlength<0
chars = self.text.scan(/./m) chars=self.text.scan(/./m)
chars = chars[0, @maxlength] if chars.length > @maxlength && @maxlength >= 0 if chars.length>@maxlength && @maxlength>=0
@text = "" chars=chars[0,@maxlength]
chars.each do |ch| end
@text += ch if ch @text=""
for ch in chars
@text+=ch if ch
end end
end end
end end
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
class Window_TextEntry < SpriteWindow_Base class Window_TextEntry < SpriteWindow_Base
def initialize(text, x, y, width, height, heading = nil, usedarkercolor = false) def initialize(text,x,y,width,height,heading=nil,usedarkercolor=false)
super(x, y, width, height) super(x,y,width,height)
@baseColor, @shadowColor = getDefaultTextColors(self.windowskin) colors=getDefaultTextColors(self.windowskin)
@baseColor=colors[0]
@shadowColor=colors[1]
if usedarkercolor if usedarkercolor
@baseColor = Color.new(16, 24, 32) @baseColor=Color.new(16,24,32)
@shadowColor = Color.new(168, 184, 184) @shadowColor=Color.new(168,184,184)
end end
@helper = CharacterEntryHelper.new(text) @helper=CharacterEntryHelper.new(text)
@heading = heading @heading=heading
@cursor_timer_start = System.uptime self.active=true
@cursor_shown = true @frame=0
self.active = true
refresh refresh
end end
@@ -111,24 +120,23 @@ class Window_TextEntry < SpriteWindow_Base
end end
def text=(value) def text=(value)
@helper.text = value @helper.text=value
self.refresh self.refresh
end end
def passwordChar=(value) def passwordChar=(value)
@helper.passwordChar = value @helper.passwordChar=value
refresh refresh
end end
def maxlength=(value) def maxlength=(value)
@helper.maxlength = value @helper.maxlength=value
self.refresh self.refresh
end end
def insert(ch) def insert(ch)
if @helper.insert(ch) if @helper.insert(ch)
@cursor_timer_start = System.uptime @frame=0
@cursor_shown = true
self.refresh self.refresh
return true return true
end end
@@ -137,8 +145,7 @@ class Window_TextEntry < SpriteWindow_Base
def delete def delete
if @helper.delete if @helper.delete
@cursor_timer_start = System.uptime @frame=0
@cursor_shown = true
self.refresh self.refresh
return true return true
end end
@@ -146,25 +153,21 @@ class Window_TextEntry < SpriteWindow_Base
end end
def update def update
cursor_to_show = ((System.uptime - @cursor_timer_start) / 0.35).to_i.even? @frame += 1
if cursor_to_show != @cursor_shown @frame %= 20
@cursor_shown = cursor_to_show self.refresh if (@frame%10)==0
refresh
end
return if !self.active return if !self.active
# Moving cursor # Moving cursor
if Input.repeat?(Input::LEFT) && Input.press?(Input::ACTION) if Input.repeat?(Input::LEFT) && Input.press?(Input::ACTION)
if @helper.cursor > 0 if @helper.cursor > 0
@helper.cursor -= 1 @helper.cursor -= 1
@cursor_timer_start = System.uptime @frame = 0
@cursor_shown = true
self.refresh self.refresh
end end
elsif Input.repeat?(Input::RIGHT) && Input.press?(Input::ACTION) elsif Input.repeat?(Input::RIGHT) && Input.press?(Input::ACTION)
if @helper.cursor < self.text.scan(/./m).length if @helper.cursor < self.text.scan(/./m).length
@helper.cursor += 1 @helper.cursor += 1
@cursor_timer_start = System.uptime @frame = 0
@cursor_shown = true
self.refresh self.refresh
end end
elsif Input.repeat?(Input::BACK) # Backspace elsif Input.repeat?(Input::BACK) # Backspace
@@ -173,81 +176,79 @@ class Window_TextEntry < SpriteWindow_Base
end end
def refresh def refresh
self.contents = pbDoEnsureBitmap(self.contents, self.width - self.borderX, self.contents=pbDoEnsureBitmap(self.contents,self.width-self.borderX,
self.height - self.borderY) self.height-self.borderY)
bitmap = self.contents bitmap=self.contents
bitmap.clear bitmap.clear
x = 0 x=0
y = 0 y=0
if @heading if @heading
textwidth = bitmap.text_size(@heading).width textwidth=bitmap.text_size(@heading).width
pbDrawShadowText(bitmap, x, y, textwidth + 4, 32, @heading, @baseColor, @shadowColor) pbDrawShadowText(bitmap,x,y, textwidth+4, 32, @heading,@baseColor,@shadowColor)
y += 32 y+=32
end end
x += 4 x+=4
width = self.width - self.borderX width=self.width-self.borderX
cursorcolor = Color.new(16, 24, 32) cursorcolor=Color.new(16,24,32)
textscan = self.text.scan(/./m) textscan=self.text.scan(/./m)
scanlength = textscan.length scanlength=textscan.length
@helper.cursor = scanlength if @helper.cursor > scanlength @helper.cursor=scanlength if @helper.cursor>scanlength
@helper.cursor = 0 if @helper.cursor < 0 @helper.cursor=0 if @helper.cursor<0
startpos = @helper.cursor startpos=@helper.cursor
fromcursor = 0 fromcursor=0
while startpos > 0 while (startpos>0)
c = (@helper.passwordChar != "") ? @helper.passwordChar : textscan[startpos - 1] c=(@helper.passwordChar!="") ? @helper.passwordChar : textscan[startpos-1]
fromcursor += bitmap.text_size(c).width fromcursor+=bitmap.text_size(c).width
break if fromcursor > width - 4 break if fromcursor>width-4
startpos -= 1 startpos-=1
end end
(startpos...scanlength).each do |i| for i in startpos...scanlength
c = (@helper.passwordChar != "") ? @helper.passwordChar : textscan[i] c=(@helper.passwordChar!="") ? @helper.passwordChar : textscan[i]
textwidth = bitmap.text_size(c).width textwidth=bitmap.text_size(c).width
next if c == "\n" next if c=="\n"
# Draw text # Draw text
pbDrawShadowText(bitmap, x, y, textwidth + 4, 32, c, @baseColor, @shadowColor) pbDrawShadowText(bitmap,x,y, textwidth+4, 32, c,@baseColor,@shadowColor)
# Draw cursor if necessary # Draw cursor if necessary
if i == @helper.cursor && @cursor_shown if ((@frame/10)&1) == 0 && i==@helper.cursor
bitmap.fill_rect(x, y + 4, 2, 24, cursorcolor) bitmap.fill_rect(x,y+4,2,24,cursorcolor)
end end
# Add x to drawn text width # Add x to drawn text width
x += textwidth x += textwidth
end end
if textscan.length == @helper.cursor && @cursor_shown if ((@frame/10)&1) == 0 && textscan.length==@helper.cursor
bitmap.fill_rect(x, y + 4, 2, 24, cursorcolor) bitmap.fill_rect(x,y+4,2,24,cursorcolor)
end end
end end
end end
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
class Window_TextEntry_Keyboard < Window_TextEntry class Window_TextEntry_Keyboard < Window_TextEntry
def update def update
cursor_to_show = ((System.uptime - @cursor_timer_start) / 0.35).to_i.even? @frame+=1
if cursor_to_show != @cursor_shown @frame%=20
@cursor_shown = cursor_to_show self.refresh if ((@frame%10)==0)
refresh
end
return if !self.active return if !self.active
# Moving cursor # Moving cursor
if Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT) if Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT)
if @helper.cursor > 0 if @helper.cursor > 0
@helper.cursor -= 1 @helper.cursor-=1
@cursor_timer_start = System.uptime @frame=0
@cursor_shown = true
self.refresh self.refresh
end end
return return
elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT) elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT)
if @helper.cursor < self.text.scan(/./m).length if @helper.cursor < self.text.scan(/./m).length
@helper.cursor += 1 @helper.cursor+=1
@cursor_timer_start = System.uptime @frame=0
@cursor_shown = true
self.refresh self.refresh
end end
return return
elsif Input.triggerex?(:BACKSPACE) || Input.repeatex?(:BACKSPACE) elsif Input.triggerex?(:BACKSPACE) || Input.repeatex?(:BACKSPACE)
self.delete if @helper.cursor > 0 self.delete if @helper.cursor>0
return return
elsif Input.triggerex?(:RETURN) || Input.triggerex?(:ESCAPE) elsif Input.triggerex?(:RETURN) || Input.triggerex?(:ESCAPE)
return return
@@ -256,20 +257,23 @@ class Window_TextEntry_Keyboard < Window_TextEntry
end end
end end
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
class Window_MultilineTextEntry < SpriteWindow_Base class Window_MultilineTextEntry < SpriteWindow_Base
def initialize(text, x, y, width, height) def initialize(text,x,y,width,height)
super(x, y, width, height) super(x,y,width,height)
@baseColor, @shadowColor = getDefaultTextColors(self.windowskin) colors=getDefaultTextColors(self.windowskin)
@helper = CharacterEntryHelper.new(text) @baseColor=colors[0]
@firstline = 0 @shadowColor=colors[1]
@cursorLine = 0 @helper=CharacterEntryHelper.new(text)
@cursorColumn = 0 @firstline=0
@cursor_timer_start = System.uptime @cursorLine=0
@cursor_shown = true @cursorColumn=0
self.active = true @frame=0
self.active=true
refresh refresh
end end
@@ -277,12 +281,12 @@ class Window_MultilineTextEntry < SpriteWindow_Base
attr_reader :shadowColor attr_reader :shadowColor
def baseColor=(value) def baseColor=(value)
@baseColor = value @baseColor=value
refresh refresh
end end
def shadowColor=(value) def shadowColor=(value)
@shadowColor = value @shadowColor=value
refresh refresh
end end
@@ -295,24 +299,23 @@ class Window_MultilineTextEntry < SpriteWindow_Base
end end
def text=(value) def text=(value)
@helper.text = value @helper.text=value
@textchars = nil @textchars=nil
self.refresh self.refresh
end end
def maxlength=(value) def maxlength=(value)
@helper.maxlength = value @helper.maxlength=value
@textchars = nil @textchars=nil
self.refresh self.refresh
end end
def insert(ch) def insert(ch)
@helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn) @helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn)
if @helper.insert(ch) if @helper.insert(ch)
@cursor_timer_start = System.uptime @frame=0
@cursor_shown = true @textchars=nil
@textchars = nil moveCursor(0,1)
moveCursor(0, 1)
self.refresh self.refresh
return true return true
end end
@@ -320,12 +323,11 @@ class Window_MultilineTextEntry < SpriteWindow_Base
end end
def delete def delete
@helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn) @helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn)
if @helper.delete if @helper.delete
@cursor_timer_start = System.uptime @frame=0
@cursor_shown = true moveCursor(0,-1) # use old textchars
moveCursor(0, -1) # use old textchars @textchars=nil
@textchars = nil
self.refresh self.refresh
return true return true
end end
@@ -334,155 +336,163 @@ class Window_MultilineTextEntry < SpriteWindow_Base
def getTextChars def getTextChars
if !@textchars if !@textchars
@textchars = getLineBrokenText(self.contents, @helper.text, @textchars=getLineBrokenText(self.contents,@helper.text,
self.contents.width, nil) self.contents.width,nil)
end end
return @textchars return @textchars
end end
def getTotalLines def getTotalLines
textchars = getTextChars textchars=getTextChars
return 1 if textchars.length == 0 return 1 if textchars.length==0
tchar = textchars[textchars.length - 1] tchar=textchars[textchars.length-1]
return tchar[5] + 1 return tchar[5]+1
end end
def getLineY(line) def getLineY(line)
textchars = getTextChars textchars=getTextChars
return 0 if textchars.length == 0 return 0 if textchars.length==0
totallines = getTotalLines totallines=getTotalLines()
line = 0 if line < 0 line=0 if line<0
line = totallines - 1 if line >= totallines line=totallines-1 if line>=totallines
maximumY = 0 maximumY=0
textchars.each do |text| for i in 0...textchars.length
thisline = text[5] thisline=textchars[i][5]
y = text[2] y=textchars[i][2]
return y if thisline == line return y if thisline==line
maximumY = y if maximumY < y maximumY=y if maximumY<y
end end
return maximumY return maximumY
end end
def getColumnsInLine(line) def getColumnsInLine(line)
textchars = getTextChars textchars=getTextChars
return 0 if textchars.length == 0 return 0 if textchars.length==0
totallines = getTotalLines totallines=getTotalLines()
line = 0 if line < 0 line=0 if line<0
line = totallines - 1 if line >= totallines line=totallines-1 if line>=totallines
endpos = 0 endpos=0
textchars.each do |text| for i in 0...textchars.length
thisline = text[5] thisline=textchars[i][5]
thislength = text[8] thislength=textchars[i][8]
endpos += thislength if thisline == line endpos+=thislength if thisline==line
end end
return endpos return endpos
end end
def getPosFromLineAndColumn(line, column) def getPosFromLineAndColumn(line,column)
textchars = getTextChars textchars=getTextChars
return 0 if textchars.length == 0 return 0 if textchars.length==0
totallines = getTotalLines totallines=getTotalLines()
line = 0 if line < 0 line=0 if line<0
line = totallines - 1 if line >= totallines line=totallines-1 if line>=totallines
endpos = 0 endpos=0
textchars.each do |text| for i in 0...textchars.length
thisline = text[5] thisline=textchars[i][5]
thispos = text[6] thispos=textchars[i][6]
thiscolumn = text[7] thiscolumn=textchars[i][7]
thislength = text[8] thislength=textchars[i][8]
next if thisline != line if thisline==line
endpos = thispos + thislength endpos=thispos+thislength
next if column < thiscolumn || column > thiscolumn + thislength || thislength == 0 # echoln [endpos,thispos+(column-thiscolumn),textchars[i]]
return thispos + column - thiscolumn if column>=thiscolumn && column<=thiscolumn+thislength && thislength>0
return thispos+(column-thiscolumn)
end end
end
end
# if endpos==0
# echoln [totallines,line,column]
# echoln textchars
# end
# echoln "endpos=#{endpos}"
return endpos return endpos
end end
def getLastVisibleLine def getLastVisibleLine
getTextChars getTextChars()
textheight = [1, self.contents.text_size("X").height].max textheight=[1,self.contents.text_size("X").height].max
lastVisible = @firstline + ((self.height - self.borderY) / textheight) - 1 lastVisible=@firstline+((self.height-self.borderY)/textheight)-1
return lastVisible return lastVisible
end end
def updateCursorPos(doRefresh) def updateCursorPos(doRefresh)
# Calculate new cursor position # Calculate new cursor position
@helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn) @helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn)
if doRefresh if doRefresh
@cursor_timer_start = System.uptime @frame=0
@cursor_shown = true
self.refresh self.refresh
end end
@firstline = @cursorLine if @cursorLine < @firstline @firstline=@cursorLine if @cursorLine<@firstline
lastVisible = getLastVisibleLine lastVisible=getLastVisibleLine()
@firstline += (@cursorLine - lastVisible) if @cursorLine > lastVisible @firstline+=(@cursorLine-lastVisible) if @cursorLine>lastVisible
end end
def moveCursor(lineOffset, columnOffset) def moveCursor(lineOffset, columnOffset)
# Move column offset first, then lines (since column offset # Move column offset first, then lines (since column offset
# can affect line offset) # can affect line offset)
# echoln ["beforemoving",@cursorLine,@cursorColumn] # echoln ["beforemoving",@cursorLine,@cursorColumn]
totalColumns = getColumnsInLine(@cursorLine) # check current line totalColumns=getColumnsInLine(@cursorLine) # check current line
totalLines = getTotalLines totalLines=getTotalLines()
oldCursorLine = @cursorLine oldCursorLine=@cursorLine
oldCursorColumn = @cursorColumn oldCursorColumn=@cursorColumn
@cursorColumn += columnOffset @cursorColumn+=columnOffset
if @cursorColumn < 0 && @cursorLine > 0 if @cursorColumn<0 && @cursorLine>0
# Will happen if cursor is moved left from the beginning of a line # Will happen if cursor is moved left from the beginning of a line
@cursorLine -= 1 @cursorLine-=1
@cursorColumn = getColumnsInLine(@cursorLine) @cursorColumn=getColumnsInLine(@cursorLine)
elsif @cursorColumn > totalColumns && @cursorLine < totalLines - 1 elsif @cursorColumn>totalColumns && @cursorLine<totalLines-1
# Will happen if cursor is moved right from the end of a line # Will happen if cursor is moved right from the end of a line
@cursorLine += 1 @cursorLine+=1
@cursorColumn = 0 @cursorColumn=0
end end
# Ensure column bounds # Ensure column bounds
totalColumns = getColumnsInLine(@cursorLine) totalColumns=getColumnsInLine(@cursorLine)
@cursorColumn = totalColumns if @cursorColumn > totalColumns @cursorColumn=totalColumns if @cursorColumn>totalColumns
@cursorColumn = 0 if @cursorColumn < 0 # totalColumns can be 0 @cursorColumn=0 if @cursorColumn<0 # totalColumns can be 0
# Move line offset # Move line offset
@cursorLine += lineOffset @cursorLine+=lineOffset
@cursorLine = 0 if @cursorLine < 0 @cursorLine=0 if @cursorLine<0
@cursorLine = totalLines - 1 if @cursorLine >= totalLines @cursorLine=totalLines-1 if @cursorLine>=totalLines
# Ensure column bounds again # Ensure column bounds again
totalColumns = getColumnsInLine(@cursorLine) totalColumns=getColumnsInLine(@cursorLine)
@cursorColumn = totalColumns if @cursorColumn > totalColumns @cursorColumn=totalColumns if @cursorColumn>totalColumns
@cursorColumn = 0 if @cursorColumn < 0 # totalColumns can be 0 @cursorColumn=0 if @cursorColumn<0 # totalColumns can be 0
updateCursorPos(oldCursorLine != @cursorLine || oldCursorColumn != @cursorColumn) updateCursorPos(
oldCursorLine!=@cursorLine ||
oldCursorColumn!=@cursorColumn
)
# echoln ["aftermoving",@cursorLine,@cursorColumn] # echoln ["aftermoving",@cursorLine,@cursorColumn]
end end
def update def update
cursor_to_show = ((System.uptime - @cursor_timer_start) / 0.35).to_i.even? @frame+=1
if cursor_to_show != @cursor_shown @frame%=20
@cursor_shown = cursor_to_show self.refresh if ((@frame%10)==0)
refresh
end
return if !self.active return if !self.active
# Moving cursor # Moving cursor
if Input.triggerex?(:UP) || Input.repeatex?(:UP) if Input.triggerex?(:UP) || Input.repeatex?(:UP)
moveCursor(-1, 0) moveCursor(-1,0)
return return
elsif Input.triggerex?(:DOWN) || Input.repeatex?(:DOWN) elsif Input.triggerex?(:DOWN) || Input.repeatex?(:DOWN)
moveCursor(1, 0) moveCursor(1,0)
return return
elsif Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT) elsif Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT)
moveCursor(0, -1) moveCursor(0,-1)
return return
elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT) elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT)
moveCursor(0, 1) moveCursor(0,1)
return return
end end
if Input.press?(Input::CTRL) && Input.triggerex?(:HOME) if Input.press?(Input::CTRL) && Input.triggerex?(:HOME)
# Move cursor to beginning # Move cursor to beginning
@cursorLine = 0 @cursorLine=0
@cursorColumn = 0 @cursorColumn=0
updateCursorPos(true) updateCursorPos(true)
return return
elsif Input.press?(Input::CTRL) && Input.triggerex?(:END) elsif Input.press?(Input::CTRL) && Input.triggerex?(:END)
# Move cursor to end # Move cursor to end
@cursorLine = getTotalLines - 1 @cursorLine=getTotalLines()-1
@cursorColumn = getColumnsInLine(@cursorLine) @cursorColumn=getColumnsInLine(@cursorLine)
updateCursorPos(true) updateCursorPos(true)
return return
elsif Input.triggerex?(:RETURN) || Input.repeatex?(:RETURN) elsif Input.triggerex?(:RETURN) || Input.repeatex?(:RETURN)
@@ -492,61 +502,63 @@ class Window_MultilineTextEntry < SpriteWindow_Base
self.delete self.delete
return return
end end
Input.gets.each_char { |c| insert(c) } Input.gets.each_char{|c|insert(c)}
end end
def refresh def refresh
newContents = pbDoEnsureBitmap(self.contents, self.width - self.borderX, newContents=pbDoEnsureBitmap(self.contents,self.width-self.borderX,
self.height - self.borderY) self.height-self.borderY)
@textchars = nil if self.contents != newContents @textchars=nil if self.contents!=newContents
self.contents = newContents self.contents=newContents
bitmap = self.contents bitmap=self.contents
bitmap.clear bitmap.clear
getTextChars getTextChars
height = self.height - self.borderY height=self.height-self.borderY
cursorcolor = Color.black cursorcolor=Color.new(0,0,0)
textchars = getTextChars textchars=getTextChars()
startY = getLineY(@firstline) startY=getLineY(@firstline)
textchars.each do |text| for i in 0...textchars.length
thisline = text[5] thisline=textchars[i][5]
thislength = text[8] thiscolumn=textchars[i][7]
textY = text[2] - startY thislength=textchars[i][8]
textY=textchars[i][2]-startY
# Don't draw lines before the first or zero-length segments # Don't draw lines before the first or zero-length segments
next if thisline < @firstline || thislength == 0 next if thisline<@firstline || thislength==0
# Don't draw lines beyond the window's height # Don't draw lines beyond the window's height
break if textY >= height break if textY >= height
c = text[0] c=textchars[i][0]
# Don't draw spaces # Don't draw spaces
next if c == " " next if c==" "
textwidth = text[3] + 4 # add 4 to prevent draw_text from stretching text textwidth=textchars[i][3]+4 # add 4 to prevent draw_text from stretching text
textheight = text[4] textheight=textchars[i][4]
# Draw text # Draw text
pbDrawShadowText(bitmap, text[1], textY, textwidth, textheight, c, @baseColor, @shadowColor) pbDrawShadowText(bitmap, textchars[i][1], textY, textwidth, textheight, c, @baseColor, @shadowColor)
end end
# Draw cursor # Draw cursor
if @cursor_shown if ((@frame/10)&1) == 0
textheight = bitmap.text_size("X").height textheight=bitmap.text_size("X").height
cursorY = (textheight * @cursorLine) - startY cursorY=(textheight*@cursorLine)-startY
cursorX = 0 cursorX=0
textchars.each do |text| for i in 0...textchars.length
thisline = text[5] thisline=textchars[i][5]
thiscolumn = text[7] thiscolumn=textchars[i][7]
thislength = text[8] thislength=textchars[i][8]
next if thisline != @cursorLine || @cursorColumn < thiscolumn || if thisline==@cursorLine && @cursorColumn>=thiscolumn &&
@cursorColumn > thiscolumn + thislength @cursorColumn<=thiscolumn+thislength
cursorY = text[2] - startY cursorY=textchars[i][2]-startY
cursorX = text[1] cursorX=textchars[i][1]
textheight = text[4] textheight=textchars[i][4]
posToCursor = @cursorColumn - thiscolumn posToCursor=@cursorColumn-thiscolumn
if posToCursor >= 0 if posToCursor>=0
partialString = text[0].scan(/./m)[0, posToCursor].join partialString=textchars[i][0].scan(/./m)[0,posToCursor].join("")
cursorX += bitmap.text_size(partialString).width cursorX+=bitmap.text_size(partialString).width
end end
break break
end end
cursorY += 4 end
cursorHeight = [4, textheight - 4, bitmap.text_size("X").height - 4].max cursorY+=4
bitmap.fill_rect(cursorX, cursorY, 2, cursorHeight, cursorcolor) cursorHeight=[4,textheight-4,bitmap.text_size("X").height-4].max
bitmap.fill_rect(cursorX,cursorY,2,cursorHeight,cursorcolor)
end end
end end
end end

View File

@@ -1,9 +1,25 @@
#####################################
# 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
#=============================================================================== #===============================================================================
# Methods that determine the duration of an audio file. # Methods that determine the duration of an audio file.
#=============================================================================== #===============================================================================
def getOggPage(file) def getOggPage(file)
fgetdw = proc { |f| fgetdw = proc { |file|
(f.eof? ? 0 : (f.read(4).unpack("V")[0] || 0)) (file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0))
} }
dw = fgetdw.call(file) dw = fgetdw.call(file)
return nil if dw != 0x5367674F return nil if dw != 0x5367674F
@@ -19,8 +35,8 @@ end
# internal function # internal function
def oggfiletime(file) def oggfiletime(file)
fgetdw = proc { |f| fgetdw = proc { |file|
(f.eof? ? 0 : (f.read(4).unpack("V")[0] || 0)) (file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0))
} }
pages = [] pages = []
page = nil page = nil
@@ -35,8 +51,8 @@ def oggfiletime(file)
i = -1 i = -1
pcmlengths = [] pcmlengths = []
rates = [] rates = []
pages.each do |pg| for page in pages
header = pg[0] header = page[0]
serial = header[10, 4].unpack("V") serial = header[10, 4].unpack("V")
frame = header[2, 8].unpack("C*") frame = header[2, 8].unpack("C*")
frameno = frame[7] frameno = frame[7]
@@ -49,7 +65,7 @@ def oggfiletime(file)
frameno = (frameno << 8) | frame[0] frameno = (frameno << 8) | frame[0]
if serial != curserial if serial != curserial
curserial = serial curserial = serial
file.pos = pg[1] file.pos = page[1]
packtype = (file.read(1)[0].ord rescue 0) packtype = (file.read(1)[0].ord rescue 0)
string = file.read(6) string = file.read(6)
return -1 if string != "vorbis" return -1 if string != "vorbis"
@@ -62,26 +78,28 @@ def oggfiletime(file)
pcmlengths[i] = frameno pcmlengths[i] = frameno
end end
ret = 0.0 ret = 0.0
pcmlengths.each_with_index { |length, j| ret += length.to_f / rates[j] } for i in 0...pcmlengths.length
ret += pcmlengths[i].to_f / rates[i].to_f
end
return ret * 256.0 return ret * 256.0
end end
# Gets the length of an audio file in seconds. Supports WAV, MP3, and OGG files. # Gets the length of an audio file in seconds. Supports WAV, MP3, and OGG files.
def getPlayTime(filename) def getPlayTime(filename)
if FileTest.exist?(filename) if safeExists?(filename)
return [getPlayTime2(filename), 0].max return [getPlayTime2(filename), 0].max
elsif FileTest.exist?(filename + ".wav") elsif safeExists?(filename + ".wav")
return [getPlayTime2(filename + ".wav"), 0].max return [getPlayTime2(filename + ".wav"), 0].max
elsif FileTest.exist?(filename + ".mp3") elsif safeExists?(filename + ".mp3")
return [getPlayTime2(filename + ".mp3"), 0].max return [getPlayTime2(filename + ".mp3"), 0].max
elsif FileTest.exist?(filename + ".ogg") elsif safeExists?(filename + ".ogg")
return [getPlayTime2(filename + ".ogg"), 0].max return [getPlayTime2(filename + ".ogg"), 0].max
end end
return 0 return 0
end end
def getPlayTime2(filename) def getPlayTime2(filename)
return -1 if !FileTest.exist?(filename) return -1 if !safeExists?(filename)
time = -1 time = -1
fgetdw = proc { |file| fgetdw = proc { |file|
(file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0)) (file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0))
@@ -89,40 +107,39 @@ def getPlayTime2(filename)
fgetw = proc { |file| fgetw = proc { |file|
(file.eof? ? 0 : (file.read(2).unpack("v")[0] || 0)) (file.eof? ? 0 : (file.read(2).unpack("v")[0] || 0))
} }
File.open(filename, "rb") do |file| File.open(filename, "rb") { |file|
file.pos = 0 file.pos = 0
fdw = fgetdw.call(file) fdw = fgetdw.call(file)
case fdw if fdw == 0x46464952 # "RIFF"
when 0x46464952 # "RIFF"
filesize = fgetdw.call(file) filesize = fgetdw.call(file)
wave = fgetdw.call(file) wave = fgetdw.call(file)
return -1 if wave != 0x45564157 # "WAVE" return -1 if wave != 0x45564157 # "WAVE"
fmt = fgetdw.call(file) fmt = fgetdw.call(file)
return -1 if fmt != 0x20746d66 # "fmt " return -1 if fmt != 0x20746d66 # "fmt "
fgetdw.call(file) # fmtsize fmtsize = fgetdw.call(file)
fgetw.call(file) # format format = fgetw.call(file)
fgetw.call(file) # channels channels = fgetw.call(file)
fgetdw.call(file) # rate rate = fgetdw.call(file)
bytessec = fgetdw.call(file) bytessec = fgetdw.call(file)
return -1 if bytessec == 0 return -1 if bytessec == 0
fgetw.call(file) # bytessample bytessample = fgetw.call(file)
fgetw.call(file) # bitssample bitssample = fgetw.call(file)
data = fgetdw.call(file) data = fgetdw.call(file)
return -1 if data != 0x61746164 # "data" return -1 if data != 0x61746164 # "data"
datasize = fgetdw.call(file) datasize = fgetdw.call(file)
time = datasize.to_f / bytessec time = (datasize*1.0)/bytessec
return time return time
when 0x5367674F # "OggS" elsif fdw == 0x5367674F # "OggS"
file.pos = 0 file.pos = 0
time = oggfiletime(file) time = oggfiletime(file)
return time return time
end end
file.pos = 0 file.pos = 0
# Find the length of an MP3 file # Find the length of an MP3 file
loop do while true
rstr = "" rstr = ""
ateof = false ateof = false
until file.eof? while !file.eof?
if (file.read(1)[0] rescue 0) == 0xFF if (file.read(1)[0] rescue 0) == 0xFF
begin begin
rstr = file.read(3) rstr = file.read(3)
@@ -135,20 +152,20 @@ def getPlayTime2(filename)
break if ateof || !rstr || rstr.length != 3 break if ateof || !rstr || rstr.length != 3
if rstr[0] == 0xFB if rstr[0] == 0xFB
t = rstr[1] >> 4 t = rstr[1] >> 4
next if [0, 15].include?(t) next if t == 0 || t == 15
freqs = [44_100, 22_050, 11_025, 48_000] freqs = [44100, 22050, 11025, 48000]
bitrates = [32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320] bitrates = [32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320]
bitrate = bitrates[t] bitrate = bitrates[t]
t = (rstr[1] >> 2) & 3 t = (rstr[1] >> 2) & 3
freq = freqs[t] freq = freqs[t]
t = (rstr[1] >> 1) & 1 t = (rstr[1] >> 1) & 1
filesize = FileTest.size(filename) filesize = FileTest.size(filename)
frameLength = ((144_000 * bitrate) / freq) + t frameLength = ((144000 * bitrate) / freq) + t
numFrames = filesize / (frameLength + 4) numFrames = filesize / (frameLength + 4)
time = (numFrames * 1152.0 / freq) time = (numFrames * 1152.0 / freq)
break break
end end
end end
end }
return time return time
end end

View File

@@ -1,18 +1,15 @@
#===============================================================================
#
#===============================================================================
def pbStringToAudioFile(str) def pbStringToAudioFile(str)
if str[/^(.*)\:\s*(\d+)\s*\:\s*(\d+)\s*$/] # Of the format "XXX: ###: ###" if str[/^(.*)\:\s*(\d+)\s*\:\s*(\d+)\s*$/] # Of the format "XXX: ###: ###"
file = $1 file = $1
volume = $2.to_i volume = $2.to_i
pitch = $3.to_i pitch = $3.to_i
return RPG::AudioFile.new(file, volume, pitch) return RPG::AudioFile.new(file,volume,pitch)
elsif str[/^(.*)\:\s*(\d+)\s*$/] # Of the format "XXX: ###" elsif str[/^(.*)\:\s*(\d+)\s*$/] # Of the format "XXX: ###"
file = $1 file = $1
volume = $2.to_i volume = $2.to_i
return RPG::AudioFile.new(file, volume, 100) return RPG::AudioFile.new(file,volume,100)
else else
return RPG::AudioFile.new(str, 100, 100) return RPG::AudioFile.new(str,100,100)
end end
end end
@@ -24,7 +21,7 @@ end
# filename:volume:pitch # filename:volume:pitch
# volume -- Volume of the file, up to 100 # volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100 # pitch -- Pitch of the file, normally 100
def pbResolveAudioFile(str, volume = nil, pitch = nil) def pbResolveAudioFile(str,volume=nil,pitch=nil)
if str.is_a?(String) if str.is_a?(String)
str = pbStringToAudioFile(str) str = pbStringToAudioFile(str)
str.volume = volume || 100 str.volume = volume || 100
@@ -32,7 +29,7 @@ def pbResolveAudioFile(str, volume = nil, pitch = nil)
end end
if str.is_a?(RPG::AudioFile) if str.is_a?(RPG::AudioFile)
if volume || pitch if volume || pitch
return RPG::AudioFile.new(str.name, volume || str.volume || 100, return RPG::AudioFile.new(str.name,volume || str.volume || 100 ,
pitch || str.pitch || 100) pitch || str.pitch || 100)
else else
return str return str
@@ -41,9 +38,8 @@ def pbResolveAudioFile(str, volume = nil, pitch = nil)
return str return str
end end
#=============================================================================== ################################################################################
#
#===============================================================================
# Plays a BGM file. # Plays a BGM file.
# param -- Either a string showing the filename # param -- Either a string showing the filename
# (relative to Audio/BGM/) or an RPG::AudioFile object. # (relative to Audio/BGM/) or an RPG::AudioFile object.
@@ -53,48 +49,47 @@ end
# filename:volume:pitch # filename:volume:pitch
# volume -- Volume of the file, up to 100 # volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100 # pitch -- Pitch of the file, normally 100
def pbBGMPlay(param, volume = nil, pitch = nil) def pbBGMPlay(param,volume=nil,pitch=nil)
return if !param return if !param
param = pbResolveAudioFile(param, volume, pitch) param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name != "" if param.name && param.name!=""
if $game_system if $game_system && $game_system.respond_to?("bgm_play")
$game_system.bgm_play(param) $game_system.bgm_play(param)
return return
elsif (RPG.const_defined?(:BGM) rescue false) elsif (RPG.const_defined?(:BGM) rescue false)
b = RPG::BGM.new(param.name, param.volume, param.pitch) b=RPG::BGM.new(param.name,param.volume,param.pitch)
if b.respond_to?("play") if b && b.respond_to?("play")
b.play b.play
return return
end end
end end
Audio.bgm_play(canonicalize("Audio/BGM/" + param.name), param.volume, param.pitch) Audio.bgm_play(canonicalize("Audio/BGM/"+param.name),param.volume,param.pitch)
end end
end end
# Fades out or stops BGM playback. 'x' is the time in seconds to fade out. # Fades out or stops BGM playback. 'x' is the time in seconds to fade out.
def pbBGMFade(x = 0.0); pbBGMStop(x); end def pbBGMFade(x=0.0); pbBGMStop(x);end
# Fades out or stops BGM playback. 'x' is the time in seconds to fade out. # Fades out or stops BGM playback. 'x' is the time in seconds to fade out.
def pbBGMStop(timeInSeconds = 0.0) def pbBGMStop(timeInSeconds=0.0)
if $game_system && timeInSeconds > 0.0 if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("bgm_fade")
$game_system.bgm_fade(timeInSeconds) $game_system.bgm_fade(timeInSeconds)
return return
elsif $game_system elsif $game_system && $game_system.respond_to?("bgm_stop")
$game_system.bgm_stop $game_system.bgm_stop
return return
elsif (RPG.const_defined?(:BGM) rescue false) elsif (RPG.const_defined?(:BGM) rescue false)
begin begin
(timeInSeconds > 0.0) ? RPG::BGM.fade((timeInSeconds * 1000).floor) : RPG::BGM.stop (timeInSeconds>0.0) ? RPG::BGM.fade((timeInSeconds*1000).floor) : RPG::BGM.stop
return return
rescue rescue
end end
end end
(timeInSeconds > 0.0) ? Audio.bgm_fade((timeInSeconds * 1000).floor) : Audio.bgm_stop (timeInSeconds>0.0) ? Audio.bgm_fade((timeInSeconds*1000).floor) : Audio.bgm_stop
end end
#=============================================================================== ################################################################################
#
#===============================================================================
# Plays an ME file. # Plays an ME file.
# param -- Either a string showing the filename # param -- Either a string showing the filename
# (relative to Audio/ME/) or an RPG::AudioFile object. # (relative to Audio/ME/) or an RPG::AudioFile object.
@@ -104,48 +99,46 @@ end
# filename:volume:pitch # filename:volume:pitch
# volume -- Volume of the file, up to 100 # volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100 # pitch -- Pitch of the file, normally 100
def pbMEPlay(param, volume = nil, pitch = nil) def pbMEPlay(param,volume=nil,pitch=nil)
return if !param return if !param
param = pbResolveAudioFile(param, volume, pitch) param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name != "" if param.name && param.name!=""
if $game_system if $game_system && $game_system.respond_to?("me_play")
$game_system.me_play(param) $game_system.me_play(param)
return return
elsif (RPG.const_defined?(:ME) rescue false) elsif (RPG.const_defined?(:ME) rescue false)
b = RPG::ME.new(param.name, param.volume, param.pitch) b=RPG::ME.new(param.name,param.volume,param.pitch)
if b.respond_to?("play") if b && b.respond_to?("play")
b.play b.play; return
return
end end
end end
Audio.me_play(canonicalize("Audio/ME/" + param.name), param.volume, param.pitch) Audio.me_play(canonicalize("Audio/ME/"+param.name),param.volume,param.pitch)
end end
end end
# Fades out or stops ME playback. 'x' is the time in seconds to fade out. # Fades out or stops ME playback. 'x' is the time in seconds to fade out.
def pbMEFade(x = 0.0); pbMEStop(x); end def pbMEFade(x=0.0); pbMEStop(x);end
# Fades out or stops ME playback. 'x' is the time in seconds to fade out. # Fades out or stops ME playback. 'x' is the time in seconds to fade out.
def pbMEStop(timeInSeconds = 0.0) def pbMEStop(timeInSeconds=0.0)
if $game_system && timeInSeconds > 0.0 && $game_system.respond_to?("me_fade") if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("me_fade")
$game_system.me_fade(timeInSeconds) $game_system.me_fade(timeInSeconds)
return return
elsif $game_system.respond_to?("me_stop") elsif $game_system && $game_system.respond_to?("me_stop")
$game_system.me_stop(nil) $game_system.me_stop(nil)
return return
elsif (RPG.const_defined?(:ME) rescue false) elsif (RPG.const_defined?(:ME) rescue false)
begin begin
(timeInSeconds > 0.0) ? RPG::ME.fade((timeInSeconds * 1000).floor) : RPG::ME.stop (timeInSeconds>0.0) ? RPG::ME.fade((timeInSeconds*1000).floor) : RPG::ME.stop
return return
rescue rescue
end end
end end
(timeInSeconds > 0.0) ? Audio.me_fade((timeInSeconds * 1000).floor) : Audio.me_stop (timeInSeconds>0.0) ? Audio.me_fade((timeInSeconds*1000).floor) : Audio.me_stop
end end
#=============================================================================== ################################################################################
#
#===============================================================================
# Plays a BGS file. # Plays a BGS file.
# param -- Either a string showing the filename # param -- Either a string showing the filename
# (relative to Audio/BGS/) or an RPG::AudioFile object. # (relative to Audio/BGS/) or an RPG::AudioFile object.
@@ -155,48 +148,46 @@ end
# filename:volume:pitch # filename:volume:pitch
# volume -- Volume of the file, up to 100 # volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100 # pitch -- Pitch of the file, normally 100
def pbBGSPlay(param, volume = nil, pitch = nil) def pbBGSPlay(param,volume=nil,pitch=nil)
return if !param return if !param
param = pbResolveAudioFile(param, volume, pitch) param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name != "" if param.name && param.name!=""
if $game_system if $game_system && $game_system.respond_to?("bgs_play")
$game_system.bgs_play(param) $game_system.bgs_play(param)
return return
elsif (RPG.const_defined?(:BGS) rescue false) elsif (RPG.const_defined?(:BGS) rescue false)
b = RPG::BGS.new(param.name, param.volume, param.pitch) b=RPG::BGS.new(param.name,param.volume,param.pitch)
if b.respond_to?("play") if b && b.respond_to?("play")
b.play b.play; return
return
end end
end end
Audio.bgs_play(canonicalize("Audio/BGS/" + param.name), param.volume, param.pitch) Audio.bgs_play(canonicalize("Audio/BGS/"+param.name),param.volume,param.pitch)
end end
end end
# Fades out or stops BGS playback. 'x' is the time in seconds to fade out. # Fades out or stops BGS playback. 'x' is the time in seconds to fade out.
def pbBGSFade(x = 0.0); pbBGSStop(x); end def pbBGSFade(x=0.0); pbBGSStop(x);end
# Fades out or stops BGS playback. 'x' is the time in seconds to fade out. # Fades out or stops BGS playback. 'x' is the time in seconds to fade out.
def pbBGSStop(timeInSeconds = 0.0) def pbBGSStop(timeInSeconds=0.0)
if $game_system && timeInSeconds > 0.0 if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("bgs_fade")
$game_system.bgs_fade(timeInSeconds) $game_system.bgs_fade(timeInSeconds)
return return
elsif $game_system elsif $game_system && $game_system.respond_to?("bgs_play")
$game_system.bgs_play(nil) $game_system.bgs_play(nil)
return return
elsif (RPG.const_defined?(:BGS) rescue false) elsif (RPG.const_defined?(:BGS) rescue false)
begin begin
(timeInSeconds > 0.0) ? RPG::BGS.fade((timeInSeconds * 1000).floor) : RPG::BGS.stop (timeInSeconds>0.0) ? RPG::BGS.fade((timeInSeconds*1000).floor) : RPG::BGS.stop
return return
rescue rescue
end end
end end
(timeInSeconds > 0.0) ? Audio.bgs_fade((timeInSeconds * 1000).floor) : Audio.bgs_stop (timeInSeconds>0.0) ? Audio.bgs_fade((timeInSeconds*1000).floor) : Audio.bgs_stop
end end
#=============================================================================== ################################################################################
#
#===============================================================================
# Plays an SE file. # Plays an SE file.
# param -- Either a string showing the filename # param -- Either a string showing the filename
# (relative to Audio/SE/) or an RPG::AudioFile object. # (relative to Audio/SE/) or an RPG::AudioFile object.
@@ -206,30 +197,30 @@ end
# filename:volume:pitch # filename:volume:pitch
# volume -- Volume of the file, up to 100 # volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100 # pitch -- Pitch of the file, normally 100
def pbSEPlay(param, volume = nil, pitch = nil) def pbSEPlay(param,volume=nil,pitch=nil)
return if !param return if !param
param = pbResolveAudioFile(param, volume, pitch) param = pbResolveAudioFile(param,volume,pitch)
if param.name && param.name != "" if param.name && param.name!=""
if $game_system if $game_system && $game_system.respond_to?("se_play")
$game_system.se_play(param) $game_system.se_play(param)
return return
end end
if (RPG.const_defined?(:SE) rescue false) if (RPG.const_defined?(:SE) rescue false)
b = RPG::SE.new(param.name, param.volume, param.pitch) b = RPG::SE.new(param.name,param.volume,param.pitch)
if b.respond_to?("play") if b && b.respond_to?("play")
b.play b.play
return return
end end
end end
Audio.se_play(canonicalize("Audio/SE/" + param.name), param.volume, param.pitch) Audio.se_play(canonicalize("Audio/SE/"+param.name),param.volume,param.pitch)
end end
end end
# Stops SE playback. # Stops SE playback.
def pbSEFade(x = 0.0); pbSEStop(x); end def pbSEFade(x=0.0); pbSEStop(x);end
# Stops SE playback. # Stops SE playback.
def pbSEStop(_timeInSeconds = 0.0) def pbSEStop(_timeInSeconds=0.0)
if $game_system if $game_system
$game_system.se_stop $game_system.se_stop
elsif (RPG.const_defined?(:SE) rescue false) elsif (RPG.const_defined?(:SE) rescue false)
@@ -239,45 +230,63 @@ def pbSEStop(_timeInSeconds = 0.0)
end end
end end
################################################################################
# Plays a sound effect that plays when the player moves the cursor. # Plays a sound effect that plays when the player moves the cursor.
def pbPlayCursorSE def pbPlayCursorSE
if !nil_or_empty?($data_system&.cursor_se&.name) if $data_system && $data_system.respond_to?("cursor_se") &&
$data_system.cursor_se && $data_system.cursor_se.name!=""
pbSEPlay($data_system.cursor_se) 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") elsif FileTest.audio_exist?("Audio/SE/GUI sel cursor")
pbSEPlay("GUI sel cursor", 80) pbSEPlay("GUI sel cursor",80)
end end
end end
# Plays a sound effect that plays when a decision is confirmed or a choice is made. # Plays a sound effect that plays when a decision is confirmed or a choice is made.
def pbPlayDecisionSE def pbPlayDecisionSE
if !nil_or_empty?($data_system&.decision_se&.name) if $data_system && $data_system.respond_to?("decision_se") &&
$data_system.decision_se && $data_system.decision_se.name!=""
pbSEPlay($data_system.decision_se) 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") elsif FileTest.audio_exist?("Audio/SE/GUI sel decision")
pbSEPlay("GUI sel decision", 80) pbSEPlay("GUI sel decision",80)
end end
end end
# Plays a sound effect that plays when a choice is canceled. # Plays a sound effect that plays when a choice is canceled.
def pbPlayCancelSE def pbPlayCancelSE
if !nil_or_empty?($data_system&.cancel_se&.name) if $data_system && $data_system.respond_to?("cancel_se") &&
$data_system.cancel_se && $data_system.cancel_se.name!=""
pbSEPlay($data_system.cancel_se) 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") elsif FileTest.audio_exist?("Audio/SE/GUI sel cancel")
pbSEPlay("GUI sel cancel", 80) pbSEPlay("GUI sel cancel",80)
end end
end end
# Plays a buzzer sound effect. # Plays a buzzer sound effect.
def pbPlayBuzzerSE def pbPlayBuzzerSE
if !nil_or_empty?($data_system&.buzzer_se&.name) if $data_system && $data_system.respond_to?("buzzer_se") &&
$data_system.buzzer_se && $data_system.buzzer_se.name!=""
pbSEPlay($data_system.buzzer_se) 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") elsif FileTest.audio_exist?("Audio/SE/GUI sel buzzer")
pbSEPlay("GUI sel buzzer", 80) pbSEPlay("GUI sel buzzer",80)
end end
end end
# Plays a sound effect that plays when the player closes a menu. # Plays a sound effect that plays when the player moves the cursor.
def pbPlayCloseMenuSE def pbPlayCloseMenuSE
if FileTest.audio_exist?("Audio/SE/GUI menu close") if FileTest.audio_exist?("Audio/SE/GUI menu close")
pbSEPlay("GUI menu close", 80) pbSEPlay("GUI menu close",80)
end end
end end

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,4 @@
#=============================================================================== class PictureSprite < SpriteWrapper
#
#===============================================================================
class PictureSprite < Sprite
def initialize(viewport, picture) def initialize(viewport, picture)
super(viewport) super(viewport)
@picture = picture @picture = picture
@@ -13,7 +10,7 @@ class PictureSprite < Sprite
end end
def dispose def dispose
@pictureBitmap&.dispose @pictureBitmap.dispose if @pictureBitmap
super super
end end
@@ -25,9 +22,9 @@ class PictureSprite < Sprite
def update def update
super super
@pictureBitmap&.update @pictureBitmap.update if @pictureBitmap
# If picture file name is different from current one # If picture file name is different from current one
if @customBitmap && @picture.name == "" if @customBitmap && @picture.name==""
self.bitmap = (@customBitmapIsBitmap) ? @customBitmap : @customBitmap.bitmap self.bitmap = (@customBitmapIsBitmap) ? @customBitmap : @customBitmap.bitmap
elsif @picture_name != @picture.name || @picture.hue.to_i != @hue.to_i elsif @picture_name != @picture.name || @picture.hue.to_i != @hue.to_i
# Remember file name to instance variables # Remember file name to instance variables
@@ -35,13 +32,13 @@ class PictureSprite < Sprite
@hue = @picture.hue.to_i @hue = @picture.hue.to_i
# If file name is not empty # If file name is not empty
if @picture_name == "" if @picture_name == ""
@pictureBitmap&.dispose @pictureBitmap.dispose if @pictureBitmap
@pictureBitmap = nil @pictureBitmap = nil
self.visible = false self.visible = false
return return
end end
# Get picture graphic # Get picture graphic
@pictureBitmap&.dispose @pictureBitmap.dispose if @pictureBitmap
@pictureBitmap = AnimatedBitmap.new(@picture_name, @hue) @pictureBitmap = AnimatedBitmap.new(@picture_name, @hue)
self.bitmap = (@pictureBitmap) ? @pictureBitmap.bitmap : nil self.bitmap = (@pictureBitmap) ? @pictureBitmap.bitmap : nil
elsif @picture_name == "" elsif @picture_name == ""
@@ -49,24 +46,28 @@ class PictureSprite < Sprite
self.visible = false self.visible = false
return return
end end
setPictureSprite(self, @picture) setPictureSprite(self,@picture)
end end
end end
def pbTextBitmap(text, maxwidth = Graphics.width)
tmp = Bitmap.new(maxwidth, Graphics.height)
def pbTextBitmap(text, maxwidth=Graphics.width)
tmp = Bitmap.new(maxwidth,Graphics.height)
pbSetSystemFont(tmp) pbSetSystemFont(tmp)
drawFormattedTextEx(tmp, 0, 4, maxwidth, text, Color.new(248, 248, 248), Color.new(168, 184, 184)) drawFormattedTextEx(tmp,0,0,maxwidth,text,Color.new(248,248,248),Color.new(168,184,184))
return tmp return tmp
end end
#=============================================================================== #===============================================================================
# # EventScene
#=============================================================================== #===============================================================================
class EventScene class EventScene
attr_accessor :onCTrigger, :onBTrigger, :onUpdate attr_accessor :onCTrigger,:onBTrigger,:onUpdate
def initialize(viewport = nil) def initialize(viewport=nil)
@viewport = viewport @viewport = viewport
@onCTrigger = Event.new @onCTrigger = Event.new
@onBTrigger = Event.new @onBTrigger = Event.new
@@ -79,8 +80,12 @@ class EventScene
def dispose def dispose
return if disposed? return if disposed?
@picturesprites.each { |sprite| sprite.dispose } for sprite in @picturesprites
@usersprites.each { |sprite| sprite.dispose } sprite.dispose
end
for sprite in @usersprites
sprite.dispose
end
@onCTrigger.clear @onCTrigger.clear
@onBTrigger.clear @onBTrigger.clear
@onUpdate.clear @onUpdate.clear
@@ -100,26 +105,26 @@ class EventScene
# EventScene doesn't take ownership of the passed-in bitmap # EventScene doesn't take ownership of the passed-in bitmap
num = @pictures.length num = @pictures.length
picture = PictureEx.new(num) picture = PictureEx.new(num)
picture.setXY(0, x, y) picture.setXY(0,x,y)
picture.setVisible(0, true) picture.setVisible(0,true)
@pictures[num] = picture @pictures[num] = picture
@picturesprites[num] = PictureSprite.new(@viewport, picture) @picturesprites[num] = PictureSprite.new(@viewport,picture)
@picturesprites[num].setCustomBitmap(bitmap) @picturesprites[num].setCustomBitmap(bitmap)
return picture return picture
end end
def addLabel(x, y, width, text) def addLabel(x, y, width, text)
addBitmap(x, y, pbTextBitmap(text, width)) addBitmap(x,y,pbTextBitmap(text,width))
end end
def addImage(x, y, name) def addImage(x, y, name)
num = @pictures.length num = @pictures.length
picture = PictureEx.new(num) picture = PictureEx.new(num)
picture.name = name picture.name = name
picture.setXY(0, x, y) picture.setXY(0,x,y)
picture.setVisible(0, true) picture.setVisible(0,true)
@pictures[num] = picture @pictures[num] = picture
@picturesprites[num] = PictureSprite.new(@viewport, picture) @picturesprites[num] = PictureSprite.new(@viewport,picture)
return picture return picture
end end
@@ -131,38 +136,36 @@ class EventScene
return @pictures[num] return @pictures[num]
end end
# ticks is in 1/20ths of a second. def wait(frames)
def wait(ticks) frames.times { update }
return if ticks <= 0
timer_start = System.uptime
loop do
update
break if System.uptime - timer_start >= ticks / 20.0
end
end end
# extra_ticks is in 1/20ths of a second. def pictureWait(extraframes=0)
def pictureWait(extra_ticks = 0)
loop do loop do
hasRunning = false hasRunning = false
@pictures.each { |pic| hasRunning = true if pic.running? } for pic in @pictures
hasRunning = true if pic.running?
end
break if !hasRunning break if !hasRunning
update update
end end
wait(extra_ticks) extraframes.times { update }
end end
def update def update
return if disposed? return if disposed?
Graphics.update Graphics.update
Input.update Input.update
@pictures.each { |picture| picture.update } for picture in @pictures
@picturesprites.each { |sprite| sprite.update } picture.update
@usersprites.each do |sprite| end
for sprite in @picturesprites
sprite.update
end
for sprite in @usersprites
next if !sprite || sprite.disposed? || !sprite.is_a?(Sprite) next if !sprite || sprite.disposed? || !sprite.is_a?(Sprite)
sprite.update sprite.update
end end
@usersprites.delete_if { |sprite| sprite.disposed? }
@onUpdate.trigger(self) @onUpdate.trigger(self)
if Input.trigger?(Input::BACK) if Input.trigger?(Input::BACK)
@onBTrigger.trigger(self) @onBTrigger.trigger(self)
@@ -172,21 +175,24 @@ class EventScene
end end
def main def main
loop do while !disposed?
update update
break if disposed?
end end
end end
end end
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
def pbEventScreen(cls) def pbEventScreen(cls)
pbFadeOutIn do pbFadeOutIn {
viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) viewport = Viewport.new(0,0,Graphics.width,Graphics.height)
viewport.z = 99999 viewport.z = 99999
PBDebug.logonerr { cls.new(viewport).main } PBDebug.logonerr {
cls.new(viewport).main
}
viewport.dispose viewport.dispose
end }
end end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module GameData module GameData
#============================================================================= #=============================================================================
# A mixin module for data classes which provides common class methods (called # A mixin module for data classes which provides common class methods (called
@@ -9,10 +6,6 @@ module GameData
# For data that is known by a symbol or an ID number. # For data that is known by a symbol or an ID number.
#============================================================================= #=============================================================================
module ClassMethods module ClassMethods
def schema
return self::SCHEMA
end
def register(hash) def register(hash)
self::DATA[hash[:id]] = self::DATA[hash[:id_number]] = self.new(hash) self::DATA[hash[:id]] = self::DATA[hash[:id_number]] = self.new(hash)
end end
@@ -33,6 +26,9 @@ module GameData
validate other => [Symbol, self, String, Integer] validate other => [Symbol, self, String, Integer]
return other if other.is_a?(self) return other if other.is_a?(self)
other = other.to_sym if other.is_a?(String) other = other.to_sym if other.is_a?(String)
# if other.is_a?(Integer)
# p "Please switch to symbols, thanks."
# end
raise "Unknown ID #{other}." unless self::DATA.has_key?(other) raise "Unknown ID #{other}." unless self::DATA.has_key?(other)
return self::DATA[other] return self::DATA[other]
end end
@@ -44,6 +40,9 @@ module GameData
validate other => [Symbol, self, String, Integer] validate other => [Symbol, self, String, Integer]
return other if other.is_a?(self) return other if other.is_a?(self)
other = other.to_sym if other.is_a?(String) other = other.to_sym if other.is_a?(String)
# if other.is_a?(Integer)
# p "Please switch to symbols, thanks."
# end
return (self::DATA.has_key?(other)) ? self::DATA[other] : nil return (self::DATA.has_key?(other)) ? self::DATA[other] : nil
end end
@@ -55,12 +54,8 @@ module GameData
# Yields all data in order of their id_number. # Yields all data in order of their id_number.
def each def each
sorted_keys = self::DATA.keys.sort { |a, b| self::DATA[a].id_number <=> self::DATA[b].id_number } keys = self::DATA.keys.sort { |a, b| self::DATA[a].id_number <=> self::DATA[b].id_number }
sorted_keys.each { |key| yield self::DATA[key] if !key.is_a?(Integer) } keys.each { |key| yield self::DATA[key] if !key.is_a?(Integer) }
end
def count
return self::DATA.length / 2
end end
def load def load
@@ -79,10 +74,6 @@ module GameData
# For data that is only known by a symbol. # For data that is only known by a symbol.
#============================================================================= #=============================================================================
module ClassMethodsSymbols module ClassMethodsSymbols
def schema
return self::SCHEMA
end
def register(hash) def register(hash)
self::DATA[hash[:id]] = self.new(hash) self::DATA[hash[:id]] = self.new(hash)
end end
@@ -123,21 +114,12 @@ module GameData
return self::DATA.keys return self::DATA.keys
end end
# Yields all data in the order they were defined.
def each
self::DATA.each_value { |value| yield value }
end
# Yields all data in alphabetical order. # Yields all data in alphabetical order.
def each_alphabetically def each
keys = self::DATA.keys.sort { |a, b| self::DATA[a].real_name <=> self::DATA[b].real_name } keys = self::DATA.keys.sort { |a, b| self::DATA[a].real_name <=> self::DATA[b].real_name }
keys.each { |key| yield self::DATA[key] } keys.each { |key| yield self::DATA[key] }
end end
def count
return self::DATA.length
end
def load def load
const_set(:DATA, load_data("Data/#{self::DATA_FILENAME}")) const_set(:DATA, load_data("Data/#{self::DATA_FILENAME}"))
end end
@@ -154,10 +136,6 @@ module GameData
# For data that is only known by an ID number. # For data that is only known by an ID number.
#============================================================================= #=============================================================================
module ClassMethodsIDNumbers module ClassMethodsIDNumbers
def schema
return self::SCHEMA
end
def register(hash) def register(hash)
self::DATA[hash[:id]] = self.new(hash) self::DATA[hash[:id]] = self.new(hash)
end end
@@ -193,16 +171,12 @@ module GameData
return self::DATA.keys return self::DATA.keys
end end
# Yields all data in numerical order. # Yields all data in numberical order.
def each def each
keys = self::DATA.keys.sort keys = self::DATA.keys.sort
keys.each { |key| yield self::DATA[key] } keys.each { |key| yield self::DATA[key] }
end end
def count
return self::DATA.length
end
def load def load
const_set(:DATA, load_data("Data/#{self::DATA_FILENAME}")) const_set(:DATA, load_data("Data/#{self::DATA_FILENAME}"))
end end
@@ -222,65 +196,34 @@ module GameData
# @return [Boolean] whether other represents the same thing as this thing # @return [Boolean] whether other represents the same thing as this thing
def ==(other) def ==(other)
return false if other.nil? return false if other.nil?
case other if other.is_a?(Symbol)
when Symbol
return @id == other return @id == other
when self.class elsif other.is_a?(self.class)
return @id == other.id return @id == other.id
when String elsif other.is_a?(String)
return @id == other.to_sym return @id_number == other.to_sym
when Integer elsif other.is_a?(Integer)
return @id_number == other return @id_number == other
end end
return false return false
end end
def get_property_for_PBS(key)
ret = nil
if self.class::SCHEMA.include?(key) && self.respond_to?(self.class::SCHEMA[key][0])
ret = self.send(self.class::SCHEMA[key][0])
ret = nil if ret == false || (ret.is_a?(Array) && ret.length == 0)
end
return ret
end
end end
#============================================================================= #=============================================================================
# A bulk loader method for all data stored in .dat files in the Data folder. # A bulk loader method for all data stored in .dat files in the Data folder.
#============================================================================= #=============================================================================
def self.load_all def self.load_all
self.constants.each do |c| Type.load
next if !self.const_get(c).is_a?(Class) Ability.load
self.const_get(c).load if self.const_get(c).const_defined?(:DATA_FILENAME) Move.load
end Item.load
end BerryPlant.load
Species.load
def self.get_all_data_filenames Ribbon.load
ret = [] Encounter.load
self.constants.each do |c| TrainerType.load
next if !self.const_get(c).is_a?(Class) Trainer.load
next if !self.const_get(c).const_defined?(:DATA_FILENAME) Metadata.load
if self.const_get(c).const_defined?(:OPTIONAL) && self.const_get(c)::OPTIONAL MapMetadata.load
ret.push([self.const_get(c)::DATA_FILENAME, false])
else
ret.push([self.const_get(c)::DATA_FILENAME, true])
end
end
return ret
end
def self.get_all_pbs_base_filenames
ret = {}
self.constants.each do |c|
next if !self.const_get(c).is_a?(Class)
ret[c] = self.const_get(c)::PBS_BASE_FILENAME if self.const_get(c).const_defined?(:PBS_BASE_FILENAME)
next if !ret[c].is_a?(Array)
ret[c].length.times do |i|
next if i == 0
ret[(c.to_s + i.to_s).to_sym] = ret[c][i] # :Species1 => "pokemon_forms"
end
ret[c] = ret[c][0] # :Species => "pokemon"
end
return ret
end end
end end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module GameData module GameData
class GrowthRate class GrowthRate
attr_reader :id attr_reader :id
@@ -27,8 +24,6 @@ module GameData
return Settings::MAXIMUM_LEVEL return Settings::MAXIMUM_LEVEL
end end
#---------------------------------------------------------------------------
def initialize(hash) def initialize(hash)
@id = hash[:id] @id = hash[:id]
@real_name = hash[:name] || "Unnamed" @real_name = hash[:name] || "Unnamed"
@@ -69,7 +64,7 @@ module GameData
return ArgumentError.new("Exp amount #{level} is invalid.") if !exp || exp < 0 return ArgumentError.new("Exp amount #{level} is invalid.") if !exp || exp < 0
max = GrowthRate.max_level max = GrowthRate.max_level
return max if exp >= maximum_exp return max if exp >= maximum_exp
(1..max).each do |level| for level in 1..max
return level - 1 if exp < minimum_exp_for_level(level) return level - 1 if exp < minimum_exp_for_level(level)
end end
return max return max
@@ -77,8 +72,6 @@ module GameData
end end
end end
#===============================================================================
#
#=============================================================================== #===============================================================================
GameData::GrowthRate.register({ GameData::GrowthRate.register({
@@ -95,13 +88,14 @@ GameData::GrowthRate.register({
357911, 373248, 389017, 405224, 421875, 438976, 456533, 474552, 493039, 512000, 357911, 373248, 389017, 405224, 421875, 438976, 456533, 474552, 493039, 512000,
531441, 551368, 571787, 592704, 614125, 636056, 658503, 681472, 704969, 729000, 531441, 551368, 571787, 592704, 614125, 636056, 658503, 681472, 704969, 729000,
753571, 778688, 804357, 830584, 857375, 884736, 912673, 941192, 970299, 1000000], 753571, 778688, 804357, 830584, 857375, 884736, 912673, 941192, 970299, 1000000],
:exp_formula => proc { |level| next level**3 } :exp_formula => proc { |level| next level ** 3 }
}) })
# Erratic (600000): # Erratic (600000):
# For levels 0-50: n**3 * (100 - n) / 50 # For levels 0-50: n**3 * (100 - n) / 50
# For levels 51-68: n**3 * (150 - n) / 100 # For levels 51-68: n**3 * (150 - n) / 100
# For levels 69-98: n**3 * ((1911 - (10 * level)) / 3).floor / 500 # For levels 69-98: n**3 * 1.274 - (n / 150) - p(n mod 3)
# where p(x) = array(0.000, 0.008, 0.014)[x]
# For levels 99-100: n**3 * (160 - n) / 100 # For levels 99-100: n**3 * (160 - n) / 100
GameData::GrowthRate.register({ GameData::GrowthRate.register({
:id => :Erratic, :id => :Erratic,
@@ -117,7 +111,7 @@ GameData::GrowthRate.register({
286328, 296358, 305767, 316074, 326531, 336255, 346965, 357812, 367807, 378880, 286328, 296358, 305767, 316074, 326531, 336255, 346965, 357812, 367807, 378880,
390077, 400293, 411686, 423190, 433572, 445239, 457001, 467489, 479378, 491346, 390077, 400293, 411686, 423190, 433572, 445239, 457001, 467489, 479378, 491346,
501878, 513934, 526049, 536557, 548720, 560922, 571333, 583539, 591882, 600000], 501878, 513934, 526049, 536557, 548720, 560922, 571333, 583539, 591882, 600000],
:exp_formula => proc { |level| next ((level**4) + ((level**3) * 2000)) / 3500 } :exp_formula => proc { |level| next (level ** 4) * 3 / 500 }
}) })
# Fluctuating (1640000): # Fluctuating (1640000):
@@ -139,7 +133,8 @@ GameData::GrowthRate.register({
765275, 804997, 834809, 877201, 908905, 954084, 987754, 1035837, 1071552, 1122660, 765275, 804997, 834809, 877201, 908905, 954084, 987754, 1035837, 1071552, 1122660,
1160499, 1214753, 1254796, 1312322, 1354652, 1415577, 1460276, 1524731, 1571884, 1640000], 1160499, 1214753, 1254796, 1312322, 1354652, 1415577, 1460276, 1524731, 1571884, 1640000],
:exp_formula => proc { |level| :exp_formula => proc { |level|
next ((level**3) * ((level / 2) + 32)) * 4 / (100 + level) rate = [82 - (level - 100) / 2.0, 40].max
next (level ** 4) * rate / 5000
} }
}) })
@@ -157,7 +152,7 @@ GameData::GrowthRate.register({
360838, 377197, 394045, 411388, 429235, 447591, 466464, 485862, 505791, 526260, 360838, 377197, 394045, 411388, 429235, 447591, 466464, 485862, 505791, 526260,
547274, 568841, 590969, 613664, 636935, 660787, 685228, 710266, 735907, 762160, 547274, 568841, 590969, 613664, 636935, 660787, 685228, 710266, 735907, 762160,
789030, 816525, 844653, 873420, 902835, 932903, 963632, 995030, 1027103, 1059860], 789030, 816525, 844653, 873420, 902835, 932903, 963632, 995030, 1027103, 1059860],
:exp_formula => proc { |level| next ((level**3) * 6 / 5) - (15 * (level**2)) + (100 * level) - 140 } :exp_formula => proc { |level| next ((level ** 3) * 6 / 5) - 15 * (level ** 2) + 100 * level - 140 }
}) })
GameData::GrowthRate.register({ GameData::GrowthRate.register({
@@ -174,7 +169,7 @@ GameData::GrowthRate.register({
286328, 298598, 311213, 324179, 337500, 351180, 365226, 379641, 394431, 409600, 286328, 298598, 311213, 324179, 337500, 351180, 365226, 379641, 394431, 409600,
425152, 441094, 457429, 474163, 491300, 508844, 526802, 545177, 563975, 583200, 425152, 441094, 457429, 474163, 491300, 508844, 526802, 545177, 563975, 583200,
602856, 622950, 643485, 664467, 685900, 707788, 730138, 752953, 776239, 800000], 602856, 622950, 643485, 664467, 685900, 707788, 730138, 752953, 776239, 800000],
:exp_formula => proc { |level| (level**3) * 4 / 5 } :exp_formula => proc { |level| (level ** 3) * 4 / 5 }
}) })
GameData::GrowthRate.register({ GameData::GrowthRate.register({
@@ -191,5 +186,5 @@ GameData::GrowthRate.register({
447388, 466560, 486271, 506530, 527343, 548720, 570666, 593190, 616298, 640000, 447388, 466560, 486271, 506530, 527343, 548720, 570666, 593190, 616298, 640000,
664301, 689210, 714733, 740880, 767656, 795070, 823128, 851840, 881211, 911250, 664301, 689210, 714733, 740880, 767656, 795070, 823128, 851840, 881211, 911250,
941963, 973360, 1005446, 1038230, 1071718, 1105920, 1140841, 1176490, 1212873, 1250000], 941963, 973360, 1005446, 1038230, 1071718, 1105920, 1140841, 1176490, 1212873, 1250000],
:exp_formula => proc { |level| (level**3) * 5 / 4 } :exp_formula => proc { |level| (level ** 3) * 5 / 4 }
}) })

View File

@@ -1,9 +1,7 @@
#===============================================================================
# If a Pokémon's gender ratio is none of :AlwaysMale, :AlwaysFemale or # If a Pokémon's gender ratio is none of :AlwaysMale, :AlwaysFemale or
# :Genderless, then it will choose a random number between 0 and 255 inclusive, # :Genderless, then it will choose a random number between 0 and 255 inclusive,
# and compare it to the @female_chance. If the random number is lower than this # and compare it to the @female_chance. If the random number is lower than this
# chance, it will be female; otherwise, it will be male. # chance, it will be female; otherwise, it will be male.
#===============================================================================
module GameData module GameData
class GenderRatio class GenderRatio
attr_reader :id attr_reader :id
@@ -18,8 +16,6 @@ module GameData
def self.load; end def self.load; end
def self.save; end def self.save; end
#---------------------------------------------------------------------------
def initialize(hash) def initialize(hash)
@id = hash[:id] @id = hash[:id]
@real_name = hash[:name] || "Unnamed" @real_name = hash[:name] || "Unnamed"
@@ -30,17 +26,9 @@ module GameData
def name def name
return _INTL(@real_name) return _INTL(@real_name)
end end
# @return [Boolean] whether a Pokémon with this gender ratio can only ever
# be a single gender
def single_gendered?
return @female_chance.nil?
end
end end
end end
#===============================================================================
#
#=============================================================================== #===============================================================================
GameData::GenderRatio.register({ GameData::GenderRatio.register({

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module GameData module GameData
class EggGroup class EggGroup
attr_reader :id attr_reader :id
@@ -14,8 +11,6 @@ module GameData
def self.load; end def self.load; end
def self.save; end def self.save; end
#---------------------------------------------------------------------------
def initialize(hash) def initialize(hash)
@id = hash[:id] @id = hash[:id]
@real_name = hash[:name] || "Unnamed" @real_name = hash[:name] || "Unnamed"
@@ -28,8 +23,6 @@ module GameData
end end
end end
#===============================================================================
#
#=============================================================================== #===============================================================================
GameData::EggGroup.register({ GameData::EggGroup.register({

View File

@@ -1,29 +1,26 @@
#=============================================================================== # NOTE: The id_number is only used to determine the order that body shapes are
# NOTE: The order these shapes are registered are the order they are listed in # listed in the Pokédex search screen. Number 0 (:None) is ignored; they
# the Pokédex search screen. # start with shape 1.
# "Graphics/UI/Pokedex/icon_shapes.png" contains icons for these # "Graphics/Pictures/Pokedex/icon_shapes.png" contains icons for these
# shapes. # shapes.
#===============================================================================
module GameData module GameData
class BodyShape class BodyShape
attr_reader :id attr_reader :id
attr_reader :id_number
attr_reader :real_name attr_reader :real_name
attr_reader :icon_position # Where this shape's icon is within icon_shapes.png
DATA = {} DATA = {}
extend ClassMethodsSymbols extend ClassMethods
include InstanceMethods include InstanceMethods
def self.load; end def self.load; end
def self.save; end def self.save; end
#---------------------------------------------------------------------------
def initialize(hash) def initialize(hash)
@id = hash[:id] @id = hash[:id]
@id_number = hash[:id_number] || -1
@real_name = hash[:name] || "Unnamed" @real_name = hash[:name] || "Unnamed"
@icon_position = hash[:icon_position] || 0
end end
# @return [String] the translated name of this body shape # @return [String] the translated name of this body shape
@@ -33,90 +30,88 @@ module GameData
end end
end end
#===============================================================================
#
#=============================================================================== #===============================================================================
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :Head, :id => :Head,
:name => _INTL("Head"), :id_number => 1,
:icon_position => 0 :name => _INTL("Head")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :Serpentine, :id => :Serpentine,
:name => _INTL("Serpentine"), :id_number => 2,
:icon_position => 1 :name => _INTL("Serpentine")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :Finned, :id => :Finned,
:name => _INTL("Finned"), :id_number => 3,
:icon_position => 2 :name => _INTL("Finned")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :HeadArms, :id => :HeadArms,
:name => _INTL("Head and arms"), :id_number => 4,
:icon_position => 3 :name => _INTL("Head and arms")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :HeadBase, :id => :HeadBase,
:name => _INTL("Head and base"), :id_number => 5,
:icon_position => 4 :name => _INTL("Head and base")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :BipedalTail, :id => :BipedalTail,
:name => _INTL("Bipedal with tail"), :id_number => 6,
:icon_position => 5 :name => _INTL("Bipedal with tail")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :HeadLegs, :id => :HeadLegs,
:name => _INTL("Head and legs"), :id_number => 7,
:icon_position => 6 :name => _INTL("Head and legs")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :Quadruped, :id => :Quadruped,
:name => _INTL("Quadruped"), :id_number => 8,
:icon_position => 7 :name => _INTL("Quadruped")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :Winged, :id => :Winged,
:name => _INTL("Winged"), :id_number => 9,
:icon_position => 8 :name => _INTL("Winged")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :Multiped, :id => :Multiped,
:name => _INTL("Multiped"), :id_number => 10,
:icon_position => 9 :name => _INTL("Multiped")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :MultiBody, :id => :MultiBody,
:name => _INTL("Multi Body"), :id_number => 11,
:icon_position => 10 :name => _INTL("Multi Body")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :Bipedal, :id => :Bipedal,
:name => _INTL("Bipedal"), :id_number => 12,
:icon_position => 11 :name => _INTL("Bipedal")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :MultiWinged, :id => :MultiWinged,
:name => _INTL("Multi Winged"), :id_number => 13,
:icon_position => 12 :name => _INTL("Multi Winged")
}) })
GameData::BodyShape.register({ GameData::BodyShape.register({
:id => :Insectoid, :id => :Insectoid,
:name => _INTL("Insectoid"), :id_number => 14,
:icon_position => 13 :name => _INTL("Insectoid")
}) })

View File

@@ -1,24 +1,22 @@
#=============================================================================== # NOTE: The id_number is only used to determine the order that body colors are
# NOTE: The order these colors are registered are the order they are listed in # listed in the Pokédex search screen.
# the Pokédex search screen.
#===============================================================================
module GameData module GameData
class BodyColor class BodyColor
attr_reader :id attr_reader :id
attr_reader :id_number
attr_reader :real_name attr_reader :real_name
DATA = {} DATA = {}
extend ClassMethodsSymbols extend ClassMethods
include InstanceMethods include InstanceMethods
def self.load; end def self.load; end
def self.save; end def self.save; end
#---------------------------------------------------------------------------
def initialize(hash) def initialize(hash)
@id = hash[:id] @id = hash[:id]
@id_number = hash[:id_number] || -1
@real_name = hash[:name] || "Unnamed" @real_name = hash[:name] || "Unnamed"
end end
@@ -29,56 +27,64 @@ module GameData
end end
end end
#===============================================================================
#
#=============================================================================== #===============================================================================
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :Red, :id => :Red,
:id_number => 0,
:name => _INTL("Red") :name => _INTL("Red")
}) })
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :Blue, :id => :Blue,
:id_number => 1,
:name => _INTL("Blue") :name => _INTL("Blue")
}) })
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :Yellow, :id => :Yellow,
:id_number => 2,
:name => _INTL("Yellow") :name => _INTL("Yellow")
}) })
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :Green, :id => :Green,
:id_number => 3,
:name => _INTL("Green") :name => _INTL("Green")
}) })
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :Black, :id => :Black,
:id_number => 4,
:name => _INTL("Black") :name => _INTL("Black")
}) })
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :Brown, :id => :Brown,
:id_number => 5,
:name => _INTL("Brown") :name => _INTL("Brown")
}) })
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :Purple, :id => :Purple,
:id_number => 6,
:name => _INTL("Purple") :name => _INTL("Purple")
}) })
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :Gray, :id => :Gray,
:id_number => 7,
:name => _INTL("Gray") :name => _INTL("Gray")
}) })
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :White, :id => :White,
:id_number => 8,
:name => _INTL("White") :name => _INTL("White")
}) })
GameData::BodyColor.register({ GameData::BodyColor.register({
:id => :Pink, :id => :Pink,
:id_number => 9,
:name => _INTL("Pink") :name => _INTL("Pink")
}) })

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module GameData module GameData
class Habitat class Habitat
attr_reader :id attr_reader :id
@@ -14,8 +11,6 @@ module GameData
def self.load; end def self.load; end
def self.save; end def self.save; end
#---------------------------------------------------------------------------
def initialize(hash) def initialize(hash)
@id = hash[:id] @id = hash[:id]
@real_name = hash[:name] || "Unnamed" @real_name = hash[:name] || "Unnamed"
@@ -28,8 +23,6 @@ module GameData
end end
end end
#===============================================================================
#
#=============================================================================== #===============================================================================
GameData::Habitat.register({ GameData::Habitat.register({

View File

@@ -1,18 +1,12 @@
#===============================================================================
#
#===============================================================================
module GameData module GameData
class Evolution class Evolution
attr_reader :id attr_reader :id
attr_reader :real_name attr_reader :real_name
attr_reader :parameter attr_reader :parameter
attr_reader :any_level_up # false means parameter is the minimum level attr_reader :minimum_level # 0 means parameter is the minimum level
attr_reader :level_up_proc attr_reader :level_up_proc
attr_reader :battle_level_up_proc
attr_reader :use_item_proc attr_reader :use_item_proc
attr_reader :on_trade_proc attr_reader :on_trade_proc
attr_reader :after_battle_proc
attr_reader :event_proc
attr_reader :after_evolution_proc attr_reader :after_evolution_proc
DATA = {} DATA = {}
@@ -23,37 +17,21 @@ module GameData
def self.load; end def self.load; end
def self.save; end def self.save; end
#---------------------------------------------------------------------------
def initialize(hash) def initialize(hash)
@id = hash[:id] @id = hash[:id]
@real_name = hash[:id].to_s || "Unnamed" @real_name = hash[:id].to_s || "Unnamed"
@parameter = hash[:parameter] @parameter = hash[:parameter]
@any_level_up = hash[:any_level_up] || false @minimum_level = hash[:minimum_level] || 0
@level_up_proc = hash[:level_up_proc] @level_up_proc = hash[:level_up_proc]
@battle_level_up_proc = hash[:battle_level_up_proc]
@use_item_proc = hash[:use_item_proc] @use_item_proc = hash[:use_item_proc]
@on_trade_proc = hash[:on_trade_proc] @on_trade_proc = hash[:on_trade_proc]
@after_battle_proc = hash[:after_battle_proc]
@event_proc = hash[:event_proc]
@after_evolution_proc = hash[:after_evolution_proc] @after_evolution_proc = hash[:after_evolution_proc]
end end
alias name real_name
def call_level_up(*args) def call_level_up(*args)
return (@level_up_proc) ? @level_up_proc.call(*args) : nil return (@level_up_proc) ? @level_up_proc.call(*args) : nil
end end
def call_battle_level_up(*args)
if @battle_level_up_proc
return @battle_level_up_proc.call(*args)
elsif @level_up_proc
return @level_up_proc.call(*args)
end
return nil
end
def call_use_item(*args) def call_use_item(*args)
return (@use_item_proc) ? @use_item_proc.call(*args) : nil return (@use_item_proc) ? @use_item_proc.call(*args) : nil
end end
@@ -62,32 +40,18 @@ module GameData
return (@on_trade_proc) ? @on_trade_proc.call(*args) : nil return (@on_trade_proc) ? @on_trade_proc.call(*args) : nil
end end
def call_after_battle(*args)
return (@after_battle_proc) ? @after_battle_proc.call(*args) : nil
end
def call_event(*args)
return (@event_proc) ? @event_proc.call(*args) : nil
end
def call_after_evolution(*args) def call_after_evolution(*args)
@after_evolution_proc&.call(*args) @after_evolution_proc.call(*args) if @after_evolution_proc
end end
end end
end end
#===============================================================================
#
#=============================================================================== #===============================================================================
GameData::Evolution.register({ GameData::Evolution.register({
:id => :None :id => :None
}) })
#===============================================================================
#
#===============================================================================
GameData::Evolution.register({ GameData::Evolution.register({
:id => :Level, :id => :Level,
:parameter => Integer, :parameter => Integer,
@@ -220,23 +184,12 @@ GameData::Evolution.register({
} }
}) })
GameData::Evolution.register({
:id => :LevelCoins,
:parameter => Integer,
:level_up_proc => proc { |pkmn, parameter|
next $player.coins >= parameter
},
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
$player.coins -= parameter
next true
}
})
GameData::Evolution.register({ GameData::Evolution.register({
:id => :LevelDarkness, :id => :LevelDarkness,
:parameter => Integer, :parameter => Integer,
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.level >= parameter && $game_map.metadata&.dark_map map_metadata = GameData::MapMetadata.try_get($game_map.map_id)
next pkmn.level >= parameter && map_metadata && map_metadata.dark_map
} }
}) })
@@ -244,7 +197,7 @@ GameData::Evolution.register({
:id => :LevelDarkInParty, :id => :LevelDarkInParty,
:parameter => Integer, :parameter => Integer,
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.level >= parameter && $player.has_pokemon_of_type?(:DARK, [pkmn]) next pkmn.level >= parameter && $Trainer.has_pokemon_of_type?(:DARK)
} }
}) })
@@ -299,61 +252,64 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :Shedinja, :id => :Shedinja,
:parameter => Integer, :parameter => Integer,
:level_up_proc => proc { |pkmn, parameter|
next false # This is a dummy proc and shouldn't next true
},
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species| :after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
next false if $player.party_full? next false if $Trainer.party_full?
next false if !$bag.has?(:POKEBALL) next false if !$PokemonBag.pbHasItem?(:POKEBALL)
PokemonEvolutionScene.pbDuplicatePokemon(pkmn, new_species) PokemonEvolutionScene.pbDuplicatePokemon(pkmn, new_species)
$bag.remove(:POKEBALL) $PokemonBag.pbDeleteItem(:POKEBALL)
next true next true
} }
}) })
GameData::Evolution.register({ GameData::Evolution.register({
:id => :Happiness, :id => :Happiness,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) next pkmn.happiness >= 220
} }
}) })
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HappinessMale, :id => :HappinessMale,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && pkmn.male? next pkmn.happiness >= 220 && pkmn.male?
} }
}) })
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HappinessFemale, :id => :HappinessFemale,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && pkmn.female? next pkmn.happiness >= 220 && pkmn.female?
} }
}) })
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HappinessDay, :id => :HappinessDay,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && PBDayNight.isDay? next pkmn.happiness >= 220 && PBDayNight.isDay?
} }
}) })
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HappinessNight, :id => :HappinessNight,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && PBDayNight.isNight? next pkmn.happiness >= 220 && PBDayNight.isNight?
} }
}) })
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HappinessMove, :id => :HappinessMove,
:parameter => :Move, :parameter => :Move,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
if pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) if pkmn.happiness >= 220
next pkmn.moves.any? { |m| m && m.id == parameter } next pkmn.moves.any? { |m| m && m.id == parameter }
end end
} }
@@ -362,10 +318,10 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HappinessMoveType, :id => :HappinessMoveType,
:parameter => :Type, :parameter => :Type,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
if pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) if pkmn.happiness >= 220
next pkmn.moves.any? { |m| m && m.type == parameter } next pkmn.moves.any? { |m| m && m.id > 0 && m.type == parameter }
end end
} }
}) })
@@ -373,9 +329,9 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HappinessHoldItem, :id => :HappinessHoldItem,
:parameter => :Item, :parameter => :Item,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) next pkmn.item == parameter && pkmn.happiness >= 220
}, },
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species| :after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
next false if evo_species != new_species || !pkmn.hasItem?(parameter) next false if evo_species != new_species || !pkmn.hasItem?(parameter)
@@ -386,7 +342,7 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :MaxHappiness, :id => :MaxHappiness,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness == 255 next pkmn.happiness == 255
} }
@@ -395,7 +351,7 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :Beauty, # Feebas :id => :Beauty, # Feebas
:parameter => Integer, :parameter => Integer,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.beauty >= parameter next pkmn.beauty >= parameter
} }
@@ -404,7 +360,7 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HoldItem, :id => :HoldItem,
:parameter => :Item, :parameter => :Item,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter next pkmn.item == parameter
}, },
@@ -418,7 +374,7 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HoldItemMale, :id => :HoldItemMale,
:parameter => :Item, :parameter => :Item,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.male? next pkmn.item == parameter && pkmn.male?
}, },
@@ -432,7 +388,7 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HoldItemFemale, :id => :HoldItemFemale,
:parameter => :Item, :parameter => :Item,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.female? next pkmn.item == parameter && pkmn.female?
}, },
@@ -446,7 +402,7 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :DayHoldItem, :id => :DayHoldItem,
:parameter => :Item, :parameter => :Item,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && PBDayNight.isDay? next pkmn.item == parameter && PBDayNight.isDay?
}, },
@@ -460,7 +416,7 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :NightHoldItem, :id => :NightHoldItem,
:parameter => :Item, :parameter => :Item,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && PBDayNight.isNight? next pkmn.item == parameter && PBDayNight.isNight?
}, },
@@ -474,9 +430,9 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HoldItemHappiness, :id => :HoldItemHappiness,
:parameter => :Item, :parameter => :Item,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) next pkmn.item == parameter && pkmn.happiness >= 220
}, },
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species| :after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
next false if evo_species != new_species || !pkmn.hasItem?(parameter) next false if evo_species != new_species || !pkmn.hasItem?(parameter)
@@ -488,7 +444,7 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HasMove, :id => :HasMove,
:parameter => :Move, :parameter => :Move,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.moves.any? { |m| m && m.id == parameter } next pkmn.moves.any? { |m| m && m.id == parameter }
} }
@@ -497,7 +453,7 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HasMoveType, :id => :HasMoveType,
:parameter => :Type, :parameter => :Type,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next pkmn.moves.any? { |m| m && m.type == parameter } next pkmn.moves.any? { |m| m && m.type == parameter }
} }
@@ -506,69 +462,35 @@ GameData::Evolution.register({
GameData::Evolution.register({ GameData::Evolution.register({
:id => :HasInParty, :id => :HasInParty,
:parameter => :Species, :parameter => :Species,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next $player.has_species?(parameter) next $Trainer.has_species?(parameter)
} }
}) })
GameData::Evolution.register({ GameData::Evolution.register({
:id => :Location, :id => :Location,
:parameter => Integer, :parameter => Integer,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
next $game_map.map_id == parameter next $game_map.map_id == parameter
} }
}) })
GameData::Evolution.register({
:id => :LocationFlag,
:parameter => String,
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next $game_map.metadata&.has_flag?(parameter)
}
})
GameData::Evolution.register({ GameData::Evolution.register({
:id => :Region, :id => :Region,
:parameter => Integer, :parameter => Integer,
:any_level_up => true, # Needs any level up :minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter| :level_up_proc => proc { |pkmn, parameter|
map_metadata = $game_map.metadata map_metadata = GameData::MapMetadata.try_get($game_map.map_id)
next map_metadata&.town_map_position && map_metadata.town_map_position[0] == parameter next map_metadata && map_metadata.town_map_position &&
} map_metadata.town_map_position[0] == parameter
})
GameData::Evolution.register({
:id => :Counter,
:parameter => Integer,
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.evolution_counter >= parameter
},
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
pkmn.evolution_counter = 0
next true
} }
}) })
#=============================================================================== #===============================================================================
# Evolution methods that trigger when levelling up in battle. # Evolution methods that trigger when using an item on the Pokémon
#=============================================================================== #===============================================================================
GameData::Evolution.register({
:id => :LevelBattle,
:parameter => Integer,
:battle_level_up_proc => proc { |pkmn, parameter|
next pkmn.level >= parameter
}
})
#===============================================================================
# Evolution methods that trigger when using an item on the Pokémon.
#===============================================================================
GameData::Evolution.register({ GameData::Evolution.register({
:id => :Item, :id => :Item,
:parameter => :Item, :parameter => :Item,
@@ -613,14 +535,13 @@ GameData::Evolution.register({
:id => :ItemHappiness, :id => :ItemHappiness,
:parameter => :Item, :parameter => :Item,
:use_item_proc => proc { |pkmn, parameter, item| :use_item_proc => proc { |pkmn, parameter, item|
next item == parameter && pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) next item == parameter && pkmn.happiness >= 220
} }
}) })
#=============================================================================== #===============================================================================
# Evolution methods that trigger when the Pokémon is obtained in a trade. # Evolution methods that trigger when the Pokémon is obtained in a trade
#=============================================================================== #===============================================================================
GameData::Evolution.register({ GameData::Evolution.register({
:id => :Trade, :id => :Trade,
:on_trade_proc => proc { |pkmn, parameter, other_pkmn| :on_trade_proc => proc { |pkmn, parameter, other_pkmn|
@@ -673,72 +594,6 @@ GameData::Evolution.register({
:id => :TradeSpecies, :id => :TradeSpecies,
:parameter => :Species, :parameter => :Species,
:on_trade_proc => proc { |pkmn, parameter, other_pkmn| :on_trade_proc => proc { |pkmn, parameter, other_pkmn|
next other_pkmn.species == parameter && !other_pkmn.hasItem?(:EVERSTONE) next pkmn.species == parameter && !other_pkmn.hasItem?(:EVERSTONE)
}
})
#===============================================================================
# Evolution methods that are triggered after any battle.
#===============================================================================
GameData::Evolution.register({
:id => :AfterBattleCounter,
:parameter => Integer,
:any_level_up => true, # Needs any level up
:after_battle_proc => proc { |pkmn, party_index, parameter|
ret = pkmn.evolution_counter >= parameter
pkmn.evolution_counter = 0 # Always resets after battle
next ret
}
})
# Doesn't cause an evolution itself. Just makes the Pokémon ready to evolve by
# another means (e.g. via an event). Note that pkmn.evolution_counter is not
# reset after the battle.
GameData::Evolution.register({
:id => :AfterBattleCounterMakeReady,
:parameter => Integer,
:any_level_up => true, # Needs any level up
:after_battle_proc => proc { |pkmn, party_index, parameter|
pkmn.ready_to_evolve = true if pkmn.evolution_counter >= parameter
next false
}
})
#===============================================================================
# Evolution methods that are triggered by an event.
# Each event has its own number, which is the value of the parameter as defined
# in pokemon.txt/pokemon_forms.txt. It is also 'number' in def pbEvolutionEvent,
# which triggers evolution checks for a particular event number. 'value' in an
# event_proc is the number of the evolution event currently being triggered.
# Evolutions caused by different events should have different numbers. Used
# event numbers are:
# 1: Kubfu -> Urshifu
# 2: Galarian Yamask -> Runerigus
# These used event numbers are only used in pokemon.txt/pokemon_forms.txt and in
# map events that call pbEvolutionEvent, so they are relatively easy to change
# if you need to (no script changes are required). However, you could just
# ignore them instead if you don't want to use them.
#===============================================================================
def pbEvolutionEvent(number)
return if !$player
$player.able_party.each do |pkmn|
pkmn.trigger_event_evolution(number)
end
end
GameData::Evolution.register({
:id => :Event,
:parameter => Integer,
:event_proc => proc { |pkmn, parameter, value|
next value == parameter
}
})
GameData::Evolution.register({
:id => :EventReady,
:parameter => Integer,
:event_proc => proc { |pkmn, parameter, value|
next value == parameter && pkmn.ready_to_evolve
} }
}) })

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