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

8
.gitignore vendored
View File

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

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 #
# Version 21.1 #
# Version 19.1.dev #
# https://github.com/Maruno17/pokemon-essentials #
#==============================================================================#
module Settings
# 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
# 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
# single generation's mechanics. It's considered to be good enough. Only
# 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
# translated by wrapping them in _INTL() as shown. Blank lines are just "".
# To split a line into two columns, put "<s>" in it. Plugin credits and
# Essentials engine credits are added to the end of these credits
# automatically.
def self.game_credits
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 default screen width (at a scale of 1.0).
SCREEN_WIDTH = 512
# The default screen height (at a scale of 1.0).
SCREEN_HEIGHT = 384
# The default screen scale factor. Possible values are 0.5, 1.0, 1.5 and 2.0.
SCREEN_SCALE = 1.0
#-----------------------------------------------------------------------------
# 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.
MAX_MONEY = 9_999_999
MAX_MONEY = 999_999
# The maximum number of Game Corner coins the player can have.
MAX_COINS = 99_999
# The maximum number of Battle Points the player can have.
@@ -54,56 +55,65 @@ module Settings
# The maximum amount of soot the player can have.
MAX_SOOT = 9_999
# The maximum length, in characters, that the player's name can be.
MAX_PLAYER_NAME_SIZE = 12
# A set of arrays each containing a trainer type followed by a Game 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.
MAX_PLAYER_NAME_SIZE = 10
# The maximum number of Pokémon that can be in the party.
MAX_PARTY_SIZE = 6
#=============================================================================
# A set of arrays each containing a trainer type followed by a Global Variable
# number. If the variable isn't set to 0, then all trainers with the
# associated trainer type will be named as whatever is in that variable.
RIVAL_NAMES = [
[:RIVAL1, 12],
[:RIVAL2, 12],
[:CHAMPION, 12]
]
#-----------------------------------------------------------------------------
# Overworld
#-----------------------------------------------------------------------------
#=============================================================================
# Whether outdoor maps should be shaded according to the time of day.
TIME_SHADING = true
# Whether the reflections of the player/events will ripple horizontally.
ANIMATE_REFLECTIONS = true
TIME_SHADING = 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
# mechanics (false).
NEW_BERRY_PLANTS = (MECHANICS_GENERATION >= 4)
NEW_BERRY_PLANTS = (MECHANICS_GENERATION >= 4)
# Whether fishing automatically hooks the Pokémon (true), or whether there is
# a reaction test first (false).
FISHING_AUTO_HOOK = false
FISHING_AUTO_HOOK = false
# The ID of the common event that runs when the player starts fishing (runs
# instead of showing the casting animation).
FISHING_BEGIN_COMMON_EVENT = -1
# The ID of the common event that runs when the player stops fishing (runs
# instead of showing the reeling in animation).
FISHING_END_COMMON_EVENT = -1
#=============================================================================
# The number of steps allowed before a Safari Zone game is over (0=infinite).
SAFARI_STEPS = 600
# The number of seconds a Bug-Catching Contest lasts for (0=infinite).
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.
SAFARI_STEPS = 600
# The number of seconds a Bug Catching Contest lasts for (0=infinite).
BUG_CONTEST_TIME = 20 * 60 # 20 minutes
#=============================================================================
# Pairs of map IDs, where the location signpost isn't shown when moving from
# one of the maps in a pair to the other (and vice versa). Useful for single
# long routes/towns that are spread over multiple maps.
# e.g. [4,5,16,17,42,43] will be map pairs 4,5 and 16,17 and 42,43.
# Moving between two maps that have the exact same name won't show the
# location sign anyway, so you don't need to list those maps here.
NO_LOCATION_SIGNS = []
# 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)
# location signpost anyway, so you don't need to list those maps here.
NO_SIGNPOSTS = []
#=============================================================================
#-----------------------------------------------------------------------------
# Using moves in the overworld
#-----------------------------------------------------------------------------
# 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
# them (false). The amounts/specific badges are defined below.
@@ -123,54 +133,105 @@ module Settings
BADGE_FOR_DIVE = 7
BADGE_FOR_WATERFALL = 8
#-----------------------------------------------------------------------------
# Pokémon
#-----------------------------------------------------------------------------
#=============================================================================
# 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
# Whether super shininess is enabled (uses a different shiny animation).
SUPER_SHINY = (MECHANICS_GENERATION == 8)
# Whether Pokémon with the "Legendary", "Mythical" or "Ultra Beast" flags will
# have at least 3 perfect IVs.
LEGENDARIES_HAVE_SOME_PERFECT_IVS = (MECHANICS_GENERATION >= 6)
# The odds of a wild Pokémon/bred egg having Pokérus (out of 65536).
POKERUS_CHANCE = 3
# 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)
# 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 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 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 Rage Candy Bar acts as a Full Heal (true) or a Potion (false).
RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS = (MECHANICS_GENERATION >= 7)
#-----------------------------------------------------------------------------
# Breeding Pokémon and Day Care
#-----------------------------------------------------------------------------
#=============================================================================
# Whether Pokémon in the Day Care gain Exp for each step the player takes.
# This should be true for the Day Care and false for the Pokémon Nursery, both
# of which use the same code in Essentials.
DAY_CARE_POKEMON_GAIN_EXP_FROM_WALKING = (MECHANICS_GENERATION <= 6)
# Whether two Pokémon in the Day Care can learn egg moves from each other if
# they are the same species.
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)
# The name of the person who created the Pokémon storage system.
def self.storage_creator_name
return _INTL("Bill")
end
# The number of boxes in Pokémon storage.
NUM_STORAGE_BOXES = 30
#-----------------------------------------------------------------------------
# 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
# it can lead to.
@@ -186,198 +247,45 @@ module Settings
66 => [5, 21, 28, 31, 39, 41, 44, 47, 69],
69 => [5, 21, 28, 31, 39, 41, 44, 47, 66 ]
}
# A set of hashes, each containing the details of a roaming Pokémon. The
# information within each hash is as follows:
# * :species
# * :level
# * :icon - Filename in Graphics/UI/Town Map/ of the roamer's Town Map icon.
# * :game_switch - The Pokémon roams if this is nil or <=0 or if that Game
# Switch is ON. Optional.
# * :encounter_type - One of:
# :all = grass, walking in cave, surfing (default)
# :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.
# A set of arrays, each containing the details of a roaming Pokémon. The
# information within each array is as follows:
# * Species.
# * Level.
# * Game Switch; the Pokémon roams while this is ON.
# * Encounter type (0=any, 1=grass/walking in cave, 2=surfing, 3=fishing,
# 4=surfing/fishing). See the bottom of PField_RoamingPokemon for lists.
# * Name of BGM to play for that encounter (optional).
# * Roaming areas specifically for this Pokémon (optional).
ROAMING_SPECIES = [
# {
# :species => :LATIAS,
# :level => 30,
# :icon => "pin_latias",
# :game_switch => 53,
# :encounter_type => :all,
# :bgm => "Battle roaming"
# },
# {
# :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
# }
[:LATIAS, 30, 53, 0, "Battle roaming"],
[:LATIOS, 30, 53, 0, "Battle roaming"],
[:KYOGRE, 40, 54, 2, nil, {
2 => [ 21, 31 ],
21 => [2, 31, 69],
31 => [2, 21, 69],
69 => [ 21, 31 ]
}],
[:ENTEI, 40, 55, 1, nil]
]
#-----------------------------------------------------------------------------
# Party and Pokémon storage
#-----------------------------------------------------------------------------
#=============================================================================
# The maximum number of Pokémon that can be in the party.
MAX_PARTY_SIZE = 6
# The number of boxes in Pokémon storage.
NUM_STORAGE_BOXES = 40
# Whether putting a Pokémon into Pokémon storage will heal it. If false, they
# are healed by the Recover All: Entire Party event command (at Poké Centers).
HEAL_STORED_POKEMON = (MECHANICS_GENERATION <= 7)
#-----------------------------------------------------------------------------
# Items
#-----------------------------------------------------------------------------
# Whether various HP-healing items heal the amounts they do in Gen 7+ (true)
# 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]
# A set of arrays, each containing the details of a wild encounter that can
# only occur via using the Poké Radar. The information within each array is as
# follows:
# * Map ID on which this encounter can occur.
# * Probability that this encounter will occur (as a percentage).
# * Species.
# * Minimum possible level.
# * Maximum possible level (optional).
POKE_RADAR_ENCOUNTERS = [
[5, 20, :STARLY, 12, 15],
[21, 10, :STANTLER, 14],
[28, 20, :BUTTERFREE, 15, 18],
[28, 20, :BEEDRILL, 15, 18]
]
# 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.
STARTING_OVER_SWITCH = 1
@@ -389,23 +297,14 @@ module Settings
# The Game Switch which, while ON, makes all Pokémon created considered to be
# met via a fateful encounter.
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).
GRASS_ANIMATION_ID = 1
# ID of the animation played when the player lands on the ground after hopping
# over a ledge (shows a dust impact).
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
# bubble).
EXCLAMATION_ANIMATION_ID = 3
@@ -422,46 +321,17 @@ module Settings
# is on the map (for new plant growth mechanics only).
PLANT_SPARKLE_ANIMATION_ID = 7
#-----------------------------------------------------------------------------
# Files
#-----------------------------------------------------------------------------
#=============================================================================
DEFAULT_WILD_BATTLE_BGM = "Battle wild"
DEFAULT_WILD_VICTORY_BGM = "Battle victory"
DEFAULT_WILD_CAPTURE_ME = "Battle capture success"
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).
# An array of available languages in the game, and their corresponding message
# file in the Data folder. Edit only if you have 2 or more languages to choose
# from.
LANGUAGES = [
# ["English", "english"],
# ["Deutsch", "deutsch"]
# ["English", "english.dat"],
# ["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/".
SPEECH_WINDOWSKINS = [
@@ -487,6 +357,7 @@ module Settings
"speech hgss 20",
"speech pl 18"
]
# Available menu frames. These are graphic files in "Graphics/Windowskins/".
MENU_WINDOWSKINS = [
"choice 1",
@@ -518,30 +389,10 @@ module Settings
"choice 27",
"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
#===============================================================================
# DO NOT EDIT THESE!
#===============================================================================
module Essentials
VERSION = "21.1"
VERSION = "19.1.dev"
ERROR_TEXT = ""
MKXPZ_VERSION = "2.4.2/c9378cf"
end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module PBDebug
@@log = []
@@ -10,65 +7,28 @@ module PBDebug
rescue
PBDebug.log("")
PBDebug.log("**Exception: #{$!.message}")
backtrace = ""
$!.backtrace.each { |line| backtrace += line + "\r\n" }
PBDebug.log(backtrace)
PBDebug.log("#{$!.backtrace.inspect}")
PBDebug.log("")
pbPrintException($!) # if $INTERNAL
# if $INTERNAL
pbPrintException($!)
# end
PBDebug.flush
end
end
def self.flush
if $DEBUG && $INTERNAL && @@log.length > 0
File.open("Data/debuglog.txt", "a+b") { |f| f.write(@@log.join) }
if $DEBUG && $INTERNAL && @@log.length>0
File.open("Data/debuglog.txt", "a+b") { |f| f.write("#{@@log}") }
end
@@log.clear
end
def self.log(msg)
if $DEBUG && $INTERNAL
echoln msg.gsub("%", "%%")
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
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
@@log.push("#{msg}\r\n")
# if @@log.length>1024
PBDebug.flush
# end
end
end

View File

@@ -1,24 +1,25 @@
#===============================================================================
# To use the console, use the executable explicitly built with the console
# enabled on Windows. On Linux and macOS, just launch the executable directly
# from a terminal.
#===============================================================================
# To use the console, use the executable explicitly built
# with the console enabled on Windows. On Linux and macOS,
# just launch the executable directly from a terminal.
module Console
def self.setup_console
return unless $DEBUG
echoln "GPU Cache Max: #{Bitmap.max_size}"
echoln "-------------------------------------------------------------------------------"
echoln "--------------------------------"
echoln "#{System.game_title} Output Window"
echoln "-------------------------------------------------------------------------------"
echoln "If you can see this window, you are running the game in Debug Mode. This means"
echoln "that you're either playing a debug version of the game, or you're playing from"
echoln "within RPG Maker XP."
echoln "--------------------------------"
echoln "If you are seeing this window, you are running"
echoln "#{System.game_title} in Debug Mode. This means"
echoln "that you're either playing a Debug Version, or"
echoln "you are playing from within RPG Maker XP."
echoln ""
echoln "Closing this window will close the game. If you want to get rid of this window,"
echoln "run the program from the Shell, or download a release version of the game."
echoln "-------------------------------------------------------------------------------"
echoln "Closing this window will close the game. If"
echoln "you want to get rid of this window, run the"
echoln "program from the Shell, or download a Release"
echoln "version."
echoln ""
echoln "--------------------------------"
echoln "Debug Output:"
echoln "-------------------------------------------------------------------------------"
echoln "--------------------------------"
echoln ""
end
@@ -35,9 +36,6 @@ module Console
end
end
#===============================================================================
#
#===============================================================================
module Kernel
def echo(string)
return unless $DEBUG
@@ -45,199 +43,9 @@ module Kernel
end
def echoln(string)
caller_info = caller(1..1).first
file, line, method = caller_info.split(":")
echo "#{file}, #{line}:\t"
echo(string)
echo("\r\n")
end
end
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
end
#===============================================================================
#
#===============================================================================
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
def pbGetExceptionMessage(e,_script="")
emessage = e.message.dup
emessage.force_encoding(Encoding::UTF_8)
case e
when Hangup
if e.is_a?(Hangup)
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 - ", "")
emessage = "File #{filename} not found."
end
@@ -35,28 +18,30 @@ def pbGetExceptionMessage(e, _script = "")
end
def pbPrintException(e)
emessage = pbGetExceptionMessage(e)
emessage = ""
if $EVENTHANGUPMSG && $EVENTHANGUPMSG!=""
emessage = $EVENTHANGUPMSG # Message with map/event ID generated elsewhere
$EVENTHANGUPMSG = nil
else
emessage = pbGetExceptionMessage(e)
end
# begin message formatting
message = "[Pokémon Essentials version #{Essentials::VERSION}]\r\n"
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 += "Message: "
end
message += emessage
message += "Exception: #{e.class}\r\n"
message += "Message: #{emessage}\r\n"
# show last 10/25 lines of backtrace
if !e.is_a?(EventScriptError)
message += "\r\n\r\nBacktrace:\r\n"
backtrace_text = ""
if e.backtrace
maxlength = ($INTERNAL) ? 25 : 10
e.backtrace[0, maxlength].each { |i| backtrace_text += "#{i}\r\n" }
end
backtrace_text.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] } rescue nil
message += backtrace_text
message += "\r\nBacktrace:\r\n"
btrace = ""
if e.backtrace
maxlength = ($INTERNAL) ? 25 : 10
e.backtrace[0, maxlength].each { |i| btrace += "#{i}\r\n" }
end
btrace.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] } rescue nil
message += btrace
# output to log
errorlog = "errorlog.txt"
errorlog = RTP.getSaveFileName("errorlog.txt") if (Object.const_defined?(:RTP) rescue false)
File.open(errorlog, "ab") do |f|
f.write("\r\n=================\r\n\r\n[#{Time.now}]\r\n")
f.write(message)
@@ -69,8 +54,8 @@ def pbPrintException(e)
# output message
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
t = System.uptime
until System.uptime - t >= 0.5
t = System.delta
until (System.delta - t) >= 500000
Input.update
if Input.press?(Input::CTRL)
Input.clipboard = message

View File

@@ -1,9 +1,7 @@
#===============================================================================
# The Kernel module is extended to include the validate method.
#===============================================================================
module Kernel
private
# Used to check whether method arguments are of a given class or respond to a method.
# @param value_pairs [Hash{Object => Class, Array<Class>, Symbol}] value pairs to validate
# @example Validate a class or method

View File

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

View File

@@ -1,16 +1,27 @@
#===============================================================================
# Using mkxp-z v2.4.2/c9378cf - built 2023-07-07
# https://github.com/mkxp-z/mkxp-z/actions/runs/5482601942
#===============================================================================
# Using mkxp-z v2.2.0 - https://gitlab.com/mkxp-z/mkxp-z/-/releases/v2.2.0
$VERBOSE = nil
Font.default_shadow = false if Font.respond_to?(:default_shadow)
Encoding.default_internal = Encoding::UTF_8
Encoding.default_external = Encoding::UTF_8
Graphics.frame_rate = 40
def pbSetWindowText(string)
System.set_window_title(string || System.game_title)
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)
if !$ResizeInitialized
Graphics.resize_screen(Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT)
@@ -24,33 +35,3 @@ def pbSetResizeFactor(factor)
Graphics.center
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

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

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module FileInputMixin
def fgetb
ret = 0
@@ -70,7 +67,7 @@ module FileInputMixin
self.pos = 0
offset = fgetdw >> 3
return 0 if index >= offset
self.pos = (index * 8) + 4
self.pos = index * 8 + 4
return fgetdw
end
@@ -88,9 +85,6 @@ module FileInputMixin
end
end
#===============================================================================
#
#===============================================================================
module FileOutputMixin
def fputb(b)
b &= 0xFF
@@ -114,32 +108,27 @@ module FileOutputMixin
end
end
#===============================================================================
#
#===============================================================================
class File < IO
# unless defined?(debugopen)
# class << self
# alias debugopen open
# end
# end
# def open(f, m = "r")
# debugopen("debug.txt", "ab") { |file| file.write([f, m, Time.now.to_f].inspect + "\r\n") }
# if block_given?
# debugopen(f, m) { |file| yield file }
# else
# return debugopen(f, m)
# end
# end
=begin
unless defined?(debugopen)
class << self
alias debugopen open
end
end
def open(f, m = "r")
debugopen("debug.txt", "ab") { |file| file.write([f, m, Time.now.to_f].inspect + "\r\n") }
if block_given?
debugopen(f, m) { |file| yield file }
else
return debugopen(f, m)
end
end
=end
include FileInputMixin
include FileOutputMixin
end
#===============================================================================
#
#===============================================================================
class StringInput
include FileInputMixin
@@ -148,7 +137,7 @@ class StringInput
end
def each_byte
until eof?
while !eof?
yield getc
end
end
@@ -156,9 +145,6 @@ class StringInput
def binmode; end
end
#===============================================================================
#
#===============================================================================
class StringOutput
include FileOutputMixin
end

View File

@@ -1,35 +1,37 @@
#===============================================================================
#############################
#
# HTTP utility functions
#===============================================================================
def pbPostData(url, postdata, filename = nil, depth = 0)
#
#############################
def pbPostData(url, postdata, filename=nil, depth=0)
if url[/^http:\/\/([^\/]+)(.*)$/]
host = $1
# path = $2
# path = "/" if path.length == 0
path = $2
path = "/" if path.length==0
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
body = postdata.map do |key, value|
body = postdata.map { |key, value|
keyString = key.to_s
valueString = value.to_s
keyString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf("%%%02x", s[0]) }
valueString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf("%%%02x", s[0]) }
keyString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
valueString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
next "#{keyString}=#{valueString}"
end.join("&")
}.join('&')
ret = HTTPLite.post_body(
url,
body,
"application/x-www-form-urlencoded",
{
"Host" => host, # might not be necessary
"Host" => host, # might not be necessary
"Proxy-Connection" => "Close",
"Content-Length" => body.bytesize.to_s,
"Pragma" => "no-cache",
"User-Agent" => userAgent
"Content-Length" => body.bytesize.to_s,
"Pragma" => "no-cache",
"User-Agent" => userAgent
}
) rescue ""
return ret if !ret.is_a?(Hash)
return "" if ret[:status] != 200
return ret[:body] if !filename
File.open(filename, "wb") { |f| f.write(ret[:body]) }
File.open(filename, "wb"){|f|f.write(ret[:body])}
return ""
end
return ""
@@ -38,8 +40,8 @@ end
def pbDownloadData(url, filename = nil, authorization = nil, depth = 0, &block)
headers = {
"Proxy-Connection" => "Close",
"Pragma" => "no-cache",
"User-Agent" => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
"Pragma" => "no-cache",
"User-Agent" => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
}
headers["authorization"] = authorization if authorization
ret = HTTPLite.get(url, headers) rescue ""
@@ -61,7 +63,7 @@ end
def pbDownloadToFile(url, file)
begin
pbDownloadData(url, file)
pbDownloadData(url,file)
rescue
end
end
@@ -77,7 +79,7 @@ end
def pbPostToFile(url, postdata, file)
begin
pbPostData(url, postdata, file)
pbPostData(url, postdata,file)
rescue
end
end

View File

@@ -2,7 +2,7 @@
# class Object
#===============================================================================
class Object
alias full_inspect inspect unless method_defined?(:full_inspect)
alias full_inspect inspect
def inspect
return "#<#{self.class}>"
@@ -23,21 +23,32 @@ end
#===============================================================================
class String
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
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)
string = self
width -= bitmap.text_size("...").width
string_width = 0
text = []
string.scan(/./).each do |char|
for char in string.scan(/./)
wdh = bitmap.text_size(char).width
next if (wdh + string_width) > width
string_width += wdh
@@ -45,30 +56,19 @@ class String
end
text.push("...") if text.length < string.length
new_string = ""
text.each do |char|
for char in text
new_string += char
end
return new_string
end
def numeric?
return !self[/\A[+-]?\d+(?:\.\d+)?\Z/].nil?
end
end
#===============================================================================
# class Numeric
#===============================================================================
class Numeric
# Turns a number into a string formatted like 12,345,678. Some languages use
# different characters as the thousands separator.
# Turns a number into a string formatted like 12,345,678.
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
end
@@ -79,47 +79,17 @@ class Numeric
_INTL("twelve"), _INTL("thirteen"), _INTL("fourteen"), _INTL("fifteen"),
_INTL("sixteen"), _INTL("seventeen"), _INTL("eighteen"), _INTL("nineteen"),
_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
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
#===============================================================================
# class Array
#===============================================================================
class Array
# xor of two arrays
def ^(other)
return (self | other) - (self & other)
def ^(other) # xor of two arrays
return (self|other) - (self&other)
end
def swap(val1, val2)
@@ -130,29 +100,6 @@ class Array
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
#===============================================================================
@@ -164,238 +111,6 @@ module Enumerable
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
#===============================================================================
@@ -417,26 +132,11 @@ class << Kernel
return oldRand(a)
end
elsif a.nil?
return oldRand(b)
return (b) ? oldRand(b) : oldRand(2)
end
return oldRand
end
end
def nil_or_empty?(string)
return string.nil? || !string.is_a?(String) || string.size == 0
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
USE = C
BACK = B
@@ -19,17 +16,18 @@ module Input
def self.update
update_KGC_ScreenCapture
pbScreenCapture if trigger?(Input::F8)
if trigger?(Input::F8)
pbScreenCapture
end
end
end
#===============================================================================
#
#===============================================================================
module Mouse
module_function
# Returns the position of the mouse relative to the game window.
def self.getMousePos(catch_anywhere = false)
return nil unless Input.mouse_in_window || catch_anywhere
def getMousePos(catch_anywhere = false)
return nil unless System.mouse_in_window || catch_anywhere
return Input.mouse_x, Input.mouse_y
end
end

View File

@@ -1,8 +1,8 @@
#==============================================================================#
# Plugin Manager #
# by Marin #
# Support for external plugin scripts by Luka S.J. #
# Tweaked by Maruno #
# support for external plugin scripts by Luka S.J. #
# tweaked by Maruno #
#------------------------------------------------------------------------------#
# Provides a simple interface that allows plugins to require dependencies #
# at specific versions, and to specify incompatibilities between plugins. #
@@ -12,118 +12,181 @@
#------------------------------------------------------------------------------#
# Usage: #
# #
# Each plugin should have its own folder in the "Plugins" folder found in the #
# main directory. The "Plugins" folder is similar in concept to the "PBS" #
# 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 Pokémon Essentials plugin should register itself using the PluginManager. #
# The simplest way to do so, for a plugin without dependencies, is as follows: #
# #
# A plugin's folder must also contain a "meta.txt" file. This file is what #
# makes Essentials recognise that the plugin exists, and contains important #
# information about the plugin; if this file does not exist, the folder's #
# contents are ignored. Each line in this file is a property. #
# PluginManager.register({ #
# :name => "Basic Plugin", #
# :version => "1.0", #
# :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 #
# Version = 1.0 The plugin's version #
# Essentials = 19.1,20 Compatible version(s) of Essentials #
# 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.Y.Z, but the number of digits #
# you use does not matter. You can also use Xa, Xb, Xc, Ya, etc. #
# What matters is that you use it consistently, so that it can be compared. #
# #
# A plugin's version should be in the format X or X.Y or X.Y.Z, where X/Y/Z #
# are numbers. You can also use Xa, Xb, Xc, Ya, etc. What matters is that you #
# use version numbers consistently for your plugin. A later version will be #
# alphanumerically higher than an older version. #
# IF there are multiple people to credit, their names should be in an array. #
# If there is only one credit, it does not need an array: #
# #
# Plugins can interact with each other in several ways, such as requiring #
# another one to exist or by clashing with each other. These interactions are #
# 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. #
# :credits => "Marin" #
# :credits => ["Marin", "Maruno"], #
# #
# 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 #
# "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. #
# Dependency: #
# #
# The "Plugins" folder should be deleted when the game is released. Scripts in #
# there are compiled, but any other files used by a plugin (graphics/audio) #
# should go into other folders and not the plugin's folder. #
# A plugin can require another plugin to be installed in order to work. For #
# example, the "Simple Extension" plugin depends on the above "Basic Plugin" #
# 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 #
# PluginManager. A simple example of registering a plugin is as follows: #
# The Plugin folder is treated like the PBS folder, but for script files for #
# 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({ #
# :name => "Basic Plugin", #
# :version => "1.0", #
# :essentials => "20", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => ["Marin"] #
# }) #
# Scripts must be in .rb files. You should not put any other files into a #
# plugin's folder except for script files and meta.txt. #
# #
# The :link value is optional, but recommended. This will be shown in the #
# message if the PluginManager detects that this plugin needs to be updated. #
# When the game is compiled, scripts in these folders are read and converted #
# 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({ #
# :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 contents of meta.txt are as follows: #
# #
# The example dependencies/conflict are the same as the examples shown above #
# for lines in "meta.txt". :optional_exact is a combination of :exact and #
# :optional, and there is no way to make use of its combined functionality via #
# "meta.txt". #
# Name = Simple Extension #
# Version = 1.0 #
# Requires = Basic Plugin #
# 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. #
#==============================================================================#
module PluginManager
# Holds all registered plugin data.
@@Plugins = {}
#-----------------------------------------------------------------------------
# Registers a plugin and tests its dependencies and incompatibilities.
#-----------------------------------------------------------------------------
def self.register(options)
name = nil
version = nil
essentials = nil
link = nil
dependencies = nil
incompats = nil
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,
# by sorting the keys
keys = options.keys.sort do |a, b|
idx_a = order.index(a) || order.size
idx_b = order.index(b) || order.size
idx_a = order.index(a)
idx_a = order.size if idx_a == -1
idx_b = order.index(b)
idx_b = order.size if idx_b == -1
next idx_a <=> idx_b
end
keys.each do |key|
for key in keys
value = options[key]
case key
when :name # Plugin name
@@ -135,10 +198,10 @@ module PluginManager
end
name = value
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
when :essentials
essentials = value
when :link # Plugin website
if nil_or_empty?(value)
self.error("Plugin link must be a non-empty string.")
@@ -147,13 +210,12 @@ module PluginManager
when :dependencies # Plugin dependencies
dependencies = value
dependencies = [dependencies] if !dependencies.is_a?(Array) || !dependencies[0].is_a?(Array)
value.each do |dep|
case dep
when String # "plugin name"
for dep in value
if dep.is_a?(String) # "plugin name"
if !self.installed?(dep)
self.error("Plugin '#{name}' requires plugin '#{dep}' to be installed above it.")
end
when Array
elsif dep.is_a?(Array)
case dep.size
when 1 # ["plugin name"]
if dep[0].is_a?(String)
@@ -174,8 +236,7 @@ module PluginManager
if self.installed?(dep_name) # Have plugin but lower version
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} or higher, " +
"but the installed version is #{self.version(dep_name)}."
dep_link = self.link(dep_name)
if dep_link
if dep_link = self.link(dep_name)
msg += "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end
self.error(msg)
@@ -217,8 +278,7 @@ module PluginManager
msg = "Plugin '#{name}' requires plugin '#{dep_name}', if installed, to be version #{dep_version}"
msg << " or higher" if !exact
msg << ", but the installed version was #{self.version(dep_name)}."
dep_link = self.link(dep_name)
if dep_link
if dep_link = self.link(dep_name)
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end
self.error(msg)
@@ -228,16 +288,16 @@ module PluginManager
msg = "Plugin '#{name}' requires plugin '#{dep_name}' to be version #{dep_version}"
msg << " or later" if !exact
msg << ", but the installed version was #{self.version(dep_name)}."
dep_link = self.link(dep_name)
if dep_link
if dep_link = self.link(dep_name)
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end
self.error(msg)
else # Don't have plugin
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} "
msg << "or later " if !exact
msg << "or later" if !exact
msg << "to be installed above it."
self.error(msg)
end
self.error(msg)
end
end
end
@@ -245,19 +305,20 @@ module PluginManager
when :incompatibilities # Plugin incompatibilities
incompats = value
incompats = [incompats] if !incompats.is_a?(Array)
incompats.each do |incompat|
for incompat in incompats
if self.installed?(incompat)
self.error("Plugin '#{name}' is incompatible with '#{incompat}'. They cannot both be used at the same time.")
self.error("Plugin '#{name}' is incompatible with '#{incompat}'. " +
"They cannot both be used at the same time.")
end
end
when :credits # Plugin credits
value = [value] if value.is_a?(String)
if value.is_a?(Array)
value.each do |entry|
if entry.is_a?(String)
credits << entry
else
for entry in value
if !entry.is_a?(String)
self.error("Plugin '#{name}'s credits array contains a non-string value.")
else
credits << entry
end
end
else
@@ -267,29 +328,30 @@ module PluginManager
self.error("Invalid plugin registry key '#{key}'.")
end
end
@@Plugins.each_value do |plugin|
if plugin[:incompatibilities]&.include?(name)
self.error("Plugin '#{plugin[:name]}' is incompatible with '#{name}'. They cannot both be used at the same time.")
for plugin in @@Plugins.values
if plugin[:incompatibilities] && plugin[:incompatibilities].include?(name)
self.error("Plugin '#{plugin[:name]}' is incompatible with '#{name}'. " +
"They cannot both be used at the same time.")
end
end
# Add plugin to class variable
@@Plugins[name] = {
:name => name,
:version => version,
:essentials => essentials,
:link => link,
:dependencies => dependencies,
:name => name,
:version => version,
:link => link,
:dependencies => dependencies,
:incompatibilities => incompats,
:credits => credits
:credits => credits
}
end
#-----------------------------------------------------------------------------
# Throws a pure error message without stack trace or any other useless info.
#-----------------------------------------------------------------------------
def self.error(msg)
Graphics.update
t = Thread.new do
Console.echo_error("Plugin Error:\r\n#{msg}")
print("Plugin Error:\r\n#{msg}")
echoln "Plugin Error:\r\n#{msg}"
p "Plugin Error: #{msg}"
Thread.exit
end
while t.status
@@ -297,10 +359,11 @@ module PluginManager
end
Kernel.exit! true
end
#-----------------------------------------------------------------------------
# Returns true if the specified plugin is installed.
# If the version is specified, this version is taken into account.
# If mustequal is true, the version must be a match with the specified version.
#-----------------------------------------------------------------------------
def self.installed?(plugin_name, plugin_version = nil, mustequal = false)
plugin = @@Plugins[plugin_name]
return false if plugin.nil?
@@ -309,97 +372,87 @@ module PluginManager
return true if !mustequal && comparison >= 0
return true if mustequal && comparison == 0
end
#-----------------------------------------------------------------------------
# Returns the string names of all installed plugins.
#-----------------------------------------------------------------------------
def self.plugins
return @@Plugins.keys
end
#-----------------------------------------------------------------------------
# Returns the installed version of the specified plugin.
#-----------------------------------------------------------------------------
def self.version(plugin_name)
return if !installed?(plugin_name)
return @@Plugins[plugin_name][:version]
end
#-----------------------------------------------------------------------------
# Returns the link of the specified plugin.
#-----------------------------------------------------------------------------
def self.link(plugin_name)
return if !installed?(plugin_name)
return @@Plugins[plugin_name][:link]
end
#-----------------------------------------------------------------------------
# Returns the credits of the specified plugin.
#-----------------------------------------------------------------------------
def self.credits(plugin_name)
return if !installed?(plugin_name)
return @@Plugins[plugin_name][:credits]
end
#-----------------------------------------------------------------------------
# Compares two versions given in string form. v1 should be the plugin version
# you actually have, and v2 should be the minimum/desired plugin version.
# Return values:
# 1 if v1 is higher than v2
# 0 if v1 is equal to v2
# -1 if v1 is lower than v2
#-----------------------------------------------------------------------------
def self.compare_versions(v1, v2)
version_chunks1 = v1.split(".")
version_chunks1.each_with_index do |val, i|
next if val != ""
version_chunks1[i] = (i == 0) ? "0" : nil
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
d1 = v1.split("")
d1.insert(0, "0") if d1[0] == "." # Turn ".123" into "0.123"
while d1[-1] == "."; d1 = d1[0..-2]; end # Turn "123." into "123"
d2 = v2.split("")
d2.insert(0, "0") if d2[0] == "." # Turn ".123" into "0.123"
while d2[-1] == "."; d2 = d2[0..-2]; end # Turn "123." into "123"
for i in 0...[d1.size, d2.size].max # Compare each digit in turn
c1 = d1[i]
c2 = d2[i]
if c1
return 1 if !c2
return 1 if c1.to_i(16) > c2.to_i(16)
return -1 if c1.to_i(16) < c2.to_i(16)
else
return -1 if c2
end
# 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
return 0
end
# Formats the error message
#-----------------------------------------------------------------------------
# formats the error message
#-----------------------------------------------------------------------------
def self.pluginErrorMsg(name, script)
e = $!
# 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 += "Error in Plugin: [#{name}]\r\n"
message += "Exception: #{e.class}\r\n"
message += "Message: "
message += e.message
message += "Error in Plugin [#{name}]:\r\n"
message += "#{$!.class} occurred.\r\n"
# go through message content
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
message += "\r\n\r\nBacktrace:\r\n"
e.backtrace[0, 10].each { |i| message += "#{i}\r\n" }
message += "\r\nBacktrace:\r\n"
$!.backtrace[0, 10].each { |i| message += "#{i}\r\n" }
# output to log
errorlog = "errorlog.txt"
errorlog = RTP.getSaveFileName("errorlog.txt") if (Object.const_defined?(:RTP) rescue false)
File.open(errorlog, "ab") do |f|
f.write("\r\n=================\r\n\r\n[#{Time.now}]\r\n")
f.write(message)
@@ -412,8 +465,8 @@ module PluginManager
# output message
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
t = System.uptime
until System.uptime - t >= 0.5
t = System.delta
until (System.delta - t) >= 500000
Input.update
if Input.press?(Input::CTRL)
Input.clipboard = message
@@ -421,26 +474,24 @@ module PluginManager
end
end
end
#-----------------------------------------------------------------------------
# Used to read the metadata file
#-----------------------------------------------------------------------------
def self.readMeta(dir, file)
filename = "#{dir}/#{file}"
meta = {}
# read file
Compiler.pbCompilerEachPreppedLine(filename) do |line, line_no|
Compiler.pbCompilerEachPreppedLine(filename) { |line, line_no|
# split line up into property name and values
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
property = $~[1].upcase
data = $~[2].split(",")
data = $~[2].split(',')
data.each_with_index { |value, i| data[i] = value.strip }
# begin formatting data hash
case property
when "ESSENTIALS"
meta[:essentials] = [] if !meta[:essentials]
data.each { |ver| meta[:essentials].push(ver) }
when "REQUIRES"
when 'REQUIRES'
meta[:dependencies] = [] if !meta[:dependencies]
if data.length < 2 # No version given, just push name of plugin dependency
meta[:dependencies].push(data[0])
@@ -450,33 +501,33 @@ module PluginManager
else # Push dependency type, name and version of plugin dependency
meta[:dependencies].push([data[2].downcase.to_sym, data[0], data[1]])
end
when "EXACT"
when 'EXACT'
next if data.length < 2 # Exact dependencies must have a version given; ignore if not
meta[:dependencies] = [] if !meta[:dependencies]
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
meta[:dependencies] = [] if !meta[:dependencies]
meta[:dependencies].push([:optional, data[0], data[1]])
when "CONFLICTS"
when 'CONFLICTS'
meta[:incompatibilities] = [] if !meta[:incompatibilities]
data.each { |value| meta[:incompatibilities].push(value) if value && !value.empty? }
when "SCRIPTS"
when 'SCRIPTS'
meta[:scripts] = [] if !meta[:scripts]
data.each { |scr| meta[:scripts].push(scr) }
when "CREDITS"
when 'CREDITS'
meta[:credits] = data
when "LINK", "WEBSITE"
when 'LINK', 'WEBSITE'
meta[:link] = data[0]
else
meta[property.downcase.to_sym] = data[0]
end
end
}
# 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)
meta[:scripts] = [] if !meta[:scripts]
# get all script files from plugin Dir
Dir.all(dir).each do |fl|
for fl in Dir.all(dir)
next if !fl.include?(".rb")
meta[:scripts].push(fl.gsub("#{dir}/", ""))
end
@@ -485,24 +536,26 @@ module PluginManager
# return meta hash
return meta
end
#-----------------------------------------------------------------------------
# Get a list of all the plugin directories to inspect
#-----------------------------------------------------------------------------
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
dirs = []
Dir.get("Plugins").each { |d| dirs.push(d) if Dir.safe?(d) }
# return all plugins
return dirs
end
#-----------------------------------------------------------------------------
# Catch any potential loop with dependencies and raise an error
#-----------------------------------------------------------------------------
def self.validateDependencies(name, meta, og = nil)
# exit if no registered dependency
return nil if !meta[name] || !meta[name][:dependencies]
og = [name] if !og
# go through all dependencies
meta[name][:dependencies].each do |dname|
for dname in meta[name][:dependencies]
# clean the name to a simple string
dname = dname[0] if dname.is_a?(Array) && dname.length == 2
dname = dname[1] if dname.is_a?(Array) && dname.length == 3
@@ -514,25 +567,20 @@ module PluginManager
end
return name
end
#-----------------------------------------------------------------------------
# Sort load order based on dependencies (this ends up in reverse order)
#-----------------------------------------------------------------------------
def self.sortLoadOrder(order, plugins)
# go through the load order
order.each do |o|
for o in order
next if !plugins[o] || !plugins[o][:dependencies]
# go through all dependencies
plugins[o][:dependencies].each do |dname|
optional = false
for dname in plugins[o][:dependencies]
# clean the name to a simple string
if dname.is_a?(Array)
optional = [:optional, :optional_exact].include?(dname[0])
dname = dname[dname.length - 2]
end
dname = dname[0] if dname.is_a?(Array) && dname.length == 2
dname = dname[1] if dname.is_a?(Array) && dname.length == 3
# catch missing dependency
if !order.include?(dname)
next if optional
self.error("Plugin '#{o}' requires plugin '#{dname}' to work properly.")
end
self.error("Plugin '#{o}' requires plugin '#{dname}' to work properly.") if !order.include?(dname)
# skip if already sorted
next if order.index(dname) > order.index(o)
# catch looping dependency issue
@@ -542,16 +590,17 @@ module PluginManager
end
return order
end
#-----------------------------------------------------------------------------
# Get the order in which to load plugins
#-----------------------------------------------------------------------------
def self.getPluginOrder
plugins = {}
order = []
# Find all plugin folders that have a meta.txt and add them to the list of
# plugins.
self.listAll.each do |dir|
for dir in self.listAll
# skip if there is no meta file
next if !FileTest.exist?(dir + "/meta.txt")
next if !safeExists?(dir + "/meta.txt")
ndx = order.length
meta = self.readMeta(dir, "meta.txt")
meta[:dir] = dir
@@ -569,53 +618,44 @@ module PluginManager
# sort the load order
return self.sortLoadOrder(order, plugins).reverse, plugins
end
#-----------------------------------------------------------------------------
# Check if plugins need compiling
#-----------------------------------------------------------------------------
def self.needCompiling?(order, plugins)
# fixed actions
return false if !$DEBUG || FileTest.exist?("Game.rgssad")
return true if $full_compile
return true if !FileTest.exist?("Data/PluginScripts.rxdata")
return false if !$DEBUG || safeExists?("Game.rgssad")
return true if !safeExists?("Data/PluginScripts.rxdata")
Input.update
# Force compiling if holding Shift or Ctrl
return true if Input.press?(Input::SHIFT) || Input.press?(Input::CTRL)
# 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
return true if Input.press?(Input::CTRL)
# analyze whether or not to push recompile
mtime = File.mtime("Data/PluginScripts.rxdata")
order.each do |o|
for o in order
# go through all the registered plugin scripts
scr = plugins[o][:scripts]
dir = plugins[o][:dir]
scr.each do |sc|
for sc in scr
return true if File.mtime("#{dir}/#{sc}") > mtime
end
return true if File.mtime("#{dir}/meta.txt") > mtime
end
return false
end
#-----------------------------------------------------------------------------
# Check if plugins need compiling
#-----------------------------------------------------------------------------
def self.compilePlugins(order, plugins)
Console.echo_li("Compiling plugin scripts...")
echo 'Compiling plugin scripts...'
scripts = []
# go through the entire order one by one
order.each do |o|
for o in order
# save name, metadata and scripts array
meta = plugins[o].clone
meta.delete(:scripts)
meta.delete(:dir)
dat = [o, meta, []]
# iterate through each file to deflate
plugins[o][:scripts].each do |file|
File.open("#{plugins[o][:dir]}/#{file}", "rb") do |f|
for file in plugins[o][:scripts]
File.open("#{plugins[o][:dir]}/#{file}", 'rb') do |f|
dat[2].push([file, Zlib::Deflate.deflate(f.read)])
end
end
@@ -623,47 +663,41 @@ module PluginManager
scripts.push(dat)
end
# 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
GC.start
Console.echo_done(true)
echoln ' done.'
echoln ''
end
#-----------------------------------------------------------------------------
# Check if plugins need compiling
#-----------------------------------------------------------------------------
def self.runPlugins
Console.echo_h1("Checking plugins")
# get the order of plugins to interpret
order, plugins = self.getPluginOrder
# compile if necessary
if self.needCompiling?(order, plugins)
self.compilePlugins(order, plugins)
else
Console.echoln_li("Plugins were not compiled")
end
self.compilePlugins(order, plugins) if self.needCompiling?(order, plugins)
# load plugins
scripts = load_data("Data/PluginScripts.rxdata")
echoed_plugins = []
scripts.each do |plugin|
for plugin in scripts
# get the required data
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
self.register(meta)
# go through each script and interpret
script.each do |scr|
for scr in script
# turn code into plaintext
code = Zlib::Inflate.inflate(scr[1]).force_encoding(Encoding::UTF_8)
# get rid of tabs
code.gsub!("\t", " ")
# construct filename
sname = scr[0].gsub("\\", "/").split("/")[-1]
sname = scr[0].gsub("\\","/").split("/")[-1]
fname = "[#{name}] #{sname}"
# try to run the code
begin
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)
rescue Exception # format error message to display
self.pluginErrorMsg(name, sname)
@@ -671,24 +705,7 @@ module PluginManager
end
end
end
if scripts.length > 0
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
echoln '' if !echoed_plugins.empty?
end
#-----------------------------------------------------------------------------
end

View File

@@ -1,103 +1,534 @@
#===============================================================================
# Additions to class Sprite that allows class AnimationContainerSprite to attach
# overworld animations to itself.
#===============================================================================
class SpriteAnimation
@@_animations = []
@@_reference_count = {}
def initialize(sprite)
@sprite = sprite
end
%w[
x y ox oy viewport flash src_rect opacity tone
].each_with_index do |s, _i|
eval <<-__END__
def #{s}(*arg)
@sprite.#{s}(*arg)
end
__END__
end
def self.clear
@@_animations.clear
end
def dispose
dispose_animation
dispose_loop_animation
end
def animation(animation, hit, height = 3)
dispose_animation
@_animation = animation
return if @_animation == nil
@_animation_hit = hit
@_animation_height = height
@_animation_duration = @_animation.frame_max
fr = 20
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
fr = $~[1].to_i
end
@_animation_frame_skip = Graphics.frame_rate / fr
animation_name = @_animation.animation_name
animation_hue = @_animation.animation_hue
bitmap = pbGetAnimation(animation_name, animation_hue)
if @@_reference_count.include?(bitmap)
@@_reference_count[bitmap] += 1
else
@@_reference_count[bitmap] = 1
end
@_animation_sprites = []
if @_animation.position != 3 || !@@_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
def dispose_animation
return if @_animation_sprites == nil
sprite = @_animation_sprites[0]
if sprite != nil
@@_reference_count[sprite.bitmap] -= 1
if @@_reference_count[sprite.bitmap] == 0
sprite.bitmap.dispose
end
end
for sprite in @_animation_sprites
sprite.dispose
end
@_animation_sprites = nil
@_animation = nil
end
def dispose_loop_animation
return if @_loop_animation_sprites == nil
sprite = @_loop_animation_sprites[0]
if sprite != nil
@@_reference_count[sprite.bitmap] -= 1
if @@_reference_count[sprite.bitmap] == 0
sprite.bitmap.dispose
end
end
for sprite in @_loop_animation_sprites
sprite.dispose
end
@_loop_animation_sprites = nil
@_loop_animation = nil
end
def active?
return @_loop_animation_sprites != nil || @_animation_sprites != nil
end
def effect?
return @_animation_duration > 0
end
def update
if @_animation != nil
quick_update = true
if Graphics.frame_count % @_animation_frame_skip == 0
@_animation_duration -= 1
quick_update = false
end
update_animation(quick_update)
end
if @_loop_animation != nil
quick_update = (Graphics.frame_count % @_loop_animation_frame_skip != 0)
update_loop_animation(quick_update)
if !quick_update
@_loop_animation_index += 1
@_loop_animation_index %= @_loop_animation.frame_max
end
end
end
def update_animation(quick_update = false)
if @_animation_duration <= 0
dispose_animation
return
end
frame_index = @_animation.frame_max - @_animation_duration
cell_data = @_animation.frames[frame_index].cell_data
position = @_animation.position
animation_set_sprites(@_animation_sprites, cell_data, position, quick_update)
return if quick_update
for timing in @_animation.timings
next if timing.frame != frame_index
animation_process_timing(timing, @_animation_hit)
end
end
def update_loop_animation(quick_update = false)
frame_index = @_loop_animation_index
cell_data = @_loop_animation.frames[frame_index].cell_data
position = @_loop_animation.position
animation_set_sprites(@_loop_animation_sprites, cell_data, position, quick_update)
return if quick_update
for timing in @_loop_animation.timings
next if timing.frame != frame_index
animation_process_timing(timing, true)
end
end
def animation_set_sprites(sprites, cell_data, position, quick_update = false)
sprite_x = 320
sprite_y = 240
if position == 3
if self.viewport != nil
sprite_x = self.viewport.rect.width / 2
sprite_y = self.viewport.rect.height - 160
end
else
sprite_x = self.x - self.ox + self.src_rect.width / 2
sprite_y = self.y - self.oy
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
def x=(x)
sx = x - self.x
return if sx == 0
if @_animation_sprites != nil
for i in 0..15
@_animation_sprites[i].x += sx
end
end
if @_loop_animation_sprites != nil
for i in 0..15
@_loop_animation_sprites[i].x += sx
end
end
end
def y=(y)
sy = y - self.y
return if sy == 0
if @_animation_sprites != nil
for i in 0..15
@_animation_sprites[i].y += sy
end
end
if @_loop_animation_sprites != nil
for i in 0..15
@_loop_animation_sprites[i].y += sy
end
end
end
end
module RPG
class Sprite < ::Sprite
def initialize(viewport = nil)
super(viewport)
@_whiten_duration = 0
@_appear_duration = 0
@_escape_duration = 0
@_collapse_duration = 0
@_damage_duration = 0
@_animation_duration = 0
@_animation_frame = 0
@animations = []
@_blink = false
@animations = []
@loopAnimations = []
end
def dispose
dispose_damage
dispose_animation
dispose_loop_animation
super
end
def dispose_animation
@animations.each { |a| a&.dispose_animation }
@animations.clear
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 dispose_loop_animation
@loopAnimations.each { |a| a&.dispose_loop_animation }
@loopAnimations.clear
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 x=(x)
@animations.each { |a| a.x = x if a }
@loopAnimations.each { |a| a.x = x if a }
super
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 y=(y)
@animations.each { |a| a.y = y if a }
@loopAnimations.each { |a| a.y = y if a }
super
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)
array.length.times do |i|
next if array[i]&.active?
for i in 0...array.length
next if array[i] && array[i].active?
array[i] = anim
return
end
array.push(anim)
end
def animation(animation, hit, height = 3, no_tone = false)
def animation(animation, hit, height = 3)
anim = SpriteAnimation.new(self)
anim.animation(animation, hit, height, no_tone)
pushAnimation(@animations, anim)
anim.animation(animation,hit,height)
pushAnimation(@animations,anim)
end
def loop_animation(animation)
anim = SpriteAnimation.new(self)
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
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
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
super
@animations.each { |a| a.update }
@loopAnimations.each { |a| a.update }
if @_whiten_duration > 0
@_whiten_duration -= 1
self.color.alpha = 128 - (16 - @_whiten_duration) * 10
end
if @_appear_duration > 0
@_appear_duration -= 1
self.opacity = (16 - @_appear_duration) * 16
end
if @_escape_duration > 0
@_escape_duration -= 1
self.opacity = 256 - (32 - @_escape_duration) * 10
end
if @_collapse_duration > 0
@_collapse_duration -= 1
self.opacity = 256 - (48 - @_collapse_duration) * 6
end
if @_damage_duration > 0
@_damage_duration -= 1
case @_damage_duration
when 38..39
@_damage_sprite.y -= 4
when 36..37
@_damage_sprite.y -= 2
when 34..35
@_damage_sprite.y += 2
when 28..33
@_damage_sprite.y += 4
end
@_damage_sprite.opacity = 256 - (12 - @_damage_duration) * 32
if @_damage_duration == 0
dispose_damage
end
end
for a in @animations
a.update
end
for a in @loopAnimations
a.update
end
if @_blink
@_blink_count = (@_blink_count + 1) % 32
if @_blink_count < 16
alpha = (16 - @_blink_count) * 6
else
alpha = (@_blink_count - 16) * 6
end
self.color.set(255, 255, 255, alpha)
end
SpriteAnimation.clear
end
def update_animation
for a in @animations
a.update_animation if a && a.active?
end
end
def update_loop_animation
for a in @loopAnimations
a.update_loop_animation if a && a.active?
end
end
def x=(x)
for a in @animations
a.x = x if a
end
for a in @loopAnimations
a.x = x if a
end
super
end
def y=(y)
for a in @animations
a.y = y if a
end
for a in @loopAnimations
a.y = y if a
end
super
end
end
end
#===============================================================================
# A version of class Sprite that allows its coordinates to be floats rather than
# integers.
#===============================================================================
class FloatSprite < Sprite
def x; return @float_x; end
def y; return @float_y; end
def x=(value)
@float_x = value
super
end
def y=(value)
@float_y = value
super
end
end

View File

@@ -1,49 +1,39 @@
#===============================================================================
#
#===============================================================================
module Settings
#-----------------------------------------------------------------------------
# Turn order and disobedience
#-----------------------------------------------------------------------------
# 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 turn order is recalculated after a Pokémon Mega Evolves.
RECALCULATE_TURN_ORDER_AFTER_MEGA_EVOLUTION = (MECHANICS_GENERATION >= 7)
# Whether turn order is recalculated after a Pokémon's Speed stat changes.
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
# damage and have 5 stages as in Gen 5 (false). Also determines whether
# critical hit rate can be copied by Transform/Psych Up.
NEW_CRITICAL_HIT_RATE_MECHANICS = (MECHANICS_GENERATION >= 6)
NEW_CRITICAL_HIT_RATE_MECHANICS = (MECHANICS_GENERATION >= 6)
# Whether several effects apply relating to a Pokémon's type:
# * Electric-type immunity to paralysis
# * Ghost-type immunity to being trapped
# * Grass-type immunity to powder moves and Effect Spore
# * Poison-type Pokémon can't miss when using Toxic
MORE_TYPE_EFFECTS = (MECHANICS_GENERATION >= 6)
# The minimum number of Gym Badges required to boost each stat of a player's
MORE_TYPE_EFFECTS = (MECHANICS_GENERATION >= 6)
# Whether weather caused by an ability lasts 5 rounds (true) or forever (false).
FIXED_DURATION_WEATHER_FROM_ABILITY = (MECHANICS_GENERATION >= 6)
#=============================================================================
# Whether X items (X Attack, etc.) raise their stat by 2 stages (true) or 1
# (false).
X_STAT_ITEMS_RAISE_BY_TWO_STAGES = (MECHANICS_GENERATION >= 7)
# Whether some Poké Balls have catch rate multipliers from Gen 7 (true) or
# from earlier generations (false).
NEW_POKE_BALL_CATCH_RATES = (MECHANICS_GENERATION >= 7)
# Whether Soul Dew powers up Psychic and Dragon-type moves by 20% (true) or
# raises the holder's Special Attack and Special Defense by 50% (false).
SOUL_DEW_POWERS_UP_TYPES = (MECHANICS_GENERATION >= 7)
#=============================================================================
# The minimum number of 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
@@ -51,94 +41,38 @@ module Settings
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).
FIXED_DURATION_WEATHER_FROM_ABILITY = (MECHANICS_GENERATION >= 6)
# Whether X items (X Attack, etc.) raise their stat by 2 stages (true) or 1
# (false).
X_STAT_ITEMS_RAISE_BY_TWO_STAGES = (MECHANICS_GENERATION >= 7)
# Whether some Poké Balls have catch rate multipliers from Gen 7 (true) or
# from earlier generations (false).
NEW_POKE_BALL_CATCH_RATES = (MECHANICS_GENERATION >= 7)
# Whether Soul Dew powers up Psychic and Dragon-type moves by 20% (true) or
# raises the holder's Special Attack and Special Defense by 50% (false).
SOUL_DEW_POWERS_UP_TYPES = (MECHANICS_GENERATION >= 7)
# An array of items which act as Mega Rings for the player (NPCs don't need a
# Mega Ring item, just a Mega Stone held by their Pokémon).
MEGA_RINGS = [:MEGARING, :MEGABRACELET, :MEGACUFF, :MEGACHARM]
# The Game Switch which, while ON, prevents all Pokémon in battle from Mega
# Evolving even if they otherwise could.
NO_MEGA_EVOLUTION = 34
#-----------------------------------------------------------------------------
# Affection
#-----------------------------------------------------------------------------
# Whether Pokémon with high happiness will gain more Exp from battles, have a
# chance of avoiding/curing negative effects by themselves, resisting
# fainting, etc.
AFFECTION_EFFECTS = false
# Whether a Pokémon's happiness is limited to 179, and can only be increased
# further with friendship-raising berries. Related to AFFECTION_EFFECTS by
# default because affection effects only start applying above a happiness of
# 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
# based on a total of 600+ species (i.e. that many species need to be caught
# to provide the greatest critical capture chance of 2.5x), and there may be
# fewer species in your game.
ENABLE_CRITICAL_CAPTURES = (MECHANICS_GENERATION >= 5)
# Whether the player is asked what to do with a newly caught Pokémon if their
# 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
# the gainer's level.
SCALED_EXP_FORMULA = (MECHANICS_GENERATION == 5 || MECHANICS_GENERATION >= 7)
SCALED_EXP_FORMULA = (MECHANICS_GENERATION == 5 || MECHANICS_GENERATION >= 7)
# Whether the Exp gained from beating a Pokémon should be divided equally
# between each participant (true), or whether each participant should gain
# that much Exp (false). This also applies to Exp gained via the Exp Share
# (held item version) being distributed to all Exp Share holders.
SPLIT_EXP_BETWEEN_GAINERS = (MECHANICS_GENERATION <= 5)
# Whether the Exp gained from beating a Pokémon is multiplied by 1.5 if that
# Pokémon is owned by another trainer.
MORE_EXP_FROM_TRAINER_POKEMON = (MECHANICS_GENERATION <= 6)
# Whether a Pokémon holding a Power item gains 8 (true) or 4 (false) EVs in
# the relevant stat.
MORE_EVS_FROM_POWER_ITEMS = (MECHANICS_GENERATION >= 7)
SPLIT_EXP_BETWEEN_GAINERS = (MECHANICS_GENERATION <= 5)
# Whether the critical capture mechanic applies. Note that its calculation is
# based on a total of 600+ species (i.e. that many species need to be caught
# to provide the greatest critical capture chance of 2.5x), and there may be
# fewer species in your game.
ENABLE_CRITICAL_CAPTURES = (MECHANICS_GENERATION >= 5)
# Whether Pokémon gain Exp for capturing a Pokémon.
GAIN_EXP_FOR_CAPTURE = (MECHANICS_GENERATION >= 6)
#-----------------------------------------------------------------------------
# End of battle
#-----------------------------------------------------------------------------
CAN_FORFEIT_TRAINER_BATTLES = (MECHANICS_GENERATION >= 9)
# The Game Switch which, while ON, prevents the player from losing money if
GAIN_EXP_FOR_CAPTURE = (MECHANICS_GENERATION >= 6)
# The Game Switch which, whie ON, prevents the player from losing money if
# 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
# regardless of the outcome (true), or only after battles the player won (false).
CHECK_EVOLUTION_AFTER_ALL_BATTLES = (MECHANICS_GENERATION >= 6)
# Whether fainted Pokémon can try to evolve after a battle.
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

View File

@@ -1,30 +1,19 @@
#===============================================================================
# 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
# between Essentials and game versions.
# @see SaveData.register
# @see SaveData.register_conversion
#===============================================================================
module SaveData
DIRECTORY = (File.directory?(System.data_directory)) ? System.data_directory : "./"
FILENAME_REGEX = /Game(\d*)\.rxdata$/
# Contains the file path of the save file.
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?
return !all_save_files.empty?
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
return File.file?(FILE_PATH)
end
# Fetches the save data from the given file.
@@ -54,9 +43,9 @@ module SaveData
def self.read_from_file(file_path)
validate file_path => String
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)
File.open(file_path, "wb") { |file| Marshal.dump(save_data, file) }
File.open(file_path, 'wb') { |file| Marshal.dump(save_data, file) }
end
return save_data
end
@@ -68,19 +57,14 @@ module SaveData
def self.save_to_file(file_path)
validate file_path => String
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
# Deletes the save file (and a possible .bak backup file if one exists)
# @raise [Error::ENOENT]
def self.delete_file(filename)
File.delete(DIRECTORY + filename)
File.delete(DIRECTORY + filename + ".bak") if File.file?(DIRECTORY + filename + ".bak")
end
def self.filename_from_index(index = 0)
return "Game.rxdata" if index <= 0
return "Game#{index}.rxdata"
def self.delete_file
File.delete(FILE_PATH)
File.delete(FILE_PATH + '.bak') if File.file?(FILE_PATH + '.bak')
end
# Converts the pre-v19 format data to the new format.
@@ -95,4 +79,19 @@ module SaveData
end
return hash
end
# Moves a save file from the old Saved Games folder to the new
# location specified by {FILE_PATH}. Does nothing if a save file
# already exists in {FILE_PATH}.
def self.move_old_windows_save
return if File.file?(FILE_PATH)
game_title = System.game_title.gsub(/[^\w ]/, '_')
home = ENV['HOME'] || ENV['HOMEPATH']
return if home.nil?
old_location = File.join(home, 'Saved Games', game_title)
return unless File.directory?(old_location)
old_file = File.join(old_location, 'Game.rxdata')
return unless File.file?(old_file)
File.move(old_file, FILE_PATH)
end
end

View File

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

View File

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

View File

@@ -1,27 +1,41 @@
#===============================================================================
# Contains the save values defined in Essentials by default.
#===============================================================================
SaveData.register(:player) do
ensure_class :Player
save_value { $player }
load_value { |value| $player = value }
new_game_value { Player.new("Unnamed", GameData::TrainerType.keys.first) }
save_value { $Trainer }
load_value { |value| $Trainer = value }
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
SaveData.register(:game_system) do
load_in_bootup
ensure_class :Game_System
save_value { $game_system }
load_value { |value| $game_system = value }
new_game_value { Game_System.new }
from_old_format { |old_format| old_format[2] }
end
SaveData.register(:pokemon_system) do
load_in_bootup # Because this contains values for the Options screen
load_in_bootup
ensure_class :PokemonSystem
save_value { $PokemonSystem }
load_value { |value| $PokemonSystem = value }
new_game_value { PokemonSystem.new }
from_old_format { |old_format| old_format[3] }
end
SaveData.register(:switches) do
@@ -29,6 +43,7 @@ SaveData.register(:switches) do
save_value { $game_switches }
load_value { |value| $game_switches = value }
new_game_value { Game_Switches.new }
from_old_format { |old_format| old_format[5] }
end
SaveData.register(:variables) do
@@ -36,6 +51,7 @@ SaveData.register(:variables) do
save_value { $game_variables }
load_value { |value| $game_variables = value }
new_game_value { Game_Variables.new }
from_old_format { |old_format| old_format[6] }
end
SaveData.register(:self_switches) do
@@ -43,6 +59,7 @@ SaveData.register(:self_switches) do
save_value { $game_self_switches }
load_value { |value| $game_self_switches = value }
new_game_value { Game_SelfSwitches.new }
from_old_format { |old_format| old_format[7] }
end
SaveData.register(:game_screen) do
@@ -50,12 +67,14 @@ SaveData.register(:game_screen) do
save_value { $game_screen }
load_value { |value| $game_screen = value }
new_game_value { Game_Screen.new }
from_old_format { |old_format| old_format[8] }
end
SaveData.register(:map_factory) do
ensure_class :PokemonMapFactory
save_value { $map_factory }
load_value { |value| $map_factory = value }
save_value { $MapFactory }
load_value { |value| $MapFactory = value }
from_old_format { |old_format| old_format[9] }
end
SaveData.register(:game_player) do
@@ -63,6 +82,7 @@ SaveData.register(:game_player) do
save_value { $game_player }
load_value { |value| $game_player = value }
new_game_value { Game_Player.new }
from_old_format { |old_format| old_format[10] }
end
SaveData.register(:global_metadata) do
@@ -70,6 +90,7 @@ SaveData.register(:global_metadata) do
save_value { $PokemonGlobal }
load_value { |value| $PokemonGlobal = value }
new_game_value { PokemonGlobalMetadata.new }
from_old_format { |old_format| old_format[11] }
end
SaveData.register(:map_metadata) do
@@ -77,13 +98,15 @@ SaveData.register(:map_metadata) do
save_value { $PokemonMap }
load_value { |value| $PokemonMap = value }
new_game_value { PokemonMapMetadata.new }
from_old_format { |old_format| old_format[12] }
end
SaveData.register(:bag) do
ensure_class :PokemonBag
save_value { $bag }
load_value { |value| $bag = value }
save_value { $PokemonBag }
load_value { |value| $PokemonBag = value }
new_game_value { PokemonBag.new }
from_old_format { |old_format| old_format[13] }
end
SaveData.register(:storage_system) do
@@ -91,25 +114,22 @@ SaveData.register(:storage_system) do
save_value { $PokemonStorage }
load_value { |value| $PokemonStorage = value }
new_game_value { PokemonStorage.new }
from_old_format { |old_format| old_format[14] }
end
SaveData.register(:essentials_version) do
load_in_bootup
ensure_class :String
save_value { Essentials::VERSION }
load_value { |value| $save_engine_version = value }
load_value { |value| $SaveVersion = value }
new_game_value { Essentials::VERSION }
from_old_format { |old_format| old_format[15] }
end
SaveData.register(:game_version) do
load_in_bootup
ensure_class :String
save_value { Settings::GAME_VERSION }
load_value { |value| $save_game_version = value }
load_value { |value| $game_version = value }
new_game_value { Settings::GAME_VERSION }
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 @@
#===============================================================================
# Conversions required to support backwards compatibility with old save files
# (within reason).
#===============================================================================
# Contains conversions defined in Essentials by default.
SaveData.register_conversion(:v21_replace_phone_data) do
essentials_version 21
display_title "Updating Phone data format"
SaveData.register_conversion(:v19_define_versions) do
essentials_version 19
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|
if !global.phone
global.instance_eval do
@phone = Phone.new
@phoneTime = nil # Don't bother using this
if @phoneNumbers
@phoneNumbers.each do |contact|
if contact.length > 4
# 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
# Non-trainer
@phone.add(contact[3], contact[2], contact[1])
end
end
@phoneNumbers = nil
end
end
end
end
end
#===============================================================================
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
global.bridge ||= 0
global.encounter_version ||= 0
if global.pcItemStorage
global.pcItemStorage.items.each_with_index do |slot, i|
item_data = GameData::Item.try_get(slot[0])
if item_data
slot[0] = item_data.id
else
@lower_encounter_rate = @blackFluteUsed
global.pcItemStorage.items[i] = nil
end
@blackFluteUsed = nil
end
if !@whiteFluteUsed.nil?
if Settings::FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS
@lower_level_wild_pokemon = @whiteFluteUsed
else
@higher_encounter_rate = @whiteFluteUsed
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
@whiteFluteUsed = nil
end
end
if global.triads
global.triads.items.each do |card|
card[0] = GameData::Species.get(card[0]).id if card && card[0] && card[0] != 0
end
end
end
end
#===============================================================================
SaveData.register_conversion(:v21_add_bump_stat) do
essentials_version 21
display_title "Adding a bump stat"
to_value :stats do |stats|
stats.instance_eval do
@bump_count = 0 if !@bump_count
SaveData.register_conversion(:v19_1_fix_phone_contacts) do
essentials_version 19.1
display_title 'Fixing phone contacts data'
to_value :global_metadata do |global|
global.phoneNumbers.each do |contact|
contact[1] = GameData::TrainerType.get(contact[1]).id if contact && contact.length == 8
end
end
end
#===============================================================================
SaveData.register_conversion(:v22_add_adventure_magic_number) do
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"
SaveData.register_conversion(:v19_convert_bag) do
essentials_version 19
display_title 'Converting item IDs in Bag'
to_value :bag do |bag|
bag.instance_eval do
all_pockets = GameData::BagPocket.all_pockets
if @pockets.is_a?(Array)
new_pockets = {}
all_pockets.each { |pckt| new_pockets[pckt] = [] }
@pockets.each_with_index do |value, i|
next if i == 0
value.each do |item|
pckt = GameData::Item.get(item[0]).bag_pocket
new_pockets[pckt].push(item)
for pocket in self.pockets
pocket.each_with_index do |item, i|
next if !item || !item[0] || item[0] == 0
item_data = GameData::Item.try_get(item[0])
if item_data
item[0] = item_data.id
else
pocket[i] = nil
end
end
@pockets = new_pockets
pocket.compact!
end
if @last_viewed_pocket.is_a?(Integer)
@last_viewed_pocket = all_pockets[@last_viewed_pocket - 1] || all_pockets.first
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
self.registeredIndex # Just to ensure this data exists
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
@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
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.
#===============================================================================
module Game
module_function
# Initializes various global variables and loads the game data.
def initialize
def self.initialize
$PokemonTemp = PokemonTemp.new
$game_temp = Game_Temp.new
$game_system = Game_System.new
$data_animations = load_data("Data/Animations.rxdata")
$data_tilesets = load_data("Data/Tilesets.rxdata")
$data_common_events = load_data("Data/CommonEvents.rxdata")
$data_system = load_data("Data/System.rxdata")
$data_animations = load_data('Data/Animations.rxdata')
$data_tilesets = load_data('Data/Tilesets.rxdata')
$data_common_events = load_data('Data/CommonEvents.rxdata')
$data_system = load_data('Data/System.rxdata')
pbLoadBattleAnimations
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)
raise _INTL("No starting position was set in the map editor.")
raise _INTL('No starting position was set in the map editor.')
end
end
# Loads bootup data from save file (if it exists) or creates bootup data (if
# it doesn't).
def set_up_system
SaveData.initialize_bootup_values
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
else
SaveData.load_bootup_values(save_data)
end
# Set resize factor
pbSetResizeFactor([$PokemonSystem.screensize, 4].min)
# Set language (and choose language if there is no save file)
if !Settings::LANGUAGES.empty?
$PokemonSystem.language = pbChooseLanguage if !SaveData.exists? && Settings::LANGUAGES.length >= 2
MessageTypes.load_message_files(Settings::LANGUAGES[$PokemonSystem.language][1])
if Settings::LANGUAGES.length >= 2
$PokemonSystem.language = pbChooseLanguage if save_data.empty?
pbLoadMessages('Data/' + Settings::LANGUAGES[$PokemonSystem.language][1])
end
end
# Called when starting a new game. Initializes global variables
# and transfers the player into the map scene.
def start_new
# Essentials 21 renamed the global variable $Trainer
# It's still used everywhere in events, global events so this makes things simpler
if $game_map&.events
def self.start_new
if $game_map && $game_map.events
$game_map.events.each_value { |event| event.clear_starting }
end
$game_temp.common_event_id = 0 if $game_temp
pbMapInterpreter&.clear
pbMapInterpreter&.setup(nil, 0, 0)
$PokemonTemp.begunNewGame = true
$scene = Scene_Map.new
SaveData.load_new_game_values
$game_temp.last_uptime_refreshed_play_time = System.uptime
$stats.play_sessions += 1
$map_factory = PokemonMapFactory.new($data_system.start_map_id)
$MapFactory = PokemonMapFactory.new($data_system.start_map_id)
$game_player.moveto($data_system.start_x, $data_system.start_y)
$game_player.refresh
$PokemonEncounters = PokemonEncounters.new
@@ -59,17 +58,10 @@ module Game
# Loads the game from the given save data and starts the map scene.
# @param save_data [Hash] hash containing the save data
# @raise [SaveData::InvalidValueError] if an invalid value is being loaded
def 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
def self.load(save_data)
validate save_data => Hash
SaveData.load_all_values(save_data)
$game_temp.last_uptime_refreshed_play_time = System.uptime
$stats.play_sessions += 1
load_map
self.load_map
pbAutoplayOnSave
$game_map.update
$PokemonMap.updateMap
@@ -77,30 +69,32 @@ module Game
end
# Loads and validates the map. Called when loading a saved game.
def load_map
$game_map = $map_factory.map
def self.load_map
$game_map = $MapFactory.map
magic_number_matches = ($game_system.magic_number == $data_system.magic_number)
if !magic_number_matches || $PokemonGlobal.safesave
pbMapInterpreter.setup(nil, 0) if pbMapInterpreterRunning?
if pbMapInterpreterRunning?
pbMapInterpreter.setup(nil, 0)
end
begin
$map_factory.setup($game_map.map_id)
$MapFactory.setup($game_map.map_id)
rescue Errno::ENOENT
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
exit unless map
$map_factory.setup(map[0])
$MapFactory.setup(map[0])
$game_player.moveto(map[1], map[2])
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
$game_player.center($game_player.x, $game_player.y)
else
$map_factory.setMapChanged($game_map.map_id)
$MapFactory.setMapChanged($game_map.map_id)
end
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
$PokemonEncounters = PokemonEncounters.new
$PokemonEncounters.setup($game_map.map_id)
@@ -108,21 +102,17 @@ module Game
end
# 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 directory [String] the folder to put the save file in
# @param save_file [String] the save file path
# @param safe [Boolean] whether $PokemonGlobal.safesave should be set to true
# @return [Boolean] whether the operation was successful
# @raise [SaveData::InvalidValueError] if an invalid value is being saved
def save(index, directory = SaveData::DIRECTORY, safe: false)
validate index => Integer, directory => String, safe => [TrueClass, FalseClass]
filename = SaveData.filename_from_index(index)
def self.save(save_file = SaveData::FILE_PATH, safe: false)
validate save_file => String, safe => [TrueClass, FalseClass]
$PokemonGlobal.safesave = safe
$game_system.save_count += 1
$game_system.magic_number = $data_system.magic_number
$stats.set_time_last_saved
$stats.save_filename_number = index
begin
SaveData.save_to_file(directory + filename)
SaveData.save_to_file(save_file)
Graphics.frame_reset
rescue IOError, SystemCallError
$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
attr_reader :spritesetGlobal
attr_reader :map_renderer
def spriteset(map_id = -1)
return @spritesets[map_id] if map_id > 0 && @spritesets[map_id]
@spritesets.each_value do |i|
return i if i.map == $game_map
def spriteset
for i in @spritesets.values
return i if i.map==$game_map
end
return @spritesets.values[0]
end
def createSpritesets
@map_renderer = TilemapRenderer.new(Spriteset_Map.viewport) if !@map_renderer || @map_renderer.disposed?
@spritesetGlobal = Spriteset_Global.new if !@spritesetGlobal
@spritesetGlobal = Spriteset_Global.new
@spritesets = {}
$map_factory.maps.each do |map|
for map in $MapFactory.maps
@spritesets[map.map_id] = Spriteset_Map.new(map)
end
$map_factory.setSceneStarted(self)
updateSpritesets(true)
$MapFactory.setSceneStarted(self)
updateSpritesets
end
def createSingleSpriteset(map)
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)
$map_factory.setSceneStarted(self)
updateSpritesets(true)
$MapFactory.setSceneStarted(self)
updateSpritesets
end
def disposeSpritesets
return if !@spritesets
@spritesets.each_key do |i|
for i in @spritesets.keys
next if !@spritesets[i]
@spritesets[i].dispose
@spritesets[i] = nil
end
@spritesets.clear
@spritesets = {}
end
def dispose
disposeSpritesets
@map_renderer.dispose
@map_renderer = nil
@spritesetGlobal.dispose
@spritesetGlobal = nil
end
@@ -57,24 +50,26 @@ class Scene_Map
return if !playingBGM && !playingBGS
map = load_data(sprintf("Data/Map%03d.rxdata", mapid))
if playingBGM && map.autoplay_bgm
test_filename = map.bgm.name
test_filename += "_n" if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/" + test_filename + "_n")
pbBGMFade(0.8) if playingBGM.name != test_filename
if (PBDayNight.isNight? rescue false)
pbBGMFade(0.8) if playingBGM.name!=map.bgm.name && playingBGM.name!=map.bgm.name+"_n"
else
pbBGMFade(0.8) if playingBGM.name!=map.bgm.name
end
end
if playingBGS && map.autoplay_bgs && playingBGS.name != map.bgs.name
pbBGMFade(0.8)
if playingBGS && map.autoplay_bgs
pbBGMFade(0.8) if playingBGS.name!=map.bgs.name
end
Graphics.frame_reset
end
def transfer_player(cancel_swimming = true)
def transfer_player(cancelVehicles=true)
$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)
pbBridgeOff
@spritesetGlobal.playersprite.clearShadows
if $game_map.map_id != $game_temp.player_new_map_id
$map_factory.setup($game_temp.player_new_map_id)
if $game_map.map_id!=$game_temp.player_new_map_id
$MapFactory.setup($game_temp.player_new_map_id)
end
$game_player.moveto($game_temp.player_new_x, $game_temp.player_new_y)
case $game_temp.player_new_direction
@@ -84,14 +79,13 @@ class Scene_Map
when 8 then $game_player.turn_up
end
$game_player.straighten
$game_temp.followers.map_transfer_followers
$game_map.update
disposeSpritesets
RPG::Cache.clear
createSpritesets
if $game_temp.transition_processing
$game_temp.transition_processing = false
Graphics.transition
Graphics.transition(20)
end
$game_map.autoplay
Graphics.frame_reset
@@ -103,7 +97,9 @@ class Scene_Map
$game_temp.in_menu = true
$game_player.straighten
$game_map.update
UI::PauseMenu.new.main
sscene = PokemonPauseMenu_Scene.new
sscreen = PokemonPauseMenu.new(sscene)
sscreen.pbStartPokemonMenu
$game_temp.in_menu = false
end
@@ -115,119 +111,107 @@ class Scene_Map
end
def miniupdate
$game_temp.in_mini_update = true
$PokemonTemp.miniupdate = true
loop do
$game_player.update
updateMaps
$game_player.update
$game_system.update
$game_screen.update
break if !$game_temp.player_transferring
transfer_player(false)
break unless $game_temp.player_transferring
transfer_player
break if $game_temp.transition_processing
end
updateSpritesets
$game_temp.in_mini_update = false
$PokemonTemp.miniupdate = false
end
def updateMaps
$map_factory.maps.each do |map|
for map in $MapFactory.maps
map.update
end
$map_factory.updateMaps(self)
$MapFactory.updateMaps(self)
end
def updateSpritesets(refresh = false)
def updateSpritesets
@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.each do |i|
if $map_factory.hasMap?(i)
@spritesets[i].update
else
@spritesets[i]&.dispose
for i in keys
if !$MapFactory.hasMap?(i)
@spritesets[i].dispose if @spritesets[i]
@spritesets[i] = nil
@spritesets.delete(i)
else
@spritesets[i].update
end
end
@spritesetGlobal.update
pbDayNightTint(@map_renderer)
@map_renderer.refresh if refresh
@map_renderer.update
EventHandlers.trigger(:on_frame_update)
for map in $MapFactory.maps
@spritesets[map.map_id] = Spriteset_Map.new(map) if !@spritesets[map.map_id]
end
Events.onMapUpdate.trigger(self)
end
def update
loop do
updateMaps
pbMapInterpreter.update
$game_player.update
updateMaps
$game_system.update
$game_screen.update
break if !$game_temp.player_transferring
transfer_player(false)
break unless $game_temp.player_transferring
transfer_player
break if $game_temp.transition_processing
end
updateSpritesets
if $game_temp.title_screen_calling
SaveData.mark_values_as_unloaded
if $game_temp.to_title
$scene = pbCallTitle
return
end
if $game_temp.transition_processing
$game_temp.transition_processing = false
if $game_temp.transition_name == ""
Graphics.transition
Graphics.transition(20)
else
Graphics.transition(40, "Graphics/Transitions/" + $game_temp.transition_name)
end
end
return if $game_temp.message_window_showing
if !pbMapInterpreterRunning? && !$PokemonGlobal.forced_movement?
if !pbMapInterpreterRunning?
if Input.trigger?(Input::USE)
$game_temp.interact_calling = true
elsif Input.trigger?(Input::ACTION)
if !$game_system.menu_disabled && !$game_player.moving?
$PokemonTemp.hiddenMoveEventCalling = true
elsif Input.trigger?(Input::BACK)
unless $game_system.menu_disabled || $game_player.moving?
$game_temp.menu_calling = true
$game_temp.menu_beep = true
end
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)
$game_temp.debug_calling = true if $DEBUG
end
end
if !$game_player.moving?
unless $game_player.moving?
if $game_temp.menu_calling
call_menu
elsif $game_temp.debug_calling
call_debug
elsif $game_temp.ready_menu_calling
$game_temp.ready_menu_calling = false
elsif $PokemonTemp.keyItemCalling
$PokemonTemp.keyItemCalling = false
$game_player.straighten
pbUseKeyItem
elsif $game_temp.interact_calling
$game_temp.interact_calling = 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
EventHandlers.trigger(:on_player_interact)
end
elsif $PokemonTemp.hiddenMoveEventCalling
$PokemonTemp.hiddenMoveEventCalling = false
$game_player.straighten
Events.onAction.trigger(self)
end
end
end
def main
createSpritesets
Graphics.transition
Graphics.transition(20)
loop do
Graphics.update
Input.update
@@ -235,13 +219,9 @@ class Scene_Map
break if $scene != self
end
Graphics.freeze
dispose
if $game_temp.title_screen_calling
pbMapInterpreter.command_end if pbMapInterpreterRunning?
$game_temp.last_uptime_refreshed_play_time = nil
$game_temp.title_screen_calling = false
pbBGMFade(1.0)
Graphics.transition
disposeSpritesets
if $game_temp.to_title
Graphics.transition(20)
Graphics.freeze
end
end

View File

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

View File

@@ -51,7 +51,7 @@ class Interpreter
when 134 then return command_134 # Change Save Access
when 135 then return command_135 # Change Menu Access
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 203 then return command_203 # Scroll Map
when 204 then return command_204 # Change Map Settings
@@ -121,19 +121,16 @@ class Interpreter
def command_dummy
return true
end
#-----------------------------------------------------------------------------
# * End Event
#-----------------------------------------------------------------------------
def command_end
@list = nil
end_follower_overrides
# If main map event and event ID are valid, unlock event
if @main && @event_id > 0 && $game_map.events[@event_id]
$game_map.events[@event_id].unlock
end
end
#-----------------------------------------------------------------------------
# * Command Skip
#-----------------------------------------------------------------------------
@@ -144,7 +141,6 @@ class Interpreter
@index += 1
end
end
#-----------------------------------------------------------------------------
# * Command If
#-----------------------------------------------------------------------------
@@ -155,7 +151,6 @@ class Interpreter
end
return command_skip
end
#-----------------------------------------------------------------------------
# * Show Text
#-----------------------------------------------------------------------------
@@ -163,7 +158,7 @@ class Interpreter
return false if $game_temp.message_window_showing
message = @list[@index].parameters[0]
message_end = ""
choices = nil
commands = nil
number_input_variable = nil
number_input_max_digits = nil
# Check the next command(s) for things to add on to this text
@@ -179,8 +174,8 @@ class Interpreter
when 101 # Show Text
message_end = "\1"
when 102 # Show Choices
commands = @list[next_index].parameters
@index = next_index
choices = setup_choices(@list[@index].parameters)
when 103 # Input Number
number_input_variable = @list[next_index].parameters[0]
number_input_max_digits = @list[next_index].parameters[1]
@@ -190,11 +185,15 @@ class Interpreter
end
# Translate the text
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
if choices
command = pbMessage(message + message_end, choices[0], choices[1])
@branch[@list[@index].indent] = choices[2][command] || command
if commands
cmd_texts = []
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
params = ChooseNumberParams.new
params.setMaxDigits(number_input_max_digits)
@@ -207,118 +206,37 @@ class Interpreter
@message_waiting = false
return true
end
#-----------------------------------------------------------------------------
# * Show Choices
#-----------------------------------------------------------------------------
def command_102
choices = setup_choices(@list[@index].parameters)
@message_waiting = true
command = pbShowCommands(nil, choices[0], choices[1])
command = pbShowCommands(nil, @list[@index].parameters[0], @list[@index].parameters[1])
@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
return true
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 [**]
#-----------------------------------------------------------------------------
def command_402
# @parameters[0] is 0/1/2/3 for Choice 1/2/3/4 respectively
if @branch[@list[@index].indent] == @parameters[0]
@branch.delete(@list[@index].indent)
return true
end
return command_skip
end
#-----------------------------------------------------------------------------
# * When Cancel
#-----------------------------------------------------------------------------
def command_403
# @parameters[0] is 4 for "Branch"
if @branch[@list[@index].indent] == @choice_branch_index
if @branch[@list[@index].indent] == 4
@branch.delete(@list[@index].indent)
return true
end
return command_skip
end
#-----------------------------------------------------------------------------
# * Input Number
#-----------------------------------------------------------------------------
@@ -333,7 +251,6 @@ class Interpreter
@message_waiting = false
return true
end
#-----------------------------------------------------------------------------
# * Change Text Options
#-----------------------------------------------------------------------------
@@ -343,22 +260,26 @@ class Interpreter
$game_system.message_frame = @parameters[1]
return true
end
#-----------------------------------------------------------------------------
# * Button Input Processing
#-----------------------------------------------------------------------------
def pbButtonInputProcessing(variable_number = 0, timeout_frames = 0)
ret = 0
timer_start = System.uptime
timer = timeout_frames * Graphics.frame_rate / 20
loop do
Graphics.update
Input.update
pbUpdateSceneMap
# 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 the timer runs out
break if timeout_frames > 0 && System.uptime - timer_start >= timeout_frames / 20.0
# Count down the timer and break if it runs out
if timeout_frames > 0
timer -= 1
break if timer <= 0
end
end
Input.update
if variable_number && variable_number > 0
@@ -376,16 +297,13 @@ class Interpreter
@index += 1
return true
end
#-----------------------------------------------------------------------------
# * Wait
#-----------------------------------------------------------------------------
def command_106
@wait_count = @parameters[0] / 20.0
@wait_start = System.uptime
@wait_count = @parameters[0] * Graphics.frame_rate / 20
return true
end
#-----------------------------------------------------------------------------
# * Conditional Branch
#-----------------------------------------------------------------------------
@@ -400,22 +318,8 @@ class Interpreter
result = ($game_switches[@parameters[1]] == (@parameters[2] == 0))
end
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]]
end
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
value1 = $game_variables[@parameters[1]]
value2 = (@parameters[2] == 0) ? @parameters[3] : $game_variables[@parameters[3]]
case @parameters[4]
when 0 then result = (value1 == value2)
when 1 then result = (value1 >= value2)
@@ -430,8 +334,8 @@ class Interpreter
result = ($game_self_switches[key] == (@parameters[2] == 0))
end
when 3 # timer
if $game_system.timer_start
sec = $game_system.timer
if $game_system.timer_working
sec = $game_system.timer / Graphics.frame_rate
result = (@parameters[2] == 0) ? (sec >= @parameters[1]) : (sec <= @parameters[1])
end
# when 4, 5 # actor, enemy
@@ -439,7 +343,7 @@ class Interpreter
character = get_character(@parameters[1])
result = (character.direction == @parameters[2]) if character
when 7 # gold
gold = $player.money
gold = $Trainer.money
result = (@parameters[2] == 0) ? (gold >= @parameters[1]) : (gold <= @parameters[1])
# when 8, 9, 10 # item, weapon, armor
when 11 # button
@@ -455,7 +359,6 @@ class Interpreter
end
return command_skip
end
#-----------------------------------------------------------------------------
# * Else
#-----------------------------------------------------------------------------
@@ -466,14 +369,12 @@ class Interpreter
end
return command_skip
end
#-----------------------------------------------------------------------------
# * Loop
#-----------------------------------------------------------------------------
def command_112
return true
end
#-----------------------------------------------------------------------------
# * Repeat Above
#-----------------------------------------------------------------------------
@@ -484,7 +385,6 @@ class Interpreter
return true if @list[@index].indent == indent
end
end
#-----------------------------------------------------------------------------
# * Break Loop
#-----------------------------------------------------------------------------
@@ -501,7 +401,6 @@ class Interpreter
end
end
end
#-----------------------------------------------------------------------------
# * Exit Event Processing
#-----------------------------------------------------------------------------
@@ -509,19 +408,17 @@ class Interpreter
command_end
return true
end
#-----------------------------------------------------------------------------
# * Erase Event
#-----------------------------------------------------------------------------
def command_116
if @event_id > 0
$game_map.events[@event_id]&.erase
$PokemonMap&.addErasedEvent(@event_id)
$game_map.events[@event_id].erase if $game_map.events[@event_id]
$PokemonMap.addErasedEvent(@event_id) if $PokemonMap
end
@index += 1
return false
end
#-----------------------------------------------------------------------------
# * Call Common Event
#-----------------------------------------------------------------------------
@@ -533,14 +430,12 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Label
#-----------------------------------------------------------------------------
def command_118
return true
end
#-----------------------------------------------------------------------------
# * Jump to Label
#-----------------------------------------------------------------------------
@@ -559,13 +454,12 @@ class Interpreter
temp_index += 1
end
end
#-----------------------------------------------------------------------------
# * Control Switches
#-----------------------------------------------------------------------------
def command_121
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)
$game_switches[i] = (@parameters[2] == 0)
should_refresh = true
@@ -574,7 +468,6 @@ class Interpreter
$game_map.need_refresh = true if should_refresh
return true
end
#-----------------------------------------------------------------------------
# * Control Variables
#-----------------------------------------------------------------------------
@@ -602,44 +495,43 @@ class Interpreter
end
when 7 # other
case @parameters[4]
when 0 then value = $game_map.map_id # map ID
when 1 then value = $player.pokemon_count # party members
when 2 then value = $player.money # gold
when 3 then value = $stats.distance_moved # steps
when 4 then value = $stats.play_time # play time
when 5 then value = $game_system.timer # timer
when 6 then value = $game_system.save_count # save count
when 0 then value = $game_map.map_id # map ID
when 1 then value = $Trainer.pokemon_party.length # party members
when 2 then value = $Trainer.money # gold
# when 3 # steps
when 4 then value = Graphics.frame_count / Graphics.frame_rate # play time
when 5 then value = $game_system.timer / Graphics.frame_rate # timer
when 6 then value = $game_system.save_count # save count
end
end
# 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]
when 0 # set
next if $game_variables[i] == value
$game_variables[i] = value
when 1 # add
next if $game_variables[i] >= 99_999_999
next if $game_variables[i] >= 99999999
$game_variables[i] += value
when 2 # subtract
next if $game_variables[i] <= -99_999_999
next if $game_variables[i] <= -99999999
$game_variables[i] -= value
when 3 # multiply
next if value == 1
$game_variables[i] *= value
when 4 # divide
next if [0, 1].include?(value)
next if value == 1 || value == 0
$game_variables[i] /= value
when 5 # remainder
next if [0, 1].include?(value)
next if value == 1 || value == 0
$game_variables[i] %= value
end
$game_variables[i] = 99_999_999 if $game_variables[i] > 99_999_999
$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] = -99999999 if $game_variables[i] < -99999999
$game_map.need_refresh = true
end
return true
end
#-----------------------------------------------------------------------------
# * Control Self Switch
#-----------------------------------------------------------------------------
@@ -654,23 +546,21 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Control Timer
#-----------------------------------------------------------------------------
def command_124
$game_system.timer_start = (@parameters[0] == 0) ? $stats.play_time : nil
$game_system.timer_duration = @parameters[1] if @parameters[0] == 0
$game_system.timer_working = (@parameters[0] == 0)
$game_system.timer = @parameters[1] * Graphics.frame_rate if @parameters[0] == 0
return true
end
#-----------------------------------------------------------------------------
# * Change Gold
#-----------------------------------------------------------------------------
def command_125
value = (@parameters[1] == 0) ? @parameters[2] : $game_variables[@parameters[2]]
value = -value if @parameters[0] == 1 # Decrease
$player.money += value
$Trainer.money += value
return true
end
@@ -678,12 +568,11 @@ class Interpreter
def command_127; command_dummy; end # Change Weapons
def command_128; command_dummy; end # Change Armor
def command_129; command_dummy; end # Change Party Member
#-----------------------------------------------------------------------------
# * Change Windowskin
#-----------------------------------------------------------------------------
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]
$PokemonSystem.textskin = i
MessageConfig.pbSetSpeechFrame("Graphics/Windowskins/" + Settings::SPEECH_WINDOWSKINS[i])
@@ -691,7 +580,6 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Change Battle BGM
#-----------------------------------------------------------------------------
@@ -699,12 +587,13 @@ class Interpreter
($PokemonGlobal.nextBattleBGM = @parameters[0]) ? @parameters[0].clone : nil
return true
end
#-----------------------------------------------------------------------------
# * 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
#-----------------------------------------------------------------------------
@@ -712,7 +601,6 @@ class Interpreter
$game_system.save_disabled = (@parameters[0] == 0)
return true
end
#-----------------------------------------------------------------------------
# * Change Menu Access
#-----------------------------------------------------------------------------
@@ -720,7 +608,6 @@ class Interpreter
$game_system.menu_disabled = (@parameters[0] == 0)
return true
end
#-----------------------------------------------------------------------------
# * Change Encounter
#-----------------------------------------------------------------------------
@@ -729,9 +616,8 @@ class Interpreter
$game_player.make_encounter_count
return true
end
#-----------------------------------------------------------------------------
# * Transfer Overrides
# * Transfer Player
#-----------------------------------------------------------------------------
def command_201
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_x = @parameters[2]
$game_temp.player_new_y = @parameters[3]
$game_temp.player_new_direction = @parameters[4]
else # Appoint with variables
$game_temp.player_new_map_id = $game_variables[@parameters[1]]
$game_temp.player_new_x = $game_variables[@parameters[2]]
$game_temp.player_new_y = $game_variables[@parameters[3]]
$game_temp.player_new_direction = @parameters[4]
end
$game_temp.player_new_direction = @parameters[4]
@index += 1
# If transition happens with a fade, do the fade
if @parameters[5] == 0
@@ -759,7 +646,6 @@ class Interpreter
end
return false
end
#-----------------------------------------------------------------------------
# * Set Event Location
#-----------------------------------------------------------------------------
@@ -768,10 +654,9 @@ class Interpreter
character = get_character(@parameters[0])
return true if character.nil?
# Move the character
case @parameters[1]
when 0 # Direct appointment
if @parameters[1] == 0 # Direct appointment
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]])
else # Exchange with another event
character2 = get_character(@parameters[2])
@@ -791,7 +676,6 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Scroll Map
#-----------------------------------------------------------------------------
@@ -801,7 +685,6 @@ class Interpreter
$game_map.start_scroll(@parameters[0], @parameters[1], @parameters[2])
return true
end
#-----------------------------------------------------------------------------
# * Change Map Settings
#-----------------------------------------------------------------------------
@@ -824,38 +707,29 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Change Fog Color Tone
#-----------------------------------------------------------------------------
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
end
#-----------------------------------------------------------------------------
# * Change Fog Opacity
#-----------------------------------------------------------------------------
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
end
#-----------------------------------------------------------------------------
# * Show Animation
#-----------------------------------------------------------------------------
def command_207
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?
character.animation_id = @parameters[1]
return true
end
#-----------------------------------------------------------------------------
# * Change Transparent Flag
#-----------------------------------------------------------------------------
@@ -863,22 +737,15 @@ class Interpreter
$game_player.transparent = (@parameters[0] == 0)
return true
end
#-----------------------------------------------------------------------------
# * Set Move Route
#-----------------------------------------------------------------------------
def command_209
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?
character.force_move_route(@parameters[1])
return true
end
#-----------------------------------------------------------------------------
# * Wait for Move's Completion
#-----------------------------------------------------------------------------
@@ -886,7 +753,6 @@ class Interpreter
@move_route_waiting = true if !$game_temp.in_battle
return true
end
#-----------------------------------------------------------------------------
# * Prepare for Transition
#-----------------------------------------------------------------------------
@@ -895,7 +761,6 @@ class Interpreter
Graphics.freeze
return true
end
#-----------------------------------------------------------------------------
# * Execute Transition
#-----------------------------------------------------------------------------
@@ -906,31 +771,27 @@ class Interpreter
@index += 1
return false
end
#-----------------------------------------------------------------------------
# * Change Screen Color Tone
#-----------------------------------------------------------------------------
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
end
#-----------------------------------------------------------------------------
# * Screen Flash
#-----------------------------------------------------------------------------
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
end
#-----------------------------------------------------------------------------
# * Screen Shake
#-----------------------------------------------------------------------------
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
end
#-----------------------------------------------------------------------------
# * Show Picture
#-----------------------------------------------------------------------------
@@ -944,10 +805,9 @@ class Interpreter
y = $game_variables[@parameters[5]]
end
$game_screen.pictures[number].show(@parameters[1], @parameters[2],
x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9])
x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9])
return true
end
#-----------------------------------------------------------------------------
# * Move Picture
#-----------------------------------------------------------------------------
@@ -960,12 +820,10 @@ class Interpreter
x = $game_variables[@parameters[4]]
y = $game_variables[@parameters[5]]
end
$game_screen.pictures[number].move(@parameters[1], @parameters[2], x, y,
@parameters[6], @parameters[7],
@parameters[8], @parameters[9])
$game_screen.pictures[number].move(@parameters[1] * Graphics.frame_rate / 20,
@parameters[2], x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9])
return true
end
#-----------------------------------------------------------------------------
# * Rotate Picture
#-----------------------------------------------------------------------------
@@ -974,16 +832,15 @@ class Interpreter
$game_screen.pictures[number].rotate(@parameters[1])
return true
end
#-----------------------------------------------------------------------------
# * Change Picture Color Tone
#-----------------------------------------------------------------------------
def command_234
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
end
#-----------------------------------------------------------------------------
# * Erase Picture
#-----------------------------------------------------------------------------
@@ -992,7 +849,6 @@ class Interpreter
$game_screen.pictures[number].erase
return true
end
#-----------------------------------------------------------------------------
# * Set Weather Effects
#-----------------------------------------------------------------------------
@@ -1000,7 +856,6 @@ class Interpreter
$game_screen.weather(@parameters[0], @parameters[1], @parameters[2])
return true
end
#-----------------------------------------------------------------------------
# * Play BGM
#-----------------------------------------------------------------------------
@@ -1008,7 +863,6 @@ class Interpreter
pbBGMPlay(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Fade Out BGM
#-----------------------------------------------------------------------------
@@ -1016,7 +870,6 @@ class Interpreter
pbBGMFade(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Play BGS
#-----------------------------------------------------------------------------
@@ -1024,7 +877,6 @@ class Interpreter
pbBGSPlay(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Fade Out BGS
#-----------------------------------------------------------------------------
@@ -1032,7 +884,6 @@ class Interpreter
pbBGSFade(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Memorize BGM/BGS
#-----------------------------------------------------------------------------
@@ -1041,7 +892,6 @@ class Interpreter
$game_system.bgs_memorize
return true
end
#-----------------------------------------------------------------------------
# * Restore BGM/BGS
#-----------------------------------------------------------------------------
@@ -1050,7 +900,6 @@ class Interpreter
$game_system.bgs_restore
return true
end
#-----------------------------------------------------------------------------
# * Play ME
#-----------------------------------------------------------------------------
@@ -1058,7 +907,6 @@ class Interpreter
pbMEPlay(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Play SE
#-----------------------------------------------------------------------------
@@ -1066,7 +914,6 @@ class Interpreter
pbSEPlay(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Stop SE
#-----------------------------------------------------------------------------
@@ -1080,25 +927,23 @@ class Interpreter
def command_602; command_if(1); end # If Escape
def command_603; command_if(2); end # If Lose
def command_302; command_dummy; end # Shop Processing
#-----------------------------------------------------------------------------
# * Name Input Processing
#-----------------------------------------------------------------------------
def command_303
if $player
$player.name = pbEnterPlayerName(_INTL("Your name?"), 1, @parameters[1], $player.name)
if $Trainer
$Trainer.name = pbEnterPlayerName(_INTL("Your name?"), 1, @parameters[1], $Trainer.name)
return true
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
pbFadeOutIn do
pbFadeOutIn {
sscene = PokemonEntryScene.new
sscreen = PokemonEntry.new(sscene)
$game_actors[@parameters[0]].name = sscreen.pbStartScreen(
_INTL("Enter {1}'s name.", $game_actors[@parameters[0]].name),
1, @parameters[1], $game_actors[@parameters[0]].name
)
end
_INTL("Enter {1}'s name.", $game_actors[@parameters[0]].name),
1, @parameters[1], $game_actors[@parameters[0]].name)
}
end
return true
end
@@ -1106,18 +951,11 @@ class Interpreter
def command_311; command_dummy; end # Change HP
def command_312; command_dummy; end # Change SP
def command_313; command_dummy; end # Change State
#-----------------------------------------------------------------------------
# * Recover All
#-----------------------------------------------------------------------------
def command_314
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
$Trainer.heal_party if @parameters[0] == 0
return true
end
@@ -1139,7 +977,6 @@ class Interpreter
def command_338; command_dummy; end # Deal Damage
def command_339; command_dummy; end # Force Action
def command_340; command_dummy; end # Abort Battle
#-----------------------------------------------------------------------------
# * Call Menu Screen
#-----------------------------------------------------------------------------
@@ -1148,15 +985,15 @@ class Interpreter
@index += 1
return false
end
#-----------------------------------------------------------------------------
# * Call Save Screen
#-----------------------------------------------------------------------------
def command_352
pbFadeOutIn { UI::Save.new.main }
scene = PokemonSave_Scene.new
screen = PokemonSaveScreen.new(scene)
screen.pbSaveScreen
return true
end
#-----------------------------------------------------------------------------
# * Game Over
#-----------------------------------------------------------------------------
@@ -1165,15 +1002,13 @@ class Interpreter
pbBGSFade(1.0)
pbFadeOutIn { pbStartOver(true) }
end
#-----------------------------------------------------------------------------
# * Return to Title Screen
#-----------------------------------------------------------------------------
def command_354
$game_temp.title_screen_calling = true
$game_temp.to_title = true
return false
end
#-----------------------------------------------------------------------------
# * Script
#-----------------------------------------------------------------------------
@@ -1182,7 +1017,7 @@ class Interpreter
# Look for more script commands or a continuation of one, and add them to script
loop do
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
end
# Run the script

View File

@@ -13,15 +13,21 @@ class Event
end
# Removes an event handler procedure from the event.
def -(other)
@callbacks.delete(other)
def -(method)
for i in 0...@callbacks.length
next if @callbacks[i]!=method
@callbacks.delete_at(i)
break
end
return self
end
# Adds an event handler procedure from the event.
def +(other)
return self if @callbacks.include?(other)
@callbacks.push(other)
def +(method)
for i in 0...@callbacks.length
return self if @callbacks[i]==method
end
@callbacks.push(method)
return self
end
@@ -38,13 +44,13 @@ class Event
# proc { |sender,params| } where params is an array of the other parameters, and
# proc { |sender,arg0,arg1,...| }
def trigger(*arg)
arglist = arg[1, arg.length]
@callbacks.each do |callback|
if callback.arity > 2 && arg.length == callback.arity
arglist = arg[1,arg.length]
for callback in @callbacks
if callback.arity>2 && arg.length==callback.arity
# Retrofitted for callbacks that take three or more arguments
callback.call(*arg)
else
callback.call(arg[0], arglist)
callback.call(arg[0],arglist)
end
end
end
@@ -53,164 +59,16 @@ class Event
# by the code where the event occurred. The first argument is the sender of
# the event, the other arguments are the event's parameters.
def trigger2(*arg)
@callbacks.each do |callback|
for callback in @callbacks
callback.call(*arg)
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
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)
@mod = mod
@hash = {}
@@ -218,25 +76,6 @@ class HandlerHashEnum
@symbolCache = {}
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)
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
mod = Object.const_get(@mod) rescue nil
@@ -250,8 +89,8 @@ class HandlerHashEnum
return ret if ret
mod = Object.const_get(@mod) rescue nil
return nil if !mod
mod.constants.each do |key|
next if mod.const_get(key) != sym
for key in mod.constants
next if mod.const_get(key)!=sym
ret = key.to_sym
@symbolCache[sym] = ret
break
@@ -259,9 +98,15 @@ class HandlerHashEnum
return ret
end
# 'sym' can be an ID or symbol.
def add(sym, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
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
@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)"
end
id = fromSymbol(sym)
@@ -270,6 +115,126 @@ class HandlerHashEnum
@hash[symbol] = handler || handlerBlock if symbol
end
def copy(src,*dests)
handler = self[src]
if handler
for dest in dests
self.add(dest,handler)
end
end
end
def [](sym) # 'sym' can be an ID or symbol
id = fromSymbol(sym)
ret = nil
ret = @hash[id] if id && @hash[id] # Real ID from the item
symbol = toSymbol(sym)
ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string
unless ret
for addif in @addIfs
return addif[1] if addif[0].call(id)
end
end
return ret
end
def trigger(sym,*args)
handler = self[sym]
return (handler) ? handler.call(fromSymbol(sym),*args) : nil
end
def clear
@hash.clear
end
end
#===============================================================================
# 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)
if ![Proc, Hash].include?(handler.class) && !block_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
@hash.clear
@addIfs.clear
@ordered_keys.clear
end
def trigger(sym, *args)
handler = self[sym]
return (handler) ? handler.call(fromSymbol(sym), *args) : nil
def trigger(entry, *args)
handler = self[entry]
return (handler) ? handler.call(*args) : nil
end
end
#===============================================================================
#
#===============================================================================
class SpeciesHandlerHash < HandlerHashSymbol
class SpeciesHandlerHash < HandlerHash2
end
class AbilityHandlerHash < HandlerHashSymbol
class AbilityHandlerHash < HandlerHash2
end
class ItemHandlerHash < HandlerHashSymbol
class ItemHandlerHash < HandlerHash2
end
class MoveHandlerHash < HandlerHashSymbol
class MoveHandlerHash < HandlerHash2
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,
# flashing, etc. Refer to "$game_screen" for the instance of this class.
#===============================================================================
class Game_Screen
#-----------------------------------------------------------------------------
# * Public Instance Variables
#-----------------------------------------------------------------------------
attr_reader :brightness # brightness
attr_reader :tone # color tone
attr_reader :flash_color # flash color
@@ -13,106 +17,120 @@ class Game_Screen
attr_reader :weather_type # weather type
attr_reader :weather_max # max number of weather sprites
attr_accessor :weather_duration # ticks in which the weather should fade in
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@brightness = 255
@tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@tone_timer_start = nil
@flash_color = Color.new(0, 0, 0, 0)
@flash_duration = 0
@flash_timer_start = nil
@shake_power = 0
@shake_speed = 0
@shake_duration = 0
@shake = 0
@pictures = [nil]
(1..100).each { |i| @pictures.push(Game_Picture.new(i)) }
@weather_type = :None
@weather_max = 0.0
@weather_duration = 0
end
# duration is time in 1/20ths of a second.
def start_tone_change(tone, duration)
if duration == 0
@tone = tone.clone
return
@brightness = 255
@fadeout_duration = 0
@fadein_duration = 0
@tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@flash_color = Color.new(0, 0, 0, 0)
@flash_duration = 0
@shake_power = 0
@shake_speed = 0
@shake_duration = 0
@shake_direction = 1
@shake = 0
@pictures = [nil]
for i in 1..100
@pictures.push(Game_Picture.new(i))
end
@tone_initial = @tone.clone
@tone_target = tone.clone
@tone_duration = duration / 20.0
@tone_timer_start = $stats.play_time
@weather_type = 0
@weather_max = 0.0
@weather_duration = 0
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)
@tone_target = tone.clone
@tone_duration = duration
if @tone_duration == 0
@tone = @tone_target.clone
end
end
#-----------------------------------------------------------------------------
# * Start Flashing
# color : color
# duration : time
#-----------------------------------------------------------------------------
def start_flash(color, duration)
@flash_color = color.clone
@flash_initial_alpha = @flash_color.alpha
@flash_duration = duration / 20.0
@flash_timer_start = $stats.play_time
@flash_color = color.clone
@flash_duration = duration
end
# duration is time in 1/20ths of a second.
#-----------------------------------------------------------------------------
# * Start Shaking
# power : strength
# speed : speed
# duration : time
#-----------------------------------------------------------------------------
def start_shake(power, speed, duration)
@shake_power = power
@shake_speed = speed
@shake_duration = duration / 20.0
@shake_timer_start = $stats.play_time
@shake_power = power
@shake_speed = speed
@shake_duration = duration
end
# duration is time in 1/20ths of a second.
#-----------------------------------------------------------------------------
# * Set Weather
# type : type
# power : strength
# duration : time
#-----------------------------------------------------------------------------
def weather(type, power, duration)
@weather_type = GameData::Weather.get(type).id
@weather_max = (power + 1) * RPG::Weather::MAX_SPRITES / 10
@weather_duration = duration
@weather_duration = duration # In 1/20ths of a seconds
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
now = $stats.play_time
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
if @fadeout_duration && @fadeout_duration>=1
d = @fadeout_duration
@brightness = (@brightness*(d-1))/d
@fadeout_duration -= 1
end
if @flash_timer_start
@flash_color.alpha = lerp(@flash_initial_alpha, 0, @flash_duration, @flash_timer_start, now)
if now - @flash_timer_start >= @flash_duration
@flash_initial_alpha = nil
@flash_timer_start = nil
end
if @fadein_duration && @fadein_duration>=1
d = @fadein_duration
@brightness = (@brightness*(d-1)+255)/d
@fadein_duration -= 1
end
if @shake_timer_start
delta_t = now - @shake_timer_start
movement_per_second = @shake_power * @shake_speed * 4
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
if @tone_duration>=1
d = @tone_duration
@tone.red = (@tone.red*(d-1)+@tone_target.red)/d
@tone.green = (@tone.green*(d-1)+@tone_target.green)/d
@tone.blue = (@tone.blue*(d-1)+@tone_target.blue)/d
@tone.gray = (@tone.gray*(d-1)+@tone_target.gray)/d
@tone_duration -= 1
end
if @flash_duration>=1
d = @flash_duration
@flash_color.alpha = @flash_color.alpha*(d-1)/d
@flash_duration -= 1
end
if @shake_duration>=1 || @shake!=0
delta = (@shake_power*@shake_speed*@shake_direction)/10.0
if @shake_duration<=1 && @shake*(@shake+delta)<0
@shake = 0
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
end
@shake += delta
end
@shake_direction = -1 if @shake>@shake_power*2
@shake_direction = 1 if @shake<-@shake_power*2
@shake_duration -= 1 if @shake_duration>=1
end
if $game_temp.in_battle
(51..100).each { |i| @pictures[i].update }
for i in 51..100
@pictures[i].update
end
else
(1..50).each { |i| @pictures[i].update }
for i in 1..50
@pictures[i].update
end
end
end
end
@@ -120,15 +138,17 @@ end
#===============================================================================
#
#===============================================================================
def pbToneChangeAll(tone, duration)
$game_screen.start_tone_change(tone, duration)
$game_screen.pictures.each { |picture| picture&.start_tone_change(tone, duration) }
def pbToneChangeAll(tone,duration)
$game_screen.start_tone_change(tone,duration*Graphics.frame_rate/20)
for picture in $game_screen.pictures
picture.start_tone_change(tone,duration*Graphics.frame_rate/20) if picture
end
end
def pbFlash(color, frames)
$game_screen.start_flash(color, frames)
def pbShake(power,speed,frames)
$game_screen.start_shake(power,speed,frames*Graphics.frame_rate/20)
end
def pbShake(power, speed, frames)
$game_screen.start_shake(power, speed, frames)
def pbFlash(color,frames)
$game_screen.start_flash(color,frames*Graphics.frame_rate/20)
end

View File

@@ -5,83 +5,51 @@
# Refer to "$game_temp" for the instance of this class.
#===============================================================================
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 :ending_surf # jumping off surf base flag
attr_accessor :surf_base_coords # [x, y] while jumping on/off, or nil
attr_accessor :in_mini_update # performing mini update flag
# Battle
attr_accessor :common_event_id # common event ID
attr_accessor :in_battle # in-battle flag
attr_accessor :battle_abort # battle flag: interrupt
attr_accessor :battleback_name # battleback file name
attr_accessor :force_single_battle # force next battle to be 1v1 flag
attr_accessor :waiting_trainer # [trainer, event ID] or nil
attr_accessor :last_battle_record # record of actions in last recorded battle
# Overrides transfers
attr_accessor :in_menu # menu is open
attr_accessor :menu_beep # menu: play sound effect flag
attr_accessor :menu_calling # menu calling flag
attr_accessor :debug_calling # debug calling flag
attr_accessor :player_transferring # player place movement flag
attr_accessor :player_new_map_id # player destination: map ID
attr_accessor :player_new_x # player destination: x-coordinate
attr_accessor :player_new_y # player destination: y-coordinate
attr_accessor :player_new_direction # player destination: direction
attr_accessor :fly_destination # [map ID, x, y] or nil
# Transitions
attr_accessor :transition_processing # transition processing flag
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
# Other
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 :background_bitmap
attr_accessor :mart_prices
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
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
@ending_surf = false
@in_mini_update = false
# Battle
@battleback_name = ""
@force_single_battle = false
# Overrides transfers
@common_event_id = 0
@in_battle = false
@battle_abort = false
@battleback_name = ''
@in_menu = false
@menu_beep = false
@menu_calling = false
@debug_calling = false
@player_transferring = false
@player_new_map_id = 0
@player_new_x = 0
@player_new_y = 0
@player_new_direction = 0
# Transitions
@transition_processing = false
@transition_name = ""
@to_title = false
@fadestate = 0
# Other
@menu_beep = false
@memorized_bgm = nil
@memorized_bgm_position = 0
@menu_last_choice = 0
@background_bitmap = nil
@message_window_showing = false
@transition_processing = false
@mart_prices = {}
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
#===============================================================================
#==============================================================================
# ** Game_Map
#------------------------------------------------------------------------------
# This class handles the map. It includes scrolling and passable determining
# functions. Refer to "$game_map" for the instance of this class.
#===============================================================================
#==============================================================================
class Game_Map
attr_accessor :map_id
attr_accessor :tileset_name # tileset file name
@@ -43,34 +43,32 @@ class Game_Map
end
def setup(map_id)
@map_id = map_id
@map = load_data(sprintf("Data/Map%03d.rxdata", map_id))
@map_id = map_id
@map = load_data(sprintf("Data/Map%03d.rxdata",map_id))
tileset = $data_tilesets[@map.tileset_id]
updateTileset
@fog_ox = 0
@fog_oy = 0
@fog_tone = Tone.new(0, 0, 0, 0)
@fog_tone_target = Tone.new(0, 0, 0, 0)
@fog_tone_duration = 0
@fog_tone_timer_start = nil
@fog_opacity_duration = 0
@fog_opacity_target = 0
@fog_opacity_timer_start = nil
self.display_x = 0
self.display_y = 0
@need_refresh = false
EventHandlers.trigger(:on_game_map_setup, map_id, @map, tileset)
@events = {}
@map.events.each_key do |i|
@events[i] = Game_Event.new(@map_id, @map.events[i], self)
@fog_ox = 0
@fog_oy = 0
@fog_tone = Tone.new(0, 0, 0, 0)
@fog_tone_target = Tone.new(0, 0, 0, 0)
@fog_tone_duration = 0
@fog_opacity_duration = 0
@fog_opacity_target = 0
self.display_x = 0
self.display_y = 0
@need_refresh = false
Events.onMapCreate.trigger(self,map_id,@map,tileset)
@events = {}
for i in @map.events.keys
@events[i] = Game_Event.new(@map_id, @map.events[i],self)
end
@common_events = {}
(1...$data_common_events.size).each do |i|
@common_events[i] = Game_CommonEvent.new(i)
@common_events = {}
for i in 1...$data_common_events.size
@common_events[i] = Game_CommonEvent.new(i)
end
@scroll_distance_x = 0
@scroll_distance_y = 0
@scroll_speed = 4
@scroll_direction = 2
@scroll_rest = 0
@scroll_speed = 4
end
def updateTileset
@@ -97,52 +95,57 @@ class Game_Map
def encounter_list; return @map.encounter_list; end
def encounter_step; return @map.encounter_step; end
def data; return @map.data; end
def tileset_id; return @map.tileset_id; end
def bgm; return @map.bgm; end
def name
return pbGetMapNameFromId(@map_id)
ret = pbGetMessage(MessageTypes::MapNames,@map_id)
ret.gsub!(/\\PN/,$Trainer.name) if $Trainer
return ret
end
def metadata
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
#-----------------------------------------------------------------------------
# * Autoplays background music
# Plays music called "[normal BGM]_n" if it's night time and it exists
#-----------------------------------------------------------------------------
def autoplayAsCue
pbCueBGM(bgm_name, 1.0, @map.bgm.volume, @map.bgm.pitch) if @map.autoplay_bgm
pbBGSPlay(@map.bgs) if @map.autoplay_bgs
if @map.autoplay_bgm
if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/"+ @map.bgm.name+ "_n")
pbCueBGM(@map.bgm.name+"_n",1.0,@map.bgm.volume,@map.bgm.pitch)
else
pbCueBGM(@map.bgm,1.0)
end
end
if @map.autoplay_bgs
pbBGSPlay(@map.bgs)
end
end
# Plays background music
# Plays music called "[normal BGM]_n" if it's night time and it exists
#-----------------------------------------------------------------------------
# * Plays background music
# Plays music called "[normal BGM]_n" if it's night time and it exists
#-----------------------------------------------------------------------------
def autoplay
pbBGMPlay(bgm_name, @map.bgm.volume, @map.bgm.pitch) if @map.autoplay_bgm
pbBGSPlay(@map.bgs) if @map.autoplay_bgs
if @map.autoplay_bgm
if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/"+ @map.bgm.name+ "_n")
pbBGMPlay(@map.bgm.name+"_n",@map.bgm.volume,@map.bgm.pitch)
else
pbBGMPlay(@map.bgm)
end
end
if @map.autoplay_bgs
pbBGSPlay(@map.bgs)
end
end
def valid?(x, y)
return x >= 0 && x < width && y >= 0 && y < height
return x>=0 && x<width && y>=0 && y<height
end
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
def passable?(x, y, dir, self_event = nil)
def passable?(x, y, d, self_event = nil)
return false if !valid?(x, y)
bit = (1 << ((dir / 2) - 1)) & 0x0f
events.each_value do |event|
bit = (1 << (d / 2 - 1)) & 0x0f
for event in events.values
next if event.tile_id <= 0
next if event == self_event
next if !event.at_coordinate?(x, y)
@@ -153,11 +156,11 @@ class Game_Map
return false if passage & 0x0f == 0x0f
return true if @priorities[event.tile_id] == 0
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
newx = x
newy = y
case dir
case d
when 1
newx -= 1
newy += 1
@@ -180,15 +183,14 @@ class Game_Map
newy -= 1
end
return false if !valid?(newx, newy)
[2, 1, 0].each do |i|
for i in [2, 1, 0]
tile_id = data[x, y, i]
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
# If already on water, only allow movement to another water tile
if self_event && terrain.can_surf_freely
[2, 1, 0].each do |j|
if self_event != nil && terrain.can_surf_freely
for j in [2, 1, 0]
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])
if facing_terrain.id != :None && !facing_terrain.ignore_passability
return facing_terrain.can_surf_freely
@@ -198,32 +200,30 @@ class Game_Map
# Can't walk onto ice
elsif terrain.ice
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
[2, 1, 0].each do |j|
for j in [2, 1, 0]
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])
return false if facing_terrain.ledge
break if facing_terrain.id != :None && !facing_terrain.ignore_passability
end
end
next if terrain&.ignore_passability
next if tile_id == 0
# Regular passability checks
passage = @passages[tile_id]
return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0
if !terrain || !terrain.ignore_passability
passage = @passages[tile_id]
return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0
end
end
return true
end
def playerPassable?(x, y, dir, self_event = nil)
bit = (1 << ((dir / 2) - 1)) & 0x0f
[2, 1, 0].each do |i|
def playerPassable?(x, y, d, self_event = nil)
bit = (1 << (d / 2 - 1)) & 0x0f
for i in [2, 1, 0]
tile_id = data[x, y, i]
next if tile_id == 0
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
passage = @passages[tile_id]
if terrain
@@ -232,34 +232,34 @@ class Game_Map
# Make water tiles passable if player is surfing
return true if $PokemonGlobal.surfing && terrain.can_surf && !terrain.waterfall
# 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
if terrain.bridge && $PokemonGlobal.bridge > 0
return (passage & bit == 0 && passage & 0x0f != 0x0f)
end
end
next if terrain&.ignore_passability
# Regular passability checks
return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0
if !terrain || !terrain.ignore_passability
return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0
end
end
return true
end
# Returns whether the position x,y is fully passable (there is no blocking
# event there, and the tile is fully passable in all directions).
# event there, and the tile is fully passable in all directions)
def passableStrict?(x, y, d, self_event = nil)
return false if !valid?(x, y)
events.each_value do |event|
for event in events.values
next if event == self_event || event.tile_id < 0 || event.through
next if !event.at_coordinate?(x, y)
next if GameData::TerrainTag.try_get(@terrain_tags[event.tile_id]).ignore_passability
return false if @passages[event.tile_id] & 0x0f != 0
return true if @priorities[event.tile_id] == 0
end
[2, 1, 0].each do |i|
for i in [2, 1, 0]
tile_id = data[x, y, i]
next if tile_id == 0
next if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).ignore_passability
return false if @passages[tile_id] & 0x0f != 0
return true if @priorities[tile_id] == 0
@@ -267,10 +267,9 @@ class Game_Map
return true
end
def bush?(x, y)
[2, 1, 0].each do |i|
def bush?(x,y)
for i in [2, 1, 0]
tile_id = data[x, y, i]
next if tile_id == 0
return false if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).bridge &&
$PokemonGlobal.bridge > 0
return true if @passages[tile_id] & 0x40 == 0x40
@@ -278,10 +277,9 @@ class Game_Map
return false
end
def deepBush?(x, y)
[2, 1, 0].each do |i|
def deepBush?(x,y)
for i in [2, 1, 0]
tile_id = data[x, y, i]
next if tile_id == 0
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
return false if terrain.bridge && $PokemonGlobal.bridge > 0
return true if terrain.deep_bush && @passages[tile_id] & 0x40 == 0x40
@@ -289,21 +287,19 @@ class Game_Map
return false
end
def counter?(x, y)
[2, 1, 0].each do |i|
def counter?(x,y)
for i in [2, 1, 0]
tile_id = data[x, y, i]
next if tile_id == 0
passage = @passages[tile_id]
return true if passage & 0x80 == 0x80
end
return false
end
def terrain_tag(x, y, countBridge = false)
def terrain_tag(x,y,countBridge=false)
if valid?(x, y)
[2, 1, 0].each do |i|
for i in [2, 1, 0]
tile_id = data[x, y, i]
next if tile_id == 0
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
next if terrain.id == :None || terrain.ignore_passability
next if !countBridge && terrain.bridge && $PokemonGlobal.bridge == 0
@@ -314,8 +310,8 @@ class Game_Map
end
# Unused.
def check_event(x, y)
self.events.each_value do |event|
def check_event(x,y)
for event in self.events.values
return event.id if event.at_coordinate?(x, y)
end
end
@@ -323,21 +319,21 @@ class Game_Map
def display_x=(value)
return if @display_x == value
@display_x = value
if metadata&.snap_edges
max_x = (self.width - (Graphics.width.to_f / TILE_WIDTH)) * REAL_RES_X
if GameData::MapMetadata.exists?(self.map_id) && GameData::MapMetadata.get(self.map_id).snap_edges
max_x = (self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X
@display_x = [0, [@display_x, max_x].min].max
end
$map_factory&.setMapsInRange
$MapFactory.setMapsInRange if $MapFactory
end
def display_y=(value)
return if @display_y == value
@display_y = value
if metadata&.snap_edges
max_y = (self.height - (Graphics.height.to_f / TILE_HEIGHT)) * REAL_RES_Y
if GameData::MapMetadata.exists?(self.map_id) && GameData::MapMetadata.get(self.map_id).snap_edges
max_y = (self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y
@display_y = [0, [@display_y, max_y].min].max
end
$map_factory&.setMapsInRange
$MapFactory.setMapsInRange if $MapFactory
end
def scroll_up(distance)
@@ -356,135 +352,90 @@ class Game_Map
self.display_x += distance
end
# speed is:
# 1: moves 1 tile in 1.6 seconds
# 2: moves 1 tile in 0.8 seconds
# 3: moves 1 tile in 0.4 seconds
# 4: moves 1 tile in 0.2 seconds
# 5: moves 1 tile in 0.1 seconds
# 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)
def start_scroll(direction, distance, speed)
@scroll_direction = direction
if direction==2 || direction==8 # down or up
@scroll_rest = distance * REAL_RES_Y
else
@scroll_rest = distance * REAL_RES_X
end
@scroll_speed = speed
@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
def scrolling?
return (@scroll_distance_x || 0) != 0 || (@scroll_distance_y || 0) != 0
return @scroll_rest > 0
end
# duration is time in 1/20ths of a second.
def start_fog_tone_change(tone, duration)
if duration == 0
@fog_tone = tone.clone
return
end
@fog_tone_initial = @fog_tone.clone
def start_fog_tone_change(tone,duration)
@fog_tone_target = tone.clone
@fog_tone_duration = duration / 20.0
@fog_tone_timer_start = $stats.play_time
end
# duration is time in 1/20ths of a second.
def start_fog_opacity_change(opacity, duration)
if duration == 0
@fog_opacity = opacity.to_f
return
@fog_tone_duration = duration
if @fog_tone_duration == 0
@fog_tone = @fog_tone_target.clone
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)
def start_fog_opacity_change(opacity,duration)
@fog_opacity_target = opacity*1.0
@fog_opacity_duration = duration
if @fog_opacity_duration==0
@fog_opacity = @fog_opacity_target
end
end
def refresh
@events.each_value do |event|
for event in @events.values
event.refresh
end
@common_events.each_value do |common_event|
for common_event in @common_events.values
common_event.refresh
end
@need_refresh = false
end
def update
uptime_now = System.uptime
play_now = $stats.play_time
# Refresh maps if necessary
if $map_factory
$map_factory.maps.each { |i| i.refresh if i.need_refresh }
$map_factory.setCurrentMap
# refresh maps if necessary
if $MapFactory
for i in $MapFactory.maps
i.refresh if i.need_refresh
end
$MapFactory.setCurrentMap
end
# If scrolling
if (@scroll_distance_x || 0) != 0
duration = @scroll_distance_x.abs * TILE_WIDTH.to_f / (10 * (2**@scroll_speed))
scroll_offset = lerp(0, @scroll_distance_x, duration, @scroll_timer_start, uptime_now)
self.display_x = @scroll_start_x + (scroll_offset * REAL_RES_X)
@scroll_distance_x = 0 if scroll_offset == @scroll_distance_x
end
if (@scroll_distance_y || 0) != 0
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
if @scroll_rest>0
distance = (1<<@scroll_speed)*40.0/Graphics.frame_rate
distance = @scroll_rest if distance>@scroll_rest
case @scroll_direction
when 2 then scroll_down(distance)
when 4 then scroll_left(distance)
when 6 then scroll_right(distance)
when 8 then scroll_up(distance)
end
@scroll_rest -= distance
end
# Only update events that are on-screen
if !$game_temp.in_menu
@events.each_value { |event| event.update }
for event in @events.values
event.update
end
# Update common events
@common_events.each_value { |common_event| common_event.update }
# Update fog
@fog_scroll_last_update_timer = uptime_now if !@fog_scroll_last_update_timer
scroll_mult = (uptime_now - @fog_scroll_last_update_timer) * 5
@fog_ox -= @fog_sx * scroll_mult
@fog_oy -= @fog_sy * scroll_mult
@fog_scroll_last_update_timer = uptime_now
if @fog_tone_timer_start
@fog_tone.red = lerp(@fog_tone_initial.red, @fog_tone_target.red, @fog_tone_duration, @fog_tone_timer_start, play_now)
@fog_tone.green = lerp(@fog_tone_initial.green, @fog_tone_target.green, @fog_tone_duration, @fog_tone_timer_start, play_now)
@fog_tone.blue = lerp(@fog_tone_initial.blue, @fog_tone_target.blue, @fog_tone_duration, @fog_tone_timer_start, play_now)
@fog_tone.gray = lerp(@fog_tone_initial.gray, @fog_tone_target.gray, @fog_tone_duration, @fog_tone_timer_start, play_now)
if play_now - @fog_tone_timer_start >= @fog_tone_duration
@fog_tone_initial = nil
@fog_tone_timer_start = nil
end
for common_event in @common_events.values
common_event.update
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
# Update fog
@fog_ox -= @fog_sx/8.0
@fog_oy -= @fog_sy/8.0
if @fog_tone_duration>=1
d = @fog_tone_duration
target = @fog_tone_target
@fog_tone.red = (@fog_tone.red * (d - 1) + target.red) / d
@fog_tone.green = (@fog_tone.green * (d - 1) + target.green) / d
@fog_tone.blue = (@fog_tone.blue * (d - 1) + target.blue) / d
@fog_tone.gray = (@fog_tone.gray * (d - 1) + target.gray) / d
@fog_tone_duration -= 1
end
if @fog_opacity_duration >= 1
d = @fog_opacity_duration
@fog_opacity = (@fog_opacity * (d - 1) + @fog_opacity_target) / d
@fog_opacity_duration -= 1
end
end
end
@@ -492,74 +443,26 @@ end
#===============================================================================
#
#===============================================================================
# Scroll the map in the given direction by the given distance at the (optional)
# given speed.
def pbScrollMap(direction, distance, speed = 4)
if speed == 0
if [1, 2, 3].include?(direction)
$game_map.scroll_down(distance * Game_Map::REAL_RES_Y)
elsif [7, 8, 9].include?(direction)
$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)
def pbScrollMap(direction,distance,speed)
if speed==0
case direction
when 2 then $game_map.scroll_down(distance * Game_Map::REAL_RES_Y)
when 4 then $game_map.scroll_left(distance * Game_Map::REAL_RES_X)
when 6 then $game_map.scroll_right(distance * Game_Map::REAL_RES_X)
when 8 then $game_map.scroll_up(distance * Game_Map::REAL_RES_Y)
end
else
$game_map.start_scroll(direction, distance, speed)
oldx = $game_map.display_x
oldy = $game_map.display_y
loop do
Graphics.update
Input.update
pbUpdateSceneMap
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
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
# 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
attr_reader :maps
@@ -19,7 +19,7 @@ class PokemonMapFactory
@maps[0] = Game_Map.new
@mapIndex = 0
oldID = ($game_map) ? $game_map.map_id : 0
setMapChanging(id, @maps[0]) if oldID != 0 && oldID != @maps[0].map_id
setMapChanging(id,@maps[0]) if oldID!=0 && oldID!=@maps[0].map_id
$game_map = @maps[0]
@maps[0].setup(id)
setMapsInRange
@@ -27,33 +27,33 @@ class PokemonMapFactory
end
def map
@mapIndex = 0 if !@mapIndex || @mapIndex < 0
@mapIndex = 0 if !@mapIndex || @mapIndex<0
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]
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]
end
raise "No maps in save file... (all maps empty; mapIndex=#{@mapIndex})"
end
def hasMap?(id)
@maps.each do |map|
return true if map.map_id == id
for map in @maps
return true if map.map_id==id
end
return false
end
def getMapIndex(id)
@maps.length.times do |i|
return i if @maps[i].map_id == id
for i in 0...@maps.length
return i if @maps[i].map_id==id
end
return -1
end
def getMap(id, add = true)
@maps.each do |map|
return map if map.map_id == id
def getMap(id,add=true)
for map in @maps
return map if map.map_id==id
end
map = Game_Map.new
map.setup(id)
@@ -62,29 +62,31 @@ class PokemonMapFactory
end
def getMapNoAdd(id)
return getMap(id, false)
return getMap(id,false)
end
def getNewMap(playerX, playerY, map_id = nil)
id = map_id || $game_map.map_id
MapFactoryHelper.eachConnectionForMap(id) do |conn|
mapidB = nil
newx = 0
newy = 0
if conn[0] == id
mapidB = conn[3]
mapB = MapFactoryHelper.getMapDims(conn[3])
newx = conn[4] - conn[1] + playerX
newy = conn[5] - conn[2] + playerY
else
mapidB = conn[0]
mapB = MapFactoryHelper.getMapDims(conn[0])
newx = conn[1] - conn[4] + playerX
newy = conn[2] - conn[5] + playerY
end
if newx >= 0 && newx < mapB[0] && newy >= 0 && newy < mapB[1]
return [getMapNoAdd(mapidB), newx, newy] if map_id
return [getMap(mapidB), newx, newy]
def getNewMap(playerX,playerY)
id = $game_map.map_id
conns = MapFactoryHelper.getMapConnections
if conns[id]
for conn in conns[id]
mapidB = nil
newx = 0
newy = 0
if conn[0] == id
mapidB = conn[3]
mapB = MapFactoryHelper.getMapDims(conn[3])
newx = conn[4] - conn[1] + playerX
newy = conn[5] - conn[2] + playerY
else
mapidB = conn[0]
mapB = MapFactoryHelper.getMapDims(conn[0])
newx = conn[1] - conn[4] + playerX
newy = conn[2] - conn[5] + playerY
end
if newx >= 0 && newx < mapB[0] && newy >= 0 && newy < mapB[1]
return [getMap(mapidB), newx, newy]
end
end
end
return nil
@@ -93,17 +95,17 @@ class PokemonMapFactory
# Detects whether the player has moved onto a connected map, and if so, causes
# their transfer to that map.
def setCurrentMap
return if $game_player.moving? || $game_player.jumping?
return if $game_map.valid?($game_player.x, $game_player.y)
newmap = getNewMap($game_player.x, $game_player.y)
return if $game_player.moving?
return if $game_map.valid?($game_player.x,$game_player.y)
newmap = getNewMap($game_player.x,$game_player.y)
return if !newmap
oldmap = $game_map.map_id
if oldmap != 0 && oldmap != newmap[0].map_id
setMapChanging(newmap[0].map_id, newmap[0])
oldmap=$game_map.map_id
if oldmap!=0 && oldmap!=newmap[0].map_id
setMapChanging(newmap[0].map_id,newmap[0])
end
$game_map = newmap[0]
@mapIndex = getMapIndex($game_map.map_id)
$game_player.moveto(newmap[1], newmap[2])
$game_player.moveto(newmap[1],newmap[2])
$game_map.update
pbAutoplayOnTransition
$game_map.refresh
@@ -115,128 +117,148 @@ class PokemonMapFactory
return if @fixup
@fixup = true
id = $game_map.map_id
MapFactoryHelper.eachConnectionForMap(id) do |conn|
if conn[0] == id
mapA = getMap(conn[0])
newdispx = ((conn[4] - conn[1]) * Game_Map::REAL_RES_X) + mapA.display_x
newdispy = ((conn[5] - conn[2]) * Game_Map::REAL_RES_Y) + mapA.display_y
if hasMap?(conn[3]) || MapFactoryHelper.mapInRangeById?(conn[3], newdispx, newdispy)
mapB = getMap(conn[3])
mapB.display_x = newdispx if mapB.display_x != newdispx
mapB.display_y = newdispy if mapB.display_y != newdispy
end
else
mapA = getMap(conn[3])
newdispx = ((conn[1] - conn[4]) * Game_Map::REAL_RES_X) + mapA.display_x
newdispy = ((conn[2] - conn[5]) * Game_Map::REAL_RES_Y) + mapA.display_y
if hasMap?(conn[0]) || MapFactoryHelper.mapInRangeById?(conn[0], newdispx, newdispy)
mapB = getMap(conn[0])
mapB.display_x = newdispx if mapB.display_x != newdispx
mapB.display_y = newdispy if mapB.display_y != newdispy
conns = MapFactoryHelper.getMapConnections
if conns[id]
for conn in conns[id]
if conn[0] == id
mapA = getMap(conn[0])
newdispx = (conn[4] - conn[1]) * Game_Map::REAL_RES_X + mapA.display_x
newdispy = (conn[5] - conn[2]) * Game_Map::REAL_RES_Y + mapA.display_y
if hasMap?(conn[3]) || MapFactoryHelper.mapInRangeById?(conn[3], newdispx, newdispy)
mapB = getMap(conn[3])
mapB.display_x = newdispx if mapB.display_x != newdispx
mapB.display_y = newdispy if mapB.display_y != newdispy
end
else
mapA = getMap(conn[3])
newdispx = (conn[1] - conn[4]) * Game_Map::REAL_RES_X + mapA.display_x
newdispy = (conn[2] - conn[5]) * Game_Map::REAL_RES_Y + mapA.display_y
if hasMap?(conn[0]) || MapFactoryHelper.mapInRangeById?(conn[0], newdispx, newdispy)
mapB = getMap(conn[0])
mapB.display_x = newdispx if mapB.display_x != newdispx
mapB.display_y = newdispy if mapB.display_y != newdispy
end
end
end
end
@fixup = false
end
def setMapChanging(newID, newMap)
EventHandlers.trigger(:on_leave_map, newID, newMap)
def setMapChanging(newID,newMap)
Events.onMapChanging.trigger(self,newID,newMap)
end
def setMapChanged(prevMap)
EventHandlers.trigger(:on_enter_map, prevMap)
Events.onMapChange.trigger(self,prevMap)
@mapChanged = true
end
def setSceneStarted(scene)
EventHandlers.trigger(:on_map_or_spriteset_change, scene, @mapChanged)
Events.onMapSceneChange.trigger(self,scene,@mapChanged)
@mapChanged = false
end
# 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)
newmap = getNewMap(x, y, $game_map.map_id)
newmap = getNewMap(x, y)
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
def isPassable?(mapID, x, y, dir = 0, thisEvent = nil)
def isPassable?(mapID, x, y, thisEvent = nil)
thisEvent = $game_player if !thisEvent
map = getMapNoAdd(mapID)
return false if !map
return false if !map.valid?(x, y)
return true if thisEvent.through
# Check passability of tile
return true if $DEBUG && Input.press?(Input::CTRL) && thisEvent.is_a?(Game_Player)
return false if !map.passable?(x, y, dir, thisEvent)
if thisEvent.is_a?(Game_Player)
return false unless ($DEBUG && Input.press?(Input::CTRL)) ||
map.passable?(x, y, 0, thisEvent)
else
return false unless map.passable?(x, y, 0, thisEvent)
end
# 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)
return false if !event.through && event.character_name != ""
end
# Check passability of player
if !thisEvent.is_a?(Game_Player) &&
$game_map.map_id == mapID && $game_player.x == x && $game_player.y == y &&
!$game_player.through && $game_player.character_name != ""
return false
if !thisEvent.is_a?(Game_Player)
if $game_map.map_id == mapID && $game_player.x == x && $game_player.y == y
return false if !$game_player.through && $game_player.character_name != ""
end
end
return true
end
# Only used by follower events
def isPassableStrict?(mapID, x, y, thisEvent = nil)
# Only used by dependent events
def isPassableStrict?(mapID,x,y,thisEvent=nil)
thisEvent = $game_player if !thisEvent
map = getMapNoAdd(mapID)
return false if !map
return false if !map.valid?(x, y)
return false if !map.valid?(x,y)
return true if thisEvent.through
return true if $DEBUG && Input.press?(Input::CTRL) && thisEvent.is_a?(Game_Player)
return false if !map.passableStrict?(x, y, 0, thisEvent)
map.events.each_value do |event|
if thisEvent==$game_player
if !($DEBUG && Input.press?(Input::CTRL))
return false if !map.passableStrict?(x,y,0,thisEvent)
end
else
return false if !map.passableStrict?(x,y,0,thisEvent)
end
for event in map.events.values
next if event == thisEvent || !event.at_coordinate?(x, y)
return false if !event.through && event.character_name != ""
return false if !event.through && event.character_name!=""
end
return true
end
def getTerrainTag(mapid, x, y, countBridge = false)
def getTerrainTag(mapid,x,y,countBridge=false)
map = getMapNoAdd(mapid)
return map.terrain_tag(x, y, countBridge)
return map.terrain_tag(x,y,countBridge)
end
# NOTE: Assumes the event is 1x1 tile in size. Only returns one terrain tag.
def getFacingTerrainTag(dir = nil, event = nil)
tile = getFacingTile(dir, event)
def getFacingTerrainTag(dir=nil,event=nil)
tile = getFacingTile(dir,event)
return GameData::TerrainTag.get(:None) if !tile
return getTerrainTag(tile[0], tile[1], tile[2])
return getTerrainTag(tile[0],tile[1],tile[2])
end
def getTerrainTagFromCoords(mapid, x, y, countBridge = false)
tile = getRealTilePos(mapid, x, y)
def getTerrainTagFromCoords(mapid,x,y,countBridge=false)
tile = getRealTilePos(mapid,x,y)
return GameData::TerrainTag.get(:None) if !tile
return getTerrainTag(tile[0], tile[1], tile[2])
return getTerrainTag(tile[0],tile[1],tile[2])
end
def areConnected?(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
# Returns the coordinate change to go from this position to other position
def getRelativePos(thisMapID, thisX, thisY, otherMapID, otherX, otherY)
if thisMapID == otherMapID # Both events share the same map
return [otherX - thisX, otherY - thisY]
end
MapFactoryHelper.eachConnectionForMap(thisMapID) do |conn|
if conn[0] == otherMapID
posX = conn[4] - conn[1] + otherX - thisX
posY = conn[5] - conn[2] + otherY - thisY
return [posX, posY]
elsif conn[3] == otherMapID
posX = conn[1] - conn[4] + otherX - thisX
posY = conn[2] - conn[5] + otherY - thisY
return [posX, posY]
conns = MapFactoryHelper.getMapConnections
if conns[thisMapID]
for conn in conns[thisMapID]
if conn[0] == otherMapID
posX = thisX + conn[1] - conn[4] + otherX
posY = thisY + conn[2] - conn[5] + otherY
return [posX, posY]
elsif conn[1] == otherMapID
posX = thisX + conn[4] - conn[1] + otherX
posY = thisY + conn[5] - conn[2] + otherY
return [posX, posY]
end
end
end
return [0, 0]
@@ -245,37 +267,38 @@ class PokemonMapFactory
# Gets the distance from this event to another event. Example: If this event's
# coordinates are (2,5) and the other event's coordinates are (5,1), returns
# the array (3,-4), because (5-2=3) and (1-5=-4).
def getThisAndOtherEventRelativePos(thisEvent, otherEvent)
return [0, 0] if !thisEvent || !otherEvent
return getRelativePos(thisEvent.map.map_id, thisEvent.x, thisEvent.y,
otherEvent.map.map_id, otherEvent.x, otherEvent.y)
def getThisAndOtherEventRelativePos(thisEvent,otherEvent)
return [0,0] if !thisEvent || !otherEvent
return getRelativePos(
thisEvent.map.map_id,thisEvent.x,thisEvent.y,
otherEvent.map.map_id,otherEvent.x,otherEvent.y)
end
def getThisAndOtherPosRelativePos(thisEvent, otherMapID, otherX, otherY)
return [0, 0] if !thisEvent
return getRelativePos(thisEvent.map.map_id, thisEvent.x, thisEvent.y,
otherMapID, otherX, otherY)
def getThisAndOtherPosRelativePos(thisEvent,otherMapID,otherX,otherY)
return [0,0] if !thisEvent
return getRelativePos(
thisEvent.map.map_id,thisEvent.x,thisEvent.y,otherMapID,otherX,otherY)
end
# Unused
def getOffsetEventPos(event, xOffset, yOffset)
def getOffsetEventPos(event,xOffset,yOffset)
event = $game_player if !event
return nil if !event
return getRealTilePos(event.map.map_id, event.x + xOffset, event.y + yOffset)
return getRealTilePos(event.map.map_id,event.x+xOffset,event.y+yOffset)
end
# NOTE: Assumes the event is 1x1 tile in size. Only returns one tile.
def getFacingTile(direction = nil, event = nil, steps = 1)
event = $game_player if event.nil?
return [0, 0, 0] if !event
def getFacingTile(direction=nil,event=nil,steps=1)
event = $game_player if event==nil
return [0,0,0] if !event
x = event.x
y = event.y
id = event.map.map_id
direction = event.direction if direction.nil?
return getFacingTileFromPos(id, x, y, direction, steps)
direction = event.direction if direction==nil
return getFacingTileFromPos(id,x,y,direction,steps)
end
def getFacingTileFromPos(mapID, x, y, direction = 0, steps = 1)
def getFacingTileFromPos(mapID,x,y,direction=0,steps=1)
id = mapID
case direction
when 1
@@ -299,35 +322,38 @@ class PokemonMapFactory
x += steps
y -= steps
else
return [id, x, y]
return [id,x,y]
end
return getRealTilePos(mapID, x, y)
return getRealTilePos(mapID,x,y)
end
def getRealTilePos(mapID, x, y)
id = mapID
return [id, x, y] if getMapNoAdd(id).valid?(x, y)
MapFactoryHelper.eachConnectionForMap(id) do |conn|
if conn[0] == id
newX = x + conn[4] - conn[1]
newY = y + conn[5] - conn[2]
next if newX < 0 || newY < 0
dims = MapFactoryHelper.getMapDims(conn[3])
next if newX >= dims[0] || newY >= dims[1]
return [conn[3], newX, newY]
else
newX = x + conn[1] - conn[4]
newY = y + conn[2] - conn[5]
next if newX < 0 || newY < 0
dims = MapFactoryHelper.getMapDims(conn[0])
next if newX >= dims[0] || newY >= dims[1]
return [conn[0], newX, newY]
conns = MapFactoryHelper.getMapConnections
if conns[id]
for conn in conns[id]
if conn[0] == id
newX = x + conn[4] - conn[1]
newY = y + conn[5] - conn[2]
next if newX < 0 || newY < 0
dims = MapFactoryHelper.getMapDims(conn[3])
next if newX >= dims[0] || newY >= dims[1]
return [conn[3], newX, newY]
else
newX = x + conn[1] - conn[4]
newY = y + conn[2] - conn[5]
next if newX < 0 || newY < 0
dims = MapFactoryHelper.getMapDims(conn[0])
next if newX >= dims[0] || newY >= dims[1]
return [conn[0], newX, newY]
end
end
end
return nil
end
def getFacingCoords(x, y, direction = 0, steps = 1)
def getFacingCoords(x,y,direction=0,steps=1)
case direction
when 1
x -= steps
@@ -350,29 +376,36 @@ class PokemonMapFactory
x += steps
y -= steps
end
return [x, y]
return [x,y]
end
def updateMaps(scene)
updateMapsInternal
setSceneStarted(scene) if @mapChanged
$MapFactory.setSceneStarted(scene) if @mapChanged
end
def updateMapsInternal
return if $game_player.moving?
if !MapFactoryHelper.hasConnections?($game_map.map_id)
return if @maps.length == 1
@maps.delete_if { |map| map.map_id != $game_map.map_id }
return if @maps.length==1
for i in 0...@maps.length
@maps[i] = nil if $game_map.map_id!=@maps[i].map_id
end
@maps.compact!
@mapIndex = getMapIndex($game_map.map_id)
return
end
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
old_num_maps = @maps.length
@maps.delete_if { |map| !MapFactoryHelper.mapInRange?(map) }
@mapIndex = getMapIndex($game_map.map_id) if @maps.length != old_num_maps
deleted = false
for i in 0...@maps.length
next if MapFactoryHelper.mapInRange?(@maps[i])
@maps[i] = nil
deleted = true
end
if deleted
@maps.compact!
@mapIndex = getMapIndex($game_map.map_id)
end
end
end
@@ -384,14 +417,12 @@ module MapFactoryHelper
@@MapConnections = nil
@@MapDims = nil
module_function
def clear
def self.clear
@@MapConnections = nil
@@MapDims = nil
end
def getMapConnections
def self.getMapConnections
if !@@MapConnections
@@MapConnections = []
conns = load_data("Data/map_connections.dat")
@@ -429,35 +460,22 @@ module MapFactoryHelper
return @@MapConnections
end
def hasConnections?(id)
def self.hasConnections?(id)
conns = MapFactoryHelper.getMapConnections
return conns[id] ? true : false
end
def mapsConnected?(id1, id2)
MapFactoryHelper.eachConnectionForMap(id1) do |conn|
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)
# Gets the height and width of the map with id
def self.getMapDims(id)
# Create cache if doesn't exist
@@MapDims = [] if !@@MapDims
# Add map to cache if can't be found
if !@@MapDims[id]
begin
map = load_data(sprintf("Data/Map%03d.rxdata", id))
@@MapDims[id] = [map.width, map.height]
@@MapDims[id] = [map.width,map.height]
rescue
@@MapDims[id] = [0, 0]
@@MapDims[id] = [0,0]
end
end
# Return map in cache
@@ -466,32 +484,32 @@ module MapFactoryHelper
# Returns the X or Y coordinate of an edge on the map with id.
# Considers the special strings "N","W","E","S"
def getMapEdge(id, edge)
return 0 if ["N", "W"].include?(edge)
def self.getMapEdge(id,edge)
return 0 if edge=="N" || edge=="W"
dims = getMapDims(id) # Get dimensions
return dims[0] if edge == "E"
return dims[1] if edge == "S"
return dims[0] if edge=="E"
return dims[1] if edge=="S"
return dims[0] # real dimension (use width)
end
def mapInRange?(map)
def self.mapInRange?(map)
range = 6 # Number of tiles
dispx = map.display_x
dispy = map.display_y
return false if dispx >= (map.width + range) * Game_Map::REAL_RES_X
return false if dispy >= (map.height + range) * Game_Map::REAL_RES_Y
return false if dispx <= -(Graphics.width + (range * Game_Map::TILE_WIDTH)) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + (range * Game_Map::TILE_HEIGHT)) * Game_Map::Y_SUBPIXELS
return false if dispx <= -(Graphics.width + range * Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
return true
end
def mapInRangeById?(id, dispx, dispy)
def self.mapInRangeById?(id,dispx,dispy)
range = 6 # Number of tiles
dims = MapFactoryHelper.getMapDims(id)
return false if dispx >= (dims[0] + range) * Game_Map::REAL_RES_X
return false if dispy >= (dims[1] + range) * Game_Map::REAL_RES_Y
return false if dispx <= -(Graphics.width + (range * Game_Map::TILE_WIDTH)) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + (range * Game_Map::TILE_HEIGHT)) * Game_Map::Y_SUBPIXELS
return false if dispx <= -(Graphics.width + range * Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
return true
end
end
@@ -501,8 +519,8 @@ end
#===============================================================================
# Unused
def updateTilesets
maps = $map_factory.maps
maps.each do |map|
map&.updateTileset
maps = $MapFactory.maps
for map in maps
map.updateTileset if map
end
end

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,19 +1,12 @@
#===============================================================================
#
#===============================================================================
class Spriteset_Global
attr_reader :playersprite
@@viewport2 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT)
@@viewport2.z = 200
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)
@weather = RPG::Weather.new(Spriteset_Map.viewport)
@picture_sprites = []
(1..100).each do |i|
for i in 1..100
@picture_sprites.push(Sprite_Picture.new(@@viewport2, $game_screen.pictures[i]))
end
@timer_sprite = Sprite_Timer.new
@@ -21,38 +14,16 @@ class Spriteset_Global
end
def dispose
@follower_sprites.dispose
@follower_sprites = nil
@playersprite.dispose
@playersprite = nil
@weather.dispose
@weather = nil
@picture_sprites.each { |sprite| sprite.dispose }
@picture_sprites.clear
@timer_sprite.dispose
@playersprite = nil
@picture_sprites.clear
@timer_sprite = nil
end
def update
@follower_sprites.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 }
@timer_sprite.update
end

View File

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

View File

@@ -1,336 +1,86 @@
#===============================================================================
#
#===============================================================================
class SpriteAnimation
@@_animations = []
@@_reference_count = {}
def initialize(sprite)
@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)
=begin
A sprite whose sole purpose is to display an animation. This sprite
can be displayed anywhere on the map and is disposed
automatically when its animation is finished.
Used for grass rustling and so forth.
=end
class AnimationSprite < RPG::Sprite
def initialize(animID,map,tileX,tileY,viewport=nil,tinting=false,height=3)
super(viewport)
@tileX = tileX
@tileY = tileY
self.bitmap = Bitmap.new(1, 1)
self.bitmap.clear
@map = map
setCoords
pbDayNightTint(self) if tinting
self.animation($data_animations[animID], true, height)
self.animation($data_animations[animID],true,height)
end
def setCoords
self.x = (((@tileX * Game_Map::REAL_RES_X) - @map.display_x) / Game_Map::X_SUBPIXELS).ceil
self.x = ((@tileX * Game_Map::REAL_RES_X - @map.display_x) / Game_Map::X_SUBPIXELS).ceil
self.x += Game_Map::TILE_WIDTH / 2
self.y = (((@tileY * Game_Map::REAL_RES_Y) - @map.display_y) / Game_Map::Y_SUBPIXELS).ceil
self.y = ((@tileY * Game_Map::REAL_RES_Y - @map.display_y) / Game_Map::Y_SUBPIXELS).ceil
self.y += Game_Map::TILE_HEIGHT
end
def update
return if disposed?
setCoords
def dispose
self.bitmap.dispose
super
dispose if !effect?
end
def update
if !self.disposed?
setCoords
super
self.dispose if !self.effect?
end
end
end
#===============================================================================
#
#===============================================================================
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)
alias _animationSprite_update update unless method_defined?(:_animationSprite_update)
alias _animationSprite_dispose dispose unless method_defined?(:_animationSprite_dispose)
def initialize(map = nil)
@usersprites = []
def initialize(map=nil)
@usersprites=[]
_animationSprite_initialize(map)
end
# Used to display animations that remain in the same location on the map.
# Typically for grass rustling and dust clouds, and other animations that
# 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)
def addUserAnimation(animID,x,y,tinting=false,height=3)
sprite=AnimationSprite.new(animID,$game_map,x,y,@@viewport1,tinting,height)
addUserSprite(sprite)
return sprite
end
def addUserSprite(new_sprite)
@usersprites.each_with_index do |sprite, i|
next if sprite && !sprite.disposed?
@usersprites[i] = new_sprite
return
def addUserSprite(sprite)
for i in 0...@usersprites.length
if @usersprites[i]==nil || @usersprites[i].disposed?
@usersprites[i]=sprite
return
end
end
@usersprites.push(new_sprite)
@usersprites.push(sprite)
end
def dispose
_animationSprite_dispose
@usersprites.each { |sprite| sprite.dispose }
for i in 0...@usersprites.length
@usersprites[i].dispose
end
@usersprites.clear
end
def update
@@viewport3.tone.set(0, 0, 0, 0)
return if @tilemap.disposed?
pbDayNightTint(@tilemap)
@@viewport3.tone.set(0,0,0,0)
_animationSprite_update
@usersprites.each { |sprite| sprite.update if !sprite.disposed? }
@usersprites.delete_if { |sprite| sprite.disposed? }
for i in 0...@usersprites.length
@usersprites[i].update if !@usersprites[i].disposed?
end
end
end

View File

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

File diff suppressed because it is too large Load Diff

View File

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

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
attr_reader :name
def initialize(x, y, width, height, viewport = nil)
super(x, y, width, height)
self.viewport = viewport
self.contents = nil
@name = ""
@_iconbitmap = nil
def initialize(x,y,width,height,viewport=nil)
super(x,y,width,height)
self.viewport=viewport
self.contents=nil
@name=""
@_iconbitmap=nil
end
def dispose
clearBitmaps
clearBitmaps()
super
end
@@ -21,14 +21,14 @@ class IconWindow < SpriteWindow_Base
super
if @_iconbitmap
@_iconbitmap.update
self.contents = @_iconbitmap.bitmap
self.contents=@_iconbitmap.bitmap
end
end
def clearBitmaps
@_iconbitmap&.dispose
@_iconbitmap = nil
self.contents = nil if !self.disposed?
@_iconbitmap.dispose if @_iconbitmap
@_iconbitmap=nil
self.contents=nil if !self.disposed?
end
# Sets the icon's filename. Alias for setBitmap.
@@ -37,35 +37,37 @@ class IconWindow < SpriteWindow_Base
end
# Sets the icon's filename.
def setBitmap(file, hue = 0)
clearBitmaps
@name = file
return if file.nil?
if file == ""
@_iconbitmap = nil
else
@_iconbitmap = AnimatedBitmap.new(file, hue)
def setBitmap(file,hue=0)
clearBitmaps()
@name=file
return if file==nil
if file!=""
@_iconbitmap=AnimatedBitmap.new(file,hue)
# for compatibility
self.contents = @_iconbitmap ? @_iconbitmap.bitmap : nil
self.contents=@_iconbitmap ? @_iconbitmap.bitmap : nil
else
@_iconbitmap=nil
end
end
end
#===============================================================================
# Displays an icon bitmap in a window. Supports animated images.
# Accepts bitmaps and paths to bitmap files in its constructor.
#===============================================================================
class PictureWindow < SpriteWindow_Base
def initialize(pathOrBitmap)
super(0, 0, 32, 32)
self.viewport = viewport
self.contents = nil
@_iconbitmap = nil
super(0,0,32,32)
self.viewport=viewport
self.contents=nil
@_iconbitmap=nil
setBitmap(pathOrBitmap)
end
def dispose
clearBitmaps
clearBitmaps()
super
end
@@ -73,46 +75,47 @@ class PictureWindow < SpriteWindow_Base
super
if @_iconbitmap
if @_iconbitmap.is_a?(Bitmap)
self.contents = @_iconbitmap
self.contents=@_iconbitmap
else
@_iconbitmap.update
self.contents = @_iconbitmap.bitmap
self.contents=@_iconbitmap.bitmap
end
end
end
def clearBitmaps
@_iconbitmap&.dispose
@_iconbitmap = nil
self.contents = nil if !self.disposed?
@_iconbitmap.dispose if @_iconbitmap
@_iconbitmap=nil
self.contents=nil if !self.disposed?
end
# Sets the icon's bitmap or filename. (hue parameter
# is ignored unless pathOrBitmap is a filename)
def setBitmap(pathOrBitmap, hue = 0)
clearBitmaps
if pathOrBitmap && pathOrBitmap != ""
case pathOrBitmap
when Bitmap
@_iconbitmap = pathOrBitmap
self.contents = @_iconbitmap
self.width = @_iconbitmap.width + self.borderX
self.height = @_iconbitmap.height + self.borderY
when AnimatedBitmap
@_iconbitmap = pathOrBitmap
self.contents = @_iconbitmap.bitmap
self.width = @_iconbitmap.bitmap.width + self.borderX
self.height = @_iconbitmap.bitmap.height + self.borderY
def setBitmap(pathOrBitmap,hue=0)
clearBitmaps()
if pathOrBitmap!=nil && pathOrBitmap!=""
if pathOrBitmap.is_a?(Bitmap)
@_iconbitmap=pathOrBitmap
self.contents=@_iconbitmap
self.width=@_iconbitmap.width+self.borderX
self.height=@_iconbitmap.height+self.borderY
elsif pathOrBitmap.is_a?(AnimatedBitmap)
@_iconbitmap=pathOrBitmap
self.contents=@_iconbitmap.bitmap
self.width=@_iconbitmap.bitmap.width+self.borderX
self.height=@_iconbitmap.bitmap.height+self.borderY
else
@_iconbitmap = AnimatedBitmap.new(pathOrBitmap, hue)
self.contents = @_iconbitmap&.bitmap
self.width = self.borderX + (@_iconbitmap&.bitmap&.width || 32)
self.height = self.borderY + (@_iconbitmap&.bitmap&.height || 32)
@_iconbitmap=AnimatedBitmap.new(pathOrBitmap,hue)
self.contents=@_iconbitmap ? @_iconbitmap.bitmap : nil
self.width=@_iconbitmap ? @_iconbitmap.bitmap.width+self.borderX :
32+self.borderX
self.height=@_iconbitmap ? @_iconbitmap.bitmap.height+self.borderY :
32+self.borderY
end
else
@_iconbitmap = nil
self.width = 32 + self.borderX
self.height = 32 + self.borderY
@_iconbitmap=nil
self.width=32+self.borderX
self.height=32+self.borderY
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?
path = file
filename = ""
if file.last != "/" # Isn't just a directory
if file.last != '/' # Isn't just a directory
split_file = file.split(/[\\\/]/)
filename = split_file.pop
path = split_file.join("/") + "/"
path = split_file.join('/') + '/'
end
if filename[/^\[\d+(?:,\d+)?\]/] # Starts with 1 or 2 numbers in square brackets
@bitmap = PngAnimatedBitmap.new(path, filename, hue)
@@ -43,22 +43,22 @@ class PngAnimatedBitmap
def initialize(dir, filename, hue = 0)
@frames = []
@currentFrame = 0
@timer_start = System.uptime
@framecount = 0
panorama = RPG::Cache.load_bitmap(dir, filename, hue)
if filename[/^\[(\d+)(?:,(\d+))?\]/] # Starts with 1 or 2 numbers in brackets
# File has a frame count
numFrames = $1.to_i
duration = $2.to_i # In 1/20ths of a second
duration = 5 if duration == 0
delay = $2.to_i
delay = 10 if delay == 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
raise "Bitmap's width (#{panorama.width}) is not divisible by frame count: #{filename}"
end
@frame_duration = duration / 20.0
@frameDelay = delay
subWidth = panorama.width / numFrames
numFrames.times do |i|
subBitmap = Bitmap.new(subWidth, panorama.height)
for i in 0...numFrames
subBitmap = BitmapWrapper.new(subWidth, panorama.height)
subBitmap.blt(0, 0, panorama, Rect.new(subWidth * i, 0, subWidth, panorama.height))
@frames.push(subBitmap)
end
@@ -68,16 +68,6 @@ class PngAnimatedBitmap
end
end
def dispose
return if @disposed
@frames.each { |f| f.dispose }
@disposed = true
end
def disposed?
return @disposed
end
def [](index)
return @frames[index]
end
@@ -85,6 +75,15 @@ class PngAnimatedBitmap
def width; self.bitmap.width; 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
return @frames[@currentFrame]
end
@@ -93,42 +92,53 @@ class PngAnimatedBitmap
return @currentFrame
end
def length
return @frames.length
def frameDelay(_index)
return @frameDelay
end
# Actually returns the total number of 1/20ths of a second this animation lasts.
def totalFrames
return (@frame_duration * @frames.length * 20).to_i
def length
return @frames.length
end
def each
@frames.each { |item| yield item }
end
def deanimate
(1...@frames.length).each do |i|
@frames[i].dispose
end
@frames = [@frames[0]]
@currentFrame = 0
@frame_duration = 0
return @frames[0]
def totalFrames
return @frameDelay * @frames.length
end
def copy
x = self.clone
x.frames = x.frames.clone
x.frames.each_with_index { |frame, i| x.frames[i] = frame.copy }
return x
def disposed?
return @disposed
end
def update
return if disposed?
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
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
#===============================================================================
@@ -147,7 +157,7 @@ class GifBitmap
rescue
@bitmap = nil
end
@bitmap = Bitmap.new(32, 32) if @bitmap.nil?
@bitmap = BitmapWrapper.new(32, 32) if @bitmap.nil?
@bitmap.play if @bitmap&.animated?
end
@@ -210,19 +220,19 @@ end
#
#===============================================================================
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
end
}
end
def pbGetTileset(name, hue = 0)
def pbGetTileset(name,hue=0)
return AnimatedBitmap.new("Graphics/Tilesets/" + name, hue).deanimate
end
def pbGetAutotile(name, hue = 0)
def pbGetAutotile(name,hue=0)
return AnimatedBitmap.new("Graphics/Autotiles/" + name, hue).deanimate
end
def pbGetAnimation(name, hue = 0)
def pbGetAnimation(name,hue=0)
return AnimatedBitmap.new("Graphics/Animations/" + name, hue).deanimate
end

View File

@@ -6,71 +6,225 @@ class Plane
def refresh; 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
def initialize(color, viewport = nil)
super(viewport)
self.bitmap = Bitmap.new(32, 32)
set_plane_color(color)
class LargePlane < Plane
attr_accessor :borderX
attr_accessor :borderY
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
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
end
def set_plane_color(value)
self.bitmap.fill_rect(0, 0, self.bitmap.width, self.bitmap.height, value)
refresh
def setPlaneColor(value)
self.bitmap.fill_rect(0,0,self.bitmap.width,self.bitmap.height,value)
self.refresh
end
end
#===============================================================================
# A plane class that supports animated images.
#===============================================================================
class AnimatedPlane < Plane
class AnimatedPlane < LargePlane
def initialize(viewport)
super(viewport)
@bitmap = nil
@bitmap=nil
end
def dispose
clear_bitmap
clearBitmaps()
super
end
def setBitmap(file, hue = 0)
clear_bitmap
return if file.nil?
@bitmap = AnimatedBitmap.new(file, hue)
self.bitmap = @bitmap.bitmap if @bitmap
end
def set_panorama(file, hue = 0)
if file.is_a?(String) && file.length > 0
setBitmap("Graphics/Panoramas/" + file, hue)
else
clear_bitmap
def update
super
if @bitmap
@bitmap.update
self.bitmap=@bitmap.bitmap
end
end
def set_fog(file, hue = 0)
if file.is_a?(String) && file.length > 0
setBitmap("Graphics/Fogs/" + file, hue)
else
clear_bitmap
end
def clearBitmaps
@bitmap.dispose if @bitmap
@bitmap=nil
self.bitmap=nil if !self.disposed?
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
@bitmap&.dispose
@bitmap = nil
self.bitmap = nil if !self.disposed?
def setBitmap(file, hue=0)
clearBitmaps()
return if file==nil
@bitmap=AnimatedBitmap.new(file,hue)
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
attr_accessor :text
attr_reader :text
attr_accessor :maxlength
attr_reader :passwordChar
attr_accessor :cursor
def initialize(text)
@maxlength = -1
@text = text
@passwordChar = ""
@cursor = text.scan(/./m).length
@maxlength=-1
@text=text
@passwordChar=""
@cursor=text.scan(/./m).length
end
def text=(value)
@text=value
end
def textChars
chars = text.scan(/./m)
if @passwordChar != ""
chars=text.scan(/./m)
if @passwordChar!=""
chars.length.times { |i| chars[i] = @passwordChar }
end
return chars
end
def passwordChar=(value)
@passwordChar = value || ""
@passwordChar=value ? value : ""
end
def length
@@ -31,70 +35,75 @@ class CharacterEntryHelper
end
def canInsert?
chars = self.text.scan(/./m)
return false if @maxlength >= 0 && chars.length >= @maxlength
chars=self.text.scan(/./m)
return false if @maxlength>=0 && chars.length>=@maxlength
return true
end
def insert(ch)
chars = self.text.scan(/./m)
return false if @maxlength >= 0 && chars.length >= @maxlength
chars.insert(@cursor, ch)
@text = ""
chars.each { |char| @text += char if char }
@cursor += 1
chars=self.text.scan(/./m)
return false if @maxlength>=0 && chars.length>=@maxlength
chars.insert(@cursor,ch)
@text=""
for ch in chars
@text+=ch if ch
end
@cursor+=1
return true
end
def canDelete?
chars = self.text.scan(/./m)
return false if chars.length <= 0 || @cursor <= 0
chars=self.text.scan(/./m)
return false if chars.length<=0 || @cursor<=0
return true
end
def delete
chars = self.text.scan(/./m)
return false if chars.length <= 0 || @cursor <= 0
chars.delete_at(@cursor - 1)
@text = ""
chars.each do |ch|
@text += ch if ch
chars=self.text.scan(/./m)
return false if chars.length<=0 || @cursor<=0
chars.delete_at(@cursor-1)
@text=""
for ch in chars
@text+=ch if ch
end
@cursor -= 1
@cursor-=1
return true
end
#-----------------------------------------------------------------------------
private
def ensure
return if @maxlength < 0
chars = self.text.scan(/./m)
chars = chars[0, @maxlength] if chars.length > @maxlength && @maxlength >= 0
@text = ""
chars.each do |ch|
@text += ch if ch
return if @maxlength<0
chars=self.text.scan(/./m)
if chars.length>@maxlength && @maxlength>=0
chars=chars[0,@maxlength]
end
@text=""
for ch in chars
@text+=ch if ch
end
end
end
#===============================================================================
#
#===============================================================================
class Window_TextEntry < SpriteWindow_Base
def initialize(text, x, y, width, height, heading = nil, usedarkercolor = false)
super(x, y, width, height)
@baseColor, @shadowColor = getDefaultTextColors(self.windowskin)
def initialize(text,x,y,width,height,heading=nil,usedarkercolor=false)
super(x,y,width,height)
colors=getDefaultTextColors(self.windowskin)
@baseColor=colors[0]
@shadowColor=colors[1]
if usedarkercolor
@baseColor = Color.new(16, 24, 32)
@shadowColor = Color.new(168, 184, 184)
@baseColor=Color.new(16,24,32)
@shadowColor=Color.new(168,184,184)
end
@helper = CharacterEntryHelper.new(text)
@heading = heading
@cursor_timer_start = System.uptime
@cursor_shown = true
self.active = true
@helper=CharacterEntryHelper.new(text)
@heading=heading
self.active=true
@frame=0
refresh
end
@@ -111,24 +120,23 @@ class Window_TextEntry < SpriteWindow_Base
end
def text=(value)
@helper.text = value
@helper.text=value
self.refresh
end
def passwordChar=(value)
@helper.passwordChar = value
@helper.passwordChar=value
refresh
end
def maxlength=(value)
@helper.maxlength = value
@helper.maxlength=value
self.refresh
end
def insert(ch)
if @helper.insert(ch)
@cursor_timer_start = System.uptime
@cursor_shown = true
@frame=0
self.refresh
return true
end
@@ -137,8 +145,7 @@ class Window_TextEntry < SpriteWindow_Base
def delete
if @helper.delete
@cursor_timer_start = System.uptime
@cursor_shown = true
@frame=0
self.refresh
return true
end
@@ -146,25 +153,21 @@ class Window_TextEntry < SpriteWindow_Base
end
def update
cursor_to_show = ((System.uptime - @cursor_timer_start) / 0.35).to_i.even?
if cursor_to_show != @cursor_shown
@cursor_shown = cursor_to_show
refresh
end
@frame += 1
@frame %= 20
self.refresh if (@frame%10)==0
return if !self.active
# Moving cursor
if Input.repeat?(Input::LEFT) && Input.press?(Input::ACTION)
if @helper.cursor > 0
@helper.cursor -= 1
@cursor_timer_start = System.uptime
@cursor_shown = true
@frame = 0
self.refresh
end
elsif Input.repeat?(Input::RIGHT) && Input.press?(Input::ACTION)
if @helper.cursor < self.text.scan(/./m).length
@helper.cursor += 1
@cursor_timer_start = System.uptime
@cursor_shown = true
@frame = 0
self.refresh
end
elsif Input.repeat?(Input::BACK) # Backspace
@@ -173,81 +176,79 @@ class Window_TextEntry < SpriteWindow_Base
end
def refresh
self.contents = pbDoEnsureBitmap(self.contents, self.width - self.borderX,
self.height - self.borderY)
bitmap = self.contents
self.contents=pbDoEnsureBitmap(self.contents,self.width-self.borderX,
self.height-self.borderY)
bitmap=self.contents
bitmap.clear
x = 0
y = 0
x=0
y=0
if @heading
textwidth = bitmap.text_size(@heading).width
pbDrawShadowText(bitmap, x, y, textwidth + 4, 32, @heading, @baseColor, @shadowColor)
y += 32
textwidth=bitmap.text_size(@heading).width
pbDrawShadowText(bitmap,x,y, textwidth+4, 32, @heading,@baseColor,@shadowColor)
y+=32
end
x += 4
width = self.width - self.borderX
cursorcolor = Color.new(16, 24, 32)
textscan = self.text.scan(/./m)
scanlength = textscan.length
@helper.cursor = scanlength if @helper.cursor > scanlength
@helper.cursor = 0 if @helper.cursor < 0
startpos = @helper.cursor
fromcursor = 0
while startpos > 0
c = (@helper.passwordChar != "") ? @helper.passwordChar : textscan[startpos - 1]
fromcursor += bitmap.text_size(c).width
break if fromcursor > width - 4
startpos -= 1
x+=4
width=self.width-self.borderX
cursorcolor=Color.new(16,24,32)
textscan=self.text.scan(/./m)
scanlength=textscan.length
@helper.cursor=scanlength if @helper.cursor>scanlength
@helper.cursor=0 if @helper.cursor<0
startpos=@helper.cursor
fromcursor=0
while (startpos>0)
c=(@helper.passwordChar!="") ? @helper.passwordChar : textscan[startpos-1]
fromcursor+=bitmap.text_size(c).width
break if fromcursor>width-4
startpos-=1
end
(startpos...scanlength).each do |i|
c = (@helper.passwordChar != "") ? @helper.passwordChar : textscan[i]
textwidth = bitmap.text_size(c).width
next if c == "\n"
for i in startpos...scanlength
c=(@helper.passwordChar!="") ? @helper.passwordChar : textscan[i]
textwidth=bitmap.text_size(c).width
next if c=="\n"
# 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
if i == @helper.cursor && @cursor_shown
bitmap.fill_rect(x, y + 4, 2, 24, cursorcolor)
if ((@frame/10)&1) == 0 && i==@helper.cursor
bitmap.fill_rect(x,y+4,2,24,cursorcolor)
end
# Add x to drawn text width
x += textwidth
end
if textscan.length == @helper.cursor && @cursor_shown
bitmap.fill_rect(x, y + 4, 2, 24, cursorcolor)
if ((@frame/10)&1) == 0 && textscan.length==@helper.cursor
bitmap.fill_rect(x,y+4,2,24,cursorcolor)
end
end
end
#===============================================================================
#
#===============================================================================
class Window_TextEntry_Keyboard < Window_TextEntry
def update
cursor_to_show = ((System.uptime - @cursor_timer_start) / 0.35).to_i.even?
if cursor_to_show != @cursor_shown
@cursor_shown = cursor_to_show
refresh
end
@frame+=1
@frame%=20
self.refresh if ((@frame%10)==0)
return if !self.active
# Moving cursor
if Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT)
if @helper.cursor > 0
@helper.cursor -= 1
@cursor_timer_start = System.uptime
@cursor_shown = true
@helper.cursor-=1
@frame=0
self.refresh
end
return
elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT)
if @helper.cursor < self.text.scan(/./m).length
@helper.cursor += 1
@cursor_timer_start = System.uptime
@cursor_shown = true
@helper.cursor+=1
@frame=0
self.refresh
end
return
elsif Input.triggerex?(:BACKSPACE) || Input.repeatex?(:BACKSPACE)
self.delete if @helper.cursor > 0
self.delete if @helper.cursor>0
return
elsif Input.triggerex?(:RETURN) || Input.triggerex?(:ESCAPE)
return
@@ -256,20 +257,23 @@ class Window_TextEntry_Keyboard < Window_TextEntry
end
end
#===============================================================================
#
#===============================================================================
class Window_MultilineTextEntry < SpriteWindow_Base
def initialize(text, x, y, width, height)
super(x, y, width, height)
@baseColor, @shadowColor = getDefaultTextColors(self.windowskin)
@helper = CharacterEntryHelper.new(text)
@firstline = 0
@cursorLine = 0
@cursorColumn = 0
@cursor_timer_start = System.uptime
@cursor_shown = true
self.active = true
def initialize(text,x,y,width,height)
super(x,y,width,height)
colors=getDefaultTextColors(self.windowskin)
@baseColor=colors[0]
@shadowColor=colors[1]
@helper=CharacterEntryHelper.new(text)
@firstline=0
@cursorLine=0
@cursorColumn=0
@frame=0
self.active=true
refresh
end
@@ -277,12 +281,12 @@ class Window_MultilineTextEntry < SpriteWindow_Base
attr_reader :shadowColor
def baseColor=(value)
@baseColor = value
@baseColor=value
refresh
end
def shadowColor=(value)
@shadowColor = value
@shadowColor=value
refresh
end
@@ -295,24 +299,23 @@ class Window_MultilineTextEntry < SpriteWindow_Base
end
def text=(value)
@helper.text = value
@textchars = nil
@helper.text=value
@textchars=nil
self.refresh
end
def maxlength=(value)
@helper.maxlength = value
@textchars = nil
@helper.maxlength=value
@textchars=nil
self.refresh
end
def insert(ch)
@helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn)
@helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn)
if @helper.insert(ch)
@cursor_timer_start = System.uptime
@cursor_shown = true
@textchars = nil
moveCursor(0, 1)
@frame=0
@textchars=nil
moveCursor(0,1)
self.refresh
return true
end
@@ -320,12 +323,11 @@ class Window_MultilineTextEntry < SpriteWindow_Base
end
def delete
@helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn)
@helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn)
if @helper.delete
@cursor_timer_start = System.uptime
@cursor_shown = true
moveCursor(0, -1) # use old textchars
@textchars = nil
@frame=0
moveCursor(0,-1) # use old textchars
@textchars=nil
self.refresh
return true
end
@@ -334,155 +336,163 @@ class Window_MultilineTextEntry < SpriteWindow_Base
def getTextChars
if !@textchars
@textchars = getLineBrokenText(self.contents, @helper.text,
self.contents.width, nil)
@textchars=getLineBrokenText(self.contents,@helper.text,
self.contents.width,nil)
end
return @textchars
end
def getTotalLines
textchars = getTextChars
return 1 if textchars.length == 0
tchar = textchars[textchars.length - 1]
return tchar[5] + 1
textchars=getTextChars
return 1 if textchars.length==0
tchar=textchars[textchars.length-1]
return tchar[5]+1
end
def getLineY(line)
textchars = getTextChars
return 0 if textchars.length == 0
totallines = getTotalLines
line = 0 if line < 0
line = totallines - 1 if line >= totallines
maximumY = 0
textchars.each do |text|
thisline = text[5]
y = text[2]
return y if thisline == line
maximumY = y if maximumY < y
textchars=getTextChars
return 0 if textchars.length==0
totallines=getTotalLines()
line=0 if line<0
line=totallines-1 if line>=totallines
maximumY=0
for i in 0...textchars.length
thisline=textchars[i][5]
y=textchars[i][2]
return y if thisline==line
maximumY=y if maximumY<y
end
return maximumY
end
def getColumnsInLine(line)
textchars = getTextChars
return 0 if textchars.length == 0
totallines = getTotalLines
line = 0 if line < 0
line = totallines - 1 if line >= totallines
endpos = 0
textchars.each do |text|
thisline = text[5]
thislength = text[8]
endpos += thislength if thisline == line
textchars=getTextChars
return 0 if textchars.length==0
totallines=getTotalLines()
line=0 if line<0
line=totallines-1 if line>=totallines
endpos=0
for i in 0...textchars.length
thisline=textchars[i][5]
thislength=textchars[i][8]
endpos+=thislength if thisline==line
end
return endpos
end
def getPosFromLineAndColumn(line, column)
textchars = getTextChars
return 0 if textchars.length == 0
totallines = getTotalLines
line = 0 if line < 0
line = totallines - 1 if line >= totallines
endpos = 0
textchars.each do |text|
thisline = text[5]
thispos = text[6]
thiscolumn = text[7]
thislength = text[8]
next if thisline != line
endpos = thispos + thislength
next if column < thiscolumn || column > thiscolumn + thislength || thislength == 0
return thispos + column - thiscolumn
def getPosFromLineAndColumn(line,column)
textchars=getTextChars
return 0 if textchars.length==0
totallines=getTotalLines()
line=0 if line<0
line=totallines-1 if line>=totallines
endpos=0
for i in 0...textchars.length
thisline=textchars[i][5]
thispos=textchars[i][6]
thiscolumn=textchars[i][7]
thislength=textchars[i][8]
if thisline==line
endpos=thispos+thislength
# echoln [endpos,thispos+(column-thiscolumn),textchars[i]]
if column>=thiscolumn && column<=thiscolumn+thislength && thislength>0
return thispos+(column-thiscolumn)
end
end
end
# if endpos==0
# echoln [totallines,line,column]
# echoln textchars
# end
# echoln "endpos=#{endpos}"
return endpos
end
def getLastVisibleLine
getTextChars
textheight = [1, self.contents.text_size("X").height].max
lastVisible = @firstline + ((self.height - self.borderY) / textheight) - 1
getTextChars()
textheight=[1,self.contents.text_size("X").height].max
lastVisible=@firstline+((self.height-self.borderY)/textheight)-1
return lastVisible
end
def updateCursorPos(doRefresh)
# Calculate new cursor position
@helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn)
@helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn)
if doRefresh
@cursor_timer_start = System.uptime
@cursor_shown = true
@frame=0
self.refresh
end
@firstline = @cursorLine if @cursorLine < @firstline
lastVisible = getLastVisibleLine
@firstline += (@cursorLine - lastVisible) if @cursorLine > lastVisible
@firstline=@cursorLine if @cursorLine<@firstline
lastVisible=getLastVisibleLine()
@firstline+=(@cursorLine-lastVisible) if @cursorLine>lastVisible
end
def moveCursor(lineOffset, columnOffset)
# Move column offset first, then lines (since column offset
# can affect line offset)
# echoln ["beforemoving",@cursorLine,@cursorColumn]
totalColumns = getColumnsInLine(@cursorLine) # check current line
totalLines = getTotalLines
oldCursorLine = @cursorLine
oldCursorColumn = @cursorColumn
@cursorColumn += columnOffset
if @cursorColumn < 0 && @cursorLine > 0
totalColumns=getColumnsInLine(@cursorLine) # check current line
totalLines=getTotalLines()
oldCursorLine=@cursorLine
oldCursorColumn=@cursorColumn
@cursorColumn+=columnOffset
if @cursorColumn<0 && @cursorLine>0
# Will happen if cursor is moved left from the beginning of a line
@cursorLine -= 1
@cursorColumn = getColumnsInLine(@cursorLine)
elsif @cursorColumn > totalColumns && @cursorLine < totalLines - 1
@cursorLine-=1
@cursorColumn=getColumnsInLine(@cursorLine)
elsif @cursorColumn>totalColumns && @cursorLine<totalLines-1
# Will happen if cursor is moved right from the end of a line
@cursorLine += 1
@cursorColumn = 0
@cursorLine+=1
@cursorColumn=0
end
# Ensure column bounds
totalColumns = getColumnsInLine(@cursorLine)
@cursorColumn = totalColumns if @cursorColumn > totalColumns
@cursorColumn = 0 if @cursorColumn < 0 # totalColumns can be 0
totalColumns=getColumnsInLine(@cursorLine)
@cursorColumn=totalColumns if @cursorColumn>totalColumns
@cursorColumn=0 if @cursorColumn<0 # totalColumns can be 0
# Move line offset
@cursorLine += lineOffset
@cursorLine = 0 if @cursorLine < 0
@cursorLine = totalLines - 1 if @cursorLine >= totalLines
@cursorLine+=lineOffset
@cursorLine=0 if @cursorLine<0
@cursorLine=totalLines-1 if @cursorLine>=totalLines
# Ensure column bounds again
totalColumns = getColumnsInLine(@cursorLine)
@cursorColumn = totalColumns if @cursorColumn > totalColumns
@cursorColumn = 0 if @cursorColumn < 0 # totalColumns can be 0
updateCursorPos(oldCursorLine != @cursorLine || oldCursorColumn != @cursorColumn)
totalColumns=getColumnsInLine(@cursorLine)
@cursorColumn=totalColumns if @cursorColumn>totalColumns
@cursorColumn=0 if @cursorColumn<0 # totalColumns can be 0
updateCursorPos(
oldCursorLine!=@cursorLine ||
oldCursorColumn!=@cursorColumn
)
# echoln ["aftermoving",@cursorLine,@cursorColumn]
end
def update
cursor_to_show = ((System.uptime - @cursor_timer_start) / 0.35).to_i.even?
if cursor_to_show != @cursor_shown
@cursor_shown = cursor_to_show
refresh
end
@frame+=1
@frame%=20
self.refresh if ((@frame%10)==0)
return if !self.active
# Moving cursor
if Input.triggerex?(:UP) || Input.repeatex?(:UP)
moveCursor(-1, 0)
moveCursor(-1,0)
return
elsif Input.triggerex?(:DOWN) || Input.repeatex?(:DOWN)
moveCursor(1, 0)
moveCursor(1,0)
return
elsif Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT)
moveCursor(0, -1)
moveCursor(0,-1)
return
elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT)
moveCursor(0, 1)
moveCursor(0,1)
return
end
if Input.press?(Input::CTRL) && Input.triggerex?(:HOME)
# Move cursor to beginning
@cursorLine = 0
@cursorColumn = 0
@cursorLine=0
@cursorColumn=0
updateCursorPos(true)
return
elsif Input.press?(Input::CTRL) && Input.triggerex?(:END)
# Move cursor to end
@cursorLine = getTotalLines - 1
@cursorColumn = getColumnsInLine(@cursorLine)
@cursorLine=getTotalLines()-1
@cursorColumn=getColumnsInLine(@cursorLine)
updateCursorPos(true)
return
elsif Input.triggerex?(:RETURN) || Input.repeatex?(:RETURN)
@@ -492,61 +502,63 @@ class Window_MultilineTextEntry < SpriteWindow_Base
self.delete
return
end
Input.gets.each_char { |c| insert(c) }
Input.gets.each_char{|c|insert(c)}
end
def refresh
newContents = pbDoEnsureBitmap(self.contents, self.width - self.borderX,
self.height - self.borderY)
@textchars = nil if self.contents != newContents
self.contents = newContents
bitmap = self.contents
newContents=pbDoEnsureBitmap(self.contents,self.width-self.borderX,
self.height-self.borderY)
@textchars=nil if self.contents!=newContents
self.contents=newContents
bitmap=self.contents
bitmap.clear
getTextChars
height = self.height - self.borderY
cursorcolor = Color.black
textchars = getTextChars
startY = getLineY(@firstline)
textchars.each do |text|
thisline = text[5]
thislength = text[8]
textY = text[2] - startY
height=self.height-self.borderY
cursorcolor=Color.new(0,0,0)
textchars=getTextChars()
startY=getLineY(@firstline)
for i in 0...textchars.length
thisline=textchars[i][5]
thiscolumn=textchars[i][7]
thislength=textchars[i][8]
textY=textchars[i][2]-startY
# 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
break if textY >= height
c = text[0]
c=textchars[i][0]
# Don't draw spaces
next if c == " "
textwidth = text[3] + 4 # add 4 to prevent draw_text from stretching text
textheight = text[4]
next if c==" "
textwidth=textchars[i][3]+4 # add 4 to prevent draw_text from stretching text
textheight=textchars[i][4]
# Draw text
pbDrawShadowText(bitmap, text[1], textY, textwidth, textheight, c, @baseColor, @shadowColor)
pbDrawShadowText(bitmap, textchars[i][1], textY, textwidth, textheight, c, @baseColor, @shadowColor)
end
# Draw cursor
if @cursor_shown
textheight = bitmap.text_size("X").height
cursorY = (textheight * @cursorLine) - startY
cursorX = 0
textchars.each do |text|
thisline = text[5]
thiscolumn = text[7]
thislength = text[8]
next if thisline != @cursorLine || @cursorColumn < thiscolumn ||
@cursorColumn > thiscolumn + thislength
cursorY = text[2] - startY
cursorX = text[1]
textheight = text[4]
posToCursor = @cursorColumn - thiscolumn
if posToCursor >= 0
partialString = text[0].scan(/./m)[0, posToCursor].join
cursorX += bitmap.text_size(partialString).width
if ((@frame/10)&1) == 0
textheight=bitmap.text_size("X").height
cursorY=(textheight*@cursorLine)-startY
cursorX=0
for i in 0...textchars.length
thisline=textchars[i][5]
thiscolumn=textchars[i][7]
thislength=textchars[i][8]
if thisline==@cursorLine && @cursorColumn>=thiscolumn &&
@cursorColumn<=thiscolumn+thislength
cursorY=textchars[i][2]-startY
cursorX=textchars[i][1]
textheight=textchars[i][4]
posToCursor=@cursorColumn-thiscolumn
if posToCursor>=0
partialString=textchars[i][0].scan(/./m)[0,posToCursor].join("")
cursorX+=bitmap.text_size(partialString).width
end
break
end
break
end
cursorY += 4
cursorHeight = [4, textheight - 4, bitmap.text_size("X").height - 4].max
bitmap.fill_rect(cursorX, cursorY, 2, cursorHeight, cursorcolor)
cursorY+=4
cursorHeight=[4,textheight-4,bitmap.text_size("X").height-4].max
bitmap.fill_rect(cursorX,cursorY,2,cursorHeight,cursorcolor)
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.
#===============================================================================
def getOggPage(file)
fgetdw = proc { |f|
(f.eof? ? 0 : (f.read(4).unpack("V")[0] || 0))
fgetdw = proc { |file|
(file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0))
}
dw = fgetdw.call(file)
return nil if dw != 0x5367674F
@@ -19,8 +35,8 @@ end
# internal function
def oggfiletime(file)
fgetdw = proc { |f|
(f.eof? ? 0 : (f.read(4).unpack("V")[0] || 0))
fgetdw = proc { |file|
(file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0))
}
pages = []
page = nil
@@ -35,8 +51,8 @@ def oggfiletime(file)
i = -1
pcmlengths = []
rates = []
pages.each do |pg|
header = pg[0]
for page in pages
header = page[0]
serial = header[10, 4].unpack("V")
frame = header[2, 8].unpack("C*")
frameno = frame[7]
@@ -49,7 +65,7 @@ def oggfiletime(file)
frameno = (frameno << 8) | frame[0]
if serial != curserial
curserial = serial
file.pos = pg[1]
file.pos = page[1]
packtype = (file.read(1)[0].ord rescue 0)
string = file.read(6)
return -1 if string != "vorbis"
@@ -62,26 +78,28 @@ def oggfiletime(file)
pcmlengths[i] = frameno
end
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
end
# Gets the length of an audio file in seconds. Supports WAV, MP3, and OGG files.
def getPlayTime(filename)
if FileTest.exist?(filename)
if safeExists?(filename)
return [getPlayTime2(filename), 0].max
elsif FileTest.exist?(filename + ".wav")
elsif safeExists?(filename + ".wav")
return [getPlayTime2(filename + ".wav"), 0].max
elsif FileTest.exist?(filename + ".mp3")
elsif safeExists?(filename + ".mp3")
return [getPlayTime2(filename + ".mp3"), 0].max
elsif FileTest.exist?(filename + ".ogg")
elsif safeExists?(filename + ".ogg")
return [getPlayTime2(filename + ".ogg"), 0].max
end
return 0
end
def getPlayTime2(filename)
return -1 if !FileTest.exist?(filename)
return -1 if !safeExists?(filename)
time = -1
fgetdw = proc { |file|
(file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0))
@@ -89,40 +107,39 @@ def getPlayTime2(filename)
fgetw = proc { |file|
(file.eof? ? 0 : (file.read(2).unpack("v")[0] || 0))
}
File.open(filename, "rb") do |file|
File.open(filename, "rb") { |file|
file.pos = 0
fdw = fgetdw.call(file)
case fdw
when 0x46464952 # "RIFF"
if fdw == 0x46464952 # "RIFF"
filesize = fgetdw.call(file)
wave = fgetdw.call(file)
return -1 if wave != 0x45564157 # "WAVE"
fmt = fgetdw.call(file)
return -1 if fmt != 0x20746d66 # "fmt "
fgetdw.call(file) # fmtsize
fgetw.call(file) # format
fgetw.call(file) # channels
fgetdw.call(file) # rate
fmtsize = fgetdw.call(file)
format = fgetw.call(file)
channels = fgetw.call(file)
rate = fgetdw.call(file)
bytessec = fgetdw.call(file)
return -1 if bytessec == 0
fgetw.call(file) # bytessample
fgetw.call(file) # bitssample
bytessample = fgetw.call(file)
bitssample = fgetw.call(file)
data = fgetdw.call(file)
return -1 if data != 0x61746164 # "data"
datasize = fgetdw.call(file)
time = datasize.to_f / bytessec
time = (datasize*1.0)/bytessec
return time
when 0x5367674F # "OggS"
elsif fdw == 0x5367674F # "OggS"
file.pos = 0
time = oggfiletime(file)
return time
end
file.pos = 0
# Find the length of an MP3 file
loop do
while true
rstr = ""
ateof = false
until file.eof?
while !file.eof?
if (file.read(1)[0] rescue 0) == 0xFF
begin
rstr = file.read(3)
@@ -135,20 +152,20 @@ def getPlayTime2(filename)
break if ateof || !rstr || rstr.length != 3
if rstr[0] == 0xFB
t = rstr[1] >> 4
next if [0, 15].include?(t)
freqs = [44_100, 22_050, 11_025, 48_000]
next if t == 0 || t == 15
freqs = [44100, 22050, 11025, 48000]
bitrates = [32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320]
bitrate = bitrates[t]
t = (rstr[1] >> 2) & 3
freq = freqs[t]
t = (rstr[1] >> 1) & 1
filesize = FileTest.size(filename)
frameLength = ((144_000 * bitrate) / freq) + t
frameLength = ((144000 * bitrate) / freq) + t
numFrames = filesize / (frameLength + 4)
time = (numFrames * 1152.0 / freq)
break
end
end
end
}
return time
end

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module GameData
#=============================================================================
# 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.
#=============================================================================
module ClassMethods
def schema
return self::SCHEMA
end
def register(hash)
self::DATA[hash[:id]] = self::DATA[hash[:id_number]] = self.new(hash)
end
@@ -33,6 +26,9 @@ module GameData
validate other => [Symbol, self, String, Integer]
return other if other.is_a?(self)
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)
return self::DATA[other]
end
@@ -44,6 +40,9 @@ module GameData
validate other => [Symbol, self, String, Integer]
return other if other.is_a?(self)
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
end
@@ -55,12 +54,8 @@ module GameData
# Yields all data in order of their id_number.
def each
sorted_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) }
end
def count
return self::DATA.length / 2
keys = self::DATA.keys.sort { |a, b| self::DATA[a].id_number <=> self::DATA[b].id_number }
keys.each { |key| yield self::DATA[key] if !key.is_a?(Integer) }
end
def load
@@ -79,10 +74,6 @@ module GameData
# For data that is only known by a symbol.
#=============================================================================
module ClassMethodsSymbols
def schema
return self::SCHEMA
end
def register(hash)
self::DATA[hash[:id]] = self.new(hash)
end
@@ -123,21 +114,12 @@ module GameData
return self::DATA.keys
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.
def each_alphabetically
def each
keys = self::DATA.keys.sort { |a, b| self::DATA[a].real_name <=> self::DATA[b].real_name }
keys.each { |key| yield self::DATA[key] }
end
def count
return self::DATA.length
end
def load
const_set(:DATA, load_data("Data/#{self::DATA_FILENAME}"))
end
@@ -154,10 +136,6 @@ module GameData
# For data that is only known by an ID number.
#=============================================================================
module ClassMethodsIDNumbers
def schema
return self::SCHEMA
end
def register(hash)
self::DATA[hash[:id]] = self.new(hash)
end
@@ -193,16 +171,12 @@ module GameData
return self::DATA.keys
end
# Yields all data in numerical order.
# Yields all data in numberical order.
def each
keys = self::DATA.keys.sort
keys.each { |key| yield self::DATA[key] }
end
def count
return self::DATA.length
end
def load
const_set(:DATA, load_data("Data/#{self::DATA_FILENAME}"))
end
@@ -222,65 +196,34 @@ module GameData
# @return [Boolean] whether other represents the same thing as this thing
def ==(other)
return false if other.nil?
case other
when Symbol
if other.is_a?(Symbol)
return @id == other
when self.class
elsif other.is_a?(self.class)
return @id == other.id
when String
return @id == other.to_sym
when Integer
elsif other.is_a?(String)
return @id_number == other.to_sym
elsif other.is_a?(Integer)
return @id_number == other
end
return false
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
#=============================================================================
# A bulk loader method for all data stored in .dat files in the Data folder.
#=============================================================================
def self.load_all
self.constants.each do |c|
next if !self.const_get(c).is_a?(Class)
self.const_get(c).load if self.const_get(c).const_defined?(:DATA_FILENAME)
end
end
def self.get_all_data_filenames
ret = []
self.constants.each do |c|
next if !self.const_get(c).is_a?(Class)
next if !self.const_get(c).const_defined?(:DATA_FILENAME)
if self.const_get(c).const_defined?(:OPTIONAL) && self.const_get(c)::OPTIONAL
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
Type.load
Ability.load
Move.load
Item.load
BerryPlant.load
Species.load
Ribbon.load
Encounter.load
TrainerType.load
Trainer.load
Metadata.load
MapMetadata.load
end
end

View File

@@ -1,6 +1,3 @@
#===============================================================================
#
#===============================================================================
module GameData
class GrowthRate
attr_reader :id
@@ -27,8 +24,6 @@ module GameData
return Settings::MAXIMUM_LEVEL
end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@real_name = hash[:name] || "Unnamed"
@@ -69,7 +64,7 @@ module GameData
return ArgumentError.new("Exp amount #{level} is invalid.") if !exp || exp < 0
max = GrowthRate.max_level
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)
end
return max
@@ -77,47 +72,46 @@ module GameData
end
end
#===============================================================================
#
#===============================================================================
GameData::GrowthRate.register({
:id => :Medium, # Also known as Medium Fast
:name => _INTL("Medium"),
:exp_values => [-1,
0, 8, 27, 64, 125, 216, 343, 512, 729, 1000,
1331, 1728, 2197, 2744, 3375, 4096, 4913, 5832, 6859, 8000,
9261, 10648, 12167, 13824, 15625, 17576, 19683, 21952, 24389, 27000,
29791, 32768, 35937, 39304, 42875, 46656, 50653, 54872, 59319, 64000,
68921, 74088, 79507, 85184, 91125, 97336, 103823, 110592, 117649, 125000,
132651, 140608, 148877, 157464, 166375, 175616, 185193, 195112, 205379, 216000,
226981, 238328, 250047, 262144, 274625, 287496, 300763, 314432, 328509, 343000,
357911, 373248, 389017, 405224, 421875, 438976, 456533, 474552, 493039, 512000,
531441, 551368, 571787, 592704, 614125, 636056, 658503, 681472, 704969, 729000,
753571, 778688, 804357, 830584, 857375, 884736, 912673, 941192, 970299, 1000000],
:exp_formula => proc { |level| next level**3 }
0, 8, 27, 64, 125, 216, 343, 512, 729, 1000,
1331, 1728, 2197, 2744, 3375, 4096, 4913, 5832, 6859, 8000,
9261, 10648, 12167, 13824, 15625, 17576, 19683, 21952, 24389, 27000,
29791, 32768, 35937, 39304, 42875, 46656, 50653, 54872, 59319, 64000,
68921, 74088, 79507, 85184, 91125, 97336, 103823, 110592, 117649, 125000,
132651, 140608, 148877, 157464, 166375, 175616, 185193, 195112, 205379, 216000,
226981, 238328, 250047, 262144, 274625, 287496, 300763, 314432, 328509, 343000,
357911, 373248, 389017, 405224, 421875, 438976, 456533, 474552, 493039, 512000,
531441, 551368, 571787, 592704, 614125, 636056, 658503, 681472, 704969, 729000,
753571, 778688, 804357, 830584, 857375, 884736, 912673, 941192, 970299, 1000000],
:exp_formula => proc { |level| next level ** 3 }
})
# Erratic (600000):
# For levels 0-50: n**3 * (100 - n) / 50
# For levels 51-68: n**3 * (150 - n) / 100
# For levels 69-98: n**3 * ((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
GameData::GrowthRate.register({
:id => :Erratic,
:name => _INTL("Erratic"),
:exp_values => [-1,
0, 15, 52, 122, 237, 406, 637, 942, 1326, 1800,
2369, 3041, 3822, 4719, 5737, 6881, 8155, 9564, 11111, 12800,
14632, 16610, 18737, 21012, 23437, 26012, 28737, 31610, 34632, 37800,
41111, 44564, 48155, 51881, 55737, 59719, 63822, 68041, 72369, 76800,
81326, 85942, 90637, 95406, 100237, 105122, 110052, 115015, 120001, 125000,
131324, 137795, 144410, 151165, 158056, 165079, 172229, 179503, 186894, 194400,
202013, 209728, 217540, 225443, 233431, 241496, 249633, 257834, 267406, 276458,
286328, 296358, 305767, 316074, 326531, 336255, 346965, 357812, 367807, 378880,
390077, 400293, 411686, 423190, 433572, 445239, 457001, 467489, 479378, 491346,
501878, 513934, 526049, 536557, 548720, 560922, 571333, 583539, 591882, 600000],
:exp_formula => proc { |level| next ((level**4) + ((level**3) * 2000)) / 3500 }
0, 15, 52, 122, 237, 406, 637, 942, 1326, 1800,
2369, 3041, 3822, 4719, 5737, 6881, 8155, 9564, 11111, 12800,
14632, 16610, 18737, 21012, 23437, 26012, 28737, 31610, 34632, 37800,
41111, 44564, 48155, 51881, 55737, 59719, 63822, 68041, 72369, 76800,
81326, 85942, 90637, 95406, 100237, 105122, 110052, 115015, 120001, 125000,
131324, 137795, 144410, 151165, 158056, 165079, 172229, 179503, 186894, 194400,
202013, 209728, 217540, 225443, 233431, 241496, 249633, 257834, 267406, 276458,
286328, 296358, 305767, 316074, 326531, 336255, 346965, 357812, 367807, 378880,
390077, 400293, 411686, 423190, 433572, 445239, 457001, 467489, 479378, 491346,
501878, 513934, 526049, 536557, 548720, 560922, 571333, 583539, 591882, 600000],
:exp_formula => proc { |level| next (level ** 4) * 3 / 500 }
})
# Fluctuating (1640000):
@@ -128,18 +122,19 @@ GameData::GrowthRate.register({
:id => :Fluctuating,
:name => _INTL("Fluctuating"),
:exp_values => [-1,
0, 4, 13, 32, 65, 112, 178, 276, 393, 540,
745, 967, 1230, 1591, 1957, 2457, 3046, 3732, 4526, 5440,
6482, 7666, 9003, 10506, 12187, 14060, 16140, 18439, 20974, 23760,
26811, 30146, 33780, 37731, 42017, 46656, 50653, 55969, 60505, 66560,
71677, 78533, 84277, 91998, 98415, 107069, 114205, 123863, 131766, 142500,
151222, 163105, 172697, 185807, 196322, 210739, 222231, 238036, 250562, 267840,
281456, 300293, 315059, 335544, 351520, 373744, 390991, 415050, 433631, 459620,
479600, 507617, 529063, 559209, 582187, 614566, 639146, 673863, 700115, 737280,
765275, 804997, 834809, 877201, 908905, 954084, 987754, 1035837, 1071552, 1122660,
1160499, 1214753, 1254796, 1312322, 1354652, 1415577, 1460276, 1524731, 1571884, 1640000],
0, 4, 13, 32, 65, 112, 178, 276, 393, 540,
745, 967, 1230, 1591, 1957, 2457, 3046, 3732, 4526, 5440,
6482, 7666, 9003, 10506, 12187, 14060, 16140, 18439, 20974, 23760,
26811, 30146, 33780, 37731, 42017, 46656, 50653, 55969, 60505, 66560,
71677, 78533, 84277, 91998, 98415, 107069, 114205, 123863, 131766, 142500,
151222, 163105, 172697, 185807, 196322, 210739, 222231, 238036, 250562, 267840,
281456, 300293, 315059, 335544, 351520, 373744, 390991, 415050, 433631, 459620,
479600, 507617, 529063, 559209, 582187, 614566, 639146, 673863, 700115, 737280,
765275, 804997, 834809, 877201, 908905, 954084, 987754, 1035837, 1071552, 1122660,
1160499, 1214753, 1254796, 1312322, 1354652, 1415577, 1460276, 1524731, 1571884, 1640000],
: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
}
})
@@ -147,49 +142,49 @@ GameData::GrowthRate.register({
:id => :Parabolic, # Also known as Medium Slow
:name => _INTL("Parabolic"),
:exp_values => [-1,
0, 9, 57, 96, 135, 179, 236, 314, 419, 560,
742, 973, 1261, 1612, 2035, 2535, 3120, 3798, 4575, 5460,
6458, 7577, 8825, 10208, 11735, 13411, 15244, 17242, 19411, 21760,
24294, 27021, 29949, 33084, 36435, 40007, 43808, 47846, 52127, 56660,
61450, 66505, 71833, 77440, 83335, 89523, 96012, 102810, 109923, 117360,
125126, 133229, 141677, 150476, 159635, 169159, 179056, 189334, 199999, 211060,
222522, 234393, 246681, 259392, 272535, 286115, 300140, 314618, 329555, 344960,
360838, 377197, 394045, 411388, 429235, 447591, 466464, 485862, 505791, 526260,
547274, 568841, 590969, 613664, 636935, 660787, 685228, 710266, 735907, 762160,
789030, 816525, 844653, 873420, 902835, 932903, 963632, 995030, 1027103, 1059860],
:exp_formula => proc { |level| next ((level**3) * 6 / 5) - (15 * (level**2)) + (100 * level) - 140 }
0, 9, 57, 96, 135, 179, 236, 314, 419, 560,
742, 973, 1261, 1612, 2035, 2535, 3120, 3798, 4575, 5460,
6458, 7577, 8825, 10208, 11735, 13411, 15244, 17242, 19411, 21760,
24294, 27021, 29949, 33084, 36435, 40007, 43808, 47846, 52127, 56660,
61450, 66505, 71833, 77440, 83335, 89523, 96012, 102810, 109923, 117360,
125126, 133229, 141677, 150476, 159635, 169159, 179056, 189334, 199999, 211060,
222522, 234393, 246681, 259392, 272535, 286115, 300140, 314618, 329555, 344960,
360838, 377197, 394045, 411388, 429235, 447591, 466464, 485862, 505791, 526260,
547274, 568841, 590969, 613664, 636935, 660787, 685228, 710266, 735907, 762160,
789030, 816525, 844653, 873420, 902835, 932903, 963632, 995030, 1027103, 1059860],
:exp_formula => proc { |level| next ((level ** 3) * 6 / 5) - 15 * (level ** 2) + 100 * level - 140 }
})
GameData::GrowthRate.register({
:id => :Fast,
:name => _INTL("Fast"),
:exp_values => [-1,
0, 6, 21, 51, 100, 172, 274, 409, 583, 800,
1064, 1382, 1757, 2195, 2700, 3276, 3930, 4665, 5487, 6400,
7408, 8518, 9733, 11059, 12500, 14060, 15746, 17561, 19511, 21600,
23832, 26214, 28749, 31443, 34300, 37324, 40522, 43897, 47455, 51200,
55136, 59270, 63605, 68147, 72900, 77868, 83058, 88473, 94119, 100000,
106120, 112486, 119101, 125971, 133100, 140492, 148154, 156089, 164303, 172800,
181584, 190662, 200037, 209715, 219700, 229996, 240610, 251545, 262807, 274400,
286328, 298598, 311213, 324179, 337500, 351180, 365226, 379641, 394431, 409600,
425152, 441094, 457429, 474163, 491300, 508844, 526802, 545177, 563975, 583200,
602856, 622950, 643485, 664467, 685900, 707788, 730138, 752953, 776239, 800000],
:exp_formula => proc { |level| (level**3) * 4 / 5 }
0, 6, 21, 51, 100, 172, 274, 409, 583, 800,
1064, 1382, 1757, 2195, 2700, 3276, 3930, 4665, 5487, 6400,
7408, 8518, 9733, 11059, 12500, 14060, 15746, 17561, 19511, 21600,
23832, 26214, 28749, 31443, 34300, 37324, 40522, 43897, 47455, 51200,
55136, 59270, 63605, 68147, 72900, 77868, 83058, 88473, 94119, 100000,
106120, 112486, 119101, 125971, 133100, 140492, 148154, 156089, 164303, 172800,
181584, 190662, 200037, 209715, 219700, 229996, 240610, 251545, 262807, 274400,
286328, 298598, 311213, 324179, 337500, 351180, 365226, 379641, 394431, 409600,
425152, 441094, 457429, 474163, 491300, 508844, 526802, 545177, 563975, 583200,
602856, 622950, 643485, 664467, 685900, 707788, 730138, 752953, 776239, 800000],
:exp_formula => proc { |level| (level ** 3) * 4 / 5 }
})
GameData::GrowthRate.register({
:id => :Slow,
:name => _INTL("Slow"),
:exp_values => [-1,
0, 10, 33, 80, 156, 270, 428, 640, 911, 1250,
1663, 2160, 2746, 3430, 4218, 5120, 6141, 7290, 8573, 10000,
11576, 13310, 15208, 17280, 19531, 21970, 24603, 27440, 30486, 33750,
37238, 40960, 44921, 49130, 53593, 58320, 63316, 68590, 74148, 80000,
86151, 92610, 99383, 106480, 113906, 121670, 129778, 138240, 147061, 156250,
165813, 175760, 186096, 196830, 207968, 219520, 231491, 243890, 256723, 270000,
283726, 297910, 312558, 327680, 343281, 359370, 375953, 393040, 410636, 428750,
447388, 466560, 486271, 506530, 527343, 548720, 570666, 593190, 616298, 640000,
664301, 689210, 714733, 740880, 767656, 795070, 823128, 851840, 881211, 911250,
941963, 973360, 1005446, 1038230, 1071718, 1105920, 1140841, 1176490, 1212873, 1250000],
:exp_formula => proc { |level| (level**3) * 5 / 4 }
0, 10, 33, 80, 156, 270, 428, 640, 911, 1250,
1663, 2160, 2746, 3430, 4218, 5120, 6141, 7290, 8573, 10000,
11576, 13310, 15208, 17280, 19531, 21970, 24603, 27440, 30486, 33750,
37238, 40960, 44921, 49130, 53593, 58320, 63316, 68590, 74148, 80000,
86151, 92610, 99383, 106480, 113906, 121670, 129778, 138240, 147061, 156250,
165813, 175760, 186096, 196830, 207968, 219520, 231491, 243890, 256723, 270000,
283726, 297910, 312558, 327680, 343281, 359370, 375953, 393040, 410636, 428750,
447388, 466560, 486271, 506530, 527343, 548720, 570666, 593190, 616298, 640000,
664301, 689210, 714733, 740880, 767656, 795070, 823128, 851840, 881211, 911250,
941963, 973360, 1005446, 1038230, 1071718, 1105920, 1140841, 1176490, 1212873, 1250000],
: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
# :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
# chance, it will be female; otherwise, it will be male.
#===============================================================================
module GameData
class GenderRatio
attr_reader :id
@@ -18,8 +16,6 @@ module GameData
def self.load; end
def self.save; end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@real_name = hash[:name] || "Unnamed"
@@ -30,17 +26,9 @@ module GameData
def name
return _INTL(@real_name)
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
#===============================================================================
#
#===============================================================================
GameData::GenderRatio.register({

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,18 +1,12 @@
#===============================================================================
#
#===============================================================================
module GameData
class Evolution
attr_reader :id
attr_reader :real_name
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 :battle_level_up_proc
attr_reader :use_item_proc
attr_reader :on_trade_proc
attr_reader :after_battle_proc
attr_reader :event_proc
attr_reader :after_evolution_proc
DATA = {}
@@ -23,37 +17,21 @@ module GameData
def self.load; end
def self.save; end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@real_name = hash[:id].to_s || "Unnamed"
@real_name = hash[:id].to_s || "Unnamed"
@parameter = hash[:parameter]
@any_level_up = hash[:any_level_up] || false
@minimum_level = hash[:minimum_level] || 0
@level_up_proc = hash[:level_up_proc]
@battle_level_up_proc = hash[:battle_level_up_proc]
@use_item_proc = hash[:use_item_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]
end
alias name real_name
def call_level_up(*args)
return (@level_up_proc) ? @level_up_proc.call(*args) : nil
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)
return (@use_item_proc) ? @use_item_proc.call(*args) : nil
end
@@ -62,32 +40,18 @@ module GameData
return (@on_trade_proc) ? @on_trade_proc.call(*args) : nil
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)
@after_evolution_proc&.call(*args)
@after_evolution_proc.call(*args) if @after_evolution_proc
end
end
end
#===============================================================================
#
#===============================================================================
GameData::Evolution.register({
:id => :None
})
#===============================================================================
#
#===============================================================================
GameData::Evolution.register({
:id => :Level,
: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({
:id => :LevelDarkness,
:parameter => Integer,
: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,
:parameter => Integer,
: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({
:id => :Shedinja,
: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|
next false if $player.party_full?
next false if !$bag.has?(:POKEBALL)
next false if $Trainer.party_full?
next false if !$PokemonBag.pbHasItem?(:POKEBALL)
PokemonEvolutionScene.pbDuplicatePokemon(pkmn, new_species)
$bag.remove(:POKEBALL)
$PokemonBag.pbDeleteItem(:POKEBALL)
next true
}
})
GameData::Evolution.register({
:id => :Happiness,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220)
next pkmn.happiness >= 220
}
})
GameData::Evolution.register({
:id => :HappinessMale,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
: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({
:id => :HappinessFemale,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
: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({
:id => :HappinessDay,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
: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({
:id => :HappinessNight,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
: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({
:id => :HappinessMove,
:parameter => :Move,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
: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 }
end
}
@@ -362,10 +318,10 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HappinessMoveType,
:parameter => :Type,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
if pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220)
next pkmn.moves.any? { |m| m && m.type == parameter }
if pkmn.happiness >= 220
next pkmn.moves.any? { |m| m && m.id > 0 && m.type == parameter }
end
}
})
@@ -373,9 +329,9 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HappinessHoldItem,
:parameter => :Item,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
: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|
next false if evo_species != new_species || !pkmn.hasItem?(parameter)
@@ -386,7 +342,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :MaxHappiness,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness == 255
}
@@ -395,7 +351,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :Beauty, # Feebas
:parameter => Integer,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.beauty >= parameter
}
@@ -404,7 +360,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HoldItem,
:parameter => :Item,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter
},
@@ -418,7 +374,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HoldItemMale,
:parameter => :Item,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.male?
},
@@ -432,7 +388,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HoldItemFemale,
:parameter => :Item,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.female?
},
@@ -446,7 +402,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :DayHoldItem,
:parameter => :Item,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && PBDayNight.isDay?
},
@@ -460,7 +416,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :NightHoldItem,
:parameter => :Item,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && PBDayNight.isNight?
},
@@ -474,9 +430,9 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HoldItemHappiness,
:parameter => :Item,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
: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|
next false if evo_species != new_species || !pkmn.hasItem?(parameter)
@@ -488,7 +444,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HasMove,
:parameter => :Move,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.moves.any? { |m| m && m.id == parameter }
}
@@ -497,7 +453,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HasMoveType,
:parameter => :Type,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.moves.any? { |m| m && m.type == parameter }
}
@@ -506,69 +462,35 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HasInParty,
:parameter => :Species,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next $player.has_species?(parameter)
next $Trainer.has_species?(parameter)
}
})
GameData::Evolution.register({
:id => :Location,
:parameter => Integer,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, 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({
:id => :Region,
:parameter => Integer,
:any_level_up => true, # Needs any level up
:minimum_level => 1, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
map_metadata = $game_map.metadata
next 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
map_metadata = GameData::MapMetadata.try_get($game_map.map_id)
next map_metadata && map_metadata.town_map_position &&
map_metadata.town_map_position[0] == parameter
}
})
#===============================================================================
# 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({
:id => :Item,
:parameter => :Item,
@@ -613,14 +535,13 @@ GameData::Evolution.register({
:id => :ItemHappiness,
: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({
:id => :Trade,
:on_trade_proc => proc { |pkmn, parameter, other_pkmn|
@@ -673,72 +594,6 @@ GameData::Evolution.register({
:id => :TradeSpecies,
:parameter => :Species,
:on_trade_proc => proc { |pkmn, parameter, other_pkmn|
next other_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
next pkmn.species == parameter && !other_pkmn.hasItem?(:EVERSTONE)
}
})

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