diff --git a/.gitignore b/.gitignore index 2a799bddb8..08a5fc63ec 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ RGSS*.dll .vscode/ *.code-workspace .idea/ +*.mkproj # Operating system generated files & folders .DS_Store diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000000..7e36688f07 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,160 @@ +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 + +# 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 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 + +#=============================================================================== +# 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. +Style/ConditionalAssignment: + EnforcedStyle: assign_inside_condition + +# Check with yard instead. +Style/Documentation: + Enabled: false + +# It's a choice between format and sprintf. We already make use of sprintf and +# the translatable _ISPRINTF, so... +Style/FormatString: + EnforcedStyle: sprintf + +# 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 + +# 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 + +# 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 + +# Patentheses around the condition in a ternary operator helps to differentiate +# it from the true/false results. +Style/TernaryParentheses: + EnforcedStyle: require_parentheses diff --git a/Data/Scripts/001_Settings.rb b/Data/Scripts/001_Settings.rb index 963b87bb2d..16e73e2f87 100644 --- a/Data/Scripts/001_Settings.rb +++ b/Data/Scripts/001_Settings.rb @@ -1,12 +1,12 @@ #==============================================================================# # Pokémon Essentials # -# Version 19.1.dev # +# Version 20 # # 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,38 +14,10 @@ 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 = 7 + MECHANICS_GENERATION = 8 #============================================================================= - # 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 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 = 999_999 # The maximum number of Game Corner coins the player can have. @@ -58,59 +30,119 @@ module Settings 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] - ] + # 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) + # The odds of a wild Pokémon/bred egg having Pokérus (out of 65536). + POKERUS_CHANCE = 3 #============================================================================= # Whether outdoor maps should be shaded according to the time of day. - TIME_SHADING = true - - #============================================================================= - + TIME_SHADING = true # Whether poisoned Pokémon will lose HP while walking around in the field. - POISON_IN_FIELD = (MECHANICS_GENERATION <= 4) + 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) + 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 + 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 + FISHING_END_COMMON_EVENT = -1 + # Whether Pokémon in the Day Care gain Exp for each step the player takes. + # This is 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) + # 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) + # Whether you get 1 Premier Ball for every 10 of any kind of Poké Ball bought + # at once (true), or 1 Premier Ball for buying 10+ Poké Balls (false). + MORE_BONUS_PREMIER_BALLS = (MECHANICS_GENERATION >= 8) + # 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 #============================================================================= - # 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 + # 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 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) + # 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 Rage Candy Bar acts as a Full Heal (true) or a Potion (false). + RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS = (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 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) #============================================================================= - # Pairs of map IDs, where the location signpost isn't shown when moving from - # one of the maps in a pair to the other (and vice versa). Useful for single - # long routes/towns that are spread over multiple maps. - # e.g. [4,5,16,17,42,43] will be map pairs 4,5 and 16,17 and 42,43. - # Moving between two maps that have the exact same name won't show the - # location signpost anyway, so you don't need to list those maps here. - NO_SIGNPOSTS = [] + # 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 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 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) + + #============================================================================= + + # 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. + RIVAL_NAMES = [ + [:RIVAL1, 12], + [:RIVAL2, 12], + [:CHAMPION, 12] + ] #============================================================================= @@ -135,34 +167,9 @@ module Settings #============================================================================= - # 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) - - #============================================================================= - - # 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 - - #============================================================================= - - # The names of each pocket of the Bag. Ignore the first entry (""). + # The names of each pocket of the Bag. def self.bag_pocket_names - return ["", + return [ _INTL("Items"), _INTL("Medicine"), _INTL("Poké Balls"), @@ -173,14 +180,20 @@ def self.bag_pocket_names _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 slots per pocket (-1 means infinite number). + BAG_MAX_POCKET_SIZE = [-1, -1, -1, -1, -1, -1, -1, -1] + # Whether each pocket in turn auto-sorts itself by item ID number. + BAG_POCKET_AUTO_SORT = [false, false, false, true, true, false, false, false] # 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] + + #============================================================================= + + # 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 >= 8) #============================================================================= @@ -189,16 +202,16 @@ def self.bag_pocket_names # 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. + # file "regional_dexes.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 $player.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, and 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], @@ -231,6 +244,20 @@ def self.pokedex_names [0, 52, 20, 14, "mapHiddenFaraday", false] ] + # Whether the player can use Fly while looking at the Town Map. This is only + # allowed if the player can use Fly normally. + CAN_FLY_FROM_TOWN_MAP = true + + #============================================================================= + + # Pairs of map IDs, where the location signpost isn't shown when moving from + # one of the maps in a pair to the other (and vice versa). Useful for single + # long routes/towns that are spread over multiple maps. + # e.g. [4,5,16,17,42,43] will be map pairs 4,5 and 16,17 and 42,43. + # Moving between two maps that have the exact same name won't show the + # location signpost anyway, so you don't need to list those maps here. + NO_SIGNPOSTS = [] + #============================================================================= # A list of maps used by roaming Pokémon. Each map has an array of other maps @@ -297,6 +324,10 @@ def self.pokedex_names # 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 #============================================================================= @@ -323,6 +354,15 @@ def self.pokedex_names #============================================================================= + # 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 + + #============================================================================= + # 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. @@ -393,6 +433,6 @@ def self.pokedex_names # DO NOT EDIT THESE! module Essentials - VERSION = "19.1.dev" + VERSION = "20" ERROR_TEXT = "" end diff --git a/Data/Scripts/001_Technical/001_Debugging/001_PBDebug.rb b/Data/Scripts/001_Technical/001_Debugging/001_PBDebug.rb index dc7e7e91dc..81360266dc 100644 --- a/Data/Scripts/001_Technical/001_Debugging/001_PBDebug.rb +++ b/Data/Scripts/001_Technical/001_Debugging/001_PBDebug.rb @@ -7,18 +7,16 @@ def self.logonerr rescue PBDebug.log("") PBDebug.log("**Exception: #{$!.message}") - PBDebug.log("#{$!.backtrace.inspect}") + PBDebug.log($!.backtrace.inspect.to_s) PBDebug.log("") -# if $INTERNAL - pbPrintException($!) -# end + pbPrintException($!) # if $INTERNAL PBDebug.flush end end def self.flush - if $DEBUG && $INTERNAL && @@log.length>0 - File.open("Data/debuglog.txt", "a+b") { |f| f.write("#{@@log}") } + if $DEBUG && $INTERNAL && @@log.length > 0 + File.open("Data/debuglog.txt", "a+b") { |f| f.write(@@log.to_s) } end @@log.clear end @@ -26,9 +24,7 @@ def self.flush def self.log(msg) if $DEBUG && $INTERNAL @@log.push("#{msg}\r\n") -# if @@log.length>1024 - PBDebug.flush -# end + PBDebug.flush # if @@log.length > 1024 end end diff --git a/Data/Scripts/001_Technical/001_Debugging/002_DebugConsole.rb b/Data/Scripts/001_Technical/001_Debugging/002_DebugConsole.rb index 85a2600be1..775afb84cd 100644 --- a/Data/Scripts/001_Technical/001_Debugging/002_DebugConsole.rb +++ b/Data/Scripts/001_Technical/001_Debugging/002_DebugConsole.rb @@ -1,26 +1,23 @@ -# 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 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 "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 "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 "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 "--------------------------------" + echoln "-------------------------------------------------------------------------------" echoln "Debug Output:" - echoln "--------------------------------" + echoln "-------------------------------------------------------------------------------" echoln "" end @@ -44,9 +41,187 @@ def echo(string) end def echoln(string) - echo(string) - echo("\r\n") + 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 + + # 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 + "\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 diff --git a/Data/Scripts/001_Technical/001_Debugging/003_Errors.rb b/Data/Scripts/001_Technical/001_Debugging/003_Errors.rb index 80d409a055..eed3d98ee2 100644 --- a/Data/Scripts/001_Technical/001_Debugging/003_Errors.rb +++ b/Data/Scripts/001_Technical/001_Debugging/003_Errors.rb @@ -4,12 +4,23 @@ class Reset < Exception end -def pbGetExceptionMessage(e,_script="") +class EventScriptError < Exception + attr_accessor :event_message + + def initialize(message) + super(nil) + @event_message = message + end +end + +def pbGetExceptionMessage(e, _script = "") + return e.event_message.dup if e.is_a?(EventScriptError) # Message with map/event ID generated elsewhere emessage = e.message.dup emessage.force_encoding(Encoding::UTF_8) - if e.is_a?(Hangup) + case e + when Hangup emessage = "The script is taking too long. The game will restart." - elsif e.is_a?(Errno::ENOENT) + when Errno::ENOENT filename = emessage.sub("No such file or directory - ", "") emessage = "File #{filename} not found." end @@ -18,27 +29,26 @@ def pbGetExceptionMessage(e,_script="") end def pbPrintException(e) - emessage = "" - if $EVENTHANGUPMSG && $EVENTHANGUPMSG!="" - emessage = $EVENTHANGUPMSG # Message with map/event ID generated elsewhere - $EVENTHANGUPMSG = nil - else - emessage = pbGetExceptionMessage(e) - end + emessage = pbGetExceptionMessage(e) # 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 - message += "Exception: #{e.class}\r\n" - message += "Message: #{emessage}\r\n" + if !e.is_a?(EventScriptError) + message += "Exception: #{e.class}\r\n" + message += "Message: " + end + message += emessage # show last 10/25 lines of backtrace - message += "\r\nBacktrace:\r\n" - btrace = "" - if e.backtrace - maxlength = ($INTERNAL) ? 25 : 10 - e.backtrace[0, maxlength].each { |i| btrace += "#{i}\r\n" } + 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 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) @@ -55,7 +65,7 @@ def pbPrintException(e) 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.delta - until (System.delta - t) >= 500000 + until (System.delta - t) >= 500_000 Input.update if Input.press?(Input::CTRL) Input.clipboard = message diff --git a/Data/Scripts/001_Technical/001_Debugging/004_Validation.rb b/Data/Scripts/001_Technical/001_Debugging/004_Validation.rb index e3c23ccca4..779f9b4281 100644 --- a/Data/Scripts/001_Technical/001_Debugging/004_Validation.rb +++ b/Data/Scripts/001_Technical/001_Debugging/004_Validation.rb @@ -1,7 +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, Symbol}] value pairs to validate # @example Validate a class or method diff --git a/Data/Scripts/001_Technical/001_Debugging/005_Deprecation.rb b/Data/Scripts/001_Technical/001_Debugging/005_Deprecation.rb index 64660171ac..ab7689d7b8 100644 --- a/Data/Scripts/001_Technical/001_Debugging/005_Deprecation.rb +++ b/Data/Scripts/001_Technical/001_Debugging/005_Deprecation.rb @@ -8,15 +8,14 @@ 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('WARN: usage of deprecated method "{1}" or its alias.', method_name) + text = _INTL('Usage of deprecated method "{1}" or its alias.', method_name) unless removal_version.nil? - text += _INTL("\nThe method is slated to be"\ - " removed in Essentials {1}.", removal_version) + text += "\r\n" + _INTL("The method is slated to be removed in Essentials {1}.", removal_version) end unless alternative.nil? - text += _INTL("\nUse \"{1}\" instead.", alternative) + text += "\r\n" + _INTL("Use \"{1}\" instead.", alternative) end - echoln text + Console.echo_warn text end end @@ -41,11 +40,11 @@ def deprecated_method_alias(name, aliased_method, removal_in: nil, class_method: 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 = format('%s%s%s', class_name, delimiter, name) - aliased_method_name = format('%s%s%s', class_name, delimiter, aliased_method) + alias_name = sprintf("%s%s%s", class_name, delimiter, name) + aliased_method_name = sprintf("%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 diff --git a/Data/Scripts/001_Technical/001_MKXP_Compatibility.rb b/Data/Scripts/001_Technical/001_MKXP_Compatibility.rb index 3369d3e44a..6eae95ad17 100644 --- a/Data/Scripts/001_Technical/001_MKXP_Compatibility.rb +++ b/Data/Scripts/001_Technical/001_MKXP_Compatibility.rb @@ -1,18 +1,29 @@ -# Using mkxp-z v2.2.0 - https://gitlab.com/mkxp-z/mkxp-z/-/releases/v2.2.0 +# Using mkxp-z v2.3.1 - https://gitlab.com/mkxp-z/mkxp-z/-/releases/v2.3.1 $VERBOSE = nil Font.default_shadow = false if Font.respond_to?(:default_shadow) Graphics.frame_rate = 40 +Encoding.default_internal = Encoding::UTF_8 +Encoding.default_external = Encoding::UTF_8 def pbSetWindowText(string) System.set_window_title(string || System.game_title) 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, text, align = 0) - height = text_size(text).height - mkxp_draw_text(x, y, width, height, text, align) + 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 diff --git a/Data/Scripts/001_Technical/002_Files/001_FileTests.rb b/Data/Scripts/001_Technical/002_Files/001_FileTests.rb index 56def11eff..417225a479 100644 --- a/Data/Scripts/001_Technical/002_Files/001_FileTests.rb +++ b/Data/Scripts/001_Technical/002_Files/001_FileTests.rb @@ -9,8 +9,8 @@ def self.get(dir, filters = "*", full = true) files = [] filters = [filters] if !filters.is_a?(Array) self.chdir(dir) do - for filter in filters - self.glob(filter){ |f| files.push(full ? (dir + "/" + f) : f) } + filters.each do |filter| + self.glob(filter) { |f| files.push(full ? (dir + "/" + f) : f) } end end return files.sort @@ -22,7 +22,7 @@ def self.all(dir, filters = "*", full = true) # sets variables for starting files = [] subfolders = [] - for file in self.get(dir, filters, full) + self.get(dir, filters, full).each do |file| # engages in recursion to read the entire file tree if self.safe?(file) # Is a directory subfolders += self.all(file, filters, full) @@ -43,6 +43,42 @@ def self.safe?(dir) return ret end #----------------------------------------------------------------------------- + # Creates all the required directories for filename path + #----------------------------------------------------------------------------- + def self.create(path) + path.gsub!("\\", "/") # Windows compatibility + # get path tree + dirs = path.split("/") + full = "" + for dir in dirs + 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 = [] + for file in self.get(dir, "*", true) + # engages in recursion to read the entire folder tree + dirs += self.all_dirs(file) if self.safe?(file) + end + # returns all found directories + return dirs.length > 0 ? (dirs + [dir]) : [dir] + end + #----------------------------------------------------------------------------- + # Deletes all the files in a directory and all the sub directories (allows for non-empty dirs) + #----------------------------------------------------------------------------- + def self.delete_all(dir) + # delete all files in dir + self.all(dir).each { |f| File.delete(f) } + # delete all dirs in dir + self.all_dirs(dir).each { |f| Dir.delete(f) } + end + #----------------------------------------------------------------------------- end @@ -56,7 +92,15 @@ class File #----------------------------------------------------------------------------- def self.safe?(file) ret = false - self.open(file, 'rb') { ret = true } rescue nil + self.open(file, "rb") { ret = true } rescue nil + return ret + end + #----------------------------------------------------------------------------- + # Checks for existing .rxdata file + #----------------------------------------------------------------------------- + def self.safeData?(file) + ret = false + ret = (load_data(file) ? true : false) rescue false return ret end #----------------------------------------------------------------------------- @@ -79,7 +123,7 @@ def safeExists?(f) return FileTest.exist?(f) if f[/\A[\x20-\x7E]*\z/] ret = false begin - File.open(f,"rb") { ret = true } + File.open(f, "rb") { ret = true } rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES ret = false end @@ -89,13 +133,13 @@ def safeExists?(f) # 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) { afterChdir = true - Dir.glob(wildcard) { |f| ret.push(dir+"/"+f) } + Dir.glob(wildcard) { |f| ret.push(dir + "/" + f) } } rescue Errno::ENOENT raise if afterChdir @@ -108,8 +152,8 @@ def safeGlob(dir,wildcard) def pbResolveAudioSE(file) return nil if !file - if RTP.exists?("Audio/SE/"+file,["",".wav",".mp3",".ogg"]) - return RTP.getPath("Audio/SE/"+file,["",".wav",".mp3",".ogg"]) + if RTP.exists?("Audio/SE/" + file, ["", ".wav", ".ogg"]) # ".mp3" + return RTP.getPath("Audio/SE/" + file, ["", ".wav", ".ogg"]) # ".mp3" end return nil end @@ -118,18 +162,18 @@ def pbResolveAudioSE(file) # 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) { |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 + 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 @@ -157,7 +201,7 @@ def canonicalize(c) pos = -1 ret = [] retstr = "" - for x in csplit + csplit.each do |x| if x == ".." if pos >= 0 ret.delete_at(pos) @@ -168,7 +212,7 @@ def canonicalize(c) pos += 1 end end - for i in 0...ret.length + ret.length.times do |i| retstr += "/" if i > 0 retstr += ret[i] end @@ -180,31 +224,31 @@ def canonicalize(c) module RTP @rtpPaths = nil - def self.exists?(filename,extensions=[]) + def self.exists?(filename, extensions = []) return false if nil_or_empty?(filename) eachPathFor(filename) { |path| return true if safeExists?(path) - for ext in extensions - return true if safeExists?(path+ext) + extensions.each do |ext| + return true if safeExists?(path + ext) 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,["",".mp3",".wav",".wma",".mid",".ogg",".midi"]) + return self.getPath(filename, ["", ".wav", ".wma", ".mid", ".ogg", ".midi"]) # ".mp3" end - def self.getPath(filename,extensions=[]) + def self.getPath(filename, extensions = []) return filename if nil_or_empty?(filename) eachPathFor(filename) { |path| return path if safeExists?(path) - for ext in extensions - file = path+ext + extensions.each do |ext| + file = path + ext return file if safeExists?(file) end } @@ -220,10 +264,10 @@ def self.eachPathFor(filename) else # relative path RTP.eachPath { |path| - if path=="./" + if path == "./" yield filename else - yield path+filename + yield path + filename end } end @@ -237,11 +281,9 @@ def self.eachPathFor(filename) 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 @@ -260,15 +302,15 @@ def self.getSaveFolder module FileTest - Image_ext = ['.png', '.gif'] # '.jpg', '.jpeg', '.bmp', - Audio_ext = ['.mp3', '.mid', '.midi', '.ogg', '.wav', '.wma'] + IMAGE_EXTENSIONS = [".png", ".gif"] # ".jpg", ".jpeg", ".bmp", + AUDIO_EXTENSIONS = [".mid", ".midi", ".ogg", ".wav", ".wma"] # ".mp3" def self.audio_exist?(filename) - return RTP.exists?(filename,Audio_ext) + return RTP.exists?(filename, AUDIO_EXTENSIONS) end def self.image_exist?(filename) - return RTP.exists?(filename,Image_ext) + return RTP.exists?(filename, IMAGE_EXTENSIONS) end end @@ -277,11 +319,11 @@ def self.image_exist?(filename) # 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, +# NOTE: pbGetFileChar checks anything added in MKXP's RTP setting, # and matching mount points added through System.mount def pbRgssExists?(filename) if safeExists?("./Game.rgssad") - return pbGetFileChar(filename)!=nil + return pbGetFileChar(filename) != nil else filename = canonicalize(filename) return safeExists?(filename) @@ -291,16 +333,16 @@ def pbRgssExists?(filename) # 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, +# 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") } +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) @@ -320,7 +362,7 @@ def pbGetFileChar(file) canon_file = canonicalize(file) if !safeExists?("./Game.rgssad") return nil if !safeExists?(canon_file) - return nil if file.last == '/' # Is a directory + 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 @@ -338,20 +380,20 @@ def pbGetFileChar(file) def pbTryString(x) ret = pbGetFileChar(x) - return (ret!=nil && ret!="") ? x : nil + return nil_or_empty?(ret) ? nil : x 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, +# 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 !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 @@ -374,13 +416,13 @@ class StringInput include Enumerable class << self - def new( str ) + def new(str) if block_given? begin f = super yield f ensure - f.close if f + f&.close end else super @@ -389,21 +431,21 @@ def new( str ) alias open new end - def initialize( str ) + def initialize(str) @string = str @pos = 0 @closed = false @lineno = 0 end - attr_reader :lineno,:string + 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 @@ -411,7 +453,7 @@ def close def closed?; @closed; end def pos - raise IOError, 'closed stream' if @closed + raise IOError, "closed stream" if @closed [@pos, @string.size].min end @@ -421,8 +463,8 @@ def rewind; seek(0); end 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 @@ -436,12 +478,12 @@ def seek(offset, whence=IO::SEEK_SET) 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 @@ -450,14 +492,15 @@ def each( &block ) end def gets - raise IOError, 'closed stream' if @closed - if idx = @string.index(?\n, @pos) + raise IOError, "closed stream" if @closed + idx = @string.index("\n", @pos) + if idx 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 @@ -465,18 +508,18 @@ def gets 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 @@ -485,8 +528,6 @@ def read( len = nil ) @pos += 1 if @pos == @string.size str end - - def read_all; read(); end - + alias read_all read alias sysread read end diff --git a/Data/Scripts/001_Technical/002_Files/002_FileMixins.rb b/Data/Scripts/001_Technical/002_Files/002_FileMixins.rb index 187049ec68..79f525993a 100644 --- a/Data/Scripts/001_Technical/002_Files/002_FileMixins.rb +++ b/Data/Scripts/001_Technical/002_Files/002_FileMixins.rb @@ -67,7 +67,7 @@ def getLength(index) self.pos = 0 offset = fgetdw >> 3 return 0 if index >= offset - self.pos = index * 8 + 4 + self.pos = (index * 8) + 4 return fgetdw end @@ -137,7 +137,7 @@ def pos=(value) end def each_byte - while !eof? + until eof? yield getc end end diff --git a/Data/Scripts/001_Technical/002_Files/003_HTTP_Utilities.rb b/Data/Scripts/001_Technical/002_Files/003_HTTP_Utilities.rb index 7d1b4f512d..d989107a02 100644 --- a/Data/Scripts/001_Technical/002_Files/003_HTTP_Utilities.rb +++ b/Data/Scripts/001_Technical/002_Files/003_HTTP_Utilities.rb @@ -3,19 +3,19 @@ # 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 { |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}" - }.join('&') + }.join("&") ret = HTTPLite.post_body( url, body, @@ -31,7 +31,7 @@ def pbPostData(url, postdata, filename=nil, depth=0) 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 "" @@ -63,7 +63,7 @@ def pbDownloadToString(url) def pbDownloadToFile(url, file) begin - pbDownloadData(url,file) + pbDownloadData(url, file) rescue end end @@ -79,7 +79,7 @@ def pbPostToString(url, postdata) def pbPostToFile(url, postdata, file) begin - pbPostData(url, postdata,file) + pbPostData(url, postdata, file) rescue end end diff --git a/Data/Scripts/001_Technical/002_RubyUtilities.rb b/Data/Scripts/001_Technical/002_RubyUtilities.rb index 77519b63b2..3a88c12e7f 100644 --- a/Data/Scripts/001_Technical/002_RubyUtilities.rb +++ b/Data/Scripts/001_Technical/002_RubyUtilities.rb @@ -2,7 +2,7 @@ # class Object #=============================================================================== class Object - alias full_inspect inspect + alias full_inspect inspect unless method_defined?(:full_inspect) def inspect return "#<#{self.class}>" @@ -23,32 +23,21 @@ def to_sym #=============================================================================== class String def starts_with_vowel? - return ['a', 'e', 'i', 'o', 'u'].include?(self[0, 1].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? - blank = true - s = self.scan(/./) - for l in s - blank = false if l != "" - end - return blank - end + def blank?; return self.strip.empty?; end def cut(bitmap, width) string = self width -= bitmap.text_size("...").width string_width = 0 text = [] - for char in string.scan(/./) + string.scan(/./).each do |char| wdh = bitmap.text_size(char).width next if (wdh + string_width) > width string_width += wdh @@ -56,11 +45,15 @@ def cut(bitmap, width) end text.push("...") if text.length < string.length new_string = "" - for char in text + text.each do |char| new_string += char end return new_string end + + def numeric? + return !self[/^[+-]?([0-9]+)(?:\.[0-9]+)?$/].nil? + end end #=============================================================================== @@ -89,7 +82,7 @@ def to_word #=============================================================================== class Array def ^(other) # xor of two arrays - return (self|other) - (self&other) + return (self | other) - (self & other) end def swap(val1, val2) @@ -100,6 +93,29 @@ def swap(val1, val2) 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 #=============================================================================== @@ -111,6 +127,144 @@ def transform end end +#=============================================================================== +# class File +#=============================================================================== +class File + # Copies the source file to the destination path. + def self.copy(source, destination) + data = "" + t = Time.now + File.open(source, "rb") do |f| + loop do + r = f.read(4096) + break if !r + if Time.now - t > 1 + Graphics.update + t = Time.now + 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 + if args.length == 1 + if args.first.is_a?(Fixnum) + hex = args.first.to_s(16) + elsif args.first.is_a?(String) + try_rgb_format = args.first.split(",") + return 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) + elsif args.length == 3 + r, g, b = *args + end + return init_original(r, g, b) if r && g && b + return init_original(*args) + end + + # Returns this color as a hex string like "#RRGGBB". + def to_hex + r = sprintf("%02X", self.red) + g = sprintf("%02X", self.green) + b = sprintf("%02X", self.blue) + return ("#" + r + g + b).upcase + end + + # Returns this color as a 24-bit color integer. + def to_i + return self.to_hex.delete("#").to_i(16) + 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, 0, 0); end + def self.green; return Color.new( 0, 255, 0); end + def self.blue; return Color.new( 0, 0, 255); end + def self.black; return Color.new( 0, 0, 0); end + def self.white; return Color.new(255, 255, 255); end + def self.yellow; return Color.new(255, 255, 0); end + def self.magenta; return Color.new(255, 0, 255); end + def self.teal; return Color.new( 0, 255, 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.to_s}", value) + end + args.instance_eval(&execute_block) + end + + def set(params = {}) + @params = params + end +end + #=============================================================================== # Kernel methods #=============================================================================== diff --git a/Data/Scripts/001_Technical/003_Intl_Messages.rb b/Data/Scripts/001_Technical/003_Intl_Messages.rb index 7f8b21799d..8fcfc2244b 100644 --- a/Data/Scripts/001_Technical/003_Intl_Messages.rb +++ b/Data/Scripts/001_Technical/003_Intl_Messages.rb @@ -1,20 +1,20 @@ -def pbAddScriptTexts(items,script) +def pbAddScriptTexts(items, script) script.scan(/(?:_I)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s| - string=s[0] - string.gsub!(/\\\"/,"\"") - string.gsub!(/\\\\/,"\\") + string = s[0] + string.gsub!(/\\\"/, "\"") + string.gsub!(/\\\\/, "\\") items.push(string) } end -def pbAddRgssScriptTexts(items,script) +def pbAddRgssScriptTexts(items, script) script.scan(/(?:_INTL|_ISPRINTF)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s| - string=s[0] - string.gsub!(/\\r/,"\r") - string.gsub!(/\\n/,"\n") - string.gsub!(/\\1/,"\1") - string.gsub!(/\\\"/,"\"") - string.gsub!(/\\\\/,"\\") + string = s[0] + string.gsub!(/\\r/, "\r") + string.gsub!(/\\n/, "\n") + string.gsub!(/\\1/, "\1") + string.gsub!(/\\\"/, "\"") + string.gsub!(/\\\\/, "\\") items.push(string) } end @@ -23,81 +23,79 @@ def pbSetTextMessages Graphics.update begin t = Time.now.to_i - texts=[] - for script in $RGSS_SCRIPTS + texts = [] + $RGSS_SCRIPTS.each do |script| if Time.now.to_i - t >= 5 t = Time.now.to_i Graphics.update end - scr=Zlib::Inflate.inflate(script[2]) - pbAddRgssScriptTexts(texts,scr) + scr = Zlib::Inflate.inflate(script[2]) + pbAddRgssScriptTexts(texts, scr) end if safeExists?("Data/PluginScripts.rxdata") plugin_scripts = load_data("Data/PluginScripts.rxdata") - for plugin in plugin_scripts - for script in plugin[2] + plugin_scripts.each do |plugin| + plugin[2].each do |script| if Time.now.to_i - t >= 5 t = Time.now.to_i Graphics.update end scr = Zlib::Inflate.inflate(script[1]).force_encoding(Encoding::UTF_8) - pbAddRgssScriptTexts(texts,scr) + pbAddRgssScriptTexts(texts, scr) end end end # Must add messages because this code is used by both game system and Editor - MessageTypes.addMessagesAsHash(MessageTypes::ScriptTexts,texts) + MessageTypes.addMessagesAsHash(MessageTypes::ScriptTexts, texts) commonevents = load_data("Data/CommonEvents.rxdata") - items=[] - choices=[] - for event in commonevents.compact + items = [] + choices = [] + commonevents.compact.each do |event| if Time.now.to_i - t >= 5 t = Time.now.to_i Graphics.update end begin - neednewline=false - lastitem="" - for j in 0...event.list.size + neednewline = false + lastitem = "" + event.list.size.times do |j| list = event.list[j] - if neednewline && list.code!=401 - if lastitem!="" - lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1+" " } + if neednewline && list.code != 401 + if lastitem != "" + lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1 + " " } items.push(lastitem) - lastitem="" + lastitem = "" end - neednewline=false + neednewline = false end if list.code == 101 - lastitem+="#{list.parameters[0]}" - neednewline=true + lastitem += list.parameters[0].to_s + neednewline = true elsif list.code == 102 - for k in 0...list.parameters[0].length + list.parameters[0].length.times do |k| choices.push(list.parameters[0][k]) end - neednewline=false + neednewline = false elsif list.code == 401 - lastitem+=" " if lastitem!="" - lastitem+="#{list.parameters[0]}" - neednewline=true + lastitem += " " if lastitem != "" + lastitem += list.parameters[0].to_s + neednewline = true elsif list.code == 355 || list.code == 655 - pbAddScriptTexts(items,list.parameters[0]) - elsif list.code == 111 && list.parameters[0]==12 - pbAddScriptTexts(items,list.parameters[1]) + pbAddScriptTexts(items, list.parameters[0]) + elsif list.code == 111 && list.parameters[0] == 12 + pbAddScriptTexts(items, list.parameters[1]) elsif list.code == 209 - route=list.parameters[1] - for k in 0...route.list.size + route = list.parameters[1] + route.list.size.times do |k| if route.list[k].code == 45 - pbAddScriptTexts(items,route.list[k].parameters[0]) + pbAddScriptTexts(items, route.list[k].parameters[0]) end end end end - if neednewline - if lastitem!="" - items.push(lastitem) - lastitem="" - end + if neednewline && lastitem != "" + items.push(lastitem) + lastitem = "" end end end @@ -105,75 +103,68 @@ def pbSetTextMessages t = Time.now.to_i Graphics.update end - items|=[] - choices|=[] + items |= [] + choices |= [] items.concat(choices) - MessageTypes.setMapMessagesAsHash(0,items) + MessageTypes.setMapMessagesAsHash(0, items) mapinfos = pbLoadMapInfos - mapnames=[] - for id in mapinfos.keys - mapnames[id]=mapinfos[id].name - end - MessageTypes.setMessages(MessageTypes::MapNames,mapnames) - for id in mapinfos.keys + mapinfos.each_key do |id| if Time.now.to_i - t >= 5 t = Time.now.to_i Graphics.update end - filename=sprintf("Data/Map%03d.rxdata",id) + filename = sprintf("Data/Map%03d.rxdata", id) next if !pbRgssExists?(filename) map = load_data(filename) - items=[] - choices=[] - for event in map.events.values + items = [] + choices = [] + map.events.each_value do |event| if Time.now.to_i - t >= 5 t = Time.now.to_i Graphics.update end begin - for i in 0...event.pages.size - neednewline=false - lastitem="" - for j in 0...event.pages[i].list.size + event.pages.size.times do |i| + neednewline = false + lastitem = "" + event.pages[i].list.size.times do |j| list = event.pages[i].list[j] - if neednewline && list.code!=401 - if lastitem!="" - lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1+" " } + if neednewline && list.code != 401 + if lastitem != "" + lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1 + " " } items.push(lastitem) - lastitem="" + lastitem = "" end - neednewline=false + neednewline = false end if list.code == 101 - lastitem+="#{list.parameters[0]}" - neednewline=true + lastitem += list.parameters[0].to_s + neednewline = true elsif list.code == 102 - for k in 0...list.parameters[0].length + list.parameters[0].length.times do |k| choices.push(list.parameters[0][k]) end - neednewline=false + neednewline = false elsif list.code == 401 - lastitem+=" " if lastitem!="" - lastitem+="#{list.parameters[0]}" - neednewline=true - elsif list.code == 355 || list.code==655 - pbAddScriptTexts(items,list.parameters[0]) - elsif list.code == 111 && list.parameters[0]==12 - pbAddScriptTexts(items,list.parameters[1]) - elsif list.code==209 - route=list.parameters[1] - for k in 0...route.list.size - if route.list[k].code==45 - pbAddScriptTexts(items,route.list[k].parameters[0]) + lastitem += " " if lastitem != "" + lastitem += list.parameters[0].to_s + neednewline = true + elsif list.code == 355 || list.code == 655 + pbAddScriptTexts(items, list.parameters[0]) + elsif list.code == 111 && list.parameters[0] == 12 + pbAddScriptTexts(items, list.parameters[1]) + elsif list.code == 209 + route = list.parameters[1] + route.list.size.times do |k| + if route.list[k].code == 45 + pbAddScriptTexts(items, route.list[k].parameters[0]) end end end end - if neednewline - if lastitem!="" - items.push(lastitem) - lastitem="" - end + if neednewline && lastitem != "" + items.push(lastitem) + lastitem = "" end end end @@ -182,10 +173,10 @@ def pbSetTextMessages t = Time.now.to_i Graphics.update end - items|=[] - choices|=[] + items |= [] + choices |= [] items.concat(choices) - MessageTypes.setMapMessagesAsHash(id,items) + MessageTypes.setMapMessagesAsHash(id, items) if Time.now.to_i - t >= 5 t = Time.now.to_i Graphics.update @@ -197,91 +188,92 @@ def pbSetTextMessages end def pbEachIntlSection(file) - lineno=1 - re=/^\s*\[\s*([^\]]+)\s*\]\s*$/ - havesection=false - sectionname=nil - lastsection=[] + lineno = 1 + re = /^\s*\[\s*([^\]]+)\s*\]\s*$/ + havesection = false + sectionname = nil + lastsection = [] file.each_line { |line| - if lineno==1 && line[0].ord==0xEF && line[1].ord==0xBB && line[2].ord==0xBF - line=line[3,line.length-3] + if lineno == 1 && line[0].ord == 0xEF && line[1].ord == 0xBB && line[2].ord == 0xBF + line = line[3, line.length - 3] end if !line[/^\#/] && !line[/^\s*$/] if line[re] if havesection - yield lastsection,sectionname + yield lastsection, sectionname end lastsection.clear - sectionname=$~[1] - havesection=true + sectionname = $~[1] + havesection = true else - if sectionname==nil - raise _INTL("Expected a section at the beginning of the file (line {1})",lineno) + if sectionname.nil? + raise _INTL("Expected a section at the beginning of the file (line {1})", lineno) end - lastsection.push(line.gsub(/\s+$/,"")) + lastsection.push(line.gsub(/\s+$/, "")) end end - lineno+=1 - if lineno%500==0 + lineno += 1 + if lineno % 500 == 0 Graphics.update end } if havesection - yield lastsection,sectionname + yield lastsection, sectionname end end def pbGetText(infile) begin - file=File.open(infile,"rb") + file = File.open(infile, "rb") rescue - raise _INTL("Can't find {1}",infile) + raise _INTL("Can't find {1}", infile) end - intldat=[] + intldat = [] begin - pbEachIntlSection(file) { |section,name| - next if section.length==0 + pbEachIntlSection(file) { |section, name| + next if section.length == 0 if !name[/^([Mm][Aa][Pp])?(\d+)$/] - raise _INTL("Invalid section name {1}",name) + raise _INTL("Invalid section name {1}", name) end - ismap=$~[1] && $~[1]!="" - id=$~[2].to_i - itemlength=0 + ismap = $~[1] && $~[1] != "" + id = $~[2].to_i + itemlength = 0 if section[0][/^\d+$/] - intlhash=[] - itemlength=3 + intlhash = [] + itemlength = 3 if ismap - raise _INTL("Section {1} can't be an ordered list (section was recognized as an ordered list because its first line is a number)",name) + raise _INTL("Section {1} can't be an ordered list (section was recognized as an ordered list because its first line is a number)", name) end - if section.length%3!=0 - raise _INTL("Section {1}'s line count is not divisible by 3 (section was recognized as an ordered list because its first line is a number)",name) + if section.length % 3 != 0 + raise _INTL("Section {1}'s line count is not divisible by 3 (section was recognized as an ordered list because its first line is a number)", name) end else - intlhash=OrderedHash.new - itemlength=2 - if section.length%2!=0 - raise _INTL("Section {1} has an odd number of entries (section was recognized as a hash because its first line is not a number)",name) + intlhash = OrderedHash.new + itemlength = 2 + if section.length.odd? + raise _INTL("Section {1} has an odd number of entries (section was recognized as a hash because its first line is not a number)", name) end end - i=0 - loop do break unless i0 - str+=@keys[i].inspect+"=>"+self[@keys[i]].inspect + str = "{" + @keys.length.times do |i| + str += ", " if i > 0 + str += @keys[i].inspect + "=>" + self[@keys[i]].inspect end - str+="}" + str += "}" return str end - alias :to_s :inspect + alias to_s inspect - def []=(key,value) - oldvalue=self[key] + def []=(key, value) + oldvalue = self[key] if !oldvalue && value @keys.push(key) elsif !value - @keys|=[] - @keys-=[key] + @keys |= [] + @keys -= [key] end - return super(key,value) + super(key, value) end def self._load(string) - ret=self.new - keysvalues=Marshal.load(string) - keys=keysvalues[0] - values=keysvalues[1] - for i in 0...keys.length - ret[keys[i]]=values[i] + ret = self.new + keysvalues = Marshal.load(string) + keys = keysvalues[0] + values = keysvalues[1] + keys.length.times do |i| + ret[keys[i]] = values[i] end return ret end - def _dump(_depth=100) - values=[] - for key in @keys + def _dump(_depth = 100) + values = [] + @keys.each do |key| values.push(self[key]) end - return Marshal.dump([@keys,values]) + return Marshal.dump([@keys, values]) end end class Messages - def initialize(filename=nil,delayLoad=false) - @messages=nil - @filename=filename + def initialize(filename = nil, delayLoad = false) + @messages = nil + @filename = filename if @filename && !delayLoad loadMessageFile(@filename) end @@ -371,30 +363,30 @@ def initialize(filename=nil,delayLoad=false) def delayedLoad if @filename && !@messages loadMessageFile(@filename) - @filename=nil + @filename = nil end end def self.stringToKey(str) if str && str[/[\r\n\t\1]|^\s+|\s+$|\s{2,}/] - key=str.clone - key.gsub!(/^\s+/,"") - key.gsub!(/\s+$/,"") - key.gsub!(/\s{2,}/," ") - return key + key = str.clone + key.gsub!(/^\s+/, "") + key.gsub!(/\s+$/, "") + key.gsub!(/\s{2,}/, " ") + return key end return str end def self.normalizeValue(value) if value[/[\r\n\t\x01]|^[\[\]]/] - ret=value.clone - ret.gsub!(/\r/,"<>") - ret.gsub!(/\n/,"<>") - ret.gsub!(/\t/,"<>") - ret.gsub!(/\[/,"<<[>>") - ret.gsub!(/\]/,"<<]>>") - ret.gsub!(/\x01/,"<<1>>") + ret = value.clone + ret.gsub!(/\r/, "<>") + ret.gsub!(/\n/, "<>") + ret.gsub!(/\t/, "<>") + ret.gsub!(/\[/, "<<[>>") + ret.gsub!(/\]/, "<<]>>") + ret.gsub!(/\x01/, "<<1>>") return ret end return value @@ -402,45 +394,46 @@ def self.normalizeValue(value) def self.denormalizeValue(value) if value[/<<[rnt1\[\]]>>/] - ret=value.clone - ret.gsub!(/<<1>>/,"\1") - ret.gsub!(/<>/,"\r") - ret.gsub!(/<>/,"\n") - ret.gsub!(/<<\[>>/,"[") - ret.gsub!(/<<\]>>/,"]") - ret.gsub!(/<>/,"\t") + ret = value.clone + ret.gsub!(/<<1>>/, "\1") + ret.gsub!(/<>/, "\r") + ret.gsub!(/<>/, "\n") + ret.gsub!(/<<\[>>/, "[") + ret.gsub!(/<<\]>>/, "]") + ret.gsub!(/<>/, "\t") return ret end return value end - def self.writeObject(f,msgs,secname,origMessages=nil) + def self.writeObject(f, msgs, secname, origMessages = nil) return if !msgs - if msgs.is_a?(Array) + case msgs + when Array f.write("[#{secname}]\r\n") - for j in 0...msgs.length + msgs.length.times do |j| next if nil_or_empty?(msgs[j]) - value=Messages.normalizeValue(msgs[j]) - origValue="" + value = Messages.normalizeValue(msgs[j]) + origValue = "" if origMessages - origValue=Messages.normalizeValue(origMessages.get(secname,j)) + origValue = Messages.normalizeValue(origMessages.get(secname, j)) else - origValue=Messages.normalizeValue(MessageTypes.get(secname,j)) + origValue = Messages.normalizeValue(MessageTypes.get(secname, j)) end f.write("#{j}\r\n") - f.write(origValue+"\r\n") - f.write(value+"\r\n") + f.write(origValue + "\r\n") + f.write(value + "\r\n") end - elsif msgs.is_a?(OrderedHash) + when OrderedHash f.write("[#{secname}]\r\n") - keys=msgs.keys - for key in keys + keys = msgs.keys + keys.each do |key| next if nil_or_empty?(msgs[key]) - value=Messages.normalizeValue(msgs[key]) - valkey=Messages.normalizeValue(key) + value = Messages.normalizeValue(msgs[key]) + valkey = Messages.normalizeValue(key) # key is already serialized - f.write(valkey+"\r\n") - f.write(value+"\r\n") + f.write(valkey + "\r\n") + f.write(value + "\r\n") end end end @@ -451,112 +444,112 @@ def messages def extract(outfile) # return if !@messages - origMessages=Messages.new("Data/messages.dat") - File.open(outfile,"wb") { |f| + origMessages = Messages.new("Data/messages.dat") + File.open(outfile, "wb") { |f| f.write(0xef.chr) f.write(0xbb.chr) f.write(0xbf.chr) f.write("# To localize this text for a particular language, please\r\n") f.write("# translate every second line of this file.\r\n") if origMessages.messages[0] - for i in 0...origMessages.messages[0].length - msgs=origMessages.messages[0][i] - Messages.writeObject(f,msgs,"Map#{i}",origMessages) + origMessages.messages[0].length.times do |i| + msgs = origMessages.messages[0][i] + Messages.writeObject(f, msgs, "Map#{i}", origMessages) end end - for i in 1...origMessages.messages.length - msgs=origMessages.messages[i] - Messages.writeObject(f,msgs,i,origMessages) + (1...origMessages.messages.length).each do |i| + msgs = origMessages.messages[i] + Messages.writeObject(f, msgs, i, origMessages) end } end - def setMessages(type,array) - @messages=[] if !@messages - arr=[] - for i in 0...array.length - arr[i]=(array[i]) ? array[i] : "" + def setMessages(type, array) + @messages = [] if !@messages + arr = [] + array.length.times do |i| + arr[i] = (array[i]) ? array[i] : "" end - @messages[type]=arr + @messages[type] = arr end - def addMessages(type,array) - @messages=[] if !@messages - arr=(@messages[type]) ? @messages[type] : [] - for i in 0...array.length - arr[i]=(array[i]) ? array[i] : (arr[i]) ? arr[i] : "" + def addMessages(type, array) + @messages = [] if !@messages + arr = (@messages[type]) ? @messages[type] : [] + array.length.times do |i| + arr[i] = (array[i]) ? array[i] : (arr[i]) ? arr[i] : "" end - @messages[type]=arr + @messages[type] = arr end - def self.createHash(_type,array) - arr=OrderedHash.new - for i in 0...array.length + def self.createHash(_type, array) + arr = OrderedHash.new + array.length.times do |i| if array[i] - key=Messages.stringToKey(array[i]) - arr[key]=array[i] + key = Messages.stringToKey(array[i]) + arr[key] = array[i] end end return arr end - def self.addToHash(_type,array,hash) - hash=OrderedHash.new if !hash - for i in 0...array.length + def self.addToHash(_type, array, hash) + hash = OrderedHash.new if !hash + array.length.times do |i| if array[i] - key=Messages.stringToKey(array[i]) - hash[key]=array[i] + key = Messages.stringToKey(array[i]) + hash[key] = array[i] end end return hash end - def setMapMessagesAsHash(type,array) - @messages=[] if !@messages - @messages[0]=[] if !@messages[0] - @messages[0][type]=Messages.createHash(type,array) + def setMapMessagesAsHash(type, array) + @messages = [] if !@messages + @messages[0] = [] if !@messages[0] + @messages[0][type] = Messages.createHash(type, array) end - def addMapMessagesAsHash(type,array) - @messages=[] if !@messages - @messages[0]=[] if !@messages[0] - @messages[0][type]=Messages.addToHash(type,array,@messages[0][type]) + def addMapMessagesAsHash(type, array) + @messages = [] if !@messages + @messages[0] = [] if !@messages[0] + @messages[0][type] = Messages.addToHash(type, array, @messages[0][type]) end - def setMessagesAsHash(type,array) - @messages=[] if !@messages - @messages[type]=Messages.createHash(type,array) + def setMessagesAsHash(type, array) + @messages = [] if !@messages + @messages[type] = Messages.createHash(type, array) end - def addMessagesAsHash(type,array) - @messages=[] if !@messages - @messages[type]=Messages.addToHash(type,array,@messages[type]) + def addMessagesAsHash(type, array) + @messages = [] if !@messages + @messages[type] = Messages.addToHash(type, array, @messages[type]) end - def saveMessages(filename=nil) - filename="Data/messages.dat" if !filename - File.open(filename,"wb") { |f| Marshal.dump(@messages,f) } + def saveMessages(filename = nil) + filename = "Data/messages.dat" if !filename + File.open(filename, "wb") { |f| Marshal.dump(@messages, f) } end def loadMessageFile(filename) begin - pbRgssOpen(filename,"rb") { |f| @messages=Marshal.load(f) } + pbRgssOpen(filename, "rb") { |f| @messages = Marshal.load(f) } if !@messages.is_a?(Array) - @messages=nil + @messages = nil raise "Corrupted data" end return @messages rescue - @messages=nil + @messages = nil return nil end end - def set(type,id,value) + def set(type, id, value) delayedLoad return if !@messages return if !@messages[type] - @messages[type][id]=value + @messages[type][id] = value end def getCount(type) @@ -566,7 +559,7 @@ def getCount(type) return @messages[type].length end - def get(type,id) + def get(type, id) delayedLoad return "" if !@messages return "" if !@messages[type] @@ -574,21 +567,21 @@ def get(type,id) return @messages[type][id] end - def getFromHash(type,key) + def getFromHash(type, key) delayedLoad return key if !@messages || !@messages[type] || !key - id=Messages.stringToKey(key) + id = Messages.stringToKey(key) return key if !@messages[type][id] return @messages[type][id] end - def getFromMapHash(type,key) + def getFromMapHash(type, key) delayedLoad return key if !@messages return key if !@messages[0] return key if !@messages[0][type] && !@messages[0][0] - id=Messages.stringToKey(key) - if @messages[0][type] && @messages[0][type][id] + id = Messages.stringToKey(key) + if @messages[0][type] && @messages[0][type][id] return @messages[0][type][id] elsif @messages[0][0] && @messages[0][0][id] return @messages[0][0][id] @@ -627,8 +620,9 @@ module MessageTypes ScriptTexts = 24 RibbonNames = 25 RibbonDescriptions = 26 + StorageCreator = 27 @@messages = Messages.new - @@messagesFallback = Messages.new("Data/messages.dat",true) + @@messagesFallback = Messages.new("Data/messages.dat", true) def self.stringToKey(str) return Messages.stringToKey(str) @@ -642,7 +636,7 @@ def self.denormalizeValue(value) Messages.denormalizeValue(value) end - def self.writeObject(f,msgs,secname) + def self.writeObject(f, msgs, secname) Messages.denormalizeValue(str) end @@ -650,35 +644,35 @@ def self.extract(outfile) @@messages.extract(outfile) end - def self.setMessages(type,array) - @@messages.setMessages(type,array) + def self.setMessages(type, array) + @@messages.setMessages(type, array) end - def self.addMessages(type,array) - @@messages.addMessages(type,array) + def self.addMessages(type, array) + @@messages.addMessages(type, array) end - def self.createHash(type,array) - Messages.createHash(type,array) + def self.createHash(type, array) + Messages.createHash(type, array) end - def self.addMapMessagesAsHash(type,array) - @@messages.addMapMessagesAsHash(type,array) + def self.addMapMessagesAsHash(type, array) + @@messages.addMapMessagesAsHash(type, array) end - def self.setMapMessagesAsHash(type,array) - @@messages.setMapMessagesAsHash(type,array) + def self.setMapMessagesAsHash(type, array) + @@messages.setMapMessagesAsHash(type, array) end - def self.addMessagesAsHash(type,array) - @@messages.addMessagesAsHash(type,array) + def self.addMessagesAsHash(type, array) + @@messages.addMessagesAsHash(type, array) end - def self.setMessagesAsHash(type,array) - @@messages.setMessagesAsHash(type,array) + def self.setMessagesAsHash(type, array) + @@messages.setMessagesAsHash(type, array) end - def self.saveMessages(filename=nil) + def self.saveMessages(filename = nil) @@messages.saveMessages(filename) end @@ -686,30 +680,30 @@ def self.loadMessageFile(filename) @@messages.loadMessageFile(filename) end - def self.get(type,id) - ret=@@messages.get(type,id) - if ret=="" - ret=@@messagesFallback.get(type,id) + def self.get(type, id) + ret = @@messages.get(type, id) + if ret == "" + ret = @@messagesFallback.get(type, id) end return ret end def self.getCount(type) - c1=@@messages.getCount(type) - c2=@@messagesFallback.getCount(type) - return c1>c2 ? c1 : c2 + c1 = @@messages.getCount(type) + c2 = @@messagesFallback.getCount(type) + return c1 > c2 ? c1 : c2 end - def self.getOriginal(type,id) - return @@messagesFallback.get(type,id) + def self.getOriginal(type, id) + return @@messagesFallback.get(type, id) end - def self.getFromHash(type,key) - @@messages.getFromHash(type,key) + def self.getFromHash(type, key) + @@messages.getFromHash(type, key) end - def self.getFromMapHash(type,key) - @@messages.getFromMapHash(type,key) + def self.getFromMapHash(type, key) + @@messages.getFromMapHash(type, key) end end @@ -723,25 +717,25 @@ def pbGetMessageCount(type) return MessageTypes.getCount(type) end -def pbGetMessage(type,id) - return MessageTypes.get(type,id) +def pbGetMessage(type, id) + return MessageTypes.get(type, id) end -def pbGetMessageFromHash(type,id) - return MessageTypes.getFromHash(type,id) +def pbGetMessageFromHash(type, id) + return MessageTypes.getFromHash(type, id) end # Replaces first argument with a localized version and formats the other # parameters by replacing {1}, {2}, etc. with those placeholders. def _INTL(*arg) begin - string=MessageTypes.getFromHash(MessageTypes::ScriptTexts,arg[0]) + string = MessageTypes.getFromHash(MessageTypes::ScriptTexts, arg[0]) rescue - string=arg[0] + string = arg[0] end - string=string.clone - for i in 1...arg.length - string.gsub!(/\{#{i}\}/,"#{arg[i]}") + string = string.clone + (1...arg.length).each do |i| + string.gsub!(/\{#{i}\}/, arg[i].to_s) end return string end @@ -751,38 +745,38 @@ def _INTL(*arg) # This version acts more like sprintf, supports e.g. {1:d} or {2:s} def _ISPRINTF(*arg) begin - string=MessageTypes.getFromHash(MessageTypes::ScriptTexts,arg[0]) + string = MessageTypes.getFromHash(MessageTypes::ScriptTexts, arg[0]) rescue - string=arg[0] + string = arg[0] end - string=string.clone - for i in 1...arg.length + string = string.clone + (1...arg.length).each do |i| string.gsub!(/\{#{i}\:([^\}]+?)\}/) { |m| - next sprintf("%"+$1,arg[i]) + next sprintf("%" + $1, arg[i]) } end return string end -def _I(str) - return _MAPINTL($game_map.map_id,str) +def _I(str, *arg) + return _MAPINTL($game_map.map_id, str, *arg) end -def _MAPINTL(mapid,*arg) - string=MessageTypes.getFromMapHash(mapid,arg[0]) - string=string.clone - for i in 1...arg.length - string.gsub!(/\{#{i}\}/,"#{arg[i]}") +def _MAPINTL(mapid, *arg) + string = MessageTypes.getFromMapHash(mapid, arg[0]) + string = string.clone + (1...arg.length).each do |i| + string.gsub!(/\{#{i}\}/, arg[i].to_s) end return string end -def _MAPISPRINTF(mapid,*arg) - string=MessageTypes.getFromMapHash(mapid,arg[0]) - string=string.clone - for i in 1...arg.length +def _MAPISPRINTF(mapid, *arg) + string = MessageTypes.getFromMapHash(mapid, arg[0]) + string = string.clone + (1...arg.length).each do |i| string.gsub!(/\{#{i}\:([^\}]+?)\}/) { |m| - next sprintf("%"+$1,arg[i]) + next sprintf("%" + $1, arg[i]) } end return string diff --git a/Data/Scripts/001_Technical/005_PluginManager.rb b/Data/Scripts/001_Technical/005_PluginManager.rb index 650c700460..234d0886aa 100644 --- a/Data/Scripts/001_Technical/005_PluginManager.rb +++ b/Data/Scripts/001_Technical/005_PluginManager.rb @@ -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,152 +12,92 @@ #------------------------------------------------------------------------------# # Usage: # # # -# A Pokémon Essentials plugin should register itself using the PluginManager. # -# The simplest way to do so, for a plugin without dependencies, is as follows: # +# 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 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. # +# # +# Required lines: # +# # +# 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 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. # +# # +# 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. # +# # +# 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. # +# # +# 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. # # # -# PluginManager.register({ # -# :name => "Basic Plugin", # -# :version => "1.0", # -# :link => "https://reliccastle.com/link-to-the-plugin/", # -# :credits => "Marin" # -# }) # -# # -# The link portion here is optional, but recommended. This will be shown in # -# the error message if the PluginManager detects that this plugin needs to be # -# updated. # -# # -# A plugin's version 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. # -# # -# 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: # -# # -# :credits => "Marin" # -# :credits => ["Marin", "Maruno"], # -# # -# # -# # -# Dependency: # +#------------------------------------------------------------------------------# +# The code behind plugins: # # # -# 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: # +# 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: # # # # PluginManager.register({ # -# :name => "Simple Extension", # -# :version => "1.0", # -# :link => "https://reliccastle.com/link-to-the-plugin/", # -# :credits => ["Marin", "Maruno"], # -# :dependencies => ["Basic Plugin"] # +# :name => "Basic Plugin", # +# :version => "1.0", # +# :essentials => "20", # +# :link => "https://reliccastle.com/link-to-the-plugin/", # +# :credits => ["Marin"] # # }) # # # -# 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"] # -# ] # +# 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. # # # -# 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. # +# Here is the same example but also with dependencies and conflicts: # # # # PluginManager.register({ # -# :name => "QoL Improvements", # -# :version => "1.0", # -# :link => "https://reliccastle.com/link-to-the-plugin/", # -# :credits => "Marin", # -# :incompatibilities => [ # -# "Simple Extension" # -# ] # +# :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"] # # }) # # # -#------------------------------------------------------------------------------# -# Plugin folder: # -# # -# 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. # -# # -# 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. # -# # -# 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. # -# # -# 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). # -# # -# The contents of meta.txt are as follows: # -# # -# 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. # +# 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". # # # #------------------------------------------------------------------------------# # Please give credit when using this. # @@ -172,21 +112,20 @@ module PluginManager def self.register(options) name = nil version = nil + essentials = nil link = nil dependencies = nil incompats = nil credits = [] - order = [:name, :version, :link, :dependencies, :incompatibilities, :credits] + order = [:name, :version, :essentials, :link, :dependencies, :incompatibilities, :credits] # Ensure it first reads the plugin's name, which is used in error reporting, # by sorting the keys keys = options.keys.sort do |a, b| - idx_a = order.index(a) - idx_a = order.size if idx_a == -1 - idx_b = order.index(b) - idx_b = order.size if idx_b == -1 + idx_a = order.index(a) || order.size + idx_b = order.index(b) || order.size next idx_a <=> idx_b end - for key in keys + keys.each do |key| value = options[key] case key when :name # Plugin name @@ -202,6 +141,8 @@ def self.register(options) 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.") @@ -210,12 +151,13 @@ def self.register(options) when :dependencies # Plugin dependencies dependencies = value dependencies = [dependencies] if !dependencies.is_a?(Array) || !dependencies[0].is_a?(Array) - for dep in value - if dep.is_a?(String) # "plugin name" + value.each do |dep| + case dep + when String # "plugin name" if !self.installed?(dep) self.error("Plugin '#{name}' requires plugin '#{dep}' to be installed above it.") end - elsif dep.is_a?(Array) + when Array case dep.size when 1 # ["plugin name"] if dep[0].is_a?(String) @@ -236,7 +178,8 @@ def self.register(options) if self.installed?(dep_name) # Have plugin but lower version msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} or higher, " + "but the installed version is #{self.version(dep_name)}." - if dep_link = self.link(dep_name) + dep_link = self.link(dep_name) + if dep_link msg += "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'." end self.error(msg) @@ -278,7 +221,8 @@ def self.register(options) msg = "Plugin '#{name}' requires plugin '#{dep_name}', if installed, to be version #{dep_version}" msg << " or higher" if !exact msg << ", but the installed version was #{self.version(dep_name)}." - if dep_link = self.link(dep_name) + dep_link = self.link(dep_name) + if dep_link msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'." end self.error(msg) @@ -288,16 +232,16 @@ def self.register(options) msg = "Plugin '#{name}' requires plugin '#{dep_name}' to be version #{dep_version}" msg << " or later" if !exact msg << ", but the installed version was #{self.version(dep_name)}." - if dep_link = self.link(dep_name) + dep_link = self.link(dep_name) + if dep_link 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 @@ -305,7 +249,7 @@ def self.register(options) when :incompatibilities # Plugin incompatibilities incompats = value incompats = [incompats] if !incompats.is_a?(Array) - for incompat in incompats + incompats.each do |incompat| if self.installed?(incompat) self.error("Plugin '#{name}' is incompatible with '#{incompat}'. " + "They cannot both be used at the same time.") @@ -314,11 +258,11 @@ def self.register(options) when :credits # Plugin credits value = [value] if value.is_a?(String) if value.is_a?(Array) - for entry in value - if !entry.is_a?(String) - self.error("Plugin '#{name}'s credits array contains a non-string value.") - else + value.each do |entry| + if entry.is_a?(String) credits << entry + else + self.error("Plugin '#{name}'s credits array contains a non-string value.") end end else @@ -328,8 +272,8 @@ def self.register(options) self.error("Invalid plugin registry key '#{key}'.") end end - for plugin in @@Plugins.values - if plugin[:incompatibilities] && plugin[:incompatibilities].include?(name) + @@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.") end @@ -338,6 +282,7 @@ def self.register(options) @@Plugins[name] = { :name => name, :version => version, + :essentials => essentials, :link => link, :dependencies => dependencies, :incompatibilities => incompats, @@ -350,8 +295,8 @@ def self.register(options) def self.error(msg) Graphics.update t = Thread.new do - echoln "Plugin Error:\r\n#{msg}" - p "Plugin Error: #{msg}" + Console.echo_error "Plugin Error:\r\n#{msg}" + print("Plugin Error:\r\n#{msg}") Thread.exit end while t.status @@ -408,21 +353,25 @@ def self.credits(plugin_name) # -1 if v1 is lower than v2 #----------------------------------------------------------------------------- def self.compare_versions(v1, v2) - d1 = v1.split("") - d1.insert(0, "0") if d1[0] == "." # Turn ".123" into "0.123" - while d1[-1] == "."; d1 = d1[0..-2]; end # Turn "123." into "123" - d2 = v2.split("") - d2.insert(0, "0") if d2[0] == "." # Turn ".123" into "0.123" - while d2[-1] == "."; d2 = d2[0..-2]; end # Turn "123." into "123" - for i in 0...[d1.size, d2.size].max # Compare each digit in turn + d1 = v1.chars + d1.insert(0, "0") if d1[0] == "." # Turn ".123" into "0.123" + while d1[-1] == "." # Turn "123." into "123" + d1 = d1[0..-2] + end + d2 = v2.chars + d2.insert(0, "0") if d2[0] == "." # Turn ".123" into "0.123" + while d2[-1] == "." # Turn "123." into "123" + d2 = d2[0..-2] + end + [d1.size, d2.size].max.times do |i| # 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 + elsif c2 + return -1 end end return 0 @@ -431,25 +380,17 @@ def self.compare_versions(v1, v2) # 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 += "#{$!.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 + message += "Error in Plugin: [#{name}]\r\n" + message += "Exception: #{e.class}\r\n" + message += "Message: " + message += e.message # show last 10 lines of backtrace - message += "\r\nBacktrace:\r\n" - $!.backtrace[0, 10].each { |i| message += "#{i}\r\n" } + message += "\r\n\r\nBacktrace:\r\n" + e.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) @@ -466,7 +407,7 @@ def self.pluginErrorMsg(name, script) 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.delta - until (System.delta - t) >= 500000 + until (System.delta - t) >= 500_000 Input.update if Input.press?(Input::CTRL) Input.clipboard = message @@ -487,11 +428,14 @@ def self.readMeta(dir, file) 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 'REQUIRES' + when "ESSENTIALS" + meta[:essentials] = [] if !meta[:essentials] + data.each { |ver| meta[:essentials].push(ver) } + 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]) @@ -501,23 +445,23 @@ def self.readMeta(dir, file) 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] @@ -527,7 +471,7 @@ def self.readMeta(dir, file) # be loaded (files listed in the meta file are loaded first) meta[:scripts] = [] if !meta[:scripts] # get all script files from plugin Dir - for fl in Dir.all(dir) + Dir.all(dir).each do |fl| next if !fl.include?(".rb") meta[:scripts].push(fl.gsub("#{dir}/", "")) end @@ -555,7 +499,7 @@ def self.validateDependencies(name, meta, og = nil) return nil if !meta[name] || !meta[name][:dependencies] og = [name] if !og # go through all dependencies - for dname in meta[name][:dependencies] + meta[name][:dependencies].each do |dname| # 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 @@ -572,14 +516,14 @@ def self.validateDependencies(name, meta, og = nil) #----------------------------------------------------------------------------- def self.sortLoadOrder(order, plugins) # go through the load order - for o in order + order.each do |o| next if !plugins[o] || !plugins[o][:dependencies] # go through all dependencies - for dname in plugins[o][:dependencies] + plugins[o][:dependencies].each do |dname| optional = false # clean the name to a simple string if dname.is_a?(Array) - optional = [:optional,:optional_exact].include?(dname[0]) + optional = [:optional, :optional_exact].include?(dname[0]) dname = dname[dname.length - 2] end # catch missing dependency @@ -604,7 +548,7 @@ def self.getPluginOrder order = [] # Find all plugin folders that have a meta.txt and add them to the list of # plugins. - for dir in self.listAll + self.listAll.each do |dir| # skip if there is no meta file next if !safeExists?(dir + "/meta.txt") ndx = order.length @@ -632,14 +576,14 @@ def self.needCompiling?(order, plugins) return false if !$DEBUG || safeExists?("Game.rgssad") return true if !safeExists?("Data/PluginScripts.rxdata") Input.update - return true if Input.press?(Input::CTRL) + return true if Input.press?(Input::SHIFT) || Input.press?(Input::CTRL) # analyze whether or not to push recompile mtime = File.mtime("Data/PluginScripts.rxdata") - for o in order + order.each do |o| # go through all the registered plugin scripts scr = plugins[o][:scripts] dir = plugins[o][:dir] - for sc in scr + scr.each do |sc| return true if File.mtime("#{dir}/#{sc}") > mtime end return true if File.mtime("#{dir}/meta.txt") > mtime @@ -650,18 +594,18 @@ def self.needCompiling?(order, plugins) # Check if plugins need compiling #----------------------------------------------------------------------------- def self.compilePlugins(order, plugins) - echo 'Compiling plugin scripts...' + Console.echo_li "Compiling plugin scripts..." scripts = [] # go through the entire order one by one - for o in order + order.each do |o| # 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 - for file in plugins[o][:scripts] - File.open("#{plugins[o][:dir]}/#{file}", 'rb') do |f| + plugins[o][:scripts].each do |file| + File.open("#{plugins[o][:dir]}/#{file}", "rb") do |f| dat[2].push([file, Zlib::Deflate.deflate(f.read)]) end end @@ -669,16 +613,17 @@ def self.compilePlugins(order, plugins) 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 - echoln ' done.' - echoln '' + Console.echo_done(true) + echoln "" if scripts.length == 0 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 @@ -686,24 +631,27 @@ def self.runPlugins # load plugins scripts = load_data("Data/PluginScripts.rxdata") echoed_plugins = [] - for plugin in scripts + scripts.each do |plugin| # 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 - for scr in script + script.each do |scr| # 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) - echoln "Loaded plugin: #{name}" if !echoed_plugins.include?(name) + Console.echoln_li "Loaded plugin: '#{name}' (ver. #{meta[:version]})" if !echoed_plugins.include?(name) echoed_plugins.push(name) rescue Exception # format error message to display self.pluginErrorMsg(name, sname) @@ -711,7 +659,27 @@ def self.runPlugins end end end - echoln '' if !echoed_plugins.empty? + if scripts.length > 0 + echoln "" + Console.echo_h2("Successfully loaded #{scripts.length} plugin(s)", text: :green) + else + Console.echo_h2("No plugins found", text: :green) + 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 !safeExists?(dir + "/meta.txt") + # read meta + meta = self.readMeta(dir, "meta.txt") + return dir if meta[:name] == name + end + # return nil if no plugin dir found + return nil end #----------------------------------------------------------------------------- end diff --git a/Data/Scripts/001_Technical/006_RPG_Sprite.rb b/Data/Scripts/001_Technical/006_RPG_Sprite.rb index 7f5e36a3eb..eb535d4024 100644 --- a/Data/Scripts/001_Technical/006_RPG_Sprite.rb +++ b/Data/Scripts/001_Technical/006_RPG_Sprite.rb @@ -6,16 +6,14 @@ 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__ + ["x", "y", "ox", "oy", "viewport", "flash", "src_rect", "opacity", "tone"].each do |def_name| + eval <<-__END__ - def #{s}(*arg) - @sprite.#{s}(*arg) - end + def #{def_name}(*arg) # def x(*arg) + @sprite.#{def_name}(*arg) # @sprite.x(*arg) + end # end - __END__ + __END__ end def self.clear @@ -30,7 +28,7 @@ def dispose def animation(animation, hit, height = 3) dispose_animation @_animation = animation - return if @_animation == nil + return if @_animation.nil? @_animation_hit = hit @_animation_height = height @_animation_duration = @_animation.frame_max @@ -66,7 +64,7 @@ def loop_animation(animation) return if animation == @_loop_animation dispose_loop_animation @_loop_animation = animation - return if @_loop_animation == nil + return if @_loop_animation.nil? @_loop_animation_index = 0 fr = 20 if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/] @@ -92,15 +90,15 @@ def loop_animation(animation) end def dispose_animation - return if @_animation_sprites == nil + return if @_animation_sprites.nil? sprite = @_animation_sprites[0] - if sprite != nil + if sprite @@_reference_count[sprite.bitmap] -= 1 if @@_reference_count[sprite.bitmap] == 0 sprite.bitmap.dispose end end - for sprite in @_animation_sprites + @_animation_sprites.each do |sprite| sprite.dispose end @_animation_sprites = nil @@ -108,15 +106,15 @@ def dispose_animation end def dispose_loop_animation - return if @_loop_animation_sprites == nil + return if @_loop_animation_sprites.nil? sprite = @_loop_animation_sprites[0] - if sprite != nil + if sprite @@_reference_count[sprite.bitmap] -= 1 if @@_reference_count[sprite.bitmap] == 0 sprite.bitmap.dispose end end - for sprite in @_loop_animation_sprites + @_loop_animation_sprites.each do |sprite| sprite.dispose end @_loop_animation_sprites = nil @@ -124,7 +122,7 @@ def dispose_loop_animation end def active? - return @_loop_animation_sprites != nil || @_animation_sprites != nil + return @_loop_animation_sprites || @_animation_sprites end def effect? @@ -132,7 +130,7 @@ def effect? end def update - if @_animation != nil + if @_animation quick_update = true if Graphics.frame_count % @_animation_frame_skip == 0 @_animation_duration -= 1 @@ -140,7 +138,7 @@ def update end update_animation(quick_update) end - if @_loop_animation != nil + if @_loop_animation quick_update = (Graphics.frame_count % @_loop_animation_frame_skip != 0) update_loop_animation(quick_update) if !quick_update @@ -160,7 +158,7 @@ def update_animation(quick_update = false) position = @_animation.position animation_set_sprites(@_animation_sprites, cell_data, position, quick_update) return if quick_update - for timing in @_animation.timings + @_animation.timings.each do |timing| next if timing.frame != frame_index animation_process_timing(timing, @_animation_hit) end @@ -172,7 +170,7 @@ def update_loop_animation(quick_update = false) 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 + @_loop_animation.timings.each do |timing| next if timing.frame != frame_index animation_process_timing(timing, true) end @@ -182,33 +180,33 @@ def animation_set_sprites(sprites, cell_data, position, quick_update = false) sprite_x = 320 sprite_y = 240 if position == 3 - if self.viewport != nil + 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_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 + 16.times do |i| sprite = sprites[i] pattern = cell_data[i, 0] - if sprite == nil || pattern == nil || pattern == -1 - sprite.visible = false if sprite != nil + 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] + sprite.x = sprite_x + cell_data[i, 1] + sprite.y = sprite_y + cell_data[i, 2] next if quick_update - sprite.visible = true + 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 + 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 @@ -234,9 +232,7 @@ def animation_process_timing(timing, hit) 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 + self.viewport.flash(timing.flash_color, timing.flash_duration * 2) if self.viewport when 3 self.flash(nil, timing.flash_duration * 2) end @@ -246,30 +242,22 @@ def animation_process_timing(timing, hit) 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 + if @_animation_sprites + 16.times { |i| @_animation_sprites[i].x += sx } end - if @_loop_animation_sprites != nil - for i in 0..15 - @_loop_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 != nil - for i in 0..15 - @_animation_sprites[i].y += sy - end + if @_animation_sprites + 16.times { |i| @_animation_sprites[i].y += sy } end - if @_loop_animation_sprites != nil - for i in 0..15 - @_loop_animation_sprites[i].y += sy - end + if @_loop_animation_sprites + 16.times { |i| @_loop_animation_sprites[i].y += sy } end end end @@ -299,9 +287,9 @@ def dispose end def whiten - self.blend_type = 0 + self.blend_type = 0 self.color.set(255, 255, 255, 128) - self.opacity = 255 + self.opacity = 255 @_whiten_duration = 16 @_appear_duration = 0 @_escape_duration = 0 @@ -345,10 +333,10 @@ def damage(value, critical) 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) + 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 @@ -370,14 +358,14 @@ def damage(value, critical) @_damage_sprite.ox = 80 @_damage_sprite.oy = 20 @_damage_sprite.x = self.x - @_damage_sprite.y = self.y - self.oy / 2 + @_damage_sprite.y = self.y - (self.oy / 2) @_damage_sprite.z = 3000 @_damage_duration = 40 end def pushAnimation(array, anim) - for i in 0...array.length - next if array[i] && array[i].active? + array.length.times do |i| + next if array[i]&.active? array[i] = anim return end @@ -386,18 +374,18 @@ def pushAnimation(array, anim) def animation(animation, hit, height = 3) anim = SpriteAnimation.new(self) - anim.animation(animation,hit,height) - 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 + return if @_damage_sprite.nil? @_damage_sprite.bitmap.dispose @_damage_sprite.dispose @_damage_sprite = nil @@ -405,15 +393,15 @@ def dispose_damage end def dispose_animation - for a in @animations - a.dispose_animation if a + @animations.each do |a| + a&.dispose_animation end @animations.clear end def dispose_loop_animation - for a in @loopAnimations - a.dispose_loop_animation if a + @loopAnimations.each do |a| + a&.dispose_loop_animation end @loopAnimations.clear end @@ -440,7 +428,7 @@ def effect? return true if @_escape_duration > 0 return true if @_collapse_duration > 0 return true if @_damage_duration > 0 - for a in @animations + @animations.each do |a| return true if a.effect? end return false @@ -450,7 +438,7 @@ def update super if @_whiten_duration > 0 @_whiten_duration -= 1 - self.color.alpha = 128 - (16 - @_whiten_duration) * 10 + self.color.alpha = 128 - ((16 - @_whiten_duration) * 10) end if @_appear_duration > 0 @_appear_duration -= 1 @@ -458,11 +446,11 @@ def update end if @_escape_duration > 0 @_escape_duration -= 1 - self.opacity = 256 - (32 - @_escape_duration) * 10 + self.opacity = 256 - ((32 - @_escape_duration) * 10) end if @_collapse_duration > 0 @_collapse_duration -= 1 - self.opacity = 256 - (48 - @_collapse_duration) * 6 + self.opacity = 256 - ((48 - @_collapse_duration) * 6) end if @_damage_duration > 0 @_damage_duration -= 1 @@ -476,15 +464,15 @@ def update when 28..33 @_damage_sprite.y += 4 end - @_damage_sprite.opacity = 256 - (12 - @_damage_duration) * 32 + @_damage_sprite.opacity = 256 - ((12 - @_damage_duration) * 32) if @_damage_duration == 0 dispose_damage end end - for a in @animations + @animations.each do |a| a.update end - for a in @loopAnimations + @loopAnimations.each do |a| a.update end if @_blink @@ -500,32 +488,32 @@ def update end def update_animation - for a in @animations - a.update_animation if a && a.active? + @animations.each do |a| + a.update_animation if a&.active? end end def update_loop_animation - for a in @loopAnimations - a.update_loop_animation if a && a.active? + @loopAnimations.each do |a| + a.update_loop_animation if a&.active? end end def x=(x) - for a in @animations + @animations.each do |a| a.x = x if a end - for a in @loopAnimations + @loopAnimations.each do |a| a.x = x if a end super end def y=(y) - for a in @animations + @animations.each do |a| a.y = y if a end - for a in @loopAnimations + @loopAnimations.each do |a| a.y = y if a end super diff --git a/Data/Scripts/002_BattleSettings.rb b/Data/Scripts/002_BattleSettings.rb index 11af23cd02..664d2f1918 100644 --- a/Data/Scripts/002_BattleSettings.rb +++ b/Data/Scripts/002_BattleSettings.rb @@ -1,35 +1,54 @@ module Settings - # 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 + # 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) + + #============================================================================= + # 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) + 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) - - #============================================================================= - + 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) + 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) + 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) + SOUL_DEW_POWERS_UP_TYPES = (MECHANICS_GENERATION >= 7) + + #============================================================================= + + # 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 as affection effects only start applying above a happiness of 179. + # Also lowers the happiness evolution threshold to 160. + APPLY_HAPPINESS_SOFT_CAP = AFFECTION_EFFECTS #============================================================================= @@ -43,9 +62,6 @@ module Settings #============================================================================= - # 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 @@ -54,22 +70,32 @@ module Settings # 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) + SPLIT_EXP_BETWEEN_GAINERS = (MECHANICS_GENERATION <= 5) + # 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) # 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) + ENABLE_CRITICAL_CAPTURES = (MECHANICS_GENERATION >= 5) # Whether Pokémon gain Exp for capturing a Pokémon. - GAIN_EXP_FOR_CAPTURE = (MECHANICS_GENERATION >= 6) + GAIN_EXP_FOR_CAPTURE = (MECHANICS_GENERATION >= 6) + # 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) + + #============================================================================= + # 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) diff --git a/Data/Scripts/002_Save data/001_SaveData.rb b/Data/Scripts/002_Save data/001_SaveData.rb index 43c8b10f8c..6a7b766280 100644 --- a/Data/Scripts/002_Save data/001_SaveData.rb +++ b/Data/Scripts/002_Save data/001_SaveData.rb @@ -6,9 +6,9 @@ module SaveData # Contains the file path of the save file. FILE_PATH = if File.directory?(System.data_directory) - System.data_directory + '/Game.rxdata' + System.data_directory + "/Game.rxdata" else - './Game.rxdata' + "./Game.rxdata" end # @return [Boolean] whether the save file exists @@ -45,7 +45,7 @@ def self.read_from_file(file_path) save_data = get_data_from_file(file_path) save_data = to_hash_format(save_data) if save_data.is_a?(Array) 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 @@ -57,14 +57,14 @@ def self.read_from_file(file_path) 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 File.delete(FILE_PATH) - File.delete(FILE_PATH + '.bak') if File.file?(FILE_PATH + '.bak') + File.delete(FILE_PATH + ".bak") if File.file?(FILE_PATH + ".bak") end # Converts the pre-v19 format data to the new format. @@ -85,12 +85,12 @@ def self.to_hash_format(old_format) # 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'] + 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) + old_location = File.join(home, "Saved Games", game_title) return unless File.directory?(old_location) - old_file = File.join(old_location, 'Game.rxdata') + old_file = File.join(old_location, "Game.rxdata") return unless File.file?(old_file) File.move(old_file, FILE_PATH) end diff --git a/Data/Scripts/002_Save data/002_SaveData_Value.rb b/Data/Scripts/002_Save data/002_SaveData_Value.rb index 7e5bdf9da8..5a5b4426b7 100644 --- a/Data/Scripts/002_Save data/002_SaveData_Value.rb +++ b/Data/Scripts/002_Save data/002_SaveData_Value.rb @@ -114,21 +114,21 @@ def ensure_class(class_name) # 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 @@ -142,7 +142,7 @@ def load_in_bootup # 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 @@ -181,11 +181,11 @@ def from_old_format(&block) # new_game_value { Bar.new } # end # @param id [Symbol] value id - # @yieldself [Value] + # @yield the block of code to be saved as a 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 diff --git a/Data/Scripts/002_Save data/003_SaveData_Conversion.rb b/Data/Scripts/002_Save data/003_SaveData_Conversion.rb index f48bc35eae..9131f13a92 100644 --- a/Data/Scripts/002_Save data/003_SaveData_Conversion.rb +++ b/Data/Scripts/002_Save data/003_SaveData_Conversion.rb @@ -112,7 +112,7 @@ def game_version(version) # @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 @@ -122,7 +122,7 @@ def to_value(value_id, &block) # 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 @@ -152,11 +152,11 @@ def to_all(&block) # save_data[:new_value] = Foo.new # end # end - # @yield self [Conversion] + # @yield the block of code to be saved as a 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] ||= [] @@ -168,8 +168,8 @@ def self.register_conversion(id, &block) 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 @@ -194,14 +194,15 @@ def self.run_conversions(save_data) validate save_data => Hash conversions_to_run = self.get_conversions(save_data) return false if conversions_to_run.none? - File.open(SaveData::FILE_PATH + '.bak', 'wb') { |f| Marshal.dump(save_data, f) } - echoln "Running #{conversions_to_run.length} conversions..." + File.open(SaveData::FILE_PATH + ".bak", "wb") { |f| Marshal.dump(save_data, f) } + Console.echo_h1 "Running #{conversions_to_run.length} save file conversions" conversions_to_run.each do |conversion| - echo "#{conversion.title}..." + Console.echo_li "#{conversion.title}..." conversion.run(save_data) - echoln ' done.' + Console.echo_done(true) end - echoln '' if conversions_to_run.length > 0 + echoln "" if conversions_to_run.length > 0 + Console.echo_h2("All save file conversions applied successfully", text: :green) save_data[:essentials_version] = Essentials::VERSION save_data[:game_version] = Settings::GAME_VERSION return true diff --git a/Data/Scripts/002_Save data/004_Game_SaveValues.rb b/Data/Scripts/002_Save data/004_Game_SaveValues.rb index 18d3ab07bd..3cda2628f5 100644 --- a/Data/Scripts/002_Save data/004_Game_SaveValues.rb +++ b/Data/Scripts/002_Save data/004_Game_SaveValues.rb @@ -2,11 +2,11 @@ SaveData.register(:player) do ensure_class :Player - save_value { $Trainer } - load_value { |value| $Trainer = value } + save_value { $player } + load_value { |value| $player = $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 } + # Get the first defined trainer type as a placeholder + trainer_type = GameData::TrainerType.keys.first Player.new("Unnamed", trainer_type) } from_old_format { |old_format| old_format[0] } @@ -72,8 +72,8 @@ SaveData.register(:map_factory) do ensure_class :PokemonMapFactory - save_value { $MapFactory } - load_value { |value| $MapFactory = value } + save_value { $map_factory } + load_value { |value| $map_factory = $MapFactory = value } from_old_format { |old_format| old_format[9] } end @@ -103,8 +103,8 @@ SaveData.register(:bag) do ensure_class :PokemonBag - save_value { $PokemonBag } - load_value { |value| $PokemonBag = value } + save_value { $bag } + load_value { |value| $bag = $PokemonBag = value } new_game_value { PokemonBag.new } from_old_format { |old_format| old_format[13] } end @@ -121,7 +121,7 @@ load_in_bootup ensure_class :String save_value { Essentials::VERSION } - load_value { |value| $SaveVersion = value } + load_value { |value| $save_engine_version = value } new_game_value { Essentials::VERSION } from_old_format { |old_format| old_format[15] } end @@ -130,6 +130,14 @@ load_in_bootup ensure_class :String save_value { Settings::GAME_VERSION } - load_value { |value| $game_version = value } + load_value { |value| $save_game_version = value } new_game_value { Settings::GAME_VERSION } end + +SaveData.register(:stats) do + load_in_bootup + ensure_class :GameStats + save_value { $stats } + load_value { |value| $stats = value } + new_game_value { GameStats.new } +end diff --git a/Data/Scripts/002_Save data/005_Game_SaveConversions.rb b/Data/Scripts/002_Save data/005_Game_SaveConversions.rb index 554864de69..aa1c59f93e 100644 --- a/Data/Scripts/002_Save data/005_Game_SaveConversions.rb +++ b/Data/Scripts/002_Save data/005_Game_SaveConversions.rb @@ -1,242 +1,350 @@ -# Contains conversions defined in Essentials by default. +#=============================================================================== +# Conversions required to support backwards compatibility with old save files +# (within reason). +#=============================================================================== -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 +# Planted berries accidentally weren't converted in v19 to change their +# numerical IDs to symbolic IDs (for the berry planted and for mulch laid down). +# Since item numerical IDs no longer exist, this conversion needs to have a list +# of them in order to convert planted berry data properly. +SaveData.register_conversion(:v20_fix_planted_berry_numerical_ids) do + essentials_version 20 + display_title "Fixing berry plant IDs data" + to_value :global_metadata do |global| + berry_conversion = { + 389 => :CHERIBERRY, + 390 => :CHESTOBERRY, + 391 => :PECHABERRY, + 392 => :RAWSTBERRY, + 393 => :ASPEARBERRY, + 394 => :LEPPABERRY, + 395 => :ORANBERRY, + 396 => :PERSIMBERRY, + 397 => :LUMBERRY, + 398 => :SITRUSBERRY, + 399 => :FIGYBERRY, + 400 => :WIKIBERRY, + 401 => :MAGOBERRY, + 402 => :AGUAVBERRY, + 403 => :IAPAPABERRY, + 404 => :RAZZBERRY, + 405 => :BLUKBERRY, + 406 => :NANABBERRY, + 407 => :WEPEARBERRY, + 408 => :PINAPBERRY, + 409 => :POMEGBERRY, + 410 => :KELPSYBERRY, + 411 => :QUALOTBERRY, + 412 => :HONDEWBERRY, + 413 => :GREPABERRY, + 414 => :TAMATOBERRY, + 415 => :CORNNBERRY, + 416 => :MAGOSTBERRY, + 417 => :RABUTABERRY, + 418 => :NOMELBERRY, + 419 => :SPELONBERRY, + 420 => :PAMTREBERRY, + 421 => :WATMELBERRY, + 422 => :DURINBERRY, + 423 => :BELUEBERRY, + 424 => :OCCABERRY, + 425 => :PASSHOBERRY, + 426 => :WACANBERRY, + 427 => :RINDOBERRY, + 428 => :YACHEBERRY, + 429 => :CHOPLEBERRY, + 430 => :KEBIABERRY, + 431 => :SHUCABERRY, + 432 => :COBABERRY, + 433 => :PAYAPABERRY, + 434 => :TANGABERRY, + 435 => :CHARTIBERRY, + 436 => :KASIBBERRY, + 437 => :HABANBERRY, + 438 => :COLBURBERRY, + 439 => :BABIRIBERRY, + 440 => :CHILANBERRY, + 441 => :LIECHIBERRY, + 442 => :GANLONBERRY, + 443 => :SALACBERRY, + 444 => :PETAYABERRY, + 445 => :APICOTBERRY, + 446 => :LANSATBERRY, + 447 => :STARFBERRY, + 448 => :ENIGMABERRY, + 449 => :MICLEBERRY, + 450 => :CUSTAPBERRY, + 451 => :JABOCABERRY, + 452 => :ROWAPBERRY + } + mulch_conversion = { + 59 => :GROWTHMULCH, + 60 => :DAMPMULCH, + 61 => :STABLEMULCH, + 62 => :GOOEYMULCH + } + global.eventvars.each_value do |var| + next if !var || !var.is_a?(Array) + next if var.length < 6 || var.length > 8 # Neither old nor new berry plant + if !var[1].is_a?(Symbol) # Planted berry item + var[1] = berry_conversion[var[1]] || :ORANBERRY + end + if var[7] && !var[7].is_a?(Symbol) # Mulch + var[7] = mulch_conversion[var[7]] + end 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]) +SaveData.register_conversion(:v20_refactor_planted_berries_data) do + essentials_version 20 + display_title "Updating berry plant data format" + to_value :global_metadata do |global| + if global.eventvars + global.eventvars.each_pair do |key, value| + next if !value || !value.is_a?(Array) + case value.length + when 6 # Old berry plant data + data = BerryPlantData.new + if value[1].is_a?(Symbol) + plant_data = GameData::BerryPlant.get(value[1]) + data.new_mechanics = false + data.berry_id = value[1] + data.time_alive = value[0] * plant_data.hours_per_stage * 3600 + data.time_last_updated = value[3] + data.growth_stage = value[0] + data.replant_count = value[5] + data.watered_this_stage = value[2] + data.watering_count = value[4] + end + global.eventvars[key] = data + when 7, 8 # New berry plant data + data = BerryPlantData.new + if value[1].is_a?(Symbol) + data.new_mechanics = true + data.berry_id = value[1] + data.mulch_id = value[7] if value[7].is_a?(Symbol) + data.time_alive = value[2] + data.time_last_updated = value[3] + data.growth_stage = value[0] + data.replant_count = value[5] + data.moisture_level = value[4] + data.yield_penalty = value[6] + end + global.eventvars[key] = data + end + end + end 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) +#=============================================================================== + +SaveData.register_conversion(:v20_refactor_follower_data) do + essentials_version 20 + display_title "Updating follower data format" + to_value :global_metadata do |global| + # NOTE: dependentEvents is still defined in class PokemonGlobalMetadata just + # for the sake of this conversion. It will be removed in future. + if global.dependentEvents && global.dependentEvents.length > 0 + global.followers = [] + global.dependentEvents.each do |follower| + data = FollowerData.new(follower[0], follower[1], "reflection", + follower[2], follower[3], follower[4], + follower[5], follower[6], follower[7]) + data.name = follower[8] + data.common_event_id = follower[9] + global.followers.push(data) 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 + global.dependentEvents = nil end end -SaveData.register_conversion(:v19_convert_global_metadata) do - essentials_version 19 - display_title 'Adding encounter version variable to global metadata' +#=============================================================================== + +SaveData.register_conversion(:v20_refactor_day_care_variables) do + essentials_version 20 + display_title "Refactoring Day Care variables" to_value :global_metadata do |global| - 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 - global.pcItemStorage.items[i] = nil + global.instance_eval do + @day_care = DayCare.new if @day_care.nil? + if !@daycare.nil? + @daycare.each do |old_slot| + if !old_slot[0] + old_slot[0] = Pokemon.new(:MANAPHY, 50) + old_slot[1] = 4 + end + next if !old_slot[0] + @day_care.slots.each do |slot| + next if slot.filled? + slot.instance_eval do + @pokemon = old_slot[0] + @initial_level = old_slot[1] + if @pokemon && @pokemon.markings.is_a?(Integer) + markings = [] + 6.times { |i| markings[i] = ((@pokemon.markings & (1 << i)) == 0) ? 0 : 1 } + @pokemon.markings = markings + end + end + end end + @day_care.egg_generated = ((@daycareEgg.is_a?(Numeric) && @daycareEgg > 0) || @daycareEgg == true) + @day_care.step_counter = @daycareEggSteps + @daycare = nil + @daycareEgg = nil + @daycareEggSteps = nil end - 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 + +#=============================================================================== + +SaveData.register_conversion(:v20_rename_bag_variables) do + essentials_version 20 + display_title "Renaming Bag variables" + to_value :bag do |bag| + bag.instance_eval do + if !@lastpocket.nil? + @last_viewed_pocket = @lastpocket + @lastPocket = nil 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 + if !@choices.nil? + @last_pocket_selections = @choices.clone + @choices = nil 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 + if !@registeredItems.nil? + @registered_items = @registeredItems || [] + @registeredItems = 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 + if !@registeredIndex.nil? + @ready_menu_selection = @registeredIndex || [0, 0, 1] + @registeredIndex = nil end end end end -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 +#=============================================================================== + +SaveData.register_conversion(:v20_increment_player_character_id) do + essentials_version 20 + display_title "Incrementing player character ID" + to_value :player do |player| + player.character_ID += 1 end end -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 - 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 - pocket.compact! - end - 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 - 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) +SaveData.register_conversion(:v20_add_pokedex_records) do + essentials_version 20 + display_title "Adding more Pokédex records" + to_value :player do |player| + player.pokedex.instance_eval do + @caught_counts = {} if @caught_counts.nil? + @defeated_counts = {} if @defeated_counts.nil? + @seen_eggs = {} if @seen_eggs.nil? + @seen_forms.each_value do |sp| + next if !sp || sp[0][0].is_a?(Array) # Already converted to include shininess + sp[0] = [sp[0], []] + sp[1] = [sp[1], []] 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 +SaveData.register_conversion(:v20_add_new_default_options) do + essentials_version 20 + display_title "Updating Options to include new settings" + to_value :pokemon_system do |option| + option.givenicknames = 0 if option.givenicknames.nil? + option.sendtoboxes = 0 if option.sendtoboxes.nil? end end -SaveData.register_conversion(:v19_convert_game_screen) do - essentials_version 19 - display_title 'Converting game screen' +#=============================================================================== + +SaveData.register_conversion(:v20_fix_default_weather_type) do + essentials_version 20 + display_title "Fixing weather type 0 in effect" to_value :game_screen do |game_screen| - game_screen.weather(game_screen.weather_type, game_screen.weather_max, 0) + game_screen.instance_eval do + @weather_type = :None if @weather_type == 0 + end + end +end + +#=============================================================================== + +SaveData.register_conversion(:v20_add_stats) do + essentials_version 20 + display_title "Adding stats to save data" + to_all do |save_data| + unless save_data.has_key?(:stats) + save_data[:stats] = GameStats.new + save_data[:stats].play_time = save_data[:frame_count].to_f / Graphics.frame_rate + save_data[:stats].play_sessions = 1 + save_data[:stats].time_last_saved = save_data[:stats].play_time + end + end +end + +#=============================================================================== + +SaveData.register_conversion(:v20_convert_pokemon_markings) do + essentials_version 20 + display_title "Updating format of Pokémon markings" + to_all do |save_data| + # Create a lambda function that updates a Pokémon's markings + update_markings = lambda do |pkmn| + return if !pkmn || !pkmn.markings.is_a?(Integer) + markings = [] + 6.times { |i| markings[i] = ((pkmn.markings & (1 << i)) == 0) ? 0 : 1 } + pkmn.markings = markings + end + # Party Pokémon + save_data[:player].party.each { |pkmn| update_markings.call(pkmn) } + # Pokémon storage + save_data[:storage_system].boxes.each do |box| + box.pokemon.each { |pkmn| update_markings.call(pkmn) if pkmn } + end + # NOTE: Pokémon in the Day Care have their markings converted above. + # Partner trainer + if save_data[:global_metadata].partner + save_data[:global_metadata].partner[3].each { |pkmn| update_markings.call(pkmn) } + end + # Roaming Pokémon + if save_data[:global_metadata].roamPokemon + save_data[:global_metadata].roamPokemon.each { |pkmn| update_markings.call(pkmn) } + end + # Purify Chamber + save_data[:global_metadata].purifyChamber.sets.each do |set| + set.list.each { |pkmn| update_markings.call(pkmn) } + update_markings.call(set.shadow) if set.shadow + end + # Hall of Fame records + if save_data[:global_metadata].hallOfFame + save_data[:global_metadata].hallOfFame.each do |team| + next if !team + team.each { |pkmn| update_markings.call(pkmn) } + end + end + # Pokémon stored in Game Variables for some reason + variables = save_data[:variables] + (0..5000).each do |i| + value = variables[i] + case value + when Array + value.each { |value2| update_markings.call(value2) if value2.is_a?(Pokemon) } + when Pokemon + update_markings.call(value) + end + end end end diff --git a/Data/Scripts/003_Game processing/001_StartGame.rb b/Data/Scripts/003_Game processing/001_StartGame.rb index f4215ec880..b803a2e941 100644 --- a/Data/Scripts/003_Game processing/001_StartGame.rb +++ b/Data/Scripts/003_Game processing/001_StartGame.rb @@ -2,18 +2,17 @@ module Game # Initializes various global variables and loads the game data. 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 = format('Data/Map%03d.rxdata', $data_system.start_map_id) + map_file = sprintf("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 @@ -32,21 +31,22 @@ def self.set_up_system # Set language (and choose language if there is no save file) if Settings::LANGUAGES.length >= 2 $PokemonSystem.language = pbChooseLanguage if save_data.empty? - pbLoadMessages('Data/' + Settings::LANGUAGES[$PokemonSystem.language][1]) + 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 self.start_new - if $game_map && $game_map.events + if $game_map&.events $game_map.events.each_value { |event| event.clear_starting } end $game_temp.common_event_id = 0 if $game_temp - $PokemonTemp.begunNewGame = true + $game_temp.begun_new_game = true $scene = Scene_Map.new SaveData.load_new_game_values - $MapFactory = PokemonMapFactory.new($data_system.start_map_id) + $stats.play_sessions += 1 + $map_factory = PokemonMapFactory.new($data_system.start_map_id) $game_player.moveto($data_system.start_x, $data_system.start_y) $game_player.refresh $PokemonEncounters = PokemonEncounters.new @@ -61,6 +61,7 @@ def self.start_new def self.load(save_data) validate save_data => Hash SaveData.load_all_values(save_data) + $stats.play_sessions += 1 self.load_map pbAutoplayOnSave $game_map.update @@ -70,31 +71,31 @@ def self.load(save_data) # Loads and validates the map. Called when loading a saved game. def self.load_map - $game_map = $MapFactory.map + $game_map = $map_factory.map magic_number_matches = ($game_system.magic_number == $data_system.magic_number) if !magic_number_matches || $PokemonGlobal.safesave if pbMapInterpreterRunning? pbMapInterpreter.setup(nil, 0) end begin - $MapFactory.setup($game_map.map_id) + $map_factory.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 - $MapFactory.setup(map[0]) + $map_factory.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 - $MapFactory.setMapChanged($game_map.map_id) + $map_factory.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) @@ -111,6 +112,7 @@ def self.save(save_file = SaveData::FILE_PATH, safe: false) $PokemonGlobal.safesave = safe $game_system.save_count += 1 $game_system.magic_number = $data_system.magic_number + $stats.set_time_last_saved begin SaveData.save_to_file(save_file) Graphics.frame_reset diff --git a/Data/Scripts/003_Game processing/002_Scene_Map.rb b/Data/Scripts/003_Game processing/002_Scene_Map.rb index 8b5996f2a4..2ad14c8407 100644 --- a/Data/Scripts/003_Game processing/002_Scene_Map.rb +++ b/Data/Scripts/003_Game processing/002_Scene_Map.rb @@ -5,35 +5,38 @@ #=============================================================================== class Scene_Map attr_reader :spritesetGlobal + attr_reader :map_renderer - def spriteset - for i in @spritesets.values - return i if i.map==$game_map + 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 end return @spritesets.values[0] end def createSpritesets + @map_renderer = TilemapRenderer.new(Spriteset_Map.viewport) @spritesetGlobal = Spriteset_Global.new @spritesets = {} - for map in $MapFactory.maps + $map_factory.maps.each do |map| @spritesets[map.map_id] = Spriteset_Map.new(map) end - $MapFactory.setSceneStarted(self) + $map_factory.setSceneStarted(self) updateSpritesets end def createSingleSpriteset(map) temp = $scene.spriteset.getAnimations - @spritesets[map] = Spriteset_Map.new($MapFactory.maps[map]) + @spritesets[map] = Spriteset_Map.new($map_factory.maps[map]) $scene.spriteset.restoreAnimations(temp) - $MapFactory.setSceneStarted(self) + $map_factory.setSceneStarted(self) updateSpritesets end def disposeSpritesets return if !@spritesets - for i in @spritesets.keys + @spritesets.each_key do |i| next if !@spritesets[i] @spritesets[i].dispose @spritesets[i] = nil @@ -42,6 +45,8 @@ def disposeSpritesets @spritesets = {} @spritesetGlobal.dispose @spritesetGlobal = nil + @map_renderer.dispose + @map_renderer = nil end def autofade(mapid) @@ -50,26 +55,25 @@ def autofade(mapid) return if !playingBGM && !playingBGS map = load_data(sprintf("Data/Map%03d.rxdata", mapid)) if playingBGM && map.autoplay_bgm - if (PBDayNight.isNight? rescue false) - pbBGMFade(0.8) if playingBGM.name!=map.bgm.name && playingBGM.name!=map.bgm.name+"_n" - else - pbBGMFade(0.8) if playingBGM.name!=map.bgm.name + if (PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/" + map.bgm.name + "_n") && + playingBGM.name != map.bgm.name + "_n") || playingBGM.name != map.bgm.name + pbBGMFade(0.8) end end - if playingBGS && map.autoplay_bgs - pbBGMFade(0.8) if playingBGS.name!=map.bgs.name + if playingBGS && map.autoplay_bgs && playingBGS.name != map.bgs.name + pbBGMFade(0.8) end Graphics.frame_reset end - def transfer_player(cancelVehicles=true) + def transfer_player(cancel_swimming = true) $game_temp.player_transferring = false - pbCancelVehicles($game_temp.player_new_map_id) if cancelVehicles + pbCancelVehicles($game_temp.player_new_map_id, cancel_swimming) autofade($game_temp.player_new_map_id) pbBridgeOff @spritesetGlobal.playersprite.clearShadows - if $game_map.map_id!=$game_temp.player_new_map_id - $MapFactory.setup($game_temp.player_new_map_id) + if $game_map.map_id != $game_temp.player_new_map_id + $map_factory.setup($game_temp.player_new_map_id) end $game_player.moveto($game_temp.player_new_x, $game_temp.player_new_y) case $game_temp.player_new_direction @@ -79,13 +83,14 @@ def transfer_player(cancelVehicles=true) 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(20) + Graphics.transition end $game_map.autoplay Graphics.frame_reset @@ -111,60 +116,62 @@ def call_debug end def miniupdate - $PokemonTemp.miniupdate = true + $game_temp.in_mini_update = true loop do - updateMaps $game_player.update + updateMaps $game_system.update $game_screen.update break unless $game_temp.player_transferring - transfer_player + transfer_player(false) break if $game_temp.transition_processing end updateSpritesets - $PokemonTemp.miniupdate = false + $game_temp.in_mini_update = false end def updateMaps - for map in $MapFactory.maps + $map_factory.maps.each do |map| map.update end - $MapFactory.updateMaps(self) + $map_factory.updateMaps(self) end 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 - for i in keys - if !$MapFactory.hasMap?(i) - @spritesets[i].dispose if @spritesets[i] + keys.each do |i| + if $map_factory.hasMap?(i) + @spritesets[i].update + else + @spritesets[i]&.dispose @spritesets[i] = nil @spritesets.delete(i) - else - @spritesets[i].update end end @spritesetGlobal.update - for map in $MapFactory.maps - @spritesets[map.map_id] = Spriteset_Map.new(map) if !@spritesets[map.map_id] - end - Events.onMapUpdate.trigger(self) + pbDayNightTint(@map_renderer) + @map_renderer.update + EventHandlers.trigger(:on_frame_update) end def update loop do - updateMaps pbMapInterpreter.update $game_player.update + updateMaps $game_system.update $game_screen.update break unless $game_temp.player_transferring - transfer_player + transfer_player(false) break if $game_temp.transition_processing end updateSpritesets - if $game_temp.to_title - $game_temp.to_title = false + if $game_temp.title_screen_calling + $game_temp.title_screen_calling = false SaveData.mark_values_as_unloaded $scene = pbCallTitle return @@ -172,7 +179,7 @@ def update if $game_temp.transition_processing $game_temp.transition_processing = false if $game_temp.transition_name == "" - Graphics.transition(20) + Graphics.transition else Graphics.transition(40, "Graphics/Transitions/" + $game_temp.transition_name) end @@ -180,15 +187,15 @@ def update return if $game_temp.message_window_showing if !pbMapInterpreterRunning? if Input.trigger?(Input::USE) - $PokemonTemp.hiddenMoveEventCalling = true - elsif Input.trigger?(Input::BACK) + $game_temp.interact_calling = true + elsif Input.trigger?(Input::ACTION) unless $game_system.menu_disabled || $game_player.moving? $game_temp.menu_calling = true $game_temp.menu_beep = true end elsif Input.trigger?(Input::SPECIAL) unless $game_player.moving? - $PokemonTemp.keyItemCalling = true + $game_temp.ready_menu_calling = true end elsif Input.press?(Input::F9) $game_temp.debug_calling = true if $DEBUG @@ -199,21 +206,21 @@ def update call_menu elsif $game_temp.debug_calling call_debug - elsif $PokemonTemp.keyItemCalling - $PokemonTemp.keyItemCalling = false + elsif $game_temp.ready_menu_calling + $game_temp.ready_menu_calling = false $game_player.straighten pbUseKeyItem - elsif $PokemonTemp.hiddenMoveEventCalling - $PokemonTemp.hiddenMoveEventCalling = false + elsif $game_temp.interact_calling + $game_temp.interact_calling = false $game_player.straighten - Events.onAction.trigger(self) + EventHandlers.trigger(:on_player_interact) end end end def main createSpritesets - Graphics.transition(20) + Graphics.transition loop do Graphics.update Input.update @@ -222,8 +229,8 @@ def main end Graphics.freeze disposeSpritesets - if $game_temp.to_title - Graphics.transition(20) + if $game_temp.title_screen_calling + Graphics.transition Graphics.freeze end end diff --git a/Data/Scripts/003_Game processing/003_Interpreter.rb b/Data/Scripts/003_Game processing/003_Interpreter.rb index 547428d2f5..29869dcf3a 100644 --- a/Data/Scripts/003_Game processing/003_Interpreter.rb +++ b/Data/Scripts/003_Game processing/003_Interpreter.rb @@ -22,7 +22,7 @@ def initialize(depth = 0, main = false) def inspect str = super.chop - str << format(' @event_id: %d>', @event_id) + str << sprintf(" @event_id: %d>", @event_id) return str end @@ -35,6 +35,9 @@ def clear @child_interpreter = nil # child interpreter @branch = {} # branch data @buttonInput = false + @hidden_choices = [] + @renamed_choices = [] + end_follower_overrides end #----------------------------------------------------------------------------- # * Event Setup @@ -59,7 +62,7 @@ def setup_starting_event return end # Check all map events for one that wants to start, and set it up - for event in $game_map.events.values + $game_map.events.each_value do |event| next if !event.starting if event.trigger < 3 # Isn't autorun or parallel processing event.lock @@ -69,7 +72,7 @@ def setup_starting_event return end # Check all common events for one that is autorun, and set it up - for common_event in $data_common_events.compact + $data_common_events.compact.each do |common_event| next if common_event.trigger != 1 || !$game_switches[common_event.switch_id] setup(common_event.list, 0) return @@ -77,7 +80,7 @@ def setup_starting_event end def running? - return @list != nil + return !@list.nil? end #----------------------------------------------------------------------------- # * Frame Update @@ -92,7 +95,7 @@ def update 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 && !$MapFactory.areConnected?($game_map.map_id, @map_id) + if $game_map.map_id != @map_id && !$map_factory.areConnected?($game_map.map_id, @map_id) @event_id = 0 end # Update child interpreter if one exists @@ -106,7 +109,10 @@ def update # 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 - for event in $game_map.events.values + $game_map.events.each_value do |event| + return if event.move_route_forcing + end + $game_temp.followers.each_follower do |event, follower| return if event.move_route_forcing end @move_route_waiting = false @@ -138,55 +144,42 @@ def execute_script(script) return result rescue Exception e = $! - raise if e.is_a?(SystemExit) || "#{e.class}" == "Reset" + raise if e.is_a?(SystemExit) || e.class.to_s == "Reset" event = get_self - s = "Backtrace:\r\n" + # Gather text for error message message = pbGetExceptionMessage(e) + backtrace_text = "" if e.is_a?(SyntaxError) script.each_line { |line| line.gsub!(/\s+$/, "") if line[/^\s*\(/] - 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'" + 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'." end } else - for bt in e.backtrace[0, 10] - s += bt + "\r\n" - end - s.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] } + 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 - message = "Exception: #{e.class}\r\nMessage: " + message + "\r\n" - message += "\r\n***Full script:\r\n#{script}\r\n" - if event && $game_map + # Assemble error message + err = "Script error in Interpreter\r\n" + if $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 - err = "Script error in interpreter:\r\n#{message}\r\n#{s}" - if e.is_a?(Hangup) - $EVENTHANGUPMSG = err - raise + if event + err = "Script error in event #{event.id} (coords #{event.x},#{event.y}), map #{$game_map.map_id} (#{map_name})\r\n" + else + err = "Script error in Common Event, map #{$game_map.map_id} (#{map_name})\r\n" end end - raise err + 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) end end #----------------------------------------------------------------------------- @@ -221,13 +214,13 @@ def get_event(parameter) # * Freezes all events on the map (for use at the beginning of common events) #----------------------------------------------------------------------------- def pbGlobalLock - $game_map.events.values.each { |event| event.minilock } + $game_map.events.each_value { |event| event.minilock } end #----------------------------------------------------------------------------- # * Unfreezes all events on the map (for use at the end of common events) #----------------------------------------------------------------------------- def pbGlobalUnlock - $game_map.events.values.each { |event| event.unlock } + $game_map.events.each_value { |event| event.unlock } end #----------------------------------------------------------------------------- # * Gets the next index in the interpreter, ignoring certain commands between messages @@ -281,12 +274,30 @@ def pbJumpToLabel(index, label_name) 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 = number + ($game_temp.in_battle ? 50 : 0) + number += ($game_temp.in_battle ? 50 : 0) $game_screen.pictures[number].show(name, origin, x, y, zoomX, zoomY, opacity, blendType) end @@ -295,7 +306,7 @@ def pbShowPicture(number, name, origin, x, y, zoomX = 100, zoomY = 100, opacity def pbEraseThisEvent if $game_map.events[@event_id] $game_map.events[@event_id].erase - $PokemonMap.addErasedEvent(@event_id) if $PokemonMap + $PokemonMap&.addErasedEvent(@event_id) end @index += 1 return true @@ -325,8 +336,8 @@ def pbSetSelfSwitch(eventid, switch_name, value, mapid = -1) 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 && $MapFactory.hasMap?(mapid) - $MapFactory.getMap(mapid, false).need_refresh = true + if value != old_value && $map_factory.hasMap?(mapid) + $map_factory.getMap(mapid, false).need_refresh = true end end @@ -368,7 +379,7 @@ def setVariable(*arg) end def pbGetPokemon(id) - return $Trainer.party[pbGet(id)] + return $player.party[pbGet(id)] end def pbSetEventTime(*arg) @@ -377,7 +388,7 @@ def pbSetEventTime(*arg) time = time.to_i pbSetSelfSwitch(@event_id, "A", true) $PokemonGlobal.eventvars[[@map_id, @event_id]] = time - for otherevt in arg + arg.each do |otherevt| pbSetSelfSwitch(otherevt, "A", true) $PokemonGlobal.eventvars[[@map_id, otherevt]] = time end @@ -391,13 +402,14 @@ def pbPushThisEvent # 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) if $PokemonMap + $PokemonMap&.addMovedEvent(@event_id) if old_x != event.x || old_y != event.y $game_player.lock loop do @@ -426,14 +438,14 @@ def pbTrainerIntro(symbol) return true if $DEBUG && !GameData::TrainerType.exists?(symbol) tr_type = GameData::TrainerType.get(symbol).id pbGlobalLock - pbPlayTrainerIntroME(tr_type) + pbPlayTrainerIntroBGM(tr_type) return true end def pbTrainerEnd pbGlobalUnlock event = get_self - event.erase_route if event + event&.erase_route end def setPrice(item, buy_price = -1, sell_price = -1) @@ -442,8 +454,8 @@ def setPrice(item, buy_price = -1, sell_price = -1) $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 * 2 - else - $game_temp.mart_prices[item][1] = buy_price if buy_price > 0 + elsif buy_price > 0 + $game_temp.mart_prices[item][1] = buy_price end end diff --git a/Data/Scripts/003_Game processing/004_Interpreter_Commands.rb b/Data/Scripts/003_Game processing/004_Interpreter_Commands.rb index 710fbc8c68..2d573754d6 100644 --- a/Data/Scripts/003_Game processing/004_Interpreter_Commands.rb +++ b/Data/Scripts/003_Game processing/004_Interpreter_Commands.rb @@ -126,6 +126,7 @@ def command_dummy #----------------------------------------------------------------------------- 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 @@ -158,7 +159,7 @@ def command_101 return false if $game_temp.message_window_showing message = @list[@index].parameters[0] message_end = "" - commands = nil + choices = nil number_input_variable = nil number_input_max_digits = nil # Check the next command(s) for things to add on to this text @@ -174,8 +175,8 @@ def command_101 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] @@ -185,15 +186,11 @@ def command_101 end # Translate the text message = _MAPINTL($game_map.map_id, message) - # Display the text, with commands/number choosing if appropriate + # Display the text, with choices/number choosing if appropriate @message_waiting = true # Lets parallel process events work while a message is displayed - 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 + if choices + command = pbMessage(message + message_end, choices[0], choices[1]) + @branch[@list[@index].indent] = choices[2][command] || command elsif number_input_variable params = ChooseNumberParams.new params.setMaxDigits(number_input_max_digits) @@ -210,17 +207,93 @@ def command_101 # * Show Choices #----------------------------------------------------------------------------- def command_102 + choices = setup_choices(@list[@index].parameters) @message_waiting = true - command = pbShowCommands(nil, @list[@index].parameters[0], @list[@index].parameters[1]) + command = pbShowCommands(nil, choices[0], choices[1]) @message_waiting = false - @branch[@list[@index].indent] = command + @branch[@list[@index].indent] = choices[2][command] || 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 @@ -231,7 +304,8 @@ def command_402 # * When Cancel #----------------------------------------------------------------------------- def command_403 - if @branch[@list[@index].indent] == 4 + # @parameters[0] is 4 for "Branch" + if @branch[@list[@index].indent] == @choice_branch_index @branch.delete(@list[@index].indent) return true end @@ -271,7 +345,7 @@ def pbButtonInputProcessing(variable_number = 0, timeout_frames = 0) Input.update pbUpdateSceneMap # Check for input and break if there is one - for i in 1..18 + (1..18).each do |i| ret = i if Input.trigger?(i) end break if ret != 0 @@ -343,7 +417,7 @@ def command_111 character = get_character(@parameters[1]) result = (character.direction == @parameters[2]) if character when 7 # gold - gold = $Trainer.money + gold = $player.money result = (@parameters[2] == 0) ? (gold >= @parameters[1]) : (gold <= @parameters[1]) # when 8, 9, 10 # item, weapon, armor when 11 # button @@ -413,8 +487,8 @@ def command_115 #----------------------------------------------------------------------------- def command_116 if @event_id > 0 - $game_map.events[@event_id].erase if $game_map.events[@event_id] - $PokemonMap.addErasedEvent(@event_id) if $PokemonMap + $game_map.events[@event_id]&.erase + $PokemonMap&.addErasedEvent(@event_id) end @index += 1 return false @@ -459,7 +533,7 @@ def command_119 #----------------------------------------------------------------------------- def command_121 should_refresh = false - for i in @parameters[0]..@parameters[1] + (@parameters[0]..@parameters[1]).each do |i| next if $game_switches[i] == (@parameters[2] == 0) $game_switches[i] = (@parameters[2] == 0) should_refresh = true @@ -496,8 +570,8 @@ def command_122 when 7 # other case @parameters[4] 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 1 then value = $player.pokemon_party.length # party members + when 2 then value = $player.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 @@ -505,29 +579,29 @@ def command_122 end end # Apply value and operation to all specified game variables - for i in @parameters[0]..@parameters[1] + (@parameters[0]..@parameters[1]).each do |i| case @parameters[2] when 0 # set next if $game_variables[i] == value $game_variables[i] = value when 1 # add - next if $game_variables[i] >= 99999999 + next if $game_variables[i] >= 99_999_999 $game_variables[i] += value when 2 # subtract - next if $game_variables[i] <= -99999999 + next if $game_variables[i] <= -99_999_999 $game_variables[i] -= value when 3 # multiply next if value == 1 $game_variables[i] *= value when 4 # divide - next if value == 1 || value == 0 + next if [0, 1].include?(value) $game_variables[i] /= value when 5 # remainder - next if value == 1 || value == 0 + next if [0, 1].include?(value) $game_variables[i] %= value end - $game_variables[i] = 99999999 if $game_variables[i] > 99999999 - $game_variables[i] = -99999999 if $game_variables[i] < -99999999 + $game_variables[i] = 99_999_999 if $game_variables[i] > 99_999_999 + $game_variables[i] = -99_999_999 if $game_variables[i] < -99_999_999 $game_map.need_refresh = true end return true @@ -560,7 +634,7 @@ def command_124 def command_125 value = (@parameters[1] == 0) ? @parameters[2] : $game_variables[@parameters[2]] value = -value if @parameters[0] == 1 # Decrease - $Trainer.money += value + $player.money += value return true end @@ -572,7 +646,7 @@ def command_129; command_dummy; end # Change Party Member # * Change Windowskin #----------------------------------------------------------------------------- def command_131 - for i in 0...Settings::SPEECH_WINDOWSKINS.length + Settings::SPEECH_WINDOWSKINS.length.times do |i| next if Settings::SPEECH_WINDOWSKINS[i] != @parameters[0] $PokemonSystem.textskin = i MessageConfig.pbSetSpeechFrame("Graphics/Windowskins/" + Settings::SPEECH_WINDOWSKINS[i]) @@ -590,10 +664,7 @@ def command_132 #----------------------------------------------------------------------------- # * Change Battle End ME #----------------------------------------------------------------------------- - def command_133 - ($PokemonGlobal.nextBattleME = @parameters[0]) ? @parameters[0].clone : nil - return true - end + def command_133; command_dummy; end #----------------------------------------------------------------------------- # * Change Save Access #----------------------------------------------------------------------------- @@ -630,13 +701,12 @@ def command_201 $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 @@ -654,9 +724,10 @@ def command_202 character = get_character(@parameters[0]) return true if character.nil? # Move the character - if @parameters[1] == 0 # Direct appointment + case @parameters[1] + when 0 # Direct appointment character.moveto(@parameters[2], @parameters[3]) - elsif @parameters[1] == 1 # Appoint with variables + when 1 # Appoint with variables character.moveto($game_variables[@parameters[2]], $game_variables[@parameters[3]]) else # Exchange with another event character2 = get_character(@parameters[2]) @@ -726,6 +797,11 @@ def command_206 #----------------------------------------------------------------------------- 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 @@ -742,6 +818,11 @@ def command_208 #----------------------------------------------------------------------------- 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 @@ -805,7 +886,7 @@ def command_231 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 #----------------------------------------------------------------------------- @@ -821,7 +902,7 @@ def command_232 y = $game_variables[@parameters[5]] end $game_screen.pictures[number].move(@parameters[1] * Graphics.frame_rate / 20, - @parameters[2], x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9]) + @parameters[2], x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9]) return true end #----------------------------------------------------------------------------- @@ -838,7 +919,7 @@ def command_233 def command_234 number = @parameters[0] + ($game_temp.in_battle ? 50 : 0) $game_screen.pictures[number].start_tone_change(@parameters[1], - @parameters[2] * Graphics.frame_rate / 20) + @parameters[2] * Graphics.frame_rate / 20) return true end #----------------------------------------------------------------------------- @@ -931,18 +1012,19 @@ def command_302; command_dummy; end # Shop Processing # * Name Input Processing #----------------------------------------------------------------------------- def command_303 - if $Trainer - $Trainer.name = pbEnterPlayerName(_INTL("Your name?"), 1, @parameters[1], $Trainer.name) + if $player + $player.name = pbEnterPlayerName(_INTL("Your name?"), 1, @parameters[1], $player.name) return true end - if $game_actors && $data_actors && $data_actors[@parameters[0]] != nil + if $game_actors && $data_actors && $data_actors[@parameters[0]] $game_temp.battle_abort = true 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) + _INTL("Enter {1}'s name.", $game_actors[@parameters[0]].name), + 1, @parameters[1], $game_actors[@parameters[0]].name + ) } end return true @@ -955,7 +1037,13 @@ def command_313; command_dummy; end # Change State # * Recover All #----------------------------------------------------------------------------- def command_314 - $Trainer.heal_party if @parameters[0] == 0 + if @parameters[0] == 0 + if Settings::HEAL_STORED_POKEMON # No need to heal stored Pokémon + $player.heal_party + else + pbEachPokemon { |pkmn, box| pkmn.heal } # Includes party Pokémon + end + end return true end @@ -1006,7 +1094,7 @@ def command_353 # * Return to Title Screen #----------------------------------------------------------------------------- def command_354 - $game_temp.to_title = true + $game_temp.title_screen_calling = true return false end #----------------------------------------------------------------------------- @@ -1017,7 +1105,7 @@ def command_355 # 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 diff --git a/Data/Scripts/003_Game processing/005_Event_Handlers.rb b/Data/Scripts/003_Game processing/005_Event_Handlers.rb index 58129853f2..fb679a4c4f 100644 --- a/Data/Scripts/003_Game processing/005_Event_Handlers.rb +++ b/Data/Scripts/003_Game processing/005_Event_Handlers.rb @@ -13,21 +13,15 @@ def set(method) end # Removes an event handler procedure from the event. - def -(method) - for i in 0...@callbacks.length - next if @callbacks[i]!=method - @callbacks.delete_at(i) - break - end + def -(other) + @callbacks.delete(other) return self end # Adds an event handler procedure from the event. - def +(method) - for i in 0...@callbacks.length - return self if @callbacks[i]==method - end - @callbacks.push(method) + def +(other) + return self if @callbacks.include?(other) + @callbacks.push(other) return self end @@ -44,13 +38,13 @@ def clear # proc { |sender,params| } where params is an array of the other parameters, and # proc { |sender,arg0,arg1,...| } def trigger(*arg) - arglist = arg[1,arg.length] - for callback in @callbacks - if callback.arity>2 && arg.length==callback.arity + arglist = arg[1, arg.length] + @callbacks.each do |callback| + 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 @@ -59,14 +53,45 @@ def trigger(*arg) # by the code where the event occurred. The first argument is the sender of # the event, the other arguments are the event's parameters. def trigger2(*arg) - for callback in @callbacks + @callbacks.each do |callback| 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 if !@callbacks.has_key?(key) + 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 + +#=============================================================================== +# Unused. #=============================================================================== class HandlerHash def initialize(mod) @@ -89,8 +114,8 @@ def toSymbol(sym) return ret if ret mod = Object.const_get(@mod) rescue nil return nil if !mod - for key in mod.constants - next if mod.const_get(key)!=sym + mod.constants.each do |key| + next if mod.const_get(key) != sym ret = key.to_sym @symbolCache[sym] = ret break @@ -98,15 +123,15 @@ def toSymbol(sym) return ret end - def addIf(conditionProc,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]) + @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? + 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) @@ -115,11 +140,11 @@ def add(sym,handler=nil,&handlerBlock) # 'sym' can be an ID or symbol @hash[symbol] = handler || handlerBlock if symbol end - def copy(src,*dests) + def copy(src, *dests) handler = self[src] if handler - for dest in dests - self.add(dest,handler) + dests.each do |dest| + self.add(dest, handler) end end end @@ -131,16 +156,16 @@ def [](sym) # 'sym' can be an ID or symbol symbol = toSymbol(sym) ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string unless ret - for addif in @addIfs + @addIfs.each do |addif| return addif[1] if addif[0].call(id) end end return ret end - def trigger(sym,*args) + def trigger(sym, *args) handler = self[sym] - return (handler) ? handler.call(fromSymbol(sym),*args) : nil + return (handler) ? handler.call(fromSymbol(sym), *args) : nil end def clear @@ -150,7 +175,8 @@ def clear #=============================================================================== # 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. +# doesn't care about whether those symbols are defined as constants in a class +# or module. #=============================================================================== class HandlerHash2 def initialize @@ -161,32 +187,34 @@ def initialize 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 + @add_ifs.each do |add_if| return add_if[1] if add_if[0].call(sym) end return nil end - def addIf(conditionProc, handler = nil, &handlerBlock) + def add(sym, 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)" + raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)" end - @add_ifs.push([conditionProc, handler || handlerBlock]) + @hash[sym] = handler || handlerBlock if sym end - def add(sym, handler = nil, &handlerBlock) + def addIf(conditionProc, 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)" + raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)" end - @hash[sym] = handler || handlerBlock if sym + @add_ifs.push([conditionProc, handler || handlerBlock]) end def copy(src, *dests) handler = self[src] return if !handler - for dest in dests - self.add(dest, handler) - end + dests.each { |dest| add(dest, handler) } + end + + def remove(key) + @hash.delete(key) end def clear @@ -196,7 +224,7 @@ def clear 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 + return handler&.call(sym, *args) end end @@ -206,32 +234,26 @@ def trigger(sym, *args) #=============================================================================== class HandlerHashBasic def initialize - @ordered_keys = [] - @hash = {} - @addIfs = [] + @hash = {} + @addIfs = [] end def [](entry) ret = nil ret = @hash[entry] if entry && @hash[entry] unless ret - for addif in @addIfs + @addIfs.each do |addif| 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? + 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 @@ -245,17 +267,28 @@ def addIf(conditionProc, handler = nil, &handlerBlock) def copy(src, *dests) handler = self[src] return if !handler - dests.each { |dest| self.add(dest, handler) } + dests.each { |dest| add(dest, handler) } + end + + def remove(key) + @hash.delete(key) end def clear @hash.clear - @ordered_keys.clear + end + + def each + @hash.each_pair { |key, value| yield key, value } + end + + def keys + return @hash.keys.clone end def trigger(entry, *args) handler = self[entry] - return (handler) ? handler.call(*args) : nil + return handler&.call(*args) end end diff --git a/Data/Scripts/003_Game processing/006_Event_HandlerCollections.rb b/Data/Scripts/003_Game processing/006_Event_HandlerCollections.rb new file mode 100644 index 0000000000..daf628d39a --- /dev/null +++ b/Data/Scripts/003_Game processing/006_Event_HandlerCollections.rb @@ -0,0 +1,123 @@ +#=============================================================================== +# 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 = {} + + # Add a named callback for the given event. + def self.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 self.remove(event, key) + @@events[event]&.remove(key) + end + + # Clear all callbacks for the given event. + def self.clear(key) + @@events[key]&.clear + end + + # Trigger all callbacks from an Event if it has been defined. + def self.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 = {} + + def self.add(menu, option, hash) + @@handlers[menu] = HandlerHashBasic.new if !@@handlers.has_key?(menu) + @@handlers[menu].add(option, hash) + end + + def self.remove(menu, option) + @@handlers[menu]&.remove(option) + end + + def self.clear(menu) + @@handlers[menu]&.clear + end + + def self.each(menu) + return if !@@handlers.has_key?(menu) + @@handlers[menu].each { |option, hash| yield option, hash } + end + + def self.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["name"].is_a?(Proc) + name = hash["name"].call + else + name = _INTL(hash["name"]) + end + yield option, hash, name + end + end + + def self.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 diff --git a/Data/Scripts/003_Game processing/006_Event_OverworldEvents.rb b/Data/Scripts/003_Game processing/006_Event_OverworldEvents.rb deleted file mode 100644 index b83ea0d6f2..0000000000 --- a/Data/Scripts/003_Game processing/006_Event_OverworldEvents.rb +++ /dev/null @@ -1,172 +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. -#=============================================================================== -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 diff --git a/Data/Scripts/004_Game classes/001_Game_Screen.rb b/Data/Scripts/004_Game classes/001_Game_Screen.rb index 417cc2fdbe..62d7c69b3d 100644 --- a/Data/Scripts/004_Game classes/001_Game_Screen.rb +++ b/Data/Scripts/004_Game classes/001_Game_Screen.rb @@ -17,6 +17,7 @@ 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 #----------------------------------------------------------------------------- @@ -35,10 +36,10 @@ def initialize @shake_direction = 1 @shake = 0 @pictures = [nil] - for i in 1..100 + (1..100).each do |i| @pictures.push(Game_Picture.new(i)) end - @weather_type = 0 + @weather_type = :None @weather_max = 0.0 @weather_duration = 0 end @@ -89,46 +90,46 @@ def weather(type, power, duration) # * Frame Update #----------------------------------------------------------------------------- def update - if @fadeout_duration && @fadeout_duration>=1 + if @fadeout_duration && @fadeout_duration >= 1 d = @fadeout_duration - @brightness = (@brightness*(d-1))/d + @brightness = (@brightness * (d - 1)) / d @fadeout_duration -= 1 end - if @fadein_duration && @fadein_duration>=1 + if @fadein_duration && @fadein_duration >= 1 d = @fadein_duration - @brightness = (@brightness*(d-1)+255)/d + @brightness = ((@brightness * (d - 1)) + 255) / d @fadein_duration -= 1 end - if @tone_duration>=1 + 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.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 + if @flash_duration >= 1 d = @flash_duration - @flash_color.alpha = @flash_color.alpha*(d-1)/d + @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 + 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 += 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 + @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 - for i in 51..100 + (51..100).each do |i| @pictures[i].update end else - for i in 1..50 + (1..50).each do |i| @pictures[i].update end end @@ -138,17 +139,17 @@ def update #=============================================================================== # #=============================================================================== -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 +def pbToneChangeAll(tone, duration) + $game_screen.start_tone_change(tone, duration * Graphics.frame_rate / 20) + $game_screen.pictures.each do |picture| + picture&.start_tone_change(tone, duration * Graphics.frame_rate / 20) end end -def pbShake(power,speed,frames) - $game_screen.start_shake(power,speed,frames*Graphics.frame_rate/20) +def pbShake(power, speed, frames) + $game_screen.start_shake(power, speed, frames * Graphics.frame_rate / 20) end -def pbFlash(color,frames) - $game_screen.start_flash(color,frames*Graphics.frame_rate/20) +def pbFlash(color, frames) + $game_screen.start_flash(color, frames * Graphics.frame_rate / 20) end diff --git a/Data/Scripts/004_Game classes/001_Switches and Variables/001_Game_Temp.rb b/Data/Scripts/004_Game classes/001_Switches and Variables/001_Game_Temp.rb index 981a546382..5699773853 100644 --- a/Data/Scripts/004_Game classes/001_Switches and Variables/001_Game_Temp.rb +++ b/Data/Scripts/004_Game classes/001_Switches and Variables/001_Game_Temp.rb @@ -5,51 +5,86 @@ # Refer to "$game_temp" for the instance of this class. #=============================================================================== class Game_Temp - attr_accessor :message_window_showing # message window showing - attr_accessor :common_event_id # common event ID - attr_accessor :in_battle # in-battle flag - attr_accessor :battle_abort # battle flag: interrupt - attr_accessor :battleback_name # battleback file name - attr_accessor :in_menu # menu is open - attr_accessor :menu_beep # menu: play sound effect flag + # 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 + # 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 :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 + # Player transfers 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 :to_title # return to title screen flag - attr_accessor :fadestate # for sprite hashes attr_accessor :background_bitmap + attr_accessor :fadestate # for sprite hashes + # Other + attr_accessor :begun_new_game # new game flag (true fron new game until saving) + attr_accessor :menu_beep # menu: play sound effect flag + attr_accessor :menu_last_choice # pause menu: index of last selection + attr_accessor :memorized_bgm # set when trainer intro BGM is played + attr_accessor :memorized_bgm_position # set when trainer intro BGM is played + attr_accessor :darkness_sprite # DarknessSprite or nil attr_accessor :mart_prices + #----------------------------------------------------------------------------- # * Object Initialization #----------------------------------------------------------------------------- def initialize - @message_window_showing = false - @common_event_id = 0 - @in_battle = false - @battle_abort = false - @battleback_name = '' - @in_menu = false - @menu_beep = false + # 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 + # Player transfers @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 - @background_bitmap = nil - @message_window_showing = false - @transition_processing = false + # Other + @begun_new_game = false + @menu_beep = false + @memorized_bgm = nil + @memorized_bgm_position = 0 + @menu_last_choice = 0 @mart_prices = {} end diff --git a/Data/Scripts/004_Game classes/001_Switches and Variables/002_Game_Switches.rb b/Data/Scripts/004_Game classes/001_Switches and Variables/002_Game_Switches.rb index 5dc91702fc..7f09ba2d20 100644 --- a/Data/Scripts/004_Game classes/001_Switches and Variables/002_Game_Switches.rb +++ b/Data/Scripts/004_Game classes/001_Switches and Variables/002_Game_Switches.rb @@ -16,7 +16,7 @@ def initialize # switch_id : switch ID #----------------------------------------------------------------------------- def [](switch_id) - return @data[switch_id] if switch_id <= 5000 && @data[switch_id] != nil + return @data[switch_id] if switch_id <= 5000 && @data[switch_id] return false end #----------------------------------------------------------------------------- diff --git a/Data/Scripts/004_Game classes/001_Switches and Variables/004_Game_SelfSwitches.rb b/Data/Scripts/004_Game classes/001_Switches and Variables/004_Game_SelfSwitches.rb index 528e4b1c09..f5a3ccdece 100644 --- a/Data/Scripts/004_Game classes/001_Switches and Variables/004_Game_SelfSwitches.rb +++ b/Data/Scripts/004_Game classes/001_Switches and Variables/004_Game_SelfSwitches.rb @@ -16,7 +16,7 @@ def initialize # key : key #----------------------------------------------------------------------------- def [](key) - return (@data[key]==true) ? true : false + return @data[key] == true end #----------------------------------------------------------------------------- # * Set Self Switch diff --git a/Data/Scripts/004_Game classes/002_Game_System.rb b/Data/Scripts/004_Game classes/002_Game_System.rb index 7db54145f7..9b03539563 100644 --- a/Data/Scripts/004_Game classes/002_Game_System.rb +++ b/Data/Scripts/004_Game classes/002_Game_System.rb @@ -44,28 +44,27 @@ def initialize def bgm_play(bgm) old_pos = @bgm_position @bgm_position = 0 - bgm_play_internal(bgm,0) + bgm_play_internal(bgm, 0) @bgm_position = old_pos end - def bgm_play_internal2(name,volume,pitch,position) # :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) + Audio.bgm_play(name, vol, pitch, position) rescue ArgumentError - Audio.bgm_play(name,vol,pitch) + Audio.bgm_play(name, vol, pitch) end end - def bgm_play_internal(bgm,position) # :nodoc: + 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 + @playing_bgm = bgm&.clone + 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) end else @bgm_position = position if !@bgm_paused @@ -73,15 +72,15 @@ def bgm_play_internal(bgm,position) # :nodoc: Audio.bgm_stop if !@defaultBGM end if @defaultBGM - bgm_play_internal2("Audio/BGM/"+@defaultBGM.name, - @defaultBGM.volume,@defaultBGM.pitch,@bgm_position) + 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 @@ -93,7 +92,7 @@ def bgm_unpause # :nodoc: 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 @@ -108,7 +107,7 @@ def bgm_stop # :nodoc: def bgm_fade(time) # :nodoc: @bgm_position = 0 if !@bgm_paused @playing_bgm = nil - Audio.bgm_fade((time*1000).floor) if !@defaultBGM + Audio.bgm_fade((time * 1000).floor) if !@defaultBGM end def playing_bgm @@ -130,14 +129,13 @@ def getPlayingBGM return (@playing_bgm) ? @playing_bgm.clone : nil end - def setDefaultBGM(bgm,volume=80,pitch=100) - bgm = RPG::AudioFile.new(bgm,volume,pitch) if bgm.is_a?(String) - if bgm!=nil && bgm.name!="" - @defaultBGM = nil + 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 != "" self.bgm_play(bgm) @defaultBGM = bgm.clone else - @defaultBGM = nil self.bgm_play(@playing_bgm) end end @@ -146,12 +144,12 @@ def setDefaultBGM(bgm,volume=80,pitch=100) def me_play(me) me = RPG::AudioFile.new(me) if me.is_a?(String) - if me!=nil && me.name!="" - if FileTest.audio_exist?("Audio/ME/"+me.name) + if me && 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 @@ -162,13 +160,13 @@ def me_play(me) ################################################################################ def bgs_play(bgs) - @playing_bgs = (bgs==nil) ? nil : bgs.clone - if bgs!=nil && bgs.name!="" - if FileTest.audio_exist?("Audio/BGS/"+bgs.name) + @playing_bgs = (bgs.nil?) ? nil : bgs.clone + if bgs && 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 @@ -178,8 +176,8 @@ def bgs_play(bgs) 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 @@ -207,7 +205,7 @@ def bgs_stop 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 @@ -230,11 +228,11 @@ def getPlayingBGS def se_play(se) se = RPG::AudioFile.new(se) if se.is_a?(String) - if se!=nil && se.name!="" && FileTest.audio_exist?("Audio/SE/"+se.name) + if se && 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 @@ -248,37 +246,31 @@ def battle_bgm return (@battle_bgm) ? @battle_bgm : $data_system.battle_bgm end - def battle_bgm=(battle_bgm) - @battle_bgm = battle_bgm - end + attr_writer :battle_bgm 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 + attr_writer :battle_end_me ################################################################################ def windowskin_name - if @windowskin_name==nil + 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 + attr_writer :windowskin_name def update - @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) + @timer -= 1 if @timer_working && @timer > 0 + if Input.trigger?(Input::SPECIAL) && pbCurrentEventCommentInput(1, "Cut Scene") + event = @map_interpreter.get_self + @map_interpreter.pbSetSelfSwitch(event.id, "A", true) @map_interpreter.command_end event.start end diff --git a/Data/Scripts/004_Game classes/003_Game_Picture.rb b/Data/Scripts/004_Game classes/003_Game_Picture.rb index 6fe8ba6784..d02d1950ed 100644 --- a/Data/Scripts/004_Game classes/003_Game_Picture.rb +++ b/Data/Scripts/004_Game classes/003_Game_Picture.rb @@ -20,6 +20,7 @@ class Game_Picture attr_reader :blend_type # blend method attr_reader :tone # color tone attr_reader :angle # rotation angle + #----------------------------------------------------------------------------- # * Object Initialization # number : picture number @@ -65,7 +66,7 @@ def show(name, origin, x, y, zoom_x, zoom_y, opacity, blend_type) @zoom_x = zoom_x.to_f @zoom_y = zoom_y.to_f @opacity = opacity.to_f - @blend_type = blend_type ? blend_type : 0 + @blend_type = blend_type || 0 @duration = 0 @target_x = @x @target_y = @y @@ -97,7 +98,7 @@ def move(duration, origin, x, y, zoom_x, zoom_y, opacity, blend_type) @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 + @blend_type = blend_type || 0 end #----------------------------------------------------------------------------- # * Change Rotation Speed @@ -130,19 +131,19 @@ def erase def update if @duration >= 1 d = @duration - @x = (@x * (d - 1) + @target_x) / d - @y = (@y * (d - 1) + @target_y) / d - @zoom_x = (@zoom_x * (d - 1) + @target_zoom_x) / d - @zoom_y = (@zoom_y * (d - 1) + @target_zoom_y) / d - @opacity = (@opacity * (d - 1) + @target_opacity) / d + @x = ((@x * (d - 1)) + @target_x) / d + @y = ((@y * (d - 1)) + @target_y) / d + @zoom_x = ((@zoom_x * (d - 1)) + @target_zoom_x) / d + @zoom_y = ((@zoom_y * (d - 1)) + @target_zoom_y) / d + @opacity = ((@opacity * (d - 1)) + @target_opacity) / d @duration -= 1 end if @tone_duration >= 1 d = @tone_duration - @tone.red = (@tone.red * (d - 1) + @tone_target.red) / d - @tone.green = (@tone.green * (d - 1) + @tone_target.green) / d - @tone.blue = (@tone.blue * (d - 1) + @tone_target.blue) / d - @tone.gray = (@tone.gray * (d - 1) + @tone_target.gray) / d + @tone.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 diff --git a/Data/Scripts/004_Game classes/004_Game_Map.rb b/Data/Scripts/004_Game classes/004_Game_Map.rb index 864f307392..7a3ad2719c 100644 --- a/Data/Scripts/004_Game classes/004_Game_Map.rb +++ b/Data/Scripts/004_Game classes/004_Game_Map.rb @@ -43,8 +43,8 @@ def initialize 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 @@ -57,13 +57,13 @@ def setup(map_id) self.display_x = 0 self.display_y = 0 @need_refresh = false - Events.onMapCreate.trigger(self,map_id,@map,tileset) + EventHandlers.trigger(:on_game_map_setup, map_id, @map, tileset) @events = {} - for i in @map.events.keys - @events[i] = Game_Event.new(@map_id, @map.events[i],self) + @map.events.each_key do |i| + @events[i] = Game_Event.new(@map_id, @map.events[i], self) end @common_events = {} - for i in 1...$data_common_events.size + (1...$data_common_events.size).each do |i| @common_events[i] = Game_CommonEvent.new(i) end @scroll_direction = 2 @@ -95,57 +95,56 @@ def height; return @map.height; end def encounter_list; return @map.encounter_list; end def encounter_step; return @map.encounter_step; end def data; return @map.data; end + def tileset_id; return @map.tileset_id; end + def bgm; return @map.bgm; end def name - ret = pbGetMessage(MessageTypes::MapNames,@map_id) - ret.gsub!(/\\PN/,$Trainer.name) if $Trainer - return ret + return pbGetMapNameFromId(@map_id) + 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 #----------------------------------------------------------------------------- def autoplayAsCue - if @map.autoplay_bgm - if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/"+ @map.bgm.name+ "_n") - pbCueBGM(@map.bgm.name+"_n",1.0,@map.bgm.volume,@map.bgm.pitch) - else - pbCueBGM(@map.bgm,1.0) - end - end - if @map.autoplay_bgs - pbBGSPlay(@map.bgs) - end + pbCueBGM(bgm_name, 1.0, @map.bgm.volume, @map.bgm.pitch) if @map.autoplay_bgm + pbBGSPlay(@map.bgs) if @map.autoplay_bgs end #----------------------------------------------------------------------------- # * Plays background music # Plays music called "[normal BGM]_n" if it's night time and it exists #----------------------------------------------------------------------------- def autoplay - if @map.autoplay_bgm - if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/"+ @map.bgm.name+ "_n") - pbBGMPlay(@map.bgm.name+"_n",@map.bgm.volume,@map.bgm.pitch) - else - pbBGMPlay(@map.bgm) - end - end - if @map.autoplay_bgs - pbBGSPlay(@map.bgs) - end + pbBGMPlay(bgm_name, @map.bgm.volume, @map.bgm.pitch) if @map.autoplay_bgm + pbBGSPlay(@map.bgs) if @map.autoplay_bgs end def valid?(x, y) - return x>=0 && x=0 && y= 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, d, self_event = nil) return false if !valid?(x, y) - bit = (1 << (d / 2 - 1)) & 0x0f - for event in events.values + bit = (1 << ((d / 2) - 1)) & 0x0f + events.each_value do |event| next if event.tile_id <= 0 next if event == self_event next if !event.at_coordinate?(x, y) @@ -156,7 +155,7 @@ def passable?(x, y, d, self_event = nil) return false if passage & 0x0f == 0x0f return true if @priorities[event.tile_id] == 0 end - return playerPassable?(x, y, d, self_event) if self_event==$game_player + return playerPassable?(x, y, d, self_event) if self_event == $game_player # All other events newx = x newy = y @@ -183,14 +182,14 @@ def passable?(x, y, d, self_event = nil) newy -= 1 end return false if !valid?(newx, newy) - for i in [2, 1, 0] + [2, 1, 0].each do |i| 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 != nil && terrain.can_surf_freely - for j in [2, 1, 0] + if self_event && terrain.can_surf_freely + [2, 1, 0].each do |j| facing_tile_id = data[newx, newy, j] - 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 @@ -200,29 +199,28 @@ def passable?(x, y, d, self_event = nil) # Can't walk onto ice elsif terrain.ice return false - elsif self_event != nil && self_event.x == x && self_event.y == y + elsif self_event && self_event.x == x && self_event.y == y # Can't walk onto ledges - for j in [2, 1, 0] + [2, 1, 0].each do |j| facing_tile_id = data[newx, newy, j] - 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 # Regular passability checks - 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 + passage = @passages[tile_id] + return false if passage & bit != 0 || passage & 0x0f == 0x0f + return true if @priorities[tile_id] == 0 end return true end def playerPassable?(x, y, d, self_event = nil) - bit = (1 << (d / 2 - 1)) & 0x0f - for i in [2, 1, 0] + bit = (1 << ((d / 2) - 1)) & 0x0f + [2, 1, 0].each do |i| tile_id = data[x, y, i] terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) passage = @passages[tile_id] @@ -238,11 +236,10 @@ def playerPassable?(x, y, d, self_event = nil) return (passage & bit == 0 && passage & 0x0f != 0x0f) end end + next if terrain&.ignore_passability # Regular passability checks - if !terrain || !terrain.ignore_passability - return false if passage & bit != 0 || passage & 0x0f == 0x0f - return true if @priorities[tile_id] == 0 - end + return false if passage & bit != 0 || passage & 0x0f == 0x0f + return true if @priorities[tile_id] == 0 end return true end @@ -251,14 +248,14 @@ def playerPassable?(x, y, d, self_event = nil) # event there, and the tile is fully passable in all directions) def passableStrict?(x, y, d, self_event = nil) return false if !valid?(x, y) - for event in events.values + events.each_value do |event| 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 - for i in [2, 1, 0] + [2, 1, 0].each do |i| tile_id = data[x, y, i] next if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).ignore_passability return false if @passages[tile_id] & 0x0f != 0 @@ -267,8 +264,8 @@ def passableStrict?(x, y, d, self_event = nil) return true end - def bush?(x,y) - for i in [2, 1, 0] + def bush?(x, y) + [2, 1, 0].each do |i| tile_id = data[x, y, i] return false if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).bridge && $PokemonGlobal.bridge > 0 @@ -277,8 +274,8 @@ def bush?(x,y) return false end - def deepBush?(x,y) - for i in [2, 1, 0] + def deepBush?(x, y) + [2, 1, 0].each do |i| tile_id = data[x, y, i] terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) return false if terrain.bridge && $PokemonGlobal.bridge > 0 @@ -287,8 +284,8 @@ def deepBush?(x,y) return false end - def counter?(x,y) - for i in [2, 1, 0] + def counter?(x, y) + [2, 1, 0].each do |i| tile_id = data[x, y, i] passage = @passages[tile_id] return true if passage & 0x80 == 0x80 @@ -296,9 +293,9 @@ def counter?(x,y) return false end - def terrain_tag(x,y,countBridge=false) + def terrain_tag(x, y, countBridge = false) if valid?(x, y) - for i in [2, 1, 0] + [2, 1, 0].each do |i| tile_id = data[x, y, i] terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) next if terrain.id == :None || terrain.ignore_passability @@ -310,8 +307,8 @@ def terrain_tag(x,y,countBridge=false) end # Unused. - def check_event(x,y) - for event in self.events.values + def check_event(x, y) + self.events.each_value do |event| return event.id if event.at_coordinate?(x, y) end end @@ -319,21 +316,21 @@ def check_event(x,y) def display_x=(value) return if @display_x == value @display_x = value - 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 + if metadata&.snap_edges + max_x = (self.width - (Graphics.width.to_f / TILE_WIDTH)) * REAL_RES_X @display_x = [0, [@display_x, max_x].min].max end - $MapFactory.setMapsInRange if $MapFactory + $map_factory&.setMapsInRange end def display_y=(value) return if @display_y == value @display_y = value - 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 + if metadata&.snap_edges + max_y = (self.height - (Graphics.height.to_f / TILE_HEIGHT)) * REAL_RES_Y @display_y = [0, [@display_y, max_y].min].max end - $MapFactory.setMapsInRange if $MapFactory + $map_factory&.setMapsInRange end def scroll_up(distance) @@ -354,7 +351,7 @@ def scroll_right(distance) def start_scroll(direction, distance, speed) @scroll_direction = direction - if direction==2 || direction==8 # down or up + if [2, 8].include?(direction) # down or up @scroll_rest = distance * REAL_RES_Y else @scroll_rest = distance * REAL_RES_X @@ -366,7 +363,7 @@ def scrolling? return @scroll_rest > 0 end - def start_fog_tone_change(tone,duration) + def start_fog_tone_change(tone, duration) @fog_tone_target = tone.clone @fog_tone_duration = duration if @fog_tone_duration == 0 @@ -374,19 +371,27 @@ def start_fog_tone_change(tone,duration) end end - def start_fog_opacity_change(opacity,duration) - @fog_opacity_target = opacity*1.0 + def start_fog_opacity_change(opacity, duration) + @fog_opacity_target = opacity.to_f @fog_opacity_duration = duration - if @fog_opacity_duration==0 + if @fog_opacity_duration == 0 @fog_opacity = @fog_opacity_target end end + def set_tile(x, y, layer, id = 0) + self.data[x, y, layer] = id + end + + def erase_tile(x, y, layer) + set_tile(x, y, layer, 0) + end + def refresh - for event in @events.values + @events.each_value do |event| event.refresh end - for common_event in @common_events.values + @common_events.each_value do |common_event| common_event.refresh end @need_refresh = false @@ -394,16 +399,16 @@ def refresh def update # refresh maps if necessary - if $MapFactory - for i in $MapFactory.maps + if $map_factory + $map_factory.maps.each do |i| i.refresh if i.need_refresh end - $MapFactory.setCurrentMap + $map_factory.setCurrentMap end # If scrolling - if @scroll_rest>0 - distance = (1<<@scroll_speed)*40.0/Graphics.frame_rate - distance = @scroll_rest if distance>@scroll_rest + 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) @@ -413,28 +418,28 @@ def update @scroll_rest -= distance end # Only update events that are on-screen - for event in @events.values + @events.each_value do |event| event.update end # Update common events - for common_event in @common_events.values + @common_events.each_value do |common_event| common_event.update end # Update fog - @fog_ox -= @fog_sx/8.0 - @fog_oy -= @fog_sy/8.0 - if @fog_tone_duration>=1 + @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.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 = ((@fog_opacity * (d - 1)) + @fog_opacity_target) / d @fog_opacity_duration -= 1 end end @@ -443,8 +448,8 @@ def update #=============================================================================== # #=============================================================================== -def pbScrollMap(direction,distance,speed) - if speed==0 +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) @@ -460,7 +465,7 @@ def pbScrollMap(direction,distance,speed) Input.update break if !$game_map.scrolling? pbUpdateSceneMap - break if $game_map.display_x==oldx && $game_map.display_y==oldy + break if $game_map.display_x == oldx && $game_map.display_y == oldy oldx = $game_map.display_x oldy = $game_map.display_y end diff --git a/Data/Scripts/004_Game classes/005_Game_Map_Autoscroll.rb b/Data/Scripts/004_Game classes/005_Game_Map_Autoscroll.rb index 3af3ea1897..76cea20e39 100644 --- a/Data/Scripts/004_Game classes/005_Game_Map_Autoscroll.rb +++ b/Data/Scripts/004_Game classes/005_Game_Map_Autoscroll.rb @@ -77,23 +77,38 @@ class Interpreter # 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) + 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' + 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)' + 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 + 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.to_f / Game_Map::TILE_WIDTH)) * 4 * Game_Map::TILE_WIDTH + max_y = ($game_map.height - (Graphics.height.to_f / 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 = 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_y.abs : count_x.abs + else @diag = true dir = nil if count_x > 0 @@ -109,37 +124,18 @@ def autoscroll(x,y,speed=SCROLL_SPEED_DEFAULT) 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 + count = [count_x.abs, count_y.abs].min end + $game_map.start_scroll(dir, count, speed) if dir + return !@diag 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) + def autoscroll_player(speed = SCROLL_SPEED_DEFAULT) + autoscroll($game_player.x, $game_player.y, speed) end end @@ -148,20 +144,20 @@ def autoscroll_player(speed=SCROLL_SPEED_DEFAULT) class Game_Map def scroll_downright(distance) @display_x = [@display_x + distance, - (self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X].min + (self.width - (Graphics.width.to_f / TILE_WIDTH)) * REAL_RES_X].min @display_y = [@display_y + distance, - (self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y].min + (self.height - (Graphics.height.to_f / 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 + (self.height - (Graphics.height.to_f / 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 + (self.width - (Graphics.width.to_f / TILE_WIDTH)) * REAL_RES_X].min @display_y = [@display_y - distance, 0].max end @@ -174,8 +170,8 @@ 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 + 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) diff --git a/Data/Scripts/004_Game classes/006_Game_MapFactory.rb b/Data/Scripts/004_Game classes/006_Game_MapFactory.rb index 9c4dcf6ce0..39150599cc 100644 --- a/Data/Scripts/004_Game classes/006_Game_MapFactory.rb +++ b/Data/Scripts/004_Game classes/006_Game_MapFactory.rb @@ -19,7 +19,7 @@ def setup(id) @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 @@ def setup(id) 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) - for map in @maps - return true if map.map_id==id + @maps.each do |map| + return true if map.map_id == id end return false end def getMapIndex(id) - for i in 0...@maps.length - return i if @maps[i].map_id==id + @maps.length.times do |i| + return i if @maps[i].map_id == id end return -1 end - def getMap(id,add=true) - for map in @maps - return map if map.map_id==id + def getMap(id, add = true) + @maps.each do |map| + return map if map.map_id == id end map = Game_Map.new map.setup(id) @@ -62,31 +62,28 @@ def getMap(id,add=true) end def getMapNoAdd(id) - return getMap(id,false) + return getMap(id, false) end - def getNewMap(playerX,playerY) + 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 + 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 [getMap(mapidB), newx, newy] end end return nil @@ -96,16 +93,16 @@ def getNewMap(playerX,playerY) # their transfer to that map. def setCurrentMap return if $game_player.moving? - return if $game_map.valid?($game_player.x,$game_player.y) - newmap = getNewMap($game_player.x,$game_player.y) + return if $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 @@ -117,44 +114,41 @@ def setMapsInRange return if @fixup @fixup = true id = $game_map.map_id - 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 + 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 end end end @fixup = false end - def setMapChanging(newID,newMap) - Events.onMapChanging.trigger(self,newID,newMap) + def setMapChanging(newID, newMap) + EventHandlers.trigger(:on_leave_map, newID, newMap) end def setMapChanged(prevMap) - Events.onMapChange.trigger(self,prevMap) + EventHandlers.trigger(:on_enter_map, prevMap) @mapChanged = true end def setSceneStarted(scene) - Events.onMapSceneChange.trigger(self,scene,@mapChanged) + EventHandlers.trigger(:on_map_or_spriteset_change, scene, @mapChanged) @mapChanged = false end @@ -175,90 +169,85 @@ def isPassable?(mapID, x, y, thisEvent = nil) # Check passability of tile if thisEvent.is_a?(Game_Player) return false unless ($DEBUG && Input.press?(Input::CTRL)) || - map.passable?(x, y, 0, thisEvent) + 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 - for event in map.events.values + map.events.each_value do |event| 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) - 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 + 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 end return true end # Only used by dependent events - def isPassableStrict?(mapID,x,y,thisEvent=nil) + 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 - if thisEvent==$game_player - if !($DEBUG && Input.press?(Input::CTRL)) - return false if !map.passableStrict?(x,y,0,thisEvent) + if thisEvent == $game_player + if !($DEBUG && Input.press?(Input::CTRL)) && !map.passableStrict?(x, y, 0, thisEvent) + return false end - else - return false if !map.passableStrict?(x,y,0,thisEvent) + elsif !map.passableStrict?(x, y, 0, thisEvent) + return false end - for event in map.events.values + map.events.each_value do |event| 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 - conns = MapFactoryHelper.getMapConnections - if conns[mapID1] - for conn in conns[mapID1] - return true if conn[0] == mapID2 || conn[3] == mapID2 - end + MapFactoryHelper.eachConnectionForMap(mapID1) do |conn| + return true if conn[0] == mapID2 || conn[3] == mapID2 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 - 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 + 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] end end return [0, 0] @@ -267,38 +256,37 @@ def getRelativePos(thisMapID, thisX, thisY, otherMapID, otherX, otherY) # 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 @@ -322,38 +310,35 @@ def getFacingTileFromPos(mapID,x,y,direction=0,steps=1) 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) - 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 + 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] 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 @@ -376,36 +361,26 @@ def getFacingCoords(x,y,direction=0,steps=1) x += steps y -= steps end - return [x,y] + return [x, y] end def updateMaps(scene) updateMapsInternal - $MapFactory.setSceneStarted(scene) if @mapChanged + $map_factory.setSceneStarted(scene) if @mapChanged end def updateMapsInternal return if $game_player.moving? if !MapFactoryHelper.hasConnections?($game_map.map_id) - return if @maps.length==1 - for i in 0...@maps.length - @maps[i] = nil if $game_map.map_id!=@maps[i].map_id - end - @maps.compact! + return if @maps.length == 1 + @maps.delete_if { |map| $game_map.map_id != map.map_id } @mapIndex = getMapIndex($game_map.map_id) return end setMapsInRange - deleted = false - for i in 0...@maps.length - next if MapFactoryHelper.mapInRange?(@maps[i]) - @maps[i] = nil - deleted = true - end - if deleted - @maps.compact! - @mapIndex = getMapIndex($game_map.map_id) - end + old_num_maps = @maps.length + @maps.delete_if { |map| !MapFactoryHelper.mapInRange?(map) } + @mapIndex = getMapIndex($game_map.map_id) if @maps.length != old_num_maps end end @@ -465,6 +440,12 @@ def self.hasConnections?(id) return conns[id] ? true : false end + def self.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 self.getMapDims(id) # Create cache if doesn't exist @@ -473,9 +454,9 @@ def self.getMapDims(id) 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 @@ -484,11 +465,11 @@ def self.getMapDims(id) # Returns the X or Y coordinate of an edge on the map with id. # Considers the special strings "N","W","E","S" - def self.getMapEdge(id,edge) - return 0 if edge=="N" || edge=="W" + def self.getMapEdge(id, edge) + return 0 if ["N", "W"].include?(edge) 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 @@ -498,18 +479,18 @@ def self.mapInRange?(map) 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 self.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 @@ -519,8 +500,8 @@ def self.mapInRangeById?(id,dispx,dispy) #=============================================================================== # Unused def updateTilesets - maps = $MapFactory.maps - for map in maps - map.updateTileset if map + maps = $map_factory.maps + maps.each do |map| + map&.updateTileset end end diff --git a/Data/Scripts/004_Game classes/007_Game_Character.rb b/Data/Scripts/004_Game classes/007_Game_Character.rb index b7ddc4772a..9b5e84f05b 100644 --- a/Data/Scripts/004_Game classes/007_Game_Character.rb +++ b/Data/Scripts/004_Game classes/007_Game_Character.rb @@ -6,13 +6,15 @@ 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_reader :opacity + attr_accessor :opacity attr_reader :blend_type attr_accessor :direction attr_accessor :pattern @@ -26,7 +28,7 @@ class Game_Character attr_accessor :walk_anime attr_writer :bob_height - def initialize(map=nil) + def initialize(map = nil) @map = map @id = 0 @original_x = 0 @@ -35,6 +37,8 @@ def initialize(map=nil) @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] @@ -73,10 +77,14 @@ def initialize(map=nil) @bob_height = 0 @wait_count = 0 @moved_this_frame = false + @moveto_happened = false @locked = false @prelock_direction = 0 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 @@ -88,15 +96,15 @@ def in_line_with_coordinate?(check_x, check_y) end def each_occupied_tile - for i in @x...(@x + @width) - for j in (@y - @height + 1)..@y + (@x...(@x + @width)).each do |i| + ((@y - @height + 1)..@y).each do |j| yield i, j end end end def move_speed=(val) - return if val==@move_speed + return if val == @move_speed @move_speed = val # @move_speed_real is the number of quarter-pixels to move each frame. There # are 128 quarter-pixels per tile. By default, it is calculated from @@ -107,7 +115,7 @@ def move_speed=(val) # 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 + self.move_speed_real = (val == 6) ? 64 : (val == 5) ? 32 : (2**(val + 1)) * 0.8 end def move_speed_real @@ -120,7 +128,7 @@ def move_speed_real=(val) end def jump_speed_real - self.jump_speed_real = (2 ** (3 + 1)) * 0.8 if !@jump_speed_real # 3 is walking speed + self.jump_speed_real = (2**(3 + 1)) * 0.8 if !@jump_speed_real # 3 is walking speed return @jump_speed_real end @@ -129,7 +137,7 @@ def jump_speed_real=(val) end def move_frequency=(val) - return if val==@move_frequency + return if val == @move_frequency @move_frequency = val # @move_frequency_real is the number of frames to wait between each action # in a move route (not forced). Specifically, this is the number of frames @@ -142,7 +150,7 @@ def move_frequency=(val) # 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) + self.move_frequency_real = (40 - (val * 2)) * (6 - val) end def move_frequency_real @@ -199,21 +207,32 @@ def bush_depth def calculate_bush_depth if @tile_id > 0 || @always_on_top || jumping? @bush_depth = 0 + return + end + xbehind = @x + (@direction == 4 ? 1 : @direction == 6 ? -1 : 0) + ybehind = @y + (@direction == 8 ? 1 : @direction == 2 ? -1 : 0) + this_map = (self.map.valid?(@x, @y)) ? [self.map, @x, @y] : $map_factory&.getNewMap(@x, @y) + behind_map = (self.map.valid?(xbehind, ybehind)) ? [self.map, xbehind, ybehind] : $map_factory&.getNewMap(xbehind, ybehind) + if this_map && this_map[0].deepBush?(this_map[1], this_map[2]) && + (!behind_map || behind_map[0].deepBush?(behind_map[1], behind_map[2])) + @bush_depth = Game_Map::TILE_HEIGHT + elsif this_map && this_map[0].bush?(this_map[1], this_map[2]) && !moving? + @bush_depth = 12 else - deep_bush = regular_bush = false - xbehind = @x + (@direction == 4 ? 1 : @direction == 6 ? -1 : 0) - ybehind = @y + (@direction == 8 ? 1 : @direction == 2 ? -1 : 0) - 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 + @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 #============================================================================= @@ -229,12 +248,13 @@ def passable?(x, y, d, strict = false) return false unless self.map.passable?(x, y, d, self) return false unless self.map.passable?(new_x, new_y, 10 - d, self) end - for event in self.map.events.values + self.map.events.each_value do |event| 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 - return false if !$game_player.through && @character_name != "" + if $game_player.x == new_x && $game_player.y == new_y && + !$game_player.through && @character_name != "" + return false end return true end @@ -243,24 +263,24 @@ def can_move_from_coordinate?(start_x, start_y, dir, strict = false) case dir when 2, 8 # Down, up y_diff = (dir == 8) ? @height - 1 : 0 - for i in start_x...(start_x + @width) + (start_x...(start_x + @width)).each do |i| 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 - for i in (start_y - @height + 1)..start_y + ((start_y - @height + 1)..start_y).each do |i| 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 - for i in start_x...(start_x + @width) + (start_x...(start_x + @width)).each do |i| return false if !passable?(i, start_y, 2, strict) end x_diff = (dir == 3) ? @width - 1 : 0 - for i in (start_y - @height + 1)..start_y + ((start_y - @height + 1)..start_y).each do |i| return false if !passable?(start_x + x_diff, i + 1, dir + 3, strict) end return true @@ -268,12 +288,12 @@ def can_move_from_coordinate?(start_x, start_y, dir, strict = false) # 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 - for i in (start_y - @height + 1)..start_y + ((start_y - @height + 1)..start_y).each do |i| return false if !passable?(start_x + x_diff, i, dir - 3, strict) end - 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) + 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) end return true end @@ -288,13 +308,14 @@ def can_move_in_direction?(dir, strict = false) # Screen position of the character #============================================================================= def screen_x - ret = ((@real_x - self.map.display_x) / Game_Map::X_SUBPIXELS).round + ret = ((@real_x.to_f - 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 - self.map.display_y) / Game_Map::Y_SUBPIXELS).round + ret = ((@real_y.to_f - self.map.display_y) / Game_Map::Y_SUBPIXELS).round ret += Game_Map::TILE_HEIGHT return ret end @@ -307,8 +328,9 @@ def screen_y 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) + ret += @jump_peak * ((4 * (jump_fraction**2)) - 1) end + ret += self.y_offset return ret end @@ -317,7 +339,7 @@ def screen_z(height = 0) 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 @@ -345,7 +367,7 @@ def straighten 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 @@ -363,14 +385,15 @@ def moveto(x, y) @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) - Events.onLeaveTile.trigger(self,self,@oldMap,@oldX,@oldY) + (@oldX != self.x || @oldY != self.y || @oldMap != self.map.map_id) + EventHandlers.trigger(:on_leave_tile, self, @oldMap, @oldX, @oldY) end @oldX = self.x @oldY = self.y @@ -394,8 +417,8 @@ def move_type_random 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 @@ -617,21 +640,23 @@ def move_random 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) @@ -640,17 +665,17 @@ def move_random_range(xrange=-1,yrange=-1) 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 @@ -671,8 +696,8 @@ def move_toward_player 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 @@ -724,7 +749,7 @@ def jump(x_plus, y_plus) end @x = @x + x_plus @y = @y + y_plus - real_distance = Math::sqrt(x_plus * x_plus + y_plus * 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 @@ -736,27 +761,24 @@ def jump(x_plus, y_plus) @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 case self.direction - 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 + 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 end def jumpBackward case self.direction - 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 + 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 end @@ -814,8 +836,8 @@ def turn_random 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 @@ -825,8 +847,8 @@ def turn_toward_player 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 @@ -841,6 +863,8 @@ def turn_away_from_player def update @moved_last_frame = @moved_this_frame @stopped_last_frame = @stopped_this_frame + @moved_this_frame = false + @stopped_this_frame = false if !$game_temp.in_menu # Update command update_command @@ -905,12 +929,11 @@ def update_move end # End of a step, so perform events that happen at this time if !jumping? && !moving? - Events.onStepTakenFieldMovement.trigger(self, self) + EventHandlers.trigger(:on_step_taken, 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 += 1 if @walk_anime || @step_anime @@ -920,8 +943,6 @@ def update_move def update_stop @anime_count += 1 if @step_anime @stop_count += 1 if !@starting && !lock? - @moved_this_frame = false - @stopped_this_frame = false end def update_pattern diff --git a/Data/Scripts/004_Game classes/008_Game_Event.rb b/Data/Scripts/004_Game classes/008_Game_Event.rb index 40965fdc37..bf9510a109 100644 --- a/Data/Scripts/004_Game classes/008_Game_Event.rb +++ b/Data/Scripts/004_Game classes/008_Game_Event.rb @@ -6,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 @@ -54,7 +54,7 @@ def erase_route end def tsOn?(c) - return @tempSwitches && @tempSwitches[c]==true + return @tempSwitches && @tempSwitches[c] == true end def tsOff?(c) @@ -62,17 +62,17 @@ def tsOff?(c) 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) @@ -87,31 +87,31 @@ def switchIsOn?(id) 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=86400) - ontime=self.variable - time=pbGetTimeNow - return ontime && (time.to_i>ontime+secs) + def expired?(secs = 86_400) + 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)/86400 - elapsed+=1 if (now.to_i-ontime)%86400>(now.hour*3600+now.min*60+now.sec) - return elapsed>=days + 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 end def cooledDown?(seconds) @@ -141,12 +141,12 @@ def over_trigger? 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 + return if @trigger != 2 # Event touch + return if !@event.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_touch(dir) @@ -168,11 +168,12 @@ def check_event_trigger_touch(dir) end def check_event_trigger_auto - if @trigger == 2 # Event touch - if at_coordinate?($game_player.x, $game_player.y) - start if !jumping? && over_trigger? + case @trigger + when 2 # Event touch + if at_coordinate?($game_player.x, $game_player.y) && !jumping? && over_trigger? + start end - elsif @trigger == 3 # Autorun + when 3 # Autorun start end end @@ -180,7 +181,7 @@ def check_event_trigger_auto def refresh new_page = nil unless @erased - for page in @event.pages.reverse + @event.pages.reverse.each do |page| c = page.condition next if c.switch1_valid && !switchIsOn?(c.switch1_id) next if c.switch2_valid && !switchIsOn?(c.switch2_id) @@ -196,7 +197,7 @@ def refresh return if new_page == @page @page = new_page clear_starting - if @page == nil + if @page.nil? @tile_id = 0 @character_name = "" @character_hue = 0 @@ -242,15 +243,15 @@ def refresh check_event_trigger_auto end - def should_update?(recalc=false) + def should_update?(recalc = false) return @to_update if !recalc return true if @trigger && (@trigger == 3 || @trigger == 4) - return true if @move_route_forcing + return true if @move_route_forcing || @moveto_happened 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 @@ -258,6 +259,7 @@ def should_update?(recalc=false) def update @to_update = should_update?(true) return if !@to_update + @moveto_happened = false last_moving = moving? super if !moving? && last_moving @@ -268,7 +270,7 @@ def update refresh end check_event_trigger_auto - if @interpreter != nil + if @interpreter unless @interpreter.running? @interpreter.setup(@list, @event.id, @map_id) end diff --git a/Data/Scripts/004_Game classes/009_Game_Player.rb b/Data/Scripts/004_Game classes/009_Game_Player.rb index aa02767843..9bceb6e225 100644 --- a/Data/Scripts/004_Game classes/009_Game_Player.rb +++ b/Data/Scripts/004_Game classes/009_Game_Player.rb @@ -10,14 +10,16 @@ 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 + 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 + + @@bobFrameSpeed = 1.0 / 15 def initialize(*arg) super(*arg) - @lastdir=0 - @lastdirframe=0 - @bump_se=0 + @lastdir = 0 + @lastdirframe = 0 + @bump_se = 0 end def map @@ -25,77 +27,190 @@ def map return $game_map end - def pbHasDependentEvents? - return $PokemonGlobal.dependentEvents.length>0 + 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 + new_charset = pbGetPlayerCharset(meta.dive_charset) + when :surfing, :surfing_fast, :surfing_jumping, :surfing_stopped + self.move_speed = (type == :surfing_jumping) ? 3 : 4 + new_charset = pbGetPlayerCharset(meta.surf_charset) + when :cycling, :cycling_fast, :cycling_jumping, :cycling_stopped + self.move_speed = (type == :cycling_jumping) ? 3 : 5 + new_charset = pbGetPlayerCharset(meta.cycle_charset) + when :running + self.move_speed = 4 + new_charset = pbGetPlayerCharset(meta.run_charset) + when :ice_sliding + self.move_speed = 4 + new_charset = pbGetPlayerCharset(meta.walk_charset) + else # :walking, :jumping, :walking_stopped + self.move_speed = 3 + 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_se && @bump_se>0 - pbSEPlay("Player bump") - @bump_se = Graphics.frame_rate/4 + return if @bump_se && @bump_se > 0 + pbSEPlay("Player bump") if !@move_route_forcing + @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 !$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 return if pbLedge(x_offset, y_offset) return if pbEndSurf(x_offset, y_offset) turn_generic(dir, true) - if !$PokemonTemp.encounterTriggered + if !$game_temp.encounter_triggered @x += x_offset @y += y_offset - $PokemonTemp.dependentEvents.pbMoveDependentEvents + if $PokemonGlobal&.diving || $PokemonGlobal&.surfing + $stats.distance_surfed += 1 + elsif $PokemonGlobal&.bicycle + $stats.distance_cycled += 1 + else + $stats.distance_walked += 1 + end + $stats.distance_slid_on_ice += 1 if $PokemonGlobal.sliding increase_steps end elsif !check_event_trigger_touch(dir) bump_into_object end end - $PokemonTemp.encounterTriggered = false + $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? - Events.onChangeDirection.trigger(self, self) - $PokemonTemp.encounterTriggered = false if !keep_enc_indicator + EventHandlers.trigger(:on_player_change_direction) + $game_temp.encounter_triggered = false if !keep_enc_indicator + end + end + + def jump(x_plus, y_plus) + if x_plus != 0 || y_plus != 0 + if x_plus.abs > y_plus.abs + (x_plus < 0) ? turn_left : turn_right + else + (y_plus < 0) ? turn_up : turn_down + end + each_occupied_tile { |i, j| return if !passable?(i + x_plus, j + y_plus, 0) } + end + @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 + @jump_distance_left = 1 # Just needs to be non-zero + if real_distance > 0 # Jumping to somewhere else + if $PokemonGlobal&.diving || $PokemonGlobal&.surfing + $stats.distance_surfed += x_plus.abs + y_plus.abs + elsif $PokemonGlobal&.bicycle + $stats.distance_cycled += x_plus.abs + y_plus.abs + else + $stats.distance_walked += x_plus.abs + y_plus.abs + end + @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 + triggerLeaveTile end - def pbTriggeredTrainerEvents(triggers,checkIfRunning=true) + 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 - for event in $game_map.events.values - next if !event.name[/trainer\((\d+)\)/i] + $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 - # 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 + 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) + 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 + $game_map.events.each_value do |event| + next if !triggers.include?(event.trigger) 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 + next if !pbEventFacesPlayer?(event, self, distance) + next if event.jumping? || event.over_trigger? + result.push(event) end return result end @@ -105,25 +220,25 @@ def pbCheckEventTriggerAfterTurning; end def pbCheckEventTriggerFromDistance(triggers) ret = pbTriggeredTrainerEvents(triggers) ret.concat(pbTriggeredCounterEvents(triggers)) - return false if ret.length==0 - for event in ret + return false if ret.length == 0 + ret.each do |event| event.start end return true end def pbTerrainTag(countBridge = false) - return $MapFactory.getTerrainTag(self.map.map_id, @x, @y, countBridge) if $MapFactory + return $map_factory.getTerrainTag(self.map.map_id, @x, @y, countBridge) if $map_factory return $game_map.terrain_tag(@x, @y, countBridge) end - def pbFacingEvent(ignoreInterpreter=false) + 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 + $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 @@ -132,7 +247,7 @@ def pbFacingEvent(ignoreInterpreter=false) 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 + $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 @@ -143,7 +258,7 @@ def pbFacingEvent(ignoreInterpreter=false) def pbFacingTerrainTag(dir = nil) dir = self.direction if !dir - return $MapFactory.getFacingTerrainTag(dir, self) if $MapFactory + return $map_factory.getFacingTerrainTag(dir, self) if $map_factory facing = pbFacingTile(dir, self) return $game_map.terrain_tag(facing[1], facing[2]) end @@ -162,8 +277,8 @@ def passable?(x, y, d, strict = false) # 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) + return false if !$map_factory + return $map_factory.isPassableFromEdge?(new_x, new_y) end # If debug mode is ON and Ctrl key was pressed return true if $DEBUG && Input.press?(Input::CTRL) @@ -174,8 +289,8 @@ def passable?(x, y, d, strict = false) # * 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 + 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 #----------------------------------------------------------------------------- @@ -185,9 +300,7 @@ def center(x, y) #----------------------------------------------------------------------------- def moveto(x, y) super - # Centering center(x, y) - # Make encounter count make_encounter_count end @@ -219,7 +332,7 @@ def check_event_trigger_here(triggers) # If event is running return result if $game_system.map_interpreter.running? # All event loops - for event in $game_map.events.values + $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) @@ -243,33 +356,30 @@ def check_event_trigger_there(triggers) 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 + $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) - 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 + 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 end end return result @@ -284,16 +394,16 @@ def check_event_trigger_touch(dir) # 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 + $game_map.events.each_value do |event| 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] + if event.name[/(?:sight|trainer)\((\d+)\)/i] distance = $~[1].to_i - next if !pbEventCanReachPlayer?(event,self,distance) + next if !pbEventCanReachPlayer?(event, self, distance) elsif event.name[/counter\((\d+)\)/i] distance = $~[1].to_i - next if !pbEventFacesPlayer?(event,self,distance) + next if !pbEventFacesPlayer?(event, self, distance) end # If starting determinant is front event (other than jumping) next if event.jumping? || event.over_trigger? @@ -312,14 +422,18 @@ def update super update_screen_position(last_real_x, last_real_y) # Update dependent events - $PokemonTemp.dependentEvents.updateDependentEvents + if (!@moved_last_frame || @stopped_last_frame || + (@stopped_this_frame && $PokemonGlobal.sliding)) && (moving? || jumping?) + $game_temp.followers.move_followers + end + $game_temp.followers.update # Count down the time between allowed bump sounds - @bump_se -= 1 if @bump_se && @bump_se>0 + @bump_se -= 1 if @bump_se && @bump_se > 0 # Finish up dismounting from surfing - if $PokemonTemp.endSurf && !moving? + if $game_temp.ending_surf && !moving? pbCancelVehicles - $PokemonTemp.surfJump = nil - $PokemonTemp.endSurf = false + $game_temp.surf_base_coords = nil + $game_temp.ending_surf = false end update_event_triggering end @@ -327,7 +441,7 @@ def update def update_command_new dir = Input.dir4 unless pbMapInterpreterRunning? || $game_temp.message_window_showing || - $PokemonTemp.miniupdate || $game_temp.in_menu + $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 > 0 && dir == @lastdir && Graphics.frame_count - @lastdirframe > Graphics.frame_rate / 20) @@ -351,6 +465,64 @@ def update_command_new @lastdir = dir end + def update_move + if !@moved_last_frame || @stopped_last_frame # Started a new step + if pbTerrainTag.ice + set_movement_type(:ice_sliding) + else#if !@move_route_forcing + 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 + super + 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 + 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 + # 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) @@ -362,57 +534,57 @@ 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 + $game_temp.followers.turn_followers result = pbCheckEventTriggerFromDistance([2]) # Event determinant is via touch of same position event - result |= check_event_trigger_here([1,2]) + 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 + if Input.trigger?(Input::USE) && !$game_temp.in_mini_update # Same position and front event determinant check_event_trigger_here([0]) - check_event_trigger_there([0,2]) + check_event_trigger_there([0, 2]) end end end -def pbGetPlayerCharset(meta,charset,trainer=nil,force=false) - trainer = $Trainer if !trainer +#=============================================================================== +# +#=============================================================================== +def pbGetPlayerCharset(charset, trainer = nil, force = false) + trainer = $player 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 + 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 - 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 + if $PokemonGlobal&.diving + $game_player.set_movement_type(:diving) + elsif $PokemonGlobal&.surfing + $game_player.set_movement_type(:surfing) + elsif $PokemonGlobal&.bicycle + $game_player.set_movement_type(:cycling) + else + $game_player.set_movement_type(:walking) end end -def pbCancelVehicles(destination=nil) - $PokemonGlobal.surfing = false - $PokemonGlobal.diving = false +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 @@ -420,14 +592,13 @@ def pbCancelVehicles(destination=nil) 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 + 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.5) if bike_bgm diff --git a/Data/Scripts/004_Game classes/011_Game_CommonEvent.rb b/Data/Scripts/004_Game classes/010_Game_CommonEvent.rb similarity index 90% rename from Data/Scripts/004_Game classes/011_Game_CommonEvent.rb rename to Data/Scripts/004_Game classes/010_Game_CommonEvent.rb index e688c77015..1917758f21 100644 --- a/Data/Scripts/004_Game classes/011_Game_CommonEvent.rb +++ b/Data/Scripts/004_Game classes/010_Game_CommonEvent.rb @@ -56,7 +56,7 @@ def switchIsOn?(id) def refresh # Create an interpreter for parallel process if necessary if self.trigger == 2 && switchIsOn?(self.switch_id) - if @interpreter == nil + if @interpreter.nil? @interpreter = Interpreter.new end else @@ -67,15 +67,10 @@ def refresh # * 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 + 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 diff --git a/Data/Scripts/004_Game classes/010_Game_Player_Visuals.rb b/Data/Scripts/004_Game classes/010_Game_Player_Visuals.rb deleted file mode 100644 index 8deb090013..0000000000 --- a/Data/Scripts/004_Game classes/010_Game_Player_Visuals.rb +++ /dev/null @@ -1,102 +0,0 @@ -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 diff --git a/Data/Scripts/004_Game classes/011_Game_Follower.rb b/Data/Scripts/004_Game classes/011_Game_Follower.rb new file mode 100644 index 0000000000..de06b11be5 --- /dev/null +++ b/Data/Scripts/004_Game classes/011_Game_Follower.rb @@ -0,0 +1,204 @@ +#=============================================================================== +# 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 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? + @jump_speed_real = leader.jump_speed_real + else + # This is doubled because self has to jump 2 tiles in the time it + # takes the leader to move one tile. + @jump_speed_real = leader.move_speed_real * 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 + + #============================================================================= + + def turn_towards_leader(leader) + pbTurnTowardEvent(self, leader) + end + + def follow_leader(leader, instant = false, leaderIsTrueLeader = true) + 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 + + #============================================================================= + + def update_move + was_jumping = jumping? + super + if was_jumping && !jumping? + spriteset = $scene.spriteset(map_id) + spriteset&.addUserAnimation(Settings::DUST_ANIMATION_ID, self.x, self.y, true, 1) + 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 diff --git a/Data/Scripts/004_Game classes/012_Game_DependentEvents.rb b/Data/Scripts/004_Game classes/012_Game_DependentEvents.rb deleted file mode 100644 index 078c000df5..0000000000 --- a/Data/Scripts/004_Game classes/012_Game_DependentEvents.rb +++ /dev/null @@ -1,563 +0,0 @@ -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 -} diff --git a/Data/Scripts/004_Game classes/012_Game_FollowerFactory.rb b/Data/Scripts/004_Game classes/012_Game_FollowerFactory.rb new file mode 100644 index 0000000000..6c5bea3df7 --- /dev/null +++ b/Data/Scripts/004_Game classes/012_Game_FollowerFactory.rb @@ -0,0 +1,435 @@ +#=============================================================================== +# 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_accessor :dependentEvents # Deprecated + 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 + 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.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| + if event.at_coordinate?($game_player.x, $game_player.y) # Underneath player + next if !event.over_trigger? + elsif facing_tile && event.map.map_id == facing_tile[0] && + event.at_coordinate?(facing_tile[1], facing_tile[2]) # On facing tile + next if event.over_trigger? + else # Somewhere else + next + end + 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 + + +#=============================================================================== +# Deprecated methods +#=============================================================================== +# @deprecated This method is slated to be removed in v21. +def pbAddDependency2(event_id, name, common_event_id) + Deprecation.warn_method("pbAddDependency2", "v21", "Followers.add(event_id, name, common_event_id)") + Followers.add(event_id, name, common_event_id) +end + +# @deprecated This method is slated to be removed in v21. +def pbAddDependency(event) + Deprecation.warn_method("pbAddDependency", "v21", "Followers.add_event(event)") + Followers.add_event(event) +end + +# @deprecated This method is slated to be removed in v21. +def pbRemoveDependency2(name) + Deprecation.warn_method("pbRemoveDependency2", "v21", "Followers.remove(name)") + Followers.remove(name) +end + +# @deprecated This method is slated to be removed in v21. +def pbRemoveDependency(event) + Deprecation.warn_method("pbRemoveDependency", "v21", "Followers.remove_event(event)") + Followers.remove_event(event) +end + +# @deprecated This method is slated to be removed in v21. +def pbRemoveDependencies + Deprecation.warn_method("pbRemoveDependencies", "v21", "Followers.clear") + Followers.clear +end + +# @deprecated This method is slated to be removed in v21. +def pbGetDependency(name) + Deprecation.warn_method("pbGetDependency", "v21", "Followers.get(name)") + Followers.get(name) +end diff --git a/Data/Scripts/004_Game classes/013_Game_Stats.rb b/Data/Scripts/004_Game classes/013_Game_Stats.rb new file mode 100644 index 0000000000..5bf86f6fb3 --- /dev/null +++ b/Data/Scripts/004_Game classes/013_Game_Stats.rb @@ -0,0 +1,198 @@ +#=============================================================================== +# 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 :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 :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 # Lost includes fled from + 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 + 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 # Currently unused + 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_accessor :play_time # In seconds + attr_accessor :play_sessions + attr_accessor :time_last_saved # In seconds + + def initialize + # Travel + @distance_walked = 0 + @distance_cycled = 0 + @distance_surfed = 0 + @distance_slid_on_ice = 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 + @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 + @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 + @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 + 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 + end + + def play_time_per_session + return @play_time / @play_sessions + end + + def set_time_last_saved + @time_last_saved = @play_time + end + + def time_since_last_save + return @play_time - @time_last_saved + end +end + +#=============================================================================== +# +#=============================================================================== +module Graphics + unless defined?(update_stats_play_time) + class << Graphics + alias update_stats_play_time update + end + end + + def self.update + update_stats_play_time + $stats.play_time += self.delta_s if $stats && $PokemonEncounters + end +end diff --git a/Data/Scripts/005_Sprites/001_Sprite_Picture.rb b/Data/Scripts/005_Sprites/001_Sprite_Picture.rb index 47ed370eb9..ee5be7c2da 100644 --- a/Data/Scripts/005_Sprites/001_Sprite_Picture.rb +++ b/Data/Scripts/005_Sprites/001_Sprite_Picture.rb @@ -7,11 +7,11 @@ def initialize(viewport, picture) end def dispose - @sprite.dispose if @sprite + @sprite&.dispose end def update - @sprite.update if @sprite + @sprite&.update # If picture file name is different from current one if @picture_name != @picture.name # Remember file name to instance variables @@ -19,16 +19,16 @@ def update # 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 if @sprite - @sprite=nil + @sprite&.dispose + @sprite = nil end return end diff --git a/Data/Scripts/005_Sprites/002_Sprite_Timer.rb b/Data/Scripts/005_Sprites/002_Sprite_Timer.rb index 4e5b262746..74e01d2977 100644 --- a/Data/Scripts/005_Sprites/002_Sprite_Timer.rb +++ b/Data/Scripts/005_Sprites/002_Sprite_Timer.rb @@ -1,15 +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 if @timer - @timer=nil - @disposed=true + @timer&.dispose + @timer = nil + @disposed = true end def disposed? @@ -21,14 +21,14 @@ def update 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 / Graphics.frame_rate - 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 @@ -38,8 +38,8 @@ def update @timer.text = _ISPRINTF("{1:02d}:{2:02d}", min, sec) end @timer.update - else - @timer.visible=false if @timer + elsif @timer + @timer.visible = false end end end diff --git a/Data/Scripts/005_Sprites/003_Sprite_Character.rb b/Data/Scripts/005_Sprites/003_Sprite_Character.rb index 3b2254a9f5..2178062bb4 100644 --- a/Data/Scripts/005_Sprites/003_Sprite_Character.rb +++ b/Data/Scripts/005_Sprites/003_Sprite_Character.rb @@ -8,7 +8,7 @@ def initialize(bitmap, isTile, depth) end def dispose - @bitmaps.each { |b| b.dispose if b } + @bitmaps.each { |b| b&.dispose } end def bitmap @@ -28,7 +28,7 @@ def pbBushDepthBitmap(bitmap, depth) ret = Bitmap.new(bitmap.width, bitmap.height) charheight = ret.height / 4 cy = charheight - depth - 2 - for i in 0...4 + 4.times do |i| y = i * charheight if cy >= 0 ret.blt(0, y, bitmap, Rect.new(0, y, ret.width, cy)) @@ -80,13 +80,13 @@ def visible=(value) end def dispose - @bushbitmap.dispose if @bushbitmap + @bushbitmap&.dispose @bushbitmap = nil - @charbitmap.dispose if @charbitmap + @charbitmap&.dispose @charbitmap = nil - @reflection.dispose if @reflection + @reflection&.dispose @reflection = nil - @surfbase.dispose if @surfbase + @surfbase&.dispose @surfbase = nil super end @@ -102,12 +102,12 @@ def update @character_name = @character.character_name @character_hue = @character.character_hue @oldbushdepth = @character.bush_depth + @charbitmap&.dispose 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&.dispose @bushbitmap = nil @spriteoffset = false @cw = Game_Map::TILE_WIDTH * @character.width @@ -115,21 +115,20 @@ def update 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 + "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&.dispose @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 + @character.sprite_size = [@cw, @ch] end @charbitmap.update if @charbitmapAnimated bushdepth = @character.bush_depth @@ -157,17 +156,14 @@ def update self.x = @character.screen_x self.y = @character.screen_y self.z = @character.screen_z(@ch) -# self.zoom_x = Game_Map::TILE_WIDTH / 32.0 -# self.zoom_y = Game_Map::TILE_HEIGHT / 32.0 self.opacity = @character.opacity self.blend_type = @character.blend_type -# self.bush_depth = @character.bush_depth if @character.animation_id != 0 animation = $data_animations[@character.animation_id] animation(animation, true) @character.animation_id = 0 end - @reflection.update if @reflection - @surfbase.update if @surfbase + @reflection&.update + @surfbase&.update end end diff --git a/Data/Scripts/005_Sprites/004_Sprite_Reflection.rb b/Data/Scripts/005_Sprites/004_Sprite_Reflection.rb index 14dc72b9a3..993dffba40 100644 --- a/Data/Scripts/005_Sprites/004_Sprite_Reflection.rb +++ b/Data/Scripts/005_Sprites/004_Sprite_Reflection.rb @@ -2,17 +2,15 @@ class Sprite_Reflection attr_reader :visible attr_accessor :event - def initialize(sprite,event,viewport=nil) + def initialize(sprite, event, viewport = nil) @rsprite = sprite @sprite = nil @event = event @height = 0 @fixedheight = false - if @event && @event!=$game_player - if @event.name[/reflection\((\d+)\)/i] - @height = $~[1].to_i || 0 - @fixedheight = true - end + if @event && @event != $game_player && @event.name[/reflection\((\d+)\)/i] + @height = $~[1].to_i || 0 + @fixedheight = true end @viewport = viewport @disposed = false @@ -21,7 +19,7 @@ def initialize(sprite,event,viewport=nil) def dispose if !@disposed - @sprite.dispose if @sprite + @sprite&.dispose @sprite = nil @disposed = true end @@ -50,34 +48,35 @@ def update # Just-in-time creation of sprite @sprite = Sprite.new(@viewport) if !@sprite if @sprite - x = @rsprite.x-@rsprite.ox - y = @rsprite.y-@rsprite.oy + x = @rsprite.x - @rsprite.ox + y = @rsprite.y - @rsprite.oy y -= 32 if @rsprite.character.character_name[/offset/i] @height = $PokemonGlobal.bridge if !@fixedheight - y += @height*16 + 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.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.z += 1 if @event == $game_player @sprite.zoom_x = @rsprite.zoom_x @sprite.zoom_y = @rsprite.zoom_y - frame = (Graphics.frame_count%40)/10 + 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 = @rsprite.bitmap @sprite.tone = @rsprite.tone - if @height>0 - @sprite.color = Color.new(48,96,160,255) # Dark still water + 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 = @rsprite.opacity*3/4 + @sprite.color = Color.new(224, 224, 224, 96) + @sprite.opacity = @rsprite.opacity * 3 / 4 @sprite.visible = true end @sprite.src_rect = @rsprite.src_rect diff --git a/Data/Scripts/005_Sprites/005_Sprite_SurfBase.rb b/Data/Scripts/005_Sprites/005_Sprite_SurfBase.rb index d4fcbb7491..4571115184 100644 --- a/Data/Scripts/005_Sprites/005_Sprite_SurfBase.rb +++ b/Data/Scripts/005_Sprites/005_Sprite_SurfBase.rb @@ -2,7 +2,7 @@ class Sprite_SurfBase attr_reader :visible attr_accessor :event - def initialize(sprite,event,viewport=nil) + def initialize(sprite, event, viewport = nil) @rsprite = sprite @sprite = nil @event = event @@ -12,16 +12,16 @@ def initialize(sprite,event,viewport=nil) @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 if @sprite + @sprite&.dispose @sprite = nil @surfbitmap.dispose @divebitmap.dispose @@ -59,20 +59,20 @@ def update 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 + 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 + @sprite.x = ((($game_temp.surf_base_coords[0] * Game_Map::REAL_RES_X) - @event.map.display_x + 3) / 4) + (Game_Map::TILE_WIDTH / 2) + @sprite.y = ((($game_temp.surf_base_coords[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.ox = cw / 2 + @sprite.oy = ch - 16 # Assume base needs offsetting @sprite.oy -= @event.bob_height - @sprite.z = @event.screen_z(ch)-1 + @sprite.z = @event.screen_z(ch) - 1 @sprite.zoom_x = @rsprite.zoom_x @sprite.zoom_y = @rsprite.zoom_y @sprite.tone = @rsprite.tone diff --git a/Data/Scripts/005_Sprites/006_Spriteset_Global.rb b/Data/Scripts/005_Sprites/006_Spriteset_Global.rb index 2b28db7a6a..4321676cf3 100644 --- a/Data/Scripts/005_Sprites/006_Spriteset_Global.rb +++ b/Data/Scripts/005_Sprites/006_Spriteset_Global.rb @@ -1,12 +1,14 @@ class Spriteset_Global attr_reader :playersprite + @@viewport2 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) @@viewport2.z = 200 def initialize + @follower_sprites = FollowerSprites.new(Spriteset_Map.viewport) @playersprite = Sprite_Character.new(Spriteset_Map.viewport, $game_player) @picture_sprites = [] - for i in 1..100 + (1..100).each do |i| @picture_sprites.push(Sprite_Picture.new(@@viewport2, $game_screen.pictures[i])) end @timer_sprite = Sprite_Timer.new @@ -14,15 +16,18 @@ def initialize end def dispose + @follower_sprites.dispose + @follower_sprites = nil @playersprite.dispose - @picture_sprites.each { |sprite| sprite.dispose } - @timer_sprite.dispose @playersprite = nil + @picture_sprites.each { |sprite| sprite.dispose } @picture_sprites.clear + @timer_sprite.dispose @timer_sprite = nil end def update + @follower_sprites.update @playersprite.update @picture_sprites.each { |sprite| sprite.update } @timer_sprite.update diff --git a/Data/Scripts/005_Sprites/007_Spriteset_Map.rb b/Data/Scripts/005_Sprites/007_Spriteset_Map.rb index ac0f1490e5..09fc2bc112 100644 --- a/Data/Scripts/005_Sprites/007_Spriteset_Map.rb +++ b/Data/Scripts/005_Sprites/007_Spriteset_Map.rb @@ -1,29 +1,30 @@ +# 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.ox1) ? 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 if @chbitmap + @chbitmap&.dispose super end @@ -35,19 +35,18 @@ def update @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.dispose if @chbitmap - @chbitmap = AnimatedBitmap.new( - 'Graphics/Characters/'+@character.character_name,@character.character_hue) + @chbitmap = AnimatedBitmap.new("Graphics/Characters/" + @character.character_name, + @character.character_hue) @cw = @chbitmap.width / 4 @ch = @chbitmap.height / 4 self.ox = @cw / 2 @@ -75,8 +74,8 @@ def update 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 @@ -89,10 +88,10 @@ def update @deltax = ScreenPosHelper.pbScreenX(@source) - self.x @deltay = ScreenPosHelper.pbScreenY(@source) - self.y self.color = Color.new(0, 0, 0) - @distance = ((@deltax ** 2) + (@deltay ** 2)) - self.opacity = @self_opacity * 13000 / ((@distance * 370 / @distancemax) + 6000) + @distance = ((@deltax**2) + (@deltay**2)) + self.opacity = @self_opacity * 13_000 / ((@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 @@ -124,7 +123,7 @@ def in_range?(element, object, range) # From Near's Anti Lag Script, edited # ? CLASS Sprite_Character edit #=================================================== class Sprite_Character < RPG::Sprite - alias :shadow_initialize :initialize + alias shadow_initialize initialize unless private_method_defined?(:shadow_initialize) def initialize(viewport, character = nil) @ombrelist = [] @@ -132,37 +131,33 @@ def initialize(viewport, character = nil) 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 != nil - for i in 0...shadows.size - @ombrelist.push(Sprite_Shadow.new(viewport, @character, shadows[i])) + params = XPML_read(map, "Shadow", @character, 4) + if params + shadows.each do |shadow| + @ombrelist.push(Sprite_Shadow.new(viewport, @character, shadows)) end end end if character.is_a?(Game_Player) && shadows.length > 0 - for i in 0...shadows.size - @ombrelist.push(Sprite_Shadow.new(viewport, $game_player, shadows[i])) + shadows.each do |shadow| + @ombrelist.push(Sprite_Shadow.new(viewport, $game_player, shadow)) end end update end def clearShadows - @ombrelist.each { |s| s.dispose if s } + @ombrelist.each { |s| s&.dispose } @ombrelist.clear end - alias shadow_update update + alias shadow_update update unless method_defined?(:shadow_update) def update shadow_update - if @ombrelist.length>0 - for i in 0...@ombrelist.size - @ombrelist[i].update - end - end + @ombrelist.each { |ombre| ombre.update } end end @@ -183,24 +178,24 @@ class Game_Event class Spriteset_Map attr_accessor :shadows - alias shadow_initialize initialize - def initialize(map=nil) + alias shadow_initialize initialize unless private_method_defined?(:shadow_initialize) + + def initialize(map = nil) @shadows = [] warn = false map = $game_map if !map - for k in map.events.keys.sort + map.events.keys.sort.each do |k| ev = map.events[k] - 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 + 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 end if warn == true p "Warning : At least one event on this map uses the obsolete way to add shadows" end shadow_initialize(map) - for sprite in @character_sprites + @character_sprites.each do |sprite| sprite.setShadows(map, @shadows) end $scene.spritesetGlobal.playersprite.setShadows(map, @shadows) @@ -228,31 +223,24 @@ def initialize(map=nil) # 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 - 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 + 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]) end + return parameter_list if max_param_number != 0 && j == i + max_param_number end + end return parameter_list end diff --git a/Data/Scripts/005_Sprites/010_ParticleEngine.rb b/Data/Scripts/005_Sprites/010_ParticleEngine.rb index fca95b724f..8f4d7255ad 100644 --- a/Data/Scripts/005_Sprites/010_ParticleEngine.rb +++ b/Data/Scripts/005_Sprites/010_ParticleEngine.rb @@ -2,36 +2,36 @@ # 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) + 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 + # 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 + @effect.each do |particle| next if particle.nil? particle.dispose end @@ -54,47 +54,46 @@ def remove_effect(event) @effect.delete_at(event.id) end - def realloc_effect(event,particle) + def realloc_effect(event, particle) type = pbEventCommentInput(event, 1, "Particle Engine Type") if type.nil? - particle.dispose if particle + particle&.dispose return nil end type = type[0].downcase cls = @effects[type] if cls.nil? - particle.dispose if particle + particle&.dispose return nil end if !particle || !particle.is_a?(cls) - particle.dispose if particle - particle = cls.new(event,@viewport) + particle&.dispose + particle = cls.new(event, @viewport) end return particle end def pbParticleEffect(event) - return realloc_effect(event,nil) + return realloc_effect(event, nil) end def update if @firsttime @firsttime = false - for event in @map.events.values + @map.events.each_value do |event| remove_effect(event) add_effect(event) end end - for i in 0...@effect.length - particle = @effect[i] + @effect.each_with_index do |particle, i| next if particle.nil? if particle.event.pe_refresh event = particle.event event.pe_refresh = false - particle = realloc_effect(event,particle) + particle = realloc_effect(event, particle) @effect[i] = particle end - particle.update if particle + particle&.update end end end @@ -136,7 +135,7 @@ def initialize(viewport) end def dispose - @sprite.dispose if @sprite + @sprite&.dispose end def bitmap=(value) @@ -153,21 +152,21 @@ def bitmap=(value) def update w = Graphics.width h = Graphics.height - if !@sprite && @x>=@minleft && @y>=@mintop && @x= @minleft && @y >= @mintop && @x < w && @y < h @sprite = Sprite.new(@viewport) - elsif @sprite && (@x<@minleft || @y<@mintop || @x>=w || @y>=h) + 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 = @x if @sprite.x != @x @sprite.x -= @ox - @sprite.y = @y if @sprite.y!=@y + @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 + @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 @@ -177,7 +176,7 @@ def update class ParticleEffect_Event < ParticleEffect attr_accessor :event - def initialize(event,viewport=nil) + def initialize(event, viewport = nil) @event = event @viewport = viewport @particles = [] @@ -185,24 +184,24 @@ def initialize(event,viewport=nil) end def setParameters(params) - @randomhue,@leftright,@fade, - @maxparticless,@hue,@slowdown, - @ytop,@ybottom,@xleft,@xright, - @xgravity,@ygravity,@xoffset,@yoffset, - @opacityvar,@originalopacity = 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] + def loadBitmap(filename, hue) + key = [filename, hue] bitmap = @bitmaps[key] if !bitmap || bitmap.disposed? - bitmap = AnimatedBitmap.new("Graphics/Fogs/"+filename,hue).deanimate + bitmap = AnimatedBitmap.new("Graphics/Fogs/" + filename, hue).deanimate @bitmaps[key] = bitmap end return bitmap end - def initParticles(filename,opacity,zOffset=0,blendtype=1) + def initParticles(filename, opacity, zOffset = 0, blendtype = 1) @particles = [] @particlex = [] @particley = [] @@ -217,20 +216,20 @@ def initParticles(filename,opacity,zOffset=0,blendtype=1) @zoffset = zOffset @bmwidth = 32 @bmheight = 32 - for i in 0...@maxparticless + @maxparticless.times do |i| @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 + 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].z = self.z + zOffset + @opacity[i] = rand(opacity / 4) @particles[i].opacity = @opacity[i] @particles[i].update end @@ -253,29 +252,29 @@ def update 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 + @__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 + if @opacityvar > 0 && @viewport + opac = 255.0 / @opacityvar + minX = (opac * (-@xgravity.to_f / @slowdown).floor) + @startingx + maxX = (opac * (@xgravity.to_f / @slowdown).floor) + @startingx + minY = (opac * (-@ygravity.to_f / @slowdown).floor) + @startingy maxY = @startingy minX -= @bmwidth minY -= @bmheight maxX += @bmwidth maxY += @bmheight - if maxX<0 || maxY<0 || minX>=Graphics.width || minY>=Graphics.height + if maxX < 0 || maxY < 0 || minX >= Graphics.width || minY >= Graphics.height # echo "skipped" return end end - particleZ = selfZ+@zoffset - for i in 0...@maxparticless + particleZ = selfZ + @zoffset + @maxparticless.times do |i| @particles[i].z = particleZ if @particles[i].y <= @ytop @particles[i].y = @startingy + @yoffset @@ -309,14 +308,12 @@ def update @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 + elsif @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 calcParticlePos(i) if @randomhue == 1 @@ -333,26 +330,26 @@ def update def calcParticlePos(i) @leftright = rand(2) if @leftright == 1 - xo = -@xgravity*1.0 / @slowdown + xo = -@xgravity.to_f / @slowdown else - xo = @xgravity*1.0 / @slowdown + xo = @xgravity.to_f / @slowdown end - yo = -@ygravity*1.0 / @slowdown + yo = -@ygravity.to_f / @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 + @particles[i].x = @particlex[i] + @startingx + @xoffset + @particles[i].y = @particley[i] + @startingy + @yoffset end def dispose - for particle in @particles + @particles.each do |particle| particle.dispose end - for bitmap in @bitmaps.values + @bitmaps.each_value do |bitmap| bitmap.dispose end @particles.clear @@ -363,34 +360,34 @@ def dispose class Particle_Engine::Fire < ParticleEffect_Event - def initialize(event,viewport) + 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) + 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) + 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) + 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) + 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 + setParameters([1, 1, 1, 10, rand(360), 1, -64, + Graphics.height, -64, Graphics.width, 0, 3, -8, -15, 20, 0]) + initParticles("wideportal", 250) + @maxparticless.times do |i| @particles[i].ox = 16 @particles[i].oy = 16 end @@ -400,56 +397,56 @@ def initialize(event,viewport) class Particle_Engine::Spirit < ParticleEffect_Event - def initialize(event,viewport) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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 + 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) + @maxparticless.times do |i| @particles[i].blend_type = rand(6) < 3 ? 1 : 2 end end @@ -458,23 +455,23 @@ def initialize(event,viewport) class Particle_Engine::Rocket < ParticleEffect_Event - def initialize(event,viewport) + 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) + 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) + 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 + setParameters([1, 0, 1, 10, rand(360), 1, + -Graphics.height, Graphics.height, 0, Graphics.width, 0, 3, -8, -15, 20, 0]) + initParticles("wideportal", 250) + @maxparticless.times do |i| @particles[i].ox = 16 @particles[i].oy = 16 end @@ -485,12 +482,12 @@ def initialize(event,viewport) # By Peter O. class Particle_Engine::StarTeleport < ParticleEffect_Event - def initialize(event,viewport) + 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 + setParameters([0, 0, 1, 10, 0, 1, + -Graphics.height, Graphics.height, 0, Graphics.width, 0, 3, -8, -15, 10, 0]) + initParticles("star", 250) + @maxparticless.times do |i| @particles[i].ox = 48 @particles[i].oy = 48 end @@ -500,64 +497,64 @@ def initialize(event,viewport) class Particle_Engine::Smokescreen < ParticleEffect_Event - def initialize(event,viewport) + 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 + 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) + @maxparticless.times do |i| rnd = rand(3) - @opacity[i] = (rnd==0) ? 1 : 100 - filename = (rnd==0) ? "explosionsmoke" : "smoke" + @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" + 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) + 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 + @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) + 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) + 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) + 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) + 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 + @maxparticless.times do |i| @particles[i].opacity = 50 @particles[i].update end @@ -569,8 +566,9 @@ def update class Game_Event < Game_Character attr_accessor :pe_refresh - alias nf_particles_game_map_initialize initialize - def initialize(map_id,event,map=nil) + alias nf_particles_game_map_initialize initialize unless private_method_defined?(:nf_particles_game_map_initialize) + + def initialize(map_id, event, map = nil) @pe_refresh = false begin nf_particles_game_map_initialize(map_id, event, map) @@ -579,7 +577,8 @@ def initialize(map_id,event,map=nil) end end - alias nf_particles_game_map_refresh refresh + alias nf_particles_game_map_refresh refresh unless method_defined?(:nf_particles_game_map_refresh) + def refresh nf_particles_game_map_refresh @pe_refresh = true diff --git a/Data/Scripts/005_Sprites/011_PictureEx.rb b/Data/Scripts/005_Sprites/011_PictureEx.rb index 404924d913..db7c588485 100644 --- a/Data/Scripts/005_Sprites/011_PictureEx.rb +++ b/Data/Scripts/005_Sprites/011_PictureEx.rb @@ -1,71 +1,75 @@ class PictureOrigin - TopLeft = 0 - Center = 1 - TopRight = 2 - BottomLeft = 3 - LowerLeft = 3 - BottomRight = 4 - LowerRight = 4 - Top = 5 - Bottom = 6 - Left = 7 - Right = 8 + 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 - 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 + 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] +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 + 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 + 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] + return [cx, cy] end @@ -110,43 +114,47 @@ def initialize(z) @visible = true @blend_type = 0 @name = "" - @origin = PictureOrigin::TopLeft - @src_rect = Rect.new(0,0,-1,-1) + @origin = PictureOrigin::TOP_LEFT + @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) + 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) + def setCallback(delay, cb = nil) delay = ensureDelayAndDuration(delay) - @processes.push([nil,delay,0,0,cb]) + @processes.push([nil, delay, 0, 0, cb]) end def running? - return @processes.length>0 + return @processes.length > 0 end def totalDuration ret = 0 - for process in @processes - dur = process[1]+process[2] - ret = dur if dur>ret + @processes.each do |process| + dur = process[1] + process[2] + ret = dur if dur > ret end - ret *= 20.0/Graphics.frame_rate + 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 + 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 + duration *= Graphics.frame_rate / 20.0 return delay.to_i, duration.to_i end return delay.to_i @@ -162,8 +170,10 @@ def ensureDelay(delay) # 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 = speed * 20.0 / Graphics.frame_rate + while @rotate_speed < 0 + @rotate_speed += 360 + end @rotate_speed %= 360 end @@ -176,8 +186,8 @@ def clearProcesses end def adjustPosition(xOffset, yOffset) - for process in @processes - next if process[0]!=Processes::XY + @processes.each do |process| + next if process[0] != Processes::XY process[5] += xOffset process[6] += yOffset process[7] += xOffset @@ -185,262 +195,262 @@ def adjustPosition(xOffset, 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) + 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]) + 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) + 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]]) + 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]) + def moveDelta(delay, duration, x, y, cb = nil) + delay, duration = ensureDelayAndDuration(delay, duration) + @processes.push([Processes::DELTA_XY, delay, duration, 0, cb, @x, @y, x, y]) end - def setDelta(delay, x, y, cb=nil) - moveDelta(delay,0,x,y,cb) + 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]) + 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) + 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]) + 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) + 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) + 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) + 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]) + 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) + 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]) + 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) + 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]) + 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) + 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]) + 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) + 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]) + 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) + def setOpacity(delay, opacity, cb = nil) + moveOpacity(delay, 0, opacity, cb) end - def setVisible(delay, visible, cb=nil) + def setVisible(delay, visible, cb = nil) delay = ensureDelay(delay) - @processes.push([Processes::Visible,delay,0,0,cb,visible]) + @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) + def setBlendType(delay, blend, cb = nil) delay = ensureDelayAndDuration(delay) - @processes.push([Processes::BlendType,delay,0,0,cb,blend]) + @processes.push([Processes::BLEND_TYPE, delay, 0, 0, cb, blend]) end - def setSE(delay, seFile, volume=nil, pitch=nil, cb=nil) + def setSE(delay, seFile, volume = nil, pitch = nil, cb = nil) delay = ensureDelay(delay) - @processes.push([Processes::SE,delay,0,0,cb,seFile,volume,pitch]) + @processes.push([Processes::SE, delay, 0, 0, cb, seFile, volume, pitch]) end - def setName(delay, name, cb=nil) + def setName(delay, name, cb = nil) delay = ensureDelay(delay) - @processes.push([Processes::Name,delay,0,0,cb,name]) + @processes.push([Processes::NAME, delay, 0, 0, cb, name]) end - def setOrigin(delay, origin, cb=nil) + def setOrigin(delay, origin, cb = nil) delay = ensureDelay(delay) - @processes.push([Processes::Origin,delay,0,0,cb,origin]) + @processes.push([Processes::ORIGIN, delay, 0, 0, cb, origin]) end - def setSrc(delay, srcX, srcY, cb=nil) + def setSrc(delay, srcX, srcY, cb = nil) delay = ensureDelay(delay) - @processes.push([Processes::Src,delay,0,0,cb,srcX,srcY]) + @processes.push([Processes::SRC, delay, 0, 0, cb, srcX, srcY]) end - def setSrcSize(delay, srcWidth, srcHeight, cb=nil) + def setSrcSize(delay, srcWidth, srcHeight, cb = nil) delay = ensureDelay(delay) - @processes.push([Processes::SrcSize,delay,0,0,cb,srcWidth,srcHeight]) + @processes.push([Processes::SRC_SIZE, 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) + def setCropBottom(delay, y, cb = nil) delay = ensureDelay(delay) - @processes.push([Processes::CropBottom,delay,0,0,cb,y]) + @processes.push([Processes::CROP_BOTTOM, delay, 0, 0, cb, y]) end def update procEnded = false @frameUpdates.clear - for i in 0...@processes.length - process = @processes[i] + @processes.each_with_index do |process, i| # Decrease delay of processes that are scheduled to start later - if process[1]>=0 + if process[1] >= 0 # Set initial values if the process will start this frame - if process[1]==0 + if process[1] == 0 case process[0] when Processes::XY process[5] = @x process[6] = @y - when Processes::DeltaXY + when Processes::DELTA_XY process[5] = @x process[6] = @y process[7] += @x process[8] += @y - when Processes::Curve + when Processes::CURVE process[5][0] = @x process[5][1] = @y when Processes::Z process[5] = @z - when Processes::Zoom + when Processes::ZOOM process[5] = @zoom_x process[6] = @zoom_y - when Processes::Angle + when Processes::ANGLE process[5] = @angle - when Processes::Tone + when Processes::TONE process[5] = @tone.clone - when Processes::Color + when Processes::COLOR process[5] = @color.clone - when Processes::Hue + when Processes::HUE process[5] = @hue - when Processes::Opacity + 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 + 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 + 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::XY, Processes::DELTA_XY + @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 + @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 + when Processes::OPACITY + @opacity = process[5] + (fra * (process[6] - process[5]) / dur) + when Processes::VISIBLE @visible = process[5] - when Processes::BlendType + when Processes::BLEND_TYPE @blend_type = process[5] when Processes::SE - pbSEPlay(process[5],process[6],process[7]) - when Processes::Name + pbSEPlay(process[5], process[6], process[7]) + when Processes::NAME @name = process[5] - when Processes::Origin + when Processes::ORIGIN @origin = process[5] - when Processes::Src + when Processes::SRC @src_rect.x = process[5] @src_rect.y = process[6] - when Processes::SrcSize + when Processes::SRC_SIZE @src_rect.width = process[5] @src_rect.height = process[6] - when Processes::CropBottom + when Processes::CROP_BOTTOM @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 + next if process[3] <= process[2] + # Process has ended, erase it + callback(process[4]) if process[4] + @processes[i] = nil + procEnded = true 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) + @frameUpdates.push(Processes::ANGLE) if !@frameUpdates.include?(Processes::ANGLE) @angle += @rotate_speed - while @angle<0; @angle += 360; end + while @angle < 0 + @angle += 360 + end @angle %= 360 end end @@ -451,69 +461,69 @@ def update #=============================================================================== # #=============================================================================== -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 +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 + when Processes::ZOOM sprite.zoom_x = picture.zoom_x / 100.0 sprite.zoom_y = picture.zoom_y / 100.0 - when Processes::Angle + when Processes::ANGLE sprite.angle = picture.angle - when Processes::Tone + when Processes::TONE sprite.tone = picture.tone - when Processes::Color + when Processes::COLOR sprite.color = picture.color - when Processes::Hue + when Processes::HUE # This doesn't do anything. - when Processes::BlendType + when Processes::BLEND_TYPE sprite.blend_type = picture.blend_type - when Processes::Opacity + when Processes::OPACITY sprite.opacity = picture.opacity - when Processes::Visible + when Processes::VISIBLE sprite.visible = picture.visible - when Processes::Name + when Processes::NAME sprite.name = picture.name if iconSprite && sprite.name != picture.name - when Processes::Origin + when Processes::ORIGIN case picture.origin - when PictureOrigin::TopLeft, PictureOrigin::Left, PictureOrigin::BottomLeft + 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::TopRight, PictureOrigin::Right, PictureOrigin::BottomRight + 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::TopLeft, PictureOrigin::Top, PictureOrigin::TopRight + 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::BottomLeft, PictureOrigin::Bottom, PictureOrigin::BottomRight + 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 + 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 + 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 + 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) + setPictureSprite(sprite, picture, true) end diff --git a/Data/Scripts/005_Sprites/012_Interpolators.rb b/Data/Scripts/005_Sprites/012_Interpolators.rb index 567defb504..358aa0fd34 100644 --- a/Data/Scripts/005_Sprites/012_Interpolators.rb +++ b/Data/Scripts/005_Sprites/012_Interpolators.rb @@ -19,31 +19,30 @@ def tweening? return @tweening end - def tween(sprite,items,frames) + def tween(sprite, items, frames) @tweensteps = [] - if sprite && !sprite.disposed? && frames>0 + if sprite && !sprite.disposed? && frames > 0 @frames = frames @step = 0 @sprite = sprite - for item in items + items.each do |item| case item[0] when ZOOM_X - @tweensteps[item[0]] = [sprite.zoom_x,item[1]-sprite.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] + @tweensteps[item[0]] = [sprite.zoom_y, item[1] - sprite.zoom_y] when X - @tweensteps[item[0]] = [sprite.x,item[1]-sprite.x] + @tweensteps[item[0]] = [sprite.x, item[1] - sprite.x] when Y - @tweensteps[item[0]] = [sprite.y,item[1]-sprite.y] + @tweensteps[item[0]] = [sprite.y, item[1] - sprite.y] when OPACITY - @tweensteps[item[0]] = [sprite.opacity,item[1]-sprite.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 - )] + @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 @@ -52,32 +51,30 @@ def tween(sprite,items,frames) def update if @tweening - t = (@step*1.0)/@frames - for i in 0...@tweensteps.length + t = @step.to_f / @frames + @tweensteps.length.times do |i| item = @tweensteps[i] next if !item case i when ZOOM_X - @sprite.zoom_x = item[0]+item[1]*t + @sprite.zoom_x = item[0] + (item[1] * t) when ZOOM_Y - @sprite.zoom_y = item[0]+item[1]*t + @sprite.zoom_y = item[0] + (item[1] * t) when X - @sprite.x = item[0]+item[1]*t + @sprite.x = item[0] + (item[1] * t) when Y - @sprite.y = item[0]+item[1]*t + @sprite.y = item[0] + (item[1] * t) when OPACITY - @sprite.opacity = item[0]+item[1]*t + @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 - ) + @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 + if @step == @frames @step = 0 @frames = 0 @tweening = false @@ -89,46 +86,46 @@ def update class RectInterpolator - def initialize(oldrect,newrect,frames) - restart(oldrect,newrect,frames) + def initialize(oldrect, newrect, frames) + restart(oldrect, newrect, frames) end - def restart(oldrect,newrect,frames) + def restart(oldrect, newrect, frames) @oldrect = oldrect @newrect = newrect - @frames = [frames,1].max + @frames = [frames, 1].max @curframe = 0 @rect = oldrect.clone end def set(rect) - rect.set(@rect.x,@rect.y,@rect.width,@rect.height) + rect.set(@rect.x, @rect.y, @rect.width, @rect.height) end def done? - @curframe>@frames + @curframe > @frames end def update return if done? - t = (@curframe*1.0/@frames) + t = @curframe.to_f / @frames x1 = @oldrect.x x2 = @newrect.x - x = x1+t*(x2-x1) + 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 = xrx ? x : rx - miny = yry ? y : ry - @rect.set(minx,miny,maxx-minx,maxy-miny) + 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 @@ -139,11 +136,11 @@ class PointInterpolator attr_reader :x attr_reader :y - def initialize(oldx,oldy,newx,newy,frames) - restart(oldx,oldy,newx,newy,frames) + def initialize(oldx, oldy, newx, newy, frames) + restart(oldx, oldy, newx, newy, frames) end - def restart(oldx,oldy,newx,newy,frames) + def restart(oldx, oldy, newx, newy, frames) @oldx = oldx @oldy = oldy @newx = newx @@ -155,18 +152,18 @@ def restart(oldx,oldy,newx,newy,frames) end def done? - @curframe>@frames + @curframe > @frames end def update return if done? - t = (@curframe*1.0/@frames) + t = @curframe.to_f / @frames rx1 = @oldx rx2 = @newx - @x = rx1+t*(rx2-rx1) + @x = rx1 + (t * (rx2 - rx1)) ry1 = @oldy ry2 = @newy - @y = ry1+t*(ry2-ry1) + @y = ry1 + (t * (ry2 - ry1)) @curframe += 1 end end diff --git a/Data/Scripts/005_Sprites/013_ScreenPosHelper.rb b/Data/Scripts/005_Sprites/013_ScreenPosHelper.rb index 418bbd9bfd..9ba1747de7 100644 --- a/Data/Scripts/005_Sprites/013_ScreenPosHelper.rb +++ b/Data/Scripts/005_Sprites/013_ScreenPosHelper.rb @@ -1,10 +1,10 @@ module ScreenPosHelper def self.pbScreenZoomX(ch) - return Game_Map::TILE_WIDTH/32.0 + return Game_Map::TILE_WIDTH / 32.0 end def self.pbScreenZoomY(ch) - return Game_Map::TILE_HEIGHT/32.0 + return Game_Map::TILE_HEIGHT / 32.0 end def self.pbScreenX(ch) @@ -15,29 +15,29 @@ def self.pbScreenY(ch) return ch.screen_y end - @heightcache={} + @heightcache = {} def self.bmHeight(bm) - h=@heightcache[bm] + h = @heightcache[bm] if !h - bmap=AnimatedBitmap.new("Graphics/Characters/"+bm,0) - h=bmap.height - @heightcache[bm]=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 + 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 + height = 32 + elsif ch.character_name != "" + height = bmHeight(ch.character_name) / 4 end end - ret=ch.screen_z(height) + ret = ch.screen_z(height) return ret end end diff --git a/Data/Scripts/006_Map renderer/001_TilemapLoader.rb b/Data/Scripts/006_Map renderer/001_TilemapLoader.rb deleted file mode 100644 index 9540718ebd..0000000000 --- a/Data/Scripts/006_Map renderer/001_TilemapLoader.rb +++ /dev/null @@ -1,59 +0,0 @@ -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 diff --git a/Data/Scripts/006_Map renderer/001_TilemapRenderer.rb b/Data/Scripts/006_Map renderer/001_TilemapRenderer.rb new file mode 100644 index 0000000000..5f8d5b2a3b --- /dev/null +++ b/Data/Scripts/006_Map renderer/001_TilemapRenderer.rb @@ -0,0 +1,609 @@ +#=============================================================================== +# +#=============================================================================== +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 + 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" + # Ihe 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 = 0.0 + 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) + if !@current_frames[filename] + set_current_frame(filename) + end + 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] = (@timer / @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 + @timer += Graphics.delta_s + # 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, DISPLAY_TILE_WIDTH, DISPLAY_TILE_HEIGHT) + @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 + @disposed = false + end + + def dispose + return if disposed? + @tiles.each do |col| + col.each do |coord| + coord.each { |tile| tile.dispose } + end + end + @tilesets.bitmaps.each_value { |bitmap| bitmap.dispose } + @autotiles.bitmaps.each_value { |bitmap| bitmap.dispose } + @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; 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 = -100 + elsif tile.bridge && $PokemonGlobal.bridge > 0 + tile.z = 0 + else + priority = tile.priority + tile.z = (priority == 0) ? 0 : (y * DISPLAY_TILE_HEIGHT) + (priority * 32) + 32 + 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 / DISPLAY_TILE_WIDTH + new_tile_offset_y = current_map_display_y / DISPLAY_TILE_HEIGHT + 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 + new_pixel_offset_y = current_map_display_y % SOURCE_TILE_HEIGHT + 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 = @tone } + end + end + @old_color = @color.clone + end + # Recalculate autotile frames + @tilesets.update + @autotiles.update + do_full_refresh = false + 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_y = (map.display_y.to_f / Game_Map::Y_SUBPIXELS).round + 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 + @autotiles.changed = false + end +end diff --git a/Data/Scripts/006_Map renderer/002_CustomTilemap.rb b/Data/Scripts/006_Map renderer/002_CustomTilemap.rb deleted file mode 100644 index dd01df1229..0000000000 --- a/Data/Scripts/006_Map renderer/002_CustomTilemap.rb +++ /dev/null @@ -1,1018 +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) - -# ~Roza/Zoroark -#======================================================================= - -module TileWrap - - TILESET_WIDTH = 0x100 - # 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 - - def self.clamp(val, min, max) - val = max if val > max - val = min if val < min - return val - end - - def self.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 "Tilemap 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 - - columns.times{|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) - } - return bmp - end - - return originalbmp - end - - def self.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 + clamp(src_rect.x,0,TILESET_WIDTH) - ret.y = src_rect.y % MAX_TEX_SIZE - ret.width = clamp(src_rect.width, 0, TILESET_WIDTH - src_rect.x) - ret.height = clamp(src_rect.height, 0, MAX_TEX_SIZE) - return ret - end - - def self.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 - dest.blt(destX, destY, src, srcrect_mod) - else - #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])) - end - end - - def self.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 - -#=============================================================================== -# -#=============================================================================== -class CustomTilemapAutotiles - attr_accessor :changed - - def initialize - @changed = true - @tiles = [nil,nil,nil,nil,nil,nil,nil] - end - - def [](i) - return @tiles[i] - end - - def []=(i,value) - @tiles[i] = value - @changed = true - end -end - - - -class CustomTilemapSprite < Sprite -end - - - -#=============================================================================== -# -#=============================================================================== -class CustomTilemap - attr_reader :tileset - attr_reader :autotiles - attr_reader :map_data - attr_reader :flash_data - attr_reader :priorities - attr_reader :terrain_tags - attr_reader :visible - attr_reader :viewport - attr_reader :graphicsWidth - attr_reader :graphicsHeight - attr_reader :ox - attr_reader :oy - attr_accessor :tone - attr_accessor :color - - Autotiles = [ - [ [27, 28, 33, 34], [ 5, 28, 33, 34], [27, 6, 33, 34], [ 5, 6, 33, 34], - [27, 28, 33, 12], [ 5, 28, 33, 12], [27, 6, 33, 12], [ 5, 6, 33, 12] ], - [ [27, 28, 11, 34], [ 5, 28, 11, 34], [27, 6, 11, 34], [ 5, 6, 11, 34], - [27, 28, 11, 12], [ 5, 28, 11, 12], [27, 6, 11, 12], [ 5, 6, 11, 12] ], - [ [25, 26, 31, 32], [25, 6, 31, 32], [25, 26, 31, 12], [25, 6, 31, 12], - [15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ], - [ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36], - [39, 40, 45, 46], [ 5, 40, 45, 46], [39, 6, 45, 46], [ 5, 6, 45, 46] ], - [ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12], - [17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ], - [ [37, 38, 43, 44], [37, 6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44], - [37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1, 2, 7, 8] ] - ] - Animated_Autotiles_Frames = 5*Graphics.frame_rate/20 # Frequency of updating animated autotiles - FlashOpacity = [100,90,80,70,80,90] - - def initialize(viewport) - @tileset = nil # Refers to Map Tileset Name - @autotiles = CustomTilemapAutotiles.new - @map_data = nil # Refers to 3D Array Of Tile Settings - @flash_data = nil # Refers to 3D Array of Tile Flashdata - @priorities = nil # Refers to Tileset Priorities - @terrain_tags = nil # Refers to Tileset Terrain Tags - @visible = true # Refers to Tileset Visibleness - @ox = 0 # Bitmap Offsets - @oy = 0 # Bitmap Offsets - @plane = false - @haveGraphicsWH = (Graphics.width!=nil rescue false) - if @haveGraphicsWH - @graphicsWidth = Graphics.width - @graphicsHeight = Graphics.height - else - @graphicsWidth = 640 - @graphicsHeight = 480 - end - @tileWidth = Game_Map::TILE_WIDTH rescue 32 - @tileHeight = Game_Map::TILE_HEIGHT rescue 32 - @tileSrcWidth = 32 - @tileSrcHeight = 32 - @diffsizes = (@tileWidth!=@tileSrcWidth) || (@tileHeight!=@tileSrcHeight) - @tone = Tone.new(0,0,0,0) - @oldtone = Tone.new(0,0,0,0) - @color = Color.new(0,0,0,0) - @oldcolor = Color.new(0,0,0,0) - @selfviewport = Viewport.new(0,0,graphicsWidth,graphicsHeight) - @viewport = (viewport) ? viewport : @selfviewport - @tiles = [] - @autotileInfo = [] - @regularTileInfo = [] - @oldOx = 0 - @oldOy = 0 - @oldViewportOx = 0 - @oldViewportOy = 0 - @layer0 = CustomTilemapSprite.new(viewport) - @layer0.visible = true - @nowshown = false - @layer0.bitmap = Bitmap.new([graphicsWidth+320,1].max,[graphicsHeight+320,1].max) - @layer0.z = 0 - @layer0.ox = 0 - @layer0.oy = 0 - @oxLayer0 = 0 - @oyLayer0 = 0 - @flash = nil - @oxFlash = 0 - @oyFlash = 0 - @priotiles = {} - @priotilesfast = [] - @prioautotiles = {} - @autosprites = [] - @framecount = [0,0,0,0,0,0,0,0] # For autotiles - @tilesetChanged = true - @flashChanged = false - @firsttime = true - @disposed = false - @usedsprites = false - @layer0clip = true - @firsttimeflash = true - @fullyrefreshed = false - @fullyrefreshedautos = false - @shouldWrap = false - end - - def dispose - return if disposed? - @help.dispose if @help - @help = nil - i = 0; len = @autotileInfo.length - while i=xsize - xEnd = (@ox+@viewport.rect.width)/@tileWidth + 1 - xEnd = 0 if xEnd<0 - xEnd = xsize-1 if xEnd>=xsize - return false if xStart>=xEnd - ysize = @map_data.ysize - yStart = @oy/@tileHeight - 1 - yStart = 0 if yStart<0 - yStart = ysize-1 if yStart>=ysize - yEnd = (@oy+@viewport.rect.height)/@tileHeight + 1 - yEnd = 0 if yEnd<0 - yEnd = ysize-1 if yEnd>=ysize - return false if yStart>=yEnd - return true - end - - def autotileNumFrames(id) - autotile = @autotiles[id/48-1] - return 0 if !autotile || autotile.disposed? - frames = 1 - if autotile.height==@tileHeight - frames = autotile.width/@tileWidth - else - frames = autotile.width/(3*@tileWidth) - end - return frames - end - - def autotileFrame(id) - autotile = @autotiles[id/48-1] - return -1 if !autotile || autotile.disposed? - frames = 1 - if autotile.height==@tileHeight - frames = autotile.width/@tileWidth - else - frames = autotile.width/(3*@tileWidth) - end - return (Graphics.frame_count/Animated_Autotiles_Frames)%frames - end - - def repaintAutotiles - for i in 0...@autotileInfo.length - next if !@autotileInfo[i] - frame = autotileFrame(i) - @autotileInfo[i].clear - bltAutotile(@autotileInfo[i],0,0,i,frame) - end - end - - def bltAutotile(bitmap,x,y,id,frame) - return if frame<0 - autotile = @autotiles[id/48-1] - return if !autotile || autotile.disposed? - if autotile.height==@tileSrcHeight - anim = frame*@tileSrcWidth - src_rect = Rect.new(anim,0,@tileSrcWidth,@tileSrcHeight) - if @diffsizes - bitmap.stretch_blt(Rect.new(x,y,@tileWidth,@tileHeight),autotile,src_rect) - else - bitmap.blt(x,y,autotile,src_rect) - end - else - anim = frame*3*@tileSrcWidth - id %= 48 - tiles = Autotiles[id>>3][id&7] - src = Rect.new(0,0,0,0) - halfTileWidth = @tileWidth>>1 - halfTileHeight = @tileHeight>>1 - halfTileSrcWidth = @tileSrcWidth>>1 - halfTileSrcHeight = @tileSrcHeight>>1 - for i in 0...4 - tile_position = tiles[i] - 1 - src.set( (tile_position % 6)*halfTileSrcWidth + anim, - (tile_position / 6)*halfTileSrcHeight, halfTileSrcWidth, halfTileSrcHeight) - if @diffsizes - bitmap.stretch_blt( - Rect.new(i%2*halfTileWidth+x,i/2*halfTileHeight+y,halfTileWidth,halfTileHeight), - autotile,src) - else - bitmap.blt(i%2*halfTileWidth+x,i/2*halfTileHeight+y, autotile, src) - end - end - end - end - - def getAutotile(sprite,id) - frames = @framecount[id/48-1] - if frames<=1 - anim = 0 - else - anim = (Graphics.frame_count/Animated_Autotiles_Frames)%frames - end - return if anim<0 - bitmap = @autotileInfo[id] - if !bitmap - bitmap = Bitmap.new(@tileWidth,@tileHeight) - bltAutotile(bitmap,0,0,id,anim) - @autotileInfo[id] = bitmap - end - sprite.bitmap = bitmap if sprite.bitmap!=bitmap - end - - def getRegularTile(sprite,id) - if @diffsizes - bitmap = @regularTileInfo[id] - if !bitmap - bitmap = Bitmap.new(@tileWidth,@tileHeight) - rect = Rect.new(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight, - @tileSrcWidth,@tileSrcHeight) - TileWrap::stretchBlitWrappedPixels(Rect.new(0,0,@tileWidth,@tileHeight), bitmap, @tileset, rect) - @regularTileInfo[id] = bitmap - end - sprite.bitmap = bitmap if sprite.bitmap!=bitmap - else - sprite.bitmap = @tileset if sprite.bitmap!=@tileset - rect = Rect.new(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight, - @tileSrcWidth,@tileSrcHeight) - rect = TileWrap::getWrappedRect(rect) if @shouldWrap - sprite.src_rect = rect - end - end - - def addTile(tiles,count,xpos,ypos,id) - terrain = @terrain_tags[id] - priority = @priorities[id] - if id >= 384 # Tileset tile - if count>=tiles.length - sprite = CustomTilemapSprite.new(@viewport) - tiles.push(sprite,0) - else - sprite = tiles[count] - tiles[count+1] = 0 - end - sprite.visible = @visible - sprite.x = xpos - sprite.y = ypos - sprite.tone = @tone - sprite.color = @color - getRegularTile(sprite,id) - else # Autotile - if count>=tiles.length - sprite = CustomTilemapSprite.new(@viewport) - tiles.push(sprite,1) - else - sprite = tiles[count] - tiles[count+1] = 1 - end - sprite.visible = @visible - sprite.x = xpos - sprite.y = ypos - sprite.tone = @tone - sprite.color = @color - getAutotile(sprite,id) - end - terrain_tag_data = GameData::TerrainTag.try_get(terrain) - if terrain_tag_data.shows_reflections - spriteZ = -100 - elsif $PokemonGlobal.bridge > 0 && terrain_tag_data.bridge - spriteZ = 1 - else - spriteZ = (priority==0) ? 0 : ypos+priority*32+32 - end - sprite.z = spriteZ - count += 2 - return count - end - - def refresh_flash - if @flash_data && !@flash - @flash = CustomTilemapSprite.new(viewport) - @flash.visible = true - @flash.z = 1 - @flash.tone = tone - @flash.color = color - @flash.blend_type = 1 - @flash.bitmap = Bitmap.new([graphicsWidth*2,1].max,[graphicsHeight*2,1].max) - @firsttimeflash = true - elsif !@flash_data && @flash - @flash.bitmap.dispose if @flash.bitmap - @flash.dispose - @flash = nil - @firsttimeflash = false - end - end - - def refreshFlashSprite - return if !@flash || @flash_data.nil? - ptX = @ox-@oxFlash - ptY = @oy-@oyFlash - if !@firsttimeflash && !@usedsprites && - ptX>=0 && ptX+@viewport.rect.width<=@flash.bitmap.width && - ptY>=0 && ptY+@viewport.rect.height<=@flash.bitmap.height - @flash.ox = 0 - @flash.oy = 0 - @flash.src_rect.set(ptX.round,ptY.round, - @viewport.rect.width,@viewport.rect.height) - return - end - width = @flash.bitmap.width - height = @flash.bitmap.height - bitmap = @flash.bitmap - ysize = @map_data.ysize - xsize = @map_data.xsize - @firsttimeflash = false - @oxFlash = @ox-(width>>2) - @oyFlash = @oy-(height>>2) - @flash.ox = 0 - @flash.oy = 0 - @flash.src_rect.set(width>>2,height>>2, - @viewport.rect.width,@viewport.rect.height) - @flash.bitmap.clear - @oxFlash = @oxFlash.floor - @oyFlash = @oyFlash.floor - xStart = @oxFlash/@tileWidth - xStart = 0 if xStart<0 - yStart = @oyFlash/@tileHeight - yStart = 0 if yStart<0 - xEnd = xStart+(width/@tileWidth)+1 - yEnd = yStart+(height/@tileHeight)+1 - xEnd = xsize if xEnd>=xsize - yEnd = ysize if yEnd>=ysize - if xStart>8)&15 - g = (id>>4)&15 - b = (id)&15 - tmpcolor.set(r<<4,g<<4,b<<4) - bitmap.fill_rect(xpos,ypos,@tileWidth,@tileHeight,tmpcolor) - end - end - end - end - - def refresh_tileset - i = 0 - len = @regularTileInfo.length - while i < len - if @regularTileInfo[i] - @regularTileInfo[i].dispose - @regularTileInfo[i] = nil - end - i += 1 - end - @regularTileInfo.clear - @priotiles.clear - ysize = @map_data.ysize - xsize = @map_data.xsize - zsize = @map_data.zsize - if xsize > 100 || ysize > 100 - @fullyrefreshed = false - else - for z in 0...zsize - for y in 0...ysize - for x in 0...xsize - id = @map_data[x, y, z] - next if id == 0 - next if @priorities[id] == 0 && !GameData::TerrainTag.try_get(@terrain_tags[id]).shows_reflections - @priotiles[[x, y]] = [] if !@priotiles[[x, y]] - @priotiles[[x, y]].push([z, id]) - end - end - end - @fullyrefreshed = true - end - end - - def refresh_autotiles - i = 0 - len = @autotileInfo.length - while i < len - if @autotileInfo[i] - @autotileInfo[i].dispose - @autotileInfo[i] = nil - end - i += 1 - end - i = 0 - len = @autosprites.length - while i < len - if @autosprites[i] - @autosprites[i].dispose - @autosprites[i] = nil - end - i += 2 - end - @autosprites.clear - @autotileInfo.clear - @prioautotiles.clear - @priorect = nil - @priorectautos = nil - hasanimated = false - for i in 0...7 - numframes = autotileNumFrames(48 * (i + 1)) - hasanimated = true if numframes >= 2 - @framecount[i] = numframes - end - if hasanimated - ysize = @map_data.ysize - xsize = @map_data.xsize - zsize = @map_data.zsize - if xsize > 100 || ysize > 100 - @fullyrefreshedautos = false - else - for y in 0...ysize - for x in 0...xsize - for z in 0...zsize - id = @map_data[x, y, z] - next if id == 0 || id >= 384 # Skip non-autotiles - next if @priorities[id] != 0 || GameData::TerrainTag.try_get(@terrain_tags[id]).shows_reflections - next if @framecount[id / 48 - 1] < 2 - @prioautotiles[[x, y]] = true - break - end - end - end - @fullyrefreshedautos = true - end - else - @fullyrefreshedautos = true - end - end - - def refreshLayer0(autotiles = false) - return true if autotiles && !shown? - ptX = @ox - @oxLayer0 - ptY = @oy - @oyLayer0 - if !autotiles && !@firsttime && !@usedsprites && - ptX >= 0 && ptX + @viewport.rect.width <= @layer0.bitmap.width && - ptY >= 0 && ptY + @viewport.rect.height <= @layer0.bitmap.height - if @layer0clip && @viewport.ox == 0 && @viewport.oy == 0 - @layer0.ox = 0 - @layer0.oy = 0 - @layer0.src_rect.set(ptX.round, ptY.round, @viewport.rect.width, @viewport.rect.height) - else - @layer0.ox = ptX.round - @layer0.oy = ptY.round - @layer0.src_rect.set(0, 0, @layer0.bitmap.width, @layer0.bitmap.height) - end - return true - end - width = @layer0.bitmap.width - height = @layer0.bitmap.height - bitmap = @layer0.bitmap - ysize = @map_data.ysize - xsize = @map_data.xsize - zsize = @map_data.zsize - twidth = @tileWidth - theight = @tileHeight - mapdata = @map_data - if autotiles - return true if @fullyrefreshedautos && @prioautotiles.length == 0 - xStart = @oxLayer0 / twidth - xStart = 0 if xStart < 0 - yStart = @oyLayer0 / theight - yStart = 0 if yStart < 0 - xEnd = xStart + (width / twidth) + 1 - xEnd = xsize if xEnd > xsize - yEnd = yStart + (height / theight) + 1 - yEnd = ysize if yEnd > ysize - return true if xStart >= xEnd || yStart >= yEnd - trans = Color.new(0, 0, 0, 0) - temprect = Rect.new(0, 0, 0, 0) - tilerect = Rect.new(0, 0, twidth, theight) - zrange = 0...zsize - overallcount = 0 - count = 0 - if !@fullyrefreshedautos - for y in yStart..yEnd - for x in xStart..xEnd - for z in zrange - id = mapdata[x, y, z] - next if !id || id < 48 || id >= 384 # Skip non-autotiles - prioid = @priorities[id] - next if prioid != 0 || GameData::TerrainTag.try_get(@terrain_tags[id]).shows_reflections - fcount = @framecount[id / 48 - 1] - next if !fcount || fcount < 2 - overallcount += 1 - xpos = (x * twidth) - @oxLayer0 - ypos = (y * theight) - @oyLayer0 - bitmap.fill_rect(xpos, ypos, twidth, theight, trans) if overallcount <= 2000 - break - end - for z in zrange - id = mapdata[x, y, z] - next if !id || id < 48 - prioid = @priorities[id] - next if prioid != 0 || GameData::TerrainTag.try_get(@terrain_tags[id]).shows_reflections - if overallcount > 2000 - xpos = (x * twidth) - @oxLayer0 - ypos = (y * theight) - @oyLayer0 - count = addTile(@autosprites, count, xpos, ypos, id) - elsif id >= 384 # Tileset tiles - temprect.set(((id - 384) & 7) * @tileSrcWidth, - ((id - 384) >> 3) * @tileSrcHeight, - @tileSrcWidth, @tileSrcHeight) - xpos = (x * twidth) - @oxLayer0 - ypos = (y * theight) - @oyLayer0 - if @diffsizes - TileWrap::stretchBlitWrappedPixels(Rect.new(xpos, ypos, twidth, theight), bitmap, @tileset, temprect) - else - TileWrap::blitWrappedPixels(xpos,ypos, bitmap, @tileset, temprect) - end - else # Autotiles - tilebitmap = @autotileInfo[id] - if !tilebitmap - anim = autotileFrame(id) - next if anim < 0 - tilebitmap = Bitmap.new(twidth, theight) - bltAutotile(tilebitmap, 0, 0, id, anim) - @autotileInfo[id] = tilebitmap - end - xpos = (x * twidth) - @oxLayer0 - ypos = (y * theight) - @oyLayer0 - bitmap.blt(xpos, ypos, tilebitmap, tilerect) - end - end - end - end - Graphics.frame_reset - else - if !@priorect || !@priorectautos || - @priorect[0] != xStart || @priorect[1] != yStart || - @priorect[2] != xEnd || @priorect[3] != yEnd - @priorect = [xStart, yStart, xEnd, yEnd] - @priorectautos = [] - for y in yStart..yEnd - for x in xStart..xEnd - @priorectautos.push([x, y]) if @prioautotiles[[x, y]] - end - end - end - for tile in @priorectautos - x = tile[0] - y = tile[1] - overallcount += 1 - xpos = (x * twidth) - @oxLayer0 - ypos = (y * theight) - @oyLayer0 - bitmap.fill_rect(xpos, ypos, twidth, theight, trans) - z = 0 - while z < zsize - id = mapdata[x, y, z] - z += 1 - next if !id || id < 48 - prioid = @priorities[id] - next if prioid != 0 || GameData::TerrainTag.try_get(@terrain_tags[id]).shows_reflections - if id >= 384 # Tileset tiles - temprect.set(((id - 384) & 7) * @tileSrcWidth, - ((id - 384) >> 3) * @tileSrcHeight, - @tileSrcWidth, @tileSrcHeight) - if @diffsizes - TileWrap::stretchBlitWrappedPixels(Rect.new(xpos, ypos, twidth, theight), bitmap, @tileset, temprect) - else - TileWrap::blitWrappedPixels(xpos,ypos, bitmap, @tileset, temprect) - end - else # Autotiles - tilebitmap = @autotileInfo[id] - if !tilebitmap - anim = autotileFrame(id) - next if anim < 0 - tilebitmap = Bitmap.new(twidth, theight) - bltAutotile(tilebitmap, 0, 0, id, anim) - @autotileInfo[id] = tilebitmap - end - bitmap.blt(xpos, ypos, tilebitmap, tilerect) - end - end - end - Graphics.frame_reset if overallcount > 500 - end - @usedsprites = false - return true - end - return false if @usedsprites - @firsttime = false - @oxLayer0 = @ox - (width >> 2) - @oyLayer0 = @oy - (height >> 2) - if @layer0clip - @layer0.ox = 0 - @layer0.oy = 0 - @layer0.src_rect.set(width >> 2, height >> 2, @viewport.rect.width, @viewport.rect.height) - else - @layer0.ox = (width >> 2) - @layer0.oy = (height >> 2) - end - @layer0.bitmap.clear - @oxLayer0 = @oxLayer0.round - @oyLayer0 = @oyLayer0.round - xStart = @oxLayer0 / twidth - xStart = 0 if xStart < 0 - yStart = @oyLayer0 / theight - yStart = 0 if yStart < 0 - xEnd = xStart + (width / twidth) + 1 - xEnd = xsize if xEnd >= xsize - yEnd = yStart + (height / theight) + 1 - yEnd = ysize if yEnd >= ysize - if xStart < xEnd && yStart < yEnd - tmprect = Rect.new(0, 0, 0, 0) - yrange = yStart...yEnd - xrange = xStart...xEnd - for z in 0...zsize - for y in yrange - ypos = (y * theight) - @oyLayer0 - for x in xrange - xpos = (x * twidth) - @oxLayer0 - id = mapdata[x, y, z] - next if id == 0 || @priorities[id] != 0 || GameData::TerrainTag.try_get(@terrain_tags[id]).shows_reflections - if id >= 384 # Tileset tiles - tmprect.set(((id - 384) & 7) * @tileSrcWidth, - ((id - 384) >> 3) * @tileSrcHeight, - @tileSrcWidth, @tileSrcHeight) - if @diffsizes - TileWrap::stretchBlitWrappedPixels(Rect.new(xpos, ypos, twidth, theight), bitmap, @tileset, tmprect) - else - TileWrap::blitWrappedPixels(xpos,ypos, bitmap, @tileset, tmprect) - end - else # Autotiles - frames = @framecount[id / 48 - 1] - if frames <= 1 - frame = 0 - else - frame = (Graphics.frame_count / Animated_Autotiles_Frames) % frames - end - bltAutotile(bitmap, xpos, ypos, id, frame) - end - end - end - end - Graphics.frame_reset - end - return true - end - - def refresh(autotiles = false) - @oldOx = @ox - @oldOy = @oy - usesprites = false - if @layer0 - @layer0.visible = @visible - usesprites = !refreshLayer0(autotiles) - return if autotiles && !usesprites - else - usesprites = true - end - refreshFlashSprite - xsize = @map_data.xsize - ysize = @map_data.ysize - minX = (@ox / @tileWidth) - 1 - minX = minX.clamp(0, xsize - 1) - maxX = ((@ox + @viewport.rect.width) / @tileWidth) + 1 - maxX = maxX.clamp(0, xsize - 1) - minY = (@oy / @tileHeight) - 1 - minY = minY.clamp(0, ysize - 1) - maxY = ((@oy + @viewport.rect.height) / @tileHeight) + 1 - maxY = maxY.clamp(0, ysize - 1) - count = 0 - if minX < maxX && minY < maxY - @usedsprites = usesprites || @usedsprites - @layer0.visible = false if usesprites && @layer0 - if !@priotilesrect || !@priotilesfast || - @priotilesrect[0] != minX || @priotilesrect[1] != minY || - @priotilesrect[2] != maxX || @priotilesrect[3] != maxY - @priotilesrect = [minX, minY, maxX, maxY] - @priotilesfast = [] - if @fullyrefreshed - for y in minY..maxY - for x in minX..maxX - next if !@priotiles[[x, y]] - @priotiles[[x, y]].each { |tile| @priotilesfast.push([x, y, tile[0], tile[1]]) } - end - end - else - for z in 0...@map_data.zsize - for y in minY..maxY - for x in minX..maxX - id = @map_data[x, y, z] - next if id == 0 - next if @priorities[id] == 0 && !GameData::TerrainTag.try_get(@terrain_tags[id]).shows_reflections - @priotilesfast.push([x, y, z, id]) - end - end - end - end - end - for prio in @priotilesfast - xpos = (prio[0] * @tileWidth) - @ox - ypos = (prio[1] * @tileHeight) - @oy - count = addTile(@tiles, count, xpos, ypos, prio[3]) - end - end - if count < @tiles.length - bigchange = (count <= (@tiles.length * 2 / 3)) && @tiles.length > 40 - j = count - len = @tiles.length - while j < len - sprite = @tiles[j] - @tiles[j + 1] = -1 - if bigchange - sprite.dispose - @tiles[j] = nil - @tiles[j + 1] = nil - elsif !@tiles[j].disposed? - sprite.visible = false if sprite.visible - end - j += 2 - end - @tiles.compact! if bigchange - end - end - - def update - if @haveGraphicsWH - @graphicsWidth = Graphics.width - @graphicsHeight = Graphics.height - end - # Update tone - if @oldtone != @tone - @layer0.tone = @tone - @flash.tone = @tone if @flash - for sprite in @autosprites - sprite.tone = @tone if sprite.is_a?(Sprite) - end - for sprite in @tiles - sprite.tone = @tone if sprite.is_a?(Sprite) - end - @oldtone = @tone.clone - end - # Update color - if @oldcolor != @color - @layer0.color = @color - @flash.color = @color if @flash - for sprite in @autosprites - sprite.color = @color if sprite.is_a?(Sprite) - end - for sprite in @tiles - sprite.color = @color if sprite.is_a?(Sprite) - end - @oldcolor = @color.clone - end - # Refresh anything that has changed - if @autotiles.changed - refresh_autotiles - repaintAutotiles - end - refresh_flash if @flashChanged - refresh_tileset if @tilesetChanged - @flash.opacity = FlashOpacity[(Graphics.frame_count / 2) % 6] if @flash - mustrefresh = (@oldOx != @ox || @oldOy != @oy || @tilesetChanged || @autotiles.changed) - if @viewport.ox != @oldViewportOx || @viewport.oy != @oldViewportOy - mustrefresh = true - @oldViewportOx = @viewport.ox - @oldViewportOy = @viewport.oy - end - refresh if mustrefresh - if (Graphics.frame_count % Animated_Autotiles_Frames) == 0 || @nowshown - repaintAutotiles - refresh(true) - end - @nowshown = false - @autotiles.changed = false - @tilesetChanged = false - end -end diff --git a/Data/Scripts/006_Map renderer/002_TilesetWrapper.rb b/Data/Scripts/006_Map renderer/002_TilesetWrapper.rb new file mode 100644 index 0000000000..3a15224a84 --- /dev/null +++ b/Data/Scripts/006_Map renderer/002_TilesetWrapper.rb @@ -0,0 +1,96 @@ +#======================================================================= +# 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) + +# ~Roza/Zoroark +#======================================================================= +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 + 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 diff --git a/Data/Scripts/006_Map renderer/003_AutotileExpander.rb b/Data/Scripts/006_Map renderer/003_AutotileExpander.rb new file mode 100644 index 0000000000..731f9f6e6f --- /dev/null +++ b/Data/Scripts/006_Map renderer/003_AutotileExpander.rb @@ -0,0 +1,72 @@ +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 diff --git a/Data/Scripts/006_Map renderer/003_TileDrawingHelper.rb b/Data/Scripts/006_Map renderer/003_TileDrawingHelper.rb deleted file mode 100644 index 80d9e32770..0000000000 --- a/Data/Scripts/006_Map renderer/003_TileDrawingHelper.rb +++ /dev/null @@ -1,228 +0,0 @@ -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 diff --git a/Data/Scripts/006_Map renderer/004_TileDrawingHelper.rb b/Data/Scripts/006_Map renderer/004_TileDrawingHelper.rb new file mode 100644 index 0000000000..d680419802 --- /dev/null +++ b/Data/Scripts/006_Map renderer/004_TileDrawingHelper.rb @@ -0,0 +1,226 @@ +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) + 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 = [] + 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 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) + 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/Pictures/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.disposes + return ret +end diff --git a/Data/Scripts/007_Objects and windows/001_RPG_Cache.rb b/Data/Scripts/007_Objects and windows/001_RPG_Cache.rb index 75785c0dda..ed1d473fcd 100644 --- a/Data/Scripts/007_Objects and windows/001_RPG_Cache.rb +++ b/Data/Scripts/007_Objects and windows/001_RPG_Cache.rb @@ -27,7 +27,7 @@ def self.setKey(key, obj) def self.fromCache(i) return nil if !@cache.include?(i) obj = @cache[i] - return nil if obj && obj.disposed? + return nil if obj&.disposed? return obj end diff --git a/Data/Scripts/007_Objects and windows/002_MessageConfig.rb b/Data/Scripts/007_Objects and windows/002_MessageConfig.rb index 737a2dca2a..7c7a16369e 100644 --- a/Data/Scripts/007_Objects and windows/002_MessageConfig.rb +++ b/Data/Scripts/007_Objects and windows/002_MessageConfig.rb @@ -4,11 +4,14 @@ module MessageConfig DARK_TEXT_MAIN_COLOR = Color.new(80, 80, 88) DARK_TEXT_SHADOW_COLOR = Color.new(160, 160, 168) FONT_NAME = "Power Green" - FONT_SIZE = 29 + FONT_SIZE = 27 + FONT_Y_OFFSET = 8 SMALL_FONT_NAME = "Power Green Small" - SMALL_FONT_SIZE = 25 + SMALL_FONT_SIZE = 21 + SMALL_FONT_Y_OFFSET = 8 NARROW_FONT_NAME = "Power Green Narrow" - NARROW_FONT_SIZE = 29 + NARROW_FONT_SIZE = 27 + NARROW_FONT_Y_OFFSET = 8 # 0 = Pause cursor is displayed at end of text # 1 = Pause cursor is displayed at bottom right # 2 = Pause cursor is displayed at lower middle side @@ -40,39 +43,39 @@ def self.pbDefaultSpeechFrame end def self.pbDefaultWindowskin - skin=($data_system) ? $data_system.windowskin_name : nil - if skin && skin!="" - skin=pbResolveBitmap("Graphics/Windowskins/"+skin) || "" + skin = ($data_system) ? $data_system.windowskin_name : nil + if skin && skin != "" + skin = pbResolveBitmap("Graphics/Windowskins/" + skin) || "" end - skin=pbResolveBitmap("Graphics/System/Window") if nil_or_empty?(skin) - skin=pbResolveBitmap("Graphics/Windowskins/001-Blue01") if nil_or_empty?(skin) + skin = pbResolveBitmap("Graphics/System/Window") if nil_or_empty?(skin) + skin = pbResolveBitmap("Graphics/Windowskins/001-Blue01") if nil_or_empty?(skin) return skin || "" end def self.pbGetSystemFrame if !@@systemFrame - skin=MessageConfig.pbDefaultSystemFrame - skin=MessageConfig.pbDefaultWindowskin if nil_or_empty?(skin) - @@systemFrame=skin || "" + skin = MessageConfig.pbDefaultSystemFrame + skin = MessageConfig.pbDefaultWindowskin if nil_or_empty?(skin) + @@systemFrame = skin || "" end return @@systemFrame end def self.pbGetSpeechFrame if !@@defaultTextSkin - skin=MessageConfig.pbDefaultSpeechFrame - skin=MessageConfig.pbDefaultWindowskin if nil_or_empty?(skin) - @@defaultTextSkin=skin || "" + skin = MessageConfig.pbDefaultSpeechFrame + skin = MessageConfig.pbDefaultWindowskin if nil_or_empty?(skin) + @@defaultTextSkin = skin || "" end return @@defaultTextSkin end def self.pbSetSystemFrame(value) - @@systemFrame=pbResolveBitmap(value) || "" + @@systemFrame = pbResolveBitmap(value) || "" end def self.pbSetSpeechFrame(value) - @@defaultTextSkin=pbResolveBitmap(value) || "" + @@defaultTextSkin = pbResolveBitmap(value) || "" end #----------------------------------------------------------------------------- @@ -82,12 +85,12 @@ def self.pbDefaultTextSpeed end def self.pbGetTextSpeed - @@textSpeed=pbDefaultTextSpeed if !@@textSpeed + @@textSpeed = pbDefaultTextSpeed if !@@textSpeed return @@textSpeed end def self.pbSetTextSpeed(value) - @@textSpeed=value + @@textSpeed = value end def self.pbSettingToTextSpeed(speed) @@ -144,12 +147,13 @@ def self.pbSetNarrowFontName(value) end def self.pbTryFonts(*args) - for a in args + args.each do |a| next if !a - if a.is_a?(String) + case a + when String return a if Font.exist?(a) - elsif a.is_a?(Array) - for aa in a + when Array + a.each do |aa| ret = MessageConfig.pbTryFonts(aa) return ret if ret != "" end @@ -165,110 +169,106 @@ def self.pbTryFonts(*args) # Position a window #=============================================================================== def pbBottomRight(window) - window.x=Graphics.width-window.width - window.y=Graphics.height-window.height + window.x = Graphics.width - window.width + window.y = Graphics.height - window.height end def pbBottomLeft(window) - window.x=0 - window.y=Graphics.height-window.height + window.x = 0 + window.y = Graphics.height - window.height end -def pbBottomLeftLines(window,lines,width=nil) - window.x=0 - window.width=width ? width : Graphics.width - window.height=(window.borderY rescue 32)+lines*32 - window.y=Graphics.height-window.height +def pbBottomLeftLines(window, lines, width = nil) + window.x = 0 + window.width = width || Graphics.width + window.height = (window.borderY rescue 32) + (lines * 32) + window.y = Graphics.height - window.height end -def pbPositionFaceWindow(facewindow,msgwindow) +def pbPositionFaceWindow(facewindow, msgwindow) return if !facewindow if msgwindow - if facewindow.height<=msgwindow.height - facewindow.y=msgwindow.y + if facewindow.height <= msgwindow.height + facewindow.y = msgwindow.y else - facewindow.y=msgwindow.y+msgwindow.height-facewindow.height + facewindow.y = msgwindow.y + msgwindow.height - facewindow.height end - facewindow.x=Graphics.width-facewindow.width - msgwindow.x=0 - msgwindow.width=Graphics.width-facewindow.width + facewindow.x = Graphics.width - facewindow.width + msgwindow.x = 0 + msgwindow.width = Graphics.width - facewindow.width else - facewindow.height=Graphics.height if facewindow.height>Graphics.height - facewindow.x=0 - facewindow.y=0 + facewindow.height = Graphics.height if facewindow.height > Graphics.height + facewindow.x = 0 + facewindow.y = 0 end end -def pbPositionNearMsgWindow(cmdwindow,msgwindow,side) +def pbPositionNearMsgWindow(cmdwindow, msgwindow, side) return if !cmdwindow if msgwindow - height=[cmdwindow.height,Graphics.height-msgwindow.height].min - if cmdwindow.height!=height - cmdwindow.height=height - end - cmdwindow.y=msgwindow.y-cmdwindow.height - if cmdwindow.y<0 - cmdwindow.y=msgwindow.y+msgwindow.height - if cmdwindow.y+cmdwindow.height>Graphics.height - cmdwindow.y=msgwindow.y-cmdwindow.height + height = [cmdwindow.height, Graphics.height - msgwindow.height].min + if cmdwindow.height != height + cmdwindow.height = height + end + cmdwindow.y = msgwindow.y - cmdwindow.height + if cmdwindow.y < 0 + cmdwindow.y = msgwindow.y + msgwindow.height + if cmdwindow.y + cmdwindow.height > Graphics.height + cmdwindow.y = msgwindow.y - cmdwindow.height end end case side when :left - cmdwindow.x=msgwindow.x + cmdwindow.x = msgwindow.x when :right - cmdwindow.x=msgwindow.x+msgwindow.width-cmdwindow.width + cmdwindow.x = msgwindow.x + msgwindow.width - cmdwindow.width else - cmdwindow.x=msgwindow.x+msgwindow.width-cmdwindow.width + cmdwindow.x = msgwindow.x + msgwindow.width - cmdwindow.width end else - cmdwindow.height=Graphics.height if cmdwindow.height>Graphics.height - cmdwindow.x=0 - cmdwindow.y=0 + cmdwindow.height = Graphics.height if cmdwindow.height > Graphics.height + cmdwindow.x = 0 + cmdwindow.y = 0 end end # internal function -def pbRepositionMessageWindow(msgwindow, linecount=2) - msgwindow.height=32*linecount+msgwindow.borderY - msgwindow.y=(Graphics.height)-(msgwindow.height) - if $game_system && $game_system.respond_to?("message_position") +def pbRepositionMessageWindow(msgwindow, linecount = 2) + msgwindow.height = (32 * linecount) + msgwindow.borderY + msgwindow.y = (Graphics.height) - (msgwindow.height) + if $game_system case $game_system.message_position when 0 # up - msgwindow.y=0 + msgwindow.y = 0 when 1 # middle - msgwindow.y=(Graphics.height/2)-(msgwindow.height/2) + msgwindow.y = (Graphics.height / 2) - (msgwindow.height / 2) when 2 - msgwindow.y=(Graphics.height)-(msgwindow.height) - end - end - if $game_system && $game_system.respond_to?("message_frame") - if $game_system.message_frame != 0 - msgwindow.opacity = 0 + msgwindow.y = (Graphics.height) - (msgwindow.height) end + msgwindow.opacity = 0 if $game_system.message_frame != 0 end end # internal function -def pbUpdateMsgWindowPos(msgwindow,event,eventChanged=false) +def pbUpdateMsgWindowPos(msgwindow, event, eventChanged = false) if event if eventChanged - msgwindow.resizeToFit2(msgwindow.text,Graphics.width*2/3,msgwindow.height) + msgwindow.resizeToFit2(msgwindow.text, Graphics.width * 2 / 3, msgwindow.height) end - msgwindow.y=event.screen_y-48-msgwindow.height - if msgwindow.y<0 - msgwindow.y=event.screen_y+24 + msgwindow.y = event.screen_y - 48 - msgwindow.height + if msgwindow.y < 0 + msgwindow.y = event.screen_y + 24 end - msgwindow.x=event.screen_x-(msgwindow.width/2) - msgwindow.x=0 if msgwindow.x<0 - if msgwindow.x>Graphics.width-msgwindow.width - msgwindow.x=Graphics.width-msgwindow.width + msgwindow.x = event.screen_x - (msgwindow.width / 2) + msgwindow.x = 0 if msgwindow.x < 0 + if msgwindow.x > Graphics.width - msgwindow.width + msgwindow.x = Graphics.width - msgwindow.width end else - curwidth=msgwindow.width - if curwidth!=Graphics.width - msgwindow.width=Graphics.width - msgwindow.width=Graphics.width + curwidth = msgwindow.width + if curwidth != Graphics.width + msgwindow.width = Graphics.width + msgwindow.width = Graphics.width end end end @@ -276,24 +276,24 @@ def pbUpdateMsgWindowPos(msgwindow,event,eventChanged=false) #=============================================================================== # Determine the colour of a background #=============================================================================== -def isDarkBackground(background,rect=nil) +def isDarkBackground(background, rect = nil) return true if !background || background.disposed? rect = background.rect if !rect - return true if rect.width<=0 || rect.height<=0 - xSeg = (rect.width/16) - xLoop = (xSeg==0) ? 1 : 16 - xStart = (xSeg==0) ? rect.x+(rect.width/2) : rect.x+xSeg/2 - ySeg = (rect.height/16) - yLoop = (ySeg==0) ? 1 : 16 - yStart = (ySeg==0) ? rect.y+(rect.height/2) : rect.y+ySeg/2 + return true if rect.width <= 0 || rect.height <= 0 + xSeg = (rect.width / 16) + xLoop = (xSeg == 0) ? 1 : 16 + xStart = (xSeg == 0) ? rect.x + (rect.width / 2) : rect.x + (xSeg / 2) + ySeg = (rect.height / 16) + yLoop = (ySeg == 0) ? 1 : 16 + yStart = (ySeg == 0) ? rect.y + (rect.height / 2) : rect.y + (ySeg / 2) count = 0 y = yStart - r = 0; g = 0; b = 0 + r = g = b = 0 yLoop.times do x = xStart xLoop.times do - clr = background.get_pixel(x,y) - if clr.alpha!=0 + clr = background.get_pixel(x, y) + if clr.alpha != 0 r += clr.red g += clr.green b += clr.blue @@ -303,51 +303,51 @@ def isDarkBackground(background,rect=nil) end y += ySeg end - return true if count==0 + return true if count == 0 r /= count g /= count b /= count - return (r*0.299+g*0.587+b*0.114)<160 + return ((r * 0.299) + (g * 0.587) + (b * 0.114)) < 160 end def isDarkWindowskin(windowskin) return true if !windowskin || windowskin.disposed? - if windowskin.width==192 && windowskin.height==128 - return isDarkBackground(windowskin,Rect.new(0,0,128,128)) - elsif windowskin.width==128 && windowskin.height==128 - return isDarkBackground(windowskin,Rect.new(0,0,64,64)) - elsif windowskin.width==96 && windowskin.height==48 - return isDarkBackground(windowskin,Rect.new(32,16,16,16)) + if windowskin.width == 192 && windowskin.height == 128 + return isDarkBackground(windowskin, Rect.new(0, 0, 128, 128)) + elsif windowskin.width == 128 && windowskin.height == 128 + return isDarkBackground(windowskin, Rect.new(0, 0, 64, 64)) + elsif windowskin.width == 96 && windowskin.height == 48 + return isDarkBackground(windowskin, Rect.new(32, 16, 16, 16)) else - clr = windowskin.get_pixel(windowskin.width/2, windowskin.height/2) - return (clr.red*0.299+clr.green*0.587+clr.blue*0.114)<160 + clr = windowskin.get_pixel(windowskin.width / 2, windowskin.height / 2) + return ((clr.red * 0.299) + (clr.green * 0.587) + (clr.blue * 0.114)) < 160 end end #=============================================================================== # Determine which text colours to use based on the darkness of the background #=============================================================================== -def getSkinColor(windowskin,color,isDarkSkin) +def getSkinColor(windowskin, color, isDarkSkin) if !windowskin || windowskin.disposed? || - windowskin.width!=128 || windowskin.height!=128 + windowskin.width != 128 || windowskin.height != 128 # Base color, shadow color (these are reversed on dark windowskins) textcolors = [ - "0070F8","78B8E8", # 1 Blue - "E82010","F8A8B8", # 2 Red - "60B048","B0D090", # 3 Green - "48D8D8","A8E0E0", # 4 Cyan - "D038B8","E8A0E0", # 5 Magenta - "E8D020","F8E888", # 6 Yellow - "A0A0A8","D0D0D8", # 7 Grey - "F0F0F8","C8C8D0", # 8 White - "9040E8","B8A8E0", # 9 Purple - "F89818","F8C898", # 10 Orange - colorToRgb32(MessageConfig::DARK_TEXT_MAIN_COLOR), - colorToRgb32(MessageConfig::DARK_TEXT_SHADOW_COLOR), # 11 Dark default - colorToRgb32(MessageConfig::LIGHT_TEXT_MAIN_COLOR), - colorToRgb32(MessageConfig::LIGHT_TEXT_SHADOW_COLOR) # 12 Light default + "0070F8", "78B8E8", # 1 Blue + "E82010", "F8A8B8", # 2 Red + "60B048", "B0D090", # 3 Green + "48D8D8", "A8E0E0", # 4 Cyan + "D038B8", "E8A0E0", # 5 Magenta + "E8D020", "F8E888", # 6 Yellow + "A0A0A8", "D0D0D8", # 7 Grey + "F0F0F8", "C8C8D0", # 8 White + "9040E8", "B8A8E0", # 9 Purple + "F89818", "F8C898", # 10 Orange + colorToRgb32(MessageConfig::DARK_TEXT_MAIN_COLOR), + colorToRgb32(MessageConfig::DARK_TEXT_SHADOW_COLOR), # 11 Dark default + colorToRgb32(MessageConfig::LIGHT_TEXT_MAIN_COLOR), + colorToRgb32(MessageConfig::LIGHT_TEXT_SHADOW_COLOR) # 12 Light default ] - if color==0 || color>textcolors.length/2 # No special colour, use default + if color == 0 || color > textcolors.length / 2 # No special colour, use default if isDarkSkin # Dark background, light text return shadowc3tag(MessageConfig::LIGHT_TEXT_MAIN_COLOR, MessageConfig::LIGHT_TEXT_SHADOW_COLOR) end @@ -355,15 +355,15 @@ def getSkinColor(windowskin,color,isDarkSkin) return shadowc3tag(MessageConfig::DARK_TEXT_MAIN_COLOR, MessageConfig::DARK_TEXT_SHADOW_COLOR) end # Special colour as listed above - if isDarkSkin && color!=12 # Dark background, light text - return sprintf("",textcolors[2*(color-1)+1],textcolors[2*(color-1)]) + if isDarkSkin && color != 12 # Dark background, light text + return sprintf("", textcolors[(2 * (color - 1)) + 1], textcolors[2 * (color - 1)]) end # Light background, dark text - return sprintf("",textcolors[2*(color-1)],textcolors[2*(color-1)+1]) + return sprintf("", textcolors[2 * (color - 1)], textcolors[(2 * (color - 1)) + 1]) else # VX windowskin - color = 0 if color>=32 - x = 64 + (color % 8) * 8 - y = 96 + (color / 8) * 8 + color = 0 if color >= 32 + x = 64 + ((color % 8) * 8) + y = 96 + ((color / 8) * 8) pixel = windowskin.get_pixel(x, y) return shadowctagFromColor(pixel) end @@ -371,7 +371,7 @@ def getSkinColor(windowskin,color,isDarkSkin) def getDefaultTextColors(windowskin) if !windowskin || windowskin.disposed? || - windowskin.width!=128 || windowskin.height!=128 + windowskin.width != 128 || windowskin.height != 128 if isDarkWindowskin(windowskin) return [MessageConfig::LIGHT_TEXT_MAIN_COLOR, MessageConfig::LIGHT_TEXT_SHADOW_COLOR] # White else @@ -380,26 +380,26 @@ def getDefaultTextColors(windowskin) else # VX windowskin color = windowskin.get_pixel(64, 96) shadow = nil - isDark = (color.red+color.green+color.blue)/3 < 128 + isDark = (color.red + color.green + color.blue) / 3 < 128 if isDark - shadow = Color.new(color.red+64,color.green+64,color.blue+64) + shadow = Color.new(color.red + 64, color.green + 64, color.blue + 64) else - shadow = Color.new(color.red-64,color.green-64,color.blue-64) + shadow = Color.new(color.red - 64, color.green - 64, color.blue - 64) end - return [color,shadow] + return [color, shadow] end end #=============================================================================== # Makes sure a bitmap exists #=============================================================================== -def pbDoEnsureBitmap(bitmap,dwidth,dheight) - if !bitmap || bitmap.disposed? || bitmap.width0 : false + return ($game_temp) ? $game_temp.fadestate > 0 : false end # pbFadeOutIn(z) { block } # Fades out the screen before a block is run and fades it back in after the # block exits. z indicates the z-coordinate of the viewport used for this effect -def pbFadeOutIn(z=99999,nofadeout=false) - col=Color.new(0,0,0,0) - viewport=Viewport.new(0,0,Graphics.width,Graphics.height) - viewport.z=z - numFrames = (Graphics.frame_rate*0.4).floor - alphaDiff = (255.0/numFrames).ceil - for j in 0..numFrames - col.set(0,0,0,j*alphaDiff) - viewport.color=col +def pbFadeOutIn(z = 99999, nofadeout = false) + col = Color.new(0, 0, 0, 0) + viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) + viewport.z = z + numFrames = (Graphics.frame_rate * 0.4).floor + alphaDiff = (255.0 / numFrames).ceil + (0..numFrames).each do |j| + col.set(0, 0, 0, j * alphaDiff) + viewport.color = col Graphics.update Input.update end pbPushFade begin - yield if block_given? + val = 0 + val = yield if block_given? + nofadeout = true if val == 99999 # Ugly hack used by Town Map in the Bag/Pokégear ensure pbPopFade if !nofadeout - for j in 0..numFrames - col.set(0,0,0,(numFrames-j)*alphaDiff) - viewport.color=col + (0..numFrames).each do |j| + col.set(0, 0, 0, (numFrames - j) * alphaDiff) + viewport.color = col Graphics.update Input.update end @@ -578,15 +583,15 @@ def pbFadeOutIn(z=99999,nofadeout=false) end end -def pbFadeOutInWithUpdate(z,sprites,nofadeout=false) - col=Color.new(0,0,0,0) - viewport=Viewport.new(0,0,Graphics.width,Graphics.height) - viewport.z=z - numFrames = (Graphics.frame_rate*0.4).floor - alphaDiff = (255.0/numFrames).ceil - for j in 0..numFrames - col.set(0,0,0,j*alphaDiff) - viewport.color=col +def pbFadeOutInWithUpdate(z, sprites, nofadeout = false) + col = Color.new(0, 0, 0, 0) + viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) + viewport.z = z + numFrames = (Graphics.frame_rate * 0.4).floor + alphaDiff = (255.0 / numFrames).ceil + (0..numFrames).each do |j| + col.set(0, 0, 0, j * alphaDiff) + viewport.color = col pbUpdateSpriteHash(sprites) Graphics.update Input.update @@ -597,9 +602,9 @@ def pbFadeOutInWithUpdate(z,sprites,nofadeout=false) ensure pbPopFade if !nofadeout - for j in 0..numFrames - col.set(0,0,0,(numFrames-j)*alphaDiff) - viewport.color=col + (0..numFrames).each do |j| + col.set(0, 0, 0, (numFrames - j) * alphaDiff) + viewport.color = col pbUpdateSpriteHash(sprites) Graphics.update Input.update @@ -611,31 +616,31 @@ def pbFadeOutInWithUpdate(z,sprites,nofadeout=false) # Similar to pbFadeOutIn, but pauses the music as it fades out. # Requires scripts "Audio" (for bgm_pause) and "SpriteWindow" (for pbFadeOutIn). -def pbFadeOutInWithMusic(zViewport=99999) +def pbFadeOutInWithMusic(zViewport = 99999) playingBGS = $game_system.getPlayingBGS playingBGM = $game_system.getPlayingBGM $game_system.bgm_pause(1.0) $game_system.bgs_pause(1.0) pos = $game_system.bgm_position pbFadeOutIn(zViewport) { - yield - $game_system.bgm_position = pos - $game_system.bgm_resume(playingBGM) - $game_system.bgs_resume(playingBGS) + yield + $game_system.bgm_position = pos + $game_system.bgm_resume(playingBGM) + $game_system.bgs_resume(playingBGS) } end def pbFadeOutAndHide(sprites) visiblesprites = {} - numFrames = (Graphics.frame_rate*0.4).floor - alphaDiff = (255.0/numFrames).ceil + numFrames = (Graphics.frame_rate * 0.4).floor + alphaDiff = (255.0 / numFrames).ceil pbDeactivateWindows(sprites) { - for j in 0..numFrames - pbSetSpritesToColor(sprites,Color.new(0,0,0,j*alphaDiff)) + (0..numFrames).each do |j| + pbSetSpritesToColor(sprites, Color.new(0, 0, 0, j * alphaDiff)) (block_given?) ? yield : pbUpdateSpriteHash(sprites) end } - for i in sprites + sprites.each do |i| next if !i[1] next if pbDisposed?(i[1]) visiblesprites[i[0]] = true if i[1].visible @@ -644,19 +649,19 @@ def pbFadeOutAndHide(sprites) return visiblesprites end -def pbFadeInAndShow(sprites,visiblesprites=nil) +def pbFadeInAndShow(sprites, visiblesprites = nil) if visiblesprites - for i in visiblesprites + visiblesprites.each do |i| if i[1] && sprites[i[0]] && !pbDisposed?(sprites[i[0]]) sprites[i[0]].visible = true end end end - numFrames = (Graphics.frame_rate*0.4).floor - alphaDiff = (255.0/numFrames).ceil + numFrames = (Graphics.frame_rate * 0.4).floor + alphaDiff = (255.0 / numFrames).ceil pbDeactivateWindows(sprites) { - for j in 0..numFrames - pbSetSpritesToColor(sprites,Color.new(0,0,0,((numFrames-j)*alphaDiff))) + (0..numFrames).each do |j| + pbSetSpritesToColor(sprites, Color.new(0, 0, 0, ((numFrames - j) * alphaDiff))) (block_given?) ? yield : pbUpdateSpriteHash(sprites) end } @@ -664,11 +669,11 @@ def pbFadeInAndShow(sprites,visiblesprites=nil) # Restores which windows are active for the given sprite hash. # _activeStatuses_ is the result of a previous call to pbActivateWindows -def pbRestoreActivations(sprites,activeStatuses) +def pbRestoreActivations(sprites, activeStatuses) return if !sprites || !activeStatuses - for k in activeStatuses.keys - if sprites[k] && sprites[k].is_a?(Window) && !pbDisposed?(sprites[k]) - sprites[k].active=activeStatuses[k] ? true : false + activeStatuses.each_key do |k| + if sprites[k].is_a?(Window) && !pbDisposed?(sprites[k]) + sprites[k].active = activeStatuses[k] ? true : false end end end @@ -677,29 +682,29 @@ def pbRestoreActivations(sprites,activeStatuses) # runs the code in the block, and reactivates them. def pbDeactivateWindows(sprites) if block_given? - pbActivateWindow(sprites,nil) { yield } + pbActivateWindow(sprites, nil) { yield } else - pbActivateWindow(sprites,nil) + pbActivateWindow(sprites, nil) end end # Activates a specific window of a sprite hash. _key_ is the key of the window # in the sprite hash. If a code block is given, deactivates all windows except # the specified window, runs the code in the block, and reactivates them. -def pbActivateWindow(sprites,key) +def pbActivateWindow(sprites, key) return if !sprites - activeStatuses={} - for i in sprites - if i[1] && i[1].is_a?(Window) && !pbDisposed?(i[1]) - activeStatuses[i[0]]=i[1].active - i[1].active=(i[0]==key) + activeStatuses = {} + sprites.each do |i| + if i[1].is_a?(Window) && !pbDisposed?(i[1]) + activeStatuses[i[0]] = i[1].active + i[1].active = (i[0] == key) end end if block_given? begin yield ensure - pbRestoreActivations(sprites,activeStatuses) + pbRestoreActivations(sprites, activeStatuses) end return {} else @@ -715,18 +720,18 @@ def pbActivateWindow(sprites,key) # _background_ is a filename within the Graphics/Pictures/ folder and can be # an animated image. # _viewport_ is a viewport to place the background in. -def addBackgroundPlane(sprites,planename,background,viewport=nil) - sprites[planename]=AnimatedPlane.new(viewport) - bitmapName=pbResolveBitmap("Graphics/Pictures/#{background}") - if bitmapName==nil +def addBackgroundPlane(sprites, planename, background, viewport = nil) + sprites[planename] = AnimatedPlane.new(viewport) + bitmapName = pbResolveBitmap("Graphics/Pictures/#{background}") + if bitmapName.nil? # Plane should exist in any case - sprites[planename].bitmap=nil - sprites[planename].visible=false + sprites[planename].bitmap = nil + sprites[planename].visible = false else sprites[planename].setBitmap(bitmapName) - for spr in sprites.values + sprites.each_value do |spr| if spr.is_a?(Window) - spr.windowskin=nil + spr.windowskin = nil end end end @@ -738,17 +743,17 @@ def addBackgroundPlane(sprites,planename,background,viewport=nil) # an animated image. # _color_ is the color to use if the background can't be found. # _viewport_ is a viewport to place the background in. -def addBackgroundOrColoredPlane(sprites,planename,background,color,viewport=nil) - bitmapName=pbResolveBitmap("Graphics/Pictures/#{background}") - if bitmapName==nil +def addBackgroundOrColoredPlane(sprites, planename, background, color, viewport = nil) + bitmapName = pbResolveBitmap("Graphics/Pictures/#{background}") + if bitmapName.nil? # Plane should exist in any case - sprites[planename]=ColoredPlane.new(color,viewport) + sprites[planename] = ColoredPlane.new(color, viewport) else - sprites[planename]=AnimatedPlane.new(viewport) + sprites[planename] = AnimatedPlane.new(viewport) sprites[planename].setBitmap(bitmapName) - for spr in sprites.values + sprites.each_value do |spr| if spr.is_a?(Window) - spr.windowskin=nil + spr.windowskin = nil end end end @@ -772,9 +777,9 @@ def self.height; return 480; end if !defined?(_INTL) def _INTL(*args) - string=args[0].clone - for i in 1...args.length - string.gsub!(/\{#{i}\}/,"#{args[i]}") + string = args[0].clone + (1...args.length).each do |i| + string.gsub!(/\{#{i}\}/, args[i].to_s) end return string end @@ -782,10 +787,10 @@ def _INTL(*args) if !defined?(_ISPRINTF) def _ISPRINTF(*args) - string=args[0].clone - for i in 1...args.length + string = args[0].clone + (1...args.length).each do |i| string.gsub!(/\{#{i}\:([^\}]+?)\}/) { |m| - next sprintf("%"+$1,args[i]) + next sprintf("%" + $1, args[i]) } end return string @@ -794,9 +799,9 @@ def _ISPRINTF(*args) if !defined?(_MAPINTL) def _MAPINTL(*args) - string=args[1].clone - for i in 2...args.length - string.gsub!(/\{#{i}\}/,"#{args[i+1]}") + string = args[1].clone + (2...args.length).each do |i| + string.gsub!(/\{#{i}\}/, args[i + 1].to_s) end return string end diff --git a/Data/Scripts/007_Objects and windows/003_Window.rb b/Data/Scripts/007_Objects and windows/003_Window.rb index 0bd0a6f013..1de8100fbf 100644 --- a/Data/Scripts/007_Objects and windows/003_Window.rb +++ b/Data/Scripts/007_Objects and windows/003_Window.rb @@ -6,7 +6,6 @@ def initialize(window) def empty return unless needs_update?(0, 0, 0, 0) - set(0, 0, 0, 0) end @@ -16,9 +15,7 @@ def empty? def set(x, y, width, height) return unless needs_update?(x, y, width, height) - super(x, y, width, height) - @window.width = @window.width end @@ -78,108 +75,108 @@ def windowskin @_windowskin end - def initialize(viewport=nil) - @sprites={} - @spritekeys=[ - "back", - "corner0","side0","scroll0", - "corner1","side1","scroll1", - "corner2","side2","scroll2", - "corner3","side3","scroll3", - "cursor","contents","pause" + def initialize(viewport = nil) + @sprites = {} + @spritekeys = [ + "back", + "corner0", "side0", "scroll0", + "corner1", "side1", "scroll1", + "corner2", "side2", "scroll2", + "corner3", "side3", "scroll3", + "cursor", "contents", "pause" ] - @sidebitmaps=[nil,nil,nil,nil] - @cursorbitmap=nil - @bgbitmap=nil - @viewport=viewport - for i in @spritekeys - @sprites[i]=Sprite.new(@viewport) + @sidebitmaps = [nil, nil, nil, nil] + @cursorbitmap = nil + @bgbitmap = nil + @viewport = viewport + @spritekeys.each do |i| + @sprites[i] = Sprite.new(@viewport) end - @disposed=false - @tone=Tone.new(0,0,0) - @color=Color.new(0,0,0,0) - @blankcontents=Bitmap.new(1,1) # RGSS2 requires this - @contents=@blankcontents - @_windowskin=nil - @rpgvx=false # Set to true to emulate RPGVX windows - @x=0 - @y=0 - @width=0 - @openness=255 - @height=0 - @ox=0 - @oy=0 - @z=0 - @stretch=true - @visible=true - @active=true - @blend_type=0 - @contents_blend_type=0 - @opacity=255 - @back_opacity=255 - @contents_opacity=255 - @cursor_rect=WindowCursorRect.new(self) - @cursorblink=0 - @cursoropacity=255 - @pause=false - @pauseopacity=255 - @pauseframe=0 + @disposed = false + @tone = Tone.new(0, 0, 0) + @color = Color.new(0, 0, 0, 0) + @blankcontents = Bitmap.new(1, 1) # RGSS2 requires this + @contents = @blankcontents + @_windowskin = nil + @rpgvx = false # Set to true to emulate RPGVX windows + @x = 0 + @y = 0 + @width = 0 + @openness = 255 + @height = 0 + @ox = 0 + @oy = 0 + @z = 0 + @stretch = true + @visible = true + @active = true + @blend_type = 0 + @contents_blend_type = 0 + @opacity = 255 + @back_opacity = 255 + @contents_opacity = 255 + @cursor_rect = WindowCursorRect.new(self) + @cursorblink = 0 + @cursoropacity = 255 + @pause = false + @pauseopacity = 255 + @pauseframe = 0 privRefresh(true) end def dispose if !self.disposed? - for i in @sprites - i[1].dispose if i[1] - @sprites[i[0]]=nil + @sprites.each do |i| + i[1]&.dispose + @sprites[i[0]] = nil end - for i in 0...@sidebitmaps.length - @sidebitmaps[i].dispose if @sidebitmaps[i] - @sidebitmaps[i]=nil + @sidebitmaps.each_with_index do |bitmap, i| + bitmap&.dispose + @sidebitmaps[i] = nil end @blankcontents.dispose - @cursorbitmap.dispose if @cursorbitmap - @backbitmap.dispose if @backbitmap + @cursorbitmap&.dispose + @backbitmap&.dispose @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 - for i in @spritekeys + @viewport = value + @spritekeys.each do |i| @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 @@ -188,417 +185,414 @@ def disposed? end def contents=(value) - @contents=value + @contents = value privRefresh end def windowskin=(value) - @_windowskin=value - if value && value.is_a?(Bitmap) && !value.disposed? && value.width==128 - @rpgvx=true + @_windowskin = value + if 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.empty + if value + @cursor_rect.set(value.x, value.y, value.width, value.height) else - @cursor_rect.set(value.x,value.y,value.width,value.height) + @cursor_rect.empty 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? - for i in @sprites - i[1].flash(color,duration) + @sprites.each do |i| + i[1].flash(color, duration) end end def update return if disposed? - mustchange=false + mustchange = false if @active - if @cursorblink==0 - @cursoropacity-=8 - @cursorblink=1 if @cursoropacity<=128 + if @cursorblink == 0 + @cursoropacity -= 8 + @cursorblink = 1 if @cursoropacity <= 128 else - @cursoropacity+=8 - @cursorblink=0 if @cursoropacity>=255 + @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=(Graphics.frame_count / 8) % 4 - @pauseopacity=[@pauseopacity+64,255].min - mustchange=true + @pauseframe = (Graphics.frame_count / 8) % 4 + @pauseopacity = [@pauseopacity + 64, 255].min + mustchange = true end privRefresh if mustchange - for i in @sprites + @sprites.each do |i| i[1].update end end private - def ensureBitmap(bitmap,dwidth,dheight) - if !bitmap||bitmap.disposed?||bitmap.width 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 - for i in 0...4 - @sprites["corner#{i}"].visible=false - @sprites["side#{i}"].visible=false - @sprites["scroll#{i}"].visible=false + 4.times do |i| + @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 - for i in @sprites - i[1].z=@z + @sprites.each do |i| + 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 - 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) + 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) @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 - 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 + if @openness == 255 + @spritekeys.each do |k| + sprite = @sprites[k] + sprite.zoom_y = 1.0 end else - for k in @spritekeys - sprite=@sprites[k] - sprite.zoom_y=1.0 + 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 end end - i=0 + i = 0 # Ensure Z order - for k in @spritekeys - sprite=@sprites[k] - y=sprite.y - sprite.y=i - sprite.oy=(sprite.zoom_y<=0) ? 0 : (i-y)/sprite.zoom_y + @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 end end end diff --git a/Data/Scripts/007_Objects and windows/004_SpriteWindow.rb b/Data/Scripts/007_Objects and windows/004_SpriteWindow.rb index 0188cd94eb..dfd57fa568 100644 --- a/Data/Scripts/007_Objects and windows/004_SpriteWindow.rb +++ b/Data/Scripts/007_Objects and windows/004_SpriteWindow.rb @@ -48,115 +48,113 @@ module CompatBits attr_reader :compat def compat=(value) - @compat=value + @compat = value privRefresh(true) 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" ] - @viewport=viewport - @sidebitmaps=[nil,nil,nil,nil] - @cursorbitmap=nil - @bgbitmap=nil - 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 - @compat=CompatBits::ExpandBack|CompatBits::StretchSides - @x=0 - @y=0 - @width=0 - @height=0 - @offset_x=0 - @offset_y=0 - @zoom_x=1.0 - @zoom_y=1.0 - @ox=0 - @oy=0 - @z=0 - @stretch=true - @visible=true - @active=true - @openness=255 - @opacity=255 - @back_opacity=255 - @blend_type=0 - @contents_blend_type=0 - @contents_opacity=255 - @cursor_rect=WindowCursorRect.new(self) - @cursorblink=0 - @cursoropacity=255 - @pause=false - @pauseframe=0 - @flash=0 - @pauseopacity=0 - @skinformat=0 - @skinrect=Rect.new(0,0,0,0) - @trim=[16,16,16,16] + @viewport = viewport + @sidebitmaps = [nil, nil, nil, nil] + @cursorbitmap = nil + @bgbitmap = nil + @spritekeys.each do |i| + @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 + @compat = CompatBits::ExpandBack | CompatBits::StretchSides + @x = 0 + @y = 0 + @width = 0 + @height = 0 + @offset_x = 0 + @offset_y = 0 + @zoom_x = 1.0 + @zoom_y = 1.0 + @ox = 0 + @oy = 0 + @z = 0 + @stretch = true + @visible = true + @active = true + @openness = 255 + @opacity = 255 + @back_opacity = 255 + @blend_type = 0 + @contents_blend_type = 0 + @contents_opacity = 255 + @cursor_rect = WindowCursorRect.new(self) + @cursorblink = 0 + @cursoropacity = 255 + @pause = false + @pauseframe = 0 + @flash = 0 + @pauseopacity = 0 + @skinformat = 0 + @skinrect = Rect.new(0, 0, 0, 0) + @trim = [16, 16, 16, 16] privRefresh(true) end def dispose if !self.disposed? - for i in @sprites - i[1].dispose if i[1] - @sprites[i[0]]=nil + @sprites.each do |i| + i[1]&.dispose + @sprites[i[0]] = nil end - for i in 0...@sidebitmaps.length - @sidebitmaps[i].dispose if @sidebitmaps[i] - @sidebitmaps[i]=nil + @sidebitmaps.each_with_index do |bitmap, i| + bitmap&.dispose + @sidebitmaps[i] = nil end @blankcontents.dispose - @cursorbitmap.dispose if @cursorbitmap - @backbitmap.dispose if @backbitmap + @cursorbitmap&.dispose + @backbitmap&.dispose @sprites.clear @sidebitmaps.clear - @_windowskin=nil - @disposed=true + @_windowskin = nil + @disposed = true end 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 - for i in @spritekeys - @sprites[i].dispose if @sprites[i] - end - for i in @spritekeys + @viewport = value + @spritekeys.each do |i| + @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 @@ -165,165 +163,164 @@ def disposed? end def contents=(value) - if @contents!=value - @contents=value + if @contents != value + @contents = value privRefresh if @visible end end def ox=(value) - if @ox!=value - @ox=value + if @ox != value + @ox = value privRefresh if @visible end end def oy=(value) - if @oy!=value - @oy=value + if @oy != value + @oy = value privRefresh if @visible end end def active=(value) - @active=value - privRefresh(true) + @active = value + privRefresh(true) end def cursor_rect=(value) - if !value - @cursor_rect.empty + if value + @cursor_rect.set(value.x, value.y, value.width, value.height) else - @cursor_rect.set(value.x,value.y,value.width,value.height) + @cursor_rect.empty 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 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 if @visible end def x=(value) - @x=value + @x = value privRefresh if @visible end def y=(value) - @y=value + @y = value privRefresh if @visible end def zoom_x=(value) - @zoom_x=value + @zoom_x = value privRefresh if @visible end def zoom_y=(value) - @zoom_y=value + @zoom_y = value privRefresh if @visible end def offset_x=(value) - @x=value + @x = value privRefresh if @visible end def offset_y=(value) - @y=value + @y = value privRefresh if @visible 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 if @visible 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 if @visible 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 if @visible end def tone=(value) - @tone=value + @tone = value privRefresh if @visible end def color=(value) - @color=value + @color = value privRefresh if @visible end def blend_type=(value) - @blend_type=value + @blend_type = value privRefresh if @visible end - def flash(color,duration) + def flash(color, duration) return if disposed? - @flash=duration+1 - for i in @sprites - i[1].flash(color,duration) + @flash = duration + 1 + @sprites.each do |i| + i[1].flash(color, duration) end end def update return if disposed? - mustchange=false + mustchange = false if @active - if @cursorblink==0 - @cursoropacity-=8 - @cursorblink=1 if @cursoropacity<=128 + if @cursorblink == 0 + @cursoropacity -= 8 + @cursorblink = 1 if @cursoropacity <= 128 else - @cursoropacity+=8 - @cursorblink=0 if @cursoropacity>=255 + @cursoropacity += 8 + @cursorblink = 0 if @cursoropacity >= 255 end - privRefreshCursor else - @cursoropacity=128 - privRefreshCursor + @cursoropacity = 128 end + privRefreshCursor if @pause - oldpauseframe=@pauseframe - oldpauseopacity=@pauseopacity - @pauseframe=(Graphics.frame_count / 8) % 4 - @pauseopacity=[@pauseopacity+64,255].min - mustchange=@pauseframe!=oldpauseframe || @pauseopacity!=oldpauseopacity + oldpauseframe = @pauseframe + oldpauseopacity = @pauseopacity + @pauseframe = (Graphics.frame_count / 8) % 4 + @pauseopacity = [@pauseopacity + 64, 255].min + mustchange = @pauseframe != oldpauseframe || @pauseopacity != oldpauseopacity end privRefresh if mustchange - if @flash>0 - for i in @sprites.values + if @flash > 0 + @sprites.each_value do |i| i.update end - @flash-=1 + @flash -= 1 end end @@ -332,489 +329,486 @@ def update attr_reader :skinrect def loadSkinFile(_file) - if (self.windowskin.width==80 || self.windowskin.width==96) && - self.windowskin.height==48 + if (self.windowskin.width == 80 || self.windowskin.width == 96) && + self.windowskin.height == 48 # Body = X, Y, width, height of body rectangle within windowskin - @skinrect.set(32,16,16,16) + @skinrect.set(32, 16, 16, 16) # Trim = X, Y, width, height of trim rectangle within windowskin - @trim=[32,16,16,16] - elsif self.windowskin.width==80 && self.windowskin.height==80 - @skinrect.set(32,32,16,16) - @trim=[32,16,16,48] + @trim = [32, 16, 16, 16] + elsif self.windowskin.width == 80 && self.windowskin.height == 80 + @skinrect.set(32, 32, 16, 16) + @trim = [32, 16, 16, 48] end end def windowskin=(value) - oldSkinWidth=(@_windowskin && !@_windowskin.disposed?) ? @_windowskin.width : -1 - oldSkinHeight=(@_windowskin && !@_windowskin.disposed?) ? @_windowskin.height : -1 - @_windowskin=value - if @skinformat==1 - @rpgvx=false + oldSkinWidth = (@_windowskin && !@_windowskin.disposed?) ? @_windowskin.width : -1 + oldSkinHeight = (@_windowskin && !@_windowskin.disposed?) ? @_windowskin.height : -1 + @_windowskin = value + if @skinformat == 1 + @rpgvx = false if @_windowskin && !@_windowskin.disposed? - if @_windowskin.width!=oldSkinWidth || @_windowskin.height!=oldSkinHeight + if @_windowskin.width != oldSkinWidth || @_windowskin.height != oldSkinHeight # Update skinrect and trim if windowskin's dimensions have changed - @skinrect.set((@_windowskin.width-16)/2,(@_windowskin.height-16)/2,16,16) - @trim=[@skinrect.x,@skinrect.y,@skinrect.x,@skinrect.y] + @skinrect.set((@_windowskin.width - 16) / 2, (@_windowskin.height - 16) / 2, 16, 16) + @trim = [@skinrect.x, @skinrect.y, @skinrect.x, @skinrect.y] end else - @skinrect.set(16,16,16,16) - @trim=[16,16,16,16] + @skinrect.set(16, 16, 16, 16) + @trim = [16, 16, 16, 16] end else - if value && value.is_a?(Bitmap) && !value.disposed? && value.width==128 - @rpgvx=true + if value.is_a?(Bitmap) && !value.disposed? && value.width == 128 + @rpgvx = true else - @rpgvx=false + @rpgvx = false end - @trim=[16,16,16,16] + @trim = [16, 16, 16, 16] end privRefresh(true) end def skinrect=(value) - @skinrect=value + @skinrect = value privRefresh end def skinformat=(value) - if @skinformat!=value - @skinformat=value + if @skinformat != value + @skinformat = value privRefresh(true) end end def borderX - return 32 if !@trim || skinformat==0 + return 32 if !@trim || skinformat == 0 if @_windowskin && !@_windowskin.disposed? - return @trim[0]+(@_windowskin.width-@trim[2]-@trim[0]) + return @trim[0] + (@_windowskin.width - @trim[2] - @trim[0]) end return 32 end def borderY - return 32 if !@trim || skinformat==0 + return 32 if !@trim || skinformat == 0 if @_windowskin && !@_windowskin.disposed? - return @trim[1]+(@_windowskin.height-@trim[3]-@trim[1]) + return @trim[1] + (@_windowskin.height - @trim[3] - @trim[1]) end return 32 end def leftEdge; self.startX; end def topEdge; self.startY; end - def rightEdge; self.borderX-self.leftEdge; end - def bottomEdge; self.borderY-self.topEdge; end + def rightEdge; self.borderX - self.leftEdge; end + def bottomEdge; self.borderY - self.topEdge; end def startX - return !@trim || skinformat==0 ? 16 : @trim[0] + return !@trim || skinformat == 0 ? 16 : @trim[0] end def startY - return !@trim || skinformat==0 ? 16 : @trim[1] + return !@trim || skinformat == 0 ? 16 : @trim[1] end def endX - return !@trim || skinformat==0 ? 16 : @trim[2] + return !@trim || skinformat == 0 ? 16 : @trim[2] end def endY - return !@trim || skinformat==0 ? 16 : @trim[3] + return !@trim || skinformat == 0 ? 16 : @trim[3] end def startX=(value) - @trim[0]=value + @trim[0] = value privRefresh end def startY=(value) - @trim[1]=value + @trim[1] = value privRefresh end def endX=(value) - @trim[2]=value + @trim[2] = value privRefresh end def endY=(value) - @trim[3]=value + @trim[3] = value privRefresh end ############# private - def ensureBitmap(bitmap,dwidth,dheight) - if !bitmap||bitmap.disposed?||bitmap.width0 && @skinformat==0 && !@rpgvx + @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 + @spritekeys.each do |i| + @sprites[i].z = @z + end + if (@compat & CompatBits::CorrectZ) > 0 && @skinformat == 0 && !@rpgvx # Compatibility Mode: Cursor, pause, and contents have higher Z - @sprites["cursor"].z=@z+1 - @sprites["contents"].z=@z+2 - @sprites["pause"].z=@z+2 - end - if @skinformat==0 - startX=16 - startY=16 - endX=16 - endY=16 - trimStartX=16 - trimStartY=16 - trimWidth=32 - trimHeight=32 + @sprites["cursor"].z = @z + 1 + @sprites["contents"].z = @z + 2 + @sprites["pause"].z = @z + 2 + end + if @skinformat == 0 + startX = 16 + startY = 16 + endX = 16 + endY = 16 + trimStartX = 16 + trimStartY = 16 + trimWidth = 32 + trimHeight = 32 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 if @_windowskin && !@_windowskin.disposed? - @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) - ] - pauseRects=[ - trimX+32,trimY+64, - trimX+48,trimY+64, - trimX+32,trimY+80, - trimX+48,trimY+80, - ] - pauseWidth=16 - pauseHeight=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)] + 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 + pauseRects[@pauseframe * 2], + pauseRects[(@pauseframe * 2) + 1], + pauseWidth, pauseHeight ) end else - trimStartX=@trim[0] - trimStartY=@trim[1] - trimWidth=@trim[0]+(@skinrect.width-@trim[2]+@trim[0]) - trimHeight=@trim[1]+(@skinrect.height-@trim[3]+@trim[1]) + trimStartX = @trim[0] + trimStartY = @trim[1] + trimWidth = @trim[0] + (@skinrect.width - @trim[2] + @trim[0]) + trimHeight = @trim[1] + (@skinrect.height - @trim[3] + @trim[1]) if @_windowskin && !@_windowskin.disposed? # width of left end of window - startX=@skinrect.x + startX = @skinrect.x # width of top end of window - startY=@skinrect.y - cx=@skinrect.x+@skinrect.width # right side of BODY rect - cy=@skinrect.y+@skinrect.height # bottom side of BODY rect + startY = @skinrect.y + cx = @skinrect.x + @skinrect.width # right side of BODY rect + cy = @skinrect.y + @skinrect.height # bottom side of BODY rect # width of right end of window - endX=(!@_windowskin || @_windowskin.disposed?) ? @skinrect.x : @_windowskin.width-cx + endX = (!@_windowskin || @_windowskin.disposed?) ? @skinrect.x : @_windowskin.width - cx # height of bottom end of window - endY=(!@_windowskin || @_windowskin.disposed?) ? @skinrect.y : @_windowskin.height-cy - @sprites["corner0"].src_rect.set(0,0,startX,startY); - @sprites["corner1"].src_rect.set(cx,0,endX,startY); - @sprites["corner2"].src_rect.set(0,cy,startX,endY); - @sprites["corner3"].src_rect.set(cx,cy,endX,endY); - backRect=Rect.new(@skinrect.x,@skinrect.y, - @skinrect.width,@skinrect.height); - blindsRect=nil - sideRects=[ - Rect.new(startX,0,@skinrect.width,startY), # side0 (top) - Rect.new(0,startY,startX,@skinrect.height), # side1 (left) - Rect.new(cx,startY,endX,@skinrect.height), # side2 (right) - Rect.new(startX,cy,@skinrect.width,endY) # side3 (bottom) + endY = (!@_windowskin || @_windowskin.disposed?) ? @skinrect.y : @_windowskin.height - cy + @sprites["corner0"].src_rect.set(0, 0, startX, startY) + @sprites["corner1"].src_rect.set(cx, 0, endX, startY) + @sprites["corner2"].src_rect.set(0, cy, startX, endY) + @sprites["corner3"].src_rect.set(cx, cy, endX, endY) + backRect = Rect.new(@skinrect.x, @skinrect.y, @skinrect.width, @skinrect.height) + blindsRect = nil + sideRects = [ + Rect.new(startX, 0, @skinrect.width, startY), # side0 (top) + Rect.new(0, startY, startX, @skinrect.height), # side1 (left) + Rect.new(cx, startY, endX, @skinrect.height), # side2 (right) + Rect.new(startX, cy, @skinrect.width, endY) # side3 (bottom) ] end end - if @width>trimWidth && @height>trimHeight - @sprites["contents"].src_rect.set(@ox,@oy,@width-trimWidth,@height-trimHeight) + if @width > trimWidth && @height > trimHeight + @sprites["contents"].src_rect.set(@ox, @oy, @width - trimWidth, @height - trimHeight) else - @sprites["contents"].src_rect.set(0,0,0,0) - end - @sprites["contents"].x=@x+trimStartX - @sprites["contents"].y=@y+trimStartY - if (@compat & CompatBits::ShowScrollArrows)>0 && @skinformat==0 - # Compatibility mode: Make scroll arrows visible - if @skinformat==0 && @_windowskin && !@_windowskin.disposed? && - @contents && !@contents.disposed? - @sprites["scroll0"].visible = @visible && hascontents && @oy > 0 - @sprites["scroll1"].visible = @visible && hascontents && @ox > 0 - @sprites["scroll2"].visible = @visible && (@contents.width - @ox) > @width-trimWidth - @sprites["scroll3"].visible = @visible && (@contents.height - @oy) > @height-trimHeight - end + @sprites["contents"].src_rect.set(0, 0, 0, 0) + end + @sprites["contents"].x = @x + trimStartX + @sprites["contents"].y = @y + trimStartY + if (@compat & CompatBits::ShowScrollArrows) > 0 && @skinformat == 0 && + @_windowskin && !@_windowskin.disposed? && + @contents && !@contents.disposed? + @sprites["scroll0"].visible = @visible && hascontents && @oy > 0 + @sprites["scroll1"].visible = @visible && hascontents && @ox > 0 + @sprites["scroll2"].visible = @visible && (@contents.width - @ox) > @width - trimWidth + @sprites["scroll3"].visible = @visible && (@contents.height - @oy) > @height - trimHeight end if @_windowskin && !@_windowskin.disposed? - borderX=startX+endX - borderY=startY+endY - @sprites["corner0"].x=@x - @sprites["corner0"].y=@y - @sprites["corner1"].x=@x+@width-endX - @sprites["corner1"].y=@y - @sprites["corner2"].x=@x - @sprites["corner2"].y=@y+@height-endY - @sprites["corner3"].x=@x+@width-endX - @sprites["corner3"].y=@y+@height-endY - @sprites["side0"].x=@x+startX - @sprites["side0"].y=@y - @sprites["side1"].x=@x - @sprites["side1"].y=@y+startY - @sprites["side2"].x=@x+@width-endX - @sprites["side2"].y=@y+startY - @sprites["side3"].x=@x+startX - @sprites["side3"].y=@y+@height-endY - @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["cursor"].x=@x+startX+@cursor_rect.x - @sprites["cursor"].y=@y+startY+@cursor_rect.y - if (@compat & CompatBits::ExpandBack)>0 && @skinformat==0 + borderX = startX + endX + borderY = startY + endY + @sprites["corner0"].x = @x + @sprites["corner0"].y = @y + @sprites["corner1"].x = @x + @width - endX + @sprites["corner1"].y = @y + @sprites["corner2"].x = @x + @sprites["corner2"].y = @y + @height - endY + @sprites["corner3"].x = @x + @width - endX + @sprites["corner3"].y = @y + @height - endY + @sprites["side0"].x = @x + startX + @sprites["side0"].y = @y + @sprites["side1"].x = @x + @sprites["side1"].y = @y + startY + @sprites["side2"].x = @x + @width - endX + @sprites["side2"].y = @y + startY + @sprites["side3"].x = @x + startX + @sprites["side3"].y = @y + @height - endY + @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["cursor"].x = @x + startX + @cursor_rect.x + @sprites["cursor"].y = @y + startY + @cursor_rect.y + if (@compat & CompatBits::ExpandBack) > 0 && @skinformat == 0 # Compatibility mode: Expand background - @sprites["back"].x=@x+2 - @sprites["back"].y=@y+2 + @sprites["back"].x = @x + 2 + @sprites["back"].y = @y + 2 else - @sprites["back"].x=@x+startX - @sprites["back"].y=@y+startY + @sprites["back"].x = @x + startX + @sprites["back"].y = @y + startY end end if changeBitmap && @_windowskin && !@_windowskin.disposed? - if @skinformat==0 - @sprites["cursor"].x=@x+startX+@cursor_rect.x - @sprites["cursor"].y=@y+startY+@cursor_rect.y - width=@cursor_rect.width - height=@cursor_rect.height + if @skinformat == 0 + @sprites["cursor"].x = @x + startX + @cursor_rect.x + @sprites["cursor"].y = @y + startY + @cursor_rect.y + 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) @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 end - for i in 0..3 + 4.times do |i| case i when 0 - dwidth = @width-startX-endX + dwidth = @width - startX - endX dheight = startY when 1 dwidth = startX - dheight = @height-startY-endY + dheight = @height - startY - endY when 2 dwidth = endX - dheight = @height-startY-endY + dheight = @height - startY - endY when 3 - dwidth = @width-startX-endX + dwidth = @width - startX - endX dheight = endY end - @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] = 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 - if (@compat & CompatBits::StretchSides)>0 && @skinformat==0 + if sideRects[i].width > 0 && sideRects[i].height > 0 + if (@compat & CompatBits::StretchSides) > 0 && @skinformat == 0 # Compatibility mode: Stretch sides @sidebitmaps[i].stretch_blt(@sprites["side#{i}"].src_rect, - @_windowskin,sideRects[i]) + @_windowskin, sideRects[i]) else - tileBitmap(@sidebitmaps[i],@sprites["side#{i}"].src_rect, - @_windowskin,sideRects[i]) + tileBitmap(@sidebitmaps[i], @sprites["side#{i}"].src_rect, + @_windowskin, sideRects[i]) end end end - if (@compat & CompatBits::ExpandBack)>0 && @skinformat==0 + if (@compat & CompatBits::ExpandBack) > 0 && @skinformat == 0 # Compatibility mode: Expand background - backwidth=@width-4 - backheight=@height-4 + backwidth = @width - 4 + backheight = @height - 4 else - backwidth=@width-borderX - backheight=@height-borderY + backwidth = @width - borderX + backheight = @height - borderY end - if backwidth>0 && backheight>0 - @backbitmap=ensureBitmap(@backbitmap,backwidth,backheight) - @sprites["back"].bitmap=@backbitmap - @sprites["back"].src_rect.set(0,0,backwidth,backheight) + 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 - 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.zoom_x=1.0 - sprite.oy=0 - sprite.y=(@y+(@height/2.0)+(@height*ratio*opn)-(@height/2*opn)).floor + if @openness == 255 + @spritekeys.each do |k| + sprite = @sprites[k] + sprite.zoom_x = 1.0 + sprite.zoom_y = 1.0 end else - for k in @spritekeys - sprite=@sprites[k] - sprite.zoom_x=1.0 - sprite.zoom_y=1.0 + 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.zoom_x = 1.0 + sprite.oy = 0 + sprite.y = (@y + (@height / 2.0) + (@height * ratio * opn) - (@height / 2 * opn)).floor end end - i=0 + i = 0 # Ensure Z order - for k in @spritekeys - sprite=@sprites[k] - y=sprite.y - sprite.y=i - sprite.oy=(sprite.zoom_y<=0) ? 0 : (i-y)/sprite.zoom_y - sprite.zoom_x*=@zoom_x - sprite.zoom_y*=@zoom_y - sprite.x*=@zoom_x - sprite.y*=@zoom_y - sprite.x+=(@offset_x/sprite.zoom_x) - sprite.y+=(@offset_y/sprite.zoom_y) + @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 + sprite.zoom_x *= @zoom_x + sprite.zoom_y *= @zoom_y + sprite.x *= @zoom_x + sprite.y *= @zoom_y + sprite.x += (@offset_x / sprite.zoom_x) + sprite.y += (@offset_y / sprite.zoom_y) end end end @@ -825,7 +819,7 @@ def privRefresh(changeBitmap=false) # #=============================================================================== class SpriteWindow_Base < SpriteWindow - TEXTPADDING=4 # In pixels + TEXTPADDING = 4 # In pixels def initialize(x, y, width, height) super() @@ -834,89 +828,89 @@ def initialize(x, y, width, height) self.width = width self.height = height self.z = 100 - @curframe=MessageConfig.pbGetSystemFrame() - @curfont=MessageConfig.pbGetSystemFontName() - @sysframe=AnimatedBitmap.new(@curframe) + @curframe = MessageConfig.pbGetSystemFrame + @curfont = MessageConfig.pbGetSystemFontName + @sysframe = AnimatedBitmap.new(@curframe) RPG::Cache.retain(@curframe) if @curframe && !@curframe.empty? - @customskin=nil + @customskin = nil __setWindowskin(@sysframe.bitmap) - __resolveSystemFrame() + __resolveSystemFrame pbSetSystemFont(self.contents) if self.contents end def __setWindowskin(skin) - if skin && (skin.width==192 && skin.height==128) || # RPGXP Windowskin - (skin.width==128 && skin.height==128) # RPGVX Windowskin - self.skinformat=0 + if skin && ((skin.width == 192 && skin.height == 128) || # RPGXP Windowskin + (skin.width == 128 && skin.height == 128)) # RPGVX Windowskin + self.skinformat = 0 else - self.skinformat=1 + self.skinformat = 1 end - self.windowskin=skin + self.windowskin = skin end def __resolveSystemFrame - if self.skinformat==1 + if self.skinformat == 1 if !@resolvedFrame - @resolvedFrame=MessageConfig.pbGetSystemFrame() - @resolvedFrame.sub!(/\.[^\.\/\\]+$/,"") + @resolvedFrame = MessageConfig.pbGetSystemFrame + @resolvedFrame.sub!(/\.[^\.\/\\]+$/, "") end - self.loadSkinFile("#{@resolvedFrame}.txt") if @resolvedFrame!="" + self.loadSkinFile("#{@resolvedFrame}.txt") if @resolvedFrame != "" end end def setSkin(skin) # Filename of windowskin to apply. Supports XP, VX, and animated skins. - @customskin.dispose if @customskin - @customskin=nil - resolvedName=pbResolveBitmap(skin) + @customskin&.dispose + @customskin = nil + resolvedName = pbResolveBitmap(skin) return if nil_or_empty?(resolvedName) - @customskin=AnimatedBitmap.new(resolvedName) + @customskin = AnimatedBitmap.new(resolvedName) RPG::Cache.retain(resolvedName) __setWindowskin(@customskin.bitmap) - if self.skinformat==1 - skinbase=resolvedName.sub(/\.[^\.\/\\]+$/,"") + if self.skinformat == 1 + skinbase = resolvedName.sub(/\.[^\.\/\\]+$/, "") self.loadSkinFile("#{skinbase}.txt") end end def setSystemFrame - @customskin.dispose if @customskin - @customskin=nil + @customskin&.dispose + @customskin = nil __setWindowskin(@sysframe.bitmap) - __resolveSystemFrame() + __resolveSystemFrame end def update super if self.windowskin if @customskin - if @customskin.totalFrames>1 + if @customskin.totalFrames > 1 @customskin.update __setWindowskin(@customskin.bitmap) end elsif @sysframe - if @sysframe.totalFrames>1 + if @sysframe.totalFrames > 1 @sysframe.update __setWindowskin(@sysframe.bitmap) end end end - if @curframe!=MessageConfig.pbGetSystemFrame() - @curframe=MessageConfig.pbGetSystemFrame() + if @curframe != MessageConfig.pbGetSystemFrame + @curframe = MessageConfig.pbGetSystemFrame if @sysframe && !@customskin - @sysframe.dispose if @sysframe - @sysframe=AnimatedBitmap.new(@curframe) + @sysframe&.dispose + @sysframe = AnimatedBitmap.new(@curframe) RPG::Cache.retain(@curframe) if @curframe && !@curframe.empty? - @resolvedFrame=nil + @resolvedFrame = nil __setWindowskin(@sysframe.bitmap) - __resolveSystemFrame() + __resolveSystemFrame end begin refresh rescue NoMethodError end end - if @curfont!=MessageConfig.pbGetSystemFontName() - @curfont=MessageConfig.pbGetSystemFontName() + if @curfont != MessageConfig.pbGetSystemFontName + @curfont = MessageConfig.pbGetSystemFontName if self.contents && !self.contents.disposed? pbSetSystemFont(self.contents) end @@ -928,9 +922,9 @@ def update end def dispose - self.contents.dispose if self.contents + self.contents&.dispose @sysframe.dispose - @customskin.dispose if @customskin + @customskin&.dispose super end end diff --git a/Data/Scripts/007_Objects and windows/005_SpriteWindow_text.rb b/Data/Scripts/007_Objects and windows/005_SpriteWindow_text.rb index 624971ed7c..e09de47dbd 100644 --- a/Data/Scripts/007_Objects and windows/005_SpriteWindow_text.rb +++ b/Data/Scripts/007_Objects and windows/005_SpriteWindow_text.rb @@ -1,7 +1,7 @@ #=============================================================================== # #=============================================================================== -# Represents a window with no formatting capabilities. Its text color can be set, +# Represents a window with no formatting capabilities. Its text color can be set, # though, and line breaks are supported, but the text is generally unformatted. class Window_UnformattedTextPokemon < SpriteWindow_Base attr_reader :text @@ -11,65 +11,65 @@ class Window_UnformattedTextPokemon < SpriteWindow_Base attr_accessor :letterbyletter def text=(value) - @text=value + @text = value refresh end def baseColor=(value) - @baseColor=value + @baseColor = value refresh end def shadowColor=(value) - @shadowColor=value + @shadowColor = value refresh end - def initialize(text="") - super(0,0,33,33) - self.contents=Bitmap.new(1,1) + def initialize(text = "") + super(0, 0, 33, 33) + self.contents = Bitmap.new(1, 1) pbSetSystemFont(self.contents) - @text=text - @letterbyletter=false # Not supported in this class - colors=getDefaultTextColors(self.windowskin) - @baseColor=colors[0] - @shadowColor=colors[1] + @text = text + @letterbyletter = false # Not supported in this class + colors = getDefaultTextColors(self.windowskin) + @baseColor = colors[0] + @shadowColor = colors[1] resizeToFit(text) end - def self.newWithSize(text,x,y,width,height,viewport=nil) - ret=self.new(text) - ret.x=x - ret.y=y - ret.width=width - ret.height=height - ret.viewport=viewport + def self.newWithSize(text, x, y, width, height, viewport = nil) + ret = self.new(text) + ret.x = x + ret.y = y + ret.width = width + ret.height = height + ret.viewport = viewport ret.refresh return ret end - def resizeToFitInternal(text,maxwidth) # maxwidth is maximum acceptable window width - dims=[0,0] - cwidth=maxwidth<0 ? Graphics.width : maxwidth - getLineBrokenChunks(self.contents,text, - cwidth-self.borderX-SpriteWindow_Base::TEXTPADDING,dims,true) + def resizeToFitInternal(text, maxwidth) # maxwidth is maximum acceptable window width + dims = [0, 0] + cwidth = maxwidth < 0 ? Graphics.width : maxwidth + getLineBrokenChunks(self.contents, text, + cwidth - self.borderX - SpriteWindow_Base::TEXTPADDING, dims, true) return dims end - def setTextToFit(text,maxwidth=-1) - resizeToFit(text,maxwidth) - self.text=text + def setTextToFit(text, maxwidth = -1) + resizeToFit(text, maxwidth) + self.text = text end def resizeToFit(text, maxwidth = -1) # maxwidth is maximum acceptable window width - dims = resizeToFitInternal(text,maxwidth) + dims = resizeToFitInternal(text, maxwidth) self.width = dims[0] + self.borderX + SpriteWindow_Base::TEXTPADDING self.height = dims[1] + self.borderY refresh end def resizeHeightToFit(text, width = -1) # width is current window width - dims = resizeToFitInternal(text,width) + dims = resizeToFitInternal(text, width) self.width = (width < 0) ? Graphics.width : width self.height = dims[1] + self.borderY refresh @@ -89,20 +89,20 @@ def setSkin(skin) colors = getDefaultTextColors(self.windowskin) @baseColor = colors[0] @shadowColor = colors[1] - if oldbaser!=@baseColor.red || oldbaseg!=@baseColor.green || - oldbaseb!=@baseColor.blue || oldbasea!=@baseColor.alpha || - oldshadowr!=@shadowColor.red || oldshadowg!=@shadowColor.green || - oldshadowb!=@shadowColor.blue || oldshadowa!=@shadowColor.alpha + if oldbaser != @baseColor.red || oldbaseg != @baseColor.green || + oldbaseb != @baseColor.blue || oldbasea != @baseColor.alpha || + oldshadowr != @shadowColor.red || oldshadowg != @shadowColor.green || + oldshadowb != @shadowColor.blue || oldshadowa != @shadowColor.alpha self.text = self.text end end def refresh - self.contents=pbDoEnsureBitmap(self.contents,self.width-self.borderX, - self.height-self.borderY) + self.contents = pbDoEnsureBitmap(self.contents, self.width - self.borderX, + self.height - self.borderY) self.contents.clear - drawTextEx(self.contents,0,4,self.contents.width,0, - @text.gsub(/\r/,""),@baseColor,@shadowColor) + drawTextEx(self.contents, 0, -2, self.contents.width, 0, # TEXT OFFSET + @text.gsub(/\r/, ""), @baseColor, @shadowColor) end end @@ -118,7 +118,7 @@ class Window_AdvancedTextPokemon < SpriteWindow_Base attr_accessor :letterbyletter attr_reader :waitcount - def initialize(text="") + def initialize(text = "") @cursorMode = MessageConfig::CURSOR_POSITION @endOfText = nil @scrollstate = 0 @@ -134,13 +134,13 @@ def initialize(text="") @lastDrawnChar = -1 @fmtchars = [] @frameskipChanged = false - @frameskip = MessageConfig.pbGetTextSpeed() - super(0,0,33,33) + @frameskip = MessageConfig.pbGetTextSpeed + super(0, 0, 33, 33) @pausesprite = nil @text = "" - self.contents = Bitmap.new(1,1) + self.contents = Bitmap.new(1, 1) pbSetSystemFont(self.contents) - self.resizeToFit(text,Graphics.width) + self.resizeToFit(text, Graphics.width) colors = getDefaultTextColors(self.windowskin) @baseColor = colors[0] @shadowColor = colors[1] @@ -148,7 +148,7 @@ def initialize(text="") @starting = false end - def self.newWithSize(text,x,y,width,height,viewport=nil) + def self.newWithSize(text, x, y, width, height, viewport = nil) ret = self.new(text) ret.x = x ret.y = y @@ -160,13 +160,13 @@ def self.newWithSize(text,x,y,width,height,viewport=nil) def dispose return if disposed? - @pausesprite.dispose if @pausesprite + @pausesprite&.dispose @pausesprite = nil super end def waitcount=(value) - @waitcount = (value<=0) ? 0 : value + @waitcount = (value <= 0) ? 0 : value end attr_reader :cursorMode @@ -196,7 +196,7 @@ def textspeed end def textspeed=(value) - @frameskipChanged = true if @frameskip!=value + @frameskipChanged = true if @frameskip != value @frameskip = value end @@ -210,40 +210,40 @@ def height=(value) self.text = self.text if !@starting end - def resizeToFit(text,maxwidth=-1) - dims = resizeToFitInternal(text,maxwidth) + def resizeToFit(text, maxwidth = -1) + dims = resizeToFitInternal(text, maxwidth) oldstarting = @starting @starting = true - self.width = dims[0]+self.borderX+SpriteWindow_Base::TEXTPADDING - self.height = dims[1]+self.borderY + self.width = dims[0] + self.borderX + SpriteWindow_Base::TEXTPADDING + self.height = dims[1] + self.borderY @starting = oldstarting redrawText end - def resizeToFit2(text,maxwidth,maxheight) - dims = resizeToFitInternal(text,maxwidth) + def resizeToFit2(text, maxwidth, maxheight) + dims = resizeToFitInternal(text, maxwidth) oldstarting = @starting @starting = true - self.width = [dims[0]+self.borderX+SpriteWindow_Base::TEXTPADDING,maxwidth].min - self.height = [dims[1]+self.borderY,maxheight].min + self.width = [dims[0] + self.borderX + SpriteWindow_Base::TEXTPADDING, maxwidth].min + self.height = [dims[1] + self.borderY, maxheight].min @starting = oldstarting redrawText end - def resizeToFitInternal(text,maxwidth) - dims = [0,0] - cwidth = (maxwidth<0) ? Graphics.width : maxwidth - chars = getFormattedTextForDims(self.contents,0,0, - cwidth-self.borderX-2-6,-1,text,@lineHeight,true) - for ch in chars - dims[0] = [dims[0],ch[1]+ch[3]].max - dims[1] = [dims[1],ch[2]+ch[4]].max + def resizeToFitInternal(text, maxwidth) + dims = [0, 0] + cwidth = (maxwidth < 0) ? Graphics.width : maxwidth + chars = getFormattedTextForDims(self.contents, 0, 0, + cwidth - self.borderX - 2 - 6, -1, text, @lineHeight, true) + chars.each do |ch| + dims[0] = [dims[0], ch[1] + ch[3]].max + dims[1] = [dims[1], ch[2] + ch[4]].max end return dims end - def resizeHeightToFit(text,width=-1) - dims = resizeToFitInternal(text,width) + def resizeHeightToFit(text, width = -1) + dims = resizeToFitInternal(text, width) oldstarting = @starting @starting = true self.width = (width < 0) ? Graphics.width : width @@ -252,7 +252,7 @@ def resizeHeightToFit(text,width=-1) redrawText end - def setSkin(skin,redrawText=true) + def setSkin(skin, redrawText = true) super(skin) privRefresh(true) oldbaser = @baseColor.red @@ -267,16 +267,16 @@ def setSkin(skin,redrawText=true) @baseColor = colors[0] @shadowColor = colors[1] if redrawText && - (oldbaser!=@baseColor.red || oldbaseg!=@baseColor.green || - oldbaseb!=@baseColor.blue || oldbasea!=@baseColor.alpha || - oldshadowr!=@shadowColor.red || oldshadowg!=@shadowColor.green || - oldshadowb!=@shadowColor.blue || oldshadowa!=@shadowColor.alpha) + (oldbaser != @baseColor.red || oldbaseg != @baseColor.green || + oldbaseb != @baseColor.blue || oldbasea != @baseColor.alpha || + oldshadowr != @shadowColor.red || oldshadowg != @shadowColor.green || + oldshadowb != @shadowColor.blue || oldshadowa != @shadowColor.alpha) setText(self.text) end end - def setTextToFit(text,maxwidth=-1) - resizeToFit(text,maxwidth) + def setTextToFit(text, maxwidth = -1) + resizeToFit(text, maxwidth) self.text = text end @@ -299,8 +299,8 @@ def setText(value) width = 1 height = 1 numlines = 0 - visiblelines = (self.height-self.borderY)/32 - if value.length==0 + visiblelines = (self.height - self.borderY) / 32 + if value.length == 0 @fmtchars = [] @bitmapwidth = width @bitmapheight = height @@ -308,19 +308,18 @@ def setText(value) else if @letterbyletter @fmtchars = [] - fmt = getFormattedText(self.contents,0,0, - self.width-self.borderX-SpriteWindow_Base::TEXTPADDING,-1, - shadowctag(@baseColor,@shadowColor)+value,32,true) + fmt = getFormattedText(self.contents, 0, 0, + self.width - self.borderX - SpriteWindow_Base::TEXTPADDING, -1, + shadowctag(@baseColor, @shadowColor) + value, 32, true) @oldfont = self.contents.font.clone - for ch in fmt - chx = ch[1]+ch[3] - chy = ch[2]+ch[4] - width = chx if width=visiblelines + if numlines >= visiblelines fclone = ch.clone fclone[0] = "\1" @fmtchars.push(fclone) @@ -329,23 +328,22 @@ def setText(value) end # Don't add newline characters, since they # can slow down letter-by-letter display - if ch[5] || (ch[0]!="\r") + if ch[5] || (ch[0] != "\r") @fmtchars.push(ch) @textchars.push(ch[5] ? "" : ch[0]) end end fmt.clear else - @fmtchars = getFormattedText(self.contents,0,0, - self.width-self.borderX-SpriteWindow_Base::TEXTPADDING,-1, - shadowctag(@baseColor,@shadowColor)+value,32,true) + @fmtchars = getFormattedText(self.contents, 0, 0, + self.width - self.borderX - SpriteWindow_Base::TEXTPADDING, -1, + shadowctag(@baseColor, @shadowColor) + value, 32, true) @oldfont = self.contents.font.clone - for ch in @fmtchars - chx = ch[1]+ch[3] - chy = ch[2]+ch[4] - width = chx if width=@fmtchars.length + return 0 if @lastDrawnChar < 0 + return @numtextchars if @lastDrawnChar >= @fmtchars.length # index after the last character's index - return @fmtchars[@lastDrawnChar][14]+1 + return @fmtchars[@lastDrawnChar][14] + 1 end def maxPosition pos = 0 - for ch in @fmtchars + @fmtchars.each do |ch| # index after the last character's index - pos = ch[14]+1 if pos=@fmtchars.length # End of message - if @textchars[@curchar]=="\1" # Pause message - @pausing = true if @curchar<@numtextchars-1 + break if @curchar >= @fmtchars.length # End of message + if @textchars[@curchar] == "\1" # Pause message + @pausing = true if @curchar < @numtextchars - 1 self.startPause refresh break end - break if @textchars[@curchar]!="\n" # Skip past newlines only - break if @linesdrawn>=visiblelines-1 # No more empty lines to continue to + break if @textchars[@curchar] != "\n" # Skip past newlines only + break if @linesdrawn >= visiblelines - 1 # No more empty lines to continue to @linesdrawn += 1 end end def allocPause return if @pausesprite - @pausesprite = AnimatedSprite.create("Graphics/Pictures/pause",4,3) + @pausesprite = AnimatedSprite.create("Graphics/Pictures/pause", 4, 3) @pausesprite.z = 100000 @pausesprite.visible = false end @@ -441,31 +439,31 @@ def stopPause def moveCursor return if !@pausesprite cursor = @cursorMode - cursor = 2 if cursor==0 && !@endOfText + cursor = 2 if cursor == 0 && !@endOfText case cursor when 0 # End of text - @pausesprite.x = self.x+self.startX+@endOfText.x+@endOfText.width-2 - @pausesprite.y = self.y+self.startY+@endOfText.y-@scrollY + @pausesprite.x = self.x + self.startX + @endOfText.x + @endOfText.width - 2 + @pausesprite.y = self.y + self.startY + @endOfText.y - @scrollY when 1 # Lower right pauseWidth = @pausesprite.bitmap ? @pausesprite.framewidth : 16 pauseHeight = @pausesprite.bitmap ? @pausesprite.frameheight : 16 - @pausesprite.x = self.x+self.width-(20*2)+(pauseWidth/2) - @pausesprite.y = self.y+self.height-(30*2)+(pauseHeight/2) + @pausesprite.x = self.x + self.width - (20 * 2) + (pauseWidth / 2) + @pausesprite.y = self.y + self.height - (30 * 2) + (pauseHeight / 2) when 2 # Lower middle pauseWidth = @pausesprite.bitmap ? @pausesprite.framewidth : 16 pauseHeight = @pausesprite.bitmap ? @pausesprite.frameheight : 16 - @pausesprite.x = self.x+(self.width/2)-(pauseWidth/2) - @pausesprite.y = self.y+self.height-(18*2)+(pauseHeight/2) + @pausesprite.x = self.x + (self.width / 2) - (pauseWidth / 2) + @pausesprite.y = self.y + self.height - (18 * 2) + (pauseHeight / 2) end end def refresh oldcontents = self.contents - self.contents = pbDoEnsureBitmap(oldcontents,@bitmapwidth,@bitmapheight) + self.contents = pbDoEnsureBitmap(oldcontents, @bitmapwidth, @bitmapheight) self.oy = @scrollY numchars = @numtextchars - numchars = [@curchar,@numtextchars].min if self.letterbyletter - return if busy? && @drawncurchar==@curchar && @scrollstate==0 + numchars = [@curchar, @numtextchars].min if self.letterbyletter + return if busy? && @drawncurchar == @curchar && @scrollstate == 0 if !self.letterbyletter || !oldcontents.equal?(self.contents) @drawncurchar = -1 @needclear = true @@ -479,31 +477,29 @@ def refresh @nodraw = false return end - maxX = self.width-self.borderX - maxY = self.height-self.borderY - for i in @drawncurchar+1..numchars - next if i>=@fmtchars.length + maxX = self.width - self.borderX + maxY = self.height - self.borderY + (@drawncurchar + 1..numchars).each do |i| + next if i >= @fmtchars.length if !self.letterbyletter - next if @fmtchars[i][1]>=maxX - next if @fmtchars[i][2]>=maxY + next if @fmtchars[i][1] >= maxX + next if @fmtchars[i][2] >= maxY end - drawSingleFormattedChar(self.contents,@fmtchars[i]) + drawSingleFormattedChar(self.contents, @fmtchars[i]) @lastDrawnChar = i end - if !self.letterbyletter - # all characters were drawn, reset old font - self.contents.font = @oldfont if @oldfont - end - if numchars>0 && numchars!=@numtextchars - fch = @fmtchars[numchars-1] + # all characters were drawn, reset old font + self.contents.font = @oldfont if !self.letterbyletter && @oldfont + if numchars > 0 && numchars != @numtextchars + fch = @fmtchars[numchars - 1] if fch - rcdst = Rect.new(fch[1],fch[2],fch[3],fch[4]) - if @textchars[numchars]=="\1" + rcdst = Rect.new(fch[1], fch[2], fch[3], fch[4]) + if @textchars[numchars] == "\1" @endOfText = rcdst allocPause moveCursor else - @endOfText = Rect.new(rcdst.x+rcdst.width,rcdst.y,8,1) + @endOfText = Rect.new(rcdst.x + rcdst.width, rcdst.y, 8, 1) end end end @@ -514,8 +510,8 @@ def redrawText if @letterbyletter oldPosition = self.position self.text = self.text - oldPosition = @numtextchars if oldPosition>@numtextchars - while self.position!=oldPosition + oldPosition = @numtextchars if oldPosition > @numtextchars + while self.position != oldPosition refresh updateInternal end @@ -525,47 +521,47 @@ def redrawText end def updateInternal - curcharskip = @frameskip<0 ? @frameskip.abs : 1 - visiblelines = (self.height-self.borderY)/@lineHeight - if @textchars[@curchar]=="\1" + curcharskip = @frameskip < 0 ? @frameskip.abs : 1 + visiblelines = (self.height - self.borderY) / @lineHeight + if @textchars[@curchar] == "\1" if !@pausing @realframes += 1 - if @realframes>=@frameskip || @frameskip<0 + if @realframes >= @frameskip || @frameskip < 0 curcharSkip(curcharskip) @realframes = 0 end end - elsif @textchars[@curchar]=="\n" - if @linesdrawn>=visiblelines-1 - if @scrollstate<@lineHeight - @scrollstate += [(@lineHeight/4),1].max - @scrollY += [(@lineHeight/4),1].max + elsif @textchars[@curchar] == "\n" + if @linesdrawn >= visiblelines - 1 + if @scrollstate < @lineHeight + @scrollstate += [(@lineHeight / 4), 1].max + @scrollY += [(@lineHeight / 4), 1].max end - if @scrollstate>=@lineHeight + if @scrollstate >= @lineHeight @realframes += 1 - if @realframes>=@frameskip || @frameskip<0 + if @realframes >= @frameskip || @frameskip < 0 curcharSkip(curcharskip) - @linesdrawn += 1 + @linesdrawn += 1 @realframes = 0 @scrollstate = 0 end end else @realframes += 1 - if @realframes>=@frameskip || @frameskip<0 + if @realframes >= @frameskip || @frameskip < 0 curcharSkip(curcharskip) @linesdrawn += 1 @realframes = 0 end end - elsif @curchar<=@numtextchars + elsif @curchar <= @numtextchars @realframes += 1 - if @realframes>=@frameskip || @frameskip<0 + if @realframes >= @frameskip || @frameskip < 0 curcharSkip(curcharskip) @realframes = 0 end - if @textchars[@curchar]=="\1" - @pausing = true if @curchar<@numtextchars-1 + if @textchars[@curchar] == "\1" + @pausing = true if @curchar < @numtextchars - 1 self.startPause refresh end @@ -579,8 +575,8 @@ def updateInternal def update super - @pausesprite.update if @pausesprite && @pausesprite.visible - if @waitcount>0 + @pausesprite.update if @pausesprite&.visible + if @waitcount > 0 @waitcount -= 1 return end @@ -598,10 +594,10 @@ def update def curcharSkip(skip) skip.times do @curchar += 1 - break if @textchars[@curchar]=="\n" || # newline - @textchars[@curchar]=="\1" || # pause - @textchars[@curchar]=="\2" || # letter-by-letter break - @textchars[@curchar]==nil + break if @textchars[@curchar] == "\n" || # newline + @textchars[@curchar] == "\1" || # pause + @textchars[@curchar] == "\2" || # letter-by-letter break + @textchars[@curchar].nil? end end end @@ -615,19 +611,19 @@ class Window_InputNumberPokemon < SpriteWindow_Base attr_reader :sign def initialize(digits_max) - @digits_max=digits_max - @number=0 - @frame=0 - @sign=false - @negative=false - super(0,0,32,32) - self.width=digits_max*24+8+self.borderX - self.height=32+self.borderY - colors=getDefaultTextColors(self.windowskin) - @baseColor=colors[0] - @shadowColor=colors[1] - @index=digits_max-1 - self.active=true + @digits_max = digits_max + @number = 0 + @frame = 0 + @sign = false + @negative = false + super(0, 0, 32, 32) + self.width = (digits_max * 24) + 8 + self.borderX + self.height = 32 + self.borderY + colors = getDefaultTextColors(self.windowskin) + @baseColor = colors[0] + @shadowColor = colors[1] + @index = digits_max - 1 + self.active = true refresh end @@ -637,89 +633,92 @@ def active=(value) end def number - @number*(@sign && @negative ? -1 : 1) + @number * (@sign && @negative ? -1 : 1) end def number=(value) - value=0 if !value.is_a?(Numeric) + value = 0 if !value.is_a?(Numeric) if @sign - @negative=(value<0) - @number = [value.abs, 10 ** @digits_max - 1].min + @negative = (value < 0) + @number = [value.abs, (10**@digits_max) - 1].min else - @number = [[value, 0].max, 10 ** @digits_max - 1].min + @number = [[value, 0].max, (10**@digits_max) - 1].min end refresh end def sign=(value) - @sign=value - self.width=@digits_max*24+8+self.borderX+(@sign ? 24 : 0) - @index=(@digits_max-1)+(@sign ? 1 : 0) + @sign = value + self.width = (@digits_max * 24) + 8 + self.borderX + (@sign ? 24 : 0) + @index = (@digits_max - 1) + (@sign ? 1 : 0) refresh end def refresh - self.contents=pbDoEnsureBitmap(self.contents, - self.width-self.borderX,self.height-self.borderY) + self.contents = pbDoEnsureBitmap(self.contents, + self.width - self.borderX, self.height - self.borderY) pbSetSystemFont(self.contents) self.contents.clear - s=sprintf("%0*d",@digits_max,@number.abs) + s = sprintf("%0*d", @digits_max, @number.abs) if @sign - textHelper(0,0,@negative ? "-" : "+",0) + textHelper(0, 0, @negative ? "-" : "+", 0) end - for i in 0...@digits_max - index=i+(@sign ? 1 : 0) - textHelper(index*24,0,s[i,1],index) + @digits_max.times do |i| + index = i + (@sign ? 1 : 0) + textHelper(index * 24, 0, s[i, 1], index) end end def update super - digits=@digits_max+(@sign ? 1 : 0) - refresh if @frame%15==0 + digits = @digits_max + (@sign ? 1 : 0) + refresh if @frame % 15 == 0 if self.active if Input.repeat?(Input::UP) || Input.repeat?(Input::DOWN) - pbPlayCursorSE() - if @index==0 && @sign - @negative=!@negative + pbPlayCursorSE + if @index == 0 && @sign + @negative = !@negative else - place = 10 ** (digits - 1 - @index) + place = 10**(digits - 1 - @index) n = @number / place % 10 - @number -= n*place + @number -= n * place if Input.repeat?(Input::UP) n = (n + 1) % 10 elsif Input.repeat?(Input::DOWN) n = (n + 9) % 10 end - @number += n*place + @number += n * place end refresh elsif Input.repeat?(Input::RIGHT) if digits >= 2 - pbPlayCursorSE() + pbPlayCursorSE @index = (@index + 1) % digits - @frame=0 + @frame = 0 refresh end elsif Input.repeat?(Input::LEFT) if digits >= 2 - pbPlayCursorSE() + pbPlayCursorSE @index = (@index + digits - 1) % digits - @frame=0 + @frame = 0 refresh end end end - @frame=(@frame+1)%30 + @frame = (@frame + 1) % 30 end private - def textHelper(x,y,text,i) - textwidth=self.contents.text_size(text).width - pbDrawShadowText(self.contents, x+(12-textwidth/2), y, textwidth+4, 32, text, @baseColor, @shadowColor) - if @index==i && @active && @frame/15==0 - self.contents.fill_rect(x+(12-textwidth/2), y+30, textwidth, 2, @baseColor) + def textHelper(x, y, text, i) + textwidth = self.contents.text_size(text).width + pbDrawShadowText(self.contents, + x + (12 - (textwidth / 2)), + y - 2 + (self.contents.text_offset_y || 0), # TEXT OFFSET (the - 2) + textwidth + 4, 32, text, @baseColor, @shadowColor) + if @index == i && @active && @frame / 15 == 0 + self.contents.fill_rect(x + (12 - (textwidth / 2)), y + 30, textwidth, 2, @baseColor) end end end @@ -731,12 +730,13 @@ def textHelper(x,y,text,i) #=============================================================================== class SpriteWindow_Selectable < SpriteWindow_Base attr_reader :index + attr_writer :ignore_input def initialize(x, y, width, height) super(x, y, width, height) @item_max = 1 @column_max = 1 - @virtualOy=0 + @virtualOy = 2 # TEXT OFFSET @index = -1 @row_height = 32 @column_spacing = 32 @@ -748,7 +748,7 @@ def itemCount end def index=(index) - if @index!=index + if @index != index @index = index priv_update_cursor_rect(true) end @@ -759,10 +759,10 @@ def rowHeight end def rowHeight=(value) - if @row_height!=value - oldTopRow=self.top_row - @row_height=[1,value].max - self.top_row=oldTopRow + if @row_height != value + oldTopRow = self.top_row + @row_height = [1, value].max + self.top_row = oldTopRow update_cursor_rect end end @@ -772,8 +772,8 @@ def columns end def columns=(value) - if @column_max!=value - @column_max=[1,value].max + if @column_max != value + @column_max = [1, value].max update_cursor_rect end end @@ -783,16 +783,12 @@ def columnSpacing end def columnSpacing=(value) - if @column_spacing!=value - @column_spacing=[0,value].max + if @column_spacing != value + @column_spacing = [0, value].max update_cursor_rect end end - def ignore_input=(value) - @ignore_input=value - end - def count return @item_max end @@ -806,9 +802,9 @@ def top_row end def top_row=(row) - row = row_max-1 if row>row_max-1 - row = 0 if row<0 - @virtualOy = row*@row_height + row = row_max - 1 if row > row_max - 1 + row = 0 if row < 0 + @virtualOy = (row * @row_height) + 2 # TEXT OFFSET (the + 2) end def top_item @@ -824,13 +820,13 @@ def page_item_max end def itemRect(item) - if item<0 || item>=@item_max || itemself.top_item+self.page_item_max - return Rect.new(0,0,0,0) + if item < 0 || item >= @item_max || item < self.top_item || + item > self.top_item + self.page_item_max + return Rect.new(0, 0, 0, 0) else - cursor_width = (self.width-self.borderX-(@column_max-1)*@column_spacing) / @column_max + cursor_width = (self.width - self.borderX - ((@column_max - 1) * @column_spacing)) / @column_max x = item % @column_max * (cursor_width + @column_spacing) - y = item / @column_max * @row_height - @virtualOy + y = (item / @column_max * @row_height) - @virtualOy return Rect.new(x, y, cursor_width, @row_height) end end @@ -846,21 +842,21 @@ def update if self.active && @item_max > 0 && @index >= 0 && !@ignore_input if Input.repeat?(Input::UP) if @index >= @column_max || - (Input.trigger?(Input::UP) && (@item_max % @column_max)==0) + (Input.trigger?(Input::UP) && (@item_max % @column_max) == 0) oldindex = @index @index = (@index - @column_max + @item_max) % @item_max - if @index!=oldindex - pbPlayCursorSE() + if @index != oldindex + pbPlayCursorSE update_cursor_rect end end elsif Input.repeat?(Input::DOWN) if @index < @item_max - @column_max || - (Input.trigger?(Input::DOWN) && (@item_max % @column_max)==0) + (Input.trigger?(Input::DOWN) && (@item_max % @column_max) == 0) oldindex = @index @index = (@index + @column_max) % @item_max - if @index!=oldindex - pbPlayCursorSE() + if @index != oldindex + pbPlayCursorSE update_cursor_rect end end @@ -868,8 +864,8 @@ def update if @column_max >= 2 && @index > 0 oldindex = @index @index -= 1 - if @index!=oldindex - pbPlayCursorSE() + if @index != oldindex + pbPlayCursorSE update_cursor_rect end end @@ -877,27 +873,27 @@ def update if @column_max >= 2 && @index < @item_max - 1 oldindex = @index @index += 1 - if @index!=oldindex - pbPlayCursorSE() + if @index != oldindex + pbPlayCursorSE update_cursor_rect end end elsif Input.repeat?(Input::JUMPUP) if @index > 0 oldindex = @index - @index = [self.index-self.page_item_max, 0].max - if @index!=oldindex - pbPlayCursorSE() + @index = [self.index - self.page_item_max, 0].max + if @index != oldindex + pbPlayCursorSE self.top_row -= self.page_row_max update_cursor_rect end end elsif Input.repeat?(Input::JUMPDOWN) - if @index < @item_max-1 + if @index < @item_max - 1 oldindex = @index - @index = [self.index+self.page_item_max, @item_max-1].min - if @index!=oldindex - pbPlayCursorSE() + @index = [self.index + self.page_item_max, @item_max - 1].min + if @index != oldindex + pbPlayCursorSE self.top_row += self.page_row_max update_cursor_rect end @@ -916,7 +912,7 @@ def priv_page_item_max return (self.height - self.borderY) / @row_height * @column_max end - def priv_update_cursor_rect(force=false) + def priv_update_cursor_rect(force = false) if @index < 0 self.cursor_rect.empty self.refresh @@ -940,16 +936,16 @@ def priv_update_cursor_rect(force=false) # self.top_row = [self.top_row, self.row_max - self.page_row_max].min # This code makes the cursor stay in the middle of the visible list as much # as possible. - new_top_row = row - ((self.page_row_max - 1)/2).floor + new_top_row = row - ((self.page_row_max - 1) / 2).floor new_top_row = [[new_top_row, self.row_max - self.page_row_max].min, 0].max if self.top_row != new_top_row self.top_row = new_top_row # dorefresh = true end # End of code - cursor_width = (self.width-self.borderX) / @column_max + cursor_width = (self.width - self.borderX) / @column_max x = self.index % @column_max * (cursor_width + @column_spacing) - y = self.index / @column_max * @row_height - @virtualOy + y = (self.index / @column_max * @row_height) - @virtualOy self.cursor_rect.set(x, y, cursor_width, @row_height) self.refresh if dorefresh || force end @@ -962,8 +958,8 @@ def priv_update_cursor_rect(force=false) #=============================================================================== module UpDownArrowMixin def initUpDownArrow - @uparrow = AnimatedSprite.create("Graphics/Pictures/uparrow",8,2,self.viewport) - @downarrow = AnimatedSprite.create("Graphics/Pictures/downarrow",8,2,self.viewport) + @uparrow = AnimatedSprite.create("Graphics/Pictures/uparrow", 8, 2, self.viewport) + @downarrow = AnimatedSprite.create("Graphics/Pictures/downarrow", 8, 2, self.viewport) RPG::Cache.retain("Graphics/Pictures/uparrow") RPG::Cache.retain("Graphics/Pictures/downarrow") @uparrow.z = 99998 @@ -995,22 +991,22 @@ def color=(value) def adjustForZoom(sprite) sprite.zoom_x = self.zoom_x sprite.zoom_y = self.zoom_y - sprite.x = sprite.x*self.zoom_x + self.offset_x/self.zoom_x - sprite.y = sprite.y*self.zoom_y + self.offset_y/self.zoom_y + sprite.x = (sprite.x * self.zoom_x) + (self.offset_x / self.zoom_x) + sprite.y = (sprite.y * self.zoom_y) + (self.offset_y / self.zoom_y) end def update super - @uparrow.x = self.x+(self.width/2)-(@uparrow.framewidth/2) - @downarrow.x = self.x+(self.width/2)-(@downarrow.framewidth/2) + @uparrow.x = self.x + (self.width / 2) - (@uparrow.framewidth / 2) + @downarrow.x = self.x + (self.width / 2) - (@downarrow.framewidth / 2) @uparrow.y = self.y - @downarrow.y = self.y+self.height-@downarrow.frameheight - @uparrow.visible = self.visible && self.active && (self.top_item!=0 && + @downarrow.y = self.y + self.height - @downarrow.frameheight + @uparrow.visible = self.visible && self.active && (self.top_item != 0 && @item_max > self.page_item_max) @downarrow.visible = self.visible && self.active && - (self.top_item+self.page_item_max<@item_max && @item_max > self.page_item_max) - @uparrow.z = self.z+1 - @downarrow.z = self.z+1 + (self.top_item + self.page_item_max < @item_max && @item_max > self.page_item_max) + @uparrow.z = self.z + 1 + @downarrow.z = self.z + 1 adjustForZoom(@uparrow) adjustForZoom(@downarrow) @uparrow.viewport = self.viewport @@ -1043,8 +1039,8 @@ class Window_DrawableCommand < SpriteWindow_SelectableEx attr_reader :baseColor attr_reader :shadowColor - def initialize(x,y,width,height,viewport=nil) - super(x,y,width,height) + def initialize(x, y, width, height, viewport = nil) + super(x, y, width, height) self.viewport = viewport if viewport if isDarkWindowskin(self.windowskin) @selarrow = AnimatedBitmap.new("Graphics/Pictures/selarrow_white") @@ -1075,30 +1071,30 @@ def shadowColor=(value) refresh end - def textWidth(bitmap,text) + def textWidth(bitmap, text) return bitmap.text_size(text).width end - def getAutoDims(commands,dims,width=nil) + def getAutoDims(commands, dims, width = nil) rowMax = ((commands.length + self.columns - 1) / self.columns).to_i - windowheight = (rowMax*self.rowHeight) + windowheight = (rowMax * self.rowHeight) windowheight += self.borderY - if !width || width<0 - width=0 - tmpbitmap = BitmapWrapper.new(1,1) + if !width || width < 0 + width = 0 + tmpbitmap = BitmapWrapper.new(1, 1) pbSetSystemFont(tmpbitmap) - for i in commands - width = [width,tmpbitmap.text_size(i).width].max + commands.each do |i| + width = [width, tmpbitmap.text_size(i).width].max end # one 16 to allow cursor - width += 16+16+SpriteWindow_Base::TEXTPADDING + width += 16 + 16 + SpriteWindow_Base::TEXTPADDING tmpbitmap.dispose end # Store suggested width and height of window - dims[0] = [self.borderX+1,(width*self.columns)+self.borderX+ - (self.columns-1)*self.columnSpacing].max - dims[1] = [self.borderY+1,windowheight].max - dims[1] = [dims[1],Graphics.height].min + dims[0] = [self.borderX + 1, + (width * self.columns) + self.borderX + ((self.columns - 1) * self.columnSpacing)].max + dims[1] = [self.borderY + 1, windowheight].max + dims[1] = [dims[1], Graphics.height].min end def setSkin(skin) @@ -1109,36 +1105,35 @@ def setSkin(skin) @shadowColor = colors[1] end - def drawCursor(index,rect) - if self.index==index - pbCopyBitmap(self.contents,@selarrow.bitmap,rect.x,rect.y) + def drawCursor(index, rect) + if self.index == index + pbCopyBitmap(self.contents, @selarrow.bitmap, rect.x, rect.y + 2) # TEXT OFFSET (counters the offset above) end - return Rect.new(rect.x+16,rect.y,rect.width-16,rect.height) + return Rect.new(rect.x + 16, rect.y, rect.width - 16, rect.height) end def itemCount # to be implemented by derived classes return 0 end - def drawItem(index,count,rect) # to be implemented by derived classes - end + def drawItem(index, count, rect); end # to be implemented by derived classes def refresh - @item_max = itemCount() - dwidth = self.width-self.borderX - dheight = self.height-self.borderY - self.contents = pbDoEnsureBitmap(self.contents,dwidth,dheight) + @item_max = itemCount + dwidth = self.width - self.borderX + dheight = self.height - self.borderY + self.contents = pbDoEnsureBitmap(self.contents, dwidth, dheight) self.contents.clear - for i in 0...@item_max - next if iself.top_item+self.page_item_max - drawItem(i,@item_max,itemRect(i)) + @item_max.times do |i| + next if i < self.top_item || i > self.top_item + self.page_item_max + drawItem(i, @item_max, itemRect(i)) end end def update oldindex = self.index super - refresh if self.index!=oldindex + refresh if self.index != oldindex end end @@ -1150,40 +1145,40 @@ def update class Window_CommandPokemon < Window_DrawableCommand attr_reader :commands - def initialize(commands,width=nil) - @starting=true - @commands=[] - dims=[] - super(0,0,32,32) - getAutoDims(commands,dims,width) - self.width=dims[0] - self.height=dims[1] - @commands=commands - self.active=true - colors=getDefaultTextColors(self.windowskin) - self.baseColor=colors[0] - self.shadowColor=colors[1] + def initialize(commands, width = nil) + @starting = true + @commands = [] + dims = [] + super(0, 0, 32, 32) + getAutoDims(commands, dims, width) + self.width = dims[0] + self.height = dims[1] + @commands = commands + self.active = true + colors = getDefaultTextColors(self.windowskin) + self.baseColor = colors[0] + self.shadowColor = colors[1] refresh - @starting=false + @starting = false end - def self.newWithSize(commands,x,y,width,height,viewport=nil) - ret=self.new(commands,width) - ret.x=x - ret.y=y - ret.width=width - ret.height=height - ret.viewport=viewport + def self.newWithSize(commands, x, y, width, height, viewport = nil) + ret = self.new(commands, width) + ret.x = x + ret.y = y + ret.width = width + ret.height = height + ret.viewport = viewport return ret end - def self.newEmpty(x,y,width,height,viewport=nil) - ret=self.new([],width) - ret.x=x - ret.y=y - ret.width=width - ret.height=height - ret.viewport=viewport + def self.newEmpty(x, y, width, height, viewport = nil) + ret = self.new([], width) + ret.x = x + ret.y = y + ret.width = width + ret.height = height + ret.viewport = viewport return ret end @@ -1193,8 +1188,8 @@ def index=(value) end def commands=(value) - @commands=value - @item_max=commands.length + @commands = value + @item_max = commands.length self.update_cursor_rect self.refresh end @@ -1202,7 +1197,7 @@ def commands=(value) def width=(value) super if !@starting - self.index=self.index + self.index = self.index self.update_cursor_rect end end @@ -1210,27 +1205,27 @@ def width=(value) def height=(value) super if !@starting - self.index=self.index + self.index = self.index self.update_cursor_rect end end - def resizeToFit(commands,width=nil) - dims=[] - getAutoDims(commands,dims,width) - self.width=dims[0] - self.height=dims[1] + def resizeToFit(commands, width = nil) + dims = [] + getAutoDims(commands, dims, width) + self.width = dims[0] + self.height = dims[1] end def itemCount return @commands ? @commands.length : 0 end - def drawItem(index,_count,rect) + def drawItem(index, _count, rect) pbSetSystemFont(self.contents) if @starting - rect=drawCursor(index,rect) - pbDrawShadowText(self.contents,rect.x,rect.y,rect.width,rect.height, - @commands[index],self.baseColor,self.shadowColor) + rect = drawCursor(index, rect) + pbDrawShadowText(self.contents, rect.x, rect.y + (self.contents.text_offset_y || 0), + rect.width, rect.height, @commands[index], self.baseColor, self.shadowColor) end end @@ -1249,53 +1244,53 @@ class Window_CommandPokemonEx < Window_CommandPokemon class Window_AdvancedCommandPokemon < Window_DrawableCommand attr_reader :commands - def textWidth(bitmap,text) - dims=[nil,0] - chars=getFormattedText(bitmap,0,0, - Graphics.width-self.borderX-SpriteWindow_Base::TEXTPADDING-16, - -1,text,self.rowHeight,true,true) - for ch in chars - dims[0]=dims[0] ? [dims[0],ch[1]].min : ch[1] - dims[1]=[dims[1],ch[1]+ch[3]].max + def textWidth(bitmap, text) + dims = [nil, 0] + chars = getFormattedText(bitmap, 0, 0, + Graphics.width - self.borderX - SpriteWindow_Base::TEXTPADDING - 16, + -1, text, self.rowHeight, true, true) + chars.each do |ch| + dims[0] = dims[0] ? [dims[0], ch[1]].min : ch[1] + dims[1] = [dims[1], ch[1] + ch[3]].max end - dims[0]=0 if !dims[0] - return dims[1]-dims[0] - end - - def initialize(commands,width=nil) - @starting=true - @commands=[] - dims=[] - super(0,0,32,32) - getAutoDims(commands,dims,width) - self.width=dims[0] - self.height=dims[1] - @commands=commands - self.active=true - colors=getDefaultTextColors(self.windowskin) - self.baseColor=colors[0] - self.shadowColor=colors[1] + dims[0] = 0 if !dims[0] + return dims[1] - dims[0] + end + + def initialize(commands, width = nil) + @starting = true + @commands = [] + dims = [] + super(0, 0, 32, 32) + getAutoDims(commands, dims, width) + self.width = dims[0] + self.height = dims[1] + @commands = commands + self.active = true + colors = getDefaultTextColors(self.windowskin) + self.baseColor = colors[0] + self.shadowColor = colors[1] refresh - @starting=false + @starting = false end - def self.newWithSize(commands,x,y,width,height,viewport=nil) - ret=self.new(commands,width) - ret.x=x - ret.y=y - ret.width=width - ret.height=height - ret.viewport=viewport + def self.newWithSize(commands, x, y, width, height, viewport = nil) + ret = self.new(commands, width) + ret.x = x + ret.y = y + ret.width = width + ret.height = height + ret.viewport = viewport return ret end - def self.newEmpty(x,y,width,height,viewport=nil) - ret=self.new([],width) - ret.x=x - ret.y=y - ret.width=width - ret.height=height - ret.viewport=viewport + def self.newEmpty(x, y, width, height, viewport = nil) + ret = self.new([], width) + ret.x = x + ret.y = y + ret.width = width + ret.height = height + ret.viewport = viewport return ret end @@ -1305,53 +1300,52 @@ def index=(value) end def commands=(value) - @commands=value - @item_max=commands.length + @commands = value + @item_max = commands.length self.update_cursor_rect self.refresh end def width=(value) - oldvalue=self.width + oldvalue = self.width super - if !@starting && oldvalue!=value - self.index=self.index + if !@starting && oldvalue != value + self.index = self.index self.update_cursor_rect end end def height=(value) - oldvalue=self.height + oldvalue = self.height super - if !@starting && oldvalue!=value - self.index=self.index + if !@starting && oldvalue != value + self.index = self.index self.update_cursor_rect end end - def resizeToFit(commands,width=nil) - dims=[] - getAutoDims(commands,dims,width) - self.width=dims[0] - self.height=dims[1] - 6 + def resizeToFit(commands, width = nil) + dims = [] + getAutoDims(commands, dims, width) + self.width = dims[0] + self.height = dims[1] - 6 end def itemCount return @commands ? @commands.length : 0 end - def drawItem(index,_count,rect) + def drawItem(index, _count, rect) pbSetSystemFont(self.contents) - rect=drawCursor(index,rect) - if toUnformattedText(@commands[index]).gsub(/\n/,"")==@commands[index] + rect = drawCursor(index, rect) + if toUnformattedText(@commands[index]).gsub(/\n/, "") == @commands[index] # Use faster alternative for unformatted text without line breaks - pbDrawShadowText(self.contents,rect.x,rect.y,rect.width,rect.height, - @commands[index],self.baseColor,self.shadowColor) + pbDrawShadowText(self.contents, rect.x, rect.y, rect.width, rect.height, + @commands[index], self.baseColor, self.shadowColor) else - chars=getFormattedText( - self.contents,rect.x,rect.y+4,rect.width,rect.height, - @commands[index],rect.height,true,true) - drawFormattedChars(self.contents,chars) + chars = getFormattedText(self.contents, rect.x, rect.y + 4, rect.width, rect.height, + @commands[index], rect.height, true, true) + drawFormattedChars(self.contents, chars) end end end diff --git a/Data/Scripts/007_Objects and windows/006_SpriteWindow_pictures.rb b/Data/Scripts/007_Objects and windows/006_SpriteWindow_pictures.rb index 33a367d0e6..a5e248da06 100644 --- a/Data/Scripts/007_Objects and windows/006_SpriteWindow_pictures.rb +++ b/Data/Scripts/007_Objects and windows/006_SpriteWindow_pictures.rb @@ -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 @@ def update super if @_iconbitmap @_iconbitmap.update - self.contents=@_iconbitmap.bitmap + self.contents = @_iconbitmap.bitmap end end def clearBitmaps - @_iconbitmap.dispose if @_iconbitmap - @_iconbitmap=nil - self.contents=nil if !self.disposed? + @_iconbitmap&.dispose + @_iconbitmap = nil + self.contents = nil if !self.disposed? end # Sets the icon's filename. Alias for setBitmap. @@ -37,16 +37,16 @@ def name=(value) end # Sets the icon's filename. - 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 + def setBitmap(file, hue = 0) + clearBitmaps + @name = file + return if file.nil? + if file == "" + @_iconbitmap = nil else - @_iconbitmap=nil + @_iconbitmap = AnimatedBitmap.new(file, hue) + # for compatibility + self.contents = @_iconbitmap ? @_iconbitmap.bitmap : nil end end end @@ -59,15 +59,15 @@ def setBitmap(file,hue=0) #=============================================================================== 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 @@ -75,47 +75,46 @@ def update 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 if @_iconbitmap - @_iconbitmap=nil - self.contents=nil if !self.disposed? + @_iconbitmap&.dispose + @_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!=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 + 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 else - @_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 + @_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) 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 diff --git a/Data/Scripts/007_Objects and windows/007_SpriteWrapper.rb b/Data/Scripts/007_Objects and windows/007_SpriteWrapper.rb index 7b77982c52..b1908c1f65 100644 --- a/Data/Scripts/007_Objects and windows/007_SpriteWrapper.rb +++ b/Data/Scripts/007_Objects and windows/007_SpriteWrapper.rb @@ -2,52 +2,52 @@ # SpriteWrapper is a class which wraps (most of) Sprite's properties. #=============================================================================== class SpriteWrapper - def initialize(viewport=nil) + 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 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 + return if self.viewport == value bitmap = @sprite.bitmap src_rect = @sprite.src_rect visible = @sprite.visible @@ -94,10 +94,10 @@ def viewport=(value) # This bitmap can't be changed to a different one. #=============================================================================== class BitmapSprite < SpriteWrapper - def initialize(width,height,viewport=nil) + def initialize(width, height, viewport = nil) super(viewport) - self.bitmap=Bitmap.new(width,height) - @initialized=true + self.bitmap = Bitmap.new(width, height) + @initialized = true end def bitmap=(value) @@ -122,82 +122,82 @@ class AnimatedSprite < SpriteWrapper 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 + 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 + @animbitmap = AnimatedBitmap.new(animname).deanimate rescue - @animbitmap=Bitmap.new(framewidth,frameheight) + @animbitmap = Bitmap.new(framewidth, frameheight) end - if @animbitmap.width%framewidth!=0 + 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) + @animbitmap.width, framewidth, animname) end - if @animbitmap.height%frameheight!=0 + 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) + @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 + @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 + 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 + @animbitmap = AnimatedBitmap.new(animname).deanimate rescue - @animbitmap=Bitmap.new(framecount*4,32) + @animbitmap = Bitmap.new(framecount * 4, 32) end - if @animbitmap.width%framecount!=0 + 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) + @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 + @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 + if args.length == 1 super(args[0][3]) - initializeShort(args[0][0],args[0][1],args[0][2]) + 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]) + 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]) + 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 + @animbitmap = nil super end @@ -206,31 +206,31 @@ def playing? end def frame=(value) - @frame=value - @realframes=0 - self.src_rect.x=@frame%@framesperrow*@framewidth - self.src_rect.y=@frame/@framesperrow*@frameheight + @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 + @playing = true + @realframes = 0 end alias play start def stop - @playing=false + @playing = false end def update super if @playing - @realframes+=1 - if @realframes==@frameskip - @realframes=0 - self.frame+=1 - self.frame%=self.framecount + @realframes += 1 + if @realframes == @frameskip + @realframes = 0 + self.frame += 1 + self.frame %= self.framecount end end end @@ -245,27 +245,28 @@ class IconSprite < SpriteWrapper attr_reader :name def initialize(*args) - if args.length==0 + case args.length + when 0 super(nil) - self.bitmap=nil - elsif args.length==1 + self.bitmap = nil + when 1 super(args[0]) - self.bitmap=nil - elsif args.length==2 + self.bitmap = nil + when 2 super(nil) - self.x=args[0] - self.y=args[1] + self.x = args[0] + self.y = args[1] else super(args[2]) - self.x=args[0] - self.y=args[1] + self.x = args[0] + self.y = args[1] end - @name="" - @_iconbitmap=nil + @name = "" + @_iconbitmap = nil end def dispose - clearBitmaps() + clearBitmaps super end @@ -275,35 +276,35 @@ def name=(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 + def setBitmap(file, hue = 0) + oldrc = self.src_rect + clearBitmaps + @name = file + return if file.nil? + if file == "" + @_iconbitmap = nil else - @_iconbitmap=nil + @_iconbitmap = AnimatedBitmap.new(file, hue) + # for compatibility + self.bitmap = @_iconbitmap ? @_iconbitmap.bitmap : nil + self.src_rect = oldrc end end def clearBitmaps - @_iconbitmap.dispose if @_iconbitmap - @_iconbitmap=nil - self.bitmap=nil if !self.disposed? + @_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 + if self.bitmap != @_iconbitmap.bitmap + oldrc = self.src_rect + self.bitmap = @_iconbitmap.bitmap + self.src_rect = oldrc end end end @@ -315,7 +316,7 @@ def update #=============================================================================== class GifSprite < IconSprite def initialize(path) - super(0,0) + super(0, 0) setBitmap(path) end end @@ -326,7 +327,7 @@ def initialize(path) # SpriteWrapper that stores multiple bitmaps, and displays only one at once. #=============================================================================== class ChangelingSprite < SpriteWrapper - def initialize(x=0,y=0,viewport=nil) + def initialize(x = 0, y = 0, viewport = nil) super(viewport) self.x = x self.y = y @@ -334,8 +335,8 @@ def initialize(x=0,y=0,viewport=nil) @currentBitmap = nil end - def addBitmap(key,path) - @bitmaps[key].dispose if @bitmaps[key] + def addBitmap(key, path) + @bitmaps[key]&.dispose @bitmaps[key] = AnimatedBitmap.new(path) end @@ -346,14 +347,14 @@ def changeBitmap(key) def dispose return if disposed? - for bm in @bitmaps.values; bm.dispose; end + @bitmaps.each_value { |bm| bm.dispose } @bitmaps.clear super end def update return if disposed? - for bm in @bitmaps.values; bm.update; end + @bitmaps.each_value { |bm| bm.update } self.bitmap = (@currentBitmap) ? @currentBitmap.bitmap : nil end end diff --git a/Data/Scripts/007_Objects and windows/008_AnimatedBitmap.rb b/Data/Scripts/007_Objects and windows/008_AnimatedBitmap.rb index 3f3dfb16f7..0a5aea4eed 100644 --- a/Data/Scripts/007_Objects and windows/008_AnimatedBitmap.rb +++ b/Data/Scripts/007_Objects and windows/008_AnimatedBitmap.rb @@ -6,10 +6,10 @@ def initialize(file, hue = 0) 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) @@ -57,7 +57,7 @@ def initialize(dir, filename, hue = 0) end @frameDelay = delay subWidth = panorama.width / numFrames - for i in 0...numFrames + numFrames.times do |i| subBitmap = BitmapWrapper.new(subWidth, panorama.height) subBitmap.blt(0, 0, panorama, Rect.new(subWidth * i, 0, subWidth, panorama.height)) @frames.push(subBitmap) @@ -76,7 +76,7 @@ def width; self.bitmap.width; end def height; self.bitmap.height; end def deanimate - for i in 1...@frames.length + (1...@frames.length).each do |i| @frames[i].dispose end @frames = [@frames[0]] @@ -134,9 +134,7 @@ def dispose 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 + x.frames.each_with_index { |frame, i| x.frames[i] = frame.copy } return x end end @@ -225,14 +223,14 @@ def pbGetTileBitmap(filename, tile_id, hue, width = 1, height = 1) } 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 diff --git a/Data/Scripts/007_Objects and windows/009_Planes.rb b/Data/Scripts/007_Objects and windows/009_Planes.rb index 26258bc244..2a94b53077 100644 --- a/Data/Scripts/007_Objects and windows/009_Planes.rb +++ b/Data/Scripts/007_Objects and windows/009_Planes.rb @@ -6,225 +6,69 @@ def update; end def refresh; end end - - -#=============================================================================== -# This class works around a limitation that planes are always -# 640 by 480 pixels in size regardless of the window's size. -#=============================================================================== -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 - 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.width0; left -= srcbitmap.width; end - while top>0; top -= srcbitmap.height; end - y = top - while y 0 + setBitmap("Graphics/Panoramas/" + file, hue) + else + clear_bitmap + end end - def setPanorama(file, hue=0) - clearBitmaps() - return if file==nil - @bitmap=AnimatedBitmap.new("Graphics/Panoramas/"+file,hue) + def set_fog(file, hue = 0) + if file.is_a?(String) && file.length > 0 + setBitmap("Graphics/Fogs/" + file, hue) + else + clear_bitmap + end end - def setFog(file, hue=0) - clearBitmaps() - return if file==nil - @bitmap=AnimatedBitmap.new("Graphics/Fogs/"+file,hue) - end + private - def setBitmap(file, hue=0) - clearBitmaps() - return if file==nil - @bitmap=AnimatedBitmap.new(file,hue) + def clear_bitmap + @bitmap&.dispose + @bitmap = nil + self.bitmap = nil if !self.disposed? end end diff --git a/Data/Scripts/007_Objects and windows/010_DrawText.rb b/Data/Scripts/007_Objects and windows/010_DrawText.rb index c12f31b31f..f3dc1b5612 100644 --- a/Data/Scripts/007_Objects and windows/010_DrawText.rb +++ b/Data/Scripts/007_Objects and windows/010_DrawText.rb @@ -2,23 +2,23 @@ # Text colours #=============================================================================== def ctag(color) - ret=(color.red.to_i << 24) - ret|=((color.green.to_i) << 16) - ret|=((color.blue.to_i) << 8) - ret|=((color.alpha.to_i)) - return sprintf("",ret) + ret = (color.red.to_i << 24) + ret |= ((color.green.to_i) << 16) + ret |= ((color.blue.to_i) << 8) + ret |= ((color.alpha.to_i)) + return sprintf("", ret) end -def shadowctag(base,shadow) - return sprintf("",colorToRgb16(base),colorToRgb16(shadow)) +def shadowctag(base, shadow) + return sprintf("", colorToRgb16(base), colorToRgb16(shadow)) end -def shadowc3tag(base,shadow) - return sprintf("",colorToRgb32(base),colorToRgb32(shadow)) +def shadowc3tag(base, shadow) + return sprintf("", colorToRgb32(base), colorToRgb32(shadow)) end def shadowctagFromColor(color) - return shadowc3tag(color,getContrastColor(color)) + return shadowc3tag(color, getContrastColor(color)) end def shadowctagFromRgb(param) @@ -27,88 +27,91 @@ def shadowctagFromRgb(param) def colorToRgb32(color) return "" if !color - if color.alpha.to_i==255 - return sprintf("%02X%02X%02X",color.red.to_i,color.green.to_i,color.blue.to_i) + if color.alpha.to_i == 255 + return sprintf("%02X%02X%02X", color.red.to_i, color.green.to_i, color.blue.to_i) else return sprintf("%02X%02X%02X%02X", - color.red.to_i,color.green.to_i,color.blue.to_i,color.alpha.to_i) + color.red.to_i, color.green.to_i, color.blue.to_i, color.alpha.to_i) end end def colorToRgb16(color) - ret=(color.red.to_i>>3) - ret|=((color.green.to_i>>3)<<5) - ret|=((color.blue.to_i>>3)<<10) - return sprintf("%04X",ret) + ret = (color.red.to_i >> 3) + ret |= ((color.green.to_i >> 3) << 5) + ret |= ((color.blue.to_i >> 3) << 10) + return sprintf("%04X", ret) end def rgbToColor(param) return Font.default_color if !param - baseint=param.to_i(16) - if param.length==8 # 32-bit hex + baseint = param.to_i(16) + case param.length + when 8 # 32-bit hex return Color.new( - (baseint>>24)&0xFF, - (baseint>>16)&0xFF, - (baseint>>8)&0xFF, - (baseint)&0xFF + (baseint >> 24) & 0xFF, + (baseint >> 16) & 0xFF, + (baseint >> 8) & 0xFF, + (baseint) & 0xFF ) - elsif param.length==6 # 24-bit hex + when 6 # 24-bit hex return Color.new( - (baseint>>16)&0xFF, - (baseint>>8)&0xFF, - (baseint)&0xFF + (baseint >> 16) & 0xFF, + (baseint >> 8) & 0xFF, + (baseint) & 0xFF ) - elsif param.length==4 # 16-bit hex + when 4 # 16-bit hex return Color.new( - ((baseint)&0x1F)<<3, - ((baseint>>5)&0x1F)<<3, - ((baseint>>10)&0x1F)<<3 + ((baseint) & 0x1F) << 3, + ((baseint >> 5) & 0x1F) << 3, + ((baseint >> 10) & 0x1F) << 3 ) - elsif param.length==1 # Color number - i=param.to_i - return Font.default_color if i>=8 - return [ - Color.new(255, 255, 255, 255), - Color.new(128, 128, 255, 255), - Color.new(255, 128, 128, 255), - Color.new(128, 255, 128, 255), - Color.new(128, 255, 255, 255), - Color.new(255, 128, 255, 255), - Color.new(255, 255, 128, 255), - Color.new(192, 192, 192, 255) - ][i] + when 1 # Color number + i = param.to_i + return Font.default_color if i >= 8 + return [ + Color.new(255, 255, 255, 255), + Color.new(128, 128, 255, 255), + Color.new(255, 128, 128, 255), + Color.new(128, 255, 128, 255), + Color.new(128, 255, 255, 255), + Color.new(255, 128, 255, 255), + Color.new(255, 255, 128, 255), + Color.new(192, 192, 192, 255) + ][i] else return Font.default_color end end def Rgb16ToColor(param) - baseint=param.to_i(16) + baseint = param.to_i(16) return Color.new( - ((baseint)&0x1F)<<3, - ((baseint>>5)&0x1F)<<3, - ((baseint>>10)&0x1F)<<3 + ((baseint) & 0x1F) << 3, + ((baseint >> 5) & 0x1F) << 3, + ((baseint >> 10) & 0x1F) << 3 ) end def getContrastColor(color) raise "No color given" if !color - r=color.red; g=color.green; b=color.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 + r = color.red + g = color.green + b = color.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 + if yuv[0] < 127.5 + yuv[0] += (255 - yuv[0]) / 2 else - yuv[0]=yuv[0]/2 + 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), - color.alpha + 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)), + color.alpha ) end @@ -121,21 +124,21 @@ def getContrastColor(color) def fmtescape(text) if text[/[&<>]/] - text2=text.gsub(/&/,"&") - text2.gsub!(//,">") + text2 = text.gsub(/&/, "&") + text2.gsub!(//, ">") return text2 end return text end def toUnformattedText(text) - text2=text.gsub(FORMATREGEXP,"") - text2.gsub!(/</,"<") - text2.gsub!(/>/,">") - text2.gsub!(/'/,"'") - text2.gsub!(/"/,"\"") - text2.gsub!(/&/,"&") + text2 = text.gsub(FORMATREGEXP, "") + text2.gsub!(/</, "<") + text2.gsub!(/>/, ">") + text2.gsub!(/'/, "'") + text2.gsub!(/"/, "\"") + text2.gsub!(/&/, "&") return text2 end @@ -146,177 +149,135 @@ def unformattedTextLength(text) def itemIconTag(item) return "" if !item if item.respond_to?("icon_name") - return sprintf("",item.icon_name) + return sprintf("", item.icon_name) else - ix=item.icon_index % 16 * 24 - iy=item.icon_index / 16 * 24 - return sprintf("",ix,iy) + ix = item.icon_index % 16 * 24 + iy = item.icon_index / 16 * 24 + return sprintf("", ix, iy) end end -def getFormattedTextForDims(bitmap,xDst,yDst,widthDst,heightDst,text,lineheight, - newlineBreaks=true,explicitBreaksOnly=false) - text2=text.gsub(/<(\/?)(c|c2|c3|o|u|s)(\s*\=\s*([^>]*))?>/i,"") +def getFormattedTextForDims(bitmap, xDst, yDst, widthDst, heightDst, text, lineheight, + newlineBreaks = true, explicitBreaksOnly = false) + text2 = text.gsub(/<(\/?)(c|c2|c3|o|u|s)(\s*\=\s*([^>]*))?>/i, "") if newlineBreaks - text2.gsub!(/<(\/?)(br)(\s*\=\s*([^>]*))?>/i,"\n") + text2.gsub!(/<(\/?)(br)(\s*\=\s*([^>]*))?>/i, "\n") end - return getFormattedText( - bitmap,xDst,yDst,widthDst,heightDst, - text2,lineheight,newlineBreaks, - explicitBreaksOnly,true) + return getFormattedText(bitmap, xDst, yDst, widthDst, heightDst, + text2, lineheight, newlineBreaks, + explicitBreaksOnly, true) end -def getFormattedTextFast(bitmap,xDst,yDst,widthDst,heightDst,text,lineheight, - newlineBreaks=true,explicitBreaksOnly=false) - x=y=0 - characters=[] - textchunks=[] +def getFormattedTextFast(bitmap, xDst, yDst, widthDst, heightDst, text, lineheight, + newlineBreaks = true, explicitBreaksOnly = false) + x = y = 0 + characters = [] + textchunks = [] textchunks.push(text) - text=textchunks.join("") - textchars=text.scan(/./m) - lastword=[0,0] # position of last word - hadspace=false - hadnonspace=false - bold=bitmap.font.bold - italic=bitmap.font.italic - colorclone=bitmap.font.color - defaultfontname=bitmap.font.name + text = textchunks.join + textchars = text.scan(/./m) + lastword = [0, 0] # position of last word + hadspace = false + hadnonspace = false + bold = bitmap.font.bold + italic = bitmap.font.italic + colorclone = bitmap.font.color + defaultfontname = bitmap.font.name if defaultfontname.is_a?(Array) - defaultfontname=defaultfontname.find { |i| Font.exist?(i) } || "Arial" + defaultfontname = defaultfontname.find { |i| Font.exist?(i) } || "Arial" elsif !Font.exist?(defaultfontname) - defaultfontname="Arial" + defaultfontname = "Arial" end - defaultfontname=defaultfontname.clone - havenl=false - position=0 - while positionwidthDst && lastword[1]!=0 && + x += width + if !explicitBreaksOnly && x + 2 > widthDst && lastword[1] != 0 && (!hadnonspace || !hadspace) - havenl=true - characters.insert(lastword[0],["\n",x,y*lineheight+yDst,0,lineheight, - false,false,false,colorclone,nil,false,false,"",8,position]) - lastword[0]+=1 - y+=1 - x=0 - for i in lastword[0]...characters.length - characters[i][2]+=lineheight - charwidth=characters[i][3]-2 - characters[i][1]=x - x+=charwidth + havenl = true + characters.insert(lastword[0], ["\n", x, (y * lineheight) + yDst, 0, lineheight, + false, false, false, colorclone, nil, false, false, "", 8, position]) + lastword[0] += 1 + y += 1 + x = 0 + (lastword[0]...characters.length).each do |i| + characters[i][2] += lineheight + charwidth = characters[i][3] - 2 + characters[i][1] = x + x += charwidth end - lastword[1]=0 + lastword[1] = 0 end - position+=1 + position += 1 end - # This code looks at whether the text occupies exactly two lines when - # displayed. If it does, it balances the length of each line. -=begin - # Count total number of lines - numlines = (x==0 && y>0) ? y-1 : y - realtext = (newlineBreaks) ? text : text.gsub(/\n/," ") - if numlines==2 && !explicitBreaksOnly && !realtext[/\n/] && realtext.length>=50 - # Set half to middle of text (known to contain no formatting) - half = realtext.length/2 - leftSearch = 0 - rightSearch = 0 - # Search left for a space - i = half; while i>=0 - break if realtext[i,1][/\s/]||isWaitChar(realtext[i]) # found a space - leftSearch += 1 - i -= 1 - end - # Search right for a space - i = half; while i=0 - for j in firstspace...i - characters[j]=nil + firstspace = -1 + characters.length.times do |i| + if characters[i][5] != false # If not a character + firstspace = -1 + elsif (characters[i][0] == "\n" || isWaitChar(characters[i][0])) && + firstspace >= 0 + (firstspace...i).each do |j| + characters[j] = nil end - firstspace=-1 + firstspace = -1 elsif characters[i][0][/[ \r\t]/] - if firstspace<0 - firstspace=i - end + firstspace = i if firstspace < 0 else - firstspace=-1 + firstspace = -1 end end - if firstspace>0 - for j in firstspace...characters.length - characters[j]=nil + if firstspace > 0 + (firstspace...characters.length).each do |j| + characters[j] = nil end end characters.compact! end - for i in 0...characters.length - characters[i][1]=xDst+characters[i][1] - end + characters.each { |char| char[1] = xDst + char[1] } # Remove all characters with Y greater or equal to _yDst_+_heightDst_ - if heightDst>=0 - for i in 0...characters.length - if characters[i][2]>=yDst+heightDst - characters[i]=nil - end + if heightDst >= 0 + characters.each_with_index do |char, i| + characters[i] = nil if char[2] >= yDst + heightDst end characters.compact! end @@ -324,26 +285,26 @@ def getFormattedTextFast(bitmap,xDst,yDst,widthDst,heightDst,text,lineheight, end def isWaitChar(x) - return (x=="\001" || x=="\002") + return (["\001", "\002"].include?(x)) end -def getLastParam(array,default) - i=array.length-1 - while i>=0 +def getLastParam(array, default) + i = array.length - 1 + while i >= 0 return array[i] if array[i] - i-=1 + i -= 1 end return default end -def getLastColors(colorstack,opacitystack,defaultcolors) - colors=getLastParam(colorstack,defaultcolors) - opacity=getLastParam(opacitystack,255) - if opacity!=255 - colors=[Color.new(colors[0].red,colors[0].green,colors[0].blue, - colors[0].alpha*opacity/255), - colors[1] ? Color.new(colors[1].red,colors[1].green,colors[1].blue, - colors[1].alpha*opacity/255) : nil] +def getLastColors(colorstack, opacitystack, defaultcolors) + colors = getLastParam(colorstack, defaultcolors) + opacity = getLastParam(opacitystack, 255) + if opacity != 255 + colors = [ + Color.new(colors[0].red, colors[0].green, colors[0].blue, colors[0].alpha * opacity / 255), + colors[1] ? Color.new(colors[1].red, colors[1].green, colors[1].blue, colors[1].alpha * opacity / 255) : nil + ] end return colors end @@ -418,331 +379,329 @@ def getLastColors(colorstack,opacitystack,defaultcolors) _drawFormattedChars_ function. =end -def getFormattedText(bitmap,xDst,yDst,widthDst,heightDst,text,lineheight=32, - newlineBreaks=true,explicitBreaksOnly=false, - collapseAlignments=false) - dummybitmap=nil +def getFormattedText(bitmap, xDst, yDst, widthDst, heightDst, text, lineheight = 32, + newlineBreaks = true, explicitBreaksOnly = false, + collapseAlignments = false) + dummybitmap = nil if !bitmap || bitmap.disposed? # allows function to be called with nil bitmap - dummybitmap=Bitmap.new(1,1) - bitmap=dummybitmap + dummybitmap = Bitmap.new(1, 1) + bitmap = dummybitmap return end - if !bitmap || bitmap.disposed? || widthDst<=0 || heightDst==0 || text.length==0 + if !bitmap || bitmap.disposed? || widthDst <= 0 || heightDst == 0 || text.length == 0 return [] end - textchunks=[] - controls=[] - oldtext=text + textchunks = [] + controls = [] +# oldtext = text while text[FORMATREGEXP] textchunks.push($~.pre_match) if $~[3] - controls.push([$~[2].downcase,$~[4],-1,$~[1]=="/" ? true : false]) + controls.push([$~[2].downcase, $~[4], -1, $~[1] == "/"]) else - controls.push([$~[2].downcase,"",-1,$~[1]=="/" ? true : false]) + controls.push([$~[2].downcase, "", -1, $~[1] == "/"]) end - text=$~.post_match + text = $~.post_match end - if controls.length==0 - ret=getFormattedTextFast(bitmap,xDst,yDst,widthDst,heightDst,text,lineheight, - newlineBreaks,explicitBreaksOnly) - dummybitmap.dispose if dummybitmap + if controls.length == 0 + ret = getFormattedTextFast(bitmap, xDst, yDst, widthDst, heightDst, text, lineheight, + newlineBreaks, explicitBreaksOnly) + dummybitmap&.dispose return ret end - x=y=0 - characters=[] - charactersInternal=[] - realtext=nil - realtextStart="" - if !explicitBreaksOnly && textchunks.join("").length==0 - # All commands occurred at the beginning of the text string - realtext=(newlineBreaks) ? text : text.gsub(/\n/," ") - realtextStart=oldtext[0,oldtext.length-realtext.length] - realtextHalf=text.length/2 - end + x = y = 0 + characters = [] + charactersInternal = [] +# realtext = nil +# realtextStart = "" +# if !explicitBreaksOnly && textchunks.join.length == 0 +# # All commands occurred at the beginning of the text string +# realtext = (newlineBreaks) ? text : text.gsub(/\n/, " ") +# realtextStart = oldtext[0, oldtext.length - realtext.length] +# end textchunks.push(text) - for chunk in textchunks - chunk.gsub!(/</,"<") - chunk.gsub!(/>/,">") - chunk.gsub!(/'/,"'") - chunk.gsub!(/"/,"\"") - chunk.gsub!(/&/,"&") + textchunks.each do |chunk| + chunk.gsub!(/</, "<") + chunk.gsub!(/>/, ">") + chunk.gsub!(/'/, "'") + chunk.gsub!(/"/, "\"") + chunk.gsub!(/&/, "&") end - textlen=0 - for i in 0...controls.length - textlen+=textchunks[i].scan(/./m).length - controls[i][2]=textlen + textlen = 0 + controls.each_with_index do |control, i| + textlen += textchunks[i].scan(/./m).length + control[2] = textlen end - text=textchunks.join("") - textchars=text.scan(/./m) - colorstack=[] - boldcount=0 - italiccount=0 - outlinecount=0 - underlinecount=0 - strikecount=0 - rightalign=0 - outline2count=0 - opacitystack=[] - oldfont=bitmap.font.clone - defaultfontname=bitmap.font.name - defaultfontsize=bitmap.font.size - fontsize=defaultfontsize - fontnamestack=[] - fontsizestack=[] - defaultcolors=[oldfont.color.clone,nil] + text = textchunks.join + textchars = text.scan(/./m) + colorstack = [] + boldcount = 0 + italiccount = 0 + outlinecount = 0 + underlinecount = 0 + strikecount = 0 + rightalign = 0 + outline2count = 0 + opacitystack = [] + oldfont = bitmap.font.clone + defaultfontname = bitmap.font.name + defaultfontsize = bitmap.font.size + fontsize = defaultfontsize + fontnamestack = [] + fontsizestack = [] + defaultcolors = [oldfont.color.clone, nil] if defaultfontname.is_a?(Array) - defaultfontname=defaultfontname.find { |i| Font.exist?(i) } || "Arial" + defaultfontname = defaultfontname.find { |i| Font.exist?(i) } || "Arial" elsif !Font.exist?(defaultfontname) - defaultfontname="Arial" + defaultfontname = "Arial" end - defaultfontname=defaultfontname.clone - fontname=defaultfontname - alignstack=[] - lastword=[0,0] # position of last word - hadspace=false - hadnonspace=false - havenl=false - position=0 - while position0 && nextline==0 - else + fontname = getLastParam(fontnamestack, defaultfontname) + bitmap.font.name = fontname + when "ar" # Right align + if endtag alignstack.pop - nextline=1 if x>0 && nextline==0 - end - elsif control=="al" # Left align - if !endtag - alignstack.push(0) - nextline=1 if x>0 && nextline==0 else - alignstack.pop - nextline=1 if x>0 && nextline==0 + alignstack.push(1) end - elsif control=="ac" # Center align - if !endtag - alignstack.push(2) - nextline=1 if x>0 && nextline==0 + nextline = 1 if x > 0 && nextline == 0 + when "al" # Left align + if endtag + alignstack.pop else + alignstack.push(0) + end + nextline = 1 if x > 0 && nextline == 0 + when "ac" # Center align + if endtag alignstack.pop - nextline=1 if x>0 && nextline==0 + else + alignstack.push(2) end - elsif control=="icon" # Icon + nextline = 1 if x > 0 && nextline == 0 + when "icon" # Icon if !endtag - param=param.sub(/\s+$/,"") - graphic="Graphics/Icons/#{param}" - controls[i]=nil + param = param.sub(/\s+$/, "") + graphic = "Graphics/Icons/#{param}" + controls[i] = nil break end - elsif control=="img" # Icon + when "img" # Icon if !endtag - param=param.sub(/\s+$/,"") - param=param.split("|") - graphic=param[0] - if param.length>1 - graphicX=param[1].to_i - graphicY=param[2].to_i - graphicWidth=param[3].to_i - graphicHeight=param[4].to_i + param = param.sub(/\s+$/, "") + param = param.split("|") + graphic = param[0] + if param.length > 1 + graphicX = param[1].to_i + graphicY = param[2].to_i + graphicWidth = param[3].to_i + graphicHeight = param[4].to_i end - controls[i]=nil + controls[i] = nil break end - elsif control=="br" # Line break + when "br" # Line break if !endtag - nextline+=1 + nextline += 1 end - elsif control=="r" # Right align this line + when "r" # Right align this line if !endtag - x=0 - rightalign=1; lastword=[characters.length,x] + x = 0 + rightalign = 1 + lastword = [characters.length, x] end end - controls[i]=nil + controls[i] = nil end end - bitmap.font.bold=(boldcount>0) - bitmap.font.italic=(italiccount>0) + bitmap.font.bold = (boldcount > 0) + bitmap.font.italic = (italiccount > 0) if graphic if !graphicWidth - tempgraphic=Bitmap.new(graphic) - graphicWidth=tempgraphic.width - graphicHeight=tempgraphic.height + tempgraphic = Bitmap.new(graphic) + graphicWidth = tempgraphic.width + graphicHeight = tempgraphic.height tempgraphic.dispose end - width=graphicWidth # +8 # No padding - xStart=0 # 4 - yStart=[(lineheight/2)-(graphicHeight/2),0].max - graphicRect=Rect.new(graphicX,graphicY,graphicWidth,graphicHeight) + width = graphicWidth # +8 # No padding + xStart = 0 # 4 + yStart = [(lineheight / 2) - (graphicHeight / 2), 0].max + yStart += 4 # TEXT OFFSET + graphicRect = Rect.new(graphicX, graphicY, graphicWidth, graphicHeight) else - xStart=0 - yStart=0 - width=isWaitChar(textchars[position]) ? 0 : bitmap.text_size(textchars[position]).width - width+=2 if width>0 && outline2count>0 + xStart = 0 + yStart = 0 + width = isWaitChar(textchars[position]) ? 0 : bitmap.text_size(textchars[position]).width + width += 2 if width > 0 && outline2count > 0 end - if rightalign==1 && nextline==0 - alignment=1 + if rightalign == 1 && nextline == 0 + alignment = 1 else - alignment=getLastParam(alignstack,0) + alignment = getLastParam(alignstack, 0) end nextline.times do - havenl=true - characters.push(["\n",x,y*lineheight+yDst,0,lineheight,false,false,false, - defaultcolors[0],defaultcolors[1],false,false,"",8,position,nil,0]) - charactersInternal.push([alignment,y,0]) - y+=1 - x=0 - rightalign=0 - lastword=[characters.length,x] - hadspace=false - hadnonspace=false + havenl = true + characters.push(["\n", x, (y * lineheight) + yDst, 0, lineheight, false, false, false, + defaultcolors[0], defaultcolors[1], false, false, "", 8, position, nil, 0]) + charactersInternal.push([alignment, y, 0]) + y += 1 + x = 0 + rightalign = 0 + lastword = [characters.length, x] + hadspace = false + hadnonspace = false end - if textchars[position]=="\n" + if textchars[position] == "\n" if newlineBreaks - if nextline==0 - havenl=true - characters.push(["\n",x,y*lineheight+yDst,0,lineheight,false,false,false, - defaultcolors[0],defaultcolors[1],false,false,"",8,position,nil,0]) - charactersInternal.push([alignment,y,0]) - y+=1 - x=0 + if nextline == 0 + havenl = true + characters.push(["\n", x, (y * lineheight) + yDst, 0, lineheight, false, false, false, + defaultcolors[0], defaultcolors[1], false, false, "", 8, position, nil, 0]) + charactersInternal.push([alignment, y, 0]) + y += 1 + x = 0 end - rightalign=0 - hadspace=true - hadnonspace=false - position+=1 + rightalign = 0 + hadspace = true + hadnonspace = false + position += 1 next else - textchars[position]=" " + textchars[position] = " " if !graphic - width=bitmap.text_size(textchars[position]).width - width+=2 if width>0 && outline2count>0 + width = bitmap.text_size(textchars[position]).width + width += 2 if width > 0 && outline2count > 0 end end end - isspace=(textchars[position][/\s/] || isWaitChar(textchars[position])) ? true : false + isspace = (textchars[position][/\s/] || isWaitChar(textchars[position])) ? true : false if hadspace && !isspace # set last word to here - lastword[0]=characters.length - lastword[1]=x - hadspace=false - hadnonspace=true + lastword[0] = characters.length + lastword[1] = x + hadspace = false + hadnonspace = true elsif isspace - hadspace=true + hadspace = true end - texty=(lineheight*y)+yDst+yStart - colors=getLastColors(colorstack,opacitystack,defaultcolors) + texty = (lineheight * y) + yDst + yStart - 2 # TEXT OFFSET + colors = getLastColors(colorstack, opacitystack, defaultcolors) # Push character - if heightDst<0 || texty0) ? 2+(width/2) : 2 - characters.push([ - graphic ? graphic : textchars[position], - x+xStart,texty,width+extraspace,lineheight, - graphic ? true : false, - (boldcount>0),(italiccount>0),colors[0],colors[1], - (underlinecount>0),(strikecount>0),fontname,fontsize, - position,graphicRect, - ((outlinecount>0) ? 1 : 0)+((outline2count>0) ? 2 : 0) - ]) - charactersInternal.push([alignment,y,xStart,textchars[position],extraspace]) + if heightDst < 0 || texty < yDst + heightDst + havenl = true if !graphic && isWaitChar(textchars[position]) + extraspace = (!graphic && italiccount > 0) ? 2 + (width / 2) : 2 + characters.push([graphic || textchars[position], + x + xStart, texty, width + extraspace, lineheight, + graphic ? true : false, + (boldcount > 0), (italiccount > 0), colors[0], colors[1], + (underlinecount > 0), (strikecount > 0), fontname, fontsize, + position, graphicRect, + ((outlinecount > 0) ? 1 : 0) + ((outline2count > 0) ? 2 : 0)]) + charactersInternal.push([alignment, y, xStart, textchars[position], extraspace]) end - x+=width - if !explicitBreaksOnly && x+2>widthDst && lastword[1]!=0 && + x += width + if !explicitBreaksOnly && x + 2 > widthDst && lastword[1] != 0 && (!hadnonspace || !hadspace) - havenl=true - characters.insert(lastword[0],["\n",x,y*lineheight+yDst,0,lineheight,false, - false,false,defaultcolors[0],defaultcolors[1],false,false,"",8,position, - nil]) - charactersInternal.insert(lastword[0],[alignment,y,0]) - lastword[0]+=1 - y+=1 - x=0 - for i in lastword[0]...characters.length - characters[i][2]+=lineheight - charactersInternal[i][1]+=1 - extraspace=(charactersInternal[i][4]) ? charactersInternal[i][4] : 0 - charwidth=characters[i][3]-extraspace - characters[i][1]=x+charactersInternal[i][2] - x+=charwidth + havenl = true + characters.insert(lastword[0], ["\n", x, (y * lineheight) + yDst, 0, lineheight, + false, false, false, + defaultcolors[0], defaultcolors[1], + false, false, "", 8, position, nil]) + charactersInternal.insert(lastword[0], [alignment, y, 0]) + lastword[0] += 1 + y += 1 + x = 0 + (lastword[0]...characters.length).each do |i| + characters[i][2] += lineheight + charactersInternal[i][1] += 1 + extraspace = (charactersInternal[i][4]) ? charactersInternal[i][4] : 0 + charwidth = characters[i][3] - extraspace + characters[i][1] = x + charactersInternal[i][2] + x += charwidth end - lastword[1]=0 + lastword[1] = 0 end - position+=1 if !graphic + position += 1 if !graphic end # This code looks at whether the text occupies exactly two lines when # displayed. If it does, it balances the length of each line. @@ -755,13 +714,15 @@ def getFormattedText(bitmap,xDst,yDst,widthDst,heightDst,text,lineheight=32, leftSearch = 0 rightSearch = 0 # Search left for a space - i = half; while i>=0 + i = half + while i>=0 break if realtext[i,1][/\s/]||isWaitChar(realtext[i,1]) # found a space leftSearch += 1 i -= 1 end # Search right for a space - i = half; while i=0 - for j in firstspace...i - characters[j]=nil - charactersInternal[j]=nil + firstspace = -1 + characters.length.times do |i| + if characters[i][5] != false # If not a character + firstspace = -1 + elsif (characters[i][0] == "\n" || isWaitChar(characters[i][0])) && + firstspace >= 0 + (firstspace...i).each do |j| + characters[j] = nil + charactersInternal[j] = nil end - firstspace=-1 + firstspace = -1 elsif characters[i][0][/[ \r\t]/] - if firstspace<0 - firstspace=i + if firstspace < 0 + firstspace = i end else - firstspace=-1 + firstspace = -1 end end - if firstspace>0 - for j in firstspace...characters.length - characters[j]=nil - charactersInternal[j]=nil + if firstspace > 0 + (firstspace...characters.length).each do |j| + characters[j] = nil + charactersInternal[j] = nil end end characters.compact! @@ -819,47 +780,47 @@ def getFormattedText(bitmap,xDst,yDst,widthDst,heightDst,text,lineheight=32, end # Calculate Xs based on alignment # First, find all text runs with the same alignment on the same line - totalwidth=0 - widthblocks=[] - lastalign=0 - lasty=0 - runstart=0 - for i in 0...characters.length - c=characters[i] - if i>0 && (charactersInternal[i][0]!=lastalign || - charactersInternal[i][1]!=lasty) + totalwidth = 0 + widthblocks = [] + lastalign = 0 + lasty = 0 + runstart = 0 + characters.length.times do |i| + c = characters[i] + if i > 0 && (charactersInternal[i][0] != lastalign || + charactersInternal[i][1] != lasty) # Found end of run - widthblocks.push([runstart,i,lastalign,totalwidth,lasty]) - runstart=i - totalwidth=0 + widthblocks.push([runstart, i, lastalign, totalwidth, lasty]) + runstart = i + totalwidth = 0 end - lastalign=charactersInternal[i][0] - lasty=charactersInternal[i][1] - extraspace=(charactersInternal[i][4]) ? charactersInternal[i][4] : 0 - totalwidth+=c[3]-extraspace + lastalign = charactersInternal[i][0] + lasty = charactersInternal[i][1] + extraspace = (charactersInternal[i][4]) ? charactersInternal[i][4] : 0 + totalwidth += c[3] - extraspace end - widthblocks.push([runstart,characters.length,lastalign,totalwidth,lasty]) + widthblocks.push([runstart, characters.length, lastalign, totalwidth, lasty]) if collapseAlignments # Calculate the total width of each line - totalLineWidths=[] - for block in widthblocks - y=block[4] + totalLineWidths = [] + widthblocks.each do |block| + y = block[4] if !totalLineWidths[y] - totalLineWidths[y]=0 + totalLineWidths[y] = 0 end - if totalLineWidths[y]!=0 + if totalLineWidths[y] != 0 # padding in case more than one line has different alignments - totalLineWidths[y]+=16 + totalLineWidths[y] += 16 end - totalLineWidths[y]+=block[3] + totalLineWidths[y] += block[3] end # Calculate a new width for the next step - widthDst=[widthDst,(totalLineWidths.compact.max || 0)].min + widthDst = [widthDst, (totalLineWidths.compact.max || 0)].min end # Now, based on the text runs found, recalculate Xs - for block in widthblocks - next if block[0]>=block[1] - for i in block[0]...block[1] + widthblocks.each do |block| + next if block[0] >= block[1] + (block[0]...block[1]).each do |i| case block[2] when 1 then characters[i][1] = xDst + (widthDst - block[3] - 4) + characters[i][1] when 2 then characters[i][1] = xDst + ((widthDst / 2) - (block[3] / 2)) + characters[i][1] @@ -868,16 +829,9 @@ def getFormattedText(bitmap,xDst,yDst,widthDst,heightDst,text,lineheight=32, end end # Remove all characters with Y greater or equal to _yDst_+_heightDst_ - if heightDst>=0 - for i in 0...characters.length - if characters[i][2]>=yDst+heightDst - characters[i]=nil - end - end - characters.compact! - end - bitmap.font=oldfont - dummybitmap.dispose if dummybitmap + characters.delete_if { |ch| ch[2] >= yDst + heightDst } if heightDst >= 0 + bitmap.font = oldfont + dummybitmap&.dispose return characters end @@ -886,299 +840,297 @@ def getFormattedText(bitmap,xDst,yDst,widthDst,heightDst,text,lineheight=32, #=============================================================================== # Draw text and images on a bitmap #=============================================================================== -def getLineBrokenText(bitmap,value,width,dims) - x=0 - y=0 - textheight=0 - ret=[] +def getLineBrokenText(bitmap, value, width, dims) + x = 0 + y = 0 + textheight = 0 + ret = [] if dims - dims[0]=0 - dims[1]=0 + dims[0] = 0 + dims[1] = 0 end - line=0 - position=0 - column=0 - return ret if !bitmap || bitmap.disposed? || width<=0 - textmsg=value.clone - ret.push(["",0,0,0,bitmap.text_size("X").height,0,0,0,0]) - while ((c = textmsg.slice!(/\n|(\S*([ \r\t\f]?))/)) != nil) - break if c=="" - length=c.scan(/./m).length - ccheck=c - if ccheck=="\n" - ret.push(["\n",x,y,0,textheight,line,position,column,0]) - x=0 - y+=(textheight==0) ? bitmap.text_size("X").height : textheight - line+=1 - textheight=0 - column=0 - position+=length - ret.push(["",x,y,0,textheight,line,position,column,0]) + line = 0 + position = 0 + column = 0 + return ret if !bitmap || bitmap.disposed? || width <= 0 + textmsg = value.clone + ret.push(["", 0, 0, 0, bitmap.text_size("X").height, 0, 0, 0, 0]) + while (c = textmsg.slice!(/\n|(\S*([ \r\t\f]?))/)) != nil + break if c == "" + length = c.scan(/./m).length + ccheck = c + if ccheck == "\n" + ret.push(["\n", x, y, 0, textheight, line, position, column, 0]) + x = 0 + y += (textheight == 0) ? bitmap.text_size("X").height : textheight + line += 1 + textheight = 0 + column = 0 + position += length + ret.push(["", x, y, 0, textheight, line, position, column, 0]) next end - words=[ccheck] - for i in 0...words.length - word=words[i] - if word && word!="" - textSize=bitmap.text_size(word) - textwidth=textSize.width - if x>0 && x+textwidth>=width-2 + words = [ccheck] + words.length.times do |i| + word = words[i] + if word && word != "" + textSize = bitmap.text_size(word) + textwidth = textSize.width + if x > 0 && x + textwidth >= width - 2 # Zero-length word break - ret.push(["",x,y,0,textheight,line,position,column,0]) - x=0 - column=0 - y+=(textheight==0) ? bitmap.text_size("X").height : textheight - line+=1 - textheight=0 + ret.push(["", x, y, 0, textheight, line, position, column, 0]) + x = 0 + column = 0 + y += (textheight == 0) ? bitmap.text_size("X").height : textheight + line += 1 + textheight = 0 end - textheight=[textheight,textSize.height].max - ret.push([word,x,y,textwidth,textheight,line,position,column,length]) - x+=textwidth - dims[0]=x if dims && dims[0]]+)>/ - reNoMatch=/]+>/ - return ret if !bitmap || bitmap.disposed? || width<=0 - textmsg=value.clone - color=Font.default_color + re = /]+)>/ + reNoMatch = /]+>/ + return ret if !bitmap || bitmap.disposed? || width <= 0 + textmsg = value.clone + color = Font.default_color while (c = textmsg.slice!(/\n|[^ \r\t\f\n\-]*\-+|(\S*([ \r\t\f]?))/)) != nil - break if c=="" - ccheck=c - if ccheck=="\n" - x=0 - y+=32 + break if c == "" + ccheck = c + if ccheck == "\n" + x = 0 + y += 32 next end + textcols = [] if ccheck[/0 && x+textwidth>width - minTextSize=bitmap.text_size(word.gsub(/\s*/,"")) - if x>0 && x+minTextSize.width>width - x=0 - y+=32 + words.length.times do |i| + word = words[i] + if word && word != "" + textSize = bitmap.text_size(word) + textwidth = textSize.width + if x > 0 && x + textwidth > width + minTextSize = bitmap.text_size(word.gsub(/\s*/, "")) + if x > 0 && x + minTextSize.width > width + x = 0 + y += 32 end end - ret.push([word,x,y,textwidth,32,color]) - x+=textwidth - dims[0]=x if dims && dims[0]"+text - chars=getFormattedText(bitmap,x,y,width,-1,text,lineheight) - drawFormattedChars(bitmap,chars) +def drawFormattedTextEx(bitmap, x, y, width, text, baseColor = nil, shadowColor = nil, lineheight = 32) + base = baseColor ? baseColor.clone : Color.new(96, 96, 96) + shadow = shadowColor ? shadowColor.clone : Color.new(208, 208, 200) + text = "" + text + chars = getFormattedText(bitmap, x, y, width, -1, text, lineheight) + drawFormattedChars(bitmap, chars) end # Unused -def pbDrawShadow(bitmap,x,y,width,height,string) +def pbDrawShadow(bitmap, x, y, width, height, string) return if !bitmap || !string - pbDrawShadowText(bitmap,x,y,width,height,string,nil,bitmap.font.color) + pbDrawShadowText(bitmap, x, y, width, height, string, nil, bitmap.font.color) end -def pbDrawShadowText(bitmap,x,y,width,height,string,baseColor,shadowColor=nil,align=0) +def pbDrawShadowText(bitmap, x, y, width, height, string, baseColor, shadowColor = nil, align = 0) return if !bitmap || !string - width=(width<0) ? bitmap.text_size(string).width+1 : width - height=(height<0) ? bitmap.text_size(string).height+1 : height - y += 4 - if shadowColor && shadowColor.alpha>0 - bitmap.font.color=shadowColor - bitmap.draw_text(x+2,y,width,height,string,align) - bitmap.draw_text(x,y+2,width,height,string,align) - bitmap.draw_text(x+2,y+2,width,height,string,align) + width = (width < 0) ? bitmap.text_size(string).width + 1 : width + height = (height < 0) ? bitmap.text_size(string).height + 1 : height + if shadowColor && shadowColor.alpha > 0 + bitmap.font.color = shadowColor + bitmap.draw_text(x + 2, y, width, height, string, align) + bitmap.draw_text(x, y + 2, width, height, string, align) + bitmap.draw_text(x + 2, y + 2, width, height, string, align) end - if baseColor && baseColor.alpha>0 - bitmap.font.color=baseColor - bitmap.draw_text(x,y,width,height,string,align) + if baseColor && baseColor.alpha > 0 + bitmap.font.color = baseColor + bitmap.draw_text(x, y, width, height, string, align) end end -def pbDrawOutlineText(bitmap,x,y,width,height,string,baseColor,shadowColor=nil,align=0) +def pbDrawOutlineText(bitmap, x, y, width, height, string, baseColor, shadowColor = nil, align = 0) return if !bitmap || !string - width=(width<0) ? bitmap.text_size(string).width+4 : width - height=(height<0) ? bitmap.text_size(string).height+4 : height - if shadowColor && shadowColor.alpha>0 - bitmap.font.color=shadowColor - bitmap.draw_text(x-2,y-2,width,height,string,align) - bitmap.draw_text(x,y-2,width,height,string,align) - bitmap.draw_text(x+2,y-2,width,height,string,align) - bitmap.draw_text(x-2,y,width,height,string,align) - bitmap.draw_text(x+2,y,width,height,string,align) - bitmap.draw_text(x-2,y+2,width,height,string,align) - bitmap.draw_text(x,y+2,width,height,string,align) - bitmap.draw_text(x+2,y+2,width,height,string,align) + width = (width < 0) ? bitmap.text_size(string).width + 4 : width + height = (height < 0) ? bitmap.text_size(string).height + 4 : height + if shadowColor && shadowColor.alpha > 0 + bitmap.font.color = shadowColor + bitmap.draw_text(x - 2, y - 2, width, height, string, align) + bitmap.draw_text(x, y - 2, width, height, string, align) + bitmap.draw_text(x + 2, y - 2, width, height, string, align) + bitmap.draw_text(x - 2, y, width, height, string, align) + bitmap.draw_text(x + 2, y, width, height, string, align) + bitmap.draw_text(x - 2, y + 2, width, height, string, align) + bitmap.draw_text(x, y + 2, width, height, string, align) + bitmap.draw_text(x + 2, y + 2, width, height, string, align) end - if baseColor && baseColor.alpha>0 - bitmap.font.color=baseColor - bitmap.draw_text(x,y,width,height,string,align) + if baseColor && baseColor.alpha > 0 + bitmap.font.color = baseColor + bitmap.draw_text(x, y, width, height, string, align) end end @@ -1192,20 +1144,21 @@ def pbDrawOutlineText(bitmap,x,y,width,height,string,baseColor,shadowColor=nil,a # 4 - Base color # 5 - Shadow color # 6 - If true or 1, the text has an outline. Otherwise, the text has a shadow. -def pbDrawTextPositions(bitmap,textpos) - for i in textpos +def pbDrawTextPositions(bitmap, textpos) + textpos.each do |i| textsize = bitmap.text_size(i[0]) x = i[1] - y = i[2] + 6 - if i[3]==true || i[3]==1 # right align + y = i[2] + case i[3] + when true, 1 # right align x -= textsize.width - elsif i[3]==2 # centered - x -= (textsize.width/2) + when 2 # centered + x -= (textsize.width / 2) end - if i[6]==true || i[6]==1 # outline text - pbDrawOutlineText(bitmap,x,y,textsize.width,textsize.height,i[0],i[4],i[5]) + if i[6] == true || i[6] == 1 # outline text + pbDrawOutlineText(bitmap, x, y, textsize.width, textsize.height, i[0], i[4], i[5]) else - pbDrawShadowText(bitmap,x,y,textsize.width,textsize.height,i[0],i[4],i[5]) + pbDrawShadowText(bitmap, x, y, textsize.width, textsize.height, i[0], i[4], i[5]) end end end @@ -1215,22 +1168,22 @@ def pbDrawTextPositions(bitmap,textpos) #=============================================================================== # Draw images on a bitmap #=============================================================================== -def pbCopyBitmap(dstbm,srcbm,x,y,opacity=255) - rc = Rect.new(0,0,srcbm.width,srcbm.height) - dstbm.blt(x,y,srcbm,rc,opacity) +def pbCopyBitmap(dstbm, srcbm, x, y, opacity = 255) + rc = Rect.new(0, 0, srcbm.width, srcbm.height) + dstbm.blt(x, y, srcbm, rc, opacity) end -def pbDrawImagePositions(bitmap,textpos) - for i in textpos - srcbitmap=AnimatedBitmap.new(pbBitmapName(i[0])) - x=i[1] - y=i[2] - srcx=i[3] || 0 - srcy=i[4] || 0 - width=(i[5] && i[5]>=0) ? i[5] : srcbitmap.width - height=(i[6] && i[6]>=0) ? i[6] : srcbitmap.height - srcrect=Rect.new(srcx,srcy,width,height) - bitmap.blt(x,y,srcbitmap.bitmap,srcrect) +def pbDrawImagePositions(bitmap, textpos) + textpos.each do |i| + srcbitmap = AnimatedBitmap.new(pbBitmapName(i[0])) + x = i[1] + y = i[2] + srcx = i[3] || 0 + srcy = i[4] || 0 + width = (i[5] && i[5] >= 0) ? i[5] : srcbitmap.width + height = (i[6] && i[6] >= 0) ? i[6] : srcbitmap.height + srcrect = Rect.new(srcx, srcy, width, height) + bitmap.blt(x, y, srcbitmap.bitmap, srcrect) srcbitmap.dispose end end diff --git a/Data/Scripts/007_Objects and windows/011_Messages.rb b/Data/Scripts/007_Objects and windows/011_Messages.rb index 20effc8e26..0fa08333c8 100644 --- a/Data/Scripts/007_Objects and windows/011_Messages.rb +++ b/Data/Scripts/007_Objects and windows/011_Messages.rb @@ -1,102 +1,22 @@ #=============================================================================== # #=============================================================================== -class Scene_Map - def updatemini - oldmws=$game_temp.message_window_showing - $game_temp.message_window_showing=true - loop do - $game_map.update - $game_player.update - $game_system.update - if $game_screen - $game_screen.update - else - $game_map.screen.update - end - break unless $game_temp.player_transferring - transfer_player - break if $game_temp.transition_processing - end - $game_temp.message_window_showing=oldmws - @spriteset.update if @spriteset - @message_window.update if @message_window - end -end - - - -class Scene_Battle - def updatemini - if self.respond_to?("update_basic") - update_basic(true) - update_info_viewport # Update information viewport - else - oldmws=$game_temp.message_window_showing - $game_temp.message_window_showing=true - # Update system (timer) and screen - $game_system.update - if $game_screen - $game_screen.update - else - $game_map.screen.update - end - # If timer has reached 0 - if $game_system.timer_working && $game_system.timer == 0 - # Abort battle - $game_temp.battle_abort = true - end - # Update windows - @help_window.update if @help_window - @party_command_window.update if @party_command_window - @actor_command_window.update if @actor_command_window - @status_window.update if @status_window - $game_temp.message_window_showing=oldmws - @message_window.update if @message_window - # Update sprite set - @spriteset.update if @spriteset - end - end -end - - - def pbMapInterpreter - if $game_map.respond_to?("interpreter") - return $game_map.interpreter - elsif $game_system - return $game_system.map_interpreter - end - return nil + return $game_system&.map_interpreter end def pbMapInterpreterRunning? interp = pbMapInterpreter - return interp && interp.running? + return interp&.running? end +# Unused def pbRefreshSceneMap - if $scene && $scene.is_a?(Scene_Map) - if $scene.respond_to?("miniupdate") - $scene.miniupdate - else - $scene.updatemini - end - elsif $scene && $scene.is_a?(Scene_Battle) - $scene.updatemini - end + $scene.miniupdate if $scene.is_a?(Scene_Map) end def pbUpdateSceneMap - if $scene && $scene.is_a?(Scene_Map) && !pbIsFaded? - if $scene.respond_to?("miniupdate") - $scene.miniupdate - else - $scene.updatemini - end - elsif $scene && $scene.is_a?(Scene_Battle) - $scene.updatemini - end + $scene.miniupdate if $scene.is_a?(Scene_Map) && !pbIsFaded? end #=============================================================================== @@ -107,28 +27,26 @@ def pbEventCommentInput(*args) list = args[0].list # List of commands for event or event page elements = args[1] # Number of elements trigger = args[2] # Trigger - return nil if list == nil + return nil if list.nil? return nil unless list.is_a?(Array) - for item in list - next unless item.code == 108 || item.code == 408 - if item.parameters[0] == trigger - start = list.index(item) + 1 - finish = start + elements - for id in start...finish - next if !list[id] - parameters.push(list[id].parameters[0]) - end - return parameters + list.each do |item| + next if ![108, 108].include?(item.code) + next if item.parameters[0] != trigger + start = list.index(item) + 1 + finish = start + elements + (start...finish).each do |id| + parameters.push(list[id].parameters[0]) if list[id] end + return parameters end return nil end -def pbCurrentEventCommentInput(elements,trigger) +def pbCurrentEventCommentInput(elements, trigger) return nil if !pbMapInterpreterRunning? - event = pbMapInterpreter.get_character(0) + event = pbMapInterpreter.get_self return nil if !event - return pbEventCommentInput(event,elements,trigger) + return pbEventCommentInput(event, elements, trigger) end @@ -137,63 +55,58 @@ def pbCurrentEventCommentInput(elements,trigger) # #=============================================================================== class ChooseNumberParams + attr_reader :messageSkin # Set the full path for the message's window skin + attr_reader :skin + def initialize - @maxDigits=0 - @minNumber=0 - @maxNumber=0 - @skin=nil - @messageSkin=nil - @negativesAllowed=false - @initialNumber=0 - @cancelNumber=nil + @maxDigits = 0 + @minNumber = 0 + @maxNumber = 0 + @skin = nil + @messageSkin = nil + @negativesAllowed = false + @initialNumber = 0 + @cancelNumber = nil end def setMessageSkin(value) - @messageSkin=value - end - - def messageSkin # Set the full path for the message's window skin - @messageSkin + @messageSkin = value end def setSkin(value) - @skin=value - end - - def skin - @skin + @skin = value end def setNegativesAllowed(value) - @negativeAllowed=value + @negativeAllowed = value end def negativesAllowed @negativeAllowed ? true : false end - def setRange(minNumber,maxNumber) - maxNumber=minNumber if minNumber>maxNumber - @maxDigits=0 - @minNumber=minNumber - @maxNumber=maxNumber + def setRange(minNumber, maxNumber) + maxNumber = minNumber if minNumber > maxNumber + @maxDigits = 0 + @minNumber = minNumber + @maxNumber = maxNumber end def setDefaultValue(number) - @initialNumber=number - @cancelNumber=nil + @initialNumber = number + @cancelNumber = nil end def setInitialValue(number) - @initialNumber=number + @initialNumber = number end def setCancelValue(number) - @cancelNumber=number + @cancelNumber = number end def initialNumber - return clamp(@initialNumber,self.minNumber,self.maxNumber) + return clamp(@initialNumber, self.minNumber, self.maxNumber) end def cancelNumber @@ -201,51 +114,51 @@ def cancelNumber end def minNumber - ret=0 - if @maxDigits>0 - ret=-((10**@maxDigits)-1) + ret = 0 + if @maxDigits > 0 + ret = -((10**@maxDigits) - 1) else - ret=@minNumber + ret = @minNumber end - ret=0 if !@negativeAllowed && ret<0 + ret = 0 if !@negativeAllowed && ret < 0 return ret end def maxNumber - ret=0 - if @maxDigits>0 - ret=((10**@maxDigits)-1) + ret = 0 + if @maxDigits > 0 + ret = ((10**@maxDigits) - 1) else - ret=@maxNumber + ret = @maxNumber end - ret=0 if !@negativeAllowed && ret<0 + ret = 0 if !@negativeAllowed && ret < 0 return ret end def setMaxDigits(value) - @maxDigits=[1,value].max + @maxDigits = [1, value].max end def maxDigits - if @maxDigits>0 + if @maxDigits > 0 return @maxDigits else - return [numDigits(self.minNumber),numDigits(self.maxNumber)].max + return [numDigits(self.minNumber), numDigits(self.maxNumber)].max end end private - def clamp(v,mn,mx) - return vmx ? mx : v) + def clamp(v, mn, mx) + return v < mn ? mn : (v > mx ? mx : v) end def numDigits(number) ans = 1 - number=number.abs + number = number.abs while number >= 10 - ans+=1 - number/=10 + ans += 1 + number /= 10 end return ans end @@ -253,40 +166,40 @@ def numDigits(number) -def pbChooseNumber(msgwindow,params) +def pbChooseNumber(msgwindow, params) return 0 if !params - ret=0 - maximum=params.maxNumber - minimum=params.minNumber - defaultNumber=params.initialNumber - cancelNumber=params.cancelNumber - cmdwindow=Window_InputNumberPokemon.new(params.maxDigits) - cmdwindow.z=99999 - cmdwindow.visible=true + ret = 0 + maximum = params.maxNumber + minimum = params.minNumber + defaultNumber = params.initialNumber + cancelNumber = params.cancelNumber + cmdwindow = Window_InputNumberPokemon.new(params.maxDigits) + cmdwindow.z = 99999 + cmdwindow.visible = true cmdwindow.setSkin(params.skin) if params.skin - cmdwindow.sign=params.negativesAllowed # must be set before number - cmdwindow.number=defaultNumber - pbPositionNearMsgWindow(cmdwindow,msgwindow,:right) + cmdwindow.sign = params.negativesAllowed # must be set before number + cmdwindow.number = defaultNumber + pbPositionNearMsgWindow(cmdwindow, msgwindow, :right) loop do Graphics.update Input.update pbUpdateSceneMap cmdwindow.update - msgwindow.update if msgwindow + msgwindow&.update yield if block_given? if Input.trigger?(Input::USE) - ret=cmdwindow.number - if ret>maximum - pbPlayBuzzerSE() - elsif ret maximum + pbPlayBuzzerSE + elsif ret < minimum + pbPlayBuzzerSE else - pbPlayDecisionSE() + pbPlayDecisionSE break end elsif Input.trigger?(Input::BACK) - pbPlayCancelSE() - ret=cancelNumber + pbPlayCancelSE + ret = cancelNumber break end end @@ -302,35 +215,31 @@ def pbChooseNumber(msgwindow,params) #=============================================================================== class FaceWindowVX < SpriteWindow_Base def initialize(face) - super(0,0,128,128) - faceinfo=face.split(",") - facefile=pbResolveBitmap("Graphics/Faces/"+faceinfo[0]) - facefile=pbResolveBitmap("Graphics/Pictures/"+faceinfo[0]) if !facefile - self.contents.dispose if self.contents - @faceIndex=faceinfo[1].to_i - @facebitmaptmp=AnimatedBitmap.new(facefile) - @facebitmap=BitmapWrapper.new(96,96) - @facebitmap.blt(0,0,@facebitmaptmp.bitmap,Rect.new( - (@faceIndex % 4) * 96, - (@faceIndex / 4) * 96, 96, 96 - )) - self.contents=@facebitmap + super(0, 0, 128, 128) + faceinfo = face.split(",") + facefile = pbResolveBitmap("Graphics/Faces/" + faceinfo[0]) + facefile = pbResolveBitmap("Graphics/Pictures/" + faceinfo[0]) if !facefile + self.contents&.dispose + @faceIndex = faceinfo[1].to_i + @facebitmaptmp = AnimatedBitmap.new(facefile) + @facebitmap = BitmapWrapper.new(96, 96) + @facebitmap.blt(0, 0, @facebitmaptmp.bitmap, + Rect.new((@faceIndex % 4) * 96, (@faceIndex / 4) * 96, 96, 96)) + self.contents = @facebitmap end def update super - if @facebitmaptmp.totalFrames>1 + if @facebitmaptmp.totalFrames > 1 @facebitmaptmp.update - @facebitmap.blt(0,0,@facebitmaptmp.bitmap,Rect.new( - (@faceIndex % 4) * 96, - (@faceIndex / 4) * 96, 96, 96 - )) + @facebitmap.blt(0, 0, @facebitmaptmp.bitmap, + Rect.new((@faceIndex % 4) * 96, (@faceIndex / 4) * 96, 96, 96)) end end def dispose @facebitmaptmp.dispose - @facebitmap.dispose if @facebitmap + @facebitmap&.dispose super end end @@ -351,50 +260,51 @@ def pbGetBasicMapNameFromId(id) end def pbGetMapNameFromId(id) - map=pbGetBasicMapNameFromId(id) - map.gsub!(/\\PN/,$Trainer.name) if $Trainer - return map + name = pbGetMessage(MessageTypes::MapNames, id) + name = pbGetBasicMapNameFromId(id) if nil_or_empty?(name) + name.gsub!(/\\PN/, $player.name) if $player + return name end def pbCsvField!(str) - ret="" - str.sub!(/\A\s*/,"") - if str[0,1]=="\"" - str[0,1]="" - escaped=false - fieldbytes=0 + ret = "" + str.sub!(/\A\s*/, "") + if str[0, 1] == "\"" + str[0, 1] = "" + escaped = false + fieldbytes = 0 str.scan(/./) do |s| - fieldbytes+=s.length - break if s=="\"" && !escaped - if s=="\\" && !escaped - escaped=true + fieldbytes += s.length + break if s == "\"" && !escaped + if s == "\\" && !escaped + escaped = true else - ret+=s - escaped=false + ret += s + escaped = false end end - str[0,fieldbytes]="" + str[0, fieldbytes] = "" if !str[/\A\s*,/] && !str[/\A\s*$/] - raise _INTL("Invalid quoted field (in: {1})",ret) + raise _INTL("Invalid quoted field (in: {1})", ret) end - str[0,str.length]=$~.post_match + str[0, str.length] = $~.post_match else if str[/,/] - str[0,str.length]=$~.post_match - ret=$~.pre_match + str[0, str.length] = $~.post_match + ret = $~.pre_match else - ret=str.clone - str[0,str.length]="" + ret = str.clone + str[0, str.length] = "" end - ret.gsub!(/\s+$/,"") + ret.gsub!(/\s+$/, "") end return ret end def pbCsvPosInt!(str) - ret=pbCsvField!(str) + ret = pbCsvField!(str) if !ret[/\A\d+$/] - raise _INTL("Field {1} is not a positive integer",ret) + raise _INTL("Field {1} is not a positive integer", ret) end return ret.to_i end @@ -405,64 +315,54 @@ def pbCsvPosInt!(str) # Money and coins windows #=============================================================================== def pbGetGoldString - moneyString="" - begin - moneyString=_INTL("${1}",$Trainer.money.to_s_formatted) - rescue - if $data_system.respond_to?("words") - moneyString=_INTL("{1} {2}",$game_party.gold,$data_system.words.gold) - else - moneyString=_INTL("{1} {2}",$game_party.gold,Vocab.gold) - end - end - return moneyString + return _INTL("${1}", $player.money.to_s_formatted) end def pbDisplayGoldWindow(msgwindow) - moneyString=pbGetGoldString() - goldwindow=Window_AdvancedTextPokemon.new(_INTL("Money:\n{1}",moneyString)) + moneyString = pbGetGoldString + goldwindow = Window_AdvancedTextPokemon.new(_INTL("Money:\n{1}", moneyString)) goldwindow.setSkin("Graphics/Windowskins/goldskin") - goldwindow.resizeToFit(goldwindow.text,Graphics.width) - goldwindow.width=160 if goldwindow.width<=160 - if msgwindow.y==0 - goldwindow.y=Graphics.height-goldwindow.height + goldwindow.resizeToFit(goldwindow.text, Graphics.width) + goldwindow.width = 160 if goldwindow.width <= 160 + if msgwindow.y == 0 + goldwindow.y = Graphics.height - goldwindow.height else - goldwindow.y=0 + goldwindow.y = 0 end - goldwindow.viewport=msgwindow.viewport - goldwindow.z=msgwindow.z + goldwindow.viewport = msgwindow.viewport + goldwindow.z = msgwindow.z return goldwindow end -def pbDisplayCoinsWindow(msgwindow,goldwindow) - coinString=($Trainer) ? $Trainer.coins.to_s_formatted : "0" - coinwindow=Window_AdvancedTextPokemon.new(_INTL("Coins:\n{1}",coinString)) +def pbDisplayCoinsWindow(msgwindow, goldwindow) + coinString = ($player) ? $player.coins.to_s_formatted : "0" + coinwindow = Window_AdvancedTextPokemon.new(_INTL("Coins:\n{1}", coinString)) coinwindow.setSkin("Graphics/Windowskins/goldskin") - coinwindow.resizeToFit(coinwindow.text,Graphics.width) - coinwindow.width=160 if coinwindow.width<=160 - if msgwindow.y==0 - coinwindow.y=(goldwindow) ? goldwindow.y-coinwindow.height : Graphics.height-coinwindow.height + coinwindow.resizeToFit(coinwindow.text, Graphics.width) + coinwindow.width = 160 if coinwindow.width <= 160 + if msgwindow.y == 0 + coinwindow.y = (goldwindow) ? goldwindow.y - coinwindow.height : Graphics.height - coinwindow.height else - coinwindow.y=(goldwindow) ? goldwindow.height : 0 + coinwindow.y = (goldwindow) ? goldwindow.height : 0 end - coinwindow.viewport=msgwindow.viewport - coinwindow.z=msgwindow.z + coinwindow.viewport = msgwindow.viewport + coinwindow.z = msgwindow.z return coinwindow end def pbDisplayBattlePointsWindow(msgwindow) - pointsString = ($Trainer) ? $Trainer.battle_points.to_s_formatted : "0" - pointswindow=Window_AdvancedTextPokemon.new(_INTL("Battle Points:\n{1}", pointsString)) + pointsString = ($player) ? $player.battle_points.to_s_formatted : "0" + pointswindow = Window_AdvancedTextPokemon.new(_INTL("Battle Points:\n{1}", pointsString)) pointswindow.setSkin("Graphics/Windowskins/goldskin") - pointswindow.resizeToFit(pointswindow.text,Graphics.width) - pointswindow.width=160 if pointswindow.width<=160 - if msgwindow.y==0 - pointswindow.y=Graphics.height-pointswindow.height + pointswindow.resizeToFit(pointswindow.text, Graphics.width) + pointswindow.width = 160 if pointswindow.width <= 160 + if msgwindow.y == 0 + pointswindow.y = Graphics.height - pointswindow.height else - pointswindow.y=0 + pointswindow.y = 0 end - pointswindow.viewport=msgwindow.viewport - pointswindow.z=msgwindow.z + pointswindow.viewport = msgwindow.viewport + pointswindow.z = msgwindow.z return pointswindow end @@ -471,40 +371,40 @@ def pbDisplayBattlePointsWindow(msgwindow) #=============================================================================== # #=============================================================================== -def pbCreateStatusWindow(viewport=nil) - msgwindow=Window_AdvancedTextPokemon.new("") - if !viewport - msgwindow.z=99999 +def pbCreateStatusWindow(viewport = nil) + msgwindow = Window_AdvancedTextPokemon.new("") + if viewport + msgwindow.viewport = viewport else - msgwindow.viewport=viewport + msgwindow.z = 99999 end - msgwindow.visible=false - msgwindow.letterbyletter=false - pbBottomLeftLines(msgwindow,2) - skinfile=MessageConfig.pbGetSpeechFrame() + msgwindow.visible = false + msgwindow.letterbyletter = false + pbBottomLeftLines(msgwindow, 2) + skinfile = MessageConfig.pbGetSpeechFrame msgwindow.setSkin(skinfile) return msgwindow end -def pbCreateMessageWindow(viewport=nil,skin=nil) - msgwindow=Window_AdvancedTextPokemon.new("") - if !viewport - msgwindow.z=99999 +def pbCreateMessageWindow(viewport = nil, skin = nil) + msgwindow = Window_AdvancedTextPokemon.new("") + if viewport + msgwindow.viewport = viewport else - msgwindow.viewport=viewport - end - msgwindow.visible=true - msgwindow.letterbyletter=true - msgwindow.back_opacity=MessageConfig::WINDOW_OPACITY - pbBottomLeftLines(msgwindow,2) - $game_temp.message_window_showing=true if $game_temp - skin=MessageConfig.pbGetSpeechFrame() if !skin + msgwindow.z = 99999 + end + msgwindow.visible = true + msgwindow.letterbyletter = true + msgwindow.back_opacity = MessageConfig::WINDOW_OPACITY + pbBottomLeftLines(msgwindow, 2) + $game_temp.message_window_showing = true if $game_temp + skin = MessageConfig.pbGetSpeechFrame if !skin msgwindow.setSkin(skin) return msgwindow end def pbDisposeMessageWindow(msgwindow) - $game_temp.message_window_showing=false if $game_temp + $game_temp.message_window_showing = false if $game_temp msgwindow.dispose end @@ -513,60 +413,60 @@ def pbDisposeMessageWindow(msgwindow) #=============================================================================== # Main message-displaying function #=============================================================================== -def pbMessageDisplay(msgwindow,message,letterbyletter=true,commandProc=nil) +def pbMessageDisplay(msgwindow, message, letterbyletter = true, commandProc = nil) return if !msgwindow - oldletterbyletter=msgwindow.letterbyletter - msgwindow.letterbyletter=(letterbyletter) ? true : false - ret=nil - commands=nil - facewindow=nil - goldwindow=nil - coinwindow=nil - battlepointswindow=nil - cmdvariable=0 - cmdIfCancel=0 - msgwindow.waitcount=0 - autoresume=false - text=message.clone - msgback=nil - linecount=(Graphics.height>400) ? 3 : 2 + oldletterbyletter = msgwindow.letterbyletter + msgwindow.letterbyletter = (letterbyletter) ? true : false + ret = nil + commands = nil + facewindow = nil + goldwindow = nil + coinwindow = nil + battlepointswindow = nil + cmdvariable = 0 + cmdIfCancel = 0 + msgwindow.waitcount = 0 + autoresume = false + text = message.clone + msgback = nil + linecount = (Graphics.height > 400) ? 3 : 2 ### Text replacement text.gsub!(/\\sign\[([^\]]*)\]/i) { # \sign[something] gets turned into - next "\\op\\cl\\ts[]\\w["+$1+"]" # \op\cl\ts[]\w[something] + next "\\op\\cl\\ts[]\\w[" + $1 + "]" # \op\cl\ts[]\w[something] } - text.gsub!(/\\\\/,"\5") - text.gsub!(/\\1/,"\1") + text.gsub!(/\\\\/, "\5") + text.gsub!(/\\1/, "\1") if $game_actors text.gsub!(/\\n\[([1-8])\]/i) { m = $1.to_i next $game_actors[m].name } end - text.gsub!(/\\pn/i,$Trainer.name) if $Trainer - text.gsub!(/\\pm/i,_INTL("${1}",$Trainer.money.to_s_formatted)) if $Trainer - text.gsub!(/\\n/i,"\n") - text.gsub!(/\\\[([0-9a-f]{8,8})\]/i) { "" } - text.gsub!(/\\pg/i,"\\b") if $Trainer && $Trainer.male? - text.gsub!(/\\pg/i,"\\r") if $Trainer && $Trainer.female? - text.gsub!(/\\pog/i,"\\r") if $Trainer && $Trainer.male? - text.gsub!(/\\pog/i,"\\b") if $Trainer && $Trainer.female? - text.gsub!(/\\pg/i,"") - text.gsub!(/\\pog/i,"") - text.gsub!(/\\b/i,"") - text.gsub!(/\\r/i,"") + text.gsub!(/\\pn/i, $player.name) if $player + text.gsub!(/\\pm/i, _INTL("${1}", $player.money.to_s_formatted)) if $player + text.gsub!(/\\n/i, "\n") + text.gsub!(/\\\[([0-9a-f]{8,8})\]/i) { "" } + text.gsub!(/\\pg/i, "\\b") if $player&.male? + text.gsub!(/\\pg/i, "\\r") if $player&.female? + text.gsub!(/\\pog/i, "\\r") if $player&.male? + text.gsub!(/\\pog/i, "\\b") if $player&.female? + text.gsub!(/\\pg/i, "") + text.gsub!(/\\pog/i, "") + text.gsub!(/\\b/i, "") + text.gsub!(/\\r/i, "") text.gsub!(/\\[Ww]\[([^\]]*)\]/) { w = $1.to_s - if w=="" + if w == "" msgwindow.windowskin = nil else - msgwindow.setSkin("Graphics/Windowskins/#{w}",false) + msgwindow.setSkin("Graphics/Windowskins/#{w}", false) end next "" } isDarkSkin = isDarkWindowskin(msgwindow.windowskin) - text.gsub!(/\\[Cc]\[([0-9]+)\]/) { + text.gsub!(/\\c\[([0-9]+)\]/i) { m = $1.to_i - next getSkinColor(msgwindow.windowskin,m,isDarkSkin) + next getSkinColor(msgwindow.windowskin, m, isDarkSkin) } loop do last_text = text.clone @@ -576,37 +476,36 @@ def pbMessageDisplay(msgwindow,message,letterbyletter=true,commandProc=nil) loop do last_text = text.clone text.gsub!(/\\l\[([0-9]+)\]/i) { - linecount = [1,$1.to_i].max + linecount = [1, $1.to_i].max next "" } break if text == last_text end colortag = "" - if $game_system && $game_system.respond_to?("message_frame") && - $game_system.message_frame != 0 - colortag = getSkinColor(msgwindow.windowskin,0,true) + if $game_system && $game_system.message_frame != 0 + colortag = getSkinColor(msgwindow.windowskin, 0, true) else - colortag = getSkinColor(msgwindow.windowskin,0,isDarkSkin) + colortag = getSkinColor(msgwindow.windowskin, 0, isDarkSkin) end - text = colortag+text + text = colortag + text ### Controls - textchunks=[] - controls=[] + textchunks = [] + controls = [] while text[/(?:\\(f|ff|ts|cl|me|se|wt|wtnp|ch)\[([^\]]*)\]|\\(g|cn|pt|wd|wm|op|cl|wu|\.|\||\!|\^))/i] textchunks.push($~.pre_match) if $~[1] - controls.push([$~[1].downcase,$~[2],-1]) + controls.push([$~[1].downcase, $~[2], -1]) else - controls.push([$~[3].downcase,"",-1]) + controls.push([$~[3].downcase, "", -1]) end - text=$~.post_match + text = $~.post_match end textchunks.push(text) - for chunk in textchunks - chunk.gsub!(/\005/,"\\") + textchunks.each do |chunk| + chunk.gsub!(/\005/, "\\") end textlen = 0 - for i in 0...controls.length + controls.length.times do |i| control = controls[i][0] case control when "wt", "wtnp", ".", "|" @@ -617,125 +516,126 @@ def pbMessageDisplay(msgwindow,message,letterbyletter=true,commandProc=nil) textlen += toUnformattedText(textchunks[i]).scan(/./m).length controls[i][2] = textlen end - text = textchunks.join("") + text = textchunks.join signWaitCount = 0 - signWaitTime = Graphics.frame_rate/2 + signWaitTime = Graphics.frame_rate / 2 haveSpecialClose = false specialCloseSE = "" - for i in 0...controls.length + startSE = nil + controls.length.times do |i| control = controls[i][0] param = controls[i][1] case control when "op" - signWaitCount = signWaitTime+1 + signWaitCount = signWaitTime + 1 when "cl" - text = text.sub(/\001\z/,"") # fix: '$' can match end of line as well + text = text.sub(/\001\z/, "") # fix: '$' can match end of line as well haveSpecialClose = true specialCloseSE = param when "f" - facewindow.dispose if facewindow + facewindow&.dispose facewindow = PictureWindow.new("Graphics/Pictures/#{param}") when "ff" - facewindow.dispose if facewindow + facewindow&.dispose facewindow = FaceWindowVX.new(param) when "ch" cmds = param.clone cmdvariable = pbCsvPosInt!(cmds) cmdIfCancel = pbCsvField!(cmds).to_i commands = [] - while cmds.length>0 + while cmds.length > 0 commands.push(pbCsvField!(cmds)) end when "wtnp", "^" - text = text.sub(/\001\z/,"") # fix: '$' can match end of line as well + text = text.sub(/\001\z/, "") # fix: '$' can match end of line as well when "se" - if controls[i][2]==0 + if controls[i][2] == 0 startSE = param controls[i] = nil end end end - if startSE!=nil + if startSE pbSEPlay(pbStringToAudioFile(startSE)) - elsif signWaitCount==0 && letterbyletter - pbPlayDecisionSE() + elsif signWaitCount == 0 && letterbyletter + pbPlayDecisionSE end ########## Position message window ############## - pbRepositionMessageWindow(msgwindow,linecount) + pbRepositionMessageWindow(msgwindow, linecount) if facewindow - pbPositionNearMsgWindow(facewindow,msgwindow,:left) + pbPositionNearMsgWindow(facewindow, msgwindow, :left) facewindow.viewport = msgwindow.viewport facewindow.z = msgwindow.z end - atTop = (msgwindow.y==0) + atTop = (msgwindow.y == 0) ########## Show text ############################# msgwindow.text = text - Graphics.frame_reset if Graphics.frame_rate>40 + Graphics.frame_reset if Graphics.frame_rate > 40 loop do - if signWaitCount>0 + if signWaitCount > 0 signWaitCount -= 1 if atTop - msgwindow.y = -msgwindow.height*signWaitCount/signWaitTime + msgwindow.y = -msgwindow.height * signWaitCount / signWaitTime else - msgwindow.y = Graphics.height-msgwindow.height*(signWaitTime-signWaitCount)/signWaitTime + msgwindow.y = Graphics.height - (msgwindow.height * (signWaitTime - signWaitCount) / signWaitTime) end end - for i in 0...controls.length + controls.length.times do |i| next if !controls[i] - next if controls[i][2]>msgwindow.position || msgwindow.waitcount!=0 + next if controls[i][2] > msgwindow.position || msgwindow.waitcount != 0 control = controls[i][0] param = controls[i][1] case control when "f" - facewindow.dispose if facewindow + facewindow&.dispose facewindow = PictureWindow.new("Graphics/Pictures/#{param}") - pbPositionNearMsgWindow(facewindow,msgwindow,:left) + pbPositionNearMsgWindow(facewindow, msgwindow, :left) facewindow.viewport = msgwindow.viewport facewindow.z = msgwindow.z when "ff" - facewindow.dispose if facewindow + facewindow&.dispose facewindow = FaceWindowVX.new(param) - pbPositionNearMsgWindow(facewindow,msgwindow,:left) + pbPositionNearMsgWindow(facewindow, msgwindow, :left) facewindow.viewport = msgwindow.viewport facewindow.z = msgwindow.z when "g" # Display gold window - goldwindow.dispose if goldwindow + goldwindow&.dispose goldwindow = pbDisplayGoldWindow(msgwindow) when "cn" # Display coins window - coinwindow.dispose if coinwindow - coinwindow = pbDisplayCoinsWindow(msgwindow,goldwindow) + coinwindow&.dispose + coinwindow = pbDisplayCoinsWindow(msgwindow, goldwindow) when "pt" # Display battle points window - battlepointswindow.dispose if battlepointswindow + battlepointswindow&.dispose battlepointswindow = pbDisplayBattlePointsWindow(msgwindow) when "wu" msgwindow.y = 0 atTop = true msgback.y = msgwindow.y if msgback - pbPositionNearMsgWindow(facewindow,msgwindow,:left) - msgwindow.y = -msgwindow.height*signWaitCount/signWaitTime + pbPositionNearMsgWindow(facewindow, msgwindow, :left) + msgwindow.y = -msgwindow.height * signWaitCount / signWaitTime when "wm" atTop = false - msgwindow.y = (Graphics.height-msgwindow.height)/2 + msgwindow.y = (Graphics.height - msgwindow.height) / 2 msgback.y = msgwindow.y if msgback - pbPositionNearMsgWindow(facewindow,msgwindow,:left) + pbPositionNearMsgWindow(facewindow, msgwindow, :left) when "wd" atTop = false - msgwindow.y = Graphics.height-msgwindow.height + msgwindow.y = Graphics.height - msgwindow.height msgback.y = msgwindow.y if msgback - pbPositionNearMsgWindow(facewindow,msgwindow,:left) - msgwindow.y = Graphics.height-msgwindow.height*(signWaitTime-signWaitCount)/signWaitTime + pbPositionNearMsgWindow(facewindow, msgwindow, :left) + msgwindow.y = Graphics.height - (msgwindow.height * (signWaitTime - signWaitCount) / signWaitTime) when "ts" # Change text speed - msgwindow.textspeed = (param=="") ? -999 : param.to_i + msgwindow.textspeed = (param == "") ? -999 : param.to_i when "." # Wait 0.25 seconds - msgwindow.waitcount += Graphics.frame_rate/4 + msgwindow.waitcount += Graphics.frame_rate / 4 when "|" # Wait 1 second msgwindow.waitcount += Graphics.frame_rate when "wt" # Wait X/20 seconds - param = param.sub(/\A\s+/,"").sub(/\s+\z/,"") - msgwindow.waitcount += param.to_i*Graphics.frame_rate/20 + param = param.sub(/\A\s+/, "").sub(/\s+\z/, "") + msgwindow.waitcount += param.to_i * Graphics.frame_rate / 20 when "wtnp" # Wait X/20 seconds, no pause - param = param.sub(/\A\s+/,"").sub(/\s+\z/,"") - msgwindow.waitcount = param.to_i*Graphics.frame_rate/20 + param = param.sub(/\A\s+/, "").sub(/\s+\z/, "") + msgwindow.waitcount = param.to_i * Graphics.frame_rate / 20 autoresume = true when "^" # Wait, no pause autoresume = true @@ -749,8 +649,8 @@ def pbMessageDisplay(msgwindow,message,letterbyletter=true,commandProc=nil) break if !letterbyletter Graphics.update Input.update - facewindow.update if facewindow - if autoresume && msgwindow.waitcount==0 + facewindow&.update + if autoresume && msgwindow.waitcount == 0 msgwindow.resume if msgwindow.busy? break if !msgwindow.busy? end @@ -758,8 +658,8 @@ def pbMessageDisplay(msgwindow,message,letterbyletter=true,commandProc=nil) if msgwindow.busy? pbPlayDecisionSE if msgwindow.pausing? msgwindow.resume - else - break if signWaitCount==0 + elsif signWaitCount == 0 + break end end pbUpdateSceneMap @@ -768,27 +668,27 @@ def pbMessageDisplay(msgwindow,message,letterbyletter=true,commandProc=nil) break if (!letterbyletter || commandProc || commands) && !msgwindow.busy? end Input.update # Must call Input.update again to avoid extra triggers - msgwindow.letterbyletter=oldletterbyletter + msgwindow.letterbyletter = oldletterbyletter if commands - $game_variables[cmdvariable]=pbShowCommands(msgwindow,commands,cmdIfCancel) + $game_variables[cmdvariable] = pbShowCommands(msgwindow, commands, cmdIfCancel) $game_map.need_refresh = true if $game_map end if commandProc - ret=commandProc.call(msgwindow) + ret = commandProc.call(msgwindow) end - msgback.dispose if msgback - goldwindow.dispose if goldwindow - coinwindow.dispose if coinwindow - battlepointswindow.dispose if battlepointswindow - facewindow.dispose if facewindow + msgback&.dispose + goldwindow&.dispose + coinwindow&.dispose + battlepointswindow&.dispose + facewindow&.dispose if haveSpecialClose pbSEPlay(pbStringToAudioFile(specialCloseSE)) - atTop = (msgwindow.y==0) - for i in 0..signWaitTime + atTop = (msgwindow.y == 0) + (0..signWaitTime).each do |i| if atTop - msgwindow.y = -msgwindow.height*i/signWaitTime + msgwindow.y = -msgwindow.height * i / signWaitTime else - msgwindow.y = Graphics.height-msgwindow.height*(signWaitTime-i)/signWaitTime + msgwindow.y = Graphics.height - (msgwindow.height * (signWaitTime - i) / signWaitTime) end Graphics.update Input.update @@ -804,134 +704,134 @@ def pbMessageDisplay(msgwindow,message,letterbyletter=true,commandProc=nil) #=============================================================================== # Message-displaying functions #=============================================================================== -def pbMessage(message,commands=nil,cmdIfCancel=0,skin=nil,defaultCmd=0,&block) +def pbMessage(message, commands = nil, cmdIfCancel = 0, skin = nil, defaultCmd = 0, &block) ret = 0 - msgwindow = pbCreateMessageWindow(nil,skin) + msgwindow = pbCreateMessageWindow(nil, skin) if commands - ret = pbMessageDisplay(msgwindow,message,true, - proc { |msgwindow| - next Kernel.pbShowCommands(msgwindow,commands,cmdIfCancel,defaultCmd,&block) - },&block) + ret = pbMessageDisplay(msgwindow, message, true, + proc { |msgwindow| + next Kernel.pbShowCommands(msgwindow, commands, cmdIfCancel, defaultCmd, &block) + }, &block) else - pbMessageDisplay(msgwindow,message,&block) + pbMessageDisplay(msgwindow, message, &block) end pbDisposeMessageWindow(msgwindow) Input.update return ret end -def pbConfirmMessage(message,&block) - return (pbMessage(message,[_INTL("Yes"),_INTL("No")],2,&block)==0) +def pbConfirmMessage(message, &block) + return (pbMessage(message, [_INTL("Yes"), _INTL("No")], 2, &block) == 0) end -def pbConfirmMessageSerious(message,&block) - return (pbMessage(message,[_INTL("No"),_INTL("Yes")],1,&block)==1) +def pbConfirmMessageSerious(message, &block) + return (pbMessage(message, [_INTL("No"), _INTL("Yes")], 1, &block) == 1) end -def pbMessageChooseNumber(message,params,&block) - msgwindow = pbCreateMessageWindow(nil,params.messageSkin) - ret = pbMessageDisplay(msgwindow,message,true, - proc { |msgwindow| - next pbChooseNumber(msgwindow,params,&block) - },&block) +def pbMessageChooseNumber(message, params, &block) + msgwindow = pbCreateMessageWindow(nil, params.messageSkin) + ret = pbMessageDisplay(msgwindow, message, true, + proc { |msgwindow| + next pbChooseNumber(msgwindow, params, &block) + }, &block) pbDisposeMessageWindow(msgwindow) return ret end -def pbShowCommands(msgwindow,commands=nil,cmdIfCancel=0,defaultCmd=0) +def pbShowCommands(msgwindow, commands = nil, cmdIfCancel = 0, defaultCmd = 0) return 0 if !commands - cmdwindow=Window_CommandPokemonEx.new(commands) - cmdwindow.z=99999 - cmdwindow.visible=true + cmdwindow = Window_CommandPokemonEx.new(commands) + cmdwindow.z = 99999 + cmdwindow.visible = true cmdwindow.resizeToFit(cmdwindow.commands) - pbPositionNearMsgWindow(cmdwindow,msgwindow,:right) - cmdwindow.index=defaultCmd - command=0 + pbPositionNearMsgWindow(cmdwindow, msgwindow, :right) + cmdwindow.index = defaultCmd + command = 0 loop do Graphics.update Input.update cmdwindow.update - msgwindow.update if msgwindow + msgwindow&.update yield if block_given? if Input.trigger?(Input::BACK) - if cmdIfCancel>0 - command=cmdIfCancel-1 + if cmdIfCancel > 0 + command = cmdIfCancel - 1 break - elsif cmdIfCancel<0 - command=cmdIfCancel + elsif cmdIfCancel < 0 + command = cmdIfCancel break end end if Input.trigger?(Input::USE) - command=cmdwindow.index + command = cmdwindow.index break end pbUpdateSceneMap end - ret=command + ret = command cmdwindow.dispose Input.update return ret end -def pbShowCommandsWithHelp(msgwindow,commands,help,cmdIfCancel=0,defaultCmd=0) - msgwin=msgwindow - msgwin=pbCreateMessageWindow(nil) if !msgwindow - oldlbl=msgwin.letterbyletter - msgwin.letterbyletter=false +def pbShowCommandsWithHelp(msgwindow, commands, help, cmdIfCancel = 0, defaultCmd = 0) + msgwin = msgwindow + msgwin = pbCreateMessageWindow(nil) if !msgwindow + oldlbl = msgwin.letterbyletter + msgwin.letterbyletter = false if commands - cmdwindow=Window_CommandPokemonEx.new(commands) - cmdwindow.z=99999 - cmdwindow.visible=true + cmdwindow = Window_CommandPokemonEx.new(commands) + cmdwindow.z = 99999 + cmdwindow.visible = true cmdwindow.resizeToFit(cmdwindow.commands) - cmdwindow.height=msgwin.y if cmdwindow.height>msgwin.y - cmdwindow.index=defaultCmd - command=0 - msgwin.text=help[cmdwindow.index] - msgwin.width=msgwin.width # Necessary evil to make it use the proper margins + cmdwindow.height = msgwin.y if cmdwindow.height > msgwin.y + cmdwindow.index = defaultCmd + command = 0 + msgwin.text = help[cmdwindow.index] + msgwin.width = msgwin.width # Necessary evil to make it use the proper margins loop do Graphics.update Input.update - oldindex=cmdwindow.index + oldindex = cmdwindow.index cmdwindow.update - if oldindex!=cmdwindow.index - msgwin.text=help[cmdwindow.index] + if oldindex != cmdwindow.index + msgwin.text = help[cmdwindow.index] end msgwin.update yield if block_given? if Input.trigger?(Input::BACK) - if cmdIfCancel>0 - command=cmdIfCancel-1 + if cmdIfCancel > 0 + command = cmdIfCancel - 1 break - elsif cmdIfCancel<0 - command=cmdIfCancel + elsif cmdIfCancel < 0 + command = cmdIfCancel break end end if Input.trigger?(Input::USE) - command=cmdwindow.index + command = cmdwindow.index break end pbUpdateSceneMap end - ret=command + ret = command cmdwindow.dispose Input.update end - msgwin.letterbyletter=oldlbl + msgwin.letterbyletter = oldlbl msgwin.dispose if !msgwindow return ret end # frames is the number of 1/20 seconds to wait for -def pbMessageWaitForInput(msgwindow,frames,showPause=false) - return if !frames || frames<=0 +def pbMessageWaitForInput(msgwindow, frames, showPause = false) + return if !frames || frames <= 0 msgwindow.startPause if msgwindow && showPause - frames = frames*Graphics.frame_rate/20 + frames = frames * Graphics.frame_rate / 20 frames.times do Graphics.update Input.update - msgwindow.update if msgwindow + msgwindow&.update pbUpdateSceneMap if Input.trigger?(Input::USE) || Input.trigger?(Input::BACK) break @@ -941,28 +841,28 @@ def pbMessageWaitForInput(msgwindow,frames,showPause=false) msgwindow.stopPause if msgwindow && showPause end -def pbFreeText(msgwindow,currenttext,passwordbox,maxlength,width=240) - window=Window_TextEntry_Keyboard.new(currenttext,0,0,width,64) - ret="" - window.maxlength=maxlength - window.visible=true - window.z=99999 - pbPositionNearMsgWindow(window,msgwindow,:right) - window.text=currenttext - window.passwordChar="*" if passwordbox +def pbFreeText(msgwindow, currenttext, passwordbox, maxlength, width = 240) + window = Window_TextEntry_Keyboard.new(currenttext, 0, 0, width, 64) + ret = "" + window.maxlength = maxlength + window.visible = true + window.z = 99999 + pbPositionNearMsgWindow(window, msgwindow, :right) + window.text = currenttext + window.passwordChar = "*" if passwordbox Input.text_input = true loop do Graphics.update Input.update if Input.triggerex?(:ESCAPE) - ret=currenttext + ret = currenttext break elsif Input.triggerex?(:RETURN) - ret=window.text + ret = window.text break end window.update - msgwindow.update if msgwindow + msgwindow&.update yield if block_given? end Input.text_input = false @@ -971,12 +871,12 @@ def pbFreeText(msgwindow,currenttext,passwordbox,maxlength,width=240) return ret end -def pbMessageFreeText(message,currenttext,passwordbox,maxlength,width=240,&block) - msgwindow=pbCreateMessageWindow - retval=pbMessageDisplay(msgwindow,message,true, - proc { |msgwindow| - next pbFreeText(msgwindow,currenttext,passwordbox,maxlength,width,&block) - },&block) +def pbMessageFreeText(message, currenttext, passwordbox, maxlength, width = 240, &block) + msgwindow = pbCreateMessageWindow + retval = pbMessageDisplay(msgwindow, message, true, + proc { |msgwindow| + next pbFreeText(msgwindow, currenttext, passwordbox, maxlength, width, &block) + }, &block) pbDisposeMessageWindow(msgwindow) return retval end diff --git a/Data/Scripts/007_Objects and windows/012_TextEntry.rb b/Data/Scripts/007_Objects and windows/012_TextEntry.rb index 2e5d201a92..1b5a84cbea 100644 --- a/Data/Scripts/007_Objects and windows/012_TextEntry.rb +++ b/Data/Scripts/007_Objects and windows/012_TextEntry.rb @@ -2,32 +2,28 @@ # #=============================================================================== class CharacterEntryHelper - attr_reader :text + attr_accessor :text attr_accessor :maxlength attr_reader :passwordChar attr_accessor :cursor def initialize(text) - @maxlength=-1 - @text=text - @passwordChar="" - @cursor=text.scan(/./m).length - end - - def text=(value) - @text=value + @maxlength = -1 + @text = text + @passwordChar = "" + @cursor = text.scan(/./m).length 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 ? value : "" + @passwordChar = value || "" end def length @@ -35,52 +31,52 @@ def length 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="" - for ch in chars - @text+=ch if ch + chars = self.text.scan(/./m) + return false if @maxlength >= 0 && chars.length >= @maxlength + chars.insert(@cursor, ch) + @text = "" + chars.each do |ch| + @text += ch if ch end - @cursor+=1 + @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="" - for ch in chars - @text+=ch if ch + 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 end - @cursor-=1 + @cursor -= 1 return true end private def ensure - return if @maxlength<0 - chars=self.text.scan(/./m) - if chars.length>@maxlength && @maxlength>=0 - chars=chars[0,@maxlength] + 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 + @text = "" + chars.each do |ch| + @text += ch if ch end end end @@ -91,19 +87,19 @@ def ensure # #=============================================================================== class Window_TextEntry < SpriteWindow_Base - 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] + 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 - self.active=true - @frame=0 + @helper = CharacterEntryHelper.new(text) + @heading = heading + self.active = true + @frame = 0 refresh end @@ -120,23 +116,23 @@ def passwordChar 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) - @frame=0 + @frame = 0 self.refresh return true end @@ -145,7 +141,7 @@ def insert(ch) def delete if @helper.delete - @frame=0 + @frame = 0 self.refresh return true end @@ -155,7 +151,7 @@ def delete def update @frame += 1 @frame %= 20 - self.refresh if (@frame%10)==0 + self.refresh if (@frame % 10) == 0 return if !self.active # Moving cursor if Input.repeat?(Input::LEFT) && Input.press?(Input::ACTION) @@ -176,47 +172,47 @@ def update 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 - for i in startpos...scanlength - c=(@helper.passwordChar!="") ? @helper.passwordChar : textscan[i] - textwidth=bitmap.text_size(c).width - next if c=="\n" + (startpos...scanlength).each do |i| + 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 ((@frame/10)&1) == 0 && i==@helper.cursor - 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 ((@frame/10)&1) == 0 && textscan.length==@helper.cursor - 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 @@ -228,27 +224,27 @@ def refresh #=============================================================================== class Window_TextEntry_Keyboard < Window_TextEntry def update - @frame+=1 - @frame%=20 - self.refresh if ((@frame%10)==0) + @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 - @frame=0 + @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 - @frame=0 + @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 @@ -263,17 +259,17 @@ def update # #=============================================================================== class Window_MultilineTextEntry < SpriteWindow_Base - 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 + 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 @@ -281,12 +277,12 @@ def initialize(text,x,y,width,height) attr_reader :shadowColor def baseColor=(value) - @baseColor=value + @baseColor = value refresh end def shadowColor=(value) - @shadowColor=value + @shadowColor = value refresh end @@ -299,23 +295,23 @@ def maxlength 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) - @frame=0 - @textchars=nil - moveCursor(0,1) + @frame = 0 + @textchars = nil + moveCursor(0, 1) self.refresh return true end @@ -323,11 +319,11 @@ def insert(ch) end def delete - @helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn) + @helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn) if @helper.delete - @frame=0 - moveCursor(0,-1) # use old textchars - @textchars=nil + @frame = 0 + moveCursor(0, -1) # use old textchars + @textchars = nil self.refresh return true end @@ -336,67 +332,67 @@ def delete 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 - for i in 0...textchars.length - thisline=textchars[i][5] - y=textchars[i][2] - return y if thisline==line - maximumY=y if maximumY= totallines + maximumY = 0 + textchars.each do |text| + thisline = text[5] + y = text[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 - for i in 0...textchars.length - thisline=textchars[i][5] - thislength=textchars[i][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 + textchars.each do |text| + thisline = text[5] + thislength = text[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 - 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 + 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] + 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) + if column >= thiscolumn && column <= thiscolumn + thislength && thislength > 0 + return thispos + (column - thiscolumn) end end end @@ -409,90 +405,87 @@ def getPosFromLineAndColumn(line,column) 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 - @frame=0 + @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 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 - @frame+=1 - @frame%=20 - self.refresh if ((@frame%10)==0) + @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) @@ -502,63 +495,62 @@ def update 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.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 + height = self.height - self.borderY + cursorcolor = Color.new(0, 0, 0) + textchars = getTextChars + startY = getLineY(@firstline) + textchars.each do |text| + thisline = text[5] + thislength = text[8] + textY = text[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=textchars[i][0] + c = text[0] # Don't draw spaces - next if c==" " - textwidth=textchars[i][3]+4 # add 4 to prevent draw_text from stretching text - textheight=textchars[i][4] + next if c == " " + textwidth = text[3] + 4 # add 4 to prevent draw_text from stretching text + textheight = text[4] # Draw text - pbDrawShadowText(bitmap, textchars[i][1], textY, textwidth, textheight, c, @baseColor, @shadowColor) + pbDrawShadowText(bitmap, text[1], textY, textwidth, textheight, c, @baseColor, @shadowColor) end # Draw cursor - 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 + if ((@frame / 10) & 1) == 0 + 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] + 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 end break end 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 diff --git a/Data/Scripts/008_Audio/001_Audio.rb b/Data/Scripts/008_Audio/001_Audio.rb index c0ebc8301b..23b578651d 100644 --- a/Data/Scripts/008_Audio/001_Audio.rb +++ b/Data/Scripts/008_Audio/001_Audio.rb @@ -1,10 +1,10 @@ ##################################### # Needed because RGSS doesn't call at_exit procs on exit # Exit is not called when game is reset (using F12) -$AtExitProcs=[] if !$AtExitProcs +$AtExitProcs = [] if !$AtExitProcs -def exit(code=0) - for p in $AtExitProcs +def exit(code = 0) + $AtExitProcs.each do |p| p.call end raise SystemExit.new(code) @@ -51,7 +51,7 @@ def oggfiletime(file) i = -1 pcmlengths = [] rates = [] - for page in pages + pages.each do |page| header = page[0] serial = header[10, 4].unpack("V") frame = header[2, 8].unpack("C*") @@ -78,9 +78,7 @@ def oggfiletime(file) pcmlengths[i] = frameno end ret = 0.0 - for i in 0...pcmlengths.length - ret += pcmlengths[i].to_f / rates[i].to_f - end + pcmlengths.each_with_index { |length, i| ret += length.to_f / rates[i] } return ret * 256.0 end @@ -110,36 +108,37 @@ def getPlayTime2(filename) File.open(filename, "rb") { |file| file.pos = 0 fdw = fgetdw.call(file) - if fdw == 0x46464952 # "RIFF" + case fdw + when 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 " - fmtsize = fgetdw.call(file) - format = fgetw.call(file) - channels = fgetw.call(file) - rate = fgetdw.call(file) + fgetdw.call(file) # fmtsize + fgetw.call(file) # format + fgetw.call(file) # channels + fgetdw.call(file) # rate bytessec = fgetdw.call(file) return -1 if bytessec == 0 - bytessample = fgetw.call(file) - bitssample = fgetw.call(file) + fgetw.call(file) # bytessample + fgetw.call(file) # bitssample data = fgetdw.call(file) return -1 if data != 0x61746164 # "data" datasize = fgetdw.call(file) - time = (datasize*1.0)/bytessec + time = datasize.to_f / bytessec return time - elsif fdw == 0x5367674F # "OggS" + when 0x5367674F # "OggS" file.pos = 0 time = oggfiletime(file) return time end file.pos = 0 # Find the length of an MP3 file - while true + loop do rstr = "" ateof = false - while !file.eof? + until file.eof? if (file.read(1)[0] rescue 0) == 0xFF begin rstr = file.read(3) @@ -152,15 +151,15 @@ def getPlayTime2(filename) break if ateof || !rstr || rstr.length != 3 if rstr[0] == 0xFB t = rstr[1] >> 4 - next if t == 0 || t == 15 - freqs = [44100, 22050, 11025, 48000] + next if [0, 15].include?(t) + freqs = [44_100, 22_050, 11_025, 48_000] 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 = ((144000 * bitrate) / freq) + t + frameLength = ((144_000 * bitrate) / freq) + t numFrames = filesize / (frameLength + 4) time = (numFrames * 1152.0 / freq) break diff --git a/Data/Scripts/008_Audio/002_Audio_Play.rb b/Data/Scripts/008_Audio/002_Audio_Play.rb index d18fca354f..be632542b0 100644 --- a/Data/Scripts/008_Audio/002_Audio_Play.rb +++ b/Data/Scripts/008_Audio/002_Audio_Play.rb @@ -3,13 +3,13 @@ def pbStringToAudioFile(str) 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 @@ -21,7 +21,7 @@ def pbStringToAudioFile(str) # 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 @@ -29,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 @@ -49,43 +49,43 @@ def pbResolveAudioFile(str,volume=nil,pitch=nil) # 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 && $game_system.respond_to?("bgm_play") + param = pbResolveAudioFile(param, volume, pitch) + if param.name && param.name != "" + if $game_system $game_system.bgm_play(param) return elsif (RPG.const_defined?(:BGM) rescue false) - b=RPG::BGM.new(param.name,param.volume,param.pitch) - if b && b.respond_to?("play") + b = RPG::BGM.new(param.name, param.volume, param.pitch) + if 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 && $game_system.respond_to?("bgm_fade") +def pbBGMStop(timeInSeconds = 0.0) + if $game_system && timeInSeconds > 0.0 $game_system.bgm_fade(timeInSeconds) return - elsif $game_system && $game_system.respond_to?("bgm_stop") + elsif $game_system $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 ################################################################################ @@ -99,42 +99,43 @@ def pbBGMStop(timeInSeconds=0.0) # 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 && $game_system.respond_to?("me_play") + param = pbResolveAudioFile(param, volume, pitch) + if param.name && param.name != "" + if $game_system $game_system.me_play(param) return elsif (RPG.const_defined?(:ME) rescue false) - b=RPG::ME.new(param.name,param.volume,param.pitch) - if b && b.respond_to?("play") - b.play; return + b = RPG::ME.new(param.name, param.volume, param.pitch) + if 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 && $game_system.respond_to?("me_stop") + elsif $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 ################################################################################ @@ -148,42 +149,43 @@ def pbMEStop(timeInSeconds=0.0) # 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 && $game_system.respond_to?("bgs_play") + param = pbResolveAudioFile(param, volume, pitch) + if param.name && param.name != "" + if $game_system $game_system.bgs_play(param) return elsif (RPG.const_defined?(:BGS) rescue false) - b=RPG::BGS.new(param.name,param.volume,param.pitch) - if b && b.respond_to?("play") - b.play; return + b = RPG::BGS.new(param.name, param.volume, param.pitch) + if 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 && $game_system.respond_to?("bgs_fade") +def pbBGSStop(timeInSeconds = 0.0) + if $game_system && timeInSeconds > 0.0 $game_system.bgs_fade(timeInSeconds) return - elsif $game_system && $game_system.respond_to?("bgs_play") + elsif $game_system $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 ################################################################################ @@ -197,30 +199,30 @@ def pbBGSStop(timeInSeconds=0.0) # 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 && $game_system.respond_to?("se_play") + param = pbResolveAudioFile(param, volume, pitch) + if param.name && param.name != "" + if $game_system $game_system.se_play(param) return end if (RPG.const_defined?(:SE) rescue false) - b = RPG::SE.new(param.name,param.volume,param.pitch) - if b && b.respond_to?("play") + b = RPG::SE.new(param.name, param.volume, param.pitch) + if 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) @@ -234,59 +236,43 @@ def pbSEStop(_timeInSeconds=0.0) # Plays a sound effect that plays when the player moves the cursor. def pbPlayCursorSE - if $data_system && $data_system.respond_to?("cursor_se") && - $data_system.cursor_se && $data_system.cursor_se.name!="" + if !nil_or_empty?($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 $data_system && $data_system.respond_to?("decision_se") && - $data_system.decision_se && $data_system.decision_se.name!="" + if !nil_or_empty?($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 $data_system && $data_system.respond_to?("cancel_se") && - $data_system.cancel_se && $data_system.cancel_se.name!="" + if !nil_or_empty?($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 $data_system && $data_system.respond_to?("buzzer_se") && - $data_system.buzzer_se && $data_system.buzzer_se.name!="" + if !nil_or_empty?($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 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 diff --git a/Data/Scripts/009_Scenes/001_Transitions.rb b/Data/Scripts/009_Scenes/001_Transitions.rb index 4222ab80d8..1ab6877ff3 100644 --- a/Data/Scripts/009_Scenes/001_Transitions.rb +++ b/Data/Scripts/009_Scenes/001_Transitions.rb @@ -1,3 +1,6 @@ +#=============================================================================== +# +#=============================================================================== module Graphics @@transition = nil STOP_WHILE_TRANSITION = true @@ -12,18 +15,18 @@ class << Graphics end end + # duration is in 1/20ths of a second def self.transition(duration = 8, filename = "", vague = 20) duration = duration.floor if judge_special_transition(duration, filename) duration = 0 filename = "" end + duration *= Graphics.frame_rate / 20 # For default fade-in animation begin transition_KGC_SpecialTransition(duration, filename, vague) rescue Exception - if filename != "" - transition_KGC_SpecialTransition(duration, "", vague) - end + transition_KGC_SpecialTransition(duration, "", vague) if filename != "" end if STOP_WHILE_TRANSITION && !@_interrupt_transition while @@transition && !@@transition.disposed? @@ -34,31 +37,25 @@ def self.transition(duration = 8, filename = "", vague = 20) def self.update update_KGC_SpecialTransition -=begin - if Graphics.frame_count % 40 == 0 - count = 0 - ObjectSpace.each_object(Object) { |o| count += 1 } - echoln("Objects: #{count}") - end -=end @@transition.update if @@transition && !@@transition.disposed? - @@transition = nil if @@transition && @@transition.disposed? + @@transition = nil if @@transition&.disposed? end - def self.judge_special_transition(duration,filename) + def self.judge_special_transition(duration, filename) return false if @_interrupt_transition ret = true if @@transition && !@@transition.disposed? @@transition.dispose @@transition = nil end + duration /= 20.0 # Turn into seconds dc = File.basename(filename).downcase case dc # Other coded transitions when "breakingglass" then @@transition = Transitions::BreakingGlass.new(duration) when "rotatingpieces" then @@transition = Transitions::ShrinkingPieces.new(duration, true) when "shrinkingpieces" then @@transition = Transitions::ShrinkingPieces.new(duration, false) - when "splash" then @@transition = Transitions::SplashTransition.new(duration) + when "splash" then @@transition = Transitions::SplashTransition.new(duration, 9.6) when "random_stripe_v" then @@transition = Transitions::RandomStripeTransition.new(duration, 0) when "random_stripe_h" then @@transition = Transitions::RandomStripeTransition.new(duration, 1) when "zoomin" then @@transition = Transitions::ZoomInTransition.new(duration) @@ -85,9 +82,13 @@ def self.judge_special_transition(duration,filename) when "wavythreeballup" then @@transition = Transitions::WavyThreeBallUp.new(duration) when "wavyspinball" then @@transition = Transitions::WavySpinBall.new(duration) when "fourballburst" then @@transition = Transitions::FourBallBurst.new(duration) + when "vstrainer" then @@transition = Transitions::VSTrainer.new(duration) + when "vselitefour" then @@transition = Transitions::VSEliteFour.new(duration) + when "rocketgrunt" then @@transition = Transitions::RocketGrunt.new(duration) + when "vsrocketadmin" then @@transition = Transitions::VSRocketAdmin.new(duration) # Graphic transitions when "fadetoblack" then @@transition = Transitions::FadeToBlack.new(duration) - when "" then @@transition = Transitions::FadeFromBlack.new(duration) + when "fadefromblack" then @@transition = Transitions::FadeFromBlack.new(duration) else ret = false end Graphics.frame_reset if ret @@ -95,436 +96,264 @@ def self.judge_special_transition(duration,filename) end end - - #=============================================================================== -# Screen transition classes +# Screen transition animation classes. #=============================================================================== module Transitions #============================================================================= - # + # A base class that all other screen transition animations inherit from. #============================================================================= - class BreakingGlass - def initialize(numframes) + class Transition_Base + DURATION = nil + + def initialize(duration, *args) @disposed = false - @numframes = numframes - @opacitychange = (numframes<=0) ? 255 : 255.0/numframes - cx = 6 - cy = 5 - @bitmap = Graphics.snap_to_bitmap - if !@bitmap + if duration <= 0 @disposed = true return end - width = @bitmap.width/cx - height = @bitmap.height/cy - @numtiles = cx*cy - @viewport = Viewport.new(0,0,Graphics.width,Graphics.height) + @duration = self.class::DURATION || duration + @parameters = args + @timer = 0.0 + @overworld_bitmap = $game_temp.background_bitmap + initialize_bitmaps + return if disposed? + @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) @viewport.z = 99999 @sprites = [] - @offset = [] - @y = [] - for i in 0...@numtiles - @sprites[i] = Sprite.new(@viewport) - @sprites[i].bitmap = @bitmap - @sprites[i].x = width*(i%cx) - @sprites[i].y = height*(i/cx) - @sprites[i].src_rect.set(@sprites[i].x,@sprites[i].y,width,height) - @offset[i] = (rand(100)+1)*3.0/100.0 - @y[i] = @sprites[i].y - end + @overworld_sprite = new_sprite(0, 0, @overworld_bitmap) + @overworld_sprite.z = -1 + initialize_sprites + @timings = [] + set_up_timings end - def disposed?; @disposed; end + def new_sprite(x, y, bitmap, ox = 0, oy = 0) + s = Sprite.new(@viewport) + s.x = x + s.y = y + s.ox = ox + s.oy = oy + s.bitmap = bitmap + return s + end def dispose - if !disposed? - @bitmap.dispose - for i in 0...@numtiles - @sprites[i].visible = false - @sprites[i].dispose - end - @sprites.clear - @viewport.dispose if @viewport - @disposed = true - end + return if disposed? + dispose_all + @sprites.each { |s| s&.dispose } + @sprites.clear + @overworld_sprite.dispose + @overworld_bitmap&.dispose + @viewport&.dispose + @disposed = true end + def disposed?; return @disposed; end + def update return if disposed? - continue = false - for i in 0...@numtiles - @sprites[i].opacity -= @opacitychange - @y[i] += @offset[i] - @sprites[i].y = @y[i] - continue = true if @sprites[i].opacity>0 + @timer += Graphics.delta_s + if @timer >= @duration + dispose + return end - self.dispose if !continue + update_anim end + + def initialize_bitmaps; end + def initialize_sprites; end + def set_up_timings; end + def dispose_all; end + def update_anim; end end #============================================================================= # #============================================================================= - class ShrinkingPieces - def initialize(numframes,rotation) - @disposed = false - @rotation = rotation - @numframes = numframes - @opacitychange = (numframes<=0) ? 255 : 255.0/numframes - cx = 6 - cy = 5 - @bitmap = Graphics.snap_to_bitmap - if !@bitmap - @disposed = true - return - end - width = @bitmap.width/cx - height = @bitmap.height/cy - @numtiles = cx*cy - @viewport = Viewport.new(0,0,Graphics.width,Graphics.height) - @viewport.z = 99999 - @sprites = [] - for i in 0...@numtiles - @sprites[i] = Sprite.new(@viewport) - @sprites[i].bitmap = @bitmap - @sprites[i].ox = width/2 - @sprites[i].oy = height/2 - @sprites[i].x = width*(i%cx)+@sprites[i].ox - @sprites[i].y = height*(i/cx)+@sprites[i].oy - @sprites[i].src_rect.set(width*(i%cx),height*(i/cx),width,height) + class BreakingGlass < Transition_Base + NUM_SPRITES_X = 8 + NUM_SPRITES_Y = 6 + + def initialize_sprites + @overworld_sprite.visible = false + # Overworld sprites + sprite_width = @overworld_bitmap.width / NUM_SPRITES_X + sprite_height = @overworld_bitmap.height / NUM_SPRITES_Y + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + @sprites[idx_sprite] = new_sprite(i * sprite_width, j * sprite_height, @overworld_bitmap) + @sprites[idx_sprite].src_rect.set(i * sprite_width, j * sprite_height, sprite_width, sprite_height) + end end end - def disposed?; @disposed; end - - def dispose - if !disposed? - @bitmap.dispose - for i in 0...@numtiles - @sprites[i].visible = false - @sprites[i].dispose + def set_up_timings + @start_y = [] + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + @start_y[idx_sprite] = @sprites[idx_sprite].y + @timings[idx_sprite] = 0.5 + rand end - @sprites.clear - @viewport.dispose if @viewport - @disposed = true end end - def update - return if disposed? - continue = false - for i in 0...@numtiles - @sprites[i].opacity -= @opacitychange - if @rotation - @sprites[i].angle += 40 - @sprites[i].angle %= 360 - end - @sprites[i].zoom_x = @sprites[i].opacity/255.0 - @sprites[i].zoom_y = @sprites[i].opacity/255.0 - continue = true if @sprites[i].opacity>0 + def update_anim + proportion = @timer / @duration + @sprites.each_with_index do |sprite, i| + sprite.y = @start_y[i] + (Graphics.height * @timings[i] * proportion * proportion) + sprite.opacity = 255 * (1 - proportion) end - self.dispose if !continue end end #============================================================================= # #============================================================================= - class SplashTransition - SPLASH_SIZE = 32 - - def initialize(numframes,vague=9.6) - @duration = numframes - @numframes = numframes - @splash_dir = [] - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @buffer = Graphics.snap_to_bitmap - if !@buffer - @disposed = true - return - end - @viewport = Viewport.new(0,0,Graphics.width,Graphics.height) - @viewport.z = 99999 - @sprite = Sprite.new(@viewport) - @sprite.bitmap = Bitmap.new(Graphics.width, Graphics.height) - size = SPLASH_SIZE - size = [size,1].max - cells = Graphics.width*Graphics.height/(size**2) - rows = Graphics.width/size - rect = Rect.new(0,0,size,size) - mag = 40.0/@numframes - cells.times { |i| - rect.x = i%rows*size - rect.y = i/rows*size - x = rect.x/size-(rows>>1) - y = rect.y/size-((cells/rows)>>1) - r = Math.sqrt(x**2+y**2)/vague - @splash_dir[i] = [] - if r!=0 - @splash_dir[i][0] = x/r - @splash_dir[i][1] = y/r - else - @splash_dir[i][0] = (x!= 0) ? x*1.5 : pmrand*vague - @splash_dir[i][1] = (y!= 0) ? y*1.5 : pmrand*vague + class ShrinkingPieces < Transition_Base + NUM_SPRITES_X = 8 + NUM_SPRITES_Y = 6 + + def initialize_sprites + @overworld_sprite.visible = false + # Overworld sprites + sprite_width = @overworld_bitmap.width / NUM_SPRITES_X + sprite_height = @overworld_bitmap.height / NUM_SPRITES_Y + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + @sprites[idx_sprite] = new_sprite((i + 0.5) * sprite_width, (j + 0.5) * sprite_height, + @overworld_bitmap, sprite_width / 2, sprite_height / 2) + @sprites[idx_sprite].src_rect.set(i * sprite_width, j * sprite_height, sprite_width, sprite_height) end - @splash_dir[i][0] += (rand-0.5)*vague - @splash_dir[i][1] += (rand-0.5)*vague - @splash_dir[i][0] *= mag - @splash_dir[i][1] *= mag - } - @sprite.bitmap.blt(0,0,@buffer,@buffer.rect) - end - - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @sprite.visible = false - @sprite.bitmap.dispose - @sprite.dispose - @viewport.dispose if @viewport - @disposed = true - end - - def update - return if disposed? - if @duration==0 - dispose - else - size = SPLASH_SIZE - cells = Graphics.width*Graphics.height/(size**2) - rows = Graphics.width/size - rect = Rect.new(0,0,size,size) - buffer = @buffer - sprite = @sprite - phase = @numframes-@duration - sprite.bitmap.clear - cells.times { |i| - rect.x = (i%rows)*size - rect.y = (i/rows)*size - dx = rect.x+@splash_dir[i][0]*phase - dy = rect.y+@splash_dir[i][1]*phase - sprite.bitmap.blt(dx,dy,buffer,rect) - } - sprite.opacity = 384*@duration/@numframes - @duration -= 1 end end - private - - def pmrand - return (rand(2)==0) ? 1 : -1 + def update_anim + proportion = @timer / @duration + @sprites.each_with_index do |sprite, i| + sprite.zoom_x = (1 - proportion).to_f + sprite.zoom_y = sprite.zoom_x + if @parameters[0] # Rotation + direction = (1 - (2 * (((i / NUM_SPRITES_X) + (i % NUM_SPRITES_X)) % 2))) + sprite.angle = direction * 360 * 2 * proportion + end + end end end #============================================================================= # #============================================================================= - class RandomStripeTransition - RAND_STRIPE_SIZE = 2 - - def initialize(numframes,direction) - @duration = numframes - @numframes = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @buffer = Graphics.snap_to_bitmap - if !@buffer - @disposed = true - return + class SplashTransition < Transition_Base + NUM_SPRITES_X = 16 + NUM_SPRITES_Y = 12 + SPEED = 40 + + def initialize_sprites + @overworld_sprite.visible = false + # Black background + @black_sprite = BitmapSprite.new(Graphics.width, Graphics.height, @viewport) + @black_sprite.bitmap.fill_rect(0, 0, Graphics.width, Graphics.height, Color.new(0, 0, 0)) + # Overworld sprites + sprite_width = @overworld_bitmap.width / NUM_SPRITES_X + sprite_height = @overworld_bitmap.height / NUM_SPRITES_Y + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + @sprites[idx_sprite] = new_sprite((i + 0.5) * sprite_width, (j + 0.5) * sprite_height, + @overworld_bitmap, sprite_width / 2, sprite_height / 2) + @sprites[idx_sprite].src_rect.set(i * sprite_width, j * sprite_height, sprite_width, sprite_height) + end end - @viewport = Viewport.new(0,0,Graphics.width,Graphics.height) - @viewport.z = 99999 - @sprite = Sprite.new(@viewport) - @sprite.bitmap = Bitmap.new(Graphics.width,Graphics.height) - ########## - @direction = direction - size = RAND_STRIPE_SIZE - bands = ((@direction==0) ? Graphics.width : Graphics.height)/size - @rand_stripe_deleted = [] - @rand_stripe_deleted_count = 0 - ary = (0...bands).to_a - @rand_stripe_index_array = ary.sort_by { rand } - ########## - @sprite.bitmap.blt(0,0,@buffer,@buffer.rect) - end - - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @sprite.visible = false - @sprite.bitmap.dispose - @sprite.dispose - @viewport.dispose if @viewport - @disposed = true end - def update - return if disposed? - if @duration==0 - dispose - else - dir = @direction - size = RAND_STRIPE_SIZE - bands = ((dir==0) ? Graphics.width : Graphics.height)/size - rect = Rect.new(0,0,(dir==0) ? size : Graphics.width,(dir==0) ? Graphics.height : size) - buffer = @buffer - sprite = @sprite - count = (bands-bands*@duration/@numframes)-@rand_stripe_deleted_count - while count > 0 - @rand_stripe_deleted[@rand_stripe_index_array.pop] = true - @rand_stripe_deleted_count += 1 - count -= 1 - end - sprite.bitmap.clear - bands.to_i.times { |i| - unless @rand_stripe_deleted[i] - if dir==0 - rect.x = i*size - sprite.bitmap.blt(rect.x,0,buffer,rect) - else - rect.y = i*size - sprite.bitmap.blt(0,rect.y,buffer,rect) - end + def set_up_timings + @start_positions = [] + @move_vectors = [] + vague = (@parameters[0] || 9.6) * SPEED + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + spr = @sprites[idx_sprite] + @start_positions[idx_sprite] = [spr.x, spr.y] + dx = spr.x - (Graphics.width / 2) + dy = spr.y - (Graphics.height / 2) + move_x = move_y = 0 + if dx == 0 && dy == 0 + move_x = (dx == 0) ? rand_sign * vague : dx * SPEED * 1.5 + move_y = (dy == 0) ? rand_sign * vague : dy * SPEED * 1.5 + else + radius = Math.sqrt((dx**2) + (dy**2)) + move_x = dx * vague / radius + move_y = dy * vague / radius end - } - @duration -= 1 + move_x += (rand - 0.5) * vague + move_y += (rand - 0.5) * vague + @move_vectors[idx_sprite] = [move_x, move_y] + end end end - end - #============================================================================= - # - #============================================================================= - class ZoomInTransition - def initialize(numframes) - @duration = numframes - @numframes = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @buffer = Graphics.snap_to_bitmap - if !@buffer - @disposed = true - return + def update_anim + proportion = @timer / @duration + @sprites.each_with_index do |sprite, i| + sprite.x = @start_positions[i][0] + (@move_vectors[i][0] * proportion) + sprite.y = @start_positions[i][1] + (@move_vectors[i][1] * proportion) + sprite.opacity = 384 * (1 - proportion) end - @width = @buffer.width - @height = @buffer.height - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @sprite = Sprite.new(@viewport) - @sprite.bitmap = @buffer - @sprite.ox = @width/2 - @sprite.oy = @height/2 - @sprite.x = @width/2 - @sprite.y = @height/2 end - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @sprite.dispose if @sprite - @viewport.dispose if @viewport - @disposed = true - end + private - def update - return if disposed? - if @duration==0 - dispose - else - @sprite.zoom_x += 0.2 - @sprite.zoom_y += 0.2 - @sprite.opacity = (@duration-1)*255/@numframes - @duration -= 1 - end + def rand_sign + return (rand(2) == 0) ? 1 : -1 end end #============================================================================= # #============================================================================= - class ScrollScreen - def initialize(numframes,direction) - @numframes = numframes - @duration = numframes - @dir = direction - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @buffer = Graphics.snap_to_bitmap - if !@buffer - @disposed = true - return + class RandomStripeTransition < Transition_Base + STRIPE_WIDTH = 2 + + def initialize_sprites + @overworld_sprite.visible = false + # Overworld sprites + if @parameters[0] == 0 # Vertical stripes + sprite_width = STRIPE_WIDTH + sprite_height = @overworld_bitmap.height + num_stripes_x = @overworld_bitmap.width / STRIPE_WIDTH + num_stripes_y = 1 + else # Horizontal stripes + sprite_width = @overworld_bitmap.width + sprite_height = STRIPE_WIDTH + num_stripes_x = 1 + num_stripes_y = @overworld_bitmap.height / STRIPE_WIDTH + end + num_stripes_y.times do |j| + num_stripes_x.times do |i| + idx_sprite = (j * num_stripes_x) + i + @sprites[idx_sprite] = new_sprite(i * sprite_width, j * sprite_height, @overworld_bitmap) + @sprites[idx_sprite].src_rect.set(i * sprite_width, j * sprite_height, sprite_width, sprite_height) + end end - @width = @buffer.width - @height = @buffer.height - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @sprite = Sprite.new(@viewport) - @sprite.bitmap = @buffer end - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @sprite.dispose if @sprite - @viewport.dispose if @viewport - @disposed = true + def set_up_timings + @sprites.length.times do |i| + @timings[i] = @duration * i / @sprites.length + end + @timings.shuffle! end - def update - return if disposed? - if @duration==0 - dispose - else - case @dir - when 1 # down left - @sprite.y += (@height/@numframes) - @sprite.x -= (@width/@numframes) - when 2 # down - @sprite.y += (@height/@numframes) - when 3 # down right - @sprite.y += (@height/@numframes) - @sprite.x += (@width/@numframes) - when 4 # left - @sprite.x -= (@width/@numframes) - when 6 # right - @sprite.x += (@width/@numframes) - when 7 # up left - @sprite.y -= (@height/@numframes) - @sprite.x -= (@width/@numframes) - when 8 # up - @sprite.y -= (@height/@numframes) - when 9 # up right - @sprite.y -= (@height/@numframes) - @sprite.x += (@width/@numframes) - end - @duration -= 1 + def update_anim + @sprites.each_with_index do |sprite, i| + next if @timings[i] < 0 || @timer < @timings[i] + sprite.visible = false + @timings[i] = -1 end end end @@ -532,53 +361,33 @@ def update #============================================================================= # #============================================================================= - class MosaicTransition - def initialize(numframes) - @duration = numframes - @numframes = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @buffer = Graphics.snap_to_bitmap - if !@buffer - @disposed = true - return - end - @viewport = Viewport.new(0,0,Graphics.width,Graphics.height) - @viewport.z = 99999 - @sprite = Sprite.new(@viewport) - @sprite.bitmap = @buffer - @bitmapclone = @buffer.clone - @bitmapclone2 = @buffer.clone + class ZoomInTransition < Transition_Base + def initialize_sprites + @overworld_sprite.x = Graphics.width / 2 + @overworld_sprite.y = Graphics.height / 2 + @overworld_sprite.ox = @overworld_bitmap.width / 2 + @overworld_sprite.oy = @overworld_bitmap.height / 2 end - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @sprite.dispose if @sprite - @viewport.dispose if @viewport - @disposed = true + def update_anim + proportion = @timer / @duration + @overworld_sprite.zoom_x = 1 + (7 * proportion) + @overworld_sprite.zoom_y = @overworld_sprite.zoom_x + @overworld_sprite.opacity = 255 * (1 - proportion) end + end - def update - return if disposed? - if @duration==0 - dispose - else - @bitmapclone2.stretch_blt( - Rect.new(0,0,@buffer.width*@duration/@numframes,@buffer.height*@duration/@numframes), - @bitmapclone, - Rect.new(0,0,@buffer.width,@buffer.height)) - @buffer.stretch_blt( - Rect.new(0,0,@buffer.width,@buffer.height), - @bitmapclone2, - Rect.new(0,0,@buffer.width*@duration/@numframes,@buffer.height*@duration/@numframes)) - @duration -= 1 + #============================================================================= + # + #============================================================================= + class ScrollScreen < Transition_Base + def update_anim + proportion = @timer / @duration + if (@parameters[0] % 3) != 2 + @overworld_sprite.x = [1, -1, 0][@parameters[0] % 3] * Graphics.width * proportion + end + if ((@parameters[0] - 1) / 3) != 1 + @overworld_sprite.y = [1, 0, -1][(@parameters[0] - 1) / 3] * Graphics.height * proportion end end end @@ -586,38 +395,36 @@ def update #============================================================================= # #============================================================================= - class FadeToBlack - def initialize(numframes) - @duration = numframes - @numframes = numframes - @disposed = false - if @duration<=0 - @disposed = true - return - end - @viewport = Viewport.new(0,0,Graphics.width,Graphics.height) - @viewport.z = 99999 - @sprite = BitmapSprite.new(Graphics.width,Graphics.height,@viewport) - @sprite.bitmap.fill_rect(0,0,Graphics.width,Graphics.height,Color.new(0,0,0)) - @sprite.opacity = 0 + class MosaicTransition < Transition_Base + MAX_PIXELLATION_FACTOR = 16 + + def initialize_bitmaps + @buffer_original = @overworld_bitmap.clone # Copy of original, never changes + @buffer_temp = @overworld_bitmap.clone # "Clipboard" holding shrunken overworld end - def disposed?; @disposed; end + def set_up_timings + @start_black_fade = @duration * 0.8 + end - def dispose - return if disposed? - @sprite.dispose if @sprite - @viewport.dispose if @viewport - @disposed = true + def dispose_all + @buffer_original&.dispose + @buffer_temp&.dispose end - def update - return if disposed? - if @duration==0 - dispose - else - @sprite.opacity = (@numframes - @duration + 1) * 255 / @numframes - @duration -= 1 + def update_anim + proportion = @timer / @duration + inv_proportion = 1 / (1 + (proportion * (MAX_PIXELLATION_FACTOR - 1))) + new_size_rect = Rect.new(0, 0, @overworld_bitmap.width * inv_proportion, + @overworld_bitmap.height * inv_proportion) + # Take all of buffer_original, shrink it and put it into buffer_temp + @buffer_temp.stretch_blt(new_size_rect, + @buffer_original, Rect.new(0, 0, @overworld_bitmap.width, @overworld_bitmap.height)) + # Take shrunken area from buffer_temp and stretch it into buffer + @overworld_bitmap.stretch_blt(Rect.new(0, 0, @overworld_bitmap.width, @overworld_bitmap.height), + @buffer_temp, new_size_rect) + if @timer >= @start_black_fade + @overworld_sprite.opacity = 255 * (1 - ((@timer - @start_black_fade) / (@duration - @start_black_fade))) end end end @@ -625,117 +432,84 @@ def update #============================================================================= # #============================================================================= - class FadeFromBlack - def initialize(numframes) - @duration = numframes - @numframes = numframes - @disposed = false - if @duration<=0 - @disposed = true - return - end - @viewport = Viewport.new(0,0,Graphics.width,Graphics.height) - @viewport.z = 99999 - @sprite = BitmapSprite.new(Graphics.width,Graphics.height,@viewport) - @sprite.bitmap.fill_rect(0,0,Graphics.width,Graphics.height,Color.new(0,0,0)) - @sprite.opacity = 255 - end - - def disposed?; @disposed; end - - def dispose - return if disposed? - @sprite.dispose if @sprite - @viewport.dispose if @viewport - @disposed = true + class FadeToBlack < Transition_Base + def update_anim + @overworld_sprite.opacity = 255 * (1 - (@timer / @duration)) end + end - def update - return if disposed? - if @duration==0 - dispose - else - @sprite.opacity = (@duration-1)*255/@numframes - @duration -= 1 - end + #============================================================================= + # + #============================================================================= + class FadeFromBlack < Transition_Base + def update_anim + @overworld_sprite.opacity = 255 * @timer / @duration end end #============================================================================= # HGSS wild outdoor #============================================================================= - class SnakeSquares - def initialize(numframes) - @numframes = numframes - @duration = numframes - @disposed = false - @bitmap = RPG::Cache.transition("black_square") - if !@bitmap + class SnakeSquares < Transition_Base + DURATION = 1.25 + TIME_TO_ZOOM = 0.2 # In seconds + NUM_SPRITES_X = 8 + NUM_SPRITES_Y = 6 # Must be an even number + TOTAL_SPRITES = NUM_SPRITES_X * NUM_SPRITES_Y + + def initialize_bitmaps + @black_bitmap = RPG::Cache.transition("black_square") + if !@black_bitmap @disposed = true return end - width = @bitmap.width - height = @bitmap.height - cx = Graphics.width/width # 8 - cy = Graphics.height/height # 6 - @numtiles = cx*cy - @viewport = Viewport.new(0,0,Graphics.width,Graphics.height) - @viewport.z = 99999 - @sprites = [] - @frame = [] - @addzoom = 0.125*50/@numframes - for i in 0...cy - for j in 0...cx - k = i*cx+j - x = width*(j%cx) - x = (cx-1)*width-x if (i<3 && i%2==1) || (i>=3 && i%2==0) - @sprites[k] = Sprite.new(@viewport) - @sprites[k].x = x+width/2 - @sprites[k].y = height*i - @sprites[k].ox = width/2 - @sprites[k].visible = false - @sprites[k].bitmap = @bitmap - if k>=@numtiles/2 - @frame[k] = 2*(@numtiles-k-1)*(@numframes-1/@addzoom)/50 + @zoom_x_target = Graphics.width.to_f / (@black_bitmap.width * NUM_SPRITES_X) + @zoom_y_target = Graphics.height.to_f / (@black_bitmap.height * NUM_SPRITES_Y) + end + + def initialize_sprites + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + if idx_sprite >= TOTAL_SPRITES / 2 + sprite_x = ((j.odd?) ? i : (NUM_SPRITES_X - i - 1)) * @black_bitmap.width else - @frame[k] = 2*k*(@numframes-1/@addzoom)/50 + sprite_x = ((j.even?) ? i : (NUM_SPRITES_X - i - 1)) * @black_bitmap.width end + sprite_x += @black_bitmap.width / 2 + @sprites[idx_sprite] = new_sprite(sprite_x, j * @black_bitmap.height * @zoom_y_target, + @black_bitmap, @black_bitmap.width / 2) + @sprites[idx_sprite].zoom_y = @zoom_y_target + @sprites[idx_sprite].visible = false end end end - def disposed?; @disposed; end - - def dispose - if !disposed? - @bitmap.dispose - for i in 0...@numtiles - if @sprites[i] - @sprites[i].visible = false - @sprites[i].dispose - end + def set_up_timings + time_between_zooms = (@duration - TIME_TO_ZOOM) * 2 / (TOTAL_SPRITES - 1) + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + idx_from_start = (idx_sprite >= TOTAL_SPRITES / 2) ? TOTAL_SPRITES - 1 - idx_sprite : idx_sprite + @timings[idx_sprite] = time_between_zooms * idx_from_start end - @sprites.clear - @viewport.dispose if @viewport - @disposed = true end end - def update - return if disposed? - if @duration==0 - dispose - else - count = @numframes-@duration - for i in 0...@numtiles - if @frame[i]=count - @sprites[i].visible = true - @sprites[i].zoom_x = @addzoom*(count-@frame[i]) - @sprites[i].zoom_x = 1.0 if @sprites[i].zoom_x>1.0 - end + def dispose_all + @black_bitmap.dispose + end + + def update_anim + @sprites.each_with_index do |sprite, i| + next if @timings[i] < 0 || @timer < @timings[i] + sprite.visible = true + sprite.zoom_x = @zoom_x_target * (@timer - @timings[i]) / TIME_TO_ZOOM + if sprite.zoom_x >= @zoom_x_target + sprite.zoom_x = @zoom_x_target + @timings[i] = -1 end end - @duration -= 1 end end @@ -744,491 +518,415 @@ def update # HGSS wild indoor night (origin=3) # HGSS wild cave (origin=3) #============================================================================= - class DiagonalBubble - def initialize(numframes,origin=0) - @numframes = numframes - @duration = numframes - @disposed = false + class DiagonalBubble < Transition_Base + DURATION = 1.25 + TIME_TO_ZOOM = 0.2 # In seconds + NUM_SPRITES_X = 8 + NUM_SPRITES_Y = 6 + TOTAL_SPRITES = NUM_SPRITES_X * NUM_SPRITES_Y + + def initialize_bitmaps @bitmap = RPG::Cache.transition("black_square") if !@bitmap @disposed = true return end - width = @bitmap.width - height = @bitmap.height - cx = Graphics.width/width # 8 - cy = Graphics.height/height # 6 - @numtiles = cx*cy - @viewport = Viewport.new(0,0,Graphics.width,Graphics.height) - @viewport.z = 99999 - @sprites = [] - @frame = [] - # 1.2, 0.6 and 0.8 determined by trigonometry of default screen size - l = 1.2*Graphics.width/(@numtiles-8) - for i in 0...cy - for j in 0...cx - k = i*cx+j - @sprites[k] = Sprite.new(@viewport) - @sprites[k].x = width*j+width/2 - @sprites[k].y = height*i+height/2 - @sprites[k].ox = width/2 - @sprites[k].oy = height/2 - @sprites[k].visible = false - @sprites[k].bitmap = @bitmap - case origin - when 1 then k = i*cx+(cx-1-j) # Top right - when 2 then k = @numtiles-1-(i*cx+(cx-1-j)) # Bottom left - when 3 then k = @numtiles-1-k # Bottom right - end - @frame[k] = ((0.6*j*width+0.8*i*height)*(@numframes/50.0)/l).floor + @zoom_x_target = Graphics.width.to_f / (@bitmap.width * NUM_SPRITES_X) + @zoom_y_target = Graphics.height.to_f / (@bitmap.height * NUM_SPRITES_Y) + end + + def initialize_sprites + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + @sprites[idx_sprite] = new_sprite(((i * @bitmap.width) + (@bitmap.width / 2)) * @zoom_x_target, + ((j * @bitmap.height) + (@bitmap.height / 2)) * @zoom_y_target, + @bitmap, @bitmap.width / 2, @bitmap.height / 2) + @sprites[idx_sprite].visible = false end end - @addzoom = 0.125*50/@numframes end - def disposed?; @disposed; end - - def dispose - if !disposed? - @bitmap.dispose - for i in 0...@numtiles - if @sprites[i] - @sprites[i].visible = false - @sprites[i].dispose + def set_up_timings + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_from_start = (j * NUM_SPRITES_X) + i # Top left -> bottom right + case @parameters[0] # Origin + when 1 # Top right -> bottom left + idx_from_start = (j * NUM_SPRITES_X) + NUM_SPRITES_X - i - 1 + when 2 # Bottom left -> top right + idx_from_start = TOTAL_SPRITES - 1 - ((j * NUM_SPRITES_X) + NUM_SPRITES_X - i - 1) + when 3 # Bottom right -> top left + idx_from_start = TOTAL_SPRITES - 1 - idx_from_start end + dist = i + (j.to_f * (NUM_SPRITES_X - 1) / (NUM_SPRITES_Y - 1)) + @timings[idx_from_start] = (dist / ((NUM_SPRITES_X - 1) * 2)) * (@duration - TIME_TO_ZOOM) end - @sprites.clear - @viewport.dispose if @viewport - @disposed = true end end - def update - return if disposed? - if @duration==0 - dispose - else - count = @numframes-@duration - for i in 0...@numtiles - if @frame[i]=count - @sprites[i].visible = true - @sprites[i].zoom_x = @addzoom*(count-@frame[i]) - @sprites[i].zoom_x = 1.0 if @sprites[i].zoom_x>1.0 - @sprites[i].zoom_y = @sprites[i].zoom_x - end - end + def dispose_all + @bitmap.dispose + end + + def update_anim + @sprites.each_with_index do |sprite, i| + next if @timings[i] < 0 || @timer < @timings[i] + sprite.visible = true + size = (@timer - @timings[i]) / TIME_TO_ZOOM + sprite.zoom_x = @zoom_x_target * size + sprite.zoom_y = @zoom_y_target * size + next if size < 1.0 + sprite.zoom_x = @zoom_x_target + sprite.zoom_y = @zoom_y_target + @timings[i] = -1 end - @duration -= 1 end end #============================================================================= # HGSS wild water #============================================================================= - class RisingSplash - def initialize(numframes) - @numframes = numframes - @duration = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @bubblebitmap = RPG::Cache.transition("water_1") - @splashbitmap = RPG::Cache.transition("water_2") - @blackbitmap = RPG::Cache.transition("black_half") - @buffer = Graphics.snap_to_bitmap - if !@bubblebitmap || !@splashbitmap || !@blackbitmap || !@buffer - @disposed = true - return - end - @width = @buffer.width - @height = @buffer.height - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @rearsprite = Sprite.new(@viewport) - @rearsprite.z = 1 - @rearsprite.zoom_y = 2.0 - @rearsprite.bitmap = @blackbitmap - @bgsprites = [] - rect = Rect.new(0,0,@width,2) - for i in 0...@height/2 - @bgsprites[i] = Sprite.new(@viewport) - @bgsprites[i].y = i*2 - @bgsprites[i].z = 2 - @bgsprites[i].bitmap = @buffer - rect.y = i*2 - @bgsprites[i].src_rect = rect - end - @bubblesprite = Sprite.new(@viewport) - @bubblesprite.y = @height - @bubblesprite.z = 3 - @bubblesprite.bitmap = @bubblebitmap - @splashsprite = Sprite.new(@viewport) - @splashsprite.y = @height - @splashsprite.z = 4 - @splashsprite.bitmap = @splashbitmap - @blacksprite = Sprite.new(@viewport) - @blacksprite.y = @height - @blacksprite.z = 5 - @blacksprite.zoom_y = 2.0 - @blacksprite.bitmap = @blackbitmap - @bubblesuby = @height*2/@numframes - @splashsuby = @bubblesuby*2 - @blacksuby = @height/(@numframes*0.1).floor - @angmult = 2/(@numframes/50.0) - end - - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @bubblebitmap.dispose if @bubblebitmap - @bubblebitmap = nil - @splashbitmap.dispose if @splashbitmap - @splashbitmap = nil - @blackbitmap.dispose if @blackbitmap - @blackbitmap = nil - @rearsprite.dispose if @rearsprite - for i in @bgsprites; i.dispose if i; end - @bgsprites.clear - @bubblesprite.dispose if @bubblesprite - @splashsprite.dispose if @splashsprite - @blacksprite.dispose if @blacksprite - @viewport.dispose if @viewport - @disposed = true - end - - def update - return if disposed? - if @duration==0 - dispose - else - angadd = (@numframes-@duration)*@angmult - amp = 6*angadd/8; amp = 6 if amp>6 - for i in 0...@bgsprites.length - @bgsprites[i].x = amp*Math.sin((i+angadd)*Math::PI/10) - end - @bubblesprite.x = (@width-@bubblebitmap.width)/2 - @bubblesprite.x -= 32*Math.sin((@numframes-@duration)/(@numframes/50)*3*Math::PI/60) - @bubblesprite.y -= @bubblesuby - if @duration<@numframes*0.5 - @splashsprite.y -= @splashsuby - end - if @duration<@numframes*0.1 - @blacksprite.y -= @blacksuby - @blacksprite.y = 0 if @blacksprite.y<0 - end + class RisingSplash < Transition_Base + DURATION = 1.25 + MAX_WAVE_AMPLITUDE = 6 + WAVE_SPACING = Math::PI / 10 # Density of overworld waves (20 strips per wave) + WAVE_SPEED = Math::PI * 10 # Speed of overworld waves going up the screen + MAX_BUBBLE_AMPLITUDE = -32 + BUBBLES_WAVE_SPEED = Math::PI * 2 + + def initialize_bitmaps + @bubble_bitmap = RPG::Cache.transition("water_1") + @splash_bitmap = RPG::Cache.transition("water_2") + @black_bitmap = RPG::Cache.transition("black_half") + dispose if !@bubble_bitmap || !@splash_bitmap || !@black_bitmap + end + + def initialize_sprites + @overworld_sprite.visible = false + # Overworld strips (they go all wavy) + rect = Rect.new(0, 0, Graphics.width, 2) + (Graphics.height / 2).times do |i| + @sprites[i] = new_sprite(0, i * 2, @overworld_bitmap) + @sprites[i].z = 2 + rect.y = i * 2 + @sprites[i].src_rect = rect + end + # Bubbles + @bubbles_sprite = new_sprite(0, Graphics.height, @bubble_bitmap) + @bubbles_sprite.z = 3 + # Water splash + @splash_sprite = new_sprite(0, Graphics.height, @splash_bitmap) + @splash_sprite.z = 4 + # Foreground black + @black_sprite = new_sprite(0, Graphics.height, @black_bitmap) + @black_sprite.z = 5 + @black_sprite.zoom_y = 2.0 + end + + def set_up_timings + @splash_rising_start = @duration * 0.5 + @black_rising_start = @duration * 0.9 + end + + def dispose_all + # Dispose sprites + @bubbles_sprite&.dispose + @splash_sprite&.dispose + @black_sprite&.dispose + # Dispose bitmaps + @bubble_bitmap&.dispose + @splash_bitmap&.dispose + @black_bitmap&.dispose + end + + def update_anim + # Make overworld wave strips oscillate + amplitude = MAX_WAVE_AMPLITUDE * [@timer / 0.1, 1].min # Build up to max in 0.1 seconds + @sprites.each_with_index do |sprite, i| + sprite.x = amplitude * Math.sin((@timer * WAVE_SPEED) + (i * WAVE_SPACING)) + end + # Move bubbles sprite up and oscillate side to side + @bubbles_sprite.x = (Graphics.width - @bubble_bitmap.width) / 2 + @bubbles_sprite.x += MAX_BUBBLE_AMPLITUDE * Math.sin(@timer * BUBBLES_WAVE_SPEED) + @bubbles_sprite.y = Graphics.height * (1 - (@timer * 1.2)) + # Move splash sprite up + if @timer >= @splash_rising_start + proportion = (@timer - @splash_rising_start) / (@duration - @splash_rising_start) + @splash_sprite.y = Graphics.height * (1 - (proportion * 2)) + end + # Move black sprite up + if @timer >= @black_rising_start + proportion = (@timer - @black_rising_start) / (@duration - @black_rising_start) + @black_sprite.y = Graphics.height * (1 - proportion) end - @duration -= 1 end end #============================================================================= # HGSS trainer outdoor day #============================================================================= - class TwoBallPass - def initialize(numframes) - @numframes = numframes - @duration = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @blackbitmap = RPG::Cache.transition("black_half") - @ballbitmap = RPG::Cache.transition("ball_small") - @buffer = Graphics.snap_to_bitmap - if !@blackbitmap || !@ballbitmap || !@buffer - @disposed = true - return - end - @width = @buffer.width - @height = @buffer.height - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @bgsprite = Sprite.new(@viewport) - @bgsprite.x = @width/2 - @bgsprite.y = @height/2 - @bgsprite.ox = @width/2 - @bgsprite.oy = @height/2 - @bgsprite.bitmap = @buffer - @blacksprites = [] - @ballsprites = [] - for i in 0...2 - @blacksprites[i] = Sprite.new(@viewport) - @blacksprites[i].x = (1-i*2)*@width - @blacksprites[i].y = i*@height/2 - @blacksprites[i].z = 1 - @blacksprites[i].bitmap = @blackbitmap - @ballsprites[i] = Sprite.new(@viewport) - @ballsprites[i].x = (1-i)*@width + (1-i*2)*@ballbitmap.width/2 - @ballsprites[i].y = @height/2 + (i*2-1)*@ballbitmap.height/2 - @ballsprites[i].z = 2 - @ballsprites[i].ox = @ballbitmap.width/2 - @ballsprites[i].oy = @ballbitmap.height/2 - @ballsprites[i].bitmap = @ballbitmap - end - @blacksprites[2] = Sprite.new(@viewport) - @blacksprites[2].y = @height/2 - @blacksprites[2].z = 1 - @blacksprites[2].oy = @height/4 - @blacksprites[2].zoom_y = 0.0 - @blacksprites[2].bitmap = @blackbitmap - @addxmult = 2.0*@width/((@numframes*0.6)**2) - @addzoom = 0.02*50/@numframes - end - - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @blackbitmap.dispose if @blackbitmap - @blackbitmap = nil - @ballbitmap.dispose if @ballbitmap - @ballbitmap = nil - @bgsprite.dispose if @bgsprite - for i in @blacksprites; i.dispose if i; end - @blacksprites.clear - for i in @ballsprites; i.dispose if i; end - @ballsprites.clear - @viewport.dispose if @viewport - @disposed = true - end - - def update - return if disposed? - if @duration==0 - dispose - elsif @duration>=@numframes*0.6 - for i in 0...2 - @ballsprites[i].x += (2*i-1)*((@width+@ballbitmap.width)/(0.4*@numframes)) - @ballsprites[i].angle += (2*i-1)*(360.0/(0.2*@numframes)) + class TwoBallPass < Transition_Base + DURATION = 1.0 + + def initialize_bitmaps + @black_bitmap = RPG::Cache.transition("black_half") + @ball_bitmap = RPG::Cache.transition("ball_small") + dispose if !@black_bitmap || !@ball_bitmap + end + + def initialize_sprites + @overworld_sprite.x = Graphics.width / 2 + @overworld_sprite.y = Graphics.height / 2 + @overworld_sprite.ox = @overworld_bitmap.width / 2 + @overworld_sprite.oy = @overworld_bitmap.height / 2 + # Balls that roll across the screen + @ball_sprites = [] + 2.times do |i| + x = ((1 - i) * Graphics.width) + ((1 - (i * 2)) * @ball_bitmap.width / 2) + y = (Graphics.height + (((i * 2) - 1) * @ball_bitmap.width)) / 2 + @ball_sprites[i] = new_sprite(x, y, @ball_bitmap, + @ball_bitmap.width / 2, @ball_bitmap.height / 2) + @ball_sprites[i].z = 2 + end + # Black foreground sprites + 2.times do |i| + @sprites[i] = new_sprite((1 - (i * 2)) * Graphics.width, i * Graphics.height / 2, @black_bitmap) + @sprites[i].z = 1 + end + @sprites[2] = new_sprite(0, Graphics.height / 2, @black_bitmap, 0, @black_bitmap.height / 2) + @sprites[2].z = 1 + @sprites[2].zoom_y = 0.0 + end + + def set_up_timings + @ball_start_x = [@ball_sprites[0].x, @ball_sprites[1].x] + @ball_roll_end = @duration * 0.4 + end + + def dispose_all + # Dispose sprites + if @ball_sprites + @ball_sprites.each { |s| s&.dispose } + @ball_sprites.clear + end + # Dispose bitmaps + @black_bitmap&.dispose + @ball_bitmap&.dispose + end + + def update_anim + if @timer <= @ball_roll_end + # Roll ball sprites across screen + proportion = @timer / @ball_roll_end + total_distance = Graphics.width + @ball_bitmap.width + @ball_sprites.each_with_index do |sprite, i| + sprite.x = @ball_start_x[i] + (((2 * i) - 1) * (total_distance * proportion)) + sprite.angle = ((2 * i) - 1) * 360 * proportion * 2 end else - addx = (@numframes*0.6-@duration)*@addxmult - for i in 0...2 - @bgsprite.zoom_x += @addzoom - @bgsprite.zoom_y += @addzoom - @blacksprites[i].x += (2*i-1)*addx + proportion = (@timer - @ball_roll_end) / (@duration - @ball_roll_end) + # Hide ball sprites + if @ball_sprites[0].visible + @ball_sprites.each { |s| s.visible = false } end - @blacksprites[2].zoom_y += 2*addx/@width - @blacksprites[0].x = 0 if @blacksprites[0].x<0 - @blacksprites[1].x = 0 if @blacksprites[1].x>0 + # Zoom in overworld sprite + @overworld_sprite.zoom_x = 1.0 + (proportion * proportion) # Ends at 2x zoom + @overworld_sprite.zoom_y = @overworld_sprite.zoom_x + # Slide first two black bars across + @sprites[0].x = Graphics.width * (1 - (proportion * proportion)) + @sprites[1].x = Graphics.width * ((proportion * proportion) - 1) + # Expand third black bar + @sprites[2].zoom_y = 2.0 * proportion * proportion # Ends at 2x zoom end - @duration -= 1 end end #============================================================================= # HGSS trainer outdoor night #============================================================================= - class SpinBallSplit - def initialize(numframes) - @numframes = numframes - @duration = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @blackbitmap = RPG::Cache.transition("black_half") - @ballbitmap = RPG::Cache.transition("ball_large") - @buffer = Graphics.snap_to_bitmap - if !@blackbitmap || !@ballbitmap || !@buffer - @disposed = true - return - end - @width = @buffer.width - @height = @buffer.height - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @bgsprites = [] - @blacksprites = [] - @ballsprites = [] - for i in 0...2 - @bgsprites[i] = Sprite.new(@viewport) - @bgsprites[i].x = @width/2 - @bgsprites[i].y = @height/2 - @bgsprites[i].ox = @width/2 - @bgsprites[i].oy = (1-i)*@height/2 - @bgsprites[i].bitmap = @buffer - @bgsprites[i].src_rect.set(0,i*@height/2,@width,@height/2) - @blacksprites[i] = Sprite.new(@viewport) - @blacksprites[i].x = (1-i*2)*@width - @blacksprites[i].y = i*@height/2 - @blacksprites[i].z = 1 - @blacksprites[i].bitmap = @blackbitmap - @ballsprites[i] = Sprite.new(@viewport) - @ballsprites[i].x = @width/2 - @ballsprites[i].y = @height/2 - @ballsprites[i].z = 2 - @ballsprites[i].ox = @ballbitmap.width/2 - @ballsprites[i].oy = (1-i)*@ballbitmap.height/2 - @ballsprites[i].zoom_x = 0.0 - @ballsprites[i].zoom_y = 0.0 - @ballsprites[i].bitmap = @ballbitmap - end - @addxmult = 2.0*@width/((@numframes*0.5)**2) - @addzoom = 0.02*50/@numframes - end - - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @blackbitmap.dispose if @blackbitmap - @blackbitmap = nil - @ballbitmap.dispose if @ballbitmap - @ballbitmap = nil - for i in @bgsprites; i.dispose if i; end - @bgsprites.clear - for i in @blacksprites; i.dispose if i; end - @blacksprites.clear - for i in @ballsprites; i.dispose if i; end - @ballsprites.clear - @viewport.dispose if @viewport - @disposed = true - end - - def update - return if disposed? - if @duration==0 - dispose - elsif @duration>=@numframes*0.6 - if @ballsprites[0].zoom_x<1.0 - @ballsprites[0].zoom_x += (1.0/(0.4*@numframes)) - @ballsprites[0].zoom_y += (1.0/(0.4*@numframes)) - @ballsprites[0].angle -= (360.0/(0.4*@numframes)) - if @ballsprites[0].zoom_x>=1.0 - for i in 0...2 - @ballsprites[i].src_rect.set(0,i*@ballbitmap.height/2, - @ballbitmap.width,@ballbitmap.height/2) - @ballsprites[i].zoom_x = @ballsprites[i].zoom_y = 1.0 - @ballsprites[i].angle = 0.0 - end + class SpinBallSplit < Transition_Base + DURATION = 1.0 + + def initialize_bitmaps + @black_bitmap = RPG::Cache.transition("black_half") + @ball_bitmap = RPG::Cache.transition("ball_large") + dispose if !@black_bitmap || !@ball_bitmap + end + + def initialize_sprites + @overworld_sprite.visible = false + @overworld_sprites = [] + @black_sprites = [] + @ball_sprites = [] + 2.times do |i| + # Overworld sprites (they split apart) + @overworld_sprites[i] = new_sprite(Graphics.width / 2, Graphics.height / 2, @overworld_bitmap, + Graphics.width / 2, (1 - i) * Graphics.height / 2) + @overworld_sprites[i].src_rect.set(0, i * Graphics.height / 2, Graphics.width, Graphics.height / 2) + # Black sprites + @black_sprites[i] = new_sprite((1 - i) * Graphics.width, i * Graphics.height / 2, @black_bitmap, + i * @black_bitmap.width, 0) + @black_sprites[i].z = 1 + # Ball sprites + @ball_sprites[i] = new_sprite(Graphics.width / 2, Graphics.height / 2, @ball_bitmap, + @ball_bitmap.width / 2, (1 - i) * @ball_bitmap.height / 2) + @ball_sprites[i].z = 2 + @ball_sprites[i].zoom_x = 0.0 + @ball_sprites[i].zoom_y = 0.0 + end + end + + def set_up_timings + @ball_spin_end = @duration * 0.4 + @slide_start = @duration * 0.5 + end + + def dispose_all + # Dispose sprites + if @overworld_sprites + @overworld_sprites.each { |s| s&.dispose } + @overworld_sprites.clear + end + if @black_sprites + @black_sprites.each { |s| s&.dispose } + @black_sprites.clear + end + if @ball_sprites + @ball_sprites.each { |s| s&.dispose } + @ball_sprites.clear + end + # Dispose bitmaps + @black_bitmap&.dispose + @ball_bitmap&.dispose + end + + def update_anim + if @timer < @ball_spin_end + # Ball spin + proportion = @timer / @ball_spin_end + @ball_sprites[0].zoom_x = proportion + @ball_sprites[0].zoom_y = proportion + @ball_sprites[0].angle = 360 * (1 - proportion) + elsif @timer < @slide_start + # Fix zoom/angle of ball sprites + if @ball_sprites[0].src_rect.height == @ball_bitmap.height + @ball_sprites.each_with_index do |sprite, i| + sprite.zoom_x = 1.0 + sprite.zoom_y = 1.0 + sprite.angle = 0 + sprite.src_rect.set(0, i * @ball_bitmap.height / 2, + @ball_bitmap.width, @ball_bitmap.height / 2) end end - # Gap between 0.6*@numframes and 0.5*@numframes - elsif @duration<@numframes*0.5 - addx = (@numframes*0.5-@duration)*@addxmult - for i in 0...2 - @bgsprites[i].x += (2*i-1)*addx - @bgsprites[i].zoom_x += @addzoom - @bgsprites[i].zoom_y += @addzoom - @blacksprites[i].x += (2*i-1)*addx - @ballsprites[i].x += (2*i-1)*addx + else + # Split overworld/ball apart, move blackness in following them + proportion = (@timer - @slide_start) / (@duration - @slide_start) + @overworld_sprites.each_with_index do |sprite, i| + sprite.x = (0.5 + (((i * 2) - 1) * proportion * proportion)) * Graphics.width + sprite.zoom_x = 1.0 + (proportion * proportion) # Ends at 2x zoom + sprite.zoom_y = sprite.zoom_x + @black_sprites[i].x = sprite.x + ((1 - (i * 2)) * Graphics.width / 2) + @ball_sprites[i].x = sprite.x end - @blacksprites[0].x = 0 if @blacksprites[0].x<0 - @blacksprites[1].x = 0 if @blacksprites[1].x>0 end - @duration -= 1 end end #============================================================================= # HGSS trainer indoor day #============================================================================= - class ThreeBallDown - def initialize(numframes) - @numframes = numframes - @duration = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @blackbitmap = RPG::Cache.transition("black_square") - @ballbitmap = RPG::Cache.transition("ball_small") - @buffer = Graphics.snap_to_bitmap - if !@blackbitmap || !@ballbitmap || !@buffer - @disposed = true + class ThreeBallDown < Transition_Base + DURATION = 2.0 + NUM_SPRITES_X = 8 + NUM_SPRITES_Y = 6 + TOTAL_SPRITES = NUM_SPRITES_X * NUM_SPRITES_Y + BALL_START_Y_OFFSETS = [400, 0, 100] + + def initialize_bitmaps + @black_bitmap = RPG::Cache.transition("black_square") + @ball_bitmap = RPG::Cache.transition("ball_small") + if !@black_bitmap || !@ball_bitmap + dispose return end - @width = @buffer.width - @height = @buffer.height - cx = Graphics.width/@blackbitmap.width # 8 - cy = Graphics.height/@blackbitmap.height # 6 - @numtiles = cx*cy - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @bgsprite = Sprite.new(@viewport) - @bgsprite.x = @width/2 - @bgsprite.y = @height/2 - @bgsprite.ox = @width/2 - @bgsprite.oy = @height/2 - @bgsprite.bitmap = @buffer - @frame = [] - @blacksprites = [] - for i in 0...cy - for j in 0...cx - k = i*cx+j - @blacksprites[k] = Sprite.new(@viewport) - @blacksprites[k].x = @blackbitmap.width*j - @blacksprites[k].y = @blackbitmap.height*i - @blacksprites[k].visible = false - @blacksprites[k].bitmap = @blackbitmap - @frame[k] = (((cy-i-1)*8+[0,4,1,6,7,2,5,3][j])*(@numframes*0.75)/@numtiles).floor + @zoom_x_target = Graphics.width.to_f / (@black_bitmap.width * NUM_SPRITES_X) + @zoom_y_target = Graphics.height.to_f / (@black_bitmap.height * NUM_SPRITES_Y) + end + + def initialize_sprites + @overworld_sprite.x = Graphics.width / 2 + @overworld_sprite.y = Graphics.height / 2 + @overworld_sprite.ox = @overworld_bitmap.width / 2 + @overworld_sprite.oy = @overworld_bitmap.height / 2 + # Black squares + NUM_SPRITES_Y.times do |j| + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + @sprites[idx_sprite] = new_sprite(i * @black_bitmap.width * @zoom_x_target, + j * @black_bitmap.height * @zoom_y_target, @black_bitmap) + @sprites[idx_sprite].visible = false end end - @ballsprites = [] - for i in 0...3 - @ballsprites[i] = Sprite.new(@viewport) - @ballsprites[i].x = 96+i*160 - @ballsprites[i].y = -@ballbitmap.height-[400,0,100][i] - @ballsprites[i].z = 2 - @ballsprites[i].ox = @ballbitmap.width/2 - @ballsprites[i].oy = @ballbitmap.height/2 - @ballsprites[i].bitmap = @ballbitmap + # Falling balls + @ball_sprites = [] + 3.times do |i| + @ball_sprites[i] = new_sprite((Graphics.width / 2) + ((i - 1) * 160), + -@ball_bitmap.height - BALL_START_Y_OFFSETS[i], + @ball_bitmap, @ball_bitmap.width / 2, @ball_bitmap.height / 2) + @ball_sprites[i].z = 2 + end + end + + def set_up_timings + @black_appear_start = @duration * 0.2 + appear_order = [0, 4, 1, 6, 7, 2, 5, 3] + period = @duration - @black_appear_start + NUM_SPRITES_Y.times do |j| + row_offset = NUM_SPRITES_Y - j - 1 + NUM_SPRITES_X.times do |i| + idx_sprite = (j * NUM_SPRITES_X) + i + @timings[idx_sprite] = period * ((row_offset * NUM_SPRITES_X) + appear_order[i]) / TOTAL_SPRITES + @timings[idx_sprite] += @black_appear_start + end end - @addyball = (@height+400+@ballbitmap.height*2)/(0.25*@numframes) - @addangle = 1.5*360/(0.25*@numframes) - @addzoom = 0.02*50/@numframes end - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @blackbitmap.dispose if @blackbitmap - @blackbitmap = nil - @ballbitmap.dispose if @ballbitmap - @ballbitmap = nil - @bgsprite.dispose if @bgsprite - for i in @blacksprites; i.dispose if i; end - @blacksprites.clear - for i in @ballsprites; i.dispose if i; end - @ballsprites.clear - @viewport.dispose if @viewport - @disposed = true + def dispose_all + # Dispose sprites + if @ball_sprites + @ball_sprites.each { |s| s&.dispose } + @ball_sprites.clear + end + # Dispose bitmaps + @black_bitmap&.dispose + @ball_bitmap&.dispose end - def update - return if disposed? - if @duration==0 - dispose - elsif @duration>=@numframes*0.75 - for i in 0...@ballsprites.length - @ballsprites[i].y += @addyball - @ballsprites[i].angle -= @addangle*([1,-1][(i==2) ? 1 : 0]) + def update_anim + if @timer < @black_appear_start + # Balls drop down screen while spinning + proportion = @timer / @black_appear_start + @ball_sprites.each_with_index do |sprite, i| + sprite.y = -@ball_bitmap.height - BALL_START_Y_OFFSETS[i] + sprite.y += (Graphics.height + BALL_START_Y_OFFSETS.max + (@ball_bitmap.height * 2)) * proportion + sprite.angle = 1.5 * 360 * proportion * ([1, -1][(i == 2) ? 0 : 1]) end else - count = (@numframes*0.75).floor-@duration - for i in 0...@numtiles - @blacksprites[i].visible = true if @frame[i]<=count + if @ball_sprites[0].visible + @ball_sprites.each { |s| s.visible = false } + end + # Black squares appear + @timings.each_with_index do |timing, i| + next if timing < 0 || @timer < timing + @sprites[i].visible = true + @timings[i] = -1 end - @bgsprite.zoom_x += @addzoom - @bgsprite.zoom_y += @addzoom + # Zoom in overworld sprite + proportion = (@timer - @black_appear_start) / (@duration - @black_appear_start) + @overworld_sprite.zoom_x = 1.0 + (proportion * proportion) # Ends at 2x zoom + @overworld_sprite.zoom_y = @overworld_sprite.zoom_x end - @duration -= 1 end end @@ -1236,392 +934,987 @@ def update # HGSS trainer indoor night # HGSS trainer cave #============================================================================= - class BallDown - def initialize(numframes) - @numframes = numframes - @duration = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @blackbitmap = RPG::Cache.transition("black_half") - @curvebitmap = RPG::Cache.transition("black_curve") - @ballbitmap = RPG::Cache.transition("ball_small") - @buffer = Graphics.snap_to_bitmap - if !@blackbitmap || !@curvebitmap || !@ballbitmap || !@buffer - @disposed = true - return + class BallDown < Transition_Base + DURATION = 0.9 + + def initialize_bitmaps + @black_bitmap = RPG::Cache.transition("black_half") + @curve_bitmap = RPG::Cache.transition("black_curve") + @ball_bitmap = RPG::Cache.transition("ball_small") + dispose if !@black_bitmap || !@curve_bitmap || !@ball_bitmap + end + + def initialize_sprites + @overworld_sprite.x = Graphics.width / 2 + @overworld_sprite.y = Graphics.height / 2 + @overworld_sprite.ox = @overworld_bitmap.width / 2 + @overworld_sprite.oy = @overworld_bitmap.height / 2 + # Black sprites + @sprites[0] = new_sprite(0, -@curve_bitmap.height, @black_bitmap, 0, @black_bitmap.height) + @sprites[0].z = 1 + @sprites[0].zoom_y = 2.0 + @sprites[1] = new_sprite(0, -@curve_bitmap.height, @curve_bitmap) + @sprites[1].z = 1 + # Ball sprite + @ball_sprite = new_sprite(Graphics.width / 2, -@ball_bitmap.height / 2, @ball_bitmap, + @ball_bitmap.width / 2, @ball_bitmap.height / 2) + @ball_sprite.z = 2 + @ball_sprite.zoom_x = 0.0 + @ball_sprite.zoom_y = 0.0 + end + + def set_up_timings + @ball_appear_end = @duration * 0.7 + end + + def dispose_all + # Dispose sprites + @ball_sprite&.dispose + # Dispose bitmaps + @black_bitmap&.dispose + @curve_bitmap&.dispose + @ball_bitmap&.dispose + end + + def update_anim + if @timer <= @ball_appear_end + # Make ball drop down and zoom in + proportion = @timer / @ball_appear_end + @ball_sprite.y = (-@ball_bitmap.height / 2) + ((Graphics.height + (@ball_bitmap.height * 3)) * proportion * proportion) + @ball_sprite.angle = -1.5 * 360 * proportion + @ball_sprite.zoom_x = 3 * proportion * proportion + @ball_sprite.zoom_y = @ball_sprite.zoom_x + else + @ball_sprite.visible = false + # Black curve and blackness descends + proportion = (@timer - @ball_appear_end) / (@duration - @ball_appear_end) + @sprites.each do |sprite| + sprite.y = -@curve_bitmap.height + ((Graphics.height + @curve_bitmap.height) * proportion) + end + # Zoom in overworld sprite + @overworld_sprite.zoom_x = 1.0 + (proportion * proportion) # Ends at 2x zoom + @overworld_sprite.zoom_y = @overworld_sprite.zoom_x end - @width = @buffer.width - @height = @buffer.height - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @bgsprite = Sprite.new(@viewport) - @bgsprite.x = @width/2 - @bgsprite.y = @height/2 - @bgsprite.ox = @width/2 - @bgsprite.oy = @height/2 - @bgsprite.bitmap = @buffer - @blacksprites = [] - @blacksprites[0] = Sprite.new(@viewport) - @blacksprites[0].y = -@curvebitmap.height - @blacksprites[0].z = 1 - @blacksprites[0].oy = @blackbitmap.height - @blacksprites[0].zoom_y = 2.0 - @blacksprites[0].bitmap = @blackbitmap - @blacksprites[1] = Sprite.new(@viewport) - @blacksprites[1].y = -@curvebitmap.height - @blacksprites[1].z = 1 - @blacksprites[1].bitmap = @curvebitmap - @ballsprite = Sprite.new(@viewport) - @ballsprite.x = @width/2 - @ballsprite.y = -@ballbitmap.height/2 - @ballsprite.z = 2 - @ballsprite.ox = @ballbitmap.width/2 - @ballsprite.oy = @ballbitmap.height/2 - @ballsprite.zoom_x = 0.25 - @ballsprite.zoom_y = 0.25 - @ballsprite.bitmap = @ballbitmap - @addyball = (@height+@ballbitmap.height*2.5)/(0.5*@numframes) - @addangle = 1.5*360/(0.5*@numframes) - @addzoomball = 2.5/(0.5*@numframes) - @addy = (@height+@curvebitmap.height)/(@numframes*0.5) - @addzoom = 0.02*50/@numframes - end - - def disposed?; @disposed; end + end + end - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @blackbitmap.dispose if @blackbitmap - @blackbitmap = nil - @curvebitmap.dispose if @curvebitmap - @curvebitmap = nil - @ballbitmap.dispose if @ballbitmap - @ballbitmap = nil - @bgsprite.dispose if @bgsprite - for i in @blacksprites; i.dispose if i; end - @blacksprites.clear - @ballsprite.dispose - @viewport.dispose if @viewport - @disposed = true + #============================================================================= + # HGSS trainer water day + #============================================================================= + class WavyThreeBallUp < Transition_Base + DURATION = 1.1 + BALL_OFFSETS = [1.5, 3.25, 2.5] + MAX_WAVE_AMPLITUDE = 24 + WAVE_SPACING = Math::PI / 24 # Density of overworld waves (48 strips per wave) + WAVE_SPEED = Math::PI * 4 # Speed of overworld waves going up the screen + + def initialize_bitmaps + @black_bitmap = RPG::Cache.transition("black_half") + @ball_bitmap = RPG::Cache.transition("ball_small") + dispose if !@black_bitmap || !@ball_bitmap + end + + def initialize_sprites + @overworld_sprite.visible = false + # Overworld strips (they go all wavy) + rect = Rect.new(0, 0, Graphics.width, 4) + (Graphics.height / 4).times do |i| + @sprites[i] = new_sprite(0, i * 4, @overworld_bitmap) + @sprites[i].z = 2 + rect.y = i * 4 + @sprites[i].src_rect = rect + end + # Ball sprites + @ball_sprites = [] + 3.times do |i| + @ball_sprites[i] = new_sprite(((2 * i) + 1) * Graphics.width / 6, + BALL_OFFSETS[i] * Graphics.height, + @ball_bitmap, @ball_bitmap.width / 2, @ball_bitmap.height / 2) + @ball_sprites[i].z = 4 + end + # Black columns that follow the ball sprites + @black_trail_sprites = [] + 3.times do |i| + @black_trail_sprites[i] = new_sprite((i - 1) * Graphics.width * 2 / 3, + BALL_OFFSETS[i] * Graphics.height, @black_bitmap) + @black_trail_sprites[i].z = 3 + @black_trail_sprites[i].zoom_y = 2.0 + end + end + + def set_up_timings + @ball_rising_start = @duration * 0.4 + end + + def dispose_all + # Dispose sprites + if @ball_sprites + @ball_sprites.each { |s| s&.dispose } + @ball_sprites.clear + end + if @black_trail_sprites + @black_trail_sprites.each { |s| s&.dispose } + @black_trail_sprites.clear + end + # Dispose bitmaps + @black_bitmap&.dispose + @ball_bitmap&.dispose + end + + def update_anim + # Make overworld wave strips oscillate + amplitude = MAX_WAVE_AMPLITUDE * [@timer / 0.1, 1].min # Build up to max in 0.1 seconds + @sprites.each_with_index do |sprite, i| + sprite.x = (1 - ((i % 2) * 2)) * amplitude * Math.sin((@timer * WAVE_SPEED) + (i * WAVE_SPACING)) + end + # Move balls and trailing blackness up + if @timer >= @ball_rising_start + proportion = (@timer - @ball_rising_start) / (@duration - @ball_rising_start) + @ball_sprites.each_with_index do |sprite, i| + sprite.y = (BALL_OFFSETS[i] * Graphics.height) - (Graphics.height * 3.5 * proportion) + sprite.angle = [-1, -1, 1][i] * 360 * 2 * proportion + @black_trail_sprites[i].y = sprite.y + @black_trail_sprites[i].y = 0 if @black_trail_sprites[i].y < 0 + end + end end + end - def update - return if disposed? - if @duration==0 - dispose - elsif @duration>=@numframes*0.5 - @ballsprite.y += @addyball - @ballsprite.angle -= @addangle - @ballsprite.zoom_x += @addzoomball - @ballsprite.zoom_y += @addzoomball + #============================================================================= + # HGSS trainer water night + #============================================================================= + class WavySpinBall < Transition_Base + DURATION = 1.0 + MAX_WAVE_AMPLITUDE = 24 + WAVE_SPACING = Math::PI / 24 # Density of overworld waves (48 strips per wave) + WAVE_SPEED = Math::PI * 4 # Speed of overworld waves going up the screen + + def initialize_bitmaps + @black_bitmap = RPG::Cache.transition("black_half") + @ball_bitmap = RPG::Cache.transition("ball_large") + dispose if !@black_bitmap || !@ball_bitmap + end + + def initialize_sprites + @overworld_sprite.visible = false + # Overworld strips (they go all wavy) + rect = Rect.new(0, 0, Graphics.width, 4) + (Graphics.height / 4).times do |i| + @sprites[i] = new_sprite(0, i * 4, @overworld_bitmap) + @sprites[i].z = 2 + rect.y = i * 4 + @sprites[i].src_rect = rect + end + # Ball sprite + @ball_sprite = new_sprite(Graphics.width / 2, Graphics.height / 2, @ball_bitmap, + @ball_bitmap.width / 2, @ball_bitmap.height / 2) + @ball_sprite.z = 3 + @ball_sprite.opacity = 0 + # Black sprite + @black_sprite = new_sprite(Graphics.width / 2, Graphics.height / 2, @black_bitmap, + @black_bitmap.width / 2, @black_bitmap.height / 2) + @black_sprite.z = 4 + @black_sprite.zoom_x = 0.0 + @black_sprite.zoom_y = 0.0 + end + + def set_up_timings + @ball_appear_end = @duration * 0.4 + @black_appear_start = @duration * 0.5 + end + + def dispose_all + # Dispose sprites + @ball_sprite&.dispose + @black_sprite&.dispose + # Dispose bitmaps + @black_bitmap&.dispose + @ball_bitmap&.dispose + end + + def update_anim + # Make overworld wave strips oscillate + amplitude = MAX_WAVE_AMPLITUDE * [@timer / 0.1, 1].min # Build up to max in 0.1 seconds + @sprites.each_with_index do |sprite, i| + sprite.x = (1 - ((i % 2) * 2)) * amplitude * Math.sin((@timer * WAVE_SPEED) + (i * WAVE_SPACING)) + end + if @timer <= @ball_appear_end + # Fade in ball while spinning + proportion = @timer / @ball_appear_end + @ball_sprite.opacity = 255 * proportion + @ball_sprite.angle = -360 * proportion + elsif @timer <= @black_appear_start + # Fix opacity/angle of ball sprite + @ball_sprite.opacity = 255 + @ball_sprite.angle = 0 else - @blacksprites[1].y += @addy - @blacksprites[0].y = @blacksprites[1].y - @bgsprite.zoom_x += @addzoom - @bgsprite.zoom_y += @addzoom + # Spread blackness from centre + proportion = (@timer - @black_appear_start) / (@duration - @black_appear_start) + @black_sprite.zoom_x = proportion + @black_sprite.zoom_y = proportion * 2 end - @duration -= 1 end end #============================================================================= - # HGSS trainer water day + # HGSS double trainers #============================================================================= - class WavyThreeBallUp - def initialize(numframes) - @numframes = numframes - @duration = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @blackbitmap = RPG::Cache.transition("black_half") - @ballbitmap = RPG::Cache.transition("ball_small") - @buffer = Graphics.snap_to_bitmap - if !@blackbitmap || !@ballbitmap || !@buffer - @disposed = true - return + class FourBallBurst < Transition_Base + DURATION = 0.9 + + def initialize_bitmaps + @black_1_bitmap = RPG::Cache.transition("black_wedge_1") + @black_2_bitmap = RPG::Cache.transition("black_wedge_2") + @black_3_bitmap = RPG::Cache.transition("black_wedge_3") + @black_4_bitmap = RPG::Cache.transition("black_wedge_4") + @ball_bitmap = RPG::Cache.transition("ball_small") + dispose if !@black_1_bitmap || !@black_2_bitmap || !@black_3_bitmap || + !@black_4_bitmap || !@ball_bitmap + end + + def initialize_sprites + # Ball sprites + @ball_sprites = [] + 4.times do |i| + @ball_sprites[i] = new_sprite(Graphics.width / 2, Graphics.height / 2, @ball_bitmap, + @ball_bitmap.width / 2, @ball_bitmap.height / 2) + @ball_sprites[i].z = [2, 1, 3, 0][i] + end + # Black wedges + 4.times do |i| + b = [@black_1_bitmap, @black_2_bitmap, @black_3_bitmap, @black_4_bitmap][i] + @sprites[i] = new_sprite((i == 1) ? 0 : Graphics.width / 2, (i == 2) ? 0 : Graphics.height / 2, b, + (i.even?) ? b.width / 2 : 0, (i.even?) ? 0 : b.height / 2) + @sprites[i].zoom_x = 0.0 if i.even? + @sprites[i].zoom_y = 0.0 if i.odd? + @sprites[i].visible = false + end + end + + def set_up_timings + @ball_appear_end = @duration * 0.4 + end + + def dispose_all + # Dispose sprites + @ball_sprites.each { |s| s&.dispose } + @ball_sprites.clear + # Dispose bitmaps + @black_1_bitmap&.dispose + @black_2_bitmap&.dispose + @black_3_bitmap&.dispose + @black_4_bitmap&.dispose + @ball_bitmap&.dispose + end + + def update_anim + if @timer <= @ball_appear_end + # Balls fly out from centre of screen + proportion = @timer / @ball_appear_end + ball_travel_x = (Graphics.width + (@ball_bitmap.width * 2)) / 2 + ball_travel_y = (Graphics.height + (@ball_bitmap.height * 2)) / 2 + @ball_sprites.each_with_index do |sprite, i| + sprite.x = (Graphics.width / 2) + ([0, 1, 0, -1][i] * ball_travel_x * proportion) if i.odd? + sprite.y = (Graphics.height / 2) + ([1, 0, -1, 0][i] * ball_travel_y * proportion) if i.even? + end + else + # Black wedges expand to fill screen + proportion = (@timer - @ball_appear_end) / (@duration - @ball_appear_end) + @sprites.each_with_index do |sprite, i| + sprite.visible = true + sprite.zoom_x = proportion if i.even? + sprite.zoom_y = proportion if i.odd? + end end - @width = @buffer.width - @height = @buffer.height - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @rearsprite = Sprite.new(@viewport) - @rearsprite.z = 1 - @rearsprite.zoom_y = 2.0 - @rearsprite.bitmap = @blackbitmap - @bgsprites = [] - rect = Rect.new(0,0,@width,2) - for i in 0...@height/2 - @bgsprites[i] = Sprite.new(@viewport) - @bgsprites[i].y = i*2 - @bgsprites[i].z = 2 - @bgsprites[i].bitmap = @buffer - rect.y = i*2 - @bgsprites[i].src_rect = rect - end - @blacksprites = [] - @ballsprites = [] - for i in 0...3 - @blacksprites[i] = Sprite.new(@viewport) - @blacksprites[i].x = (i-1)*@width*2/3 - @blacksprites[i].y = [@height*1.5,@height*3.25,@height*2.5][i] - @blacksprites[i].z = 3 - @blacksprites[i].zoom_y = 2.0 - @blacksprites[i].bitmap = @blackbitmap - @ballsprites[i] = Sprite.new(@viewport) - @ballsprites[i].x = (2*i+1)*@width/6 - @ballsprites[i].y = [@height*1.5,@height*3.25,@height*2.5][i] - @ballsprites[i].z = 4 - @ballsprites[i].ox = @ballbitmap.width/2 - @ballsprites[i].oy = @ballbitmap.height/2 - @ballsprites[i].bitmap = @ballbitmap - end - @suby = (@height*3.5)/(@numframes*0.6) - @angmult = 4/(@numframes/50.0) - end - - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @blackbitmap.dispose if @blackbitmap - @blackbitmap = nil - @ballbitmap.dispose if @ballbitmap - @ballbitmap = nil - @rearsprite.dispose if @rearsprite - for i in @bgsprites; i.dispose if i; end - @bgsprites.clear - for i in @blacksprites; i.dispose if i; end - @blacksprites.clear - for i in @ballsprites; i.dispose if i; end - @ballsprites.clear - @viewport.dispose if @viewport - @disposed = true end + end - def update - return if disposed? - if @duration==0 - dispose - else - angadd = (@numframes-@duration)*@angmult - amp = 24*angadd/16; amp = 24 if amp>24 - for i in 0...@bgsprites.length - @bgsprites[i].x = amp*Math.sin((i+angadd)*Math::PI/48)*((i%2)*2-1) + #============================================================================= + # HGSS VS Trainer animation + # Uses $game_temp.transition_animation_data, and expects it to be an array + # like so: [:TRAINERTYPE, "display name"] + # Bar graphics are named hgss_vsBar_TRAINERTYPE.png. + # Trainer sprites are named hgss_vs_TRAINERTYPE.png. + #============================================================================= + class VSTrainer < Transition_Base + DURATION = 4.0 + BAR_Y = 80 + BAR_SCROLL_SPEED = 1800 + BAR_MASK = [8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7] + FOE_SPRITE_X_LIMIT = 384 # Slides to here before jumping to final position + FOE_SPRITE_X = 428 # Final position of foe sprite + + def initialize_bitmaps + @bar_bitmap = RPG::Cache.transition("hgss_vsBar_#{$game_temp.transition_animation_data[0]}") + @vs_1_bitmap = RPG::Cache.transition("hgss_vs1") + @vs_2_bitmap = RPG::Cache.transition("hgss_vs2") + @foe_bitmap = RPG::Cache.transition("hgss_vs_#{$game_temp.transition_animation_data[0]}") + @black_bitmap = RPG::Cache.transition("black_half") + dispose if !@bar_bitmap || !@vs_1_bitmap || !@vs_2_bitmap || !@foe_bitmap || !@black_bitmap + end + + def initialize_sprites + @flash_viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) + @flash_viewport.z = 99999 + @flash_viewport.color = Color.new(255, 255, 255, 0) + # Background black + @rear_black_sprite = new_sprite(0, 0, @black_bitmap) + @rear_black_sprite.z = 1 + @rear_black_sprite.zoom_y = 2.0 + @rear_black_sprite.opacity = 224 + @rear_black_sprite.visible = false + # Bar sprites (need 2 of them to make them loop around) + ((Graphics.width.to_f / @bar_bitmap.width).ceil + 1).times do |i| + spr = new_sprite(@bar_bitmap.width * i, BAR_Y, @bar_bitmap) + spr.z = 2 + @sprites.push(spr) + end + # Overworld sprite + @bar_mask_sprite = new_sprite(0, 0, @overworld_bitmap.clone) + @bar_mask_sprite.z = 3 + # VS logo + @vs_x = 144 + @vs_y = @sprites[0].y + (@sprites[0].height / 2) + @vs_main_sprite = new_sprite(@vs_x, @vs_y, @vs_1_bitmap, @vs_1_bitmap.width / 2, @vs_1_bitmap.height / 2) + @vs_main_sprite.z = 4 + @vs_main_sprite.visible = false + @vs_1_sprite = new_sprite(@vs_x, @vs_y, @vs_2_bitmap, @vs_2_bitmap.width / 2, @vs_2_bitmap.height / 2) + @vs_1_sprite.z = 5 + @vs_1_sprite.zoom_x = 2.0 + @vs_1_sprite.zoom_y = @vs_1_sprite.zoom_x + @vs_1_sprite.visible = false + @vs_2_sprite = new_sprite(@vs_x, @vs_y, @vs_2_bitmap, @vs_2_bitmap.width / 2, @vs_2_bitmap.height / 2) + @vs_2_sprite.z = 6 + @vs_2_sprite.zoom_x = 2.0 + @vs_2_sprite.zoom_y = @vs_2_sprite.zoom_x + @vs_2_sprite.visible = false + # Foe sprite + @foe_sprite = new_sprite(Graphics.width + @foe_bitmap.width, @sprites[0].y + @sprites[0].height - 12, + @foe_bitmap, @foe_bitmap.width / 2, @foe_bitmap.height) + @foe_sprite.z = 7 + @foe_sprite.color = Color.new(0, 0, 0) + # Sprite with foe's name written in it + @text_sprite = BitmapSprite.new(Graphics.width, @bar_bitmap.height, @viewport) + @text_sprite.y = BAR_Y + @text_sprite.z = 8 + @text_sprite.visible = false + pbSetSystemFont(@text_sprite.bitmap) + pbDrawTextPositions(@text_sprite.bitmap, + [[$game_temp.transition_animation_data[1], 244, 86, 0, + Color.new(248, 248, 248), Color.new(72, 80, 80)]]) + # Foreground black + @black_sprite = new_sprite(0, 0, @black_bitmap) + @black_sprite.z = 10 + @black_sprite.zoom_y = 2.0 + @black_sprite.visible = false + end + + def set_up_timings + @bar_x = 0 + @bar_appear_end = 0.2 # Starts appearing at 0.0 + @vs_appear_start = 0.7 + @vs_appear_start_2 = 0.9 + @vs_shrink_time = @vs_appear_start_2 - @vs_appear_start + @vs_appear_final = @vs_appear_start_2 + @vs_shrink_time + @foe_appear_start = 1.25 + @foe_appear_end = 1.4 + @flash_start = 1.9 + @flash_duration = 0.25 + @fade_to_white_start = 3.0 + @fade_to_white_end = 3.5 + @fade_to_black_start = 3.8 + end + + def dispose_all + # Dispose sprites + @rear_black_sprite&.dispose + @bar_mask_sprite&.dispose + @vs_main_sprite&.dispose + @vs_1_sprite&.dispose + @vs_2_sprite&.dispose + @foe_sprite&.dispose + @text_sprite&.dispose + @black_sprite&.dispose + # Dispose bitmaps + @bar_bitmap&.dispose + @vs_1_bitmap&.dispose + @vs_2_bitmap&.dispose + @foe_bitmap&.dispose + @black_bitmap&.dispose + # Dispose viewport + @flash_viewport&.dispose + end + + def update_anim + # Bar scrolling + @bar_x -= Graphics.delta_s * BAR_SCROLL_SPEED + @bar_x += @bar_bitmap.width if @bar_x <= -@bar_bitmap.width + @sprites.each_with_index { |spr, i| spr.x = @bar_x + (i * @bar_bitmap.width) } + # Vibrate VS sprite + vs_phase = (@timer * 30).to_i % 3 + @vs_main_sprite.x = @vs_x + [0, 4, 0][vs_phase] + @vs_main_sprite.y = @vs_y + [0, 0, -4][vs_phase] + if @timer >= @fade_to_black_start + # Fade to black + @black_sprite.visible = true + proportion = (@timer - @fade_to_black_start) / (@duration - @fade_to_black_start) + @flash_viewport.color.alpha = 255 * (1 - proportion) + elsif @timer >= @fade_to_white_start + # Slowly fade to white + proportion = (@timer - @fade_to_white_start) / (@fade_to_white_end - @fade_to_white_start) + @flash_viewport.color.alpha = 255 * proportion + elsif @timer >= @flash_start + @flash_duration + @flash_viewport.color.alpha = 0 + elsif @timer >= @flash_start + # Flash the screen white + proportion = (@timer - @flash_start) / @flash_duration + if proportion >= 0.5 + @flash_viewport.color.alpha = 320 * 2 * (1 - proportion) + @rear_black_sprite.visible = true + @foe_sprite.color.alpha = 0 + @text_sprite.visible = true + else + @flash_viewport.color.alpha = 320 * 2 * proportion end - if @duration<@numframes*0.6 - for i in 0...3 - @blacksprites[i].y -= @suby - @blacksprites[i].y = 0 if @blacksprites[i].y<0 - @ballsprites[i].y -= @suby - @ballsprites[i].angle += (2*(i%2)-1)*(360.0/(0.2*@numframes)) + elsif @timer >= @foe_appear_end + @foe_sprite.x = FOE_SPRITE_X + elsif @timer >= @foe_appear_start + # Foe sprite appears + proportion = (@timer - @foe_appear_start) / (@foe_appear_end - @foe_appear_start) + start_x = Graphics.width + (@foe_bitmap.width / 2) + @foe_sprite.x = start_x + (FOE_SPRITE_X_LIMIT - start_x) * proportion + elsif @timer >= @vs_appear_final + @vs_1_sprite.visible = false + elsif @timer >= @vs_appear_start_2 + # Temp VS sprites enlarge and shrink again + if @vs_2_sprite.visible + @vs_2_sprite.zoom_x = 1.6 - 0.8 * (@timer - @vs_appear_start_2) / @vs_shrink_time + @vs_2_sprite.zoom_y = @vs_2_sprite.zoom_x + if @vs_2_sprite.zoom_x <= 1.2 + @vs_2_sprite.visible = false + @vs_main_sprite.visible = true end end + @vs_1_sprite.zoom_x = 2.0 - 0.8 * (@timer - @vs_appear_start_2) / @vs_shrink_time + @vs_1_sprite.zoom_y = @vs_1_sprite.zoom_x + elsif @timer >= @vs_appear_start + # Temp VS sprites appear and start shrinking + @vs_2_sprite.visible = true + @vs_2_sprite.zoom_x = 2.0 - 0.8 * (@timer - @vs_appear_start) / @vs_shrink_time + @vs_2_sprite.zoom_y = @vs_2_sprite.zoom_x + if @vs_1_sprite.visible || @vs_2_sprite.zoom_x <= 1.6 # Halfway between 2.0 and 1.2 + @vs_1_sprite.visible = true + @vs_1_sprite.zoom_x = 2.0 - 0.8 * (@timer - @vs_appear_start - (@vs_shrink_time / 2)) / @vs_shrink_time + @vs_1_sprite.zoom_y = @vs_1_sprite.zoom_x + end + elsif @timer >= @bar_appear_end + @bar_mask_sprite.visible = false + else + start_x = Graphics.width * (1 - (@timer / @bar_appear_end)) + color = Color.new(0, 0, 0, 0) # Transparent + (@sprites[0].height / 2).times do |i| + x = start_x - BAR_MASK[i % BAR_MASK.length] * 4 + @bar_mask_sprite.bitmap.fill_rect(x, BAR_Y + (i * 2), @bar_mask_sprite.width - x, 2, color) + end end - @duration -= 1 end end #============================================================================= - # HGSS trainer water night + # HGSS VS Elite Four/Champion animation + # Uses $game_temp.transition_animation_data, and expects it to be an array + # like so: [:TRAINERTYPE, "display name", "player sprite name minus 'vsE4_'"] + # Bar graphics are named vsE4Bar_TRAINERTYPE.png. + # Trainer sprites are named vsE4_TRAINERTYPE.png. #============================================================================= - class WavySpinBall - def initialize(numframes) - @numframes = numframes - @duration = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @blackbitmap = RPG::Cache.transition("black_half") - @ballbitmap = RPG::Cache.transition("ball_large") - @buffer = Graphics.snap_to_bitmap - if !@blackbitmap || !@ballbitmap || !@buffer - @disposed = true - return + class VSEliteFour < Transition_Base + DURATION = 3.5 + BAR_X_INDENT = 48 + BAR_Y_INDENT = 64 # = height of trainer sprite / 2 + BAR_OVERSHOOT = 20 + TRAINER_X_OFFSET = 160 + TRAINER_Y_OFFSET = 8 + BAR_HEIGHT = 192 # = Graphics.height / 2 + FOE_SPRITE_X_LIMIT = 384 # Slides to here before jumping to final position + FOE_SPRITE_X = 428 # Final position of foe sprite + + def initialize_bitmaps + @bar_bitmap = RPG::Cache.transition("vsE4Bar_#{$game_temp.transition_animation_data[0]}") + @vs_1_bitmap = RPG::Cache.transition("hgss_vs1") + @vs_2_bitmap = RPG::Cache.transition("hgss_vs2") + @player_bitmap = RPG::Cache.transition("vsE4_#{$game_temp.transition_animation_data[2]}") + @foe_bitmap = RPG::Cache.transition("vsE4_#{$game_temp.transition_animation_data[0]}") + @black_bitmap = RPG::Cache.transition("black_half") + dispose if !@bar_bitmap || !@vs_1_bitmap || !@vs_2_bitmap || !@foe_bitmap || !@black_bitmap + @num_bar_frames = @bar_bitmap.height / BAR_HEIGHT + end + + def initialize_sprites + @flash_viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) + @flash_viewport.z = 99999 + @flash_viewport.color = Color.new(255, 255, 255, 0) + # Background black + @rear_black_sprite = new_sprite(0, 0, @black_bitmap) + @rear_black_sprite.z = 1 + @rear_black_sprite.zoom_y = 2.0 + @rear_black_sprite.opacity = 192 + @rear_black_sprite.visible = false + # Player's bar sprite + @player_bar_x = -BAR_X_INDENT + @player_bar_start_x = @player_bar_x - (@bar_bitmap.width / 2) + @player_bar_y = BAR_Y_INDENT + @player_bar_sprite = new_sprite(@player_bar_start_x, @player_bar_y, @bar_bitmap) + @player_bar_sprite.z = 2 + @player_bar_sprite.src_rect.width = @bar_bitmap.width / 2 + @player_bar_sprite.src_rect.height = BAR_HEIGHT + # Foe's bar sprite + @foe_bar_x = Graphics.width + BAR_X_INDENT - (@bar_bitmap.width / 2) + @foe_bar_start_x = @foe_bar_x + (@bar_bitmap.width / 2) + @foe_bar_y = Graphics.height - BAR_HEIGHT - BAR_Y_INDENT + @foe_bar_sprite = new_sprite(@foe_bar_start_x, @foe_bar_y, @bar_bitmap) + @foe_bar_sprite.z = 2 + @foe_bar_sprite.src_rect.x = @bar_bitmap.width / 2 + @foe_bar_sprite.src_rect.width = @bar_bitmap.width / 2 + @foe_bar_sprite.src_rect.height = BAR_HEIGHT + # Player sprite + @player_sprite = new_sprite(@player_bar_sprite.x + TRAINER_X_OFFSET, + @player_bar_sprite.y + BAR_HEIGHT - TRAINER_Y_OFFSET, + @player_bitmap, @player_bitmap.width / 2, @player_bitmap.height) + @player_sprite.z = 7 + @player_sprite.color = Color.new(0, 0, 0) + # Foe sprite + @foe_sprite = new_sprite(@foe_bar_sprite.x + (@bar_bitmap.width / 2) - TRAINER_X_OFFSET, + @foe_bar_sprite.y + @foe_bitmap.height - TRAINER_Y_OFFSET, + @foe_bitmap, @foe_bitmap.width / 2, @foe_bitmap.height) + @foe_sprite.z = 7 + @foe_sprite.color = Color.new(0, 0, 0) + # Sprite with foe's name written in it + @text_sprite = BitmapSprite.new(@bar_bitmap.width / 2, BAR_HEIGHT, @viewport) + @text_sprite.x = @foe_bar_start_x + @text_sprite.y = @foe_bar_y + @text_sprite.z = 8 + pbSetSystemFont(@text_sprite.bitmap) + pbDrawTextPositions(@text_sprite.bitmap, + [[$game_temp.transition_animation_data[1], 160, 86, 0, + Color.new(248, 248, 248), Color.new(72, 80, 80)]]) + # VS logo + @vs_main_sprite = new_sprite(Graphics.width / 2, Graphics.height / 2, @vs_1_bitmap, + @vs_1_bitmap.width / 2, @vs_1_bitmap.height / 2) + @vs_main_sprite.z = 14 + @vs_main_sprite.visible = false + @vs_1_sprite = new_sprite(Graphics.width / 2, Graphics.height / 2, @vs_2_bitmap, + @vs_2_bitmap.width / 2, @vs_2_bitmap.height / 2) + @vs_1_sprite.z = 15 + @vs_1_sprite.zoom_x = 2.0 + @vs_1_sprite.zoom_y = @vs_1_sprite.zoom_x + @vs_1_sprite.visible = false + @vs_2_sprite = new_sprite(Graphics.width / 2, Graphics.height / 2, @vs_2_bitmap, + @vs_2_bitmap.width / 2, @vs_2_bitmap.height / 2) + @vs_2_sprite.z = 16 + @vs_2_sprite.zoom_x = 2.0 + @vs_2_sprite.zoom_y = @vs_2_sprite.zoom_x + @vs_2_sprite.visible = false + # Foreground black + @black_sprite = new_sprite(0, 0, @black_bitmap) + @black_sprite.z = 20 + @black_sprite.zoom_y = 2.0 + @black_sprite.visible = false + end + + def set_up_timings + @flash_1_start = 0.0 + @flash_1_duration = 0.25 + @bar_appear_start = 0.5 + @bar_appear_end = 0.7 + @vs_appear_start = 0.6 + @vs_appear_start_2 = 0.8 + @vs_shrink_time = @vs_appear_start_2 - @vs_appear_start + @vs_appear_final = @vs_appear_start_2 + @vs_shrink_time + @flash_start = 1.7 + @flash_duration = 0.35 + @fade_to_white_start = 2.7 + @fade_to_white_end = 3.0 + @fade_to_black_start = 3.3 + end + + def dispose_all + # Dispose sprites + @rear_black_sprite&.dispose + @player_bar_sprite&.dispose + @foe_bar_sprite&.dispose + @player_sprite&.dispose + @foe_sprite&.dispose + @text_sprite&.dispose + @vs_main_sprite&.dispose + @vs_1_sprite&.dispose + @vs_2_sprite&.dispose + @black_sprite&.dispose + # Dispose bitmaps + @bar_bitmap&.dispose + @vs_1_bitmap&.dispose + @vs_2_bitmap&.dispose + @player_bitmap&.dispose + @foe_bitmap&.dispose + @black_bitmap&.dispose + # Dispose viewport + @flash_viewport&.dispose + end + + def update_anim + # Bars/trainer sprites slide in + if @timer > @bar_appear_end + @player_bar_sprite.x = @player_bar_x + @player_sprite.x = @player_bar_sprite.x + TRAINER_X_OFFSET + @foe_bar_sprite.x = @foe_bar_x + @foe_sprite.x = @foe_bar_sprite.x + (@bar_bitmap.width / 2) - TRAINER_X_OFFSET + @text_sprite.x = @foe_bar_sprite.x + elsif @timer > @bar_appear_start + # Bars/trainer sprites slide in + proportion = (@timer - @bar_appear_start) / (@bar_appear_end - @bar_appear_start) + sqrt_proportion = Math.sqrt(proportion) + @player_bar_sprite.x = @player_bar_start_x + (@player_bar_x + BAR_OVERSHOOT - @player_bar_start_x) * sqrt_proportion + @player_sprite.x = @player_bar_sprite.x + TRAINER_X_OFFSET + @foe_bar_sprite.x = @foe_bar_start_x + (@foe_bar_x - BAR_OVERSHOOT - @foe_bar_start_x) * sqrt_proportion + @foe_sprite.x = @foe_bar_sprite.x + (@bar_bitmap.width / 2) - TRAINER_X_OFFSET + @text_sprite.x = @foe_bar_sprite.x + end + # Animate bars + if @timer >= @flash_start + 0.33 * @flash_duration + bar_phase = (@timer * 30).to_i % @num_bar_frames + @player_bar_sprite.src_rect.y = bar_phase * BAR_HEIGHT + @foe_bar_sprite.src_rect.y = bar_phase * BAR_HEIGHT + end + # Vibrate VS sprite + vs_phase = (@timer * 30).to_i % 3 + @vs_main_sprite.x = (Graphics.width / 2) + [0, 4, 0][vs_phase] + @vs_main_sprite.y = (Graphics.height / 2) + [0, 0, -4][vs_phase] + # VS sprites appearing + if @timer >= @vs_appear_final + @vs_1_sprite.visible = false + elsif @timer >= @vs_appear_start_2 + # Temp VS sprites enlarge and shrink again + if @vs_2_sprite.visible + @vs_2_sprite.zoom_x = 1.6 - 0.8 * (@timer - @vs_appear_start_2) / @vs_shrink_time + @vs_2_sprite.zoom_y = @vs_2_sprite.zoom_x + if @vs_2_sprite.zoom_x <= 1.2 + @vs_2_sprite.visible = false + @vs_main_sprite.visible = true + end + end + @vs_1_sprite.zoom_x = 2.0 - 0.8 * (@timer - @vs_appear_start_2) / @vs_shrink_time + @vs_1_sprite.zoom_y = @vs_1_sprite.zoom_x + elsif @timer >= @vs_appear_start + # Temp VS sprites appear and start shrinking + @vs_2_sprite.visible = true + @vs_2_sprite.zoom_x = 2.0 - 0.8 * (@timer - @vs_appear_start) / @vs_shrink_time + @vs_2_sprite.zoom_y = @vs_2_sprite.zoom_x + if @vs_1_sprite.visible || @vs_2_sprite.zoom_x <= 1.6 # Halfway between 2.0 and 1.2 + @vs_1_sprite.visible = true + @vs_1_sprite.zoom_x = 2.0 - 0.8 * (@timer - @vs_appear_start - (@vs_shrink_time / 2)) / @vs_shrink_time + @vs_1_sprite.zoom_y = @vs_1_sprite.zoom_x + end end - @width = @buffer.width - @height = @buffer.height - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @rearsprite = Sprite.new(@viewport) - @rearsprite.z = 1 - @rearsprite.zoom_y = 2.0 - @rearsprite.bitmap = @blackbitmap - @bgsprites = [] - rect = Rect.new(0,0,@width,2) - for i in 0...@height/2 - @bgsprites[i] = Sprite.new(@viewport) - @bgsprites[i].y = i*2 - @bgsprites[i].z = 2 - @bgsprites[i].bitmap = @buffer - rect.y = i*2 - @bgsprites[i].src_rect = rect - end - @ballsprite = Sprite.new(@viewport) - @ballsprite.x = @width/2 - @ballsprite.y = @height/2 - @ballsprite.z = 3 - @ballsprite.ox = @ballbitmap.width/2 - @ballsprite.oy = @ballbitmap.height/2 - @ballsprite.visible = false - @ballsprite.bitmap = @ballbitmap - @blacksprite = Sprite.new(@viewport) - @blacksprite.x = @width/2 - @blacksprite.y = @height/2 - @blacksprite.z = 4 - @blacksprite.ox = @blackbitmap.width/2 - @blacksprite.oy = @blackbitmap.height/2 - @blacksprite.visible = false - @blacksprite.bitmap = @blackbitmap - @angmult = 4/(@numframes/50.0) - end - - def disposed?; @disposed; end - - def dispose - return if disposed? - @buffer.dispose if @buffer - @buffer = nil - @blackbitmap.dispose if @blackbitmap - @blackbitmap = nil - @ballbitmap.dispose if @ballbitmap - @ballbitmap = nil - @rearsprite.dispose if @rearsprite - for i in @bgsprites; i.dispose if i; end - @bgsprites.clear - @ballsprite.dispose if @ballsprite - @blacksprite.dispose if @blacksprite - @viewport.dispose if @viewport - @disposed = true - end - - def update - return if disposed? - if @duration==0 - dispose - else - angadd = (@numframes-@duration)*@angmult - amp = 24*angadd/16; amp = 24 if amp>24 - for i in 0...@bgsprites.length - @bgsprites[i].x = amp*Math.sin((i+angadd)*Math::PI/48)*((i%2)*2-1) + # Flash white (two flashes) + if @timer >= @flash_start + @flash_duration + @flash_viewport.color.alpha = 0 + elsif @timer >= @flash_start + # Flash the screen white (coming from white lasts twice as long as going to white) + proportion = (@timer - @flash_start) / @flash_duration + if proportion >= 0.33 # Coming from white + @flash_viewport.color.alpha = 320 * 3 * (1 - proportion) / 2 + @player_sprite.color.alpha = 0 + @foe_sprite.color.alpha = 0 + else # Going to white + @flash_viewport.color.alpha = 320 * 3 * proportion end - @ballsprite.visible = true - if @duration>=@numframes*0.6 - @ballsprite.opacity = 255*(@numframes-@duration)/(@numframes*0.4) - @ballsprite.angle = -360.0*(@numframes-@duration)/(@numframes*0.4) - elsif @duration<@numframes*0.5 - @blacksprite.visible = true - @blacksprite.zoom_x = (@numframes*0.5-@duration)/(@numframes*0.5) - @blacksprite.zoom_y = 2*(@numframes*0.5-@duration)/(@numframes*0.5) + elsif @timer >= @flash_1_start + @flash_1_duration + @flash_viewport.color.alpha = 0 + elsif @timer >= @flash_1_start + # Flash the screen white + proportion = (@timer - @flash_1_start) / @flash_1_duration + if proportion >= 0.5 # Coming from white + @flash_viewport.color.alpha = 320 * 2 * (1 - proportion) + @rear_black_sprite.visible = true + else # Going to white + @flash_viewport.color.alpha = 320 * 2 * proportion end end - @duration -= 1 + # Fade to white at end + if @timer >= @fade_to_black_start + # Fade to black + @black_sprite.visible = true + proportion = (@timer - @fade_to_black_start) / (@duration - @fade_to_black_start) + @flash_viewport.color.alpha = 255 * (1 - proportion) + elsif @timer >= @fade_to_white_start + @text_sprite.visible = false + # Slowly fade to white + proportion = (@timer - @fade_to_white_start) / (@fade_to_white_end - @fade_to_white_start) + @flash_viewport.color.alpha = 255 * proportion + # Move bars and trainer sprites off-screen + dist = BAR_Y_INDENT + BAR_HEIGHT + @player_bar_sprite.x = @player_bar_x - dist * proportion + @player_bar_sprite.y = @player_bar_y - dist * proportion + @player_sprite.x = @player_bar_sprite.x + TRAINER_X_OFFSET + @player_sprite.y = @player_bar_sprite.y + BAR_HEIGHT - TRAINER_Y_OFFSET + @foe_bar_sprite.x = @foe_bar_x + dist * proportion + @foe_bar_sprite.y = @foe_bar_y + dist * proportion + @foe_sprite.x = @foe_bar_sprite.x + (@bar_bitmap.width / 2) - TRAINER_X_OFFSET + @foe_sprite.y = @foe_bar_sprite.y + @foe_bitmap.height - TRAINER_Y_OFFSET + end end end #============================================================================= - # HGSS double trainers + # HGSS Rocket Grunt trainer(s) #============================================================================= - class FourBallBurst - def initialize(numframes) - @numframes = numframes - @duration = numframes - @disposed = false - if @numframes<=0 - @disposed = true - return - end - @black1bitmap = RPG::Cache.transition("black_wedge_1") - @black2bitmap = RPG::Cache.transition("black_wedge_2") - @black3bitmap = RPG::Cache.transition("black_wedge_3") - @black4bitmap = RPG::Cache.transition("black_wedge_4") - @ballbitmap = RPG::Cache.transition("ball_small") - if !@black1bitmap || !@black2bitmap || !@black3bitmap || !@black4bitmap || !@ballbitmap - @disposed = true - return + class RocketGrunt < Transition_Base + DURATION = 1.6 + ROCKET_X = [ 1.5, -0.5, -0.5, 0.75, 1.5, -0.5] # * Graphics.width + ROCKET_Y = [-0.5, 1.0, -0.5, 1.5, 0.5, 0.75] # * Graphics.height + ROCKET_ANGLE = [ 1, 0.5, -1.5, -1, -1.5, 0.5] # * 360 * sprite.zoom_x + + def initialize_bitmaps + @black_1_bitmap = RPG::Cache.transition("black_wedge_1") + @black_2_bitmap = RPG::Cache.transition("black_wedge_2") + @black_3_bitmap = RPG::Cache.transition("black_wedge_3") + @black_4_bitmap = RPG::Cache.transition("black_wedge_4") + @rocket_bitmap = RPG::Cache.transition("rocket_logo") + dispose if !@black_1_bitmap || !@black_2_bitmap || !@black_3_bitmap || + !@black_4_bitmap || !@rocket_bitmap + end + + def initialize_sprites + # Rocket sprites + @rocket_sprites = [] + ROCKET_X.length.times do |i| + @rocket_sprites[i] = new_sprite( + ROCKET_X[i] * Graphics.width, ROCKET_Y[i] * Graphics.height, + @rocket_bitmap, @rocket_bitmap.width / 2, @rocket_bitmap.height / 2 + ) + end + # Black wedges + 4.times do |i| + b = [@black_1_bitmap, @black_2_bitmap, @black_3_bitmap, @black_4_bitmap][i] + @sprites[i] = new_sprite((i == 1) ? 0 : Graphics.width / 2, (i == 2) ? 0 : Graphics.height / 2, b, + (i.even?) ? b.width / 2 : 0, (i.even?) ? 0 : b.height / 2) + @sprites[i].zoom_x = 0.0 if i.even? + @sprites[i].zoom_y = 0.0 if i.odd? + @sprites[i].visible = false + end + end + + def set_up_timings + @rocket_appear_end = @duration * 0.75 + @rocket_appear_delay = 1.0 / (ROCKET_X.length + 1) + @rocket_appear_time = @rocket_appear_delay * 2 # 2 logos on screen at once + end + + def dispose_all + # Dispose sprites + @rocket_sprites.each { |s| s&.dispose } + @rocket_sprites.clear + # Dispose bitmaps + @black_1_bitmap&.dispose + @black_2_bitmap&.dispose + @black_3_bitmap&.dispose + @black_4_bitmap&.dispose + @rocket_bitmap&.dispose + end + + def update_anim + if @timer <= @rocket_appear_end + # Rocket logos fly in from edges of screen + proportion = @timer / @rocket_appear_end + @rocket_sprites.each_with_index do |sprite, i| + next if !sprite.visible + start_time = i * @rocket_appear_delay + next if proportion < start_time + single_proportion = (proportion - start_time) / @rocket_appear_time + sqrt_single_proportion = Math.sqrt(single_proportion) + sprite.x = (ROCKET_X[i] + (0.5 - ROCKET_X[i]) * sqrt_single_proportion) * Graphics.width + sprite.y = (ROCKET_Y[i] + (0.5 - ROCKET_Y[i]) * sqrt_single_proportion) * Graphics.height + sprite.zoom_x = 2.5 * (1 - single_proportion) + sprite.zoom_y = sprite.zoom_x + sprite.angle = sprite.zoom_x * ROCKET_ANGLE[i] * 360 + sprite.visible = false if sprite.zoom_x <= 0 + end + else + @rocket_sprites.last.visible = false + # Black wedges expand to fill screen + proportion = (@timer - @rocket_appear_end) / (@duration - @rocket_appear_end) + @sprites.each_with_index do |sprite, i| + sprite.visible = true + sprite.zoom_x = proportion if i.even? + sprite.zoom_y = proportion if i.odd? + end end - @width = Graphics.width - @height = Graphics.height - @viewport = Viewport.new(0,0,@width,@height) - @viewport.z = 99999 - @ballsprites = [] - for i in 0...4 - @ballsprites[i] = Sprite.new(@viewport) - @ballsprites[i].x = @width/2 - @ballsprites[i].y = @height/2 - @ballsprites[i].z = [2,1,3,0][i] - @ballsprites[i].ox = @ballbitmap.width/2 - @ballsprites[i].oy = @ballbitmap.height/2 - @ballsprites[i].bitmap = @ballbitmap - end - @blacksprites = [] - for i in 0...4 - b = [@black1bitmap,@black2bitmap,@black3bitmap,@black4bitmap][i] - @blacksprites[i] = Sprite.new(@viewport) - @blacksprites[i].x = (i==1) ? 0 : @width/2 - @blacksprites[i].y = (i==2) ? 0 : @height/2 - @blacksprites[i].ox = (i%2==0) ? b.width/2 : 0 - @blacksprites[i].oy = (i%2==0) ? 0 : b.height/2 - @blacksprites[i].zoom_x = (i%2==0) ? 0.0 : 1.0 - @blacksprites[i].zoom_y = (i%2==0) ? 1.0 : 0.0 - @blacksprites[i].visible = false - @blacksprites[i].bitmap = b - end - @addxball = (@width/2+@ballbitmap.width/2)/(@numframes*0.4) - @addyball = (@height/2+@ballbitmap.height/2)/(@numframes*0.4) - @addzoom = 1.0/(@numframes*0.6) - end - - def disposed?; @disposed; end - - def dispose - return if disposed? - @black1bitmap.dispose if @black1bitmap - @black1bitmap = nil - @black2bitmap.dispose if @black2bitmap - @black2bitmap = nil - @black3bitmap.dispose if @black3bitmap - @black3bitmap = nil - @black4bitmap.dispose if @black4bitmap - @black4bitmap = nil - @ballbitmap.dispose if @ballbitmap - @ballbitmap = nil - for i in @ballsprites; i.dispose if i; end - @ballsprites.clear - for i in @blacksprites; i.dispose if i; end - @blacksprites.clear - @viewport.dispose if @viewport - @disposed = true end + end - def update - return if disposed? - if @duration==0 - dispose - elsif @duration>=@numframes*0.6 - for i in 0...@ballsprites.length - @ballsprites[i].x += (i==1) ? @addxball : (i==3) ? -@addxball : 0 - @ballsprites[i].y += (i==0) ? @addyball : (i==2) ? -@addyball : 0 + #============================================================================= + # HGSS VS Team Rocket Admin animation + # Uses $game_temp.transition_animation_data, and expects it to be an array + # like so: [:TRAINERTYPE, "display name"] + # Bar graphics are named hgss_vsBar_TRAINERTYPE.png. + # Trainer sprites are named hgss_vs_TRAINERTYPE.png. + #============================================================================= + class VSRocketAdmin < Transition_Base + DURATION = 3.1 + STROBE_SCROLL_SPEED = 1440 + FOE_SPRITE_Y = 318 + + def initialize_bitmaps + @strobes_bitmap = RPG::Cache.transition("rocket_strobes") + @bg_1_bitmap = RPG::Cache.transition("rocket_bg_1") + @bg_2_bitmap = RPG::Cache.transition("rocket_bg_2") + @foe_bitmap = RPG::Cache.transition("rocket_#{$game_temp.transition_animation_data[0]}") + @black_bitmap = RPG::Cache.transition("black_half") + dispose if !@strobes_bitmap || !@bg_1_bitmap || !@bg_2_bitmap || !@foe_bitmap || !@black_bitmap + end + + def initialize_sprites + @flash_viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) + @flash_viewport.z = 99999 + @flash_viewport.color = Color.new(255, 255, 255, 0) + # Strobe sprites (need 2 of them to make them loop around) + ((Graphics.width.to_f / @strobes_bitmap.width).ceil + 1).times do |i| + spr = new_sprite(@strobes_bitmap.width * i, 0, @strobes_bitmap) + spr.z = 1 + spr.opacity = 0 + @sprites.push(spr) + end + # First bg sprite + @bg_1_sprite = new_sprite(0, Graphics.height / 2, @bg_1_bitmap) + @bg_1_sprite.z = 3 + @bg_1_sprite.src_rect.height = 0 + # Second bg sprite + @bg_2_sprite = new_sprite(0, 0, @bg_2_bitmap) + @bg_2_sprite.z = 5 + @bg_2_sprite.opacity = 0 + # Foe sprite + @foe_sprite = new_sprite(Graphics.width + @foe_bitmap.width, FOE_SPRITE_Y, + @foe_bitmap, @foe_bitmap.width / 2, @foe_bitmap.height) + @foe_sprite.z = 7 + # Sprite with foe's name written in it + @text_sprite = BitmapSprite.new(Graphics.width, Graphics.height - FOE_SPRITE_Y, @viewport) + @text_sprite.y = FOE_SPRITE_Y + @text_sprite.z = 8 + @text_sprite.visible = false + pbSetSystemFont(@text_sprite.bitmap) + pbDrawTextPositions(@text_sprite.bitmap, + [[$game_temp.transition_animation_data[1], 272, 8, 0, + Color.new(248, 248, 248), Color.new(72, 80, 80)]]) + # Foreground black + @black_sprite = new_sprite(0, 0, @black_bitmap) + @black_sprite.z = 10 + @black_sprite.zoom_y = 2.0 + @black_sprite.visible = false + end + + def set_up_timings + @strobes_x = 0 + @strobe_appear_end = 0.15 # Starts appearing at 0.0 + # White flash between these two times + @bg_1_appear_start = 0.5 + @bg_1_appear_end = 0.65 + @bg_2_appear_start = 1.0 # Also when foe sprite/name start appearing + @bg_2_appear_end = 1.1 # Also when foe sprite/name end appearing + @flash_end = 1.35 # Starts at @bg_2_appear_end + @foe_disappear_start = 2.1 + @foe_disappear_end = 2.3 # Also when screen starts turning white + @fade_to_white_end = 2.5 + @fade_to_black_start = 2.9 + end + + def dispose_all + # Dispose sprites + @bg_1_sprite&.dispose + @bg_2_sprite&.dispose + @foe_sprite&.dispose + @text_sprite&.dispose + @black_sprite&.dispose + # Dispose bitmaps + @strobes_bitmap&.dispose + @bg_1_bitmap&.dispose + @bg_2_bitmap&.dispose + @foe_bitmap&.dispose + @black_bitmap&.dispose + # Dispose viewport + @flash_viewport&.dispose + end + + def update_anim + # Strobes scrolling + if @sprites[0].visible + @strobes_x -= Graphics.delta_s * STROBE_SCROLL_SPEED + @strobes_x += @strobes_bitmap.width if @strobes_x <= -@strobes_bitmap.width + @sprites.each_with_index { |spr, i| spr.x = @strobes_x + (i * @strobes_bitmap.width) } + end + if @timer >= @fade_to_black_start + # Fade to black + proportion = (@timer - @fade_to_black_start) / (@duration - @fade_to_black_start) + @flash_viewport.color.alpha = 255 * (1 - proportion) + elsif @timer >= @fade_to_white_end + @flash_viewport.color.alpha = 255 # Ensure screen is white + @black_sprite.visible = true # Make black overlay visible + elsif @timer >= @foe_disappear_end + @foe_sprite.visible = false # Ensure foe sprite has vanished + @text_sprite.visible = false # Ensure name sprite has vanished + # Slowly fade to white + proportion = (@timer - @foe_disappear_end) / (@fade_to_white_end - @foe_disappear_end) + @flash_viewport.color.alpha = 255 * proportion + elsif @timer >= @foe_disappear_start + # Slide foe sprite/name off-screen + proportion = (@timer - @foe_disappear_start) / (@foe_disappear_end - @foe_disappear_start) + start_x = Graphics.width / 2 + @foe_sprite.x = start_x - (@foe_bitmap.width + start_x) * proportion * proportion + @text_sprite.x = @foe_sprite.x - Graphics.width / 2 + elsif @timer >= @flash_end + @flash_viewport.color.alpha = 0 # Ensure flash has ended + elsif @timer >= @bg_2_appear_end + @bg_2_sprite.opacity = 255 # Ensure BG 2 sprite is fully opaque + @foe_sprite.x = Graphics.width / 2 # Ensure foe sprite is in the right place + @text_sprite.x = 0 # Ensure name sprite is in the right place + # Flash screen + proportion = (@timer - @bg_2_appear_end) / (@flash_end - @bg_2_appear_end) + @flash_viewport.color.alpha = 320 * (1 - proportion) + elsif @timer >= @bg_2_appear_start + # BG 2 sprite appears + proportion = (@timer - @bg_2_appear_start) / (@bg_2_appear_end - @bg_2_appear_start) + @bg_2_sprite.opacity = 255 * proportion + # Foe sprite/name appear + start_x = Graphics.width + (@foe_bitmap.width / 2) + @foe_sprite.x = start_x + ((Graphics.width / 2) - start_x) * Math.sqrt(proportion) + @text_sprite.x = @foe_sprite.x - Graphics.width / 2 + @text_sprite.visible = true + elsif @timer >= @bg_1_appear_end + @bg_1_sprite.oy = Graphics.height / 2 + @bg_1_sprite.src_rect.y = 0 + @bg_1_sprite.src_rect.height = @bg_1_bitmap.height + @sprites.each { |sprite| sprite.visible = false } # Hide strobes + elsif @timer >= @bg_1_appear_start + @flash_viewport.color.alpha = 0 # Ensure flash has ended + # BG 1 sprite appears + proportion = (@timer - @bg_1_appear_start) / (@bg_1_appear_end - @bg_1_appear_start) + half_height = ((proportion * @bg_1_bitmap.height) / 2).to_i + @bg_1_sprite.src_rect.height = half_height * 2 + @bg_1_sprite.src_rect.y = (@bg_1_bitmap.height / 2) - half_height + @bg_1_sprite.oy = half_height + elsif @timer >= @strobe_appear_end + @sprites.each { |sprite| sprite.opacity = 255 } # Ensure strobes are fully opaque + # Flash the screen white + proportion = (@timer - @strobe_appear_end) / (@bg_1_appear_start - @strobe_appear_end) + if proportion >= 0.5 + @flash_viewport.color.alpha = 320 * 2 * (1 - proportion) + else + @flash_viewport.color.alpha = 320 * proportion end else - for i in 0...@blacksprites.length - @blacksprites[i].visible = true - @blacksprites[i].zoom_x += (i%2==0) ? @addzoom : 0 - @blacksprites[i].zoom_y += (i%2==0) ? 0 : @addzoom + # Strobes fade in + @sprites.each do |sprite| + sprite.opacity = 255 * (@timer / @strobe_appear_end) end end - @duration -= 1 end end end diff --git a/Data/Scripts/009_Scenes/002_EventScene.rb b/Data/Scripts/009_Scenes/002_EventScene.rb index 94e9de3e1d..519466ca3d 100644 --- a/Data/Scripts/009_Scenes/002_EventScene.rb +++ b/Data/Scripts/009_Scenes/002_EventScene.rb @@ -10,7 +10,7 @@ def initialize(viewport, picture) end def dispose - @pictureBitmap.dispose if @pictureBitmap + @pictureBitmap&.dispose super end @@ -22,23 +22,23 @@ def setCustomBitmap(bitmap) def update super - @pictureBitmap.update if @pictureBitmap + @pictureBitmap&.update # 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 if @pictureBitmap + @pictureBitmap&.dispose @pictureBitmap = nil self.visible = false return end # Get picture graphic - @pictureBitmap.dispose if @pictureBitmap + @pictureBitmap&.dispose @pictureBitmap = AnimatedBitmap.new(@picture_name, @hue) self.bitmap = (@pictureBitmap) ? @pictureBitmap.bitmap : nil elsif @picture_name == "" @@ -46,16 +46,16 @@ def update 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,0,maxwidth,text,Color.new(248,248,248),Color.new(168,184,184)) + drawFormattedTextEx(tmp, 0, 4, maxwidth, text, Color.new(248, 248, 248), Color.new(168, 184, 184)) return tmp end @@ -65,9 +65,9 @@ def pbTextBitmap(text, maxwidth=Graphics.width) # 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 @@ -80,10 +80,10 @@ def initialize(viewport=nil) def dispose return if disposed? - for sprite in @picturesprites + @picturesprites.each do |sprite| sprite.dispose end - for sprite in @usersprites + @usersprites.each do |sprite| sprite.dispose end @onCTrigger.clear @@ -105,26 +105,26 @@ def addBitmap(x, y, bitmap) # 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 @@ -140,10 +140,10 @@ def wait(frames) frames.times { update } end - def pictureWait(extraframes=0) + def pictureWait(extraframes = 0) loop do hasRunning = false - for pic in @pictures + @pictures.each do |pic| hasRunning = true if pic.running? end break if !hasRunning @@ -156,13 +156,13 @@ def update return if disposed? Graphics.update Input.update - for picture in @pictures + @pictures.each do |picture| picture.update end - for sprite in @picturesprites + @picturesprites.each do |sprite| sprite.update end - for sprite in @usersprites + @usersprites.each do |sprite| next if !sprite || sprite.disposed? || !sprite.is_a?(Sprite) sprite.update end @@ -175,7 +175,7 @@ def update end def main - while !disposed? + until disposed? update end end @@ -188,7 +188,7 @@ def main #=============================================================================== def pbEventScreen(cls) pbFadeOutIn { - viewport = Viewport.new(0,0,Graphics.width,Graphics.height) + viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) viewport.z = 99999 PBDebug.logonerr { cls.new(viewport).main diff --git a/Data/Scripts/010_Data/001_GameData.rb b/Data/Scripts/010_Data/001_GameData.rb index 19e3e62e1c..97daec1ab1 100644 --- a/Data/Scripts/010_Data/001_GameData.rb +++ b/Data/Scripts/010_Data/001_GameData.rb @@ -26,9 +26,6 @@ def get(other) 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 @@ -40,9 +37,6 @@ def try_get(other) 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 @@ -54,8 +48,12 @@ def keys # Yields all data in order of their id_number. def each - 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) } + 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 end def load @@ -114,12 +112,21 @@ def keys return self::DATA.keys end - # Yields all data in alphabetical order. + # 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 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 @@ -177,6 +184,10 @@ def each 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 @@ -196,13 +207,14 @@ module InstanceMethods # @return [Boolean] whether other represents the same thing as this thing def ==(other) return false if other.nil? - if other.is_a?(Symbol) + case other + when Symbol return @id == other - elsif other.is_a?(self.class) + when self.class return @id == other.id - elsif other.is_a?(String) - return @id_number == other.to_sym - elsif other.is_a?(Integer) + when String + return @id == other.to_sym + when Integer return @id_number == other end return false @@ -219,11 +231,14 @@ def self.load_all Item.load BerryPlant.load Species.load + SpeciesMetrics.load + ShadowPokemon.load Ribbon.load Encounter.load TrainerType.load Trainer.load Metadata.load + PlayerMetadata.load MapMetadata.load end end diff --git a/Data/Scripts/010_Data/001_Hardcoded data/001_GrowthRate.rb b/Data/Scripts/010_Data/001_Hardcoded data/001_GrowthRate.rb index 9df096a616..6fa9c71a07 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/001_GrowthRate.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/001_GrowthRate.rb @@ -64,7 +64,7 @@ def level_from_exp(exp) return ArgumentError.new("Exp amount #{level} is invalid.") if !exp || exp < 0 max = GrowthRate.max_level return max if exp >= maximum_exp - for level in 1..max + (1..max).each do |level| return level - 1 if exp < minimum_exp_for_level(level) end return max @@ -78,17 +78,17 @@ def level_from_exp(exp) :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): @@ -101,17 +101,17 @@ def level_from_exp(exp) :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) * 3 / 500 } + 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): @@ -122,19 +122,19 @@ def level_from_exp(exp) :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| - rate = [82 - (level - 100) / 2.0, 40].max - next (level ** 4) * rate / 5000 + rate = [82 - ((level - 100) / 2.0), 40].max + next (level**4) * rate / 5000 } }) @@ -142,49 +142,49 @@ def level_from_exp(exp) :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 } }) diff --git a/Data/Scripts/010_Data/001_Hardcoded data/002_GenderRatio.rb b/Data/Scripts/010_Data/001_Hardcoded data/002_GenderRatio.rb index b2d75ff509..efde3e0a04 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/002_GenderRatio.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/002_GenderRatio.rb @@ -26,6 +26,12 @@ def initialize(hash) 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 diff --git a/Data/Scripts/010_Data/001_Hardcoded data/004_BodyShape.rb b/Data/Scripts/010_Data/001_Hardcoded data/004_BodyShape.rb index ff3b4465fb..8d5a7b5b0f 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/004_BodyShape.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/004_BodyShape.rb @@ -1,26 +1,25 @@ -# 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. +# NOTE: The order these shapes are registered are the order they are listed in +# the Pokédex search screen. # "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 ClassMethods + extend ClassMethodsSymbols include InstanceMethods def self.load; end def self.save; end def initialize(hash) - @id = hash[:id] - @id_number = hash[:id_number] || -1 - @real_name = hash[:name] || "Unnamed" + @id = hash[:id] + @real_name = hash[:name] || "Unnamed" + @icon_position = hash[:icon_position] || 0 end # @return [String] the translated name of this body shape @@ -33,85 +32,85 @@ def name #=============================================================================== GameData::BodyShape.register({ - :id => :Head, - :id_number => 1, - :name => _INTL("Head") + :id => :Head, + :name => _INTL("Head"), + :icon_position => 0 }) GameData::BodyShape.register({ - :id => :Serpentine, - :id_number => 2, - :name => _INTL("Serpentine") + :id => :Serpentine, + :name => _INTL("Serpentine"), + :icon_position => 1 }) GameData::BodyShape.register({ - :id => :Finned, - :id_number => 3, - :name => _INTL("Finned") + :id => :Finned, + :name => _INTL("Finned"), + :icon_position => 2 }) GameData::BodyShape.register({ - :id => :HeadArms, - :id_number => 4, - :name => _INTL("Head and arms") + :id => :HeadArms, + :name => _INTL("Head and arms"), + :icon_position => 3 }) GameData::BodyShape.register({ - :id => :HeadBase, - :id_number => 5, - :name => _INTL("Head and base") + :id => :HeadBase, + :name => _INTL("Head and base"), + :icon_position => 4 }) GameData::BodyShape.register({ - :id => :BipedalTail, - :id_number => 6, - :name => _INTL("Bipedal with tail") + :id => :BipedalTail, + :name => _INTL("Bipedal with tail"), + :icon_position => 5 }) GameData::BodyShape.register({ - :id => :HeadLegs, - :id_number => 7, - :name => _INTL("Head and legs") + :id => :HeadLegs, + :name => _INTL("Head and legs"), + :icon_position => 6 }) GameData::BodyShape.register({ - :id => :Quadruped, - :id_number => 8, - :name => _INTL("Quadruped") + :id => :Quadruped, + :name => _INTL("Quadruped"), + :icon_position => 7 }) GameData::BodyShape.register({ - :id => :Winged, - :id_number => 9, - :name => _INTL("Winged") + :id => :Winged, + :name => _INTL("Winged"), + :icon_position => 8 }) GameData::BodyShape.register({ - :id => :Multiped, - :id_number => 10, - :name => _INTL("Multiped") + :id => :Multiped, + :name => _INTL("Multiped"), + :icon_position => 9 }) GameData::BodyShape.register({ - :id => :MultiBody, - :id_number => 11, - :name => _INTL("Multi Body") + :id => :MultiBody, + :name => _INTL("Multi Body"), + :icon_position => 10 }) GameData::BodyShape.register({ - :id => :Bipedal, - :id_number => 12, - :name => _INTL("Bipedal") + :id => :Bipedal, + :name => _INTL("Bipedal"), + :icon_position => 11 }) GameData::BodyShape.register({ - :id => :MultiWinged, - :id_number => 13, - :name => _INTL("Multi Winged") + :id => :MultiWinged, + :name => _INTL("Multi Winged"), + :icon_position => 12 }) GameData::BodyShape.register({ - :id => :Insectoid, - :id_number => 14, - :name => _INTL("Insectoid") + :id => :Insectoid, + :name => _INTL("Insectoid"), + :icon_position => 13 }) diff --git a/Data/Scripts/010_Data/001_Hardcoded data/005_BodyColor.rb b/Data/Scripts/010_Data/001_Hardcoded data/005_BodyColor.rb index 542c723be4..f0dcbd48e8 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/005_BodyColor.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/005_BodyColor.rb @@ -1,14 +1,13 @@ -# NOTE: The id_number is only used to determine the order that body colors are -# listed in the Pokédex search screen. +# NOTE: The order these colors are registered are the order they 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 ClassMethods + extend ClassMethodsSymbols include InstanceMethods def self.load; end @@ -16,8 +15,7 @@ def self.save; end def initialize(hash) @id = hash[:id] - @id_number = hash[:id_number] || -1 - @real_name = hash[:name] || "Unnamed" + @real_name = hash[:name] || "Unnamed" end # @return [String] the translated name of this body color @@ -30,61 +28,51 @@ def name #=============================================================================== GameData::BodyColor.register({ - :id => :Red, - :id_number => 0, - :name => _INTL("Red") + :id => :Red, + :name => _INTL("Red") }) GameData::BodyColor.register({ - :id => :Blue, - :id_number => 1, - :name => _INTL("Blue") + :id => :Blue, + :name => _INTL("Blue") }) GameData::BodyColor.register({ - :id => :Yellow, - :id_number => 2, - :name => _INTL("Yellow") + :id => :Yellow, + :name => _INTL("Yellow") }) GameData::BodyColor.register({ - :id => :Green, - :id_number => 3, - :name => _INTL("Green") + :id => :Green, + :name => _INTL("Green") }) GameData::BodyColor.register({ - :id => :Black, - :id_number => 4, - :name => _INTL("Black") + :id => :Black, + :name => _INTL("Black") }) GameData::BodyColor.register({ - :id => :Brown, - :id_number => 5, - :name => _INTL("Brown") + :id => :Brown, + :name => _INTL("Brown") }) GameData::BodyColor.register({ - :id => :Purple, - :id_number => 6, - :name => _INTL("Purple") + :id => :Purple, + :name => _INTL("Purple") }) GameData::BodyColor.register({ - :id => :Gray, - :id_number => 7, - :name => _INTL("Gray") + :id => :Gray, + :name => _INTL("Gray") }) GameData::BodyColor.register({ - :id => :White, - :id_number => 8, - :name => _INTL("White") + :id => :White, + :name => _INTL("White") }) GameData::BodyColor.register({ - :id => :Pink, - :id_number => 9, - :name => _INTL("Pink") + :id => :Pink, + :name => _INTL("Pink") }) diff --git a/Data/Scripts/010_Data/001_Hardcoded data/007_Evolution.rb b/Data/Scripts/010_Data/001_Hardcoded data/007_Evolution.rb index cb908da6d1..054787621e 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/007_Evolution.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/007_Evolution.rb @@ -7,6 +7,8 @@ class Evolution attr_reader :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 = {} @@ -25,6 +27,8 @@ def initialize(hash) @level_up_proc = hash[: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 @@ -40,8 +44,16 @@ def call_on_trade(*args) 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) if @after_evolution_proc + @after_evolution_proc&.call(*args) end end end @@ -188,8 +200,7 @@ def call_after_evolution(*args) :id => :LevelDarkness, :parameter => Integer, :level_up_proc => proc { |pkmn, parameter| - map_metadata = GameData::MapMetadata.try_get($game_map.map_id) - next pkmn.level >= parameter && map_metadata && map_metadata.dark_map + next pkmn.level >= parameter && $game_map.metadata&.dark_map } }) @@ -197,7 +208,7 @@ def call_after_evolution(*args) :id => :LevelDarkInParty, :parameter => Integer, :level_up_proc => proc { |pkmn, parameter| - next pkmn.level >= parameter && $Trainer.has_pokemon_of_type?(:DARK) + next pkmn.level >= parameter && $player.has_pokemon_of_type?(:DARK) } }) @@ -252,14 +263,11 @@ def call_after_evolution(*args) 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 $Trainer.party_full? - next false if !$PokemonBag.pbHasItem?(:POKEBALL) + next false if $player.party_full? + next false if !$bag.has?(:POKEBALL) PokemonEvolutionScene.pbDuplicatePokemon(pkmn, new_species) - $PokemonBag.pbDeleteItem(:POKEBALL) + $bag.remove(:POKEBALL) next true } }) @@ -268,7 +276,7 @@ def call_after_evolution(*args) :id => :Happiness, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - next pkmn.happiness >= 220 + next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) } }) @@ -276,7 +284,7 @@ def call_after_evolution(*args) :id => :HappinessMale, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - next pkmn.happiness >= 220 && pkmn.male? + next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && pkmn.male? } }) @@ -284,7 +292,7 @@ def call_after_evolution(*args) :id => :HappinessFemale, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - next pkmn.happiness >= 220 && pkmn.female? + next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && pkmn.female? } }) @@ -292,7 +300,7 @@ def call_after_evolution(*args) :id => :HappinessDay, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - next pkmn.happiness >= 220 && PBDayNight.isDay? + next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && PBDayNight.isDay? } }) @@ -300,7 +308,7 @@ def call_after_evolution(*args) :id => :HappinessNight, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - next pkmn.happiness >= 220 && PBDayNight.isNight? + next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && PBDayNight.isNight? } }) @@ -309,7 +317,7 @@ def call_after_evolution(*args) :parameter => :Move, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - if pkmn.happiness >= 220 + if pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) next pkmn.moves.any? { |m| m && m.id == parameter } end } @@ -320,7 +328,7 @@ def call_after_evolution(*args) :parameter => :Type, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - if pkmn.happiness >= 220 + if pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) next pkmn.moves.any? { |m| m && m.type == parameter } end } @@ -331,7 +339,7 @@ def call_after_evolution(*args) :parameter => :Item, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - next pkmn.item == parameter && pkmn.happiness >= 220 + next pkmn.item == parameter && pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) }, :after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species| next false if evo_species != new_species || !pkmn.hasItem?(parameter) @@ -432,7 +440,7 @@ def call_after_evolution(*args) :parameter => :Item, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - next pkmn.item == parameter && pkmn.happiness >= 220 + next pkmn.item == parameter && pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) }, :after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species| next false if evo_species != new_species || !pkmn.hasItem?(parameter) @@ -464,7 +472,7 @@ def call_after_evolution(*args) :parameter => :Species, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - next $Trainer.has_species?(parameter) + next $player.has_species?(parameter) } }) @@ -477,14 +485,22 @@ def call_after_evolution(*args) } }) +GameData::Evolution.register({ + :id => :LocationFlag, + :parameter => String, + :minimum_level => 1, # Needs any level up + :level_up_proc => proc { |pkmn, parameter| + next $game_map.metadata&.has_flag?(parameter) + } +}) + GameData::Evolution.register({ :id => :Region, :parameter => Integer, :minimum_level => 1, # Needs any level up :level_up_proc => proc { |pkmn, parameter| - 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 + map_metadata = $game_map.metadata + next map_metadata&.town_map_position && map_metadata.town_map_position[0] == parameter } }) @@ -535,7 +551,7 @@ def call_after_evolution(*args) :id => :ItemHappiness, :parameter => :Item, :use_item_proc => proc { |pkmn, parameter, item| - next item == parameter && pkmn.happiness >= 220 + next item == parameter && pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) } }) @@ -597,3 +613,62 @@ def call_after_evolution(*args) next pkmn.species == parameter && !other_pkmn.hasItem?(:EVERSTONE) } }) + +#=============================================================================== +# Evolution methods that are triggered after any battle +#=============================================================================== +GameData::Evolution.register({ + :id => :BattleDealCriticalHit, + :parameter => Integer, + :after_battle_proc => proc { |pkmn, party_index, parameter| + next $game_temp.party_critical_hits_dealt && + $game_temp.party_critical_hits_dealt[party_index] && + $game_temp.party_critical_hits_dealt[party_index] >= parameter + } +}) + +#=============================================================================== +# 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 => :EventAfterDamageTaken, + :parameter => Integer, + :after_battle_proc => proc { |pkmn, party_index, parameter| + if $game_temp.party_direct_damage_taken && + $game_temp.party_direct_damage_taken[party_index] && + $game_temp.party_direct_damage_taken[party_index] >= 49 + pkmn.ready_to_evolve = true + end + next false + }, + :event_proc => proc { |pkmn, parameter, value| + next value == parameter && pkmn.ready_to_evolve + } +}) diff --git a/Data/Scripts/010_Data/001_Hardcoded data/008_Stat.rb b/Data/Scripts/010_Data/001_Hardcoded data/008_Stat.rb index 07e6e4d80a..ae8b112b19 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/008_Stat.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/008_Stat.rb @@ -1,5 +1,3 @@ -# The id_number value determines which order the stats are iterated through by -# the "each" methods. # The pbs_order value determines the order in which the stats are written in # several PBS files, where base stats/IVs/EVs/EV yields are defined. Only stats # which are yielded by the "each_main" method can have stat numbers defined in @@ -8,7 +6,6 @@ module GameData class Stat attr_reader :id - attr_reader :id_number attr_reader :real_name attr_reader :real_name_brief attr_reader :type @@ -16,7 +13,7 @@ class Stat DATA = {} - extend ClassMethods + extend ClassMethodsSymbols include InstanceMethods def self.load; end @@ -39,7 +36,6 @@ def self.each_battle def initialize(hash) @id = hash[:id] - @id_number = hash[:id_number] || -1 @real_name = hash[:name] || "Unnamed" @real_name_brief = hash[:name_brief] || "None" @type = hash[:type] || :none @@ -62,7 +58,6 @@ def name_brief GameData::Stat.register({ :id => :HP, - :id_number => 0, :name => _INTL("HP"), :name_brief => _INTL("HP"), :type => :main, @@ -71,7 +66,6 @@ def name_brief GameData::Stat.register({ :id => :ATTACK, - :id_number => 1, :name => _INTL("Attack"), :name_brief => _INTL("Atk"), :type => :main_battle, @@ -80,7 +74,6 @@ def name_brief GameData::Stat.register({ :id => :DEFENSE, - :id_number => 2, :name => _INTL("Defense"), :name_brief => _INTL("Def"), :type => :main_battle, @@ -89,7 +82,6 @@ def name_brief GameData::Stat.register({ :id => :SPECIAL_ATTACK, - :id_number => 3, :name => _INTL("Special Attack"), :name_brief => _INTL("SpAtk"), :type => :main_battle, @@ -98,7 +90,6 @@ def name_brief GameData::Stat.register({ :id => :SPECIAL_DEFENSE, - :id_number => 4, :name => _INTL("Special Defense"), :name_brief => _INTL("SpDef"), :type => :main_battle, @@ -107,7 +98,6 @@ def name_brief GameData::Stat.register({ :id => :SPEED, - :id_number => 5, :name => _INTL("Speed"), :name_brief => _INTL("Spd"), :type => :main_battle, @@ -116,7 +106,6 @@ def name_brief GameData::Stat.register({ :id => :ACCURACY, - :id_number => 6, :name => _INTL("accuracy"), :name_brief => _INTL("Acc"), :type => :battle @@ -124,7 +113,6 @@ def name_brief GameData::Stat.register({ :id => :EVASION, - :id_number => 7, :name => _INTL("evasiveness"), :name_brief => _INTL("Eva"), :type => :battle diff --git a/Data/Scripts/010_Data/001_Hardcoded data/009_Nature.rb b/Data/Scripts/010_Data/001_Hardcoded data/009_Nature.rb index f745ef21d4..e48f6d2d04 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/009_Nature.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/009_Nature.rb @@ -1,13 +1,12 @@ module GameData class Nature attr_reader :id - attr_reader :id_number attr_reader :real_name attr_reader :stat_changes DATA = {} - extend ClassMethods + extend ClassMethodsSymbols include InstanceMethods def self.load; end @@ -15,7 +14,6 @@ def self.save; end def initialize(hash) @id = hash[:id] - @id_number = hash[:id_number] || -1 @real_name = hash[:name] || "Unnamed" @stat_changes = hash[:stat_changes] || [] end @@ -31,170 +29,145 @@ def name GameData::Nature.register({ :id => :HARDY, - :id_number => 0, :name => _INTL("Hardy") }) GameData::Nature.register({ :id => :LONELY, - :id_number => 1, :name => _INTL("Lonely"), :stat_changes => [[:ATTACK, 10], [:DEFENSE, -10]] }) GameData::Nature.register({ :id => :BRAVE, - :id_number => 2, :name => _INTL("Brave"), :stat_changes => [[:ATTACK, 10], [:SPEED, -10]] }) GameData::Nature.register({ :id => :ADAMANT, - :id_number => 3, :name => _INTL("Adamant"), :stat_changes => [[:ATTACK, 10], [:SPECIAL_ATTACK, -10]] }) GameData::Nature.register({ :id => :NAUGHTY, - :id_number => 4, :name => _INTL("Naughty"), :stat_changes => [[:ATTACK, 10], [:SPECIAL_DEFENSE, -10]] }) GameData::Nature.register({ :id => :BOLD, - :id_number => 5, :name => _INTL("Bold"), :stat_changes => [[:DEFENSE, 10], [:ATTACK, -10]] }) GameData::Nature.register({ :id => :DOCILE, - :id_number => 6, :name => _INTL("Docile") }) GameData::Nature.register({ :id => :RELAXED, - :id_number => 7, :name => _INTL("Relaxed"), :stat_changes => [[:DEFENSE, 10], [:SPEED, -10]] }) GameData::Nature.register({ :id => :IMPISH, - :id_number => 8, :name => _INTL("Impish"), :stat_changes => [[:DEFENSE, 10], [:SPECIAL_ATTACK, -10]] }) GameData::Nature.register({ :id => :LAX, - :id_number => 9, :name => _INTL("Lax"), :stat_changes => [[:DEFENSE, 10], [:SPECIAL_DEFENSE, -10]] }) GameData::Nature.register({ :id => :TIMID, - :id_number => 10, :name => _INTL("Timid"), :stat_changes => [[:SPEED, 10], [:ATTACK, -10]] }) GameData::Nature.register({ :id => :HASTY, - :id_number => 11, :name => _INTL("Hasty"), :stat_changes => [[:SPEED, 10], [:DEFENSE, -10]] }) GameData::Nature.register({ :id => :SERIOUS, - :id_number => 12, :name => _INTL("Serious") }) GameData::Nature.register({ :id => :JOLLY, - :id_number => 13, :name => _INTL("Jolly"), :stat_changes => [[:SPEED, 10], [:SPECIAL_ATTACK, -10]] }) GameData::Nature.register({ :id => :NAIVE, - :id_number => 14, :name => _INTL("Naive"), :stat_changes => [[:SPEED, 10], [:SPECIAL_DEFENSE, -10]] }) GameData::Nature.register({ :id => :MODEST, - :id_number => 15, :name => _INTL("Modest"), :stat_changes => [[:SPECIAL_ATTACK, 10], [:ATTACK, -10]] }) GameData::Nature.register({ :id => :MILD, - :id_number => 16, :name => _INTL("Mild"), :stat_changes => [[:SPECIAL_ATTACK, 10], [:DEFENSE, -10]] }) GameData::Nature.register({ :id => :QUIET, - :id_number => 17, :name => _INTL("Quiet"), :stat_changes => [[:SPECIAL_ATTACK, 10], [:SPEED, -10]] }) GameData::Nature.register({ :id => :BASHFUL, - :id_number => 18, :name => _INTL("Bashful") }) GameData::Nature.register({ :id => :RASH, - :id_number => 19, :name => _INTL("Rash"), :stat_changes => [[:SPECIAL_ATTACK, 10], [:SPECIAL_DEFENSE, -10]] }) GameData::Nature.register({ :id => :CALM, - :id_number => 20, :name => _INTL("Calm"), :stat_changes => [[:SPECIAL_DEFENSE, 10], [:ATTACK, -10]] }) GameData::Nature.register({ :id => :GENTLE, - :id_number => 21, :name => _INTL("Gentle"), :stat_changes => [[:SPECIAL_DEFENSE, 10], [:DEFENSE, -10]] }) GameData::Nature.register({ :id => :SASSY, - :id_number => 22, :name => _INTL("Sassy"), :stat_changes => [[:SPECIAL_DEFENSE, 10], [:SPEED, -10]] }) GameData::Nature.register({ :id => :CAREFUL, - :id_number => 23, :name => _INTL("Careful"), :stat_changes => [[:SPECIAL_DEFENSE, 10], [:SPECIAL_ATTACK, -10]] }) GameData::Nature.register({ :id => :QUIRKY, - :id_number => 24, :name => _INTL("Quirky") }) diff --git a/Data/Scripts/010_Data/001_Hardcoded data/010_Status.rb b/Data/Scripts/010_Data/001_Hardcoded data/010_Status.rb index 0b852bd04c..43ec0e1ffd 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/010_Status.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/010_Status.rb @@ -1,31 +1,29 @@ -# NOTE: The id_number is only used to determine the order of the status icons in -# the graphics containing them. Number 0 (:NONE) is ignored; they start -# with status 1. -# "Graphics/Pictures/statuses.png" also contains icons for being fainted +# NOTE: "Graphics/Pictures/statuses.png" also contains icons for being fainted # and for having Pokérus, in that order, at the bottom of the graphic. # "Graphics/Pictures/Battle/icon_statuses.png" also contains an icon for # bad poisoning (toxic), at the bottom of the graphic. -# Both graphics automatically handle varying numbers of defined statuses. +# Both graphics automatically handle varying numbers of defined statuses, +# as long as their extra icons remain at the bottom of them. module GameData class Status attr_reader :id - attr_reader :id_number attr_reader :real_name attr_reader :animation + attr_reader :icon_position # Where this status's icon is within statuses.png DATA = {} - extend ClassMethods + extend ClassMethodsSymbols include InstanceMethods def self.load; end def self.save; end def initialize(hash) - @id = hash[:id] - @id_number = hash[:id_number] - @real_name = hash[:name] || "Unnamed" - @animation = hash[:animation] + @id = hash[:id] + @real_name = hash[:name] || "Unnamed" + @animation = hash[:animation] + @icon_position = hash[:icon_position] || 0 end # @return [String] the translated name of this status condition @@ -38,42 +36,41 @@ def name #=============================================================================== GameData::Status.register({ - :id => :NONE, - :id_number => 0, - :name => _INTL("None") + :id => :NONE, + :name => _INTL("None") }) GameData::Status.register({ - :id => :SLEEP, - :id_number => 1, - :name => _INTL("Sleep"), - :animation => "Sleep" + :id => :SLEEP, + :name => _INTL("Sleep"), + :animation => "Sleep", + :icon_position => 0 }) GameData::Status.register({ - :id => :POISON, - :id_number => 2, - :name => _INTL("Poison"), - :animation => "Poison" + :id => :POISON, + :name => _INTL("Poison"), + :animation => "Poison", + :icon_position => 1 }) GameData::Status.register({ - :id => :BURN, - :id_number => 3, - :name => _INTL("Burn"), - :animation => "Burn" + :id => :BURN, + :name => _INTL("Burn"), + :animation => "Burn", + :icon_position => 2 }) GameData::Status.register({ - :id => :PARALYSIS, - :id_number => 4, - :name => _INTL("Paralysis"), - :animation => "Paralysis" + :id => :PARALYSIS, + :name => _INTL("Paralysis"), + :animation => "Paralysis", + :icon_position => 3 }) GameData::Status.register({ - :id => :FROZEN, - :id_number => 5, - :name => _INTL("Frozen"), - :animation => "Frozen" + :id => :FROZEN, + :name => _INTL("Frozen"), + :animation => "Frozen", + :icon_position => 4 }) diff --git a/Data/Scripts/010_Data/001_Hardcoded data/011_TerrainTag.rb b/Data/Scripts/010_Data/001_Hardcoded data/011_TerrainTag.rb index 23591902f5..575dee5ef9 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/011_TerrainTag.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/011_TerrainTag.rb @@ -170,8 +170,9 @@ def can_surf_freely :ignore_passability => true }) -# NOTE: This is referenced by ID in an Events.onStepTakenFieldMovement proc that -# adds soot to the Soot Sack if the player walks over one of these tiles. +# NOTE: This is referenced by ID in the :pick_up_soot proc added to +# EventHandlers. It adds soot to the Soot Sack if the player walks over +# one of these tiles. GameData::TerrainTag.register({ :id => :SootGrass, :id_number => 14, @@ -192,3 +193,8 @@ def can_surf_freely :battle_environment => :Puddle, :shows_reflections => true }) + +GameData::TerrainTag.register({ + :id => :NoEffect, + :id_number => 17 +}) diff --git a/Data/Scripts/010_Data/001_Hardcoded data/013_EncounterType.rb b/Data/Scripts/010_Data/001_Hardcoded data/013_EncounterType.rb index d03a543aa9..5987e31a70 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/013_EncounterType.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/013_EncounterType.rb @@ -4,7 +4,6 @@ class EncounterType attr_reader :real_name attr_reader :type # :land, :cave, :water, :fishing, :contest, :none attr_reader :trigger_chance - attr_reader :old_slots DATA = {} @@ -19,7 +18,6 @@ def initialize(hash) @real_name = hash[:id].to_s || "Unnamed" @type = hash[:type] || :none @trigger_chance = hash[:trigger_chance] || 0 - @old_slots = hash[:old_slots] end end end @@ -29,169 +27,144 @@ def initialize(hash) GameData::EncounterType.register({ :id => :Land, :type => :land, - :trigger_chance => 21, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 21 }) GameData::EncounterType.register({ :id => :LandDay, :type => :land, - :trigger_chance => 21, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 21 }) GameData::EncounterType.register({ :id => :LandNight, :type => :land, - :trigger_chance => 21, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 21 }) GameData::EncounterType.register({ :id => :LandMorning, :type => :land, - :trigger_chance => 21, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 21 }) GameData::EncounterType.register({ :id => :LandAfternoon, :type => :land, - :trigger_chance => 21, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 21 }) GameData::EncounterType.register({ :id => :LandEvening, :type => :land, - :trigger_chance => 21, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 21 }) GameData::EncounterType.register({ :id => :Cave, :type => :cave, - :trigger_chance => 5, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 5 }) GameData::EncounterType.register({ :id => :CaveDay, :type => :cave, - :trigger_chance => 5, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 5 }) GameData::EncounterType.register({ :id => :CaveNight, :type => :cave, - :trigger_chance => 5, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 5 }) GameData::EncounterType.register({ :id => :CaveMorning, :type => :cave, - :trigger_chance => 5, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 5 }) GameData::EncounterType.register({ :id => :CaveAfternoon, :type => :cave, - :trigger_chance => 5, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 5 }) GameData::EncounterType.register({ :id => :CaveEvening, :type => :cave, - :trigger_chance => 5, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 5 }) GameData::EncounterType.register({ :id => :Water, :type => :water, - :trigger_chance => 2, - :old_slots => [60, 30, 5, 4, 1] + :trigger_chance => 2 }) GameData::EncounterType.register({ :id => :WaterDay, :type => :water, - :trigger_chance => 2, - :old_slots => [60, 30, 5, 4, 1] + :trigger_chance => 2 }) GameData::EncounterType.register({ :id => :WaterNight, :type => :water, - :trigger_chance => 2, - :old_slots => [60, 30, 5, 4, 1] + :trigger_chance => 2 }) GameData::EncounterType.register({ :id => :WaterMorning, :type => :water, - :trigger_chance => 2, - :old_slots => [60, 30, 5, 4, 1] + :trigger_chance => 2 }) GameData::EncounterType.register({ :id => :WaterAfternoon, :type => :water, - :trigger_chance => 2, - :old_slots => [60, 30, 5, 4, 1] + :trigger_chance => 2 }) GameData::EncounterType.register({ :id => :WaterEvening, :type => :water, - :trigger_chance => 2, - :old_slots => [60, 30, 5, 4, 1] + :trigger_chance => 2 }) GameData::EncounterType.register({ :id => :OldRod, - :type => :fishing, - :old_slots => [70, 30] + :type => :fishing }) GameData::EncounterType.register({ :id => :GoodRod, - :type => :fishing, - :old_slots => [60, 20, 20] + :type => :fishing }) GameData::EncounterType.register({ :id => :SuperRod, - :type => :fishing, - :old_slots => [40, 40, 15, 4, 1] + :type => :fishing }) GameData::EncounterType.register({ :id => :RockSmash, :type => :none, - :trigger_chance => 50, - :old_slots => [60, 30, 5, 4, 1] + :trigger_chance => 50 }) GameData::EncounterType.register({ :id => :HeadbuttLow, - :type => :none, - :old_slots => [30, 25, 20, 10, 5, 5, 4, 1] + :type => :none }) GameData::EncounterType.register({ :id => :HeadbuttHigh, - :type => :none, - :old_slots => [30, 25, 20, 10, 5, 5, 4, 1] + :type => :none }) GameData::EncounterType.register({ :id => :BugContest, :type => :contest, - :trigger_chance => 21, - :old_slots => [20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1] + :trigger_chance => 21 }) diff --git a/Data/Scripts/010_Data/001_Hardcoded data/017_Target.rb b/Data/Scripts/010_Data/001_Hardcoded data/017_Target.rb index 1eea4e43f0..b00c9a22be 100644 --- a/Data/Scripts/010_Data/001_Hardcoded data/017_Target.rb +++ b/Data/Scripts/010_Data/001_Hardcoded data/017_Target.rb @@ -8,7 +8,6 @@ module GameData class Target attr_reader :id - attr_reader :id_number attr_reader :real_name attr_reader :num_targets # 0, 1 or 2 (meaning 2+) attr_reader :targets_foe # Is able to target one or more foes @@ -18,7 +17,7 @@ class Target DATA = {} - extend ClassMethods + extend ClassMethodsSymbols include InstanceMethods def self.load; end @@ -54,20 +53,17 @@ def can_target_one_foe? # Bide, Counter, Metal Burst, Mirror Coat (calculate a target) GameData::Target.register({ :id => :None, - :id_number => 1, :name => _INTL("None") }) GameData::Target.register({ :id => :User, - :id_number => 10, :name => _INTL("User") }) # Aromatic Mist, Helping Hand, Hold Hands GameData::Target.register({ :id => :NearAlly, - :id_number => 100, :name => _INTL("Near Ally"), :num_targets => 1 }) @@ -75,15 +71,22 @@ def can_target_one_foe? # Acupressure GameData::Target.register({ :id => :UserOrNearAlly, - :id_number => 200, :name => _INTL("User or Near Ally"), :num_targets => 1 }) +# Coaching +GameData::Target.register({ + :id => :AllAllies, + :name => _INTL("All Allies"), + :num_targets => 2, + :targets_all => true, + :long_range => true +}) + # Aromatherapy, Gear Up, Heal Bell, Life Dew, Magnetic Flux, Howl (in Gen 8+) GameData::Target.register({ :id => :UserAndAllies, - :id_number => 5, :name => _INTL("User and Allies"), :num_targets => 2, :long_range => true @@ -92,7 +95,6 @@ def can_target_one_foe? # Me First GameData::Target.register({ :id => :NearFoe, - :id_number => 400, :name => _INTL("Near Foe"), :num_targets => 1, :targets_foe => true @@ -101,7 +103,6 @@ def can_target_one_foe? # Petal Dance, Outrage, Struggle, Thrash, Uproar GameData::Target.register({ :id => :RandomNearFoe, - :id_number => 2, :name => _INTL("Random Near Foe"), :num_targets => 1, :targets_foe => true @@ -109,7 +110,6 @@ def can_target_one_foe? GameData::Target.register({ :id => :AllNearFoes, - :id_number => 4, :name => _INTL("All Near Foes"), :num_targets => 2, :targets_foe => true @@ -118,7 +118,6 @@ def can_target_one_foe? # For throwing a Poké Ball GameData::Target.register({ :id => :Foe, - :id_number => 9, :name => _INTL("Foe"), :num_targets => 1, :targets_foe => true, @@ -128,7 +127,6 @@ def can_target_one_foe? # Unused GameData::Target.register({ :id => :AllFoes, - :id_number => 6, :name => _INTL("All Foes"), :num_targets => 2, :targets_foe => true, @@ -137,7 +135,6 @@ def can_target_one_foe? GameData::Target.register({ :id => :NearOther, - :id_number => 0, :name => _INTL("Near Other"), :num_targets => 1, :targets_foe => true @@ -145,7 +142,6 @@ def can_target_one_foe? GameData::Target.register({ :id => :AllNearOthers, - :id_number => 8, :name => _INTL("All Near Others"), :num_targets => 2, :targets_foe => true @@ -154,7 +150,6 @@ def can_target_one_foe? # Most Flying-type moves, pulse moves (hits non-near targets) GameData::Target.register({ :id => :Other, - :id_number => 3, :name => _INTL("Other"), :num_targets => 1, :targets_foe => true, @@ -164,7 +159,6 @@ def can_target_one_foe? # Flower Shield, Perish Song, Rototiller, Teatime GameData::Target.register({ :id => :AllBattlers, - :id_number => 7, :name => _INTL("All Battlers"), :num_targets => 2, :targets_foe => true, @@ -174,21 +168,18 @@ def can_target_one_foe? GameData::Target.register({ :id => :UserSide, - :id_number => 40, :name => _INTL("User Side") }) # Entry hazards GameData::Target.register({ :id => :FoeSide, - :id_number => 80, :name => _INTL("Foe Side"), :affects_foe_side => true }) GameData::Target.register({ :id => :BothSides, - :id_number => 20, :name => _INTL("Both Sides"), :affects_foe_side => true }) diff --git a/Data/Scripts/010_Data/002_PBS data/001_MiscPBSData.rb b/Data/Scripts/010_Data/002_PBS data/001_MiscPBSData.rb index f60186d914..3d37cfbe9d 100644 --- a/Data/Scripts/010_Data/002_PBS data/001_MiscPBSData.rb +++ b/Data/Scripts/010_Data/002_PBS data/001_MiscPBSData.rb @@ -1,25 +1,23 @@ #=============================================================================== # Data caches. #=============================================================================== -class PokemonTemp - attr_accessor :townMapData - attr_accessor :phoneData - attr_accessor :speciesShadowMovesets - attr_accessor :regionalDexes - attr_accessor :battleAnims - attr_accessor :moveToAnim - attr_accessor :mapInfos +class Game_Temp + attr_accessor :town_map_data + attr_accessor :phone_messages_data + attr_accessor :regional_dexes_data + attr_accessor :battle_animations_data + attr_accessor :move_to_battle_animation_data + attr_accessor :map_infos end def pbClearData - if $PokemonTemp - $PokemonTemp.townMapData = nil - $PokemonTemp.phoneData = nil - $PokemonTemp.speciesShadowMovesets = nil - $PokemonTemp.regionalDexes = nil - $PokemonTemp.battleAnims = nil - $PokemonTemp.moveToAnim = nil - $PokemonTemp.mapInfos = nil + if $game_temp + $game_temp.town_map_data = nil + $game_temp.phone_messages_data = nil + $game_temp.regional_dexes_data = nil + $game_temp.battle_animations_data = nil + $game_temp.move_to_battle_animation_data = nil + $game_temp.map_infos = nil end MapFactoryHelper.clear $PokemonEncounters.setup($game_map.map_id) if $game_map && $PokemonEncounters @@ -32,76 +30,61 @@ def pbClearData # Method to get Town Map data. #=============================================================================== def pbLoadTownMapData - $PokemonTemp = PokemonTemp.new if !$PokemonTemp - if !$PokemonTemp.townMapData - $PokemonTemp.townMapData = load_data("Data/town_map.dat") + $game_temp = Game_Temp.new if !$game_temp + if !$game_temp.town_map_data + $game_temp.town_map_data = load_data("Data/town_map.dat") end - return $PokemonTemp.townMapData + return $game_temp.town_map_data end #=============================================================================== # Method to get phone call data. #=============================================================================== def pbLoadPhoneData - $PokemonTemp = PokemonTemp.new if !$PokemonTemp - if !$PokemonTemp.phoneData - if pbRgssExists?("Data/phone.dat") - $PokemonTemp.phoneData = load_data("Data/phone.dat") - end + $game_temp = Game_Temp.new if !$game_temp + if !$game_temp.phone_messages_data && pbRgssExists?("Data/phone.dat") + $game_temp.phone_messages_data = load_data("Data/phone.dat") end - return $PokemonTemp.phoneData -end - -#=============================================================================== -# Method to get Shadow Pokémon moveset data. -#=============================================================================== -def pbLoadShadowMovesets - $PokemonTemp = PokemonTemp.new if !$PokemonTemp - if !$PokemonTemp.speciesShadowMovesets - $PokemonTemp.speciesShadowMovesets = load_data("Data/shadow_movesets.dat") || [] - end - return $PokemonTemp.speciesShadowMovesets + return $game_temp.phone_messages_data end #=============================================================================== # Method to get Regional Dexes data. #=============================================================================== def pbLoadRegionalDexes - $PokemonTemp = PokemonTemp.new if !$PokemonTemp - if !$PokemonTemp.regionalDexes - $PokemonTemp.regionalDexes = load_data("Data/regional_dexes.dat") + $game_temp = Game_Temp.new if !$game_temp + if !$game_temp.regional_dexes_data + $game_temp.regional_dexes_data = load_data("Data/regional_dexes.dat") end - return $PokemonTemp.regionalDexes + return $game_temp.regional_dexes_data end #=============================================================================== # Methods relating to battle animations data. #=============================================================================== def pbLoadBattleAnimations - $PokemonTemp = PokemonTemp.new if !$PokemonTemp - if !$PokemonTemp.battleAnims - if pbRgssExists?("Data/PkmnAnimations.rxdata") - $PokemonTemp.battleAnims = load_data("Data/PkmnAnimations.rxdata") - end + $game_temp = Game_Temp.new if !$game_temp + if !$game_temp.battle_animations_data && pbRgssExists?("Data/PkmnAnimations.rxdata") + $game_temp.battle_animations_data = load_data("Data/PkmnAnimations.rxdata") end - return $PokemonTemp.battleAnims + return $game_temp.battle_animations_data end def pbLoadMoveToAnim - $PokemonTemp = PokemonTemp.new if !$PokemonTemp - if !$PokemonTemp.moveToAnim - $PokemonTemp.moveToAnim = load_data("Data/move2anim.dat") || [] + $game_temp = Game_Temp.new if !$game_temp + if !$game_temp.move_to_battle_animation_data + $game_temp.move_to_battle_animation_data = load_data("Data/move2anim.dat") || [] end - return $PokemonTemp.moveToAnim + return $game_temp.move_to_battle_animation_data end #=============================================================================== # Method relating to map infos data. #=============================================================================== def pbLoadMapInfos - $PokemonTemp = PokemonTemp.new if !$PokemonTemp - if !$PokemonTemp.mapInfos - $PokemonTemp.mapInfos = load_data("Data/MapInfos.rxdata") + $game_temp = Game_Temp.new if !$game_temp + if !$game_temp.map_infos + $game_temp.map_infos = load_data("Data/MapInfos.rxdata") end - return $PokemonTemp.mapInfos + return $game_temp.map_infos end diff --git a/Data/Scripts/010_Data/002_PBS data/002_PhoneDatabase.rb b/Data/Scripts/010_Data/002_PBS data/002_PhoneDatabase.rb index 903cf125e0..ab8cd79125 100644 --- a/Data/Scripts/010_Data/002_PBS data/002_PhoneDatabase.rb +++ b/Data/Scripts/010_Data/002_PBS data/002_PhoneDatabase.rb @@ -22,10 +22,3 @@ def initialize @trainers = [] end end - -module PhoneMsgType - Generic = 0 - Greeting = 1 - Body = 2 - BattleRequest = 3 -end diff --git a/Data/Scripts/010_Data/002_PBS data/003_Type.rb b/Data/Scripts/010_Data/002_PBS data/003_Type.rb index 6853ffcb53..0e5a2b4577 100644 --- a/Data/Scripts/010_Data/002_PBS data/003_Type.rb +++ b/Data/Scripts/010_Data/002_PBS data/003_Type.rb @@ -1,52 +1,60 @@ module GameData class Type attr_reader :id - attr_reader :id_number attr_reader :real_name attr_reader :special_type attr_reader :pseudo_type + attr_reader :flags attr_reader :weaknesses attr_reader :resistances attr_reader :immunities + attr_reader :icon_position # Where this type's icon is within types.png DATA = {} DATA_FILENAME = "types.dat" SCHEMA = { - "Name" => [1, "s"], - "InternalName" => [2, "s"], - "IsPseudoType" => [3, "b"], - "IsSpecialType" => [4, "b"], - "Weaknesses" => [5, "*s"], - "Resistances" => [6, "*s"], - "Immunities" => [7, "*s"] + "Name" => [0, "s"], + "InternalName" => [0, "s"], + "IsSpecialType" => [0, "b"], + "IsPseudoType" => [0, "b"], + "Flags" => [0, "*s"], + "Weaknesses" => [0, "*s"], + "Resistances" => [0, "*s"], + "Immunities" => [0, "*s"], + "IconPosition" => [0, "u"] } - extend ClassMethods + extend ClassMethodsSymbols include InstanceMethods def initialize(hash) - @id = hash[:id] - @id_number = hash[:id_number] || -1 - @real_name = hash[:name] || "Unnamed" - @pseudo_type = hash[:pseudo_type] || false - @special_type = hash[:special_type] || false - @weaknesses = hash[:weaknesses] || [] - @weaknesses = [@weaknesses] if !@weaknesses.is_a?(Array) - @resistances = hash[:resistances] || [] - @resistances = [@resistances] if !@resistances.is_a?(Array) - @immunities = hash[:immunities] || [] - @immunities = [@immunities] if !@immunities.is_a?(Array) + @id = hash[:id] + @real_name = hash[:name] || "Unnamed" + @special_type = hash[:special_type] || false + @pseudo_type = hash[:pseudo_type] || false + @flags = hash[:flags] || [] + @weaknesses = hash[:weaknesses] || [] + @weaknesses = [@weaknesses] if !@weaknesses.is_a?(Array) + @resistances = hash[:resistances] || [] + @resistances = [@resistances] if !@resistances.is_a?(Array) + @immunities = hash[:immunities] || [] + @immunities = [@immunities] if !@immunities.is_a?(Array) + @icon_position = hash[:icon_position] || 0 end # @return [String] the translated name of this item def name - return pbGetMessage(MessageTypes::Types, @id_number) + return pbGetMessageFromHash(MessageTypes::Types, @real_name) end def physical?; return !@special_type; end def special?; return @special_type; end + def has_flag?(flag) + return @flags.any? { |f| f.downcase == flag.downcase } + end + def effectiveness(other_type) return Effectiveness::NORMAL_EFFECTIVE_ONE if !other_type return Effectiveness::SUPER_EFFECTIVE_ONE if @weaknesses.include?(other_type) @@ -64,7 +72,7 @@ module Effectiveness NOT_VERY_EFFECTIVE_ONE = 1 NORMAL_EFFECTIVE_ONE = 2 SUPER_EFFECTIVE_ONE = 4 - NORMAL_EFFECTIVE = NORMAL_EFFECTIVE_ONE ** 3 + NORMAL_EFFECTIVE = NORMAL_EFFECTIVE_ONE**3 module_function diff --git a/Data/Scripts/010_Data/002_PBS data/004_Ability.rb b/Data/Scripts/010_Data/002_PBS data/004_Ability.rb index 2de14fccf4..a34535e306 100644 --- a/Data/Scripts/010_Data/002_PBS data/004_Ability.rb +++ b/Data/Scripts/010_Data/002_PBS data/004_Ability.rb @@ -1,31 +1,41 @@ module GameData class Ability attr_reader :id - attr_reader :id_number attr_reader :real_name attr_reader :real_description + attr_reader :flags DATA = {} DATA_FILENAME = "abilities.dat" - extend ClassMethods + extend ClassMethodsSymbols include InstanceMethods + SCHEMA = { + "Name" => [:name, "s"], + "Description" => [:description, "q"], + "Flags" => [:flags, "*s"] + } + def initialize(hash) @id = hash[:id] - @id_number = hash[:id_number] || -1 @real_name = hash[:name] || "Unnamed" @real_description = hash[:description] || "???" + @flags = hash[:flags] || [] end # @return [String] the translated name of this ability def name - return pbGetMessage(MessageTypes::Abilities, @id_number) + return pbGetMessageFromHash(MessageTypes::Abilities, @real_name) end # @return [String] the translated description of this ability def description - return pbGetMessage(MessageTypes::AbilityDescs, @id_number) + return pbGetMessageFromHash(MessageTypes::AbilityDescs, @real_description) + end + + def has_flag?(flag) + return @flags.any? { |f| f.downcase == flag.downcase } end end end diff --git a/Data/Scripts/010_Data/002_PBS data/005_Move.rb b/Data/Scripts/010_Data/002_PBS data/005_Move.rb index eeff3c73ff..7363a02af7 100644 --- a/Data/Scripts/010_Data/002_PBS data/005_Move.rb +++ b/Data/Scripts/010_Data/002_PBS data/005_Move.rb @@ -1,51 +1,70 @@ module GameData class Move attr_reader :id - attr_reader :id_number attr_reader :real_name - attr_reader :function_code - attr_reader :base_damage attr_reader :type attr_reader :category + attr_reader :base_damage attr_reader :accuracy attr_reader :total_pp - attr_reader :effect_chance attr_reader :target attr_reader :priority + attr_reader :function_code attr_reader :flags + attr_reader :effect_chance attr_reader :real_description DATA = {} DATA_FILENAME = "moves.dat" - extend ClassMethods + SCHEMA = { + "Name" => [:name, "s"], + "Type" => [:type, "e", :Type], + "Category" => [:category, "e", ["Physical", "Special", "Status"]], + "BaseDamage" => [:base_damage, "u"], + "Accuracy" => [:accuracy, "u"], + "TotalPP" => [:total_pp, "u"], + "Target" => [:target, "e", :Target], + "Priority" => [:priority, "i"], + "FunctionCode" => [:function_code, "s"], + "Flags" => [:flags, "*s"], + "EffectChance" => [:effect_chance, "u"], + "Description" => [:description, "q"] + } + + extend ClassMethodsSymbols include InstanceMethods def initialize(hash) + convert_move_data(hash) @id = hash[:id] - @id_number = hash[:id_number] || -1 - @real_name = hash[:name] || "Unnamed" - @function_code = hash[:function_code] - @base_damage = hash[:base_damage] - @type = hash[:type] - @category = hash[:category] - @accuracy = hash[:accuracy] - @total_pp = hash[:total_pp] - @effect_chance = hash[:effect_chance] - @target = hash[:target] - @priority = hash[:priority] - @flags = hash[:flags] - @real_description = hash[:description] || "???" + @real_name = hash[:name] || "Unnamed" + @type = hash[:type] || :NONE + @category = hash[:category] || 2 + @base_damage = hash[:base_damage] || 0 + @accuracy = hash[:accuracy] || 100 + @total_pp = hash[:total_pp] || 5 + @target = hash[:target] || :None + @priority = hash[:priority] || 0 + @function_code = hash[:function_code] || "None" + @flags = hash[:flags] || [] + @flags = [@flags] if !@flags.is_a?(Array) + @effect_chance = hash[:effect_chance] || 0 + @real_description = hash[:description] || "???" end # @return [String] the translated name of this move def name - return pbGetMessage(MessageTypes::Moves, @id_number) + return pbGetMessageFromHash(MessageTypes::Moves, @real_name) end # @return [String] the translated description of this move def description - return pbGetMessage(MessageTypes::MoveDescriptions, @id_number) + return pbGetMessageFromHash(MessageTypes::MoveDescriptions, @real_description) + end + + def has_flag?(flag) + return @flags.any? { |f| f.downcase == flag.downcase } end def physical? @@ -66,20 +85,729 @@ def hidden_move? end return false end - end -end -#=============================================================================== -# Deprecated methods -#=============================================================================== -# @deprecated This alias is slated to be removed in v20. -def pbGetMoveData(move_id, move_data_type = -1) - Deprecation.warn_method('pbGetMoveData', 'v20', 'GameData::Move.get(move_id)') - return GameData::Move.get(move_id) -end + def display_type(pkmn, move = nil) +=begin + case @function_code + when "TypeDependsOnUserIVs" + return pbHiddenPower(pkmn)[0] + when "TypeAndPowerDependOnUserBerry" + type_array = { + :NORMAL => [:CHILANBERRY], + :FIRE => [:CHERIBERRY, :BLUKBERRY, :WATMELBERRY, :OCCABERRY], + :WATER => [:CHESTOBERRY, :NANABBERRY, :DURINBERRY, :PASSHOBERRY], + :ELECTRIC => [:PECHABERRY, :WEPEARBERRY, :BELUEBERRY, :WACANBERRY], + :GRASS => [:RAWSTBERRY, :PINAPBERRY, :RINDOBERRY, :LIECHIBERRY], + :ICE => [:ASPEARBERRY, :POMEGBERRY, :YACHEBERRY, :GANLONBERRY], + :FIGHTING => [:LEPPABERRY, :KELPSYBERRY, :CHOPLEBERRY, :SALACBERRY], + :POISON => [:ORANBERRY, :QUALOTBERRY, :KEBIABERRY, :PETAYABERRY], + :GROUND => [:PERSIMBERRY, :HONDEWBERRY, :SHUCABERRY, :APICOTBERRY], + :FLYING => [:LUMBERRY, :GREPABERRY, :COBABERRY, :LANSATBERRY], + :PSYCHIC => [:SITRUSBERRY, :TAMATOBERRY, :PAYAPABERRY, :STARFBERRY], + :BUG => [:FIGYBERRY, :CORNNBERRY, :TANGABERRY, :ENIGMABERRY], + :ROCK => [:WIKIBERRY, :MAGOSTBERRY, :CHARTIBERRY, :MICLEBERRY], + :GHOST => [:MAGOBERRY, :RABUTABERRY, :KASIBBERRY, :CUSTAPBERRY], + :DRAGON => [:AGUAVBERRY, :NOMELBERRY, :HABANBERRY, :JABOCABERRY], + :DARK => [:IAPAPABERRY, :SPELONBERRY, :COLBURBERRY, :ROWAPBERRY, :MARANGABERRY], + :STEEL => [:RAZZBERRY, :PAMTREBERRY, :BABIRIBERRY], + :FAIRY => [:ROSELIBERRY, :KEEBERRY] + } + if pkmn.hasItem? + type_array.each do |type, items| + return type if items.include?(pkmn.item_id) && GameData::Type.exists?(type) + end + end + when "TypeDependsOnUserPlate" + item_types = { + :FISTPLATE => :FIGHTING, + :SKYPLATE => :FLYING, + :TOXICPLATE => :POISON, + :EARTHPLATE => :GROUND, + :STONEPLATE => :ROCK, + :INSECTPLATE => :BUG, + :SPOOKYPLATE => :GHOST, + :IRONPLATE => :STEEL, + :FLAMEPLATE => :FIRE, + :SPLASHPLATE => :WATER, + :MEADOWPLATE => :GRASS, + :ZAPPLATE => :ELECTRIC, + :MINDPLATE => :PSYCHIC, + :ICICLEPLATE => :ICE, + :DRACOPLATE => :DRAGON, + :DREADPLATE => :DARK, + :PIXIEPLATE => :FAIRY + } + if pkmn.hasItem? + item_types.each do |item, item_type| + return item_type if pkmn.item_id == item && GameData::Type.exists?(item_type) + end + end + when "TypeDependsOnUserMemory" + item_types = { + :FIGHTINGMEMORY => :FIGHTING, + :FLYINGMEMORY => :FLYING, + :POISONMEMORY => :POISON, + :GROUNDMEMORY => :GROUND, + :ROCKMEMORY => :ROCK, + :BUGMEMORY => :BUG, + :GHOSTMEMORY => :GHOST, + :STEELMEMORY => :STEEL, + :FIREMEMORY => :FIRE, + :WATERMEMORY => :WATER, + :GRASSMEMORY => :GRASS, + :ELECTRICMEMORY => :ELECTRIC, + :PSYCHICMEMORY => :PSYCHIC, + :ICEMEMORY => :ICE, + :DRAGONMEMORY => :DRAGON, + :DARKMEMORY => :DARK, + :FAIRYMEMORY => :FAIRY + } + if pkmn.hasItem? + item_types.each do |item, item_type| + return item_type if pkmn.item_id == item && GameData::Type.exists?(item_type) + end + end + when "TypeDependsOnUserDrive" + item_types = { + :SHOCKDRIVE => :ELECTRIC, + :BURNDRIVE => :FIRE, + :CHILLDRIVE => :ICE, + :DOUSEDRIVE => :WATER + } + if pkmn.hasItem? + item_types.each do |item, item_type| + return item_type if pkmn.item_id == item && GameData::Type.exists?(item_type) + end + end + when "TypeIsUserFirstType" + return pkmn.types[0] + end +=end + return @type + end + + def display_damage(pkmn, move = nil) +=begin + case @function_code + when "TypeDependsOnUserIVs" + return pbHiddenPower(pkmn)[1] + when "TypeAndPowerDependOnUserBerry" + damage_array = { + 60 => [:CHERIBERRY, :CHESTOBERRY, :PECHABERRY, :RAWSTBERRY, :ASPEARBERRY, + :LEPPABERRY, :ORANBERRY, :PERSIMBERRY, :LUMBERRY, :SITRUSBERRY, + :FIGYBERRY, :WIKIBERRY, :MAGOBERRY, :AGUAVBERRY, :IAPAPABERRY, + :RAZZBERRY, :OCCABERRY, :PASSHOBERRY, :WACANBERRY, :RINDOBERRY, + :YACHEBERRY, :CHOPLEBERRY, :KEBIABERRY, :SHUCABERRY, :COBABERRY, + :PAYAPABERRY, :TANGABERRY, :CHARTIBERRY, :KASIBBERRY, :HABANBERRY, + :COLBURBERRY, :BABIRIBERRY, :CHILANBERRY, :ROSELIBERRY], + 70 => [:BLUKBERRY, :NANABBERRY, :WEPEARBERRY, :PINAPBERRY, :POMEGBERRY, + :KELPSYBERRY, :QUALOTBERRY, :HONDEWBERRY, :GREPABERRY, :TAMATOBERRY, + :CORNNBERRY, :MAGOSTBERRY, :RABUTABERRY, :NOMELBERRY, :SPELONBERRY, + :PAMTREBERRY], + 80 => [:WATMELBERRY, :DURINBERRY, :BELUEBERRY, :LIECHIBERRY, :GANLONBERRY, + :SALACBERRY, :PETAYABERRY, :APICOTBERRY, :LANSATBERRY, :STARFBERRY, + :ENIGMABERRY, :MICLEBERRY, :CUSTAPBERRY, :JABOCABERRY, :ROWAPBERRY, + :KEEBERRY, :MARANGABERRY] + } + if pkmn.hasItem? + damage_array.each do |dmg, items| + next if !items.include?(pkmn.item_id) + ret = dmg + ret += 20 if Settings::MECHANICS_GENERATION >= 6 + return ret + end + end + when "ThrowUserItemAtTarget" + fling_powers = { + 130 => [:IRONBALL + ], + 100 => [:HARDSTONE,:RAREBONE, + # Fossils + :ARMORFOSSIL,:CLAWFOSSIL,:COVERFOSSIL,:DOMEFOSSIL,:HELIXFOSSIL, + :JAWFOSSIL,:OLDAMBER,:PLUMEFOSSIL,:ROOTFOSSIL,:SAILFOSSIL, + :SKULLFOSSIL + ], + 90 => [:DEEPSEATOOTH,:GRIPCLAW,:THICKCLUB, + # Plates + :DRACOPLATE,:DREADPLATE,:EARTHPLATE,:FISTPLATE,:FLAMEPLATE, + :ICICLEPLATE,:INSECTPLATE,:IRONPLATE,:MEADOWPLATE,:MINDPLATE, + :PIXIEPLATE,:SKYPLATE,:SPLASHPLATE,:SPOOKYPLATE,:STONEPLATE, + :TOXICPLATE,:ZAPPLATE + ], + 80 => [:ASSAULTVEST,:CHIPPEDPOT,:CRACKEDPOT,:DAWNSTONE,:DUSKSTONE, + :ELECTIRIZER,:HEAVYDUTYBOOTS,:MAGMARIZER,:ODDKEYSTONE,:OVALSTONE, + :PROTECTOR,:QUICKCLAW,:RAZORCLAW,:SACHET,:SAFETYGOGGLES, + :SHINYSTONE,:STICKYBARB,:WEAKNESSPOLICY,:WHIPPEDDREAM + ], + 70 => [:DRAGONFANG,:POISONBARB, + # EV-training items (Macho Brace is 60) + :POWERANKLET,:POWERBAND,:POWERBELT,:POWERBRACER,:POWERLENS, + :POWERWEIGHT, + # Drives + :BURNDRIVE,:CHILLDRIVE,:DOUSEDRIVE,:SHOCKDRIVE + ], + 60 => [:ADAMANTORB,:DAMPROCK,:GRISEOUSORB,:HEATROCK,:LEEK,:LUSTROUSORB, + :MACHOBRACE,:ROCKYHELMET,:STICK,:TERRAINEXTENDER + ], + 50 => [:DUBIOUSDISC,:SHARPBEAK, + # Memories + :BUGMEMORY,:DARKMEMORY,:DRAGONMEMORY,:ELECTRICMEMORY,:FAIRYMEMORY, + :FIGHTINGMEMORY,:FIREMEMORY,:FLYINGMEMORY,:GHOSTMEMORY, + :GRASSMEMORY,:GROUNDMEMORY,:ICEMEMORY,:POISONMEMORY, + :PSYCHICMEMORY,:ROCKMEMORY,:STEELMEMORY,:WATERMEMORY + ], + 40 => [:EVIOLITE,:ICYROCK,:LUCKYPUNCH + ], + 30 => [:ABSORBBULB,:ADRENALINEORB,:AMULETCOIN,:BINDINGBAND,:BLACKBELT, + :BLACKGLASSES,:BLACKSLUDGE,:BOTTLECAP,:CELLBATTERY,:CHARCOAL, + :CLEANSETAG,:DEEPSEASCALE,:DRAGONSCALE,:EJECTBUTTON,:ESCAPEROPE, + :EXPSHARE,:FLAMEORB,:FLOATSTONE,:FLUFFYTAIL,:GOLDBOTTLECAP, + :HEARTSCALE,:HONEY,:KINGSROCK,:LIFEORB,:LIGHTBALL,:LIGHTCLAY, + :LUCKYEGG,:LUMINOUSMOSS,:MAGNET,:METALCOAT,:METRONOME, + :MIRACLESEED,:MYSTICWATER,:NEVERMELTICE,:PASSORB,:POKEDOLL, + :POKETOY,:PRISMSCALE,:PROTECTIVEPADS,:RAZORFANG,:SACREDASH, + :SCOPELENS,:SHELLBELL,:SHOALSALT,:SHOALSHELL,:SMOKEBALL,:SNOWBALL, + :SOULDEW,:SPELLTAG,:TOXICORB,:TWISTEDSPOON,:UPGRADE, + # Healing items + :ANTIDOTE,:AWAKENING,:BERRYJUICE,:BIGMALASADA,:BLUEFLUTE, + :BURNHEAL,:CASTELIACONE,:ELIXIR,:ENERGYPOWDER,:ENERGYROOT,:ETHER, + :FRESHWATER,:FULLHEAL,:FULLRESTORE,:HEALPOWDER,:HYPERPOTION, + :ICEHEAL,:LAVACOOKIE,:LEMONADE,:LUMIOSEGALETTE,:MAXELIXIR, + :MAXETHER,:MAXHONEY,:MAXPOTION,:MAXREVIVE,:MOOMOOMILK,:OLDGATEAU, + :PARALYZEHEAL,:PARLYZHEAL,:PEWTERCRUNCHIES,:POTION,:RAGECANDYBAR, + :REDFLUTE,:REVIVALHERB,:REVIVE,:SHALOURSABLE,:SODAPOP, + :SUPERPOTION,:SWEETHEART,:YELLOWFLUTE, + # Battle items + :XACCURACY,:XACCURACY2,:XACCURACY3,:XACCURACY6, + :XATTACK,:XATTACK2,:XATTACK3,:XATTACK6, + :XDEFEND,:XDEFEND2,:XDEFEND3,:XDEFEND6, + :XDEFENSE,:XDEFENSE2,:XDEFENSE3,:XDEFENSE6, + :XSPATK,:XSPATK2,:XSPATK3,:XSPATK6, + :XSPECIAL,:XSPECIAL2,:XSPECIAL3,:XSPECIAL6, + :XSPDEF,:XSPDEF2,:XSPDEF3,:XSPDEF6, + :XSPEED,:XSPEED2,:XSPEED3,:XSPEED6, + :DIREHIT,:DIREHIT2,:DIREHIT3, + :ABILITYURGE,:GUARDSPEC,:ITEMDROP,:ITEMURGE,:RESETURGE, + :MAXMUSHROOMS, + # Vitamins + :CALCIUM,:CARBOS,:HPUP,:IRON,:PPUP,:PPMAX,:PROTEIN,:ZINC, + :RARECANDY, + # Most evolution stones (see also 80) + :EVERSTONE,:FIRESTONE,:ICESTONE,:LEAFSTONE,:MOONSTONE,:SUNSTONE, + :THUNDERSTONE,:WATERSTONE,:SWEETAPPLE,:TARTAPPLE, :GALARICACUFF, + :GALARICAWREATH, + # Repels + :MAXREPEL,:REPEL,:SUPERREPEL, + # Mulches + :AMAZEMULCH,:BOOSTMULCH,:DAMPMULCH,:GOOEYMULCH,:GROWTHMULCH, + :RICHMULCH,:STABLEMULCH,:SURPRISEMULCH, + # Shards + :BLUESHARD,:GREENSHARD,:REDSHARD,:YELLOWSHARD, + # Valuables + :BALMMUSHROOM,:BIGMUSHROOM,:BIGNUGGET,:BIGPEARL,:COMETSHARD, + :NUGGET,:PEARL,:PEARLSTRING,:RELICBAND,:RELICCOPPER,:RELICCROWN, + :RELICGOLD,:RELICSILVER,:RELICSTATUE,:RELICVASE,:STARDUST, + :STARPIECE,:STRANGESOUVENIR,:TINYMUSHROOM, + # Exp Candies + :EXPCANDYXS, :EXPCANDYS, :EXPCANDYM, :EXPCANDYL, :EXPCANDYXL + ], + 20 => [# Feathers + :CLEVERFEATHER,:GENIUSFEATHER,:HEALTHFEATHER,:MUSCLEFEATHER, + :PRETTYFEATHER,:RESISTFEATHER,:SWIFTFEATHER, + :CLEVERWING,:GENIUSWING,:HEALTHWING,:MUSCLEWING,:PRETTYWING, + :RESISTWING,:SWIFTWING + ], + 10 => [:AIRBALLOON,:BIGROOT,:BRIGHTPOWDER,:CHOICEBAND,:CHOICESCARF, + :CHOICESPECS,:DESTINYKNOT,:DISCOUNTCOUPON,:EXPERTBELT,:FOCUSBAND, + :FOCUSSASH,:LAGGINGTAIL,:LEFTOVERS,:MENTALHERB,:METALPOWDER, + :MUSCLEBAND,:POWERHERB,:QUICKPOWDER,:REAPERCLOTH,:REDCARD, + :RINGTARGET,:SHEDSHELL,:SILKSCARF,:SILVERPOWDER,:SMOOTHROCK, + :SOFTSAND,:SOOTHEBELL,:WHITEHERB,:WIDELENS,:WISEGLASSES,:ZOOMLENS, + # Terrain seeds + :ELECTRICSEED,:GRASSYSEED,:MISTYSEED,:PSYCHICSEED, + # Nectar + :PINKNECTAR,:PURPLENECTAR,:REDNECTAR,:YELLOWNECTAR, + # Incenses + :FULLINCENSE,:LAXINCENSE,:LUCKINCENSE,:ODDINCENSE,:PUREINCENSE, + :ROCKINCENSE,:ROSEINCENSE,:SEAINCENSE,:WAVEINCENSE, + # Scarves + :BLUESCARF,:GREENSCARF,:PINKSCARF,:REDSCARF,:YELLOWSCARF, + # Mints + :LONELYMINT, :ADAMANTMINT, :NAUGHTYMINT, :BRAVEMINT, :BOLDMINT, + :IMPISHMINT, :LAXMINT, :RELAXEDMINT, :MODESTMINT, :MILDMINT, + :RASHMINT, :QUIETMINT, :CALMMINT, :GENTLEMINT, :CAREFULMINT, + :SASSYMINT, :TIMIDMINT, :HASTYMINT, :JOLLYMINT, :NAIVEMINT, + :SERIOUSMINT, + # Sweets + :STRAWBERRYSWEET, :LOVESWEET, :BERRYSWEET, :CLOVERSWEET, + :FLOWERSWEET, :STARSWEET, :RIBBONSWEET + ] + } + return 0 if !pkmn.item + return 10 if pkmn.item.is_berry? + return 80 if pkmn.item.is_mega_stone? + if pkmn.item.is_TR? + ret = GameData::Move.get(pkmn.item.move).base_damage + ret = 10 if ret < 10 + return ret + end + fling_powers.each do |power,items| + return power if items.include?(pkmn.item_id) + end + return 10 + when "PowerHigherWithUserHP" + return [150 * pkmn.hp / pkmn.totalhp, 1].max + when "PowerLowerWithUserHP" + n = 48 * pkmn.hp / pkmn.totalhp + return 200 if n < 2 + return 150 if n < 5 + return 100 if n < 10 + return 80 if n < 17 + return 40 if n < 33 + return 20 + when "PowerHigherWithUserHappiness" + return [(pkmn.happiness * 2 / 5).floor, 1].max + when "PowerLowerWithUserHappiness" + return [((255 - pkmn.happiness) * 2 / 5).floor, 1].max + when "PowerHigherWithLessPP" + dmgs = [200, 80, 60, 50, 40] + ppLeft = [[(move&.pp || @total_pp) - 1, 0].max, dmgs.length - 1].min + return dmgs[ppLeft] + end +=end + return @base_damage + end + + def display_category(pkmn, move = nil); return @category; end + def display_accuracy(pkmn, move = nil); return @accuracy; end -# @deprecated This alias is slated to be removed in v20. -def pbIsHiddenMove?(move) - Deprecation.warn_method('pbIsHiddenMove?', 'v20', 'GameData::Move.get(move).hidden_move?') - return GameData::Move.get(move).hidden_move? + def convert_move_data(data) + new_code = data[:function_code] + case data[:function_code] + when "000" then new_code = "None" + when "001" then new_code = "DoesNothingUnusableInGravity" + when "002" then new_code = "Struggle" + when "003" + if data[:id] == :RELICSONG + new_code = "SleepTargetChangeUserMeloettaForm" + elsif data[:id] == :DARKVOID && Settings::MECHANICS_GENERATION >= 7 + new_code = "SleepTargetIfUserDarkrai" + else + new_code = "SleepTarget" + end + when "004" then new_code = "SleepTargetNextTurn" + when "005" then new_code = "PoisonTarget" + when "006" then new_code = "BadPoisonTarget" + when "007" + if data[:id] == :THUNDERWAVE + new_code = "ParalyzeTargetIfNotTypeImmune" + else + new_code = "ParalyzeTarget" + end + when "008" then new_code = "ParalyzeTargetAlwaysHitsInRainHitsTargetInSky" + when "009" then new_code = "ParalyzeFlinchTarget" + when "00A" then new_code = "BurnTarget" + when "00B" then new_code = "BurnFlinchTarget" + when "00C" then new_code = "FreezeTarget" + when "00D" then new_code = "FreezeTargetAlwaysHitsInHail" + when "00E" then new_code = "FreezeFlinchTarget" + when "00F", "010" then new_code = "FlinchTarget" + when "011" then new_code = "FlinchTargetFailsIfUserNotAsleep" + when "012" then new_code = "FlinchTargetFailsIfNotUserFirstTurn" + when "013", "014" then new_code = "ConfuseTarget" + when "015" then new_code = "ConfuseTargetAlwaysHitsInRainHitsTargetInSky" + when "016" then new_code = "AttractTarget" + when "017" then new_code = "ParalyzeBurnOrFreezeTarget" + when "018" then new_code = "CureUserBurnPoisonParalysis" + when "019" then new_code = "CureUserPartyStatus" + when "01A" then new_code = "StartUserSideImmunityToInflictedStatus" + when "01B" then new_code = "GiveUserStatusToTarget" + when "01C" then new_code = "RaiseUserAttack1" + when "01D" then new_code = "RaiseUserDefense1" + when "01E" then new_code = "RaiseUserDefense1CurlUpUser" + when "01F" then new_code = "RaiseUserSpeed1" + when "020" then new_code = "RaiseUserSpAtk1" + when "021" then new_code = "RaiseUserSpDef1PowerUpElectricMove" + when "022" then new_code = "RaiseUserEvasion1" + when "023" then new_code = "RaiseUserCriticalHitRate2" + when "024" then new_code = "RaiseUserAtkDef1" + when "025" then new_code = "RaiseUserAtkDefAcc1" + when "026" then new_code = "RaiseUserAtkSpd1" + when "027" then new_code = "RaiseUserAtkSpAtk1" + when "028" then new_code = "RaiseUserAtkSpAtk1Or2InSun" + when "029" then new_code = "RaiseUserAtkAcc1" + when "02A" then new_code = "RaiseUserDefSpDef1" + when "02B" then new_code = "RaiseUserSpAtkSpDefSpd1" + when "02C" then new_code = "RaiseUserSpAtkSpDef1" + when "02D" then new_code = "RaiseUserMainStats1" + when "02E" then new_code = "RaiseUserAttack2" + when "02F" then new_code = "RaiseUserDefense2" + when "030" then new_code = "RaiseUserSpeed2" + when "031" then new_code = "RaiseUserSpeed2LowerUserWeight" + when "032" then new_code = "RaiseUserSpAtk2" + when "033" then new_code = "RaiseUserSpDef2" + when "034" then new_code = "RaiseUserEvasion2MinimizeUser" + when "035" then new_code = "LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2" + when "036" then new_code = "RaiseUserAtk1Spd2" + when "037" then new_code = "RaiseTargetRandomStat2" + when "038" then new_code = "RaiseUserDefense3" + when "039" then new_code = "RaiseUserSpAtk3" + when "03A" then new_code = "MaxUserAttackLoseHalfOfTotalHP" + when "03B" then new_code = "LowerUserAtkDef1" + when "03C" then new_code = "LowerUserDefSpDef1" + when "03D" then new_code = "LowerUserDefSpDefSpd1" + when "03E" then new_code = "LowerUserSpeed1" + when "03F" then new_code = "LowerUserSpAtk2" + when "040" then new_code = "RaiseTargetSpAtk1ConfuseTarget" + when "041" then new_code = "RaiseTargetAttack2ConfuseTarget" + when "042" then new_code = "LowerTargetAttack1" + when "043" then new_code = "LowerTargetDefense1" + when "044" + if data[:id] == :BULLDOZE + new_code = "LowerTargetSpeed1WeakerInGrassyTerrain" + else + new_code = "LowerTargetSpeed1" + end + when "045" then new_code = "LowerTargetSpAtk1" + when "046" then new_code = "LowerTargetSpDef1" + when "047" then new_code = "LowerTargetAccuracy1" + when "048" + if data[:id] == :SWEETSCENT && Settings::MECHANICS_GENERATION >= 6 + new_code = "LowerTargetEvasion2" + else + new_code = "LowerTargetEvasion1" + end + when "049" then new_code = "LowerTargetEvasion1RemoveSideEffects" + when "04A" then new_code = "LowerTargetAtkDef1" + when "04B" then new_code = "LowerTargetAttack2" + when "04C" then new_code = "LowerTargetDefense2" + when "04D" then new_code = "LowerTargetSpeed2" + when "04E" then new_code = "LowerTargetSpAtk2IfCanAttract" + when "04F" then new_code = "LowerTargetSpDef2" + when "050" then new_code = "ResetTargetStatStages" + when "051" then new_code = "ResetAllBattlersStatStages" + when "052" then new_code = "UserTargetSwapAtkSpAtkStages" + when "053" then new_code = "UserTargetSwapDefSpDefStages" + when "054" then new_code = "UserTargetSwapStatStages" + when "055" then new_code = "UserCopyTargetStatStages" + when "056" then new_code = "StartUserSideImmunityToStatStageLowering" + when "057" then new_code = "UserSwapBaseAtkDef" + when "058" then new_code = "UserTargetAverageBaseAtkSpAtk" + when "059" then new_code = "UserTargetAverageBaseDefSpDef" + when "05A" then new_code = "UserTargetAverageHP" + when "05B" then new_code = "StartUserSideDoubleSpeed" + when "05C" then new_code = "ReplaceMoveThisBattleWithTargetLastMoveUsed" + when "05D" then new_code = "ReplaceMoveWithTargetLastMoveUsed" + when "05E" then new_code = "SetUserTypesToUserMoveType" + when "05F" then new_code = "SetUserTypesToResistLastAttack" + when "060" then new_code = "SetUserTypesBasedOnEnvironment" + when "061" then new_code = "SetTargetTypesToWater" + when "062" then new_code = "SetUserTypesToTargetTypes" + when "063" then new_code = "SetTargetAbilityToSimple" + when "064" then new_code = "SetTargetAbilityToInsomnia" + when "065" then new_code = "SetUserAbilityToTargetAbility" + when "066" then new_code = "SetTargetAbilityToUserAbility" + when "067" then new_code = "UserTargetSwapAbilities" + when "068" then new_code = "NegateTargetAbility" + when "069" then new_code = "TransformUserIntoTarget" + when "06A" then new_code = "FixedDamage20" + when "06B" then new_code = "FixedDamage40" + when "06C" then new_code = "FixedDamageHalfTargetHP" + when "06D" then new_code = "FixedDamageUserLevel" + when "06E" then new_code = "LowerTargetHPToUserHP" + when "06F" then new_code = "FixedDamageUserLevelRandom" + when "070" + if data[:id] == :FISSURE + new_code = "OHKOHitsUndergroundTarget" + elsif data[:id] == :SHEERCOLD && Settings::MECHANICS_GENERATION >= 7 + new_code = "OHKOIce" + else + new_code = "OHKO" + end + when "071" then new_code = "CounterPhysicalDamage" + when "072" then new_code = "CounterSpecialDamage" + when "073" then new_code = "CounterDamagePlusHalf" + when "074" then new_code = "DamageTargetAlly" + when "075" then new_code = "DoublePowerIfTargetUnderwater" + when "076" then new_code = "DoublePowerIfTargetUnderground" + when "077" then new_code = "DoublePowerIfTargetInSky" + when "078" then new_code = "FlinchTargetDoublePowerIfTargetInSky" + when "079" then new_code = "DoublePowerAfterFusionFlare" + when "07A" then new_code = "DoublePowerAfterFusionBolt" + when "07B" then new_code = "DoublePowerIfTargetPoisoned" + when "07C" then new_code = "DoublePowerIfTargetParalyzedCureTarget" + when "07D" then new_code = "DoublePowerIfTargetAsleepCureTarget" + when "07E" then new_code = "DoublePowerIfUserPoisonedBurnedParalyzed" + when "07F" then new_code = "DoublePowerIfTargetStatusProblem" + when "080" then new_code = "DoublePowerIfTargetHPLessThanHalf" + when "081" then new_code = "DoublePowerIfUserLostHPThisTurn" + when "082" then new_code = "DoublePowerIfTargetLostHPThisTurn" + when "083" then new_code = "UsedAfterAllyRoundWithDoublePower" + when "084" then new_code = "DoublePowerIfTargetActed" + when "085" then new_code = "DoublePowerIfAllyFaintedLastTurn" + when "086" then new_code = "DoublePowerIfUserHasNoItem" + when "087" then new_code = "TypeAndPowerDependOnWeather" + when "088" then new_code = "PursueSwitchingFoe" + when "089" then new_code = "PowerHigherWithUserHappiness" + when "08A" then new_code = "PowerLowerWithUserHappiness" + when "08B" then new_code = "PowerHigherWithUserHP" + when "08C" then new_code = "PowerHigherWithTargetHP" + when "08D" then new_code = "PowerHigherWithTargetFasterThanUser" + when "08E" then new_code = "PowerHigherWithUserPositiveStatStages" + when "08F" then new_code = "PowerHigherWithTargetPositiveStatStages" + when "090" then new_code = "TypeDependsOnUserIVs" + when "091" then new_code = "PowerHigherWithConsecutiveUse" + when "092" then new_code = "PowerHigherWithConsecutiveUseOnUserSide" + when "093" then new_code = "StartRaiseUserAtk1WhenDamaged" + when "094" then new_code = "RandomlyDamageOrHealTarget" + when "095" then new_code = "RandomPowerDoublePowerIfTargetUnderground" + when "096" then new_code = "TypeAndPowerDependOnUserBerry" + when "097" then new_code = "PowerHigherWithLessPP" + when "098" then new_code = "PowerLowerWithUserHP" + when "099" then new_code = "PowerHigherWithUserFasterThanTarget" + when "09A" then new_code = "PowerHigherWithTargetWeight" + when "09B" then new_code = "PowerHigherWithUserHeavierThanTarget" + when "09C" then new_code = "PowerUpAllyMove" + when "09D" then new_code = "StartWeakenElectricMoves" + when "09E" then new_code = "StartWeakenFireMoves" + when "09F" + case data[:id] + when :MULTIATTACK + new_code = "TypeDependsOnUserMemory" + when :TECHNOBLAST + new_code = "TypeDependsOnUserDrive" + else + new_code = "TypeDependsOnUserPlate" + end + when "0A0" then new_code = "AlwaysCriticalHit" + when "0A1" then new_code = "StartPreventCriticalHitsAgainstUserSide" + when "0A2" then new_code = "StartWeakenPhysicalDamageAgainstUserSide" + when "0A3" then new_code = "StartWeakenSpecialDamageAgainstUserSide" + when "0A4" then new_code = "EffectDependsOnEnvironment" + when "0A5" + new_code = "None" + data[:accuracy] = 0 + when "0A6" then new_code = "EnsureNextMoveAlwaysHits" + when "0A7" then new_code = "StartNegateTargetEvasionStatStageAndGhostImmunity" + when "0A8" then new_code = "StartNegateTargetEvasionStatStageAndDarkImmunity" + when "0A9" then new_code = "IgnoreTargetDefSpDefEvaStatStages" + when "0AA" then new_code = "ProtectUser" + when "0AB" then new_code = "ProtectUserSideFromPriorityMoves" + when "0AC" then new_code = "ProtectUserSideFromMultiTargetDamagingMoves" + when "0AD" then new_code = "RemoveProtections" + when "0AE" then new_code = "UseLastMoveUsedByTarget" + when "0AF" then new_code = "UseLastMoveUsed" + when "0B0" then new_code = "UseMoveTargetIsAboutToUse" + when "0B1" then new_code = "BounceBackProblemCausingStatusMoves" + when "0B2" then new_code = "StealAndUseBeneficialStatusMove" + when "0B3" then new_code = "UseMoveDependingOnEnvironment" + when "0B4" then new_code = "UseRandomUserMoveIfAsleep" + when "0B5" then new_code = "UseRandomMoveFromUserParty" + when "0B6" then new_code = "UseRandomMove" + when "0B7" then new_code = "DisableTargetUsingSameMoveConsecutively" + when "0B8" then new_code = "DisableTargetMovesKnownByUser" + when "0B9" then new_code = "DisableTargetLastMoveUsed" + when "0BA" then new_code = "DisableTargetStatusMoves" + when "0BB" then new_code = "DisableTargetHealingMoves" + when "0BC" then new_code = "DisableTargetUsingDifferentMove" + when "0BD" then new_code = "HitTwoTimes" + when "0BE" then new_code = "HitTwoTimesPoisonTarget" + when "0BF" then new_code = "HitThreeTimesPowersUpWithEachHit" + when "0C0" + if data[:id] == :WATERSHURIKEN + new_code = "HitTwoToFiveTimesOrThreeForAshGreninja" + else + new_code = "HitTwoToFiveTimes" + end + when "0C1" then new_code = "HitOncePerUserTeamMember" + when "0C2" then new_code = "AttackAndSkipNextTurn" + when "0C3" then new_code = "TwoTurnAttack" + when "0C4" then new_code = "TwoTurnAttackOneTurnInSun" + when "0C5" then new_code = "TwoTurnAttackParalyzeTarget" + when "0C6" then new_code = "TwoTurnAttackBurnTarget" + when "0C7" then new_code = "TwoTurnAttackFlinchTarget" + when "0C8" then new_code = "TwoTurnAttackChargeRaiseUserDefense1" + when "0C9" then new_code = "TwoTurnAttackInvulnerableInSky" + when "0CA" then new_code = "TwoTurnAttackInvulnerableUnderground" + when "0CB" then new_code = "TwoTurnAttackInvulnerableUnderwater" + when "0CC" then new_code = "TwoTurnAttackInvulnerableInSkyParalyzeTarget" + when "0CD" then new_code = "TwoTurnAttackInvulnerableRemoveProtections" + when "0CE" then new_code = "TwoTurnAttackInvulnerableInSkyTargetCannotAct" + when "0CF" then new_code = "BindTarget" + when "0D0" then new_code = "BindTargetDoublePowerIfTargetUnderwater" + when "0D1" then new_code = "MultiTurnAttackPreventSleeping" + when "0D2" then new_code = "MultiTurnAttackConfuseUserAtEnd" + when "0D3" then new_code = "MultiTurnAttackPowersUpEachTurn" + when "0D4" then new_code = "MultiTurnAttackBideThenReturnDoubleDamage" + when "0D5" then new_code = "HealUserHalfOfTotalHP" + when "0D6" then new_code = "HealUserHalfOfTotalHPLoseFlyingTypeThisTurn" + when "0D7" then new_code = "HealUserPositionNextTurn" + when "0D8" then new_code = "HealUserDependingOnWeather" + when "0D9" then new_code = "HealUserFullyAndFallAsleep" + when "0DA" then new_code = "StartHealUserEachTurn" + when "0DB" then new_code = "StartHealUserEachTurnTrapUserInBattle" + when "0DC" then new_code = "StartLeechSeedTarget" + when "0DD" then new_code = "HealUserByHalfOfDamageDone" + when "0DE" then new_code = "HealUserByHalfOfDamageDoneIfTargetAsleep" + when "0DF" then new_code = "HealTargetHalfOfTotalHP" + when "0E0" then new_code = "UserFaintsExplosive" + when "0E1" then new_code = "UserFaintsFixedDamageUserHP" + when "0E2" then new_code = "UserFaintsLowerTargetAtkSpAtk2" + when "0E3" then new_code = "UserFaintsHealAndCureReplacement" + when "0E4" then new_code = "UserFaintsHealAndCureReplacementRestorePP" + when "0E5" then new_code = "StartPerishCountsForAllBattlers" + when "0E6" then new_code = "SetAttackerMovePPTo0IfUserFaints" + when "0E7" then new_code = "AttackerFaintsIfUserFaints" + when "0E8" then new_code = "UserEnduresFaintingThisTurn" + when "0E9" then new_code = "CannotMakeTargetFaint" + when "0EA" + if Settings::MECHANICS_GENERATION >= 8 + new_code = "SwitchOutUserStatusMove" + else + new_code = "FleeFromBattle" + end + when "0EB" then new_code = "SwitchOutTargetStatusMove" + when "0EC" then new_code = "SwitchOutTargetDamagingMove" + when "0ED" then new_code = "SwitchOutUserPassOnEffects" + when "0EE" then new_code = "SwitchOutUserDamagingMove" + when "0EF" then new_code = "TrapTargetInBattle" + when "0F0" then new_code = "RemoveTargetItem" + when "0F1" then new_code = "UserTakesTargetItem" + when "0F2" then new_code = "UserTargetSwapItems" + when "0F3" then new_code = "TargetTakesUserItem" + when "0F4" then new_code = "UserConsumeTargetBerry" + when "0F5" then new_code = "DestroyTargetBerryOrGem" + when "0F6" then new_code = "RestoreUserConsumedItem" + when "0F7" then new_code = "ThrowUserItemAtTarget" + when "0F8" then new_code = "StartTargetCannotUseItem" + when "0F9" then new_code = "StartNegateHeldItems" + when "0FA" then new_code = "RecoilQuarterOfDamageDealt" + when "0FB" then new_code = "RecoilThirdOfDamageDealt" + when "0FC" then new_code = "RecoilHalfOfDamageDealt" + when "0FD" then new_code = "RecoilThirdOfDamageDealtParalyzeTarget" + when "0FE" then new_code = "RecoilThirdOfDamageDealtBurnTarget" + when "0FF" then new_code = "StartSunWeather" + when "100" then new_code = "StartRainWeather" + when "101" then new_code = "StartSandstormWeather" + when "102" then new_code = "StartHailWeather" + when "103" then new_code = "AddSpikesToFoeSide" + when "104" then new_code = "AddToxicSpikesToFoeSide" + when "105" then new_code = "AddStealthRocksToFoeSide" + when "106" then new_code = "GrassPledge" + when "107" then new_code = "FirePledge" + when "108" then new_code = "WaterPledge" + when "109" then new_code = "AddMoneyGainedFromBattle" + when "10A" then new_code = "RemoveScreens" + when "10B" then new_code = "CrashDamageIfFailsUnusableInGravity" + when "10C" then new_code = "UserMakeSubstitute" + when "10D" then new_code = "CurseTargetOrLowerUserSpd1RaiseUserAtkDef1" + when "10E" then new_code = "LowerPPOfTargetLastMoveBy4" + when "10F" then new_code = "StartDamageTargetEachTurnIfTargetAsleep" + when "110" then new_code = "RemoveUserBindingAndEntryHazards" + when "111" then new_code = "AttackTwoTurnsLater" + when "112" then new_code = "UserAddStockpileRaiseDefSpDef1" + when "113" then new_code = "PowerDependsOnUserStockpile" + when "114" then new_code = "HealUserDependingOnUserStockpile" + when "115" then new_code = "FailsIfUserDamagedThisTurn" + when "116" then new_code = "FailsIfTargetActed" + when "117" then new_code = "RedirectAllMovesToUser" + when "118" then new_code = "StartGravity" + when "119" then new_code = "StartUserAirborne" + when "11A" then new_code = "StartTargetAirborneAndAlwaysHitByMoves" + when "11B" then new_code = "HitsTargetInSky" + when "11C" then new_code = "HitsTargetInSkyGroundsTarget" + when "11D" then new_code = "TargetActsNext" + when "11E" then new_code = "TargetActsLast" + when "11F" then new_code = "StartSlowerBattlersActFirst" + when "120" then new_code = "UserSwapsPositionsWithAlly" + when "121" then new_code = "UseTargetAttackInsteadOfUserAttack" + when "122" then new_code = "UseTargetDefenseInsteadOfTargetSpDef" + when "123" then new_code = "FailsUnlessTargetSharesTypeWithUser" + when "124" then new_code = "StartSwapAllBattlersBaseDefensiveStats" + when "125" then new_code = "FailsIfUserHasUnusedMove" + when "126" then new_code = "None" + when "127" then new_code = "ParalyzeTarget" + when "128" then new_code = "BurnTarget" + when "129" then new_code = "FreezeTarget" + when "12A" then new_code = "ConfuseTarget" + when "12B" then new_code = "LowerTargetDefense2" + when "12C" then new_code = "LowerTargetEvasion2" + when "12D" then new_code = "DoublePowerIfTargetUnderwater" + when "12E" then new_code = "AllBattlersLoseHalfHPUserSkipsNextTurn" + when "12F" then new_code = "TrapTargetInBattle" + when "130" then new_code = "UserLosesHalfHP" + when "131" then new_code = "StartShadowSkyWeather" + when "132" then new_code = "RemoveAllScreens" + when "133" then new_code = "DoesNothingFailsIfNoAlly" + when "134" then new_code = "DoesNothingCongratulations" + when "135" then new_code = "FreezeTargetSuperEffectiveAgainstWater" + when "136" then new_code = "RaiseUserDefense2" + when "137" then new_code = "RaisePlusMinusUserAndAlliesDefSpDef1" + when "138" then new_code = "RaiseTargetSpDef1" + when "139" then new_code = "LowerTargetAttack1BypassSubstitute" + when "13A" then new_code = "LowerTargetAtkSpAtk1" + when "13B" then new_code = "HoopaRemoveProtectionsBypassSubstituteLowerUserDef1" + when "13C" then new_code = "LowerTargetSpAtk1" + when "13D" then new_code = "LowerTargetSpAtk2" + when "13E" then new_code = "RaiseGroundedGrassBattlersAtkSpAtk1" + when "13F" then new_code = "RaiseGrassBattlersDef1" + when "140" then new_code = "LowerPoisonedTargetAtkSpAtkSpd1" + when "141" then new_code = "InvertTargetStatStages" + when "142" then new_code = "AddGhostTypeToTarget" + when "143" then new_code = "AddGrassTypeToTarget" + when "144" then new_code = "EffectivenessIncludesFlyingType" + when "145" then new_code = "TargetMovesBecomeElectric" + when "146" then new_code = "NormalMovesBecomeElectric" + when "147" then new_code = "RemoveProtectionsBypassSubstitute" + when "148" then new_code = "TargetNextFireMoveDamagesTarget" + when "149" then new_code = "ProtectUserSideFromDamagingMovesIfUserFirstTurn" + when "14A" then new_code = "ProtectUserSideFromStatusMoves" + when "14B" then new_code = "ProtectUserFromDamagingMovesKingsShield" + when "14C" then new_code = "ProtectUserFromTargetingMovesSpikyShield" + when "14D" then new_code = "TwoTurnAttackInvulnerableRemoveProtections" + when "14E" then new_code = "TwoTurnAttackRaiseUserSpAtkSpDefSpd2" + when "14F" then new_code = "HealUserByThreeQuartersOfDamageDone" + when "150" then new_code = "RaiseUserAttack3IfTargetFaints" + when "151" then new_code = "LowerTargetAtkSpAtk1SwitchOutUser" + when "152" then new_code = "TrapAllBattlersInBattleForOneTurn" + when "153" then new_code = "AddStickyWebToFoeSide" + when "154" then new_code = "StartElectricTerrain" + when "155" then new_code = "StartGrassyTerrain" + when "156" then new_code = "StartMistyTerrain" + when "157" then new_code = "DoubleMoneyGainedFromBattle" + when "158" then new_code = "FailsIfUserNotConsumedBerry" + when "159" then new_code = "PoisonTargetLowerTargetSpeed1" + when "15A" then new_code = "CureTargetBurn" + when "15B" then new_code = "CureTargetStatusHealUserHalfOfTotalHP" + when "15C" then new_code = "RaisePlusMinusUserAndAlliesAtkSpAtk1" + when "15D" then new_code = "UserStealTargetPositiveStatStages" + when "15E" then new_code = "EnsureNextCriticalHit" + when "15F" then new_code = "LowerUserDefense1" + when "160" then new_code = "HealUserByTargetAttackLowerTargetAttack1" + when "161" then new_code = "UserTargetSwapBaseSpeed" + when "162" then new_code = "UserLosesFireType" + when "163" then new_code = "IgnoreTargetAbility" + when "164" then new_code = "CategoryDependsOnHigherDamageIgnoreTargetAbility" + when "165" then new_code = "NegateTargetAbilityIfTargetActed" + when "166" then new_code = "DoublePowerIfUserLastMoveFailed" + when "167" then new_code = "StartWeakenDamageAgainstUserSideIfHail" + when "168" then new_code = "ProtectUserBanefulBunker" + when "169" then new_code = "TypeIsUserFirstType" + when "16A" then new_code = "RedirectAllMovesToTarget" + when "16B" then new_code = "TargetUsesItsLastUsedMoveAgain" + when "16C" then new_code = "DisableTargetSoundMoves" + when "16D" then new_code = "HealUserDependingOnSandstorm" + when "16E" then new_code = "HealTargetDependingOnGrassyTerrain" + when "16F" then new_code = "HealAllyOrDamageFoe" + when "170" then new_code = "UserLosesHalfOfTotalHPExplosive" + when "171" then new_code = "UsedAfterUserTakesPhysicalDamage" + when "172" then new_code = "BurnAttackerBeforeUserActs" + when "173" then new_code = "StartPsychicTerrain" + when "174" then new_code = "FailsIfNotUserFirstTurn" + when "175" then new_code = "HitTwoTimesFlinchTarget" + end + data[:function_code] = new_code + return data + end + end end diff --git a/Data/Scripts/010_Data/002_PBS data/006_Item.rb b/Data/Scripts/010_Data/002_PBS data/006_Item.rb index 2389c13c2c..2d89838f5c 100644 --- a/Data/Scripts/010_Data/002_PBS data/006_Item.rb +++ b/Data/Scripts/010_Data/002_PBS data/006_Item.rb @@ -1,21 +1,38 @@ module GameData class Item attr_reader :id - attr_reader :id_number attr_reader :real_name attr_reader :real_name_plural attr_reader :pocket attr_reader :price + attr_reader :sell_price attr_reader :real_description attr_reader :field_use attr_reader :battle_use - attr_reader :type + attr_reader :consumable + attr_reader :flags attr_reader :move DATA = {} DATA_FILENAME = "items.dat" - extend ClassMethods + SCHEMA = { + "Name" => [:name, "s"], + "NamePlural" => [:name_plural, "s"], + "Pocket" => [:pocket, "v"], + "Price" => [:price, "u"], + "SellPrice" => [:sell_price, "u"], + "Description" => [:description, "q"], + "FieldUse" => [:field_use, "e", { "OnPokemon" => 1, "Direct" => 2, "TM" => 3, + "HM" => 4, "TR" => 5 }], + "BattleUse" => [:battle_use, "e", { "OnPokemon" => 1, "OnMove" => 2, "OnBattler" => 3, + "OnFoe" => 4, "Direct" => 5 }], + "Consumable" => [:consumable, "b"], + "Flags" => [:flags, "*s"], + "Move" => [:move, "e", :Move] + } + + extend ClassMethodsSymbols include InstanceMethods def self.icon_filename(item) @@ -65,49 +82,55 @@ def self.mail_filename(item) def initialize(hash) @id = hash[:id] - @id_number = hash[:id_number] || -1 @real_name = hash[:name] || "Unnamed" @real_name_plural = hash[:name_plural] || "Unnamed" @pocket = hash[:pocket] || 1 @price = hash[:price] || 0 + @sell_price = hash[:sell_price] || (@price / 2) @real_description = hash[:description] || "???" @field_use = hash[:field_use] || 0 @battle_use = hash[:battle_use] || 0 - @type = hash[:type] || 0 + @flags = hash[:flags] || [] + @consumable = hash[:consumable] + @consumable = !is_important? if @consumable.nil? @move = hash[:move] end # @return [String] the translated name of this item def name - return pbGetMessage(MessageTypes::Items, @id_number) + return pbGetMessageFromHash(MessageTypes::Items, @real_name) end # @return [String] the translated plural version of the name of this item def name_plural - return pbGetMessage(MessageTypes::ItemPlurals, @id_number) + return pbGetMessageFromHash(MessageTypes::ItemPlurals, @real_name_plural) end # @return [String] the translated description of this item def description - return pbGetMessage(MessageTypes::ItemDescriptions, @id_number) + return pbGetMessageFromHash(MessageTypes::ItemDescriptions, @real_description) + end + + def has_flag?(flag) + return @flags.any? { |f| f.downcase == flag.downcase } end def is_TM?; return @field_use == 3; end def is_HM?; return @field_use == 4; end - def is_TR?; return @field_use == 6; end + def is_TR?; return @field_use == 5; end def is_machine?; return is_TM? || is_HM? || is_TR?; end - def is_mail?; return @type == 1 || @type == 2; end - def is_icon_mail?; return @type == 2; end - def is_poke_ball?; return @type == 3 || @type == 4; end - def is_snag_ball?; return @type == 3 || (@type == 4 && $Trainer.has_snag_machine); end - def is_berry?; return @type == 5; end - def is_key_item?; return @type == 6; end - def is_evolution_stone?; return @type == 7; end - def is_fossil?; return @type == 8; end - def is_apricorn?; return @type == 9; end - def is_gem?; return @type == 10; end - def is_mulch?; return @type == 11; end - def is_mega_stone?; return @type == 12; end # Does NOT include Red Orb/Blue Orb + def is_mail?; return has_flag?("Mail") || has_flag?("IconMail"); end + def is_icon_mail?; return has_flag?("IconMail"); end + def is_poke_ball?; return has_flag?("PokeBall") || has_flag?("SnagBall"); end + def is_snag_ball?; return has_flag?("SnagBall") || (is_poke_ball? && $player.has_snag_machine); end + def is_berry?; return has_flag?("Berry"); end + def is_key_item?; return has_flag?("KeyItem"); end + def is_evolution_stone?; return has_flag?("EvolutionStone"); end + def is_fossil?; return has_flag?("Fossil"); end + def is_apricorn?; return has_flag?("Apricorn"); end + def is_gem?; return has_flag?("TypeGem"); end + def is_mulch?; return has_flag?("Mulch"); end + def is_mega_stone?; return has_flag?("MegaStone"); end # Does NOT include Red Orb/Blue Orb def is_important? return true if is_key_item? || is_HM? || is_TM? @@ -116,11 +139,15 @@ def is_important? def can_hold?; return !is_important?; end + def consumed_after_use? + return !is_important? && @consumable + end + def unlosable?(species, ability) return false if species == :ARCEUS && ability != :MULTITYPE return false if species == :SILVALLY && ability != :RKSSYSTEM combos = { - :ARCEUS => [:FISTPLATE, :FIGHTINIUMZ, + :ARCEUS => [:FISTPLATE, :FIGHTINIUMZ, :SKYPLATE, :FLYINIUMZ, :TOXICPLATE, :POISONIUMZ, :EARTHPLATE, :GROUNDIUMZ, @@ -137,7 +164,7 @@ def unlosable?(species, ability) :DRACOPLATE, :DRAGONIUMZ, :DREADPLATE, :DARKINIUMZ, :PIXIEPLATE, :FAIRIUMZ], - :SILVALLY => [:FIGHTINGMEMORY, + :SILVALLY => [:FIGHTINGMEMORY, :FLYINGMEMORY, :POISONMEMORY, :GROUNDMEMORY, @@ -154,159 +181,14 @@ def unlosable?(species, ability) :DRAGONMEMORY, :DARKMEMORY, :FAIRYMEMORY], - :GIRATINA => [:GRISEOUSORB], - :GENESECT => [:BURNDRIVE, :CHILLDRIVE, :DOUSEDRIVE, :SHOCKDRIVE], - :KYOGRE => [:BLUEORB], - :GROUDON => [:REDORB] + :GIRATINA => [:GRISEOUSORB], + :GENESECT => [:BURNDRIVE, :CHILLDRIVE, :DOUSEDRIVE, :SHOCKDRIVE], + :KYOGRE => [:BLUEORB], + :GROUDON => [:REDORB], + :ZACIAN => [:RUSTEDSWORD], + :ZAMAZENTA => [:RUSTEDSHIELD] } - return combos[species] && combos[species].include?(@id) + return combos[species]&.include?(@id) end end end - -#=============================================================================== -# Deprecated methods -#=============================================================================== -# @deprecated This alias is slated to be removed in v20. -def pbGetPocket(item) - Deprecation.warn_method('pbGetPocket', 'v20', 'GameData::Item.get(item).pocket') - return GameData::Item.get(item).pocket -end - -# @deprecated This alias is slated to be removed in v20. -def pbGetPrice(item) - Deprecation.warn_method('pbGetPrice', 'v20', 'GameData::Item.get(item).price') - return GameData::Item.get(item).price -end - -# @deprecated This alias is slated to be removed in v20. -def pbGetMachine(item) - Deprecation.warn_method('pbGetMachine', 'v20', 'GameData::Item.get(item).move') - return GameData::Item.get(item).move -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsTechnicalMachine?(item) - Deprecation.warn_method('pbIsTechnicalMachine?', 'v20', 'GameData::Item.get(item).is_TM?') - return GameData::Item.get(item).is_TM? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsHiddenMachine?(item) - Deprecation.warn_method('pbIsHiddenMachine?', 'v20', 'GameData::Item.get(item).is_HM?') - return GameData::Item.get(item).is_HM? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsMachine?(item) - Deprecation.warn_method('pbIsMachine?', 'v20', 'GameData::Item.get(item).is_machine?') - return GameData::Item.get(item).is_machine? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsMail?(item) - Deprecation.warn_method('pbIsMail?', 'v20', 'GameData::Item.get(item).is_mail?') - return GameData::Item.get(item).is_mail? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsMailWithPokemonIcons?(item) - Deprecation.warn_method('pbIsMailWithPokemonIcons?', 'v20', 'GameData::Item.get(item).is_icon_mail?') - return GameData::Item.get(item).is_icon_mail? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsPokeBall?(item) - Deprecation.warn_method('pbIsPokeBall?', 'v20', 'GameData::Item.get(item).is_poke_ball?') - return GameData::Item.get(item).is_poke_ball? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsSnagBall?(item) - Deprecation.warn_method('pbIsSnagBall?', 'v20', 'GameData::Item.get(item).is_snag_ball?') - return GameData::Item.get(item).is_snag_ball? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsBerry?(item) - Deprecation.warn_method('pbIsBerry?', 'v20', 'GameData::Item.get(item).is_berry?') - return GameData::Item.get(item).is_berry? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsKeyItem?(item) - Deprecation.warn_method('pbIsKeyItem?', 'v20', 'GameData::Item.get(item).is_key_item?') - return GameData::Item.get(item).is_key_item? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsEvolutionStone?(item) - Deprecation.warn_method('pbIsEvolutionStone?', 'v20', 'GameData::Item.get(item).is_evolution_stone?') - return GameData::Item.get(item).is_evolution_stone? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsFossil?(item) - Deprecation.warn_method('pbIsFossil?', 'v20', 'GameData::Item.get(item).is_fossil?') - return GameData::Item.get(item).is_fossil? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsApricorn?(item) - Deprecation.warn_method('pbIsApricorn?', 'v20', 'GameData::Item.get(item).is_apricorn?') - return GameData::Item.get(item).is_apricorn? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsGem?(item) - Deprecation.warn_method('pbIsGem?', 'v20', 'GameData::Item.get(item).is_gem?') - return GameData::Item.get(item).is_gem? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsMulch?(item) - Deprecation.warn_method('pbIsMulch?', 'v20', 'GameData::Item.get(item).is_mulch?') - return GameData::Item.get(item).is_mulch? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsMegaStone?(item) - Deprecation.warn_method('pbIsMegaStone?', 'v20', 'GameData::Item.get(item).is_mega_stone?') - return GameData::Item.get(item).is_mega_stone? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsImportantItem?(item) - Deprecation.warn_method('pbIsImportantItem?', 'v20', 'GameData::Item.get(item).is_important?') - return GameData::Item.get(item).is_important? -end - -# @deprecated This alias is slated to be removed in v20. -def pbCanHoldItem?(item) - Deprecation.warn_method('pbCanHoldItem?', 'v20', 'GameData::Item.get(item).can_hold?') - return GameData::Item.get(item).can_hold? -end - -# @deprecated This alias is slated to be removed in v20. -def pbIsUnlosableItem?(check_item, species, ability) - Deprecation.warn_method('pbIsUnlosableItem?', 'v20', 'GameData::Item.get(item).unlosable?') - return GameData::Item.get(check_item).unlosable?(species, ability) -end - -# @deprecated This alias is slated to be removed in v20. -def pbItemIconFile(item) - Deprecation.warn_method('pbItemIconFile', 'v20', 'GameData::Item.icon_filename(item)') - return GameData::Item.icon_filename(item) -end - -# @deprecated This alias is slated to be removed in v20. -def pbHeldItemIconFile(item) - Deprecation.warn_method('pbHeldItemIconFile', 'v20', 'GameData::Item.held_icon_filename(item)') - return GameData::Item.held_icon_filename(item) -end - -# @deprecated This alias is slated to be removed in v20. -def pbMailBackFile(item) - Deprecation.warn_method('pbMailBackFile', 'v20', 'GameData::Item.mail_filename(item)') - return GameData::Item.mail_filename(item) -end diff --git a/Data/Scripts/010_Data/002_PBS data/007_BerryPlant.rb b/Data/Scripts/010_Data/002_PBS data/007_BerryPlant.rb index ad47a4e758..054e84be7e 100644 --- a/Data/Scripts/010_Data/002_PBS data/007_BerryPlant.rb +++ b/Data/Scripts/010_Data/002_PBS data/007_BerryPlant.rb @@ -1,36 +1,41 @@ module GameData class BerryPlant attr_reader :id - attr_reader :id_number attr_reader :hours_per_stage attr_reader :drying_per_hour - attr_reader :minimum_yield - attr_reader :maximum_yield + attr_reader :yield DATA = {} DATA_FILENAME = "berry_plants.dat" - NUMBER_OF_REPLANTS = 9 + SCHEMA = { + "HoursPerStage" => [:hours_per_stage, "v"], + "DryingPerHour" => [:drying_per_hour, "u"], + "Yield" => [:yield, "uv"] + } - extend ClassMethods + NUMBER_OF_REPLANTS = 9 + NUMBER_OF_GROWTH_STAGES = 4 + NUMBER_OF_FULLY_GROWN_STAGES = 4 + WATERING_CANS = [:SPRAYDUCK, :SQUIRTBOTTLE, :WAILMERPAIL, :SPRINKLOTAD] + + extend ClassMethodsSymbols include InstanceMethods def initialize(hash) @id = hash[:id] - @id_number = hash[:id_number] || -1 @hours_per_stage = hash[:hours_per_stage] || 3 @drying_per_hour = hash[:drying_per_hour] || 15 - @minimum_yield = hash[:minimum_yield] || 2 - @maximum_yield = hash[:maximum_yield] || 5 + @yield = hash[:yield] || [2, 5] + @yield.reverse! if @yield[1] < @yield[0] end - end -end -#=============================================================================== -# Deprecated methods -#=============================================================================== -# @deprecated This alias is slated to be removed in v20. -def pbGetBerryPlantData(item) - Deprecation.warn_method('pbGetBerryPlantData', 'v20', 'GameData::BerryPlant.get(item)') - return GameData::BerryPlant.get(item) + def minimum_yield + return @yield[0] + end + + def maximum_yield + return @yield[1] + end + end end diff --git a/Data/Scripts/010_Data/002_PBS data/008_Species.rb b/Data/Scripts/010_Data/002_PBS data/008_Species.rb index 31add04364..ee6176d831 100644 --- a/Data/Scripts/010_Data/002_PBS data/008_Species.rb +++ b/Data/Scripts/010_Data/002_PBS data/008_Species.rb @@ -1,7 +1,6 @@ module GameData class Species attr_reader :id - attr_reader :id_number attr_reader :species attr_reader :form attr_reader :real_name @@ -9,8 +8,7 @@ class Species attr_reader :real_category attr_reader :real_pokedex_entry attr_reader :pokedex_form - attr_reader :type1 - attr_reader :type2 + attr_reader :types attr_reader :base_stats attr_reader :evs attr_reader :base_exp @@ -29,6 +27,7 @@ class Species attr_reader :egg_groups attr_reader :hatch_steps attr_reader :incense + attr_reader :offspring attr_reader :evolutions attr_reader :height attr_reader :weight @@ -36,80 +35,92 @@ class Species attr_reader :shape attr_reader :habitat attr_reader :generation + attr_reader :flags attr_reader :mega_stone attr_reader :mega_move attr_reader :unmega_form attr_reader :mega_message - attr_accessor :back_sprite_x - attr_accessor :back_sprite_y - attr_accessor :front_sprite_x - attr_accessor :front_sprite_y - attr_accessor :front_sprite_altitude - attr_accessor :shadow_x - attr_accessor :shadow_size DATA = {} DATA_FILENAME = "species.dat" - extend ClassMethods + extend ClassMethodsSymbols include InstanceMethods - # @param species [Symbol, self, String, Integer] + # @param species [Symbol, self, String] # @param form [Integer] # @return [self, nil] def self.get_species_form(species, form) return nil if !species || !form - validate species => [Symbol, self, String, Integer] + validate species => [Symbol, self, String] validate form => Integer -# if other.is_a?(Integer) -# p "Please switch to symbols, thanks." -# end species = species.species if species.is_a?(self) - species = DATA[species].species if species.is_a?(Integer) species = species.to_sym if species.is_a?(String) trial = sprintf("%s_%d", species, form).to_sym species_form = (DATA[trial].nil?) ? species : trial return (DATA.has_key?(species_form)) ? DATA[species_form] : nil end + def self.each_species + DATA.each_value { |species| yield species if species.form == 0 } + end + + def self.species_count + ret = 0 + self.each_species { |species| ret += 1 } + return ret + end + def self.schema(compiling_forms = false) ret = { "FormName" => [0, "q"], - "Kind" => [0, "s"], + "Category" => [0, "s"], "Pokedex" => [0, "q"], - "Type1" => [0, "e", :Type], - "Type2" => [0, "e", :Type], + "Types" => [0, "eE", :Type, :Type], "BaseStats" => [0, "vvvvvv"], - "EffortPoints" => [0, "uuuuuu"], - "BaseEXP" => [0, "v"], - "Rareness" => [0, "u"], + "EVs" => [0, "*ev", :Stat], + "BaseExp" => [0, "v"], + "CatchRate" => [0, "u"], "Happiness" => [0, "u"], "Moves" => [0, "*ue", nil, :Move], "TutorMoves" => [0, "*e", :Move], "EggMoves" => [0, "*e", :Move], "Abilities" => [0, "*e", :Ability], - "HiddenAbility" => [0, "*e", :Ability], - "WildItemCommon" => [0, "e", :Item], - "WildItemUncommon" => [0, "e", :Item], - "WildItemRare" => [0, "e", :Item], - "Compatibility" => [0, "*e", :EggGroup], - "StepsToHatch" => [0, "v"], + "HiddenAbilities" => [0, "*e", :Ability], + "WildItemCommon" => [0, "*e", :Item], + "WildItemUncommon" => [0, "*e", :Item], + "WildItemRare" => [0, "*e", :Item], + "EggGroups" => [0, "*e", :EggGroup], + "HatchSteps" => [0, "v"], "Height" => [0, "f"], "Weight" => [0, "f"], "Color" => [0, "e", :BodyColor], - "Shape" => [0, "y", :BodyShape], + "Shape" => [0, "e", :BodyShape], "Habitat" => [0, "e", :Habitat], "Generation" => [0, "i"], + "Flags" => [0, "*s"], "BattlerPlayerX" => [0, "i"], "BattlerPlayerY" => [0, "i"], "BattlerEnemyX" => [0, "i"], "BattlerEnemyY" => [0, "i"], "BattlerAltitude" => [0, "i"], "BattlerShadowX" => [0, "i"], - "BattlerShadowSize" => [0, "u"] + "BattlerShadowSize" => [0, "u"], + # All properties below here are old names for some properties above. + # They will be removed in v21. + "Type1" => [0, "e", :Type], + "Type2" => [0, "e", :Type], + "Rareness" => [0, "u"], + "Compatibility" => [0, "*e", :EggGroup], + "Kind" => [0, "s"], + "BaseEXP" => [0, "v"], + "EffortPoints" => [0, "*ev", :Stat], + "HiddenAbility" => [0, "*e", :Ability], + "StepsToHatch" => [0, "v"] } if compiling_forms ret["PokedexForm"] = [0, "u"] + ret["Offspring"] = [0, "*e", :Species] ret["Evolutions"] = [0, "*ees", :Species, :Evolution, nil] ret["MegaStone"] = [0, "e", :Item] ret["MegaMove"] = [0, "e", :Move] @@ -119,107 +130,112 @@ def self.schema(compiling_forms = false) ret["InternalName"] = [0, "n"] ret["Name"] = [0, "s"] ret["GrowthRate"] = [0, "e", :GrowthRate] - ret["GenderRate"] = [0, "e", :GenderRatio] + ret["GenderRatio"] = [0, "e", :GenderRatio] ret["Incense"] = [0, "e", :Item] + ret["Offspring"] = [0, "*s"] ret["Evolutions"] = [0, "*ses", nil, :Evolution, nil] + # All properties below here are old names for some properties above. + # They will be removed in v21. + ret["GenderRate"] = [0, "e", :GenderRatio] end return ret end def initialize(hash) - @id = hash[:id] - @id_number = hash[:id_number] || -1 - @species = hash[:species] || @id - @form = hash[:form] || 0 - @real_name = hash[:name] || "Unnamed" - @real_form_name = hash[:form_name] - @real_category = hash[:category] || "???" - @real_pokedex_entry = hash[:pokedex_entry] || "???" - @pokedex_form = hash[:pokedex_form] || @form - @type1 = hash[:type1] || :NORMAL - @type2 = hash[:type2] || @type1 - @base_stats = hash[:base_stats] || {} - @evs = hash[:evs] || {} + @id = hash[:id] + @species = hash[:species] || @id + @form = hash[:form] || 0 + @real_name = hash[:name] || "Unnamed" + @real_form_name = hash[:form_name] + @real_category = hash[:category] || "???" + @real_pokedex_entry = hash[:pokedex_entry] || "???" + @pokedex_form = hash[:pokedex_form] || @form + @types = hash[:types] || [:NORMAL] + @base_stats = hash[:base_stats] || {} + @evs = hash[:evs] || {} GameData::Stat.each_main do |s| @base_stats[s.id] = 1 if !@base_stats[s.id] || @base_stats[s.id] <= 0 @evs[s.id] = 0 if !@evs[s.id] || @evs[s.id] < 0 end - @base_exp = hash[:base_exp] || 100 - @growth_rate = hash[:growth_rate] || :Medium - @gender_ratio = hash[:gender_ratio] || :Female50Percent - @catch_rate = hash[:catch_rate] || 255 - @happiness = hash[:happiness] || 70 - @moves = hash[:moves] || [] - @tutor_moves = hash[:tutor_moves] || [] - @egg_moves = hash[:egg_moves] || [] - @abilities = hash[:abilities] || [] - @hidden_abilities = hash[:hidden_abilities] || [] - @wild_item_common = hash[:wild_item_common] - @wild_item_uncommon = hash[:wild_item_uncommon] - @wild_item_rare = hash[:wild_item_rare] - @egg_groups = hash[:egg_groups] || [:Undiscovered] - @hatch_steps = hash[:hatch_steps] || 1 - @incense = hash[:incense] - @evolutions = hash[:evolutions] || [] - @height = hash[:height] || 1 - @weight = hash[:weight] || 1 - @color = hash[:color] || :Red - @shape = hash[:shape] || :Head - @habitat = hash[:habitat] || :None - @generation = hash[:generation] || 0 - @mega_stone = hash[:mega_stone] - @mega_move = hash[:mega_move] - @unmega_form = hash[:unmega_form] || 0 - @mega_message = hash[:mega_message] || 0 - @back_sprite_x = hash[:back_sprite_x] || 0 - @back_sprite_y = hash[:back_sprite_y] || 0 - @front_sprite_x = hash[:front_sprite_x] || 0 - @front_sprite_y = hash[:front_sprite_y] || 0 - @front_sprite_altitude = hash[:front_sprite_altitude] || 0 - @shadow_x = hash[:shadow_x] || 0 - @shadow_size = hash[:shadow_size] || 2 + @base_exp = hash[:base_exp] || 100 + @growth_rate = hash[:growth_rate] || :Medium + @gender_ratio = hash[:gender_ratio] || :Female50Percent + @catch_rate = hash[:catch_rate] || 255 + @happiness = hash[:happiness] || 70 + @moves = hash[:moves] || [] + @tutor_moves = hash[:tutor_moves] || [] + @egg_moves = hash[:egg_moves] || [] + @abilities = hash[:abilities] || [] + @hidden_abilities = hash[:hidden_abilities] || [] + @wild_item_common = hash[:wild_item_common] || [] + @wild_item_uncommon = hash[:wild_item_uncommon] || [] + @wild_item_rare = hash[:wild_item_rare] || [] + @egg_groups = hash[:egg_groups] || [:Undiscovered] + @hatch_steps = hash[:hatch_steps] || 1 + @incense = hash[:incense] + @offspring = hash[:offspring] || [] + @evolutions = hash[:evolutions] || [] + @height = hash[:height] || 1 + @weight = hash[:weight] || 1 + @color = hash[:color] || :Red + @shape = hash[:shape] || :Head + @habitat = hash[:habitat] || :None + @generation = hash[:generation] || 0 + @flags = hash[:flags] || [] + @mega_stone = hash[:mega_stone] + @mega_move = hash[:mega_move] + @unmega_form = hash[:unmega_form] || 0 + @mega_message = hash[:mega_message] || 0 end # @return [String] the translated name of this species def name - return pbGetMessage(MessageTypes::Species, @id_number) + return pbGetMessageFromHash(MessageTypes::Species, @real_name) end # @return [String] the translated name of this form of this species def form_name - return pbGetMessage(MessageTypes::FormNames, @id_number) + return pbGetMessageFromHash(MessageTypes::FormNames, @real_form_name) end # @return [String] the translated Pokédex category of this species def category - return pbGetMessage(MessageTypes::Kinds, @id_number) + return pbGetMessageFromHash(MessageTypes::Kinds, @real_category) end # @return [String] the translated Pokédex entry of this species def pokedex_entry - return pbGetMessage(MessageTypes::Entries, @id_number) + return pbGetMessageFromHash(MessageTypes::Entries, @real_pokedex_entry) end - def apply_metrics_to_sprite(sprite, index, shadow = false) - if shadow - if (index & 1) == 1 # Foe Pokémon - sprite.x += @shadow_x * 2 - end - else - if (index & 1) == 0 # Player's Pokémon - sprite.x += @back_sprite_x * 2 - sprite.y += @back_sprite_y * 2 - else # Foe Pokémon - sprite.x += @front_sprite_x * 2 - sprite.y += @front_sprite_y * 2 - sprite.y -= @front_sprite_altitude * 2 - end + def single_gendered? + return GameData::GenderRatio.get(@gender_ratio).single_gendered? + end + + def default_form + @flags.each do |flag| + return $~[1].to_i if flag[/^DefaultForm_(\d+)$/i] end + return -1 + end + + def base_form + default = default_form + return (default >= 0) ? default : @form + end + + def has_flag?(flag) + return @flags.any? { |f| f.downcase == flag.downcase } + end + + def apply_metrics_to_sprite(sprite, index, shadow = false) + metrics_data = GameData::SpeciesMetrics.get_species_form(@species, @form) + metrics_data.apply_metrics_to_sprite(sprite, index, shadow) end def shows_shadow? - return true -# return @front_sprite_altitude > 0 + metrics_data = GameData::SpeciesMetrics.get_species_form(@species, @form) + return metrics_data.shows_shadow? end def get_evolutions(exclude_invalid = false) @@ -233,8 +249,8 @@ def get_evolutions(exclude_invalid = false) end def get_family_evolutions(exclude_invalid = true) - evos = self.get_evolutions(exclude_invalid) - evos = evos.sort { |a, b| GameData::Species.get(a[0]).id_number <=> GameData::Species.get(b[0]).id_number } + evos = get_evolutions(exclude_invalid) + evos = evos.sort { |a, b| GameData::Species.keys.index(a[0]) <=> GameData::Species.keys.index(b[0]) } ret = [] evos.each do |evo| ret.push([@species].concat(evo)) # [Prevo species, evo species, method, parameter] @@ -267,22 +283,41 @@ def get_baby_species(check_items = false, item1 = nil, item2 = nil) return ret end - def get_related_species - sp = self.get_baby_species + # Returns an array of all the species in this species' evolution family. + def get_family_species + sp = get_baby_species evos = GameData::Species.get(sp).get_family_evolutions(false) return [sp] if evos.length == 0 return [sp].concat(evos.map { |e| e[1] }).uniq end + # This takes into account whether other_species is evolved. + def breeding_can_produce?(other_species) + other_family = GameData::Species.get(other_species).get_family_species + if @offspring.length > 0 + return (other_family & @offspring).length > 0 + end + return other_family.include?(@species) + end + + # If this species doesn't have egg moves, looks at prevolutions one at a + # time and returns theirs instead. + def get_egg_moves + return @egg_moves if !@egg_moves.empty? + prevo = get_previous_species + return GameData::Species.get_species_form(prevo, @form).get_egg_moves if prevo != @species + return @egg_moves + end + def family_evolutions_have_method?(check_method, check_param = nil) - sp = self.get_baby_species + sp = get_baby_species evos = GameData::Species.get(sp).get_family_evolutions return false if evos.length == 0 evos.each do |evo| if check_method.is_a?(Array) next if !check_method.include?(evo[2]) - else - next if evo[2] != check_method + elsif evo[2] != check_method + next end return true if check_param.nil? || evo[3] == check_param end @@ -292,7 +327,7 @@ def family_evolutions_have_method?(check_method, check_param = nil) # Used by the Moon Ball when checking if a Pokémon's evolution family # includes an evolution that uses the Moon Stone. def family_item_evolutions_use_item?(check_item = nil) - sp = self.get_baby_species + sp = get_baby_species evos = GameData::Species.get(sp).get_family_evolutions return false if !evos || evos.length == 0 evos.each do |evo| @@ -315,84 +350,3 @@ def minimum_level end end end - -#=============================================================================== -# Deprecated methods -#=============================================================================== -# @deprecated This alias is slated to be removed in v20. -def pbGetSpeciesData(species, form = 0, species_data_type = -1) - Deprecation.warn_method('pbGetSpeciesData', 'v20', 'GameData::Species.get_species_form(species, form).something') - return GameData::Species.get_species_form(species, form) -end - -# @deprecated This alias is slated to be removed in v20. -def pbGetSpeciesEggMoves(species, form = 0) - Deprecation.warn_method('pbGetSpeciesEggMoves', 'v20', 'GameData::Species.get_species_form(species, form).egg_moves') - return GameData::Species.get_species_form(species, form).egg_moves -end - -# @deprecated This alias is slated to be removed in v20. -def pbGetSpeciesMoveset(species, form = 0) - Deprecation.warn_method('pbGetSpeciesMoveset', 'v20', 'GameData::Species.get_species_form(species, form).moves') - return GameData::Species.get_species_form(species, form).moves -end - -# @deprecated This alias is slated to be removed in v20. -def pbGetEvolutionData(species) - Deprecation.warn_method('pbGetEvolutionData', 'v20', 'GameData::Species.get(species).evolutions') - return GameData::Species.get(species).evolutions -end - -# @deprecated This alias is slated to be removed in v20. -def pbApplyBattlerMetricsToSprite(sprite, index, species_data, shadow = false, metrics = nil) - Deprecation.warn_method('pbApplyBattlerMetricsToSprite', 'v20', 'GameData::Species.get(species).apply_metrics_to_sprite') - GameData::Species.get(species).apply_metrics_to_sprite(sprite, index, shadow) -end - -# @deprecated This alias is slated to be removed in v20. -def showShadow?(species) - Deprecation.warn_method('showShadow?', 'v20', 'GameData::Species.get(species).shows_shadow?') - return GameData::Species.get(species).shows_shadow? -end - -# @deprecated Use {GameData#Species#get_evolutions} instead. This alias is slated to be removed in v20. -def pbGetEvolvedFormData(species, exclude_invalid = false) - Deprecation.warn_method('pbGetEvolvedFormData', 'v20', 'GameData::Species.get(species).get_evolutions') - return GameData::Species.get(species).get_evolutions(exclude_invalid) -end - -# @deprecated Use {GameData#Species#get_family_evolutions} instead. This alias is slated to be removed in v20. -def pbGetEvolutionFamilyData(species) # Unused - Deprecation.warn_method('pbGetEvolutionFamilyData', 'v20', 'GameData::Species.get(species).get_family_evolutions') - return GameData::Species.get(species).get_family_evolutions -end - -# @deprecated Use {GameData#Species#get_previous_species} instead. This alias is slated to be removed in v20. -def pbGetPreviousForm(species) # Unused - Deprecation.warn_method('pbGetPreviousForm', 'v20', 'GameData::Species.get(species).get_previous_species') - return GameData::Species.get(species).get_previous_species -end - -# @deprecated Use {GameData#Species#get_baby_species} instead. This alias is slated to be removed in v20. -def pbGetBabySpecies(species, check_items = false, item1 = nil, item2 = nil) - Deprecation.warn_method('pbGetBabySpecies', 'v20', 'GameData::Species.get(species).get_baby_species') - return GameData::Species.get(species).get_baby_species(check_items, item1, item2) -end - -# @deprecated Use {GameData#Species#family_evolutions_have_method?} instead. This alias is slated to be removed in v20. -def pbCheckEvolutionFamilyForMethod(species, method, param = nil) # Unused - Deprecation.warn_method('pbCheckEvolutionFamilyForMethod', 'v20', 'GameData::Species.get(species).family_evolutions_have_method?(method)') - return GameData::Species.get(species).family_evolutions_have_method?(method, param) -end - -# @deprecated Use {GameData#Species#family_item_evolutions_use_item?} instead. This alias is slated to be removed in v20. -def pbCheckEvolutionFamilyForItemMethodItem(species, param = nil) - Deprecation.warn_method('pbCheckEvolutionFamilyForItemMethodItem', 'v20', 'GameData::Species.get(species).family_item_evolutions_use_item?(item)') - return GameData::Species.get(species).family_item_evolutions_use_item?(param) -end - -# @deprecated Use {GameData#Species#minimum_level} instead. This alias is slated to be removed in v20. -def pbGetMinimumLevel(species) - Deprecation.warn_method('pbGetMinimumLevel', 'v20', 'GameData::Species.get(species).minimum_level') - return GameData::Species.get(species).minimum_level -end diff --git a/Data/Scripts/010_Data/002_PBS data/009_Species_Files.rb b/Data/Scripts/010_Data/002_PBS data/009_Species_Files.rb index 916fb4afde..6529134825 100644 --- a/Data/Scripts/010_Data/002_PBS data/009_Species_Files.rb +++ b/Data/Scripts/010_Data/002_PBS data/009_Species_Files.rb @@ -13,10 +13,10 @@ def self.check_graphic_file(path, species, form = 0, gender = 0, shiny = false, factors.push([1, try_form, ""]) if form > 0 factors.push([0, try_species, "000"]) # Go through each combination of parameters in turn to find an existing sprite - for i in 0...2 ** factors.length + (2**factors.length).times do |i| # Set try_ parameters for this combination factors.each_with_index do |factor, index| - value = ((i / (2 ** index)) % 2 == 0) ? factor[1] : factor[2] + value = ((i / (2**index)).even?) ? factor[1] : factor[2] case factor[0] when 0 then try_species = value when 1 then try_form = value @@ -28,7 +28,7 @@ def self.check_graphic_file(path, species, form = 0, gender = 0, shiny = false, # Look for a graphic matching this combination's parameters try_species_text = try_species ret = pbResolveBitmap(sprintf("%s%s%s%s%s%s", path, try_subfolder, - try_species_text, try_form, try_gender, try_shadow)) + try_species_text, try_form, try_gender, try_shadow)) return ret if ret end return nil @@ -158,7 +158,8 @@ def self.shadow_filename(species, form = 0) ret = pbResolveBitmap(sprintf("Graphics/Pokemon/Shadow/%s", species_data.species)) return ret if ret # Use general shadow graphic - return pbResolveBitmap(sprintf("Graphics/Pokemon/Shadow/%d", species_data.shadow_size)) + metrics_data = GameData::SpeciesMetrics.get_species_form(species_data.species, form) + return pbResolveBitmap(sprintf("Graphics/Pokemon/Shadow/%d", metrics_data.shadow_size)) end def self.shadow_bitmap(species, form = 0) @@ -173,23 +174,23 @@ def self.shadow_bitmap_from_pokemon(pkmn) #=========================================================================== - def self.check_cry_file(species, form) + def self.check_cry_file(species, form, suffix = "") species_data = self.get_species_form(species, form) return nil if species_data.nil? if form > 0 - ret = sprintf("Cries/%s_%d", species_data.species, form) + ret = sprintf("Cries/%s_%d%s", species_data.species, form, suffix) return ret if pbResolveAudioSE(ret) end - ret = sprintf("Cries/%s", species_data.species) + ret = sprintf("Cries/%s%s", species_data.species, suffix) return (pbResolveAudioSE(ret)) ? ret : nil end - def self.cry_filename(species, form = 0) - return self.check_cry_file(species, form) + def self.cry_filename(species, form = 0, suffix = "") + return self.check_cry_file(species, form || 0, suffix) end - def self.cry_filename_from_pokemon(pkmn) - return self.check_cry_file(pkmn.species, pkmn.form) + def self.cry_filename_from_pokemon(pkmn, suffix = "") + return self.check_cry_file(pkmn.species, pkmn.form, suffix) end def self.play_cry_from_species(species, form = 0, volume = 90, pitch = 100) @@ -198,122 +199,42 @@ def self.play_cry_from_species(species, form = 0, volume = 90, pitch = 100) pbSEPlay(RPG::AudioFile.new(filename, volume, pitch)) rescue nil end - def self.play_cry_from_pokemon(pkmn, volume = 90, pitch = nil) + def self.play_cry_from_pokemon(pkmn, volume = 90, pitch = 100) return if !pkmn || pkmn.egg? filename = self.cry_filename_from_pokemon(pkmn) return if !filename - pitch ||= 75 + (pkmn.hp * 25 / pkmn.totalhp) + pitch ||= 100 pbSEPlay(RPG::AudioFile.new(filename, volume, pitch)) rescue nil end - def self.play_cry(pkmn, volume = 90, pitch = nil) + def self.play_cry(pkmn, volume = 90, pitch = 100) if pkmn.is_a?(Pokemon) self.play_cry_from_pokemon(pkmn, volume, pitch) else - self.play_cry_from_species(pkmn, nil, volume, pitch) + self.play_cry_from_species(pkmn, 0, volume, pitch) end end - def self.cry_length(species, form = 0, pitch = 100) + def self.cry_length(species, form = 0, pitch = 100, suffix = "") + pitch ||= 100 return 0 if !species || pitch <= 0 pitch = pitch.to_f / 100 ret = 0.0 if species.is_a?(Pokemon) if !species.egg? - filename = pbResolveAudioSE(GameData::Species.cry_filename_from_pokemon(species)) + filename = self.cry_filename_from_pokemon(species, suffix) + filename = self.cry_filename_from_pokemon(species) if !filename && !nil_or_empty?(suffix) + filename = pbResolveAudioSE(filename) ret = getPlayTime(filename) if filename end else - filename = pbResolveAudioSE(GameData::Species.cry_filename(species, form)) + filename = self.cry_filename(species, form, suffix) + filename = self.cry_filename(species, form) if !filename && !nil_or_empty?(suffix) + filename = pbResolveAudioSE(filename) ret = getPlayTime(filename) if filename end ret /= pitch # Sound played at a lower pitch lasts longer - return (ret * Graphics.frame_rate).ceil + 4 # 4 provides a buffer between sounds + return ret end end end - -#=============================================================================== -# Deprecated methods -#=============================================================================== -# @deprecated This alias is slated to be removed in v20. -def pbLoadSpeciesBitmap(species, gender = 0, form = 0, shiny = false, shadow = false, back = false , egg = false) - Deprecation.warn_method('pbLoadSpeciesBitmap', 'v20', 'GameData::Species.sprite_bitmap(species, form, gender, shiny, shadow, back, egg)') - return GameData::Species.sprite_bitmap(species, form, gender, shiny, shadow, back, egg) -end - -# @deprecated This alias is slated to be removed in v20. -def pbLoadPokemonBitmap(pkmn, back = false) - Deprecation.warn_method('pbLoadPokemonBitmap', 'v20', 'GameData::Species.sprite_bitmap_from_pokemon(pkmn)') - return GameData::Species.sprite_bitmap_from_pokemon(pkmn, back) -end - -# @deprecated This alias is slated to be removed in v20. -def pbLoadPokemonBitmapSpecies(pkmn, species, back = false) - Deprecation.warn_method('pbLoadPokemonBitmapSpecies', 'v20', 'GameData::Species.sprite_bitmap_from_pokemon(pkmn, back, species)') - return GameData::Species.sprite_bitmap_from_pokemon(pkmn, back, species) -end - -# @deprecated This alias is slated to be removed in v20. -def pbPokemonIconFile(pkmn) - Deprecation.warn_method('pbPokemonIconFile', 'v20', 'GameData::Species.icon_filename_from_pokemon(pkmn)') - return GameData::Species.icon_filename_from_pokemon(pkmn) -end - -# @deprecated This alias is slated to be removed in v20. -def pbLoadPokemonIcon(pkmn) - Deprecation.warn_method('pbLoadPokemonIcon', 'v20', 'GameData::Species.icon_bitmap_from_pokemon(pkmn)') - return GameData::Species.icon_bitmap_from_pokemon(pkmn) -end - -# @deprecated This alias is slated to be removed in v20. -def pbPokemonFootprintFile(species, form = 0) - Deprecation.warn_method('pbPokemonFootprintFile', 'v20', 'GameData::Species.footprint_filename(species, form)') - return GameData::Species.footprint_filename(species, form) -end - -# @deprecated This alias is slated to be removed in v20. -def pbCheckPokemonShadowBitmapFiles(species, form = 0) - Deprecation.warn_method('pbCheckPokemonShadowBitmapFiles', 'v20', 'GameData::Species.shadow_filename(species, form)') - return GameData::Species.shadow_filename(species, form) -end - -# @deprecated This alias is slated to be removed in v20. -def pbLoadPokemonShadowBitmap(pkmn) - Deprecation.warn_method('pbLoadPokemonShadowBitmap', 'v20', 'GameData::Species.shadow_bitmap_from_pokemon(pkmn)') - return GameData::Species.shadow_bitmap_from_pokemon(pkmn) -end - -# @deprecated This alias is slated to be removed in v20. -def pbCryFile(species, form = 0) - if species.is_a?(Pokemon) - Deprecation.warn_method('pbCryFile', 'v20', 'GameData::Species.cry_filename_from_pokemon(pkmn)') - return GameData::Species.cry_filename_from_pokemon(species) - end - Deprecation.warn_method('pbCryFile', 'v20', 'GameData::Species.cry_filename(species, form)') - return GameData::Species.cry_filename(species, form) -end - -# @deprecated This alias is slated to be removed in v20. -def pbPlayCry(pkmn, volume = 90, pitch = nil) - Deprecation.warn_method('pbPlayCry', 'v20', 'GameData::Species.play_cry(pkmn)') - GameData::Species.play_cry(pkmn, volume, pitch) -end - -# @deprecated This alias is slated to be removed in v20. -def pbPlayCrySpecies(species, form = 0, volume = 90, pitch = nil) - Deprecation.warn_method('pbPlayCrySpecies', 'v20', 'Pokemon.play_cry(species, form)') - Pokemon.play_cry(species, form, volume, pitch) -end - -# @deprecated This alias is slated to be removed in v20. -def pbPlayCryPokemon(pkmn, volume = 90, pitch = nil) - Deprecation.warn_method('pbPlayCryPokemon', 'v20', 'pkmn.play_cry') - pkmn.play_cry(volume, pitch) -end - -# @deprecated This alias is slated to be removed in v20. -def pbCryFrameLength(species, form = 0, pitch = 100) - Deprecation.warn_method('pbCryFrameLength', 'v20', 'GameData::Species.cry_length(species, form)') - return GameData::Species.cry_length(species, form, pitch) -end diff --git a/Data/Scripts/010_Data/002_PBS data/010_Ribbon.rb b/Data/Scripts/010_Data/002_PBS data/010_Ribbon.rb deleted file mode 100644 index 77195cac52..0000000000 --- a/Data/Scripts/010_Data/002_PBS data/010_Ribbon.rb +++ /dev/null @@ -1,31 +0,0 @@ -module GameData - class Ribbon - attr_reader :id - attr_reader :id_number - attr_reader :real_name - attr_reader :real_description - - DATA = {} - DATA_FILENAME = "ribbons.dat" - - extend ClassMethods - include InstanceMethods - - def initialize(hash) - @id = hash[:id] - @id_number = hash[:id_number] || -1 - @real_name = hash[:name] || "Unnamed" - @real_description = hash[:description] || "???" - end - - # @return [String] the translated name of this ribbon - def name - return pbGetMessage(MessageTypes::RibbonNames, @id_number) - end - - # @return [String] the translated description of this ribbon - def description - return pbGetMessage(MessageTypes::RibbonDescriptions, @id_number) - end - end -end diff --git a/Data/Scripts/010_Data/002_PBS data/010_SpeciesMetrics.rb b/Data/Scripts/010_Data/002_PBS data/010_SpeciesMetrics.rb new file mode 100644 index 0000000000..ae1b222c51 --- /dev/null +++ b/Data/Scripts/010_Data/002_PBS data/010_SpeciesMetrics.rb @@ -0,0 +1,87 @@ +module GameData + class SpeciesMetrics + attr_reader :id + attr_reader :species + attr_reader :form + attr_accessor :back_sprite + attr_accessor :front_sprite + attr_accessor :front_sprite_altitude + attr_accessor :shadow_x + attr_accessor :shadow_size + + DATA = {} + DATA_FILENAME = "species_metrics.dat" + + SCHEMA = { + "BackSprite" => [0, "ii"], + "FrontSprite" => [0, "ii"], + "FrontSpriteAltitude" => [0, "i"], + "ShadowX" => [0, "i"], + "ShadowSize" => [0, "u"] + } + + extend ClassMethodsSymbols + include InstanceMethods + + # @param species [Symbol, String] + # @param form [Integer] + # @return [self, nil] + def self.get_species_form(species, form) + return nil if !species || !form + validate species => [Symbol, String] + validate form => Integer + raise _INTL("Undefined species {1}.", species) if !GameData::Species.exists?(species) + species = species.to_sym if species.is_a?(String) + if form > 0 + trial = sprintf("%s_%d", species, form).to_sym + if !DATA.has_key?(trial) + self.register({ :id => species }) if !DATA[species] + self.register({ + :id => trial, + :species => species, + :form => form, + :back_sprite => DATA[species].back_sprite.clone, + :front_sprite => DATA[species].front_sprite.clone, + :front_sprite_altitude => DATA[species].front_sprite_altitude, + :shadow_x => DATA[species].shadow_x, + :shadow_size => DATA[species].shadow_size + }) + end + return DATA[trial] + end + self.register({ :id => species }) if !DATA[species] + return DATA[species] + end + + def initialize(hash) + @id = hash[:id] + @species = hash[:species] || @id + @form = hash[:form] || 0 + @back_sprite = hash[:back_sprite] || [0, 0] + @front_sprite = hash[:front_sprite] || [0, 0] + @front_sprite_altitude = hash[:front_sprite_altitude] || 0 + @shadow_x = hash[:shadow_x] || 0 + @shadow_size = hash[:shadow_size] || 2 + end + + def apply_metrics_to_sprite(sprite, index, shadow = false) + if shadow + if (index & 1) == 1 # Foe Pokémon + sprite.x += @shadow_x * 2 + end + elsif (index & 1) == 0 # Player's Pokémon + sprite.x += @back_sprite[0] * 2 + sprite.y += @back_sprite[1] * 2 + else # Foe Pokémon + sprite.x += @front_sprite[0] * 2 + sprite.y += @front_sprite[1] * 2 + sprite.y -= @front_sprite_altitude * 2 + end + end + + def shows_shadow? + return true +# return @front_sprite_altitude > 0 + end + end +end diff --git a/Data/Scripts/010_Data/002_PBS data/011_ShadowPokemon.rb b/Data/Scripts/010_Data/002_PBS data/011_ShadowPokemon.rb new file mode 100644 index 0000000000..828ed76675 --- /dev/null +++ b/Data/Scripts/010_Data/002_PBS data/011_ShadowPokemon.rb @@ -0,0 +1,32 @@ +module GameData + class ShadowPokemon + attr_reader :id + attr_reader :moves + attr_reader :gauge_size + attr_reader :flags + + DATA = {} + DATA_FILENAME = "shadow_pokemon.dat" + + SCHEMA = { + "GaugeSize" => [:gauge_size, "v"], + "Moves" => [:moves, "*s"], # Not enumerated when compiled + "Flags" => [:flags, "*s"] + } + HEART_GAUGE_SIZE = 4000 # Default gauge size + + extend ClassMethodsSymbols + include InstanceMethods + + def initialize(hash) + @id = hash[:id] + @moves = hash[:moves] || [] + @gauge_size = hash[:gauge_size] || HEART_GAUGE_SIZE + @flags = hash[:flags] || [] + end + + def has_flag?(flag) + return @flags.any? { |f| f.downcase == flag.downcase } + end + end +end diff --git a/Data/Scripts/010_Data/002_PBS data/012_Ribbon.rb b/Data/Scripts/010_Data/002_PBS data/012_Ribbon.rb new file mode 100644 index 0000000000..e2344d217f --- /dev/null +++ b/Data/Scripts/010_Data/002_PBS data/012_Ribbon.rb @@ -0,0 +1,44 @@ +module GameData + class Ribbon + attr_reader :id + attr_reader :real_name + attr_reader :icon_position # Where this ribbon's graphic is within ribbons.png + attr_reader :real_description + attr_reader :flags + + DATA = {} + DATA_FILENAME = "ribbons.dat" + + SCHEMA = { + "Name" => [:name, "s"], + "IconPosition" => [:icon_position, "u"], + "Description" => [:description, "q"], + "Flags" => [:flags, "*s"] + } + + extend ClassMethodsSymbols + include InstanceMethods + + def initialize(hash) + @id = hash[:id] + @real_name = hash[:name] || "Unnamed" + @icon_position = hash[:icon_position] || 0 + @real_description = hash[:description] || "???" + @flags = hash[:flags] || [] + end + + # @return [String] the translated name of this ribbon + def name + return pbGetMessageFromHash(MessageTypes::RibbonNames, @real_name) + end + + # @return [String] the translated description of this ribbon + def description + return pbGetMessageFromHash(MessageTypes::RibbonDescriptions, @real_description) + end + + def has_flag?(flag) + return @flags.any? { |f| f.downcase == flag.downcase } + end + end +end diff --git a/Data/Scripts/010_Data/002_PBS data/012_TrainerType.rb b/Data/Scripts/010_Data/002_PBS data/012_TrainerType.rb deleted file mode 100644 index 12f037f729..0000000000 --- a/Data/Scripts/010_Data/002_PBS data/012_TrainerType.rb +++ /dev/null @@ -1,151 +0,0 @@ -module GameData - class TrainerType - attr_reader :id - attr_reader :id_number - attr_reader :real_name - attr_reader :base_money - attr_reader :battle_BGM - attr_reader :victory_ME - attr_reader :intro_ME - attr_reader :gender - attr_reader :skill_level - attr_reader :skill_code - - DATA = {} - DATA_FILENAME = "trainer_types.dat" - - extend ClassMethods - include InstanceMethods - - def self.check_file(tr_type, path, optional_suffix = "", suffix = "") - tr_type_data = self.try_get(tr_type) - return nil if tr_type_data.nil? - # Check for files - if optional_suffix && !optional_suffix.empty? - ret = path + tr_type_data.id.to_s + optional_suffix + suffix - return ret if pbResolveBitmap(ret) - ret = path + sprintf("%03d", tr_type_data.id_number) + optional_suffix + suffix - return ret if pbResolveBitmap(ret) - end - ret = path + tr_type_data.id.to_s + suffix - return ret if pbResolveBitmap(ret) - ret = path + sprintf("%03d", tr_type_data.id_number) + suffix - return (pbResolveBitmap(ret)) ? ret : nil - end - - def self.charset_filename(tr_type) - return self.check_file(tr_type, "Graphics/Characters/trainer_") - end - - def self.charset_filename_brief(tr_type) - ret = self.charset_filename(tr_type) - ret.slice!("Graphics/Characters/") if ret - return ret - end - - def self.front_sprite_filename(tr_type) - return self.check_file(tr_type, "Graphics/Trainers/") - end - - def self.player_front_sprite_filename(tr_type) - outfit = ($Trainer) ? $Trainer.outfit : 0 - return self.check_file(tr_type, "Graphics/Trainers/", sprintf("_%d", outfit)) - end - - def self.back_sprite_filename(tr_type) - return self.check_file(tr_type, "Graphics/Trainers/", "", "_back") - end - - def self.player_back_sprite_filename(tr_type) - outfit = ($Trainer) ? $Trainer.outfit : 0 - return self.check_file(tr_type, "Graphics/Trainers/", sprintf("_%d", outfit), "_back") - end - - def self.map_icon_filename(tr_type) - return self.check_file(tr_type, "Graphics/Pictures/mapPlayer") - end - - def self.player_map_icon_filename(tr_type) - outfit = ($Trainer) ? $Trainer.outfit : 0 - return self.check_file(tr_type, "Graphics/Pictures/mapPlayer", sprintf("_%d", outfit)) - end - - def initialize(hash) - @id = hash[:id] - @id_number = hash[:id_number] || -1 - @real_name = hash[:name] || "Unnamed" - @base_money = hash[:base_money] || 30 - @battle_BGM = hash[:battle_BGM] - @victory_ME = hash[:victory_ME] - @intro_ME = hash[:intro_ME] - @gender = hash[:gender] || 2 - @skill_level = hash[:skill_level] || @base_money - @skill_code = hash[:skill_code] - end - - # @return [String] the translated name of this trainer type - def name - return pbGetMessage(MessageTypes::TrainerTypes, @id_number) - end - - def male?; return @gender == 0; end - def female?; return @gender == 1; end - end -end - -#=============================================================================== -# Deprecated methods -#=============================================================================== -# @deprecated This alias is slated to be removed in v20. -def pbGetTrainerTypeData(tr_type) - Deprecation.warn_method('pbGetTrainerTypeData', 'v20', 'GameData::TrainerType.get(trainer_type)') - return GameData::TrainerType.get(tr_type) -end - -# @deprecated This alias is slated to be removed in v20. -def pbTrainerCharFile(tr_type) # Used by the phone - Deprecation.warn_method('pbTrainerCharFile', 'v20', 'GameData::TrainerType.charset_filename(trainer_type)') - return GameData::TrainerType.charset_filename(tr_type) -end - -# @deprecated This alias is slated to be removed in v20. -def pbTrainerCharNameFile(tr_type) # Used by Battle Frontier and compiler - Deprecation.warn_method('pbTrainerCharNameFile', 'v20', 'GameData::TrainerType.charset_filename_brief(trainer_type)') - return GameData::TrainerType.charset_filename_brief(tr_type) -end - -# @deprecated This alias is slated to be removed in v20. -def pbTrainerSpriteFile(tr_type) - Deprecation.warn_method('pbTrainerSpriteFile', 'v20', 'GameData::TrainerType.front_sprite_filename(trainer_type)') - return GameData::TrainerType.front_sprite_filename(tr_type) -end - -# @deprecated This alias is slated to be removed in v20. -def pbTrainerSpriteBackFile(tr_type) - Deprecation.warn_method('pbTrainerSpriteBackFile', 'v20', 'GameData::TrainerType.back_sprite_filename(trainer_type)') - return GameData::TrainerType.back_sprite_filename(tr_type) -end - -# @deprecated This alias is slated to be removed in v20. -def pbPlayerSpriteFile(tr_type) - Deprecation.warn_method('pbPlayerSpriteFile', 'v20', 'GameData::TrainerType.player_front_sprite_filename(trainer_type)') - return GameData::TrainerType.player_front_sprite_filename(tr_type) -end - -# @deprecated This alias is slated to be removed in v20. -def pbPlayerSpriteBackFile(tr_type) - Deprecation.warn_method('pbPlayerSpriteBackFile', 'v20', 'GameData::TrainerType.player_back_sprite_filename(trainer_type)') - return GameData::TrainerType.player_back_sprite_filename(tr_type) -end - -# @deprecated This alias is slated to be removed in v20. -def pbTrainerHeadFile(tr_type) - Deprecation.warn_method('pbTrainerHeadFile', 'v20', 'GameData::TrainerType.map_icon_filename(trainer_type)') - return GameData::TrainerType.map_icon_filename(tr_type) -end - -# @deprecated This alias is slated to be removed in v20. -def pbPlayerHeadFile(tr_type) - Deprecation.warn_method('pbPlayerHeadFile', 'v20', 'GameData::TrainerType.player_map_icon_filename(trainer_type)') - return GameData::TrainerType.player_map_icon_filename(tr_type) -end diff --git a/Data/Scripts/010_Data/002_PBS data/011_Encounter.rb b/Data/Scripts/010_Data/002_PBS data/013_Encounter.rb similarity index 80% rename from Data/Scripts/010_Data/002_PBS data/011_Encounter.rb rename to Data/Scripts/010_Data/002_PBS data/013_Encounter.rb index e0e75bb3f1..157ba26e85 100644 --- a/Data/Scripts/010_Data/002_PBS data/011_Encounter.rb +++ b/Data/Scripts/010_Data/002_PBS data/013_Encounter.rb @@ -51,8 +51,8 @@ def self.each def self.each_of_version(version = 0) self.each do |data| yield data if data.version == version - if version > 0 - yield data if data.version == 0 && !self::DATA.has_key?([data.map, version]) + if version > 0 && data.version == 0 && !self::DATA.has_key?([data.map, version]) + yield data end end end @@ -66,12 +66,3 @@ def initialize(hash) end end end - -#=============================================================================== -# Deprecated methods -#=============================================================================== -# @deprecated This alias is slated to be removed in v20. -def pbLoadEncountersData - Deprecation.warn_method('pbLoadEncountersData', 'v20', 'GameData::Encounter.get(map_id, version)') - return nil -end diff --git a/Data/Scripts/010_Data/002_PBS data/014_Metadata.rb b/Data/Scripts/010_Data/002_PBS data/014_Metadata.rb deleted file mode 100644 index 51e7a39620..0000000000 --- a/Data/Scripts/010_Data/002_PBS data/014_Metadata.rb +++ /dev/null @@ -1,146 +0,0 @@ -module GameData - class Metadata - attr_reader :id - attr_reader :home - attr_reader :wild_battle_BGM - attr_reader :trainer_battle_BGM - attr_reader :wild_victory_ME - attr_reader :trainer_victory_ME - attr_reader :wild_capture_ME - attr_reader :surf_BGM - attr_reader :bicycle_BGM - attr_reader :player_A - attr_reader :player_B - attr_reader :player_C - attr_reader :player_D - attr_reader :player_E - attr_reader :player_F - attr_reader :player_G - attr_reader :player_H - - DATA = {} - DATA_FILENAME = "metadata.dat" - - SCHEMA = { - "Home" => [1, "vuuu"], - "WildBattleBGM" => [2, "s"], - "TrainerBattleBGM" => [3, "s"], - "WildVictoryME" => [4, "s"], - "TrainerVictoryME" => [5, "s"], - "WildCaptureME" => [6, "s"], - "SurfBGM" => [7, "s"], - "BicycleBGM" => [8, "s"], - "PlayerA" => [9, "esssssss", :TrainerType], - "PlayerB" => [10, "esssssss", :TrainerType], - "PlayerC" => [11, "esssssss", :TrainerType], - "PlayerD" => [12, "esssssss", :TrainerType], - "PlayerE" => [13, "esssssss", :TrainerType], - "PlayerF" => [14, "esssssss", :TrainerType], - "PlayerG" => [15, "esssssss", :TrainerType], - "PlayerH" => [16, "esssssss", :TrainerType] - } - - extend ClassMethodsIDNumbers - include InstanceMethods - - def self.editor_properties - return [ - ["Home", MapCoordsFacingProperty, _INTL("Map ID and X and Y coordinates of where the player goes if no Pokémon Center was entered after a loss.")], - ["WildBattleBGM", BGMProperty, _INTL("Default BGM for wild Pokémon battles.")], - ["TrainerBattleBGM", BGMProperty, _INTL("Default BGM for Trainer battles.")], - ["WildVictoryME", MEProperty, _INTL("Default ME played after winning a wild Pokémon battle.")], - ["TrainerVictoryME", MEProperty, _INTL("Default ME played after winning a Trainer battle.")], - ["WildCaptureME", MEProperty, _INTL("Default ME played after catching a Pokémon.")], - ["SurfBGM", BGMProperty, _INTL("BGM played while surfing.")], - ["BicycleBGM", BGMProperty, _INTL("BGM played while on a bicycle.")], - ["PlayerA", PlayerProperty, _INTL("Specifies player A.")], - ["PlayerB", PlayerProperty, _INTL("Specifies player B.")], - ["PlayerC", PlayerProperty, _INTL("Specifies player C.")], - ["PlayerD", PlayerProperty, _INTL("Specifies player D.")], - ["PlayerE", PlayerProperty, _INTL("Specifies player E.")], - ["PlayerF", PlayerProperty, _INTL("Specifies player F.")], - ["PlayerG", PlayerProperty, _INTL("Specifies player G.")], - ["PlayerH", PlayerProperty, _INTL("Specifies player H.")] - ] - end - - def self.get - return DATA[0] - end - - def self.get_player(id) - case id - when 0 then return self.get.player_A - when 1 then return self.get.player_B - when 2 then return self.get.player_C - when 3 then return self.get.player_D - when 4 then return self.get.player_E - when 5 then return self.get.player_F - when 6 then return self.get.player_G - when 7 then return self.get.player_H - end - return nil - end - - def initialize(hash) - @id = hash[:id] - @home = hash[:home] - @wild_battle_BGM = hash[:wild_battle_BGM] - @trainer_battle_BGM = hash[:trainer_battle_BGM] - @wild_victory_ME = hash[:wild_victory_ME] - @trainer_victory_ME = hash[:trainer_victory_ME] - @wild_capture_ME = hash[:wild_capture_ME] - @surf_BGM = hash[:surf_BGM] - @bicycle_BGM = hash[:bicycle_BGM] - @player_A = hash[:player_A] - @player_B = hash[:player_B] - @player_C = hash[:player_C] - @player_D = hash[:player_D] - @player_E = hash[:player_E] - @player_F = hash[:player_F] - @player_G = hash[:player_G] - @player_H = hash[:player_H] - end - - def property_from_string(str) - case str - when "Home" then return @home - when "WildBattleBGM" then return @wild_battle_BGM - when "TrainerBattleBGM" then return @trainer_battle_BGM - when "WildVictoryME" then return @wild_victory_ME - when "TrainerVictoryME" then return @trainer_victory_ME - when "WildCaptureME" then return @wild_capture_ME - when "SurfBGM" then return @surf_BGM - when "BicycleBGM" then return @bicycle_BGM - when "PlayerA" then return @player_A - when "PlayerB" then return @player_B - when "PlayerC" then return @player_C - when "PlayerD" then return @player_D - when "PlayerE" then return @player_E - when "PlayerF" then return @player_F - when "PlayerG" then return @player_G - when "PlayerH" then return @player_H - end - return nil - end - end -end - -#=============================================================================== -# Deprecated methods -#=============================================================================== -# @deprecated This alias is slated to be removed in v20. -def pbLoadMetadata - Deprecation.warn_method('pbLoadMetadata', 'v20', 'GameData::Metadata.get or GameData::MapMetadata.get(map_id)') - return nil -end - -# @deprecated This alias is slated to be removed in v20. -def pbGetMetadata(map_id, metadata_type) - if map_id == 0 # Global metadata - Deprecation.warn_method('pbGetMetadata', 'v20', 'GameData::Metadata.get.something') - else # Map metadata - Deprecation.warn_method('pbGetMetadata', 'v20', 'GameData::MapMetadata.get(map_id).something') - end - return nil -end diff --git a/Data/Scripts/010_Data/002_PBS data/014_TrainerType.rb b/Data/Scripts/010_Data/002_PBS data/014_TrainerType.rb new file mode 100644 index 0000000000..cddc2b6eb1 --- /dev/null +++ b/Data/Scripts/010_Data/002_PBS data/014_TrainerType.rb @@ -0,0 +1,106 @@ +module GameData + class TrainerType + attr_reader :id + attr_reader :real_name + attr_reader :gender + attr_reader :base_money + attr_reader :skill_level + attr_reader :flags + attr_reader :intro_BGM + attr_reader :battle_BGM + attr_reader :victory_BGM + + DATA = {} + DATA_FILENAME = "trainer_types.dat" + + SCHEMA = { + "Name" => [:name, "s"], + "Gender" => [:gender, "e", { "Male" => 0, "male" => 0, "M" => 0, "m" => 0, "0" => 0, + "Female" => 1, "female" => 1, "F" => 1, "f" => 1, "1" => 1, + "Unknown" => 2, "unknown" => 2, "Other" => 2, "other" => 2, + "Mixed" => 2, "mixed" => 2, "X" => 2, "x" => 2, "2" => 2 }], + "BaseMoney" => [:base_money, "u"], + "SkillLevel" => [:skill_level, "u"], + "Flags" => [:flags, "*s"], + "IntroBGM" => [:intro_BGM, "s"], + "BattleBGM" => [:battle_BGM, "s"], + "VictoryBGM" => [:victory_BGM, "s"] + } + + extend ClassMethodsSymbols + include InstanceMethods + + def self.check_file(tr_type, path, optional_suffix = "", suffix = "") + tr_type_data = self.try_get(tr_type) + return nil if tr_type_data.nil? + # Check for files + if optional_suffix && !optional_suffix.empty? + ret = path + tr_type_data.id.to_s + optional_suffix + suffix + return ret if pbResolveBitmap(ret) + end + ret = path + tr_type_data.id.to_s + suffix + return (pbResolveBitmap(ret)) ? ret : nil + end + + def self.charset_filename(tr_type) + return self.check_file(tr_type, "Graphics/Characters/trainer_") + end + + def self.charset_filename_brief(tr_type) + ret = self.charset_filename(tr_type) + ret&.slice!("Graphics/Characters/") + return ret + end + + def self.front_sprite_filename(tr_type) + return self.check_file(tr_type, "Graphics/Trainers/") + end + + def self.player_front_sprite_filename(tr_type) + outfit = ($player) ? $player.outfit : 0 + return self.check_file(tr_type, "Graphics/Trainers/", sprintf("_%d", outfit)) + end + + def self.back_sprite_filename(tr_type) + return self.check_file(tr_type, "Graphics/Trainers/", "", "_back") + end + + def self.player_back_sprite_filename(tr_type) + outfit = ($player) ? $player.outfit : 0 + return self.check_file(tr_type, "Graphics/Trainers/", sprintf("_%d", outfit), "_back") + end + + def self.map_icon_filename(tr_type) + return self.check_file(tr_type, "Graphics/Pictures/mapPlayer") + end + + def self.player_map_icon_filename(tr_type) + outfit = ($player) ? $player.outfit : 0 + return self.check_file(tr_type, "Graphics/Pictures/mapPlayer", sprintf("_%d", outfit)) + end + + def initialize(hash) + @id = hash[:id] + @real_name = hash[:name] || "Unnamed" + @gender = hash[:gender] || 2 + @base_money = hash[:base_money] || 30 + @skill_level = hash[:skill_level] || @base_money + @flags = hash[:flags] || [] + @intro_BGM = hash[:intro_BGM] + @battle_BGM = hash[:battle_BGM] + @victory_BGM = hash[:victory_BGM] + end + + # @return [String] the translated name of this trainer type + def name + return pbGetMessageFromHash(MessageTypes::TrainerTypes, @real_name) + end + + def male?; return @gender == 0; end + def female?; return @gender == 1; end + + def has_flag?(flag) + return @flags.any? { |f| f.downcase == flag.downcase } + end + end +end diff --git a/Data/Scripts/010_Data/002_PBS data/015_MapMetadata.rb b/Data/Scripts/010_Data/002_PBS data/015_MapMetadata.rb deleted file mode 100644 index f3dc57946e..0000000000 --- a/Data/Scripts/010_Data/002_PBS data/015_MapMetadata.rb +++ /dev/null @@ -1,129 +0,0 @@ -module GameData - class MapMetadata - attr_reader :id - attr_reader :outdoor_map - attr_reader :announce_location - attr_reader :can_bicycle - attr_reader :always_bicycle - attr_reader :teleport_destination - attr_reader :weather - attr_reader :town_map_position - attr_reader :dive_map_id - attr_reader :dark_map - attr_reader :safari_map - attr_reader :snap_edges - attr_reader :random_dungeon - attr_reader :battle_background - attr_reader :wild_battle_BGM - attr_reader :trainer_battle_BGM - attr_reader :wild_victory_ME - attr_reader :trainer_victory_ME - attr_reader :wild_capture_ME - attr_reader :town_map_size - attr_reader :battle_environment - - DATA = {} - DATA_FILENAME = "map_metadata.dat" - - SCHEMA = { - "Outdoor" => [1, "b"], - "ShowArea" => [2, "b"], - "Bicycle" => [3, "b"], - "BicycleAlways" => [4, "b"], - "HealingSpot" => [5, "vuu"], - "Weather" => [6, "eu", :Weather], - "MapPosition" => [7, "uuu"], - "DiveMap" => [8, "v"], - "DarkMap" => [9, "b"], - "SafariMap" => [10, "b"], - "SnapEdges" => [11, "b"], - "Dungeon" => [12, "b"], - "BattleBack" => [13, "s"], - "WildBattleBGM" => [14, "s"], - "TrainerBattleBGM" => [15, "s"], - "WildVictoryME" => [16, "s"], - "TrainerVictoryME" => [17, "s"], - "WildCaptureME" => [18, "s"], - "MapSize" => [19, "us"], - "Environment" => [20, "e", :Environment] - } - - extend ClassMethodsIDNumbers - include InstanceMethods - - def self.editor_properties - return [ - ["Outdoor", BooleanProperty, _INTL("If true, this map is an outdoor map and will be tinted according to time of day.")], - ["ShowArea", BooleanProperty, _INTL("If true, the game will display the map's name upon entry.")], - ["Bicycle", BooleanProperty, _INTL("If true, the bicycle can be used on this map.")], - ["BicycleAlways", BooleanProperty, _INTL("If true, the bicycle will be mounted automatically on this map and cannot be dismounted.")], - ["HealingSpot", MapCoordsProperty, _INTL("Map ID of this Pokémon Center's town, and X and Y coordinates of its entrance within that town.")], - ["Weather", WeatherEffectProperty, _INTL("Weather conditions in effect for this map.")], - ["MapPosition", RegionMapCoordsProperty, _INTL("Identifies the point on the regional map for this map.")], - ["DiveMap", MapProperty, _INTL("Specifies the underwater layer of this map. Use only if this map has deep water.")], - ["DarkMap", BooleanProperty, _INTL("If true, this map is dark and a circle of light appears around the player. Flash can be used to expand the circle.")], - ["SafariMap", BooleanProperty, _INTL("If true, this map is part of the Safari Zone (both indoor and outdoor). Not to be used in the reception desk.")], - ["SnapEdges", BooleanProperty, _INTL("If true, when the player goes near this map's edge, the game doesn't center the player as usual.")], - ["Dungeon", BooleanProperty, _INTL("If true, this map has a randomly generated layout. See the wiki for more information.")], - ["BattleBack", StringProperty, _INTL("PNG files named 'XXX_bg', 'XXX_base0', 'XXX_base1', 'XXX_message' in Battlebacks folder, where XXX is this property's value.")], - ["WildBattleBGM", BGMProperty, _INTL("Default BGM for wild Pokémon battles on this map.")], - ["TrainerBattleBGM", BGMProperty, _INTL("Default BGM for trainer battles on this map.")], - ["WildVictoryME", MEProperty, _INTL("Default ME played after winning a wild Pokémon battle on this map.")], - ["TrainerVictoryME", MEProperty, _INTL("Default ME played after winning a Trainer battle on this map.")], - ["WildCaptureME", MEProperty, _INTL("Default ME played after catching a wild Pokémon on this map.")], - ["MapSize", MapSizeProperty, _INTL("The width of the map in Town Map squares, and a string indicating which squares are part of this map.")], - ["Environment", GameDataProperty.new(:Environment), _INTL("The default battle environment for battles on this map.")] - ] - end - - def initialize(hash) - @id = hash[:id] - @outdoor_map = hash[:outdoor_map] - @announce_location = hash[:announce_location] - @can_bicycle = hash[:can_bicycle] - @always_bicycle = hash[:always_bicycle] - @teleport_destination = hash[:teleport_destination] - @weather = hash[:weather] - @town_map_position = hash[:town_map_position] - @dive_map_id = hash[:dive_map_id] - @dark_map = hash[:dark_map] - @safari_map = hash[:safari_map] - @snap_edges = hash[:snap_edges] - @random_dungeon = hash[:random_dungeon] - @battle_background = hash[:battle_background] - @wild_battle_BGM = hash[:wild_battle_BGM] - @trainer_battle_BGM = hash[:trainer_battle_BGM] - @wild_victory_ME = hash[:wild_victory_ME] - @trainer_victory_ME = hash[:trainer_victory_ME] - @wild_capture_ME = hash[:wild_capture_ME] - @town_map_size = hash[:town_map_size] - @battle_environment = hash[:battle_environment] - end - - def property_from_string(str) - case str - when "Outdoor" then return @outdoor_map - when "ShowArea" then return @announce_location - when "Bicycle" then return @can_bicycle - when "BicycleAlways" then return @always_bicycle - when "HealingSpot" then return @teleport_destination - when "Weather" then return @weather - when "MapPosition" then return @town_map_position - when "DiveMap" then return @dive_map_id - when "DarkMap" then return @dark_map - when "SafariMap" then return @safari_map - when "SnapEdges" then return @snap_edges - when "Dungeon" then return @random_dungeon - when "BattleBack" then return @battle_background - when "WildBattleBGM" then return @wild_battle_BGM - when "TrainerBattleBGM" then return @trainer_battle_BGM - when "WildVictoryME" then return @wild_victory_ME - when "TrainerVictoryME" then return @trainer_victory_ME - when "WildCaptureME" then return @wild_capture_ME - when "MapSize" then return @town_map_size - when "Environment" then return @battle_environment - end - return nil - end - end -end diff --git a/Data/Scripts/010_Data/002_PBS data/013_Trainer.rb b/Data/Scripts/010_Data/002_PBS data/015_Trainer.rb similarity index 71% rename from Data/Scripts/010_Data/002_PBS data/013_Trainer.rb rename to Data/Scripts/010_Data/002_PBS data/015_Trainer.rb index 9761c0a24c..d583dc733b 100644 --- a/Data/Scripts/010_Data/002_PBS data/013_Trainer.rb +++ b/Data/Scripts/010_Data/002_PBS data/015_Trainer.rb @@ -1,7 +1,6 @@ module GameData class Trainer attr_reader :id - attr_reader :id_number attr_reader :trainer_type attr_reader :real_name attr_reader :version @@ -13,27 +12,28 @@ class Trainer DATA_FILENAME = "trainers.dat" SCHEMA = { - "Items" => [:items, "*e", :Item], - "LoseText" => [:lose_text, "s"], - "Pokemon" => [:pokemon, "ev", :Species], # Species, level - "Form" => [:form, "u"], - "Name" => [:name, "s"], - "Moves" => [:moves, "*e", :Move], - "Ability" => [:ability, "s"], - "AbilityIndex" => [:ability_index, "u"], - "Item" => [:item, "e", :Item], - "Gender" => [:gender, "e", { "M" => 0, "m" => 0, "Male" => 0, "male" => 0, "0" => 0, - "F" => 1, "f" => 1, "Female" => 1, "female" => 1, "1" => 1 }], - "Nature" => [:nature, "e", :Nature], - "IV" => [:iv, "uUUUUU"], - "EV" => [:ev, "uUUUUU"], - "Happiness" => [:happiness, "u"], - "Shiny" => [:shininess, "b"], - "Shadow" => [:shadowness, "b"], - "Ball" => [:poke_ball, "s"], + "Items" => [:items, "*e", :Item], + "LoseText" => [:lose_text, "q"], + "Pokemon" => [:pokemon, "ev", :Species], # Species, level + "Form" => [:form, "u"], + "Name" => [:name, "s"], + "Moves" => [:moves, "*e", :Move], + "Ability" => [:ability, "e", :Ability], + "AbilityIndex" => [:ability_index, "u"], + "Item" => [:item, "e", :Item], + "Gender" => [:gender, "e", { "M" => 0, "m" => 0, "Male" => 0, "male" => 0, "0" => 0, + "F" => 1, "f" => 1, "Female" => 1, "female" => 1, "1" => 1 }], + "Nature" => [:nature, "e", :Nature], + "IV" => [:iv, "uUUUUU"], + "EV" => [:ev, "uUUUUU"], + "Happiness" => [:happiness, "u"], + "Shiny" => [:shininess, "b"], + "SuperShiny" => [:super_shininess, "b"], + "Shadow" => [:shadowness, "b"], + "Ball" => [:poke_ball, "e", :Item] } - extend ClassMethods + extend ClassMethodsSymbols include InstanceMethods # @param tr_type [Symbol, String] @@ -72,7 +72,6 @@ def self.try_get(tr_type, tr_name, tr_version = 0) def initialize(hash) @id = hash[:id] - @id_number = hash[:id_number] @trainer_type = hash[:trainer_type] @real_name = hash[:name] || "Unnamed" @version = hash[:version] || 0 @@ -109,7 +108,7 @@ def to_trainer end # Create trainer object trainer = NPCTrainer.new(tr_name, @trainer_type) - trainer.id = $Trainer.make_foreign_ID + trainer.id = $player.make_foreign_ID trainer.items = @items.clone trainer.lose_text = self.lose_text # Create each Pokémon owned by the trainer @@ -128,15 +127,18 @@ def to_trainer else pkmn.reset_moves end - pkmn.ability_index = pkmn_data[:ability_index] + pkmn.ability_index = pkmn_data[:ability_index] || 0 pkmn.ability = pkmn_data[:ability] pkmn.gender = pkmn_data[:gender] || ((trainer.male?) ? 0 : 1) pkmn.shiny = (pkmn_data[:shininess]) ? true : false + pkmn.super_shiny = (pkmn_data[:super_shininess]) ? true : false if pkmn_data[:nature] pkmn.nature = pkmn_data[:nature] - else - nature = pkmn.species_data.id_number + GameData::TrainerType.get(trainer.trainer_type).id_number - pkmn.nature = nature % (GameData::Nature::DATA.length / 2) + else # Make the nature random but consistent for the same species used by the same trainer type + species_num = GameData::Species.keys.index(species) || 1 + tr_type_num = GameData::TrainerType.keys.index(@trainer_type) || 1 + idx = (species_num + tr_type_num) % GameData::Nature.count + pkmn.nature = GameData::Nature.get(GameData::Nature.keys[idx]).id end GameData::Stat.each_main do |s| if pkmn_data[:iv] @@ -164,12 +166,3 @@ def to_trainer end end end - -#=============================================================================== -# Deprecated methods -#=============================================================================== -# @deprecated This alias is slated to be removed in v20. -def pbGetTrainerData(tr_type, tr_name, tr_version = 0) - Deprecation.warn_method('pbGetTrainerData', 'v20', 'GameData::Trainer.get(tr_type, tr_name, tr_version)') - return GameData::Trainer.get(tr_type, tr_name, tr_version) -end diff --git a/Data/Scripts/010_Data/002_PBS data/016_Metadata.rb b/Data/Scripts/010_Data/002_PBS data/016_Metadata.rb new file mode 100644 index 0000000000..dae4443f4b --- /dev/null +++ b/Data/Scripts/010_Data/002_PBS data/016_Metadata.rb @@ -0,0 +1,94 @@ +module GameData + class Metadata + attr_reader :id + attr_reader :start_money + attr_reader :start_item_storage + attr_reader :home + attr_reader :real_storage_creator + attr_reader :wild_battle_BGM + attr_reader :trainer_battle_BGM + attr_reader :wild_victory_BGM + attr_reader :trainer_victory_BGM + attr_reader :wild_capture_ME + attr_reader :surf_BGM + attr_reader :bicycle_BGM + + DATA = {} + DATA_FILENAME = "metadata.dat" + + SCHEMA = { + "StartMoney" => [1, "u"], + "StartItemStorage" => [2, "*e", :Item], + "Home" => [3, "vuuu"], + "StorageCreator" => [4, "s"], + "WildBattleBGM" => [5, "s"], + "TrainerBattleBGM" => [6, "s"], + "WildVictoryBGM" => [7, "s"], + "TrainerVictoryBGM" => [8, "s"], + "WildCaptureME" => [9, "s"], + "SurfBGM" => [10, "s"], + "BicycleBGM" => [11, "s"] + } + + extend ClassMethodsIDNumbers + include InstanceMethods + + def self.editor_properties + return [ + ["StartMoney", LimitProperty.new(Settings::MAX_MONEY), _INTL("The amount of money that the player starts the game with.")], + ["StartItemStorage", GameDataPoolProperty.new(:Item), _INTL("Items that are already in the player's PC at the start of the game.")], + ["Home", MapCoordsFacingProperty, _INTL("Map ID and X/Y coordinates of where the player goes after a loss if no Pokémon Center was visited.")], + ["StorageCreator", StringProperty, _INTL("Name of the Pokémon Storage creator (the storage option is named \"XXX's PC\").")], + ["WildBattleBGM", BGMProperty, _INTL("Default BGM for wild Pokémon battles.")], + ["TrainerBattleBGM", BGMProperty, _INTL("Default BGM for Trainer battles.")], + ["WildVictoryBGM", BGMProperty, _INTL("Default BGM played after winning a wild Pokémon battle.")], + ["TrainerVictoryBGM", BGMProperty, _INTL("Default BGM played after winning a Trainer battle.")], + ["WildCaptureME", MEProperty, _INTL("Default ME played after catching a Pokémon.")], + ["SurfBGM", BGMProperty, _INTL("BGM played while surfing.")], + ["BicycleBGM", BGMProperty, _INTL("BGM played while on a bicycle.")] + ] + end + + def self.get + return DATA[0] + end + + def initialize(hash) + @id = hash[:id] + @start_money = hash[:start_money] || 3000 + @start_item_storage = hash[:start_item_storage] || [] + @home = hash[:home] + @real_storage_creator = hash[:storage_creator] + @wild_battle_BGM = hash[:wild_battle_BGM] + @trainer_battle_BGM = hash[:trainer_battle_BGM] + @wild_victory_BGM = hash[:wild_victory_BGM] + @trainer_victory_BGM = hash[:trainer_victory_BGM] + @wild_capture_ME = hash[:wild_capture_ME] + @surf_BGM = hash[:surf_BGM] + @bicycle_BGM = hash[:bicycle_BGM] + end + + # @return [String] the translated name of the Pokémon Storage creator + def storage_creator + ret = pbGetMessage(MessageTypes::StorageCreator, 0) + return nil_or_empty?(ret) ? _INTL("Bill") : ret + end + + def property_from_string(str) + case str + when "StartMoney" then return @start_money + when "StartItemStorage" then return @start_item_storage + when "Home" then return @home + when "StorageCreator" then return @real_storage_creator + when "WildBattleBGM" then return @wild_battle_BGM + when "TrainerBattleBGM" then return @trainer_battle_BGM + when "WildVictoryBGM" then return @wild_victory_BGM + when "TrainerVictoryBGM" then return @trainer_victory_BGM + when "WildCaptureME" then return @wild_capture_ME + when "SurfBGM" then return @surf_BGM + when "BicycleBGM" then return @bicycle_BGM + end + return nil + end + end +end diff --git a/Data/Scripts/010_Data/002_PBS data/017_PlayerMetadata.rb b/Data/Scripts/010_Data/002_PBS data/017_PlayerMetadata.rb new file mode 100644 index 0000000000..18c98a8cb7 --- /dev/null +++ b/Data/Scripts/010_Data/002_PBS data/017_PlayerMetadata.rb @@ -0,0 +1,100 @@ +module GameData + class PlayerMetadata + attr_reader :id + attr_reader :trainer_type + attr_reader :walk_charset + attr_reader :home + + DATA = {} + DATA_FILENAME = "player_metadata.dat" + + SCHEMA = { + "TrainerType" => [1, "e", :TrainerType], + "WalkCharset" => [2, "s"], + "RunCharset" => [3, "s"], + "CycleCharset" => [4, "s"], + "SurfCharset" => [5, "s"], + "DiveCharset" => [6, "s"], + "FishCharset" => [7, "s"], + "SurfFishCharset" => [8, "s"], + "Home" => [9, "vuuu"] + } + + extend ClassMethodsIDNumbers + include InstanceMethods + + def self.editor_properties + return [ + ["TrainerType", TrainerTypeProperty, _INTL("Trainer type of this player.")], + ["WalkCharset", CharacterProperty, _INTL("Charset used while the player is still or walking.")], + ["RunCharset", CharacterProperty, _INTL("Charset used while the player is running. Uses WalkCharset if undefined.")], + ["CycleCharset", CharacterProperty, _INTL("Charset used while the player is cycling. Uses RunCharset if undefined.")], + ["SurfCharset", CharacterProperty, _INTL("Charset used while the player is surfing. Uses CycleCharset if undefined.")], + ["DiveCharset", CharacterProperty, _INTL("Charset used while the player is diving. Uses SurfCharset if undefined.")], + ["FishCharset", CharacterProperty, _INTL("Charset used while the player is fishing. Uses WalkCharset if undefined.")], + ["SurfFishCharset", CharacterProperty, _INTL("Charset used while the player is fishing while surfing. Uses FishCharset if undefined.")], + ["Home", MapCoordsFacingProperty, _INTL("Map ID and X/Y coordinates of where the player goes after a loss if no Pokémon Center was visited.")] + ] + end + + # @param player_id [Integer] + # @return [self, nil] + def self.get(player_id = 1) + validate player_id => Integer + return self::DATA[player_id] if self::DATA.has_key?(player_id) + return self::DATA[1] + end + + def initialize(hash) + @id = hash[:id] + @trainer_type = hash[:trainer_type] + @walk_charset = hash[:walk_charset] + @run_charset = hash[:run_charset] + @cycle_charset = hash[:cycle_charset] + @surf_charset = hash[:surf_charset] + @dive_charset = hash[:dive_charset] + @fish_charset = hash[:fish_charset] + @surf_fish_charset = hash[:surf_fish_charset] + @home = hash[:home] + end + + def run_charset + return @run_charset || @walk_charset + end + + def cycle_charset + return @cycle_charset || run_charset + end + + def surf_charset + return @surf_charset || cycle_charset + end + + def dive_charset + return @dive_charset || surf_charset + end + + def fish_charset + return @fish_charset || @walk_charset + end + + def surf_fish_charset + return @surf_fish_charset || fish_charset + end + + def property_from_string(str) + case str + when "TrainerType" then return @trainer_type + when "WalkCharset" then return @walk_charset + when "RunCharset" then return @run_charset + when "CycleCharset" then return @cycle_charset + when "SurfCharset" then return @surf_charset + when "DiveCharset" then return @dive_charset + when "FishCharset" then return @fish_charset + when "SurfFishCharset" then return @surf_fish_charset + when "Home" then return @home + end + return nil + end + end +end diff --git a/Data/Scripts/010_Data/002_PBS data/018_MapMetadata.rb b/Data/Scripts/010_Data/002_PBS data/018_MapMetadata.rb new file mode 100644 index 0000000000..e485c86fc5 --- /dev/null +++ b/Data/Scripts/010_Data/002_PBS data/018_MapMetadata.rb @@ -0,0 +1,148 @@ +module GameData + class MapMetadata + attr_reader :id + attr_reader :real_name + attr_reader :outdoor_map + attr_reader :announce_location + attr_reader :can_bicycle + attr_reader :always_bicycle + attr_reader :teleport_destination + attr_reader :weather + attr_reader :town_map_position + attr_reader :dive_map_id + attr_reader :dark_map + attr_reader :safari_map + attr_reader :snap_edges + attr_reader :random_dungeon + attr_reader :battle_background + attr_reader :wild_battle_BGM + attr_reader :trainer_battle_BGM + attr_reader :wild_victory_BGM + attr_reader :trainer_victory_BGM + attr_reader :wild_capture_ME + attr_reader :town_map_size + attr_reader :battle_environment + attr_reader :flags + + DATA = {} + DATA_FILENAME = "map_metadata.dat" + + SCHEMA = { + "Name" => [1, "s"], + "Outdoor" => [2, "b"], + "ShowArea" => [3, "b"], + "Bicycle" => [4, "b"], + "BicycleAlways" => [5, "b"], + "HealingSpot" => [6, "vuu"], + "Weather" => [7, "eu", :Weather], + "MapPosition" => [8, "uuu"], + "DiveMap" => [9, "v"], + "DarkMap" => [10, "b"], + "SafariMap" => [11, "b"], + "SnapEdges" => [12, "b"], + "Dungeon" => [13, "b"], + "BattleBack" => [14, "s"], + "WildBattleBGM" => [15, "s"], + "TrainerBattleBGM" => [16, "s"], + "WildVictoryBGM" => [17, "s"], + "TrainerVictoryBGM" => [18, "s"], + "WildCaptureME" => [19, "s"], + "MapSize" => [20, "us"], + "Environment" => [21, "e", :Environment], + "Flags" => [22, "*s"] + } + + extend ClassMethodsIDNumbers + include InstanceMethods + + def self.editor_properties + return [ + ["Name", StringProperty, _INTL("The name of the map, as seen by the player. Can be different to the map's name as seen in RMXP.")], + ["Outdoor", BooleanProperty, _INTL("If true, this map is an outdoor map and will be tinted according to time of day.")], + ["ShowArea", BooleanProperty, _INTL("If true, the game will display the map's name upon entry.")], + ["Bicycle", BooleanProperty, _INTL("If true, the bicycle can be used on this map.")], + ["BicycleAlways", BooleanProperty, _INTL("If true, the bicycle will be mounted automatically on this map and cannot be dismounted.")], + ["HealingSpot", MapCoordsProperty, _INTL("Map ID of this Pokémon Center's town, and X and Y coordinates of its entrance within that town.")], + ["Weather", WeatherEffectProperty, _INTL("Weather conditions in effect for this map.")], + ["MapPosition", RegionMapCoordsProperty, _INTL("Identifies the point on the regional map for this map.")], + ["DiveMap", MapProperty, _INTL("Specifies the underwater layer of this map. Use only if this map has deep water.")], + ["DarkMap", BooleanProperty, _INTL("If true, this map is dark and a circle of light appears around the player. Flash can be used to expand the circle.")], + ["SafariMap", BooleanProperty, _INTL("If true, this map is part of the Safari Zone (both indoor and outdoor). Not to be used in the reception desk.")], + ["SnapEdges", BooleanProperty, _INTL("If true, when the player goes near this map's edge, the game doesn't center the player as usual.")], + ["Dungeon", BooleanProperty, _INTL("If true, this map has a randomly generated layout. See the wiki for more information.")], + ["BattleBack", StringProperty, _INTL("PNG files named 'XXX_bg', 'XXX_base0', 'XXX_base1', 'XXX_message' in Battlebacks folder, where XXX is this property's value.")], + ["WildBattleBGM", BGMProperty, _INTL("Default BGM for wild Pokémon battles on this map.")], + ["TrainerBattleBGM", BGMProperty, _INTL("Default BGM for trainer battles on this map.")], + ["WildVictoryBGM", BGMProperty, _INTL("Default BGM played after winning a wild Pokémon battle on this map.")], + ["TrainerVictoryBGM", BGMProperty, _INTL("Default BGM played after winning a Trainer battle on this map.")], + ["WildCaptureME", MEProperty, _INTL("Default ME played after catching a wild Pokémon on this map.")], + ["MapSize", MapSizeProperty, _INTL("The width of the map in Town Map squares, and a string indicating which squares are part of this map.")], + ["Environment", GameDataProperty.new(:Environment), _INTL("The default battle environment for battles on this map.")], + ["Flags", StringListProperty, _INTL("Words/phrases that distinguish this map from others.")] + ] + end + + def initialize(hash) + @id = hash[:id] + @real_name = hash[:name] + @outdoor_map = hash[:outdoor_map] + @announce_location = hash[:announce_location] + @can_bicycle = hash[:can_bicycle] + @always_bicycle = hash[:always_bicycle] + @teleport_destination = hash[:teleport_destination] + @weather = hash[:weather] + @town_map_position = hash[:town_map_position] + @dive_map_id = hash[:dive_map_id] + @dark_map = hash[:dark_map] + @safari_map = hash[:safari_map] + @snap_edges = hash[:snap_edges] + @random_dungeon = hash[:random_dungeon] + @battle_background = hash[:battle_background] + @wild_battle_BGM = hash[:wild_battle_BGM] + @trainer_battle_BGM = hash[:trainer_battle_BGM] + @wild_victory_BGM = hash[:wild_victory_BGM] + @trainer_victory_BGM = hash[:trainer_victory_BGM] + @wild_capture_ME = hash[:wild_capture_ME] + @town_map_size = hash[:town_map_size] + @battle_environment = hash[:battle_environment] + @flags = hash[:flags] || [] + end + + def property_from_string(str) + case str + when "Name" then return @real_name + when "Outdoor" then return @outdoor_map + when "ShowArea" then return @announce_location + when "Bicycle" then return @can_bicycle + when "BicycleAlways" then return @always_bicycle + when "HealingSpot" then return @teleport_destination + when "Weather" then return @weather + when "MapPosition" then return @town_map_position + when "DiveMap" then return @dive_map_id + when "DarkMap" then return @dark_map + when "SafariMap" then return @safari_map + when "SnapEdges" then return @snap_edges + when "Dungeon" then return @random_dungeon + when "BattleBack" then return @battle_background + when "WildBattleBGM" then return @wild_battle_BGM + when "TrainerBattleBGM" then return @trainer_battle_BGM + when "WildVictoryBGM" then return @wild_victory_BGM + when "TrainerVictoryBGM" then return @trainer_victory_BGM + when "WildCaptureME" then return @wild_capture_ME + when "MapSize" then return @town_map_size + when "Environment" then return @battle_environment + when "Flags" then return @flags + end + return nil + end + + # @return [String] the translated name of this map + def name + return pbGetMapNameFromId(@id) + end + + def has_flag?(flag) + return @flags.any? { |f| f.downcase == flag.downcase } + end + end +end diff --git a/Data/Scripts/011_Battle/003_Battle/002_PokeBattle_Battle.rb b/Data/Scripts/011_Battle/001_Battle/001_Battle.rb similarity index 64% rename from Data/Scripts/011_Battle/003_Battle/002_PokeBattle_Battle.rb rename to Data/Scripts/011_Battle/001_Battle/001_Battle.rb index e1ce075b67..474ad22f58 100644 --- a/Data/Scripts/011_Battle/003_Battle/002_PokeBattle_Battle.rb +++ b/Data/Scripts/011_Battle/001_Battle/001_Battle.rb @@ -17,7 +17,7 @@ # to edit some code. Mainly this is to change/add coordinates for the # sprites, describe the relationships between Pokémon and trainers, and to # change messages. The methods that will need editing are as follows: -# class PokeBattle_Battle +# class Battle # def setBattleMode # def pbGetOwnerIndexFromBattlerIndex # def pbGetOpposingIndicesInOrder @@ -26,18 +26,18 @@ # def pbEORShiftDistantBattlers # def pbCanShift? # def pbEndOfRoundPhase -# class TargetMenuDisplay +# class Battle::Scene::TargetMenu # def initialize -# class PokemonDataBox +# class Battle::Scene::PokemonDataBox # def initializeDataBoxGraphic -# module PokeBattle_SceneConstants +# class Battle::Scene # def self.pbBattlerPosition # def self.pbTrainerPosition -# class PokemonTemp -# def recordBattleRule +# class Game_Temp +# def add_battle_rule # (There is no guarantee that this list is complete.) -class PokeBattle_Battle +class Battle attr_reader :scene # Scene object for this battle attr_reader :peer attr_reader :field # Effects common to the whole of a battle @@ -54,8 +54,7 @@ class PokeBattle_Battle attr_reader :player # Player trainer (or array of trainers) attr_reader :opponent # Opponent trainer (or array of trainers) attr_accessor :items # Items held by opponents - attr_accessor :endSpeeches - attr_accessor :endSpeechesWin + attr_accessor :ally_items # Items held by allies attr_accessor :party1starts # Array of start indexes for each player-side trainer's party attr_accessor :party2starts # Array of start indexes for each opponent-side trainer's party attr_accessor :internalBattle # Internal battle flag @@ -67,6 +66,8 @@ class PokeBattle_Battle attr_accessor :controlPlayer # Whether player's Pokémon are AI controlled attr_accessor :expGain # Whether Pokémon can gain Exp/EVs attr_accessor :moneyGain # Whether the player can gain/lose money + attr_accessor :disablePokeBalls # Whether Poké Balls cannot be thrown at all + attr_accessor :sendToBoxes # Send to Boxes (0=ask, 1=don't ask, 2=must add to party) attr_accessor :rules attr_accessor :choices # Choices made by each Pokémon this round attr_accessor :megaEvolution # Battle index of each trainer's Pokémon to Mega Evolve @@ -74,38 +75,39 @@ class PokeBattle_Battle attr_reader :recycleItems attr_reader :belch attr_reader :battleBond + attr_reader :corrosiveGas attr_reader :usedInBattle # Whether each Pokémon was used in battle (for Burmy) attr_reader :successStates # Success states attr_accessor :lastMoveUsed # Last move used attr_accessor :lastMoveUser # Last move user + attr_accessor :first_poke_ball # ID of the first thrown Poké Ball that failed + attr_accessor :poke_ball_failed # Set after first_poke_ball to prevent it being set again attr_reader :switching # True if during the switching phase of the round attr_reader :futureSight # True if Future Sight is hitting attr_reader :endOfRound # True during the end of round attr_accessor :moldBreaker # True if Mold Breaker applies attr_reader :struggle # The Struggle move - include PokeBattle_BattleCommon - def pbRandom(x); return rand(x); end #============================================================================= # Creating the battle class #============================================================================= - def initialize(scene,p1,p2,player,opponent) - if p1.length==0 + def initialize(scene, p1, p2, player, opponent) + if p1.length == 0 raise ArgumentError.new(_INTL("Party 1 has no Pokémon.")) - elsif p2.length==0 + elsif p2.length == 0 raise ArgumentError.new(_INTL("Party 2 has no Pokémon.")) end @scene = scene - @peer = PokeBattle_BattlePeer.create - @battleAI = PokeBattle_AI.new(self) - @field = PokeBattle_ActiveField.new # Whole field (gravity/rooms) - @sides = [PokeBattle_ActiveSide.new, # Player's side - PokeBattle_ActiveSide.new] # Foe's side - @positions = [] # Battler positions + @peer = Peer.new + @battleAI = AI.new(self) + @field = ActiveField.new # Whole field (gravity/rooms) + @sides = [ActiveSide.new, # Player's side + ActiveSide.new] # Foe's side + @positions = [] # Battler positions @battlers = [] - @sideSizes = [1,1] # Single battle, 1v1 + @sideSizes = [1, 1] # Single battle, 1v1 @backdrop = "" @backdropBase = nil @time = 0 @@ -118,8 +120,7 @@ def initialize(scene,p1,p2,player,opponent) @player = player # Array of Player/NPCTrainer objects, or nil @opponent = opponent # Array of NPCTrainer objects, or nil @items = nil - @endSpeeches = [] - @endSpeechesWin = [] + @ally_items = nil # Array of items held by ally. This is just used for Mega Evolution for now. @party1 = p1 @party2 = p2 @party1order = Array.new(@party1.length) { |i| i } @@ -135,21 +136,24 @@ def initialize(scene,p1,p2,player,opponent) @controlPlayer = false @expGain = true @moneyGain = true + @disablePokeBalls = false + @sendToBoxes = 1 @rules = {} @priority = [] @priorityTrickRoom = false @choices = [] @megaEvolution = [ - [-1] * (@player ? @player.length : 1), - [-1] * (@opponent ? @opponent.length : 1) + [-1] * (@player ? @player.length : 1), + [-1] * (@opponent ? @opponent.length : 1) ] @initialItems = [ - Array.new(@party1.length) { |i| (@party1[i]) ? @party1[i].item_id : nil }, - Array.new(@party2.length) { |i| (@party2[i]) ? @party2[i].item_id : nil } + Array.new(@party1.length) { |i| (@party1[i]) ? @party1[i].item_id : nil }, + Array.new(@party2.length) { |i| (@party2[i]) ? @party2[i].item_id : nil } ] @recycleItems = [Array.new(@party1.length, nil), Array.new(@party2.length, nil)] @belch = [Array.new(@party1.length, false), Array.new(@party2.length, false)] @battleBond = [Array.new(@party1.length, false), Array.new(@party2.length, false)] + @corrosiveGas = [Array.new(@party1.length, false), Array.new(@party2.length, false)] @usedInBattle = [Array.new(@party1.length, false), Array.new(@party2.length, false)] @successStates = [] @lastMoveUsed = nil @@ -161,10 +165,12 @@ def initialize(scene,p1,p2,player,opponent) @runCommand = 0 @nextPickupUse = 0 if GameData::Move.exists?(:STRUGGLE) - @struggle = PokeBattle_Move.from_pokemon_move(self, Pokemon::Move.new(:STRUGGLE)) + @struggle = Move.from_pokemon_move(self, Pokemon::Move.new(:STRUGGLE)) else - @struggle = PokeBattle_Struggle.new(self, nil) + @struggle = Move::Struggle.new(self, nil) end + @mega_rings = [] + GameData::Item.each { |item| @mega_rings.push(item.id) if item.has_flag?("MegaRing") } end #============================================================================= @@ -192,15 +198,15 @@ def setBattleMode(mode) end def singleBattle? - return pbSideSize(0)==1 && pbSideSize(1)==1 + return pbSideSize(0) == 1 && pbSideSize(1) == 1 end def pbSideSize(index) - return @sideSizes[index%2] + return @sideSizes[index % 2] end def maxBattlerIndex - return (pbSideSize(0)>pbSideSize(1)) ? (pbSideSize(0)-1)*2 : pbSideSize(1)*2-1 + return (pbSideSize(0) > pbSideSize(1)) ? (pbSideSize(0) - 1) * 2 : (pbSideSize(1) * 2) - 1 end #============================================================================= @@ -217,24 +223,25 @@ def pbGetOwnerIndexFromBattlerIndex(idxBattler) return 0 if !trainer case trainer.length when 2 - n = pbSideSize(idxBattler%2) - return [0,0,1][idxBattler/2] if n==3 - return idxBattler/2 # Same as [0,1][idxBattler/2], i.e. 2 battler slots + n = pbSideSize(idxBattler % 2) + return [0, 0, 1][idxBattler / 2] if n == 3 + return idxBattler / 2 # Same as [0,1][idxBattler/2], i.e. 2 battler slots when 3 - return idxBattler/2 + return idxBattler / 2 end return 0 end def pbGetOwnerFromBattlerIndex(idxBattler) idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler) - return (opposes?(idxBattler)) ? @opponent[idxTrainer] : @player[idxTrainer] + trainer = (opposes?(idxBattler)) ? @opponent : @player + return (trainer.nil?) ? nil : trainer[idxTrainer] end - def pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty) + def pbGetOwnerIndexFromPartyIndex(idxBattler, idxParty) ret = -1 - pbPartyStarts(idxBattler).each_with_index do |start,i| - break if start>idxParty + pbPartyStarts(idxBattler).each_with_index do |start, i| + break if start > idxParty ret = i end return ret @@ -242,44 +249,49 @@ def pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty) # Only used for the purpose of an error message when one trainer tries to # switch another trainer's Pokémon. - def pbGetOwnerFromPartyIndex(idxBattler,idxParty) - idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty) - return (opposes?(idxBattler)) ? @opponent[idxTrainer] : @player[idxTrainer] + def pbGetOwnerFromPartyIndex(idxBattler, idxParty) + idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler, idxParty) + trainer = (opposes?(idxBattler)) ? @opponent : @player + return (trainer.nil?) ? nil : trainer[idxTrainer] end def pbGetOwnerName(idxBattler) idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler) return @opponent[idxTrainer].full_name if opposes?(idxBattler) # Opponent - return @player[idxTrainer].full_name if idxTrainer>0 # Ally trainer + return @player[idxTrainer].full_name if idxTrainer > 0 # Ally trainer return @player[idxTrainer].name # Player end def pbGetOwnerItems(idxBattler) - return [] if !@items || !opposes?(idxBattler) - return @items[pbGetOwnerIndexFromBattlerIndex(idxBattler)] + if opposes?(idxBattler) + return [] if !@items + return @items[pbGetOwnerIndexFromBattlerIndex(idxBattler)] + end + return [] if !@ally_items + return @ally_items[pbGetOwnerIndexFromBattlerIndex(idxBattler)] end # Returns whether the battler in position idxBattler is owned by the same # trainer that owns the Pokémon in party slot idxParty. This assumes that # both the battler position and the party slot are from the same side. - def pbIsOwner?(idxBattler,idxParty) + def pbIsOwner?(idxBattler, idxParty) idxTrainer1 = pbGetOwnerIndexFromBattlerIndex(idxBattler) - idxTrainer2 = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty) - return idxTrainer1==idxTrainer2 + idxTrainer2 = pbGetOwnerIndexFromPartyIndex(idxBattler, idxParty) + return idxTrainer1 == idxTrainer2 end def pbOwnedByPlayer?(idxBattler) return false if opposes?(idxBattler) - return pbGetOwnerIndexFromBattlerIndex(idxBattler)==0 + return pbGetOwnerIndexFromBattlerIndex(idxBattler) == 0 end # Returns the number of Pokémon positions controlled by the given trainerIndex # on the given side of battle. - def pbNumPositions(side,idxTrainer) + def pbNumPositions(side, idxTrainer) ret = 0 - for i in 0...pbSideSize(side) - t = pbGetOwnerIndexFromBattlerIndex(i*2+side) - next if t!=idxTrainer + pbSideSize(side).times do |i| + t = pbGetOwnerIndexFromBattlerIndex((i * 2) + side) + next if t != idxTrainer ret += 1 end return ret @@ -306,27 +318,26 @@ def pbPartyStarts(idxBattler) # Returns the player's team in its display order. Used when showing the party # screen. - def pbPlayerDisplayParty(idxBattler=0) + def pbPlayerDisplayParty(idxBattler = 0) partyOrders = pbPartyOrder(idxBattler) idxStart, _idxEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler) ret = [] - eachInTeamFromBattlerIndex(idxBattler) { |pkmn,i| ret[partyOrders[i]-idxStart] = pkmn } + eachInTeamFromBattlerIndex(idxBattler) { |pkmn, i| ret[partyOrders[i] - idxStart] = pkmn } return ret end - def pbAbleCount(idxBattler=0) + def pbAbleCount(idxBattler = 0) party = pbParty(idxBattler) count = 0 - party.each { |pkmn| count += 1 if pkmn && pkmn.able? } + party.each { |pkmn| count += 1 if pkmn&.able? } return count end - def pbAbleNonActiveCount(idxBattler=0) + def pbAbleNonActiveCount(idxBattler = 0) party = pbParty(idxBattler) - inBattleIndices = [] - eachSameSideBattler(idxBattler) { |b| inBattleIndices.push(b.pokemonIndex) } + inBattleIndices = allSameSideBattlers(idxBattler).map { |b| b.pokemonIndex } count = 0 - party.each_with_index do |pkmn,idxParty| + party.each_with_index do |pkmn, idxParty| next if !pkmn || !pkmn.able? next if inBattleIndices.include?(idxParty) count += 1 @@ -334,8 +345,19 @@ def pbAbleNonActiveCount(idxBattler=0) return count end - def pbAllFainted?(idxBattler=0) - return pbAbleCount(idxBattler)==0 + def pbAllFainted?(idxBattler = 0) + return pbAbleCount(idxBattler) == 0 + end + + def pbTeamAbleNonActiveCount(idxBattler = 0) + inBattleIndices = allSameSideBattlers(idxBattler).map { |b| b.pokemonIndex } + count = 0 + eachInTeamFromBattlerIndex(idxBattler) do |pkmn, i| + next if !pkmn || !pkmn.able? + next if inBattleIndices.include?(i) + count += 1 + end + return count end # For the given side of the field (0=player's, 1=opponent's), returns an array @@ -346,10 +368,10 @@ def pbAbleTeamCounts(side) ret = [] idxTeam = -1 nextStart = 0 - party.each_with_index do |pkmn,i| - if i>=nextStart + party.each_with_index do |pkmn, i| + if i >= nextStart idxTeam += 1 - nextStart = (idxTeam=idxPartyStart && i= idxPartyStart && i < idxPartyEnd } end - def eachInTeam(side,idxTrainer) + def eachInTeam(side, idxTrainer) party = pbParty(side) partyStarts = pbPartyStarts(side) idxPartyStart = partyStarts[idxTrainer] - idxPartyEnd = (idxTrainer=idxPartyStart && i= idxPartyStart && i < idxPartyEnd } end # Used for Illusion. @@ -398,8 +420,8 @@ def pbLastInTeam(idxBattler) partyOrders = pbPartyOrder(idxBattler) idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler) ret = -1 - party.each_with_index do |pkmn,i| - next if i=idxPartyEnd # Check the team only + party.each_with_index do |pkmn, i| + next if i < idxPartyStart || i >= idxPartyEnd # Check the team only next if !pkmn || !pkmn.able? # Can't copy a non-fainted Pokémon or egg ret = i if ret < 0 || partyOrders[i] > partyOrders[ret] end @@ -407,10 +429,10 @@ def pbLastInTeam(idxBattler) end # Used to calculate money gained/lost after winning/losing a battle. - def pbMaxLevelInTeam(side,idxTrainer) + def pbMaxLevelInTeam(side, idxTrainer) ret = 1 - eachInTeam(side,idxTrainer) do |pkmn,_i| - ret = pkmn.level if pkmn.level>ret + eachInTeam(side, idxTrainer) do |pkmn, _i| + ret = pkmn.level if pkmn.level > ret end return ret end @@ -418,46 +440,57 @@ def pbMaxLevelInTeam(side,idxTrainer) #============================================================================= # Iterate through battlers #============================================================================= + # Unused def eachBattler @battlers.each { |b| yield b if b && !b.fainted? } end - def eachSameSideBattler(idxBattler=0) + def allBattlers + return @battlers.select { |b| b && !b.fainted? } + end + + # Unused + def eachSameSideBattler(idxBattler = 0) idxBattler = idxBattler.index if idxBattler.respond_to?("index") @battlers.each { |b| yield b if b && !b.fainted? && !b.opposes?(idxBattler) } end - def eachOtherSideBattler(idxBattler=0) + def allSameSideBattlers(idxBattler = 0) + idxBattler = idxBattler.index if idxBattler.respond_to?("index") + return @battlers.select { |b| b && !b.fainted? && !b.opposes?(idxBattler) } + end + + # Unused + def eachOtherSideBattler(idxBattler = 0) idxBattler = idxBattler.index if idxBattler.respond_to?("index") @battlers.each { |b| yield b if b && !b.fainted? && b.opposes?(idxBattler) } end - def pbSideBattlerCount(idxBattler=0) - ret = 0 - eachSameSideBattler(idxBattler) { |_b| ret += 1 } - return ret + def allOtherSideBattlers(idxBattler = 0) + idxBattler = idxBattler.index if idxBattler.respond_to?("index") + return @battlers.select { |b| b && !b.fainted? && b.opposes?(idxBattler) } end - def pbOpposingBattlerCount(idxBattler=0) - ret = 0 - eachOtherSideBattler(idxBattler) { |_b| ret += 1 } - return ret + def pbSideBattlerCount(idxBattler = 0) + return allSameSideBattlers(idxBattler).length + end + + def pbOpposingBattlerCount(idxBattler = 0) + return allOtherSideBattlers(idxBattler).length end # This method only counts the player's Pokémon, not a partner trainer's. def pbPlayerBattlerCount - ret = 0 - eachSameSideBattler { |b| ret += 1 if b.pbOwnedByPlayer? } - return ret + return allSameSideBattlers.select { |b| b.pbOwnedByPlayer? }.length end def pbCheckGlobalAbility(abil) - eachBattler { |b| return b if b.hasActiveAbility?(abil) } + allBattlers.each { |b| return b if b.hasActiveAbility?(abil) } return nil end - def pbCheckOpposingAbility(abil,idxBattler=0,nearOnly=false) - eachOtherSideBattler(idxBattler) do |b| + def pbCheckOpposingAbility(abil, idxBattler = 0, nearOnly = false) + allOtherSideBattlers(idxBattler).each do |b| next if nearOnly && !b.near?(idxBattler) return b if b.hasActiveAbility?(abil) end @@ -478,31 +511,31 @@ def pbGetOpposingIndicesInOrder(idxBattler) return [1] when 2 # 1v2 return [0] if opposes?(idxBattler) - return [3,1] + return [3, 1] when 3 # 1v3 return [0] if opposes?(idxBattler) - return [3,5,1] + return [3, 5, 1] end when 2 case pbSideSize(1) when 1 # 2v1 - return [0,2] if opposes?(idxBattler) + return [0, 2] if opposes?(idxBattler) return [1] when 2 # 2v2 double - return [[3,1],[2,0],[1,3],[0,2]][idxBattler] + return [[3, 1], [2, 0], [1, 3], [0, 2]][idxBattler] when 3 # 2v3 - return [[5,3,1],[2,0],[3,1,5]][idxBattler] if idxBattler<3 - return [0,2] + return [[5, 3, 1], [2, 0], [3, 1, 5]][idxBattler] if idxBattler < 3 + return [0, 2] end when 3 case pbSideSize(1) when 1 # 3v1 - return [2,0,4] if opposes?(idxBattler) + return [2, 0, 4] if opposes?(idxBattler) return [1] when 2 # 3v2 - return [[3,1],[2,4,0],[3,1],[2,0,4],[1,3]][idxBattler] + return [[3, 1], [2, 4, 0], [3, 1], [2, 0, 4], [1, 3]][idxBattler] when 3 # 3v3 triple - return [[5,3,1],[4,2,0],[3,5,1],[2,0,4],[1,3,5],[0,2,4]][idxBattler] + return [[5, 3, 1], [4, 2, 0], [3, 5, 1], [2, 0, 4], [1, 3, 5], [0, 2, 4]][idxBattler] end end return [idxBattler] @@ -511,30 +544,30 @@ def pbGetOpposingIndicesInOrder(idxBattler) #============================================================================= # Comparing the positions of two battlers #============================================================================= - def opposes?(idxBattler1,idxBattler2=0) + def opposes?(idxBattler1, idxBattler2 = 0) idxBattler1 = idxBattler1.index if idxBattler1.respond_to?("index") idxBattler2 = idxBattler2.index if idxBattler2.respond_to?("index") - return (idxBattler1&1)!=(idxBattler2&1) + return (idxBattler1 & 1) != (idxBattler2 & 1) end - def nearBattlers?(idxBattler1,idxBattler2) - return false if idxBattler1==idxBattler2 - return true if pbSideSize(0)<=2 && pbSideSize(1)<=2 + def nearBattlers?(idxBattler1, idxBattler2) + return false if idxBattler1 == idxBattler2 + return true if pbSideSize(0) <= 2 && pbSideSize(1) <= 2 # Get all pairs of battler positions that are not close to each other - pairsArray = [[0,4],[1,5]] # Covers 3v1 and 1v3 + pairsArray = [[0, 4], [1, 5]] # Covers 3v1 and 1v3 case pbSideSize(0) when 3 case pbSideSize(1) when 3 # 3v3 (triple) - pairsArray.push([0,1]) - pairsArray.push([4,5]) + pairsArray.push([0, 1]) + pairsArray.push([4, 5]) when 2 # 3v2 - pairsArray.push([0,1]) - pairsArray.push([3,4]) + pairsArray.push([0, 1]) + pairsArray.push([3, 4]) end when 2 # 2v3 - pairsArray.push([0,1]) - pairsArray.push([2,5]) + pairsArray.push([0, 1]) + pairsArray.push([2, 5]) end # See if any pair matches the two battlers being assessed pairsArray.each do |pair| @@ -546,7 +579,7 @@ def nearBattlers?(idxBattler1,idxBattler2) #============================================================================= # Altering a party or rearranging battlers #============================================================================= - def pbRemoveFromParty(idxBattler,idxParty) + def pbRemoveFromParty(idxBattler, idxParty) party = pbParty(idxBattler) # Erase the Pokémon from the party party[idxParty] = nil @@ -554,27 +587,27 @@ def pbRemoveFromParty(idxBattler,idxParty) # in it (to avoid gaps) partyOrders = pbPartyOrder(idxBattler) partyStarts = pbPartyStarts(idxBattler) - idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty) + idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler, idxParty) idxPartyStart = partyStarts[idxTrainer] - idxPartyEnd = (idxTrainer=idxPartyEnd # Only check the team - next if partyOrders[i]= idxPartyEnd # Only check the team + next if partyOrders[i] < origPartyPos # Appeared before erased Pokémon partyOrders[i] -= 1 # Appeared after erased Pokémon; bump it up by 1 end end - def pbSwapBattlers(idxA,idxB) + def pbSwapBattlers(idxA, idxB) return false if !@battlers[idxA] || !@battlers[idxB] # Can't swap if battlers aren't owned by the same trainer - return false if opposes?(idxA,idxB) - return false if pbGetOwnerIndexFromBattlerIndex(idxA)!=pbGetOwnerIndexFromBattlerIndex(idxB) + return false if opposes?(idxA, idxB) + return false if pbGetOwnerIndexFromBattlerIndex(idxA) != pbGetOwnerIndexFromBattlerIndex(idxB) @battlers[idxA], @battlers[idxB] = @battlers[idxB], @battlers[idxA] @battlers[idxA].index, @battlers[idxB].index = @battlers[idxB].index, @battlers[idxA].index @choices[idxA], @choices[idxB] = @choices[idxB], @choices[idxA] - @scene.pbSwapBattlerSprites(idxA,idxB) + @scene.pbSwapBattlerSprites(idxA, idxB) # Swap the target of any battlers' effects that point at either of the # swapped battlers, to ensure they still point at the correct target # NOTE: LeechSeed is not swapped, because drained HP goes to whichever @@ -585,15 +618,17 @@ def pbSwapBattlers(idxA,idxB) effectsToSwap = [PBEffects::Attract, PBEffects::BideTarget, PBEffects::CounterTarget, + PBEffects::JawLock, PBEffects::LockOnPos, PBEffects::MeanLook, PBEffects::MirrorCoatTarget, + PBEffects::Octolock, PBEffects::SkyDrop, PBEffects::TrappingUser] - eachBattler do |b| - for i in effectsToSwap - next if b.effects[i]!=idxA && b.effects[i]!=idxB - b.effects[i] = (b.effects[i]==idxA) ? idxB : idxA + allBattlers.each do |b| + effectsToSwap.each do |i| + next if b.effects[i] != idxA && b.effects[i] != idxB + b.effects[i] = (b.effects[i] == idxA) ? idxB : idxA end end return true @@ -604,25 +639,48 @@ def pbSwapBattlers(idxA,idxB) #============================================================================= # Returns the battler representing the Pokémon at index idxParty in its party, # on the same side as a battler with battler index of idxBattlerOther. - def pbFindBattler(idxParty,idxBattlerOther=0) - eachSameSideBattler(idxBattlerOther) { |b| return b if b.pokemonIndex==idxParty } + def pbFindBattler(idxParty, idxBattlerOther = 0) + allSameSideBattlers(idxBattlerOther).each { |b| return b if b.pokemonIndex == idxParty } return nil end # Only used for Wish, as the Wishing Pokémon will no longer be in battle. - def pbThisEx(idxBattler,idxParty) + def pbThisEx(idxBattler, idxParty) party = pbParty(idxBattler) if opposes?(idxBattler) - return _INTL("The opposing {1}",party[idxParty].name) if trainerBattle? - return _INTL("The wild {1}",party[idxParty].name) + return _INTL("The opposing {1}", party[idxParty].name) if trainerBattle? + return _INTL("The wild {1}", party[idxParty].name) end - return _INTL("The ally {1}",party[idxParty].name) if !pbOwnedByPlayer?(idxBattler) + return _INTL("The ally {1}", party[idxParty].name) if !pbOwnedByPlayer?(idxBattler) return party[idxParty].name end def pbSetSeen(battler) return if !battler || !@internalBattle - pbPlayer.pokedex.register(battler.displaySpecies,battler.displayGender,battler.displayForm) + if battler.is_a?(Battler) + pbPlayer.pokedex.register(battler.displaySpecies, battler.displayGender, + battler.displayForm, battler.shiny?) + else + pbPlayer.pokedex.register(battler) + end + end + + def pbSetCaught(battler) + return if !battler || !@internalBattle + if battler.is_a?(Battler) + pbPlayer.pokedex.register_caught(battler.displaySpecies) + else + pbPlayer.pokedex.register_caught(battler.species) + end + end + + def pbSetDefeated(battler) + return if !battler || !@internalBattle + if battler.is_a?(Battler) + pbPlayer.pokedex.register_defeated(battler.displaySpecies) + else + pbPlayer.pokedex.register_defeated(battler.species) + end end def nextPickupUse @@ -631,7 +689,7 @@ def nextPickupUse end #============================================================================= - # Weather and terrain + # Weather #============================================================================= def defaultWeather=(value) @field.defaultWeather = value @@ -641,18 +699,18 @@ def defaultWeather=(value) # Returns the effective weather (note that weather effects can be negated) def pbWeather - eachBattler { |b| return :None if b.hasActiveAbility?([:CLOUDNINE, :AIRLOCK]) } + return :None if allBattlers.any? { |b| b.hasActiveAbility?([:CLOUDNINE, :AIRLOCK]) } return @field.weather end # Used for causing weather by a move or by an ability. - def pbStartWeather(user,newWeather,fixedDuration=false,showAnim=true) - return if @field.weather==newWeather + def pbStartWeather(user, newWeather, fixedDuration = false, showAnim = true) + return if @field.weather == newWeather @field.weather = newWeather duration = (fixedDuration) ? 5 : -1 - if duration>0 && user && user.itemActive? - duration = BattleHandlers.triggerWeatherExtenderItem(user.item, - @field.weather,duration,user,self) + if duration > 0 && user && user.itemActive? + duration = Battle::ItemEffects.triggerWeatherExtender(user.item, @field.weather, + duration, user, self) end @field.weatherDuration = duration weather_data = GameData::BattleWeather.try_get(@field.weather) @@ -669,7 +727,7 @@ def pbStartWeather(user,newWeather,fixedDuration=false,showAnim=true) when :ShadowSky then pbDisplay(_INTL("A shadow sky appeared!")) end # Check for end of primordial weather, and weather-triggered form changes - eachBattler { |b| b.pbCheckFormOnWeatherChange } + allBattlers.each { |b| b.pbCheckFormOnWeatherChange } pbEndPrimordialWeather end @@ -693,27 +751,44 @@ def pbEndPrimordialWeather pbDisplay("The mysterious air current has dissipated!") end end - if @field.weather!=oldWeather + if @field.weather != oldWeather # Check for form changes caused by the weather changing - eachBattler { |b| b.pbCheckFormOnWeatherChange } + allBattlers.each { |b| b.pbCheckFormOnWeatherChange } # Start up the default weather - pbStartWeather(nil,@field.defaultWeather) if @field.defaultWeather != :None + pbStartWeather(nil, @field.defaultWeather) if @field.defaultWeather != :None end end + def pbStartWeatherAbility(new_weather, battler, ignore_primal = false) + return if !ignore_primal && [:HarshSun, :HeavyRain, :StrongWinds].include?(@field.weather) + return if @field.weather == new_weather + pbShowAbilitySplash(battler) + if !Scene::USE_ABILITY_SPLASH + pbDisplay(_INTL("{1}'s {2} activated!", battler.pbThis, battler.abilityName)) + end + fixed_duration = false + fixed_duration = true if Settings::FIXED_DURATION_WEATHER_FROM_ABILITY && + ![:HarshSun, :HeavyRain, :StrongWinds].include?(new_weather) + pbStartWeather(battler, new_weather, fixed_duration) + # NOTE: The ability splash is hidden again in def pbStartWeather. + end + + #============================================================================= + # Terrain + #============================================================================= def defaultTerrain=(value) @field.defaultTerrain = value @field.terrain = value @field.terrainDuration = -1 end - def pbStartTerrain(user,newTerrain,fixedDuration=true) - return if @field.terrain==newTerrain + def pbStartTerrain(user, newTerrain, fixedDuration = true) + return if @field.terrain == newTerrain @field.terrain = newTerrain duration = (fixedDuration) ? 5 : -1 - if duration>0 && user && user.itemActive? - duration = BattleHandlers.triggerTerrainExtenderItem(user.item, - newTerrain,duration,user,self) + if duration > 0 && user && user.itemActive? + duration = Battle::ItemEffects.triggerTerrainExtender(user.item, newTerrain, + duration, user, self) end @field.terrainDuration = duration terrain_data = GameData::BattleTerrain.try_get(@field.terrain) @@ -729,44 +804,47 @@ def pbStartTerrain(user,newTerrain,fixedDuration=true) when :Psychic pbDisplay(_INTL("The battlefield got weird!")) end - # Check for terrain seeds that boost stats in a terrain - eachBattler { |b| b.pbItemTerrainStatBoostCheck } + # Check for abilities/items that trigger upon the terrain changing + allBattlers.each { |b| b.pbAbilityOnTerrainChange } + allBattlers.each { |b| b.pbItemTerrainStatBoostCheck } end #============================================================================= # Messages and animations #============================================================================= - def pbDisplay(msg,&block) - @scene.pbDisplayMessage(msg,&block) + def pbDisplay(msg, &block) + @scene.pbDisplayMessage(msg, &block) end def pbDisplayBrief(msg) - @scene.pbDisplayMessage(msg,true) + @scene.pbDisplayMessage(msg, true) end - def pbDisplayPaused(msg,&block) - @scene.pbDisplayPausedMessage(msg,&block) + def pbDisplayPaused(msg, &block) + @scene.pbDisplayPausedMessage(msg, &block) end def pbDisplayConfirm(msg) return @scene.pbDisplayConfirmMessage(msg) end - def pbShowCommands(msg,commands,canCancel=true) - @scene.pbShowCommands(msg,commands,canCancel) + # defaultValue of -1 means "can't cancel". If it's 0 or greater, returns that + # value when pressing the "Back" button. + def pbShowCommands(msg, commands, defaultValue = -1) + return @scene.pbShowCommands(msg, commands, defaultValue) end - def pbAnimation(move,user,targets,hitNum=0) - @scene.pbAnimation(move,user,targets,hitNum) if @showAnims + def pbAnimation(move, user, targets, hitNum = 0) + @scene.pbAnimation(move, user, targets, hitNum) if @showAnims end - def pbCommonAnimation(name,user=nil,targets=nil) - @scene.pbCommonAnimation(name,user,targets) if @showAnims + def pbCommonAnimation(name, user = nil, targets = nil) + @scene.pbCommonAnimation(name, user, targets) if @showAnims end - def pbShowAbilitySplash(battler,delay=false,logTrigger=true) + def pbShowAbilitySplash(battler, delay = false, logTrigger = true) PBDebug.log("[Ability triggered] #{battler.pbThis}'s #{battler.abilityName}") if logTrigger - return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH + return if !Scene::USE_ABILITY_SPLASH @scene.pbShowAbilitySplash(battler) if delay Graphics.frame_rate.times { @scene.pbUpdate } # 1 second @@ -774,12 +852,12 @@ def pbShowAbilitySplash(battler,delay=false,logTrigger=true) end def pbHideAbilitySplash(battler) - return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH + return if !Scene::USE_ABILITY_SPLASH @scene.pbHideAbilitySplash(battler) end def pbReplaceAbilitySplash(battler) - return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH + return if !Scene::USE_ABILITY_SPLASH @scene.pbReplaceAbilitySplash(battler) end end diff --git a/Data/Scripts/011_Battle/003_Battle/003_Battle_StartAndEnd.rb b/Data/Scripts/011_Battle/001_Battle/002_Battle_StartAndEnd.rb similarity index 60% rename from Data/Scripts/011_Battle/003_Battle/003_Battle_StartAndEnd.rb rename to Data/Scripts/011_Battle/001_Battle/002_Battle_StartAndEnd.rb index d685d54ca3..d88b34e96f 100644 --- a/Data/Scripts/011_Battle/003_Battle/003_Battle_StartAndEnd.rb +++ b/Data/Scripts/011_Battle/001_Battle/002_Battle_StartAndEnd.rb @@ -1,4 +1,4 @@ -class PokeBattle_Battle +class Battle class BattleAbortedException < Exception; end def pbAbort @@ -20,20 +20,20 @@ def pbEnsureParticipants # battlers will move to the centre position at the end of a round, but # because they cannot move into a position owned by a different # trainer, it's possible that battlers will be unable to move close - # enough to hit each other if there are multiple trainers on each - # side. - if trainerBattle? && (@sideSizes[0]>2 || @sideSizes[1]>2) && - @player.length>1 && @opponent.length>1 + # enough to hit each other if there are multiple trainers on both + # sides. + if trainerBattle? && (@sideSizes[0] > 2 || @sideSizes[1] > 2) && + @player.length > 1 && @opponent.length > 1 raise _INTL("Can't have battles larger than 2v2 where both sides have multiple trainers") end # Find out how many Pokémon each trainer has side1counts = pbAbleTeamCounts(0) side2counts = pbAbleTeamCounts(1) # Change the size of the battle depending on how many wild Pokémon there are - if wildBattle? && side2counts[0]!=@sideSizes[1] - if @sideSizes[0]==@sideSizes[1] + if wildBattle? && side2counts[0] != @sideSizes[1] + if @sideSizes[0] == @sideSizes[1] # Even number of battlers per side, change both equally - @sideSizes = [side2counts[0],side2counts[0]] + @sideSizes = [side2counts[0], side2counts[0]] else # Uneven number of battlers per side, just change wild side's size @sideSizes[1] = side2counts[0] @@ -43,32 +43,36 @@ def pbEnsureParticipants # side if necessary loop do needsChanging = false - for side in 0...2 # Each side in turn - next if side==1 && wildBattle? # Wild side's size already checked above - sideCounts = (side==0) ? side1counts : side2counts + 2.times do |side| # Each side in turn + next if side == 1 && wildBattle? # Wild side's size already checked above + sideCounts = (side == 0) ? side1counts : side2counts requireds = [] # Find out how many Pokémon each trainer on side needs to have - for i in 0...@sideSizes[side] - idxTrainer = pbGetOwnerIndexFromBattlerIndex(i*2+side) + @sideSizes[side].times do |i| + idxTrainer = pbGetOwnerIndexFromBattlerIndex((i * 2) + side) requireds[idxTrainer] = 0 if requireds[idxTrainer].nil? requireds[idxTrainer] += 1 end # Compare the have values with the need values - if requireds.length>sideCounts.length + if requireds.length > sideCounts.length raise _INTL("Error: def pbGetOwnerIndexFromBattlerIndex gives invalid owner index ({1} for battle type {2}v{3}, trainers {4}v{5})", - requireds.length-1,@sideSizes[0],@sideSizes[1],side1counts.length,side2counts.length) + requireds.length - 1, @sideSizes[0], @sideSizes[1], side1counts.length, side2counts.length) end - sideCounts.each_with_index do |_count,i| - if !requireds[i] || requireds[i]==0 - raise _INTL("Player-side trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)", - i+1,@sideSizes[0],@sideSizes[1]) if side==0 - raise _INTL("Opposing trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)", - i+1,@sideSizes[0],@sideSizes[1]) if side==1 + sideCounts.each_with_index do |_count, i| + if !requireds[i] || requireds[i] == 0 + case side + when 0 + raise _INTL("Player-side trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)", + i + 1, @sideSizes[0], @sideSizes[1]) + when 1 + raise _INTL("Opposing trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)", + i + 1, @sideSizes[0], @sideSizes[1]) + end end - next if requireds[i]<=sideCounts[i] # Trainer has enough Pokémon to fill their positions - if requireds[i]==1 - raise _INTL("Player-side trainer {1} has no able Pokémon",i+1) if side==0 - raise _INTL("Opposing trainer {1} has no able Pokémon",i+1) if side==1 + next if requireds[i] <= sideCounts[i] # Trainer has enough Pokémon to fill their positions + if requireds[i] == 1 + raise _INTL("Player-side trainer {1} has no able Pokémon", i + 1) if side == 0 + raise _INTL("Opposing trainer {1} has no able Pokémon", i + 1) if side == 1 end # Not enough Pokémon, try lowering the number of battler positions needsChanging = true @@ -81,18 +85,18 @@ def pbEnsureParticipants if wildBattle? PBDebug.log("#{@sideSizes[0]}v#{@sideSizes[1]} battle isn't possible " + "(#{side1counts} player-side teams versus #{side2counts[0]} wild Pokémon)") - newSize = @sideSizes[0]-1 + newSize = @sideSizes[0] - 1 else PBDebug.log("#{@sideSizes[0]}v#{@sideSizes[1]} battle isn't possible " + "(#{side1counts} player-side teams versus #{side2counts} opposing teams)") - newSize = @sideSizes.max-1 + newSize = @sideSizes.max - 1 end - if newSize==0 + if newSize == 0 raise _INTL("Couldn't lower either side's size any further, battle isn't possible") end - for side in 0...2 - next if side==1 && wildBattle? # Wild Pokémon's side size is fixed - next if @sideSizes[side]==1 || newSize>@sideSizes[side] + 2.times do |side| + next if side == 1 && wildBattle? # Wild Pokémon's side size is fixed + next if @sideSizes[side] == 1 || newSize > @sideSizes[side] @sideSizes[side] = newSize end PBDebug.log("Trying #{@sideSizes[0]}v#{@sideSizes[1]} battle instead") @@ -102,52 +106,52 @@ def pbEnsureParticipants #============================================================================= # Set up all battlers #============================================================================= - def pbCreateBattler(idxBattler,pkmn,idxParty) + def pbCreateBattler(idxBattler, pkmn, idxParty) if !@battlers[idxBattler].nil? - raise _INTL("Battler index {1} already exists",idxBattler) + raise _INTL("Battler index {1} already exists", idxBattler) end - @battlers[idxBattler] = PokeBattle_Battler.new(self,idxBattler) - @positions[idxBattler] = PokeBattle_ActivePosition.new + @battlers[idxBattler] = Battler.new(self, idxBattler) + @positions[idxBattler] = ActivePosition.new pbClearChoice(idxBattler) - @successStates[idxBattler] = PokeBattle_SuccessState.new - @battlers[idxBattler].pbInitialize(pkmn,idxParty) + @successStates[idxBattler] = SuccessState.new + @battlers[idxBattler].pbInitialize(pkmn, idxParty) end def pbSetUpSides - ret = [[],[]] - for side in 0...2 + ret = [[], []] + 2.times do |side| # Set up wild Pokémon - if side==1 && wildBattle? - pbParty(1).each_with_index do |pkmn,idxPkmn| - pbCreateBattler(2*idxPkmn+side,pkmn,idxPkmn) + if side == 1 && wildBattle? + pbParty(1).each_with_index do |pkmn, idxPkmn| + pbCreateBattler((2 * idxPkmn) + side, pkmn, idxPkmn) # Changes the Pokémon's form upon entering battle (if it should) - @peer.pbOnEnteringBattle(self,pkmn,true) - pbSetSeen(@battlers[2*idxPkmn+side]) + @peer.pbOnEnteringBattle(self, @battlers[(2 * idxPkmn) + side], pkmn, true) + pbSetSeen(@battlers[(2 * idxPkmn) + side]) @usedInBattle[side][idxPkmn] = true end next end # Set up player's Pokémon and trainers' Pokémon - trainer = (side==0) ? @player : @opponent + trainer = (side == 0) ? @player : @opponent requireds = [] # Find out how many Pokémon each trainer on side needs to have - for i in 0...@sideSizes[side] - idxTrainer = pbGetOwnerIndexFromBattlerIndex(i*2+side) + @sideSizes[side].times do |i| + idxTrainer = pbGetOwnerIndexFromBattlerIndex((i * 2) + side) requireds[idxTrainer] = 0 if requireds[idxTrainer].nil? requireds[idxTrainer] += 1 end # For each trainer in turn, find the needed number of Pokémon for them to # send out, and initialize them battlerNumber = 0 - trainer.each_with_index do |_t,idxTrainer| + trainer.each_with_index do |_t, idxTrainer| ret[side][idxTrainer] = [] - eachInTeam(side,idxTrainer) do |pkmn,idxPkmn| + eachInTeam(side, idxTrainer) do |pkmn, idxPkmn| next if !pkmn.able? - idxBattler = 2*battlerNumber+side - pbCreateBattler(idxBattler,pkmn,idxPkmn) + idxBattler = (2 * battlerNumber) + side + pbCreateBattler(idxBattler, pkmn, idxPkmn) ret[side][idxTrainer].push(idxBattler) battlerNumber += 1 - break if ret[side][idxTrainer].length>=requireds[idxTrainer] + break if ret[side][idxTrainer].length >= requireds[idxTrainer] end end end @@ -163,71 +167,71 @@ def pbStartBattleSendOut(sendOuts) foeParty = pbParty(1) case foeParty.length when 1 - pbDisplayPaused(_INTL("Oh! A wild {1} appeared!",foeParty[0].name)) + pbDisplayPaused(_INTL("Oh! A wild {1} appeared!", foeParty[0].name)) when 2 - pbDisplayPaused(_INTL("Oh! A wild {1} and {2} appeared!",foeParty[0].name, - foeParty[1].name)) + pbDisplayPaused(_INTL("Oh! A wild {1} and {2} appeared!", foeParty[0].name, + foeParty[1].name)) when 3 - pbDisplayPaused(_INTL("Oh! A wild {1}, {2} and {3} appeared!",foeParty[0].name, - foeParty[1].name,foeParty[2].name)) + pbDisplayPaused(_INTL("Oh! A wild {1}, {2} and {3} appeared!", foeParty[0].name, + foeParty[1].name, foeParty[2].name)) end else # Trainer battle case @opponent.length when 1 - pbDisplayPaused(_INTL("You are challenged by {1}!",@opponent[0].full_name)) + pbDisplayPaused(_INTL("You are challenged by {1}!", @opponent[0].full_name)) when 2 - pbDisplayPaused(_INTL("You are challenged by {1} and {2}!",@opponent[0].full_name, - @opponent[1].full_name)) + pbDisplayPaused(_INTL("You are challenged by {1} and {2}!", @opponent[0].full_name, + @opponent[1].full_name)) when 3 pbDisplayPaused(_INTL("You are challenged by {1}, {2} and {3}!", - @opponent[0].full_name,@opponent[1].full_name,@opponent[2].full_name)) + @opponent[0].full_name, @opponent[1].full_name, @opponent[2].full_name)) end end # Send out Pokémon (opposing trainers first) - for side in [1,0] - next if side==1 && wildBattle? + [1, 0].each do |side| + next if side == 1 && wildBattle? msg = "" toSendOut = [] - trainers = (side==0) ? @player : @opponent + trainers = (side == 0) ? @player : @opponent # Opposing trainers and partner trainers's messages about sending out Pokémon - trainers.each_with_index do |t,i| - next if side==0 && i==0 # The player's message is shown last - msg += "\r\n" if msg.length>0 + trainers.each_with_index do |t, i| + next if side == 0 && i == 0 # The player's message is shown last + msg += "\r\n" if msg.length > 0 sent = sendOuts[side][i] case sent.length when 1 - msg += _INTL("{1} sent out {2}!",t.full_name,@battlers[sent[0]].name) + msg += _INTL("{1} sent out {2}!", t.full_name, @battlers[sent[0]].name) when 2 - msg += _INTL("{1} sent out {2} and {3}!",t.full_name, - @battlers[sent[0]].name,@battlers[sent[1]].name) + msg += _INTL("{1} sent out {2} and {3}!", t.full_name, + @battlers[sent[0]].name, @battlers[sent[1]].name) when 3 - msg += _INTL("{1} sent out {2}, {3} and {4}!",t.full_name, - @battlers[sent[0]].name,@battlers[sent[1]].name,@battlers[sent[2]].name) + msg += _INTL("{1} sent out {2}, {3} and {4}!", t.full_name, + @battlers[sent[0]].name, @battlers[sent[1]].name, @battlers[sent[2]].name) end toSendOut.concat(sent) end # The player's message about sending out Pokémon - if side==0 - msg += "\r\n" if msg.length>0 + if side == 0 + msg += "\r\n" if msg.length > 0 sent = sendOuts[side][0] case sent.length when 1 - msg += _INTL("Go! {1}!",@battlers[sent[0]].name) + msg += _INTL("Go! {1}!", @battlers[sent[0]].name) when 2 - msg += _INTL("Go! {1} and {2}!",@battlers[sent[0]].name,@battlers[sent[1]].name) + msg += _INTL("Go! {1} and {2}!", @battlers[sent[0]].name, @battlers[sent[1]].name) when 3 - msg += _INTL("Go! {1}, {2} and {3}!",@battlers[sent[0]].name, - @battlers[sent[1]].name,@battlers[sent[2]].name) + msg += _INTL("Go! {1}, {2} and {3}!", @battlers[sent[0]].name, + @battlers[sent[1]].name, @battlers[sent[2]].name) end toSendOut.concat(sent) end - pbDisplayBrief(msg) if msg.length>0 + pbDisplayBrief(msg) if msg.length > 0 # The actual sending out of Pokémon animSendOuts = [] toSendOut.each do |idxBattler| - animSendOuts.push([idxBattler,@battlers[idxBattler].pokemon]) + animSendOuts.push([idxBattler, @battlers[idxBattler].pokemon]) end - pbSendOut(animSendOuts,true) + pbSendOut(animSendOuts, true) end end @@ -238,11 +242,11 @@ def pbStartBattle PBDebug.log("") PBDebug.log("******************************************") logMsg = "[Started battle] " - if @sideSizes[0]==1 && @sideSizes[1]==1 + if @sideSizes[0] == 1 && @sideSizes[1] == 1 logMsg += "Single " - elsif @sideSizes[0]==2 && @sideSizes[1]==2 + elsif @sideSizes[0] == 2 && @sideSizes[1] == 2 logMsg += "Double " - elsif @sideSizes[0]==3 && @sideSizes[1]==3 + elsif @sideSizes[0] == 3 && @sideSizes[1] == 3 logMsg += "Triple " else logMsg += "#{@sideSizes[0]}v#{@sideSizes[1]} " @@ -297,7 +301,7 @@ def pbStartBattleCore pbDisplay(_INTL("The battlefield is weird!")) end # Abilities upon entering battle - pbOnActiveAll + pbOnAllBattlersEnteringBattle # Main battle loop pbBattleLoop end @@ -309,8 +313,8 @@ def pbBattleLoop @turnCount = 0 loop do # Now begin the battle loop PBDebug.log("") - PBDebug.log("***Round #{@turnCount+1}***") - if @debug && @turnCount>=100 + PBDebug.log("***Round #{@turnCount + 1}***") + if @debug && @turnCount >= 100 @decision = pbDecisionOnTime PBDebug.log("") PBDebug.log("***Undecided after 100 rounds, aborting***") @@ -320,13 +324,13 @@ def pbBattleLoop PBDebug.log("") # Command phase PBDebug.logonerr { pbCommandPhase } - break if @decision>0 + break if @decision > 0 # Attack phase PBDebug.logonerr { pbAttackPhase } - break if @decision>0 + break if @decision > 0 # End of round phase PBDebug.logonerr { pbEndOfRoundPhase } - break if @decision>0 + break if @decision > 0 @turnCount += 1 end pbEndOfBattle @@ -340,27 +344,29 @@ def pbGainMoney # Money rewarded from opposing trainers if trainerBattle? tMoney = 0 - @opponent.each_with_index do |t,i| + @opponent.each_with_index do |t, i| tMoney += pbMaxLevelInTeam(1, i) * t.base_money end tMoney *= 2 if @field.effects[PBEffects::AmuletCoin] tMoney *= 2 if @field.effects[PBEffects::HappyHour] oldMoney = pbPlayer.money pbPlayer.money += tMoney - moneyGained = pbPlayer.money-oldMoney - if moneyGained>0 - pbDisplayPaused(_INTL("You got ${1} for winning!",moneyGained.to_s_formatted)) + moneyGained = pbPlayer.money - oldMoney + if moneyGained > 0 + $stats.battle_money_gained += moneyGained + pbDisplayPaused(_INTL("You got ${1} for winning!", moneyGained.to_s_formatted)) end end # Pick up money scattered by Pay Day - if @field.effects[PBEffects::PayDay]>0 + if @field.effects[PBEffects::PayDay] > 0 @field.effects[PBEffects::PayDay] *= 2 if @field.effects[PBEffects::AmuletCoin] @field.effects[PBEffects::PayDay] *= 2 if @field.effects[PBEffects::HappyHour] oldMoney = pbPlayer.money pbPlayer.money += @field.effects[PBEffects::PayDay] - moneyGained = pbPlayer.money-oldMoney - if moneyGained>0 - pbDisplayPaused(_INTL("You picked up ${1}!",moneyGained.to_s_formatted)) + moneyGained = pbPlayer.money - oldMoney + if moneyGained > 0 + $stats.battle_money_gained += moneyGained + pbDisplayPaused(_INTL("You picked up ${1}!", moneyGained.to_s_formatted)) end end end @@ -368,26 +374,27 @@ def pbGainMoney def pbLoseMoney return if !@internalBattle || !@moneyGain return if $game_switches[Settings::NO_MONEY_LOSS] - maxLevel = pbMaxLevelInTeam(0,0) # Player's Pokémon only, not partner's - multiplier = [8,16,24,36,48,64,80,100,120] + maxLevel = pbMaxLevelInTeam(0, 0) # Player's Pokémon only, not partner's + multiplier = [8, 16, 24, 36, 48, 64, 80, 100, 120] idxMultiplier = [pbPlayer.badge_count, multiplier.length - 1].min - tMoney = maxLevel*multiplier[idxMultiplier] - tMoney = pbPlayer.money if tMoney>pbPlayer.money + tMoney = maxLevel * multiplier[idxMultiplier] + tMoney = pbPlayer.money if tMoney > pbPlayer.money oldMoney = pbPlayer.money pbPlayer.money -= tMoney - moneyLost = oldMoney-pbPlayer.money - if moneyLost>0 + moneyLost = oldMoney - pbPlayer.money + if moneyLost > 0 + $stats.battle_money_lost += moneyLost if trainerBattle? - pbDisplayPaused(_INTL("You gave ${1} to the winner...",moneyLost.to_s_formatted)) + pbDisplayPaused(_INTL("You gave ${1} to the winner...", moneyLost.to_s_formatted)) else - pbDisplayPaused(_INTL("You panicked and dropped ${1}...",moneyLost.to_s_formatted)) + pbDisplayPaused(_INTL("You panicked and dropped ${1}...", moneyLost.to_s_formatted)) end end end def pbEndOfBattle oldDecision = @decision - @decision = 4 if @decision==1 && wildBattle? && @caughtPokemon.length>0 + @decision = 4 if @decision == 1 && wildBattle? && @caughtPokemon.length > 0 case oldDecision ##### WIN ##### when 1 @@ -397,52 +404,54 @@ def pbEndOfBattle @scene.pbTrainerBattleSuccess case @opponent.length when 1 - pbDisplayPaused(_INTL("You defeated {1}!",@opponent[0].full_name)) + pbDisplayPaused(_INTL("You defeated {1}!", @opponent[0].full_name)) when 2 - pbDisplayPaused(_INTL("You defeated {1} and {2}!",@opponent[0].full_name, - @opponent[1].full_name)) + pbDisplayPaused(_INTL("You defeated {1} and {2}!", @opponent[0].full_name, + @opponent[1].full_name)) when 3 - pbDisplayPaused(_INTL("You defeated {1}, {2} and {3}!",@opponent[0].full_name, - @opponent[1].full_name,@opponent[2].full_name)) + pbDisplayPaused(_INTL("You defeated {1}, {2} and {3}!", @opponent[0].full_name, + @opponent[1].full_name, @opponent[2].full_name)) end - @opponent.each_with_index do |_t,i| + @opponent.each_with_index do |trainer, i| @scene.pbShowOpponent(i) - msg = (@endSpeeches[i] && @endSpeeches[i]!="") ? @endSpeeches[i] : "..." - pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/,pbPlayer.name)) + msg = trainer.lose_text + msg = "..." if !msg || msg.empty? + pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/, pbPlayer.name)) end end # Gain money from winning a trainer battle, and from Pay Day - pbGainMoney if @decision!=4 + pbGainMoney if @decision != 4 # Hide remaining trainer - @scene.pbShowOpponent(@opponent.length) if trainerBattle? && @caughtPokemon.length>0 + @scene.pbShowOpponent(@opponent.length) if trainerBattle? && @caughtPokemon.length > 0 ##### LOSE, DRAW ##### when 2, 5 PBDebug.log("") - PBDebug.log("***Player lost***") if @decision==2 - PBDebug.log("***Player drew with opponent***") if @decision==5 + PBDebug.log("***Player lost***") if @decision == 2 + PBDebug.log("***Player drew with opponent***") if @decision == 5 if @internalBattle pbDisplayPaused(_INTL("You have no more Pokémon that can fight!")) if trainerBattle? case @opponent.length when 1 - pbDisplayPaused(_INTL("You lost against {1}!",@opponent[0].full_name)) + pbDisplayPaused(_INTL("You lost against {1}!", @opponent[0].full_name)) when 2 pbDisplayPaused(_INTL("You lost against {1} and {2}!", - @opponent[0].full_name,@opponent[1].full_name)) + @opponent[0].full_name, @opponent[1].full_name)) when 3 pbDisplayPaused(_INTL("You lost against {1}, {2} and {3}!", - @opponent[0].full_name,@opponent[1].full_name,@opponent[2].full_name)) + @opponent[0].full_name, @opponent[1].full_name, @opponent[2].full_name)) end end # Lose money from losing a battle pbLoseMoney pbDisplayPaused(_INTL("You blacked out!")) if !@canLose - elsif @decision==2 + elsif @decision == 2 # Lost in a Battle Frontier battle if @opponent - @opponent.each_with_index do |_t,i| + @opponent.each_with_index do |trainer, i| @scene.pbShowOpponent(i) - msg = (@endSpeechesWin[i] && @endSpeechesWin[i]!="") ? @endSpeechesWin[i] : "..." - pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/,pbPlayer.name)) + msg = trainer.win_text + msg = "..." if !msg || msg.empty? + pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/, pbPlayer.name)) end end end @@ -453,20 +462,20 @@ def pbEndOfBattle # Register captured Pokémon in the Pokédex, and store them pbRecordAndStoreCaughtPokemon # Collect Pay Day money in a wild battle that ended in a capture - pbGainMoney if @decision==4 + pbGainMoney if @decision == 4 # Pass on Pokérus within the party if @internalBattle infected = [] - $Trainer.party.each_with_index do |pkmn,i| - infected.push(i) if pkmn.pokerusStage==1 + $player.party.each_with_index do |pkmn, i| + infected.push(i) if pkmn.pokerusStage == 1 end infected.each do |idxParty| - strain = $Trainer.party[idxParty].pokerusStrain - if idxParty>0 && $Trainer.party[idxParty-1].pokerusStage==0 - $Trainer.party[idxParty-1].givePokerus(strain) if rand(3)==0 # 33% + strain = $player.party[idxParty].pokerusStrain + if idxParty > 0 && $player.party[idxParty - 1].pokerusStage == 0 && rand(3) == 0 # 33% + $player.party[idxParty - 1].givePokerus(strain) end - if idxParty<$Trainer.party.length-1 && $Trainer.party[idxParty+1].pokerusStage==0 - $Trainer.party[idxParty+1].givePokerus(strain) if rand(3)==0 # 33% + if idxParty < $player.party.length - 1 && $player.party[idxParty + 1].pokerusStage == 0 && rand(3) == 0 # 33% + $player.party[idxParty + 1].givePokerus(strain) end end end @@ -475,11 +484,11 @@ def pbEndOfBattle @battlers.each do |b| next if !b pbCancelChoice(b.index) # Restore unused items to Bag - BattleHandlers.triggerAbilityOnSwitchOut(b.ability,b,true) if b.abilityActive? + Battle::AbilityEffects.triggerOnSwitchOut(b.ability, b, true) if b.abilityActive? end - pbParty(0).each_with_index do |pkmn,i| + pbParty(0).each_with_index do |pkmn, i| next if !pkmn - @peer.pbOnLeavingBattle(self,pkmn,@usedInBattle[0][i],true) # Reset form + @peer.pbOnLeavingBattle(self, pkmn, @usedInBattle[0][i], true) # Reset form pkmn.item = @initialItems[0][i] end return @decision @@ -488,41 +497,41 @@ def pbEndOfBattle #============================================================================= # Judging #============================================================================= - def pbJudgeCheckpoint(user,move=nil); end + def pbJudgeCheckpoint(user, move = nil); end def pbDecisionOnTime - counts = [0,0] - hpTotals = [0,0] - for side in 0...2 + counts = [0, 0] + hpTotals = [0, 0] + 2.times do |side| pbParty(side).each do |pkmn| next if !pkmn || !pkmn.able? counts[side] += 1 hpTotals[side] += pkmn.hp end end - return 1 if counts[0]>counts[1] # Win (player has more able Pokémon) - return 2 if counts[0]hpTotals[1] # Win (player has more HP in total) - return 2 if hpTotals[0] counts[1] # Win (player has more able Pokémon) + return 2 if counts[0] < counts[1] # Loss (foe has more able Pokémon) + return 1 if hpTotals[0] > hpTotals[1] # Win (player has more HP in total) + return 2 if hpTotals[0] < hpTotals[1] # Loss (foe has more HP in total) return 5 # Draw end # Unused def pbDecisionOnTime2 - counts = [0,0] - hpTotals = [0,0] - for side in 0...2 + counts = [0, 0] + hpTotals = [0, 0] + 2.times do |side| pbParty(side).each do |pkmn| next if !pkmn || !pkmn.able? counts[side] += 1 - hpTotals[side] += 100*pkmn.hp/pkmn.totalhp + hpTotals[side] += 100 * pkmn.hp / pkmn.totalhp end - hpTotals[side] /= counts[side] if counts[side]>1 + hpTotals[side] /= counts[side] if counts[side] > 1 end - return 1 if counts[0]>counts[1] # Win (player has more able Pokémon) - return 2 if counts[0]hpTotals[1] # Win (player has a bigger average HP %) - return 2 if hpTotals[0] counts[1] # Win (player has more able Pokémon) + return 2 if counts[0] < counts[1] # Loss (foe has more able Pokémon) + return 1 if hpTotals[0] > hpTotals[1] # Win (player has a bigger average HP %) + return 2 if hpTotals[0] < hpTotals[1] # Loss (foe has a bigger average HP %) return 5 # Draw end @@ -531,9 +540,12 @@ def pbDecisionOnDraw; return 5; end # Draw def pbJudge fainted1 = pbAllFainted?(0) fainted2 = pbAllFainted?(1) - if fainted1 && fainted2; @decision = pbDecisionOnDraw # Draw - elsif fainted1; @decision = 2 # Loss - elsif fainted2; @decision = 1 # Win + if fainted1 && fainted2 + @decision = pbDecisionOnDraw # Draw + elsif fainted1 + @decision = 2 # Loss + elsif fainted2 + @decision = 1 # Win end end end diff --git a/Data/Scripts/011_Battle/003_Battle/004_Battle_ExpAndMoveLearning.rb b/Data/Scripts/011_Battle/001_Battle/003_Battle_ExpAndMoveLearning.rb similarity index 59% rename from Data/Scripts/011_Battle/003_Battle/004_Battle_ExpAndMoveLearning.rb rename to Data/Scripts/011_Battle/001_Battle/003_Battle_ExpAndMoveLearning.rb index d36553e75c..8617151f23 100644 --- a/Data/Scripts/011_Battle/003_Battle/004_Battle_ExpAndMoveLearning.rb +++ b/Data/Scripts/011_Battle/001_Battle/003_Battle_ExpAndMoveLearning.rb @@ -1,4 +1,4 @@ -class PokeBattle_Battle +class Battle #============================================================================= # Gaining Experience #============================================================================= @@ -8,46 +8,46 @@ def pbGainExp return if !@internalBattle || !@expGain # Go through each battler in turn to find the Pokémon that participated in # battle against it, and award those Pokémon Exp/EVs - expAll = (GameData::Item.exists?(:EXPALL) && $PokemonBag.pbHasItem?(:EXPALL)) + expAll = $player.has_exp_all || $bag.has?(:EXPALL) p1 = pbParty(0) @battlers.each do |b| - next unless b && b.opposes? # Can only gain Exp from fainted foes - next if b.participants.length==0 + next unless b&.opposes? # Can only gain Exp from fainted foes + next if b.participants.length == 0 next unless b.fainted? || b.captured # Count the number of participants numPartic = 0 b.participants.each do |partic| - next unless p1[partic] && p1[partic].able? && pbIsOwner?(0,partic) + next unless p1[partic]&.able? && pbIsOwner?(0, partic) numPartic += 1 end # Find which Pokémon have an Exp Share expShare = [] if !expAll - eachInTeam(0,0) do |pkmn,i| + eachInTeam(0, 0) do |pkmn, i| next if !pkmn.able? next if !pkmn.hasItem?(:EXPSHARE) && GameData::Item.try_get(@initialItems[0][i]) != :EXPSHARE expShare.push(i) end end # Calculate EV and Exp gains for the participants - if numPartic>0 || expShare.length>0 || expAll + if numPartic > 0 || expShare.length > 0 || expAll # Gain EVs and Exp for participants - eachInTeam(0,0) do |pkmn,i| + eachInTeam(0, 0) do |pkmn, i| next if !pkmn.able? next unless b.participants.include?(i) || expShare.include?(i) - pbGainEVsOne(i,b) - pbGainExpOne(i,b,numPartic,expShare,expAll) + pbGainEVsOne(i, b) + pbGainExpOne(i, b, numPartic, expShare, expAll, !pkmn.shadowPokemon?) end # Gain EVs and Exp for all other Pokémon because of Exp All if expAll showMessage = true - eachInTeam(0,0) do |pkmn,i| + eachInTeam(0, 0) do |pkmn, i| next if !pkmn.able? next if b.participants.include?(i) || expShare.include?(i) - pbDisplayPaused(_INTL("Your party Pokémon in waiting also got Exp. Points!")) if showMessage + pbDisplayPaused(_INTL("Your other Pokémon also gained Exp. Points!")) if showMessage showMessage = false - pbGainEVsOne(i,b) - pbGainExpOne(i,b,numPartic,expShare,expAll,false) + pbGainEVsOne(i, b) + pbGainExpOne(i, b, numPartic, expShare, expAll, false) end end end @@ -56,22 +56,22 @@ def pbGainExp end end - def pbGainEVsOne(idxParty,defeatedBattler) + def pbGainEVsOne(idxParty, defeatedBattler) pkmn = pbParty(0)[idxParty] # The Pokémon gaining EVs from defeatedBattler evYield = defeatedBattler.pokemon.evYield # Num of effort points pkmn already has evTotal = 0 GameData::Stat.each_main { |s| evTotal += pkmn.ev[s.id] } # Modify EV yield based on pkmn's held item - if !BattleHandlers.triggerEVGainModifierItem(pkmn.item,pkmn,evYield) - BattleHandlers.triggerEVGainModifierItem(@initialItems[0][idxParty],pkmn,evYield) + if !Battle::ItemEffects.triggerEVGainModifier(pkmn.item, pkmn, evYield) + Battle::ItemEffects.triggerEVGainModifier(@initialItems[0][idxParty], pkmn, evYield) end # Double EV gain because of Pokérus - if pkmn.pokerusStage>=1 # Infected or cured + if pkmn.pokerusStage >= 1 # Infected or cured evYield.each_key { |stat| evYield[stat] *= 2 } end # Gain EVs for each stat in turn - if pkmn.shadowPokemon? && pkmn.saved_ev + if pkmn.shadowPokemon? && pkmn.heartStage <= 3 && pkmn.saved_ev pkmn.saved_ev.each_value { |e| evTotal += e } GameData::Stat.each_main do |s| evGain = evYield[s.id].clamp(0, Pokemon::EV_STAT_LIMIT - pkmn.ev[s.id] - pkmn.saved_ev[s.id]) @@ -89,11 +89,11 @@ def pbGainEVsOne(idxParty,defeatedBattler) end end - def pbGainExpOne(idxParty,defeatedBattler,numPartic,expShare,expAll,showMessages=true) - pkmn = pbParty(0)[idxParty] # The Pokémon gaining EVs from defeatedBattler + def pbGainExpOne(idxParty, defeatedBattler, numPartic, expShare, expAll, showMessages = true) + pkmn = pbParty(0)[idxParty] # The Pokémon gaining Exp from defeatedBattler growth_rate = pkmn.growth_rate # Don't bother calculating if gainer is already at max Exp - if pkmn.exp>=growth_rate.maximum_exp + if pkmn.exp >= growth_rate.maximum_exp pkmn.calc_stats # To ensure new EVs still have an effect return end @@ -102,30 +102,30 @@ def pbGainExpOne(idxParty,defeatedBattler,numPartic,expShare,expAll,showMessages level = defeatedBattler.level # Main Exp calculation exp = 0 - a = level*defeatedBattler.pokemon.base_exp - if expShare.length>0 && (isPartic || hasExpShare) - if numPartic==0 # No participants, all Exp goes to Exp Share holders + a = level * defeatedBattler.pokemon.base_exp + if expShare.length > 0 && (isPartic || hasExpShare) + if numPartic == 0 # No participants, all Exp goes to Exp Share holders exp = a / (Settings::SPLIT_EXP_BETWEEN_GAINERS ? expShare.length : 1) elsif Settings::SPLIT_EXP_BETWEEN_GAINERS # Gain from participating and/or Exp Share - exp = a/(2*numPartic) if isPartic - exp += a/(2*expShare.length) if hasExpShare + exp = a / (2 * numPartic) if isPartic + exp += a / (2 * expShare.length) if hasExpShare else # Gain from participating and/or Exp Share (Exp not split) - exp = (isPartic) ? a : a/2 + exp = (isPartic) ? a : a / 2 end elsif isPartic # Participated in battle, no Exp Shares held by anyone exp = a / (Settings::SPLIT_EXP_BETWEEN_GAINERS ? numPartic : 1) elsif expAll # Didn't participate in battle, gaining Exp due to Exp All # NOTE: Exp All works like the Exp Share from Gen 6+, not like the Exp All # from Gen 1, i.e. Exp isn't split between all Pokémon gaining it. - exp = a/2 + exp = a / 2 end - return if exp<=0 + return if exp <= 0 # Pokémon gain more Exp from trainer battles - exp = (exp*1.5).floor if trainerBattle? + exp = (exp * 1.5).floor if trainerBattle? # Scale the gained Exp based on the gainer's level (or not) if Settings::SCALED_EXP_FORMULA exp /= 5 - levelAdjust = (2*level+10.0)/(pkmn.level+level+10.0) + levelAdjust = ((2 * level) + 10.0) / (pkmn.level + level + 10.0) levelAdjust = levelAdjust**5 levelAdjust = Math.sqrt(levelAdjust) exp *= levelAdjust @@ -139,128 +139,136 @@ def pbGainExpOne(idxParty,defeatedBattler,numPartic,expShare,expAll,showMessages (pkmn.owner.language != 0 && pkmn.owner.language != pbPlayer.language)) if isOutsider if pkmn.owner.language != 0 && pkmn.owner.language != pbPlayer.language - exp = (exp*1.7).floor + exp = (exp * 1.7).floor else - exp = (exp*1.5).floor + exp = (exp * 1.5).floor end end + # Exp. Charm increases Exp gained + exp = exp * 3 / 2 if $bag.has?(:EXPCHARM) # Modify Exp gain based on pkmn's held item - i = BattleHandlers.triggerExpGainModifierItem(pkmn.item,pkmn,exp) - if i<0 - i = BattleHandlers.triggerExpGainModifierItem(@initialItems[0][idxParty],pkmn,exp) + i = Battle::ItemEffects.triggerExpGainModifier(pkmn.item, pkmn, exp) + if i < 0 + i = Battle::ItemEffects.triggerExpGainModifier(@initialItems[0][idxParty], pkmn, exp) + end + exp = i if i >= 0 + # Boost Exp gained with high affection + if Settings::AFFECTION_EFFECTS && @internalBattle && pkmn.affection_level >= 4 && !pkmn.mega? + exp = exp * 6 / 5 + isOutsider = true # To show the "boosted Exp" message end - exp = i if i>=0 # Make sure Exp doesn't exceed the maximum expFinal = growth_rate.add_exp(pkmn.exp, exp) - expGained = expFinal-pkmn.exp - return if expGained<=0 + expGained = expFinal - pkmn.exp + return if expGained <= 0 # "Exp gained" message if showMessages if isOutsider - pbDisplayPaused(_INTL("{1} got a boosted {2} Exp. Points!",pkmn.name,expGained)) + pbDisplayPaused(_INTL("{1} got a boosted {2} Exp. Points!", pkmn.name, expGained)) else - pbDisplayPaused(_INTL("{1} got {2} Exp. Points!",pkmn.name,expGained)) + pbDisplayPaused(_INTL("{1} got {2} Exp. Points!", pkmn.name, expGained)) end end curLevel = pkmn.level newLevel = growth_rate.level_from_exp(expFinal) - if newLevelnewLevel + if curLevel > newLevel # Gained all the Exp now, end the animation pkmn.calc_stats - battler.pbUpdate(false) if battler + battler&.pbUpdate(false) @scene.pbRefreshOne(battler.index) if battler break end # Levelled up - pbCommonAnimation("LevelUp",battler) if battler + pbCommonAnimation("LevelUp", battler) if battler oldTotalHP = pkmn.totalhp oldAttack = pkmn.attack oldDefense = pkmn.defense oldSpAtk = pkmn.spatk oldSpDef = pkmn.spdef oldSpeed = pkmn.speed - if battler && battler.pokemon + if battler&.pokemon battler.pokemon.changeHappiness("levelup") end pkmn.calc_stats - battler.pbUpdate(false) if battler + battler&.pbUpdate(false) @scene.pbRefreshOne(battler.index) if battler - pbDisplayPaused(_INTL("{1} grew to Lv. {2}!",pkmn.name,curLevel)) - @scene.pbLevelUp(pkmn,battler,oldTotalHP,oldAttack,oldDefense, - oldSpAtk,oldSpDef,oldSpeed) + pbDisplayPaused(_INTL("{1} grew to Lv. {2}!", pkmn.name, curLevel)) + @scene.pbLevelUp(pkmn, battler, oldTotalHP, oldAttack, oldDefense, + oldSpAtk, oldSpDef, oldSpeed) # Learn all moves learned at this level moveList = pkmn.getMoveList - moveList.each { |m| pbLearnMove(idxParty,m[1]) if m[0]==curLevel } + moveList.each { |m| pbLearnMove(idxParty, m[1]) if m[0] == curLevel } end end #============================================================================= # Learning a move #============================================================================= - def pbLearnMove(idxParty,newMove) + def pbLearnMove(idxParty, newMove) pkmn = pbParty(0)[idxParty] return if !pkmn pkmnName = pkmn.name battler = pbFindBattler(idxParty) moveName = GameData::Move.get(newMove).name # Pokémon already knows the move - return if pkmn.moves.any? { |m| m && m.id == newMove } + return if pkmn.hasMove?(newMove) # Pokémon has space for the new move; just learn it - if pkmn.moves.length < Pokemon::MAX_MOVES - pkmn.moves.push(Pokemon::Move.new(newMove)) - pbDisplay(_INTL("{1} learned {2}!",pkmnName,moveName)) { pbSEPlay("Pkmn move learnt") } + if pkmn.numMoves < Pokemon::MAX_MOVES + pkmn.learn_move(newMove) + pbDisplay(_INTL("{1} learned {2}!", pkmnName, moveName)) { pbSEPlay("Pkmn move learnt") } if battler - battler.moves.push(PokeBattle_Move.from_pokemon_move(self, pkmn.moves.last)) + battler.moves.push(Move.from_pokemon_move(self, pkmn.moves.last)) battler.pbCheckFormOnMovesetChange end return end # Pokémon already knows the maximum number of moves; try to forget one to learn the new move - loop do - pbDisplayPaused(_INTL("{1} wants to learn {2}, but it already knows {3} moves.", - pkmnName, moveName, pkmn.moves.length.to_word)) - if pbDisplayConfirm(_INTL("Forget a move to learn {1}?",moveName)) - pbDisplayPaused(_INTL("Which move should be forgotten?")) - forgetMove = @scene.pbForgetMove(pkmn,newMove) - if forgetMove>=0 + pbDisplayPaused(_INTL("{1} wants to learn {2}, but it already knows {3} moves.", + pkmnName, moveName, pkmn.numMoves.to_word)) + if pbDisplayConfirm(_INTL("Should {1} forget a move to learn {2}?", pkmnName, moveName)) + loop do + forgetMove = @scene.pbForgetMove(pkmn, newMove) + if forgetMove >= 0 oldMoveName = pkmn.moves[forgetMove].name pkmn.moves[forgetMove] = Pokemon::Move.new(newMove) # Replaces current/total PP - battler.moves[forgetMove] = PokeBattle_Move.from_pokemon_move(self, pkmn.moves[forgetMove]) if battler - pbDisplayPaused(_INTL("1, 2, and... ... ... Ta-da!")) - pbDisplayPaused(_INTL("{1} forgot how to use {2}. And...",pkmnName,oldMoveName)) - pbDisplay(_INTL("{1} learned {2}!",pkmnName,moveName)) { pbSEPlay("Pkmn move learnt") } - battler.pbCheckFormOnMovesetChange if battler + battler.moves[forgetMove] = Move.from_pokemon_move(self, pkmn.moves[forgetMove]) if battler + pbDisplayPaused(_INTL("1, 2, and... ... ... Ta-da!")) { pbSEPlay("Battle ball drop") } + pbDisplayPaused(_INTL("{1} forgot how to use {2}. And...", pkmnName, oldMoveName)) + pbDisplay(_INTL("{1} learned {2}!", pkmnName, moveName)) { pbSEPlay("Pkmn move learnt") } + battler&.pbCheckFormOnMovesetChange break - elsif pbDisplayConfirm(_INTL("Give up on learning {1}?",moveName)) - pbDisplay(_INTL("{1} did not learn {2}.",pkmnName,moveName)) + elsif pbDisplayConfirm(_INTL("Give up on learning {1}?", moveName)) + pbDisplay(_INTL("{1} did not learn {2}.", pkmnName, moveName)) break end - elsif pbDisplayConfirm(_INTL("Give up on learning {1}?",moveName)) - pbDisplay(_INTL("{1} did not learn {2}.",pkmnName,moveName)) - break end + else + pbDisplay(_INTL("{1} did not learn {2}.", pkmnName, moveName)) end end end diff --git a/Data/Scripts/011_Battle/001_Battle/004_Battle_ActionAttacksPriority.rb b/Data/Scripts/011_Battle/001_Battle/004_Battle_ActionAttacksPriority.rb new file mode 100644 index 0000000000..938284f7db --- /dev/null +++ b/Data/Scripts/011_Battle/001_Battle/004_Battle_ActionAttacksPriority.rb @@ -0,0 +1,272 @@ +class Battle + #============================================================================= + # Choosing a move/target + #============================================================================= + def pbCanChooseMove?(idxBattler, idxMove, showMessages, sleepTalk = false) + battler = @battlers[idxBattler] + move = battler.moves[idxMove] + return false unless move + if move.pp == 0 && move.total_pp > 0 && !sleepTalk + pbDisplayPaused(_INTL("There's no PP left for this move!")) if showMessages + return false + end + if battler.effects[PBEffects::Encore] > 0 + idxEncoredMove = battler.pbEncoredMoveIndex + return false if idxEncoredMove >= 0 && idxMove != idxEncoredMove + end + return battler.pbCanChooseMove?(move, true, showMessages, sleepTalk) + end + + def pbCanChooseAnyMove?(idxBattler, sleepTalk = false) + battler = @battlers[idxBattler] + battler.eachMoveWithIndex do |m, i| + next if m.pp == 0 && m.total_pp > 0 && !sleepTalk + if battler.effects[PBEffects::Encore] > 0 + idxEncoredMove = battler.pbEncoredMoveIndex + next if idxEncoredMove >= 0 && i != idxEncoredMove + end + next if !battler.pbCanChooseMove?(m, true, false, sleepTalk) + return true + end + return false + end + + # Called when the Pokémon is Encored, or if it can't use any of its moves. + # Makes the Pokémon use the Encored move (if Encored), or Struggle. + def pbAutoChooseMove(idxBattler, showMessages = true) + battler = @battlers[idxBattler] + if battler.fainted? + pbClearChoice(idxBattler) + return true + end + # Encore + idxEncoredMove = battler.pbEncoredMoveIndex + if idxEncoredMove >= 0 && pbCanChooseMove?(idxBattler, idxEncoredMove, false) + encoreMove = battler.moves[idxEncoredMove] + @choices[idxBattler][0] = :UseMove # "Use move" + @choices[idxBattler][1] = idxEncoredMove # Index of move to be used + @choices[idxBattler][2] = encoreMove # Battle::Move object + @choices[idxBattler][3] = -1 # No target chosen yet + return true if singleBattle? + if pbOwnedByPlayer?(idxBattler) + if showMessages + pbDisplayPaused(_INTL("{1} has to use {2}!", battler.name, encoreMove.name)) + end + return pbChooseTarget(battler, encoreMove) + end + return true + end + # Struggle + if pbOwnedByPlayer?(idxBattler) && showMessages + pbDisplayPaused(_INTL("{1} has no moves left!", battler.name)) + end + @choices[idxBattler][0] = :UseMove # "Use move" + @choices[idxBattler][1] = -1 # Index of move to be used + @choices[idxBattler][2] = @struggle # Struggle Battle::Move object + @choices[idxBattler][3] = -1 # No target chosen yet + return true + end + + def pbRegisterMove(idxBattler, idxMove, showMessages = true) + battler = @battlers[idxBattler] + move = battler.moves[idxMove] + return false if !pbCanChooseMove?(idxBattler, idxMove, showMessages) + @choices[idxBattler][0] = :UseMove # "Use move" + @choices[idxBattler][1] = idxMove # Index of move to be used + @choices[idxBattler][2] = move # Battle::Move object + @choices[idxBattler][3] = -1 # No target chosen yet + return true + end + + def pbChoseMove?(idxBattler, moveID) + return false if !@battlers[idxBattler] || @battlers[idxBattler].fainted? + if @choices[idxBattler][0] == :UseMove && @choices[idxBattler][1] + return @choices[idxBattler][2].id == moveID + end + return false + end + + def pbChoseMoveFunctionCode?(idxBattler, code) + return false if @battlers[idxBattler].fainted? + if @choices[idxBattler][0] == :UseMove && @choices[idxBattler][1] + return @choices[idxBattler][2].function == code + end + return false + end + + def pbRegisterTarget(idxBattler, idxTarget) + @choices[idxBattler][3] = idxTarget # Set target of move + end + + # Returns whether the idxTarget will be targeted by a move with target_data + # used by a battler in idxUser. + def pbMoveCanTarget?(idxUser, idxTarget, target_data) + return false if target_data.num_targets == 0 + case target_data.id + when :NearAlly + return false if opposes?(idxUser, idxTarget) + return false if !nearBattlers?(idxUser, idxTarget) + when :UserOrNearAlly + return true if idxUser == idxTarget + return false if opposes?(idxUser, idxTarget) + return false if !nearBattlers?(idxUser, idxTarget) + when :AllAllies + return false if idxUser == idxTarget + return false if opposes?(idxUser, idxTarget) + when :UserAndAllies + return false if opposes?(idxUser, idxTarget) + when :NearFoe, :RandomNearFoe, :AllNearFoes + return false if !opposes?(idxUser, idxTarget) + return false if !nearBattlers?(idxUser, idxTarget) + when :Foe + return false if !opposes?(idxUser, idxTarget) + when :AllFoes + return false if !opposes?(idxUser, idxTarget) + when :NearOther, :AllNearOthers + return false if !nearBattlers?(idxUser, idxTarget) + when :Other + return false if idxUser == idxTarget + end + return true + end + + #============================================================================= + # Turn order calculation (priority) + #============================================================================= + def pbCalculatePriority(fullCalc = false, indexArray = nil) + needRearranging = false + if fullCalc + @priorityTrickRoom = (@field.effects[PBEffects::TrickRoom] > 0) + # Recalculate everything from scratch + randomOrder = Array.new(maxBattlerIndex + 1) { |i| i } + (randomOrder.length - 1).times do |i| # Can't use shuffle! here + r = i + pbRandom(randomOrder.length - i) + randomOrder[i], randomOrder[r] = randomOrder[r], randomOrder[i] + end + @priority.clear + (0..maxBattlerIndex).each do |i| + b = @battlers[i] + next if !b + # [battler, speed, sub-priority from ability, sub-priority from item, + # final sub-priority, priority, tie-breaker order] + entry = [b, b.pbSpeed, 0, 0, 0, 0, randomOrder[i]] + if @choices[b.index][0] == :UseMove || @choices[b.index][0] == :Shift + # Calculate move's priority + if @choices[b.index][0] == :UseMove + move = @choices[b.index][2] + pri = move.pbPriority(b) + if b.abilityActive? + pri = Battle::AbilityEffects.triggerPriorityChange(b.ability, b, move, pri) + end + entry[5] = pri + @choices[b.index][4] = pri + end + # Calculate sub-priority changes (first/last within priority bracket) + # Abilities (Stall) + if b.abilityActive? + entry[2] = Battle::AbilityEffects.triggerPriorityBracketChange(b.ability, b, self) + end + # Items (Quick Claw, Custap Berry, Lagging Tail, Full Incense) + if b.itemActive? + entry[3] = Battle::ItemEffects.triggerPriorityBracketChange(b.item, b, self) + end + end + @priority.push(entry) + end + needRearranging = true + else + if (@field.effects[PBEffects::TrickRoom] > 0) != @priorityTrickRoom + needRearranging = true + @priorityTrickRoom = (@field.effects[PBEffects::TrickRoom] > 0) + end + # Recheck all battler speeds and changes to priority caused by abilities + @priority.each do |entry| + next if !entry + next if indexArray && !indexArray.include?(entry[0].index) + # Recalculate speed of battler + newSpeed = entry[0].pbSpeed + needRearranging = true if newSpeed != entry[1] + entry[1] = newSpeed + # Recalculate move's priority in case ability has changed + choice = @choices[entry[0].index] + if choice[0] == :UseMove + move = choice[2] + pri = move.pbPriority(entry[0]) + if entry[0].abilityActive? + pri = Battle::AbilityEffects.triggerPriorityChange(entry[0].ability, entry[0], move, pri) + end + needRearranging = true if pri != entry[5] + entry[5] = pri + choice[4] = pri + end + # Recalculate sub-priority change caused by ability (but not by item) + if entry[0].abilityActive? + subPri = Battle::AbilityEffects.triggerPriorityBracketChange(entry[0].ability, entry[0], self) + needRearranging = true if subPri != entry[2] + entry[2] = subPri + end + end + end + # Calculate each battler's overall sub-priority, and whether its ability or + # item is responsible + # NOTE: Going fast beats going slow. A Pokémon with Stall and Quick Claw + # will go first in its priority bracket if Quick Claw triggers, + # regardless of Stall. + @priority.each do |entry| + entry[0].effects[PBEffects::PriorityAbility] = false + entry[0].effects[PBEffects::PriorityItem] = false + subpri = entry[2] # Sub-priority from ability + if (subpri == 0 && entry[3] != 0) || # Ability has no effect, item has effect + (subpri < 0 && entry[3] >= 1) # Ability makes it slower, item makes it faster + subpri = entry[3] # Sub-priority from item + entry[0].effects[PBEffects::PriorityItem] = true + elsif subpri != 0 # Ability has effect, item had superfluous/no effect + entry[0].effects[PBEffects::PriorityAbility] = true + end + entry[4] = subpri # Final sub-priority + end + # Reorder the priority array + if needRearranging + @priority.sort! { |a, b| + if a[5] != b[5] + # Sort by priority (highest value first) + b[5] <=> a[5] + elsif a[4] != b[4] + # Sort by sub-priority (highest value first) + b[4] <=> a[4] + elsif @priorityTrickRoom + # Sort by speed (lowest first), and use tie-breaker if necessary + (a[1] == b[1]) ? b[6] <=> a[6] : a[1] <=> b[1] + else + # Sort by speed (highest first), and use tie-breaker if necessary + (a[1] == b[1]) ? b[6] <=> a[6] : b[1] <=> a[1] + end + } + # Write the priority order to the debug log + logMsg = (fullCalc) ? "[Round order] " : "[Round order recalculated] " + comma = false + @priority.each do |entry| + logMsg += ", " if comma + logMsg += "#{entry[0].pbThis(comma)} (#{entry[0].index})" + comma = true + end + PBDebug.log(logMsg) + end + end + + def pbPriority(onlySpeedSort = false) + ret = [] + if onlySpeedSort + # Sort battlers by their speed stats and tie-breaker order only. + tempArray = [] + @priority.each { |pArray| tempArray.push([pArray[0], pArray[1], pArray[6]]) } + tempArray.sort! { |a, b| (a[1] == b[1]) ? b[2] <=> a[2] : b[1] <=> a[1] } + tempArray.each { |tArray| ret.push(tArray[0]) } + else + # Sort battlers by priority, sub-priority and their speed. Ties are + # resolved in the same way each time this method is called in a round. + @priority.each { |pArray| ret.push(pArray[0]) if !pArray[0].fainted? } + end + return ret + end +end diff --git a/Data/Scripts/011_Battle/001_Battle/005_Battle_ActionSwitching.rb b/Data/Scripts/011_Battle/001_Battle/005_Battle_ActionSwitching.rb new file mode 100644 index 0000000000..5dee7c8e7f --- /dev/null +++ b/Data/Scripts/011_Battle/001_Battle/005_Battle_ActionSwitching.rb @@ -0,0 +1,469 @@ +class Battle + #============================================================================= + # Choosing Pokémon to switch + #============================================================================= + # Checks whether the replacement Pokémon (at party index idxParty) can enter + # battle. + # NOTE: Messages are only shown while in the party screen when choosing a + # command for the next round. + def pbCanSwitchLax?(idxBattler, idxParty, partyScene = nil) + return true if idxParty < 0 + party = pbParty(idxBattler) + return false if idxParty >= party.length + return false if !party[idxParty] + if party[idxParty].egg? + partyScene&.pbDisplay(_INTL("An Egg can't battle!")) + return false + end + if !pbIsOwner?(idxBattler, idxParty) + if partyScene + owner = pbGetOwnerFromPartyIndex(idxBattler, idxParty) + partyScene.pbDisplay(_INTL("You can't switch {1}'s Pokémon with one of yours!", + owner.name)) + end + return false + end + if party[idxParty].fainted? + partyScene&.pbDisplay(_INTL("{1} has no energy left to battle!", party[idxParty].name)) + return false + end + if pbFindBattler(idxParty, idxBattler) + partyScene&.pbDisplay(_INTL("{1} is already in battle!", party[idxParty].name)) + return false + end + return true + end + + # Check whether the currently active Pokémon (at battler index idxBattler) can + # switch out (and that its replacement at party index idxParty can switch in). + # NOTE: Messages are only shown while in the party screen when choosing a + # command for the next round. + def pbCanSwitch?(idxBattler, idxParty = -1, partyScene = nil) + # Check whether party Pokémon can switch in + return false if !pbCanSwitchLax?(idxBattler, idxParty, partyScene) + # Make sure another battler isn't already choosing to switch to the party + # Pokémon + allSameSideBattlers(idxBattler).each do |b| + next if choices[b.index][0] != :SwitchOut || choices[b.index][1] != idxParty + partyScene&.pbDisplay(_INTL("{1} has already been selected.", + pbParty(idxBattler)[idxParty].name)) + return false + end + # Check whether battler can switch out + battler = @battlers[idxBattler] + return true if battler.fainted? + # Ability/item effects that allow switching no matter what + if battler.abilityActive? && + Battle::AbilityEffects.triggerCertainSwitching(battler.ability, battler, self) + return true + end + if battler.itemActive? && + Battle::ItemEffects.triggerCertainSwitching(battler.item, battler, self) + return true + end + # Other certain switching effects + return true if Settings::MORE_TYPE_EFFECTS && battler.pbHasType?(:GHOST) + # Other certain trapping effects + if battler.trappedInBattle? + partyScene&.pbDisplay(_INTL("{1} can't be switched out!", battler.pbThis)) + return false + end + # Trapping abilities/items + allOtherSideBattlers(idxBattler).each do |b| + next if !b.abilityActive? + if Battle::AbilityEffects.triggerTrappingByTarget(b.ability, battler, b, self) + partyScene&.pbDisplay(_INTL("{1}'s {2} prevents switching!", + b.pbThis, b.abilityName)) + return false + end + end + allOtherSideBattlers(idxBattler).each do |b| + next if !b.itemActive? + if Battle::ItemEffects.triggerTrappingByTarget(b.item, battler, b, self) + partyScene&.pbDisplay(_INTL("{1}'s {2} prevents switching!", + b.pbThis, b.itemName)) + return false + end + end + return true + end + + def pbCanChooseNonActive?(idxBattler) + pbParty(idxBattler).each_with_index do |_pkmn, i| + return true if pbCanSwitchLax?(idxBattler, i) + end + return false + end + + def pbRegisterSwitch(idxBattler, idxParty) + return false if !pbCanSwitch?(idxBattler, idxParty) + @choices[idxBattler][0] = :SwitchOut + @choices[idxBattler][1] = idxParty # Party index of Pokémon to switch in + @choices[idxBattler][2] = nil + return true + end + + #============================================================================= + # Open the party screen and potentially pick a replacement Pokémon (or AI + # chooses replacement) + #============================================================================= + # Open party screen and potentially choose a Pokémon to switch with. Used in + # all instances where the party screen is opened. + def pbPartyScreen(idxBattler, checkLaxOnly = false, canCancel = false, shouldRegister = false) + ret = -1 + @scene.pbPartyScreen(idxBattler, canCancel) { |idxParty, partyScene| + if checkLaxOnly + next false if !pbCanSwitchLax?(idxBattler, idxParty, partyScene) + elsif !pbCanSwitch?(idxBattler, idxParty, partyScene) + next false + end + if shouldRegister && (idxParty < 0 || !pbRegisterSwitch(idxBattler, idxParty)) + next false + end + ret = idxParty + next true + } + return ret + end + + # For choosing a replacement Pokémon when prompted in the middle of other + # things happening (U-turn, Baton Pass, in def pbEORSwitch). + def pbSwitchInBetween(idxBattler, checkLaxOnly = false, canCancel = false) + return pbPartyScreen(idxBattler, checkLaxOnly, canCancel) if pbOwnedByPlayer?(idxBattler) + return @battleAI.pbDefaultChooseNewEnemy(idxBattler, pbParty(idxBattler)) + end + + #============================================================================= + # Switching Pokémon + #============================================================================= + # General switching method that checks if any Pokémon need to be sent out and, + # if so, does. Called at the end of each round. + def pbEORSwitch(favorDraws = false) + return if @decision > 0 && !favorDraws + return if @decision == 5 && favorDraws + pbJudge + return if @decision > 0 + # Check through each fainted battler to see if that spot can be filled. + switched = [] + loop do + switched.clear + @battlers.each do |b| + next if !b || !b.fainted? + idxBattler = b.index + next if !pbCanChooseNonActive?(idxBattler) + if !pbOwnedByPlayer?(idxBattler) # Opponent/ally is switching in + next if b.wild? # Wild Pokémon can't switch + idxPartyNew = pbSwitchInBetween(idxBattler) + opponent = pbGetOwnerFromBattlerIndex(idxBattler) + # NOTE: The player is only offered the chance to switch their own + # Pokémon when an opponent replaces a fainted Pokémon in single + # battles. In double battles, etc. there is no such offer. + if @internalBattle && @switchStyle && trainerBattle? && pbSideSize(0) == 1 && + opposes?(idxBattler) && !@battlers[0].fainted? && !switched.include?(0) && + pbCanChooseNonActive?(0) && @battlers[0].effects[PBEffects::Outrage] == 0 + idxPartyForName = idxPartyNew + enemyParty = pbParty(idxBattler) + if enemyParty[idxPartyNew].ability == :ILLUSION && !pbCheckGlobalAbility(:NEUTRALIZINGGAS) + new_index = pbLastInTeam(idxBattler) + idxPartyForName = new_index if new_index >= 0 && new_index != idxPartyNew + end + if pbDisplayConfirm(_INTL("{1} is about to send out {2}. Will you switch your Pokémon?", + opponent.full_name, enemyParty[idxPartyForName].name)) + idxPlayerPartyNew = pbSwitchInBetween(0, false, true) + if idxPlayerPartyNew >= 0 + pbMessageOnRecall(@battlers[0]) + pbRecallAndReplace(0, idxPlayerPartyNew) + switched.push(0) + end + end + end + pbRecallAndReplace(idxBattler, idxPartyNew) + switched.push(idxBattler) + elsif trainerBattle? # Player switches in in a trainer battle + idxPlayerPartyNew = pbGetReplacementPokemonIndex(idxBattler) # Owner chooses + pbRecallAndReplace(idxBattler, idxPlayerPartyNew) + switched.push(idxBattler) + else # Player's Pokémon has fainted in a wild battle + switch = false + if pbDisplayConfirm(_INTL("Use next Pokémon?")) + switch = true + else + switch = (pbRun(idxBattler, true) <= 0) + end + if switch + idxPlayerPartyNew = pbGetReplacementPokemonIndex(idxBattler) # Owner chooses + pbRecallAndReplace(idxBattler, idxPlayerPartyNew) + switched.push(idxBattler) + end + end + end + break if switched.length == 0 + pbOnBattlerEnteringBattle(switched) + end + end + + def pbGetReplacementPokemonIndex(idxBattler, random = false) + if random + choices = [] # Find all Pokémon that can switch in + eachInTeamFromBattlerIndex(idxBattler) do |_pkmn, i| + choices.push(i) if pbCanSwitchLax?(idxBattler, i) + end + return -1 if choices.length == 0 + return choices[pbRandom(choices.length)] + else + return pbSwitchInBetween(idxBattler, true) + end + end + + # Actually performs the recalling and sending out in all situations. + def pbRecallAndReplace(idxBattler, idxParty, randomReplacement = false, batonPass = false) + @scene.pbRecall(idxBattler) if !@battlers[idxBattler].fainted? + @battlers[idxBattler].pbAbilitiesOnSwitchOut # Inc. primordial weather check + @scene.pbShowPartyLineup(idxBattler & 1) if pbSideSize(idxBattler) == 1 + pbMessagesOnReplace(idxBattler, idxParty) if !randomReplacement + pbReplace(idxBattler, idxParty, batonPass) + end + + def pbMessageOnRecall(battler) + if battler.pbOwnedByPlayer? + if battler.hp <= battler.totalhp / 4 + pbDisplayBrief(_INTL("Good job, {1}! Come back!", battler.name)) + elsif battler.hp <= battler.totalhp / 2 + pbDisplayBrief(_INTL("OK, {1}! Come back!", battler.name)) + elsif battler.turnCount >= 5 + pbDisplayBrief(_INTL("{1}, that's enough! Come back!", battler.name)) + elsif battler.turnCount >= 2 + pbDisplayBrief(_INTL("{1}, come back!", battler.name)) + else + pbDisplayBrief(_INTL("{1}, switch out! Come back!", battler.name)) + end + else + owner = pbGetOwnerName(battler.index) + pbDisplayBrief(_INTL("{1} withdrew {2}!", owner, battler.name)) + end + end + + # Only called from def pbRecallAndReplace and Battle Arena's def pbSwitch. + def pbMessagesOnReplace(idxBattler, idxParty) + party = pbParty(idxBattler) + newPkmnName = party[idxParty].name + if party[idxParty].ability == :ILLUSION && !pbCheckGlobalAbility(:NEUTRALIZINGGAS) + new_index = pbLastInTeam(idxBattler) + newPkmnName = party[new_index].name if new_index >= 0 && new_index != idxParty + end + if pbOwnedByPlayer?(idxBattler) + opposing = @battlers[idxBattler].pbDirectOpposing + if opposing.fainted? || opposing.hp == opposing.totalhp + pbDisplayBrief(_INTL("You're in charge, {1}!", newPkmnName)) + elsif opposing.hp >= opposing.totalhp / 2 + pbDisplayBrief(_INTL("Go for it, {1}!", newPkmnName)) + elsif opposing.hp >= opposing.totalhp / 4 + pbDisplayBrief(_INTL("Just a little more! Hang in there, {1}!", newPkmnName)) + else + pbDisplayBrief(_INTL("Your opponent's weak! Get 'em, {1}!", newPkmnName)) + end + else + owner = pbGetOwnerFromBattlerIndex(idxBattler) + pbDisplayBrief(_INTL("{1} sent out {2}!", owner.full_name, newPkmnName)) + end + end + + # Only called from def pbRecallAndReplace above and Battle Arena's def + # pbSwitch. + def pbReplace(idxBattler, idxParty, batonPass = false) + party = pbParty(idxBattler) + idxPartyOld = @battlers[idxBattler].pokemonIndex + # Initialise the new Pokémon + @battlers[idxBattler].pbInitialize(party[idxParty], idxParty, batonPass) + # Reorder the party for this battle + partyOrder = pbPartyOrder(idxBattler) + partyOrder[idxParty], partyOrder[idxPartyOld] = partyOrder[idxPartyOld], partyOrder[idxParty] + # Send out the new Pokémon + pbSendOut([[idxBattler, party[idxParty]]]) + pbCalculatePriority(false, [idxBattler]) if Settings::RECALCULATE_TURN_ORDER_AFTER_SPEED_CHANGES + end + + # Called from def pbReplace above and at the start of battle. + # sendOuts is an array; each element is itself an array: [idxBattler,pkmn] + def pbSendOut(sendOuts, startBattle = false) + sendOuts.each { |b| @peer.pbOnEnteringBattle(self, @battlers[b[0]], b[1]) } + @scene.pbSendOutBattlers(sendOuts, startBattle) + sendOuts.each do |b| + @scene.pbResetMoveIndex(b[0]) + pbSetSeen(@battlers[b[0]]) + @usedInBattle[b[0] & 1][b[0] / 2] = true + end + end + + #============================================================================= + # Effects upon a Pokémon entering battle + #============================================================================= + # Called at the start of battle only. + def pbOnAllBattlersEnteringBattle + pbCalculatePriority(true) + battler_indices = [] + allBattlers.each { |b| battler_indices.push(b.index) } + pbOnBattlerEnteringBattle(battler_indices) + pbCalculatePriority + # Check forms are correct + allBattlers.each { |b| b.pbCheckForm } + end + + # Called when one or more Pokémon switch in. Does a lot of things, including + # entry hazards, form changes and items/abilities that trigger upon switching + # in. + def pbOnBattlerEnteringBattle(battler_index, skip_event_reset = false) + battler_index = [battler_index] if !battler_index.is_a?(Array) + battler_index.flatten! + # NOTE: This isn't done for switch commands, because they previously call + # pbRecallAndReplace, which could cause Neutralizing Gas to end, which + # in turn could cause Intimidate to trigger another Pokémon's Eject + # Pack. That Eject Pack should trigger at the end of this method, but + # this resetting would prevent that from happening, so it is skipped + # and instead done earlier in def pbAttackPhaseSwitch. + if !skip_event_reset + allBattlers.each do |b| + b.droppedBelowHalfHP = false + b.statsDropped = false + end + end + # For each battler that entered battle, in speed order + pbPriority(true).each do |b| + next if !battler_index.include?(b.index) || b.fainted? + pbRecordBattlerAsParticipated(b) + pbMessagesOnBattlerEnteringBattle(b) + # Position/field effects triggered by the battler appearing + pbEffectsOnBattlerEnteringPosition(b) # Healing Wish/Lunar Dance + pbEntryHazards(b) + # Battler faints if it is knocked out because of an entry hazard above + if b.fainted? + b.pbFaint + pbGainExp + pbJudge + next + end + b.pbCheckForm + # Primal Revert upon entering battle + pbPrimalReversion(b.index) + # Ending primordial weather, checking Trace + b.pbContinualAbilityChecks(true) + # Abilities that trigger upon switching in + if (!b.fainted? && b.unstoppableAbility?) || b.abilityActive? + Battle::AbilityEffects.triggerOnSwitchIn(b.ability, b, self, true) + end + pbEndPrimordialWeather # Checking this again just in case + # Items that trigger upon switching in (Air Balloon message) + if b.itemActive? + Battle::ItemEffects.triggerOnSwitchIn(b.item, b, self) + end + # Berry check, status-curing ability check + b.pbHeldItemTriggerCheck + b.pbAbilityStatusCureCheck + end + # Check for triggering of Emergency Exit/Wimp Out/Eject Pack (only one will + # be triggered) + pbPriority(true).each do |b| + break if b.pbItemOnStatDropped + break if b.pbAbilitiesOnDamageTaken + end + allBattlers.each do |b| + b.droppedBelowHalfHP = false + b.statsDropped = false + end + end + + def pbRecordBattlerAsParticipated(battler) + # Record money-doubling effect of Amulet Coin/Luck Incense + if !battler.opposes? && [:AMULETCOIN, :LUCKINCENSE].include?(battler.item_id) + @field.effects[PBEffects::AmuletCoin] = true + end + # Update battlers' participants (who will gain Exp/EVs when a battler faints) + allBattlers.each { |b| b.pbUpdateParticipants } + end + + def pbMessagesOnBattlerEnteringBattle(battler) + # Introduce Shadow Pokémon + if battler.shadowPokemon? + pbCommonAnimation("Shadow", battler) + pbDisplay(_INTL("Oh!\nA Shadow Pokémon!")) if battler.opposes? + end + end + + # Called when a Pokémon enters battle, and when Ally Switch is used. + def pbEffectsOnBattlerEnteringPosition(battler) + position = @positions[battler.index] + # Healing Wish + if position.effects[PBEffects::HealingWish] + if battler.canHeal? || battler.status != :NONE + pbCommonAnimation("HealingWish", battler) + pbDisplay(_INTL("The healing wish came true for {1}!", battler.pbThis(true))) + battler.pbRecoverHP(battler.totalhp) + battler.pbCureStatus(false) + position.effects[PBEffects::HealingWish] = false + elsif Settings::MECHANICS_GENERATION < 8 + position.effects[PBEffects::HealingWish] = false + end + end + # Lunar Dance + if position.effects[PBEffects::LunarDance] + full_pp = true + battler.eachMove { |m| full_pp = false if m.pp < m.total_pp } + if battler.canHeal? || battler.status != :NONE || !full_pp + pbCommonAnimation("LunarDance", battler) + pbDisplay(_INTL("{1} became cloaked in mystical moonlight!", battler.pbThis)) + battler.pbRecoverHP(battler.totalhp) + battler.pbCureStatus(false) + battler.eachMove { |m| m.pp = m.total_pp } + position.effects[PBEffects::LunarDance] = false + elsif Settings::MECHANICS_GENERATION < 8 + position.effects[PBEffects::LunarDance] = false + end + end + end + + def pbEntryHazards(battler) + battler_side = battler.pbOwnSide + # Stealth Rock + if battler_side.effects[PBEffects::StealthRock] && battler.takesIndirectDamage? && + GameData::Type.exists?(:ROCK) && !battler.hasActiveItem?(:HEAVYDUTYBOOTS) + bTypes = battler.pbTypes(true) + eff = Effectiveness.calculate(:ROCK, bTypes[0], bTypes[1], bTypes[2]) + if !Effectiveness.ineffective?(eff) + eff = eff.to_f / Effectiveness::NORMAL_EFFECTIVE + battler.pbReduceHP(battler.totalhp * eff / 8, false) + pbDisplay(_INTL("Pointed stones dug into {1}!", battler.pbThis)) + battler.pbItemHPHealCheck + end + end + # Spikes + if battler_side.effects[PBEffects::Spikes] > 0 && battler.takesIndirectDamage? && + !battler.airborne? && !battler.hasActiveItem?(:HEAVYDUTYBOOTS) + spikesDiv = [8, 6, 4][battler_side.effects[PBEffects::Spikes] - 1] + battler.pbReduceHP(battler.totalhp / spikesDiv, false) + pbDisplay(_INTL("{1} is hurt by the spikes!", battler.pbThis)) + battler.pbItemHPHealCheck + end + # Toxic Spikes + if battler_side.effects[PBEffects::ToxicSpikes] > 0 && !battler.fainted? && !battler.airborne? + if battler.pbHasType?(:POISON) + battler_side.effects[PBEffects::ToxicSpikes] = 0 + pbDisplay(_INTL("{1} absorbed the poison spikes!", battler.pbThis)) + elsif battler.pbCanPoison?(nil, false) && !battler.hasActiveItem?(:HEAVYDUTYBOOTS) + if battler_side.effects[PBEffects::ToxicSpikes] == 2 + battler.pbPoison(nil, _INTL("{1} was badly poisoned by the poison spikes!", battler.pbThis), true) + else + battler.pbPoison(nil, _INTL("{1} was poisoned by the poison spikes!", battler.pbThis)) + end + end + end + # Sticky Web + if battler_side.effects[PBEffects::StickyWeb] && !battler.fainted? && !battler.airborne? && + !battler.hasActiveItem?(:HEAVYDUTYBOOTS) + pbDisplay(_INTL("{1} was caught in a sticky web!", battler.pbThis)) + if battler.pbCanLowerStatStage?(:SPEED) + battler.pbLowerStatStage(:SPEED, 1, nil) + battler.pbItemStatRestoreCheck + end + end + end +end diff --git a/Data/Scripts/011_Battle/003_Battle/007_Battle_Action_UseItem.rb b/Data/Scripts/011_Battle/001_Battle/006_Battle_ActionUseItem.rb similarity index 55% rename from Data/Scripts/011_Battle/003_Battle/007_Battle_Action_UseItem.rb rename to Data/Scripts/011_Battle/001_Battle/006_Battle_ActionUseItem.rb index 801583ea6b..7a29cb8c49 100644 --- a/Data/Scripts/011_Battle/003_Battle/007_Battle_Action_UseItem.rb +++ b/Data/Scripts/011_Battle/001_Battle/006_Battle_ActionUseItem.rb @@ -1,16 +1,18 @@ -class PokeBattle_Battle +class Battle #============================================================================= # Choosing to use an item #============================================================================= - def pbCanUseItemOnPokemon?(item,pkmn,battler,scene,showMessages=true) + def pbCanUseItemOnPokemon?(item, pkmn, battler, scene, showMessages = true) if !pkmn || pkmn.egg? scene.pbDisplay(_INTL("It won't have any effect.")) if showMessages return false end # Embargo - if battler && battler.effects[PBEffects::Embargo]>0 - scene.pbDisplay(_INTL("Embargo's effect prevents the item's use on {1}!", - battler.pbThis(true))) if showMessages + if battler && battler.effects[PBEffects::Embargo] > 0 + if showMessages + scene.pbDisplay(_INTL("Embargo's effect prevents the item's use on {1}!", + battler.pbThis(true))) + end return false end return true @@ -24,7 +26,7 @@ def pbItemUsesAllActions?(item) return false end - def pbRegisterItem(idxBattler,item,idxTarget=nil,idxMove=nil) + def pbRegisterItem(idxBattler, item, idxTarget = nil, idxMove = nil) # Register for use of item on a Pokémon in the party @choices[idxBattler][0] = :UseItem @choices[idxBattler][1] = item # ID of item to be used @@ -32,84 +34,78 @@ def pbRegisterItem(idxBattler,item,idxTarget=nil,idxMove=nil) @choices[idxBattler][3] = idxMove # Index of move to recharge (Ethers) # Delete the item from the Bag. If it turns out it will have no effect, it # will be re-added to the Bag later. - pbConsumeItemInBag(item,idxBattler) + pbConsumeItemInBag(item, idxBattler) return true end #============================================================================= # Using an item #============================================================================= - def pbConsumeItemInBag(item,idxBattler) + def pbConsumeItemInBag(item, idxBattler) return if !item - useType = GameData::Item.get(item).battle_use - return if useType==0 || (useType>=6 && useType<=10) # Not consumed upon use + return if !GameData::Item.get(item).consumed_after_use? if pbOwnedByPlayer?(idxBattler) - if !$PokemonBag.pbDeleteItem(item) + if !$bag.remove(item) raise _INTL("Tried to consume item that wasn't in the Bag somehow.") end else items = pbGetOwnerItems(idxBattler) - items.each_with_index do |thisItem,i| - next if thisItem!=item - items[i] = nil - break - end - items.compact! + items.delete_at(items.index(item)) end end - def pbReturnUnusedItemToBag(item,idxBattler) + def pbReturnUnusedItemToBag(item, idxBattler) return if !item - useType = GameData::Item.get(item).battle_use - return if useType==0 || (useType>=6 && useType<=10) # Not consumed upon use + return if !GameData::Item.get(item).consumed_after_use? if pbOwnedByPlayer?(idxBattler) - if $PokemonBag && $PokemonBag.pbCanStore?(item) - $PokemonBag.pbStoreItem(item) + if $bag&.can_add?(item) + $bag.add(item) else raise _INTL("Couldn't return unused item to Bag somehow.") end else items = pbGetOwnerItems(idxBattler) - items.push(item) if items + items&.push(item) end end - def pbUseItemMessage(item,trainerName) + def pbUseItemMessage(item, trainerName) itemName = GameData::Item.get(item).name if itemName.starts_with_vowel? - pbDisplayBrief(_INTL("{1} used an {2}.",trainerName,itemName)) + pbDisplayBrief(_INTL("{1} used an {2}.", trainerName, itemName)) else - pbDisplayBrief(_INTL("{1} used a {2}.",trainerName,itemName)) + pbDisplayBrief(_INTL("{1} used a {2}.", trainerName, itemName)) end end # Uses an item on a Pokémon in the trainer's party. - def pbUseItemOnPokemon(item,idxParty,userBattler) + def pbUseItemOnPokemon(item, idxParty, userBattler) trainerName = pbGetOwnerName(userBattler.index) - pbUseItemMessage(item,trainerName) + pbUseItemMessage(item, trainerName) pkmn = pbParty(userBattler.index)[idxParty] - battler = pbFindBattler(idxParty,userBattler.index) + battler = pbFindBattler(idxParty, userBattler.index) ch = @choices[userBattler.index] - if ItemHandlers.triggerCanUseInBattle(item,pkmn,battler,ch[3],true,self,@scene,false) - ItemHandlers.triggerBattleUseOnPokemon(item,pkmn,battler,ch,@scene) + if ItemHandlers.triggerCanUseInBattle(item, pkmn, battler, ch[3], true, self, @scene, false) + ItemHandlers.triggerBattleUseOnPokemon(item, pkmn, battler, ch, @scene) ch[1] = nil # Delete item from choice return end pbDisplay(_INTL("But it had no effect!")) # Return unused item to Bag - pbReturnUnusedItemToBag(item,userBattler.index) + pbReturnUnusedItemToBag(item, userBattler.index) end # Uses an item on a Pokémon in battle that belongs to the trainer. - def pbUseItemOnBattler(item,idxParty,userBattler) + def pbUseItemOnBattler(item, idxParty, userBattler) trainerName = pbGetOwnerName(userBattler.index) - pbUseItemMessage(item,trainerName) - battler = pbFindBattler(idxParty,userBattler.index) + pbUseItemMessage(item, trainerName) + battler = pbFindBattler(idxParty, userBattler.index) ch = @choices[userBattler.index] if battler - if ItemHandlers.triggerCanUseInBattle(item,battler.pokemon,battler,ch[3],true,self,@scene,false) - ItemHandlers.triggerBattleUseOnBattler(item,battler,@scene) + if ItemHandlers.triggerCanUseInBattle(item, battler.pokemon, battler, ch[3], true, self, @scene, false) + ItemHandlers.triggerBattleUseOnBattler(item, battler, @scene) ch[1] = nil # Delete item from choice + battler.pbItemOnStatDropped return else pbDisplay(_INTL("But it had no effect!")) @@ -118,31 +114,31 @@ def pbUseItemOnBattler(item,idxParty,userBattler) pbDisplay(_INTL("But it's not where this item can be used!")) end # Return unused item to Bag - pbReturnUnusedItemToBag(item,userBattler.index) + pbReturnUnusedItemToBag(item, userBattler.index) end # Uses a Poké Ball in battle directly. - def pbUsePokeBallInBattle(item,idxBattler,userBattler) - idxBattler = userBattler.index if idxBattler<0 + def pbUsePokeBallInBattle(item, idxBattler, userBattler) + idxBattler = userBattler.index if idxBattler < 0 battler = @battlers[idxBattler] - ItemHandlers.triggerUseInBattle(item,battler,self) + ItemHandlers.triggerUseInBattle(item, battler, self) @choices[userBattler.index][1] = nil # Delete item from choice end # Uses an item in battle directly. - def pbUseItemInBattle(item,idxBattler,userBattler) + def pbUseItemInBattle(item, idxBattler, userBattler) trainerName = pbGetOwnerName(userBattler.index) - pbUseItemMessage(item,trainerName) - battler = (idxBattler<0) ? userBattler : @battlers[idxBattler] + pbUseItemMessage(item, trainerName) + battler = (idxBattler < 0) ? userBattler : @battlers[idxBattler] pkmn = battler.pokemon ch = @choices[userBattler.index] - if ItemHandlers.triggerCanUseInBattle(item,pkmn,battler,ch[3],true,self,@scene,false) - ItemHandlers.triggerUseInBattle(item,battler,self) + if ItemHandlers.triggerCanUseInBattle(item, pkmn, battler, ch[3], true, self, @scene, false) + ItemHandlers.triggerUseInBattle(item, battler, self) ch[1] = nil # Delete item from choice return end pbDisplay(_INTL("But it had no effect!")) # Return unused item to Bag - pbReturnUnusedItemToBag(item,userBattler.index) + pbReturnUnusedItemToBag(item, userBattler.index) end end diff --git a/Data/Scripts/011_Battle/003_Battle/008_Battle_Action_Running.rb b/Data/Scripts/011_Battle/001_Battle/007_Battle_ActionRunning.rb similarity index 61% rename from Data/Scripts/011_Battle/003_Battle/008_Battle_Action_Running.rb rename to Data/Scripts/011_Battle/001_Battle/007_Battle_ActionRunning.rb index 815090e61a..61ad9863cc 100644 --- a/Data/Scripts/011_Battle/003_Battle/008_Battle_Action_Running.rb +++ b/Data/Scripts/011_Battle/001_Battle/007_Battle_ActionRunning.rb @@ -1,4 +1,4 @@ -class PokeBattle_Battle +class Battle #============================================================================= # Running from battle #============================================================================= @@ -8,18 +8,15 @@ def pbCanRun?(idxBattler) return false if !@canRun && !battler.opposes? return true if battler.pbHasType?(:GHOST) && Settings::MORE_TYPE_EFFECTS return true if battler.abilityActive? && - BattleHandlers.triggerRunFromBattleAbility(battler.ability,battler) + Battle::AbilityEffects.triggerCertainEscapeFromBattle(battler.ability, battler) return true if battler.itemActive? && - BattleHandlers.triggerRunFromBattleItem(battler.item,battler) - return false if battler.effects[PBEffects::Trapping]>0 || - battler.effects[PBEffects::MeanLook]>=0 || - battler.effects[PBEffects::Ingrain] || - @field.effects[PBEffects::FairyLock]>0 - eachOtherSideBattler(idxBattler) do |b| + Battle::ItemEffects.triggerCertainEscapeFromBattle(battler.item, battler) + return false if battler.trappedInBattle? + allOtherSideBattlers(idxBattler).each do |b| return false if b.abilityActive? && - BattleHandlers.triggerTrappingTargetAbility(b.ability,battler,b,self) + Battle::AbilityEffects.triggerTrappingByTarget(b.ability, battler, b, self) return false if b.itemActive? && - BattleHandlers.triggerTrappingTargetItem(b.item,battler,b,self) + Battle::ItemEffects.triggerTrappingByTarget(b.item, battler, b, self) end return true end @@ -30,7 +27,7 @@ def pbCanRun?(idxBattler) # 1: Succeeded at fleeing, battle will end # duringBattle is true for replacing a fainted Pokémon during the End Of Round # phase, and false for choosing the Run command. - def pbRun(idxBattler,duringBattle=false) + def pbRun(idxBattler, duringBattle = false) battler = @battlers[idxBattler] if battler.opposes? return 0 if trainerBattle? @@ -53,7 +50,7 @@ def pbRun(idxBattler,duringBattle=false) pbDisplayPaused(_INTL("No! There's no running from a Trainer battle!")) elsif pbDisplayConfirm(_INTL("Would you like to forfeit the match and quit now?")) pbSEPlay("Battle flee") - pbDisplay(_INTL("{1} forfeited the match!",self.pbPlayer.name)) + pbDisplay(_INTL("{1} forfeited the match!", self.pbPlayer.name)) @decision = 3 return 1 end @@ -78,46 +75,40 @@ def pbRun(idxBattler,duringBattle=false) return 1 end # Abilities that guarantee escape - if battler.abilityActive? - if BattleHandlers.triggerRunFromBattleAbility(battler.ability,battler) - pbShowAbilitySplash(battler,true) - pbHideAbilitySplash(battler) - pbSEPlay("Battle flee") - pbDisplayPaused(_INTL("You got away safely!")) - @decision = 3 - return 1 - end + if battler.abilityActive? && + Battle::AbilityEffects.triggerCertainEscapeFromBattle(battler.ability, battler) + pbShowAbilitySplash(battler, true) + pbHideAbilitySplash(battler) + pbSEPlay("Battle flee") + pbDisplayPaused(_INTL("You got away safely!")) + @decision = 3 + return 1 end # Held items that guarantee escape - if battler.itemActive? - if BattleHandlers.triggerRunFromBattleItem(battler.item,battler) - pbSEPlay("Battle flee") - pbDisplayPaused(_INTL("{1} fled using its {2}!", - battler.pbThis,battler.itemName)) - @decision = 3 - return 1 - end + if battler.itemActive? && + Battle::ItemEffects.triggerCertainEscapeFromBattle(battler.item, battler) + pbSEPlay("Battle flee") + pbDisplayPaused(_INTL("{1} fled using its {2}!", battler.pbThis, battler.itemName)) + @decision = 3 + return 1 end # Other certain trapping effects - if battler.effects[PBEffects::Trapping]>0 || - battler.effects[PBEffects::MeanLook]>=0 || - battler.effects[PBEffects::Ingrain] || - @field.effects[PBEffects::FairyLock]>0 + if battler.trappedInBattle? pbDisplayPaused(_INTL("You can't escape!")) return 0 end # Trapping abilities/items - eachOtherSideBattler(idxBattler) do |b| + allOtherSideBattlers(idxBattler).each do |b| next if !b.abilityActive? - if BattleHandlers.triggerTrappingTargetAbility(b.ability,battler,b,self) - pbDisplayPaused(_INTL("{1} prevents escape with {2}!",b.pbThis,b.abilityName)) + if Battle::AbilityEffects.triggerTrappingByTarget(b.ability, battler, b, self) + pbDisplayPaused(_INTL("{1} prevents escape with {2}!", b.pbThis, b.abilityName)) return 0 end end - eachOtherSideBattler(idxBattler) do |b| + allOtherSideBattlers(idxBattler).each do |b| next if !b.itemActive? - if BattleHandlers.triggerTrappingTargetItem(b.item,battler,b,self) - pbDisplayPaused(_INTL("{1} prevents escape with {2}!",b.pbThis,b.itemName)) + if Battle::ItemEffects.triggerTrappingByTarget(b.item, battler, b, self) + pbDisplayPaused(_INTL("{1} prevents escape with {2}!", b.pbThis, b.itemName)) return 0 end end @@ -128,18 +119,18 @@ def pbRun(idxBattler,duringBattle=false) @runCommand += 1 if !duringBattle # Make it easier to flee next time speedPlayer = @battlers[idxBattler].speed speedEnemy = 1 - eachOtherSideBattler(idxBattler) do |b| + allOtherSideBattlers(idxBattler).each do |b| speed = b.speed - speedEnemy = speed if speedEnemyspeedEnemy + if speedPlayer > speedEnemy rate = 256 else - rate = (speedPlayer*128)/speedEnemy - rate += @runCommand*30 + rate = (speedPlayer * 128) / speedEnemy + rate += @runCommand * 30 end - if rate>=256 || @battleAI.pbAIRandom(256)= 256 || @battleAI.pbAIRandom(256) < rate pbSEPlay("Battle flee") pbDisplayPaused(_INTL("You got away safely!")) @decision = 3 diff --git a/Data/Scripts/011_Battle/003_Battle/009_Battle_Action_Other.rb b/Data/Scripts/011_Battle/001_Battle/008_Battle_ActionOther.rb similarity index 64% rename from Data/Scripts/011_Battle/003_Battle/009_Battle_Action_Other.rb rename to Data/Scripts/011_Battle/001_Battle/008_Battle_ActionOther.rb index 13e00b3742..e1f87c8616 100644 --- a/Data/Scripts/011_Battle/003_Battle/009_Battle_Action_Other.rb +++ b/Data/Scripts/011_Battle/001_Battle/008_Battle_ActionOther.rb @@ -1,20 +1,20 @@ -class PokeBattle_Battle +class Battle #============================================================================= # Shifting a battler to another position in a battle larger than double #============================================================================= def pbCanShift?(idxBattler) - return false if pbSideSize(0)<=2 && pbSideSize(1)<=2 # Double battle or smaller + return false if pbSideSize(0) <= 2 && pbSideSize(1) <= 2 # Double battle or smaller idxOther = -1 case pbSideSize(idxBattler) when 1 return false # Only one battler on that side when 2 - idxOther = (idxBattler+2)%4 + idxOther = (idxBattler + 2) % 4 when 3 - return false if idxBattler==2 || idxBattler==3 # In middle spot already - idxOther = ((idxBattler%2)==0) ? 2 : 3 + return false if [2, 3].include?(idxBattler) # In middle spot already + idxOther = (idxBattler.even?) ? 2 : 3 end - return false if pbGetOwnerIndexFromBattlerIndex(idxBattler)!=pbGetOwnerIndexFromBattlerIndex(idxOther) + return false if pbGetOwnerIndexFromBattlerIndex(idxBattler) != pbGetOwnerIndexFromBattlerIndex(idxOther) return true end @@ -38,20 +38,21 @@ def pbRegisterCall(idxBattler) def pbCall(idxBattler) battler = @battlers[idxBattler] trainerName = pbGetOwnerName(idxBattler) - pbDisplay(_INTL("{1} called {2}!",trainerName,battler.pbThis(true))) - pbDisplay(_INTL("{1}!",battler.name)) + pbDisplay(_INTL("{1} called {2}!", trainerName, battler.pbThis(true))) + pbDisplay(_INTL("{1}!", battler.name)) if battler.shadowPokemon? if battler.inHyperMode? battler.pokemon.hyper_mode = false - battler.pokemon.adjustHeart(-300) - pbDisplay(_INTL("{1} came to its senses from the Trainer's call!",battler.pbThis)) + battler.pokemon.change_heart_gauge("call") + pbDisplay(_INTL("{1} came to its senses from the Trainer's call!", battler.pbThis)) else pbDisplay(_INTL("But nothing happened!")) end elsif battler.status == :SLEEP battler.pbCureStatus - elsif battler.pbCanRaiseStatStage?(:ACCURACY,battler) - battler.pbRaiseStatStage(:ACCURACY,1,battler) + elsif battler.pbCanRaiseStatStage?(:ACCURACY, battler) + battler.pbRaiseStatStage(:ACCURACY, 1, battler) + battler.pbItemOnStatDropped else pbDisplay(_INTL("But nothing happened!")) end @@ -61,34 +62,38 @@ def pbCall(idxBattler) # Choosing to Mega Evolve a battler #============================================================================= def pbHasMegaRing?(idxBattler) - return true if !pbOwnedByPlayer?(idxBattler) # Assume AI trainer have a ring - Settings::MEGA_RINGS.each { |item| return true if $PokemonBag.pbHasItem?(item) } + if pbOwnedByPlayer?(idxBattler) + @mega_rings.each { |item| return true if $bag.has?(item) } + else + trainer_items = pbGetOwnerItems(idxBattler) + return false if !trainer_items + @mega_rings.each { |item| return true if trainer_items.include?(item) } + end return false end def pbGetMegaRingName(idxBattler) - if pbOwnedByPlayer?(idxBattler) - Settings::MEGA_RINGS.each do |item| - return GameData::Item.get(item).name if $PokemonBag.pbHasItem?(item) + if !@mega_rings.empty? + if pbOwnedByPlayer?(idxBattler) + @mega_rings.each { |item| return GameData::Item.get(item).name if $bag.has?(item) } + else + trainer_items = pbGetOwnerItems(idxBattler) + @mega_rings.each { |item| return GameData::Item.get(item).name if trainer_items&.include?(item) } end end - # NOTE: Add your own Mega objects for particular NPC trainers here. -# if pbGetOwnerFromBattlerIndex(idxBattler).trainer_type == :BUGCATCHER -# return _INTL("Mega Net") -# end return _INTL("Mega Ring") end def pbCanMegaEvolve?(idxBattler) return false if $game_switches[Settings::NO_MEGA_EVOLUTION] return false if !@battlers[idxBattler].hasMega? - return false if wildBattle? && opposes?(idxBattler) + return false if @battlers[idxBattler].wild? return true if $DEBUG && Input.press?(Input::CTRL) - return false if @battlers[idxBattler].effects[PBEffects::SkyDrop]>=0 + return false if @battlers[idxBattler].effects[PBEffects::SkyDrop] >= 0 return false if !pbHasMegaRing?(idxBattler) side = @battlers[idxBattler].idxOwnSide owner = pbGetOwnerIndexFromBattlerIndex(idxBattler) - return @megaEvolution[side][owner]==-1 + return @megaEvolution[side][owner] == -1 end def pbRegisterMegaEvolution(idxBattler) @@ -100,13 +105,13 @@ def pbRegisterMegaEvolution(idxBattler) def pbUnregisterMegaEvolution(idxBattler) side = @battlers[idxBattler].idxOwnSide owner = pbGetOwnerIndexFromBattlerIndex(idxBattler) - @megaEvolution[side][owner] = -1 if @megaEvolution[side][owner]==idxBattler + @megaEvolution[side][owner] = -1 if @megaEvolution[side][owner] == idxBattler end def pbToggleRegisteredMegaEvolution(idxBattler) side = @battlers[idxBattler].idxOwnSide owner = pbGetOwnerIndexFromBattlerIndex(idxBattler) - if @megaEvolution[side][owner]==idxBattler + if @megaEvolution[side][owner] == idxBattler @megaEvolution[side][owner] = -1 else @megaEvolution[side][owner] = idxBattler @@ -116,7 +121,7 @@ def pbToggleRegisteredMegaEvolution(idxBattler) def pbRegisteredMegaEvolution?(idxBattler) side = @battlers[idxBattler].idxOwnSide owner = pbGetOwnerIndexFromBattlerIndex(idxBattler) - return @megaEvolution[side][owner]==idxBattler + return @megaEvolution[side][owner] == idxBattler end #============================================================================= @@ -126,38 +131,42 @@ def pbMegaEvolve(idxBattler) battler = @battlers[idxBattler] return if !battler || !battler.pokemon return if !battler.hasMega? || battler.mega? + $stats.mega_evolution_count += 1 if battler.pbOwnedByPlayer? trainerName = pbGetOwnerName(idxBattler) + old_ability = battler.ability_id # Break Illusion if battler.hasActiveAbility?(:ILLUSION) - BattleHandlers.triggerTargetAbilityOnHit(battler.ability,nil,battler,nil,self) + Battle::AbilityEffects.triggerOnBeingHit(battler.ability, nil, battler, nil, self) end # Mega Evolve case battler.pokemon.megaMessage when 1 # Rayquaza - pbDisplay(_INTL("{1}'s fervent wish has reached {2}!",trainerName,battler.pbThis)) + pbDisplay(_INTL("{1}'s fervent wish has reached {2}!", trainerName, battler.pbThis)) else pbDisplay(_INTL("{1}'s {2} is reacting to {3}'s {4}!", - battler.pbThis,battler.itemName,trainerName,pbGetMegaRingName(idxBattler))) + battler.pbThis, battler.itemName, trainerName, pbGetMegaRingName(idxBattler))) end - pbCommonAnimation("MegaEvolution",battler) + pbCommonAnimation("MegaEvolution", battler) battler.pokemon.makeMega battler.form = battler.pokemon.form battler.pbUpdate(true) - @scene.pbChangePokemon(battler,battler.pokemon) + @scene.pbChangePokemon(battler, battler.pokemon) @scene.pbRefreshOne(idxBattler) - pbCommonAnimation("MegaEvolution2",battler) + pbCommonAnimation("MegaEvolution2", battler) megaName = battler.pokemon.megaName megaName = _INTL("Mega {1}", battler.pokemon.speciesName) if nil_or_empty?(megaName) - pbDisplay(_INTL("{1} has Mega Evolved into {2}!",battler.pbThis,megaName)) + pbDisplay(_INTL("{1} has Mega Evolved into {2}!", battler.pbThis, megaName)) side = battler.idxOwnSide owner = pbGetOwnerIndexFromBattlerIndex(idxBattler) @megaEvolution[side][owner] = -2 if battler.isSpecies?(:GENGAR) && battler.mega? battler.effects[PBEffects::Telekinesis] = 0 end - pbCalculatePriority(false,[idxBattler]) if Settings::RECALCULATE_TURN_ORDER_AFTER_MEGA_EVOLUTION # Trigger ability - battler.pbEffectsOnSwitchIn + battler.pbOnLosingAbility(old_ability) + battler.pbTriggerAbilityOnGainingIt + # Recalculate turn order + pbCalculatePriority(false, [idxBattler]) if Settings::RECALCULATE_TURN_ORDER_AFTER_MEGA_EVOLUTION end #============================================================================= @@ -165,23 +174,23 @@ def pbMegaEvolve(idxBattler) #============================================================================= def pbPrimalReversion(idxBattler) battler = @battlers[idxBattler] - return if !battler || !battler.pokemon + return if !battler || !battler.pokemon || battler.fainted? return if !battler.hasPrimal? || battler.primal? if battler.isSpecies?(:KYOGRE) - pbCommonAnimation("PrimalKyogre",battler) + pbCommonAnimation("PrimalKyogre", battler) elsif battler.isSpecies?(:GROUDON) - pbCommonAnimation("PrimalGroudon",battler) + pbCommonAnimation("PrimalGroudon", battler) end battler.pokemon.makePrimal battler.form = battler.pokemon.form battler.pbUpdate(true) - @scene.pbChangePokemon(battler,battler.pokemon) + @scene.pbChangePokemon(battler, battler.pokemon) @scene.pbRefreshOne(idxBattler) if battler.isSpecies?(:KYOGRE) - pbCommonAnimation("PrimalKyogre2",battler) + pbCommonAnimation("PrimalKyogre2", battler) elsif battler.isSpecies?(:GROUDON) - pbCommonAnimation("PrimalGroudon2",battler) + pbCommonAnimation("PrimalGroudon2", battler) end - pbDisplay(_INTL("{1}'s Primal Reversion!\nIt reverted to its primal form!",battler.pbThis)) + pbDisplay(_INTL("{1}'s Primal Reversion!\nIt reverted to its primal form!", battler.pbThis)) end end diff --git a/Data/Scripts/011_Battle/003_Battle/010_Battle_Phase_Command.rb b/Data/Scripts/011_Battle/001_Battle/009_Battle_CommandPhase.rb similarity index 67% rename from Data/Scripts/011_Battle/003_Battle/010_Battle_Phase_Command.rb rename to Data/Scripts/011_Battle/001_Battle/009_Battle_CommandPhase.rb index 8e0ee5fdc1..fbbd876bb4 100644 --- a/Data/Scripts/011_Battle/003_Battle/010_Battle_Phase_Command.rb +++ b/Data/Scripts/011_Battle/001_Battle/009_Battle_CommandPhase.rb @@ -1,4 +1,4 @@ -class PokeBattle_Battle +class Battle #============================================================================= # Clear commands #============================================================================= @@ -25,8 +25,8 @@ def pbCancelChoice(idxBattler) #============================================================================= # Use main command menu (Fight/Pokémon/Bag/Run) #============================================================================= - def pbCommandMenu(idxBattler,firstAction) - return @scene.pbCommandMenu(idxBattler,firstAction) + def pbCommandMenu(idxBattler, firstAction) + return @scene.pbCommandMenu(idxBattler, firstAction) end #============================================================================= @@ -42,11 +42,11 @@ def pbCanShowCommands?(idxBattler) def pbCanShowFightMenu?(idxBattler) battler = @battlers[idxBattler] # Encore - return false if battler.effects[PBEffects::Encore]>0 + return false if battler.effects[PBEffects::Encore] > 0 # No moves that can be chosen (will Struggle instead) usable = false - battler.eachMoveWithIndex do |_m,i| - next if !pbCanChooseMove?(idxBattler,i,false) + battler.eachMoveWithIndex do |_m, i| + next if !pbCanChooseMove?(idxBattler, i, false) usable = true break end @@ -64,7 +64,7 @@ def pbFightMenu(idxBattler) return true if pbAutoFightMenu(idxBattler) # Regular move selection ret = false - @scene.pbFightMenu(idxBattler,pbCanMegaEvolve?(idxBattler)) { |cmd| + @scene.pbFightMenu(idxBattler, pbCanMegaEvolve?(idxBattler)) { |cmd| case cmd when -1 # Cancel when -2 # Toggle Mega Evolution @@ -75,11 +75,11 @@ def pbFightMenu(idxBattler) pbRegisterShift(idxBattler) ret = true else # Chose a move to use - next false if cmd<0 || !@battlers[idxBattler].moves[cmd] || - !@battlers[idxBattler].moves[cmd].id - next false if !pbRegisterMove(idxBattler,cmd) + next false if cmd < 0 || !@battlers[idxBattler].moves[cmd] || + !@battlers[idxBattler].moves[cmd].id + next false if !pbRegisterMove(idxBattler, cmd) next false if !singleBattle? && - !pbChooseTarget(@battlers[idxBattler],@battlers[idxBattler].moves[cmd]) + !pbChooseTarget(@battlers[idxBattler], @battlers[idxBattler].moves[cmd]) ret = true end next true @@ -89,48 +89,48 @@ def pbFightMenu(idxBattler) def pbAutoFightMenu(idxBattler); return false; end - def pbChooseTarget(battler,move) + def pbChooseTarget(battler, move) target_data = move.pbTarget(battler) - idxTarget = @scene.pbChooseTarget(battler.index,target_data) - return false if idxTarget<0 - pbRegisterTarget(battler.index,idxTarget) + idxTarget = @scene.pbChooseTarget(battler.index, target_data) + return false if idxTarget < 0 + pbRegisterTarget(battler.index, idxTarget) return true end - def pbItemMenu(idxBattler,firstAction) + def pbItemMenu(idxBattler, firstAction) if !@internalBattle pbDisplay(_INTL("Items can't be used here.")) return false end ret = false - @scene.pbItemMenu(idxBattler,firstAction) { |item,useType,idxPkmn,idxMove,itemScene| + @scene.pbItemMenu(idxBattler, firstAction) { |item, useType, idxPkmn, idxMove, itemScene| next false if !item battler = pkmn = nil case useType - when 1, 2, 6, 7 # Use on Pokémon/Pokémon's move + when 1, 2 # Use on Pokémon/Pokémon's move next false if !ItemHandlers.hasBattleUseOnPokemon(item) - battler = pbFindBattler(idxPkmn,idxBattler) + battler = pbFindBattler(idxPkmn, idxBattler) pkmn = pbParty(idxBattler)[idxPkmn] - next false if !pbCanUseItemOnPokemon?(item,pkmn,battler,itemScene) - when 3, 8 # Use on battler + next false if !pbCanUseItemOnPokemon?(item, pkmn, battler, itemScene) + when 3 # Use on battler next false if !ItemHandlers.hasBattleUseOnBattler(item) - battler = pbFindBattler(idxPkmn,idxBattler) + battler = pbFindBattler(idxPkmn, idxBattler) pkmn = battler.pokemon if battler - next false if !pbCanUseItemOnPokemon?(item,pkmn,battler,itemScene) - when 4, 9 # Poké Balls - next false if idxPkmn<0 + next false if !pbCanUseItemOnPokemon?(item, pkmn, battler, itemScene) + when 4 # Poké Balls + next false if idxPkmn < 0 battler = @battlers[idxPkmn] pkmn = battler.pokemon if battler - when 5, 10 # No target (Poké Doll, Guard Spec., Launcher items) + when 5 # No target (Poké Doll, Guard Spec., Launcher items) battler = @battlers[idxBattler] pkmn = battler.pokemon if battler else next false end next false if !pkmn - next false if !ItemHandlers.triggerCanUseInBattle(item, - pkmn,battler,idxMove,firstAction,self,itemScene) - next false if !pbRegisterItem(idxBattler,item,idxPkmn,idxMove) + next false if !ItemHandlers.triggerCanUseInBattle(item, pkmn, battler, idxMove, + firstAction, self, itemScene) + next false if !pbRegisterItem(idxBattler, item, idxPkmn, idxMove) ret = true next true } @@ -140,16 +140,16 @@ def pbItemMenu(idxBattler,firstAction) def pbPartyMenu(idxBattler) ret = -1 if @debug - ret = @battleAI.pbDefaultChooseNewEnemy(idxBattler,pbParty(idxBattler)) + ret = @battleAI.pbDefaultChooseNewEnemy(idxBattler, pbParty(idxBattler)) else - ret = pbPartyScreen(idxBattler,false,true,true) + ret = pbPartyScreen(idxBattler, false, true, true) end - return ret>=0 + return ret >= 0 end def pbRunMenu(idxBattler) # Regardless of succeeding or failing to run, stop choosing actions - return pbRun(idxBattler)!=0 + return pbRun(idxBattler) != 0 end def pbCallMenu(idxBattler) @@ -157,8 +157,15 @@ def pbCallMenu(idxBattler) end def pbDebugMenu - # NOTE: This doesn't do anything yet. Maybe you can write your own debugging - # options! + pbBattleDebug(self) + @scene.pbRefreshEverything + allBattlers.each { |b| b.pbCheckFormOnWeatherChange } + pbEndPrimordialWeather + allBattlers.each { |b| b.pbAbilityOnTerrainChange } + allBattlers.each do |b| + b.pbCheckFormOnMovesetChange + b.pbCheckFormOnStatusChange + end end #============================================================================= @@ -167,19 +174,19 @@ def pbDebugMenu def pbCommandPhase @scene.pbBeginCommandPhase # Reset choices if commands can be shown - @battlers.each_with_index do |b,i| + @battlers.each_with_index do |b, i| next if !b pbClearChoice(i) if pbCanShowCommands?(i) end # Reset choices to perform Mega Evolution if it wasn't done somehow - for side in 0...2 - @megaEvolution[side].each_with_index do |megaEvo,i| - @megaEvolution[side][i] = -1 if megaEvo>=0 + 2.times do |side| + @megaEvolution[side].each_with_index do |megaEvo, i| + @megaEvolution[side][i] = -1 if megaEvo >= 0 end end # Choose actions for the round (player first, then AI) pbCommandPhaseLoop(true) # Player chooses their actions - return if @decision!=0 # Battle ended, stop choosing actions + return if @decision != 0 # Battle ended, stop choosing actions pbCommandPhaseLoop(false) # AI chooses their actions end @@ -189,11 +196,11 @@ def pbCommandPhaseLoop(isPlayer) actioned = [] idxBattler = -1 loop do - break if @decision!=0 # Battle ended, stop choosing actions + break if @decision != 0 # Battle ended, stop choosing actions idxBattler += 1 - break if idxBattler>=@battlers.length - next if !@battlers[idxBattler] || pbOwnedByPlayer?(idxBattler)!=isPlayer - next if @choices[idxBattler][0]!=:None # Action is forced, can't choose one + break if idxBattler >= @battlers.length + next if !@battlers[idxBattler] || pbOwnedByPlayer?(idxBattler) != isPlayer + next if @choices[idxBattler][0] != :None # Action is forced, can't choose one next if !pbCanShowCommands?(idxBattler) # Action is forced, can't choose one # AI controls this battler if @controlPlayer || !pbOwnedByPlayer?(idxBattler) @@ -204,17 +211,17 @@ def pbCommandPhaseLoop(isPlayer) actioned.push(idxBattler) commandsEnd = false # Whether to cancel choosing all other actions this round loop do - cmd = pbCommandMenu(idxBattler,actioned.length==1) + cmd = pbCommandMenu(idxBattler, actioned.length == 1) # If being Sky Dropped, can't do anything except use a move - if cmd>0 && @battlers[idxBattler].effects[PBEffects::SkyDrop]>=0 - pbDisplay(_INTL("Sky Drop won't let {1} go!",@battlers[idxBattler].pbThis(true))) + if cmd > 0 && @battlers[idxBattler].effects[PBEffects::SkyDrop] >= 0 + pbDisplay(_INTL("Sky Drop won't let {1} go!", @battlers[idxBattler].pbThis(true))) next end case cmd when 0 # Fight break if pbFightMenu(idxBattler) when 1 # Bag - if pbItemMenu(idxBattler,actioned.length==1) + if pbItemMenu(idxBattler, actioned.length == 1) commandsEnd = true if pbItemUsesAllActions?(@choices[idxBattler][1]) break end @@ -235,10 +242,10 @@ def pbCommandPhaseLoop(isPlayer) pbDebugMenu next when -1 # Go back to previous battler's action choice - next if actioned.length<=1 + next if actioned.length <= 1 actioned.pop # Forget this battler was done - idxBattler = actioned.last-1 - pbCancelChoice(idxBattler+1) # Clear the previous battler's choice + idxBattler = actioned.last - 1 + pbCancelChoice(idxBattler + 1) # Clear the previous battler's choice actioned.pop # Forget the previous battler was done break end diff --git a/Data/Scripts/011_Battle/003_Battle/011_Battle_Phase_Attack.rb b/Data/Scripts/011_Battle/001_Battle/010_Battle_AttackPhase.rb similarity index 62% rename from Data/Scripts/011_Battle/003_Battle/011_Battle_Phase_Attack.rb rename to Data/Scripts/011_Battle/001_Battle/010_Battle_AttackPhase.rb index 07bcaf9139..27a265dfde 100644 --- a/Data/Scripts/011_Battle/003_Battle/011_Battle_Phase_Attack.rb +++ b/Data/Scripts/011_Battle/001_Battle/010_Battle_AttackPhase.rb @@ -1,4 +1,4 @@ -class PokeBattle_Battle +class Battle #============================================================================= # Attack phase actions #============================================================================= @@ -6,16 +6,16 @@ class PokeBattle_Battle def pbAttackPhasePriorityChangeMessages pbPriority.each do |b| if b.effects[PBEffects::PriorityAbility] && b.abilityActive? - BattleHandlers.triggerPriorityBracketUseAbility(b.ability,b,self) + Battle::AbilityEffects.triggerPriorityBracketUse(b.ability, b, self) elsif b.effects[PBEffects::PriorityItem] && b.itemActive? - BattleHandlers.triggerPriorityBracketUseItem(b.item,b,self) + Battle::ItemEffects.triggerPriorityBracketUse(b.item, b, self) end end end def pbAttackPhaseCall pbPriority.each do |b| - next unless @choices[b.index][0]==:Call && !b.fainted? + next unless @choices[b.index][0] == :Call && !b.fainted? b.lastMoveFailed = false # Counts as a successful move for Stomping Tantrum pbCall(b.index) end @@ -25,31 +25,31 @@ def pbPursuit(idxSwitcher) @switching = true pbPriority.each do |b| next if b.fainted? || !b.opposes?(idxSwitcher) # Shouldn't hit an ally - next if b.movedThisRound? || !pbChoseMoveFunctionCode?(b.index,"088") # Pursuit + next if b.movedThisRound? || !pbChoseMoveFunctionCode?(b.index, "PursueSwitchingFoe") # Check whether Pursuit can be used - next unless pbMoveCanTarget?(b.index,idxSwitcher,@choices[b.index][2].pbTarget(b)) - next unless pbCanChooseMove?(b.index,@choices[b.index][1],false) + next unless pbMoveCanTarget?(b.index, idxSwitcher, @choices[b.index][2].pbTarget(b)) + next unless pbCanChooseMove?(b.index, @choices[b.index][1], false) next if b.status == :SLEEP || b.status == :FROZEN - next if b.effects[PBEffects::SkyDrop]>=0 + next if b.effects[PBEffects::SkyDrop] >= 0 next if b.hasActiveAbility?(:TRUANT) && b.effects[PBEffects::Truant] # Mega Evolve - if !wildBattle? || !b.opposes? + if !b.wild? owner = pbGetOwnerIndexFromBattlerIndex(b.index) - pbMegaEvolve(b.index) if @megaEvolution[b.idxOwnSide][owner]==b.index + pbMegaEvolve(b.index) if @megaEvolution[b.idxOwnSide][owner] == b.index end # Use Pursuit @choices[b.index][3] = idxSwitcher # Change Pursuit's target - if b.pbProcessTurn(@choices[b.index],false) + if b.pbProcessTurn(@choices[b.index], false) b.effects[PBEffects::Pursuit] = true end - break if @decision>0 || @battlers[idxSwitcher].fainted? + break if @decision > 0 || @battlers[idxSwitcher].fainted? end @switching = false end def pbAttackPhaseSwitch pbPriority.each do |b| - next unless @choices[b.index][0]==:SwitchOut && !b.fainted? + next unless @choices[b.index][0] == :SwitchOut && !b.fainted? idxNewPkmn = @choices[b.index][1] # Party index of Pokémon to switch to b.lastMoveFailed = false # Counts as a successful move for Stomping Tantrum @lastMoveUser = b.index @@ -57,10 +57,14 @@ def pbAttackPhaseSwitch pbMessageOnRecall(b) # Pursuit interrupts switching pbPursuit(b.index) - return if @decision>0 + return if @decision > 0 # Switch Pokémon - pbRecallAndReplace(b.index,idxNewPkmn) - b.pbEffectsOnSwitchIn(true) + allBattlers.each do |b| + b.droppedBelowHalfHP = false + b.statsDropped = false + end + pbRecallAndReplace(b.index, idxNewPkmn) + pbOnBattlerEnteringBattle(b.index, true) end end @@ -71,13 +75,13 @@ def pbAttackPhaseItems item = @choices[b.index][1] next if !item case GameData::Item.get(item).battle_use - when 1, 2, 6, 7 # Use on Pokémon/Pokémon's move + when 1, 2 # Use on Pokémon/Pokémon's move pbUseItemOnPokemon(item, @choices[b.index][2], b) if @choices[b.index][2] >= 0 - when 3, 8 # Use on battler + when 3 # Use on battler pbUseItemOnBattler(item, @choices[b.index][2], b) - when 4, 9 # Use Poké Ball + when 4 # Use Poké Ball pbUsePokeBallInBattle(item, @choices[b.index][2], b) - when 5, 10 # Use directly + when 5 # Use directly pbUseItemInBattle(item, @choices[b.index][2], b) else next @@ -89,10 +93,10 @@ def pbAttackPhaseItems def pbAttackPhaseMegaEvolution pbPriority.each do |b| - next if wildBattle? && b.opposes? - next unless @choices[b.index][0]==:UseMove && !b.fainted? + next if b.wild? + next unless @choices[b.index][0] == :UseMove && !b.fainted? owner = pbGetOwnerIndexFromBattlerIndex(b.index) - next if @megaEvolution[b.idxOwnSide][owner]!=b.index + next if @megaEvolution[b.idxOwnSide][owner] != b.index pbMegaEvolve(b.index) end end @@ -100,7 +104,7 @@ def pbAttackPhaseMegaEvolution def pbAttackPhaseMoves # Show charging messages (Focus Punch) pbPriority.each do |b| - next unless @choices[b.index][0]==:UseMove && !b.fainted? + next unless @choices[b.index][0] == :UseMove && !b.fainted? next if b.movedThisRound? @choices[b.index][2].pbDisplayChargeMessage(b) end @@ -111,46 +115,56 @@ def pbAttackPhaseMoves advance = false priority.each do |b| next unless b.effects[PBEffects::MoveNext] && !b.fainted? - next unless @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift + next unless @choices[b.index][0] == :UseMove || @choices[b.index][0] == :Shift next if b.movedThisRound? advance = b.pbProcessTurn(@choices[b.index]) break if advance end - return if @decision>0 + return if @decision > 0 next if advance # Regular priority order priority.each do |b| - next if b.effects[PBEffects::Quash]>0 || b.fainted? - next unless @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift + next if b.effects[PBEffects::Quash] > 0 || b.fainted? + next unless @choices[b.index][0] == :UseMove || @choices[b.index][0] == :Shift next if b.movedThisRound? advance = b.pbProcessTurn(@choices[b.index]) break if advance end - return if @decision>0 + return if @decision > 0 next if advance # Quashed - quashLevel = 0 - loop do - quashLevel += 1 - moreQuash = false + if Settings::MECHANICS_GENERATION >= 8 priority.each do |b| - moreQuash = true if b.effects[PBEffects::Quash]>quashLevel - next unless b.effects[PBEffects::Quash]==quashLevel && !b.fainted? - next unless @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift + next unless b.effects[PBEffects::Quash] > 0 && !b.fainted? + next unless @choices[b.index][0] == :UseMove || @choices[b.index][0] == :Shift next if b.movedThisRound? advance = b.pbProcessTurn(@choices[b.index]) - break + break if advance + end + else + quashLevel = 0 + loop do + quashLevel += 1 + moreQuash = false + priority.each do |b| + moreQuash = true if b.effects[PBEffects::Quash] > quashLevel + next unless b.effects[PBEffects::Quash] == quashLevel && !b.fainted? + next unless @choices[b.index][0] == :UseMove || @choices[b.index][0] == :Shift + next if b.movedThisRound? + advance = b.pbProcessTurn(@choices[b.index]) + break + end + break if advance || !moreQuash end - break if advance || !moreQuash end - return if @decision>0 + return if @decision > 0 next if advance # Check for all done priority.each do |b| - if !b.fainted? && !b.movedThisRound? - advance = true if @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift - end - break if advance + next if b.fainted? + next if b.movedThisRound? || ![:UseMove, :Shift].include?(@choices[b.index][0]) + advance = true + break end next if advance # All Pokémon have moved; end the loop @@ -164,15 +178,15 @@ def pbAttackPhaseMoves def pbAttackPhase @scene.pbBeginAttackPhase # Reset certain effects - @battlers.each_with_index do |b,i| + @battlers.each_with_index do |b, i| next if !b b.turnCount += 1 if !b.fainted? @successStates[i].clear - if @choices[i][0]!=:UseMove && @choices[i][0]!=:Shift && @choices[i][0]!=:SwitchOut + if @choices[i][0] != :UseMove && @choices[i][0] != :Shift && @choices[i][0] != :SwitchOut b.effects[PBEffects::DestinyBond] = false b.effects[PBEffects::Grudge] = false end - b.effects[PBEffects::Rage] = false if !pbChoseMoveFunctionCode?(i,"093") # Rage + b.effects[PBEffects::Rage] = false if !pbChoseMoveFunctionCode?(i, "StartRaiseUserAtk1WhenDamaged") end PBDebug.log("") # Calculate move order for this round @@ -181,9 +195,9 @@ def pbAttackPhase pbAttackPhasePriorityChangeMessages pbAttackPhaseCall pbAttackPhaseSwitch - return if @decision>0 + return if @decision > 0 pbAttackPhaseItems - return if @decision>0 + return if @decision > 0 pbAttackPhaseMegaEvolution pbAttackPhaseMoves end diff --git a/Data/Scripts/011_Battle/001_Battle/011_Battle_EndOfRoundPhase.rb b/Data/Scripts/011_Battle/001_Battle/011_Battle_EndOfRoundPhase.rb new file mode 100644 index 0000000000..25a9ab8385 --- /dev/null +++ b/Data/Scripts/011_Battle/001_Battle/011_Battle_EndOfRoundPhase.rb @@ -0,0 +1,778 @@ +class Battle + #============================================================================= + # End Of Round end weather check and weather effects + #============================================================================= + def pbEOREndWeather(priority) + # NOTE: Primordial weather doesn't need to be checked here, because if it + # could wear off here, it will have worn off already. + # Count down weather duration + @field.weatherDuration -= 1 if @field.weatherDuration > 0 + # Weather wears off + if @field.weatherDuration == 0 + case @field.weather + when :Sun then pbDisplay(_INTL("The sunlight faded.")) + when :Rain then pbDisplay(_INTL("The rain stopped.")) + when :Sandstorm then pbDisplay(_INTL("The sandstorm subsided.")) + when :Hail then pbDisplay(_INTL("The hail stopped.")) + when :ShadowSky then pbDisplay(_INTL("The shadow sky faded.")) + end + @field.weather = :None + # Check for form changes caused by the weather changing + allBattlers.each { |battler| battler.pbCheckFormOnWeatherChange } + # Start up the default weather + pbStartWeather(nil, @field.defaultWeather) if @field.defaultWeather != :None + return if @field.weather == :None + end + # Weather continues + weather_data = GameData::BattleWeather.try_get(@field.weather) + pbCommonAnimation(weather_data.animation) if weather_data + case @field.weather +# when :Sun then pbDisplay(_INTL("The sunlight is strong.")) +# when :Rain then pbDisplay(_INTL("Rain continues to fall.")) + when :Sandstorm then pbDisplay(_INTL("The sandstorm is raging.")) + when :Hail then pbDisplay(_INTL("The hail is crashing down.")) +# when :HarshSun then pbDisplay(_INTL("The sunlight is extremely harsh.")) +# when :HeavyRain then pbDisplay(_INTL("It is raining heavily.")) +# when :StrongWinds then pbDisplay(_INTL("The wind is strong.")) + when :ShadowSky then pbDisplay(_INTL("The shadow sky continues.")) + end + # Effects due to weather + priority.each do |battler| + # Weather-related abilities + if battler.abilityActive? + Battle::AbilityEffects.triggerEndOfRoundWeather(battler.ability, battler.effectiveWeather, battler, self) + battler.pbFaint if battler.fainted? + end + # Weather damage + pbEORWeatherDamage(battler) + end + end + + def pbEORWeatherDamage(battler) + return if battler.fainted? + amt = -1 + case battler.effectiveWeather + when :Sandstorm + return if !battler.takesSandstormDamage? + pbDisplay(_INTL("{1} is buffeted by the sandstorm!", battler.pbThis)) + amt = battler.totalhp / 16 + when :Hail + return if !battler.takesHailDamage? + pbDisplay(_INTL("{1} is buffeted by the hail!", battler.pbThis)) + amt = battler.totalhp / 16 + when :ShadowSky + return if !battler.takesShadowSkyDamage? + pbDisplay(_INTL("{1} is hurt by the shadow sky!", battler.pbThis)) + amt = battler.totalhp / 16 + end + return if amt < 0 + @scene.pbDamageAnimation(battler) + battler.pbReduceHP(amt, false) + battler.pbItemHPHealCheck + battler.pbFaint if battler.fainted? + end + + #============================================================================= + # End Of Round use delayed moves (Future Sight, Doom Desire) + #============================================================================= + def pbEORUseFutureSight(position, position_index) + return if !position || position.effects[PBEffects::FutureSightCounter] == 0 + position.effects[PBEffects::FutureSightCounter] -= 1 + return if position.effects[PBEffects::FutureSightCounter] > 0 + return if !@battlers[position_index] || @battlers[position_index].fainted? # No target + moveUser = nil + allBattlers.each do |battler| + next if battler.opposes?(position.effects[PBEffects::FutureSightUserIndex]) + next if battler.pokemonIndex != position.effects[PBEffects::FutureSightUserPartyIndex] + moveUser = battler + break + end + return if moveUser && moveUser.index == position_index # Target is the user + if !moveUser # User isn't in battle, get it from the party + party = pbParty(position.effects[PBEffects::FutureSightUserIndex]) + pkmn = party[position.effects[PBEffects::FutureSightUserPartyIndex]] + if pkmn&.able? + moveUser = Battler.new(self, position.effects[PBEffects::FutureSightUserIndex]) + moveUser.pbInitDummyPokemon(pkmn, position.effects[PBEffects::FutureSightUserPartyIndex]) + end + end + return if !moveUser # User is fainted + move = position.effects[PBEffects::FutureSightMove] + pbDisplay(_INTL("{1} took the {2} attack!", @battlers[position_index].pbThis, + GameData::Move.get(move).name)) + # NOTE: Future Sight failing against the target here doesn't count towards + # Stomping Tantrum. + userLastMoveFailed = moveUser.lastMoveFailed + @futureSight = true + moveUser.pbUseMoveSimple(move, idxPos) + @futureSight = false + moveUser.lastMoveFailed = userLastMoveFailed + @battlers[position_index].pbFaint if @battlers[position_index].fainted? + position.effects[PBEffects::FutureSightCounter] = 0 + position.effects[PBEffects::FutureSightMove] = nil + position.effects[PBEffects::FutureSightUserIndex] = -1 + position.effects[PBEffects::FutureSightUserPartyIndex] = -1 + end + + #============================================================================= + # End Of Round healing from Wish + #============================================================================= + def pbEORWishHealing + @positions.each_with_index do |pos, idxPos| + next if !pos || pos.effects[PBEffects::Wish] == 0 + pos.effects[PBEffects::Wish] -= 1 + next if pos.effects[PBEffects::Wish] > 0 + next if !@battlers[idxPos] || !@battlers[idxPos].canHeal? + wishMaker = pbThisEx(idxPos, pos.effects[PBEffects::WishMaker]) + @battlers[idxPos].pbRecoverHP(pos.effects[PBEffects::WishAmount]) + pbDisplay(_INTL("{1}'s wish came true!", wishMaker)) + end + end + + #============================================================================= + # End Of Round Sea of Fire damage (Fire Pledge + Grass Pledge combination) + #============================================================================= + def pbEORSeaOfFireDamage + 2.times do |side| + next if sides[side].effects[PBEffects::SeaOfFire] == 0 + @battle.pbCommonAnimation("SeaOfFire") if side == 0 + @battle.pbCommonAnimation("SeaOfFireOpp") if side == 1 + priority.each do |battler| + next if battler.opposes?(side) + next if !battler.takesIndirectDamage? || battler.pbHasType?(:FIRE) + @scene.pbDamageAnimation(battler) + battler.pbTakeEffectDamage(battler.totalhp / 8, false) { |hp_lost| + pbDisplay(_INTL("{1} is hurt by the sea of fire!", battler.pbThis)) + } + end + end + end + + #============================================================================= + # End Of Round healing from Grassy Terrain + #============================================================================= + def pbEORTerrainHealing(battler) + return if battler.fainted? + # Grassy Terrain (healing) + if @field.terrain == :Grassy && battler.affectedByTerrain? && battler.canHeal? + PBDebug.log("[Lingering effect] Grassy Terrain heals #{battler.pbThis(true)}") + battler.pbRecoverHP(battler.totalhp / 16) + pbDisplay(_INTL("{1}'s HP was restored.", battler.pbThis)) + end + end + + #============================================================================= + # End Of Round various healing effects + #============================================================================= + def pbEORHealingEffects(priority) + # Aqua Ring + priority.each do |battler| + next if !battler.effects[PBEffects::AquaRing] + next if !battler.canHeal? + hpGain = battler.totalhp / 16 + hpGain = (hpGain * 1.3).floor if battler.hasActiveItem?(:BIGROOT) + battler.pbRecoverHP(hpGain) + pbDisplay(_INTL("Aqua Ring restored {1}'s HP!", battler.pbThis(true))) + end + # Ingrain + priority.each do |battler| + next if !battler.effects[PBEffects::Ingrain] + next if !battler.canHeal? + hpGain = battler.totalhp / 16 + hpGain = (hpGain * 1.3).floor if battler.hasActiveItem?(:BIGROOT) + battler.pbRecoverHP(hpGain) + pbDisplay(_INTL("{1} absorbed nutrients with its roots!", battler.pbThis)) + end + # Leech Seed + priority.each do |battler| + next if battler.effects[PBEffects::LeechSeed] < 0 + next if !battler.takesIndirectDamage? + recipient = @battlers[battler.effects[PBEffects::LeechSeed]] + next if !recipient || recipient.fainted? + pbCommonAnimation("LeechSeed", recipient, battler) + battler.pbTakeEffectDamage(battler.totalhp / 8) { |hp_lost| + recipient.pbRecoverHPFromDrain(hp_lost, battler, + _INTL("{1}'s health is sapped by Leech Seed!", battler.pbThis)) + recipient.pbAbilitiesOnDamageTaken + } + recipient.pbFaint if recipient.fainted? + end + end + + #============================================================================= + # End Of Round deal damage from status problems + #============================================================================= + def pbEORStatusProblemDamage(priority) + # Damage from poisoning + priority.each do |battler| + next if battler.fainted? + next if battler.status != :POISON + if battler.statusCount > 0 + battler.effects[PBEffects::Toxic] += 1 + battler.effects[PBEffects::Toxic] = 16 if battler.effects[PBEffects::Toxic] > 16 + end + if battler.hasActiveAbility?(:POISONHEAL) + if battler.canHeal? + anim_name = GameData::Status.get(:POISON).animation + pbCommonAnimation(anim_name, battler) if anim_name + pbShowAbilitySplash(battler) + battler.pbRecoverHP(battler.totalhp / 8) + if Scene::USE_ABILITY_SPLASH + pbDisplay(_INTL("{1}'s HP was restored.", battler.pbThis)) + else + pbDisplay(_INTL("{1}'s {2} restored its HP.", battler.pbThis, battler.abilityName)) + end + pbHideAbilitySplash(battler) + end + elsif battler.takesIndirectDamage? + battler.droppedBelowHalfHP = false + dmg = battler.totalhp / 8 + dmg = battler.totalhp * battler.effects[PBEffects::Toxic] / 16 if battler.statusCount > 0 + battler.pbContinueStatus { battler.pbReduceHP(dmg, false) } + battler.pbItemHPHealCheck + battler.pbAbilitiesOnDamageTaken + battler.pbFaint if battler.fainted? + battler.droppedBelowHalfHP = false + end + end + # Damage from burn + priority.each do |battler| + next if battler.status != :BURN || !battler.takesIndirectDamage? + battler.droppedBelowHalfHP = false + dmg = (Settings::MECHANICS_GENERATION >= 7) ? battler.totalhp / 16 : battler.totalhp / 8 + dmg = (dmg / 2.0).round if battler.hasActiveAbility?(:HEATPROOF) + battler.pbContinueStatus { battler.pbReduceHP(dmg, false) } + battler.pbItemHPHealCheck + battler.pbAbilitiesOnDamageTaken + battler.pbFaint if battler.fainted? + battler.droppedBelowHalfHP = false + end + end + + #============================================================================= + # End Of Round deal damage from effects (except by trapping) + #============================================================================= + def pbEOREffectDamage(priority) + # Damage from sleep (Nightmare) + priority.each do |battler| + battler.effects[PBEffects::Nightmare] = false if !battler.asleep? + next if !battler.effects[PBEffects::Nightmare] || !battler.takesIndirectDamage? + battler.pbTakeEffectDamage(battler.totalhp / 4) { |hp_lost| + pbDisplay(_INTL("{1} is locked in a nightmare!", battler.pbThis)) + } + end + # Curse + priority.each do |battler| + next if !battler.effects[PBEffects::Curse] || !battler.takesIndirectDamage? + battler.pbTakeEffectDamage(battler.totalhp / 4) { |hp_lost| + pbDisplay(_INTL("{1} is afflicted by the curse!", battler.pbThis)) + } + end + end + + #============================================================================= + # End Of Round deal damage to trapped battlers + #============================================================================= + TRAPPING_MOVE_COMMON_ANIMATIONS = { + :BIND => "Bind", + :CLAMP => "Clamp", + :FIRESPIN => "FireSpin", + :MAGMASTORM => "MagmaStorm", + :SANDTOMB => "SandTomb", + :WRAP => "Wrap", + :INFESTATION => "Infestation" + } + + def pbEORTrappingDamage(battler) + return if battler.fainted? || battler.effects[PBEffects::Trapping] == 0 + battler.effects[PBEffects::Trapping] -= 1 + move_name = GameData::Move.get(battler.effects[PBEffects::TrappingMove]).name + if battler.effects[PBEffects::Trapping] == 0 + pbDisplay(_INTL("{1} was freed from {2}!", battler.pbThis, move_name)) + return + end + anim = TRAPPING_MOVE_COMMON_ANIMATIONS[battler.effects[PBEffects::TrappingMove]] || "Wrap" + pbCommonAnimation(anim, battler) + return if !battler.takesIndirectDamage? + hpLoss = (Settings::MECHANICS_GENERATION >= 6) ? battler.totalhp / 8 : battler.totalhp / 16 + if @battlers[battler.effects[PBEffects::TrappingUser]].hasActiveItem?(:BINDINGBAND) + hpLoss = (Settings::MECHANICS_GENERATION >= 6) ? battler.totalhp / 6 : battler.totalhp / 8 + end + @scene.pbDamageAnimation(battler) + battler.pbTakeEffectDamage(hpLoss, false) { |hp_lost| + pbDisplay(_INTL("{1} is hurt by {2}!", battler.pbThis, move_name)) + } + end + + #============================================================================= + # End Of Round end effects that apply to a battler + #============================================================================= + def pbEORCountDownBattlerEffect(priority, effect) + priority.each do |battler| + next if battler.fainted? || battler.effects[effect] == 0 + battler.effects[effect] -= 1 + yield battler if block_given? && battler.effects[effect] == 0 + end + end + + def pbEOREndBattlerEffects(priority) + # Taunt + pbEORCountDownBattlerEffect(priority, PBEffects::Taunt) { |battler| + pbDisplay(_INTL("{1}'s taunt wore off!", battler.pbThis)) + } + # Encore + priority.each do |battler| + next if battler.fainted? || battler.effects[PBEffects::Encore] == 0 + idxEncoreMove = battler.pbEncoredMoveIndex + if idxEncoreMove >= 0 + battler.effects[PBEffects::Encore] -= 1 + if battler.effects[PBEffects::Encore] == 0 || battler.moves[idxEncoreMove].pp == 0 + battler.effects[PBEffects::Encore] = 0 + pbDisplay(_INTL("{1}'s encore ended!", battler.pbThis)) + end + else + PBDebug.log("[End of effect] #{battler.pbThis}'s encore ended (encored move no longer known)") + battler.effects[PBEffects::Encore] = 0 + battler.effects[PBEffects::EncoreMove] = nil + end + end + # Disable/Cursed Body + pbEORCountDownBattlerEffect(priority, PBEffects::Disable) { |battler| + battler.effects[PBEffects::DisableMove] = nil + pbDisplay(_INTL("{1} is no longer disabled!", battler.pbThis)) + } + # Magnet Rise + pbEORCountDownBattlerEffect(priority, PBEffects::MagnetRise) { |battler| + pbDisplay(_INTL("{1}'s electromagnetism wore off!", battler.pbThis)) + } + # Telekinesis + pbEORCountDownBattlerEffect(priority, PBEffects::Telekinesis) { |battler| + pbDisplay(_INTL("{1} was freed from the telekinesis!", battler.pbThis)) + } + # Heal Block + pbEORCountDownBattlerEffect(priority, PBEffects::HealBlock) { |battler| + pbDisplay(_INTL("{1}'s Heal Block wore off!", battler.pbThis)) + } + # Embargo + pbEORCountDownBattlerEffect(priority, PBEffects::Embargo) { |battler| + pbDisplay(_INTL("{1} can use items again!", battler.pbThis)) + battler.pbItemTerrainStatBoostCheck + } + # Yawn + pbEORCountDownBattlerEffect(priority, PBEffects::Yawn) { |battler| + if battler.pbCanSleepYawn? + PBDebug.log("[Lingering effect] #{battler.pbThis} fell asleep because of Yawn") + battler.pbSleep + end + } + # Perish Song + perishSongUsers = [] + priority.each do |battler| + next if battler.fainted? || battler.effects[PBEffects::PerishSong] == 0 + battler.effects[PBEffects::PerishSong] -= 1 + pbDisplay(_INTL("{1}'s perish count fell to {2}!", battler.pbThis, battler.effects[PBEffects::PerishSong])) + if battler.effects[PBEffects::PerishSong] == 0 + perishSongUsers.push(battler.effects[PBEffects::PerishSongUser]) + battler.pbReduceHP(battler.hp) + end + battler.pbItemHPHealCheck + battler.pbFaint if battler.fainted? + end + # Judge if all remaining Pokemon fainted by a Perish Song triggered by a single side + if perishSongUsers.length > 0 && + ((perishSongUsers.find_all { |idxBattler| opposes?(idxBattler) }.length == perishSongUsers.length) || + (perishSongUsers.find_all { |idxBattler| !opposes?(idxBattler) }.length == perishSongUsers.length)) + pbJudgeCheckpoint(@battlers[perishSongUsers[0]]) + end + return if @decision > 0 + end + + #============================================================================= + # End Of Round end effects that apply to one side of the field + #============================================================================= + def pbEORCountDownSideEffect(side, effect, msg) + return if @sides[side].effects[effect] <= 0 + @sides[side].effects[effect] -= 1 + pbDisplay(msg) if @sides[side].effects[effect] == 0 + end + + def pbEOREndSideEffects(side, priority) + # Reflect + pbEORCountDownSideEffect(side, PBEffects::Reflect, + _INTL("{1}'s Reflect wore off!", @battlers[side].pbTeam)) + # Light Screen + pbEORCountDownSideEffect(side, PBEffects::LightScreen, + _INTL("{1}'s Light Screen wore off!", @battlers[side].pbTeam)) + # Safeguard + pbEORCountDownSideEffect(side, PBEffects::Safeguard, + _INTL("{1} is no longer protected by Safeguard!", @battlers[side].pbTeam)) + # Mist + pbEORCountDownSideEffect(side, PBEffects::Mist, + _INTL("{1} is no longer protected by mist!", @battlers[side].pbTeam)) + # Tailwind + pbEORCountDownSideEffect(side, PBEffects::Tailwind, + _INTL("{1}'s Tailwind petered out!", @battlers[side].pbTeam)) + # Lucky Chant + pbEORCountDownSideEffect(side, PBEffects::LuckyChant, + _INTL("{1}'s Lucky Chant wore off!", @battlers[side].pbTeam)) + # Pledge Rainbow + pbEORCountDownSideEffect(side, PBEffects::Rainbow, + _INTL("The rainbow on {1}'s side disappeared!", @battlers[side].pbTeam(true))) + # Pledge Sea of Fire + pbEORCountDownSideEffect(side, PBEffects::SeaOfFire, + _INTL("The sea of fire around {1} disappeared!", @battlers[side].pbTeam(true))) + # Pledge Swamp + pbEORCountDownSideEffect(side, PBEffects::Swamp, + _INTL("The swamp around {1} disappeared!", @battlers[side].pbTeam(true))) + # Aurora Veil + pbEORCountDownSideEffect(side, PBEffects::AuroraVeil, + _INTL("{1}'s Aurora Veil wore off!", @battlers[side].pbTeam(true))) + end + + #============================================================================= + # End Of Round end effects that apply to the whole field + #============================================================================= + def pbEORCountDownFieldEffect(effect, msg) + return if @field.effects[effect] <= 0 + @field.effects[effect] -= 1 + return if @field.effects[effect] > 0 + pbDisplay(msg) + if effect == PBEffects::MagicRoom + pbPriority(true).each { |battler| battler.pbItemTerrainStatBoostCheck } + end + end + + def pbEOREndFieldEffects(priority) + # Trick Room + pbEORCountDownFieldEffect(PBEffects::TrickRoom, + _INTL("The twisted dimensions returned to normal!")) + # Gravity + pbEORCountDownFieldEffect(PBEffects::Gravity, + _INTL("Gravity returned to normal!")) + # Water Sport + pbEORCountDownFieldEffect(PBEffects::WaterSportField, + _INTL("The effects of Water Sport have faded.")) + # Mud Sport + pbEORCountDownFieldEffect(PBEffects::MudSportField, + _INTL("The effects of Mud Sport have faded.")) + # Wonder Room + pbEORCountDownFieldEffect(PBEffects::WonderRoom, + _INTL("Wonder Room wore off, and Defense and Sp. Def stats returned to normal!")) + # Magic Room + pbEORCountDownFieldEffect(PBEffects::MagicRoom, + _INTL("Magic Room wore off, and held items' effects returned to normal!")) + end + + #============================================================================= + # End Of Round end terrain check + #============================================================================= + def pbEOREndTerrain + # Count down terrain duration + @field.terrainDuration -= 1 if @field.terrainDuration > 0 + # Terrain wears off + if @field.terrain != :None && @field.terrainDuration == 0 + case @field.terrain + when :Electric + pbDisplay(_INTL("The electric current disappeared from the battlefield!")) + when :Grassy + pbDisplay(_INTL("The grass disappeared from the battlefield!")) + when :Misty + pbDisplay(_INTL("The mist disappeared from the battlefield!")) + when :Psychic + pbDisplay(_INTL("The weirdness disappeared from the battlefield!")) + end + @field.terrain = :None + allBattlers.each { |battler| battler.pbAbilityOnTerrainChange } + # Start up the default terrain + if @field.defaultTerrain != :None + pbStartTerrain(nil, @field.defaultTerrain, false) + allBattlers.each { |battler| battler.pbAbilityOnTerrainChange } + allBattlers.each { |battler| battler.pbItemTerrainStatBoostCheck } + end + return if @field.terrain == :None + end + # Terrain continues + terrain_data = GameData::BattleTerrain.try_get(@field.terrain) + pbCommonAnimation(terrain_data.animation) if terrain_data + case @field.terrain + when :Electric then pbDisplay(_INTL("An electric current is running across the battlefield.")) + when :Grassy then pbDisplay(_INTL("Grass is covering the battlefield.")) + when :Misty then pbDisplay(_INTL("Mist is swirling about the battlefield.")) + when :Psychic then pbDisplay(_INTL("The battlefield is weird.")) + end + end + + #============================================================================= + # End Of Round end self-inflicted effects on battler + #============================================================================= + def pbEOREndBattlerSelfEffects(battler) + return if battler.fainted? + # Hyper Mode (Shadow Pokémon) + if battler.inHyperMode? + if pbRandom(100) < 10 + battler.pokemon.hyper_mode = false + pbDisplay(_INTL("{1} came to its senses!", battler.pbThis)) + else + pbDisplay(_INTL("{1} is in Hyper Mode!", battler.pbThis)) + end + end + # Uproar + if battler.effects[PBEffects::Uproar] > 0 + battler.effects[PBEffects::Uproar] -= 1 + if battler.effects[PBEffects::Uproar] == 0 + pbDisplay(_INTL("{1} calmed down.", battler.pbThis)) + else + pbDisplay(_INTL("{1} is making an uproar!", battler.pbThis)) + end + end + # Slow Start's end message + if battler.effects[PBEffects::SlowStart] > 0 + battler.effects[PBEffects::SlowStart] -= 1 + if battler.effects[PBEffects::SlowStart] == 0 + pbDisplay(_INTL("{1} finally got its act together!", battler.pbThis)) + end + end + end + + #============================================================================= + # End Of Round shift distant battlers to middle positions + #============================================================================= + def pbEORShiftDistantBattlers + # Move battlers around if none are near to each other + # NOTE: This code assumes each side has a maximum of 3 battlers on it, and + # is not generalised to larger side sizes. + if !singleBattle? + swaps = [] # Each element is an array of two battler indices to swap + 2.times do |side| + next if pbSideSize(side) == 1 # Only battlers on sides of size 2+ need to move + # Check if any battler on this side is near any battler on the other side + anyNear = false + allSameSideBattlers(side).each do |battler| + anyNear = allOtherSideBattlers(battler).any? { |other| nearBattlers?(other.index, battler.index) } + break if anyNear + end + break if anyNear + # No battlers on this side are near any battlers on the other side; try + # to move them + # NOTE: If we get to here (assuming both sides are of size 3 or less), + # there is definitely only 1 able battler on this side, so we + # don't need to worry about multiple battlers trying to move into + # the same position. If you add support for a side of size 4+, + # this code will need revising to account for that, as well as to + # add more complex code to ensure battlers will end up near each + # other. + allSameSideBattlers(side).each do |battler| + # Get the position to move to + pos = -1 + case pbSideSize(side) + when 2 then pos = [2, 3, 0, 1][battler.index] # The unoccupied position + when 3 then pos = (side == 0) ? 2 : 3 # The centre position + end + next if pos < 0 + # Can't move if the same trainer doesn't control both positions + idxOwner = pbGetOwnerIndexFromBattlerIndex(battler.index) + next if pbGetOwnerIndexFromBattlerIndex(pos) != idxOwner + swaps.push([battler.index, pos]) + end + end + # Move battlers around + swaps.each do |pair| + next if pbSideSize(pair[0]) == 2 && swaps.length > 1 + next if !pbSwapBattlers(pair[0], pair[1]) + case pbSideSize(side) + when 2 + pbDisplay(_INTL("{1} moved across!", @battlers[pair[1]].pbThis)) + when 3 + pbDisplay(_INTL("{1} moved to the center!", @battlers[pair[1]].pbThis)) + end + end + end + end + + #============================================================================= + # Main End Of Round phase method + #============================================================================= + def pbEndOfRoundPhase + PBDebug.log("") + PBDebug.log("[End of round]") + @endOfRound = true + @scene.pbBeginEndOfRoundPhase + pbCalculatePriority # recalculate speeds + priority = pbPriority(true) # in order of fastest -> slowest speeds only + # Weather + pbEOREndWeather(priority) + # Future Sight/Doom Desire + @positions.each_with_index { |pos, idxPos| pbEORUseFutureSight(pos, idxPos) } + # Wish + pbEORWishHealing + # Sea of Fire damage (Fire Pledge + Grass Pledge combination) + pbEORSeaOfFireDamage + # Status-curing effects/abilities and HP-healing items + priority.each do |battler| + pbEORTerrainHealing(battler) + # Healer, Hydration, Shed Skin + if battler.abilityActive? + Battle::AbilityEffects.triggerEndOfRoundHealing(battler.ability, battler, self) + end + # Black Sludge, Leftovers + if battler.itemActive? + Battle::ItemEffects.triggerEndOfRoundHealing(battler.item, battler, self) + end + end + # Self-curing of status due to affection + if Settings::AFFECTION_EFFECTS && @internalBattle + priority.each do |battler| + next if battler.fainted? || battler.status == :NONE + next if !battler.pbOwnedByPlayer? || battler.affection_level < 4 || battler.mega? + next if pbRandom(100) < 80 + old_status = battler.status + battler.pbCureStatus(false) + case old_status + when :SLEEP + pbDisplay(_INTL("{1} shook itself awake so you wouldn't worry!", battler.pbThis)) + when :POISON + pbDisplay(_INTL("{1} managed to expel the poison so you wouldn't worry!", battler.pbThis)) + when :BURN + pbDisplay(_INTL("{1} healed its burn with its sheer determination so you wouldn't worry!", battler.pbThis)) + when :PARALYSIS + pbDisplay(_INTL("{1} gathered all its energy to break through its paralysis so you wouldn't worry!", battler.pbThis)) + when :FROZEN + pbDisplay(_INTL("{1} melted the ice with its fiery determination so you wouldn't worry!", battler.pbThis)) + end + end + end + # Healing from Aqua Ring, Ingrain, Leech Seed + pbEORHealingEffects(priority) + # Damage from Hyper Mode (Shadow Pokémon) + priority.each do |battler| + next if !battler.inHyperMode? || @choices[battler.index][0] != :UseMove + hpLoss = battler.totalhp / 24 + @scene.pbDamageAnimation(battler) + battler.pbReduceHP(hpLoss, false) + pbDisplay(_INTL("The Hyper Mode attack hurts {1}!", battler.pbThis(true))) + battler.pbFaint if battler.fainted? + end + # Damage from poison/burn + pbEORStatusProblemDamage(priority) + # Damage from Nightmare and Curse + pbEOREffectDamage(priority) + # Trapping attacks (Bind/Clamp/Fire Spin/Magma Storm/Sand Tomb/Whirlpool/Wrap) + priority.each { |battler| pbEORTrappingDamage(battler) } + # Octolock + priority.each do |battler| + next if battler.fainted? || battler.effects[PBEffects::Octolock] < 0 + pbCommonAnimation("Octolock", battler) + battler.pbLowerStatStage(:DEFENSE, 1, nil) if battler.pbCanLowerStatStage?(:DEFENSE) + battler.pbLowerStatStage(:SPECIAL_DEFENSE, 1, nil) if battler.pbCanLowerStatStage?(:SPECIAL_DEFENSE) + battler.pbItemOnStatDropped + end + # Effects that apply to a battler that wear off after a number of rounds + pbEOREndBattlerEffects(priority) + # Check for end of battle (i.e. because of Perish Song) + if @decision > 0 + pbGainExp + return + end + # Effects that apply to a side that wear off after a number of rounds + 2.times { |side| pbEOREndSideEffects(side, priority) } + # Effects that apply to the whole field that wear off after a number of rounds + pbEOREndFieldEffects(priority) + # End of terrains + pbEOREndTerrain + priority.each do |battler| + # Self-inflicted effects that wear off after a number of rounds + pbEOREndBattlerSelfEffects(battler) + # Bad Dreams, Moody, Speed Boost + if battler.abilityActive? + Battle::AbilityEffects.triggerEndOfRoundEffect(battler.ability, battler, self) + end + # Flame Orb, Sticky Barb, Toxic Orb + if battler.itemActive? + Battle::ItemEffects.triggerEndOfRoundEffect(battler.item, battler, self) + end + # Harvest, Pickup, Ball Fetch + if battler.abilityActive? + Battle::AbilityEffects.triggerEndOfRoundGainItem(battler.ability, battler, self) + end + end + pbGainExp + return if @decision > 0 + # Form checks + priority.each { |battler| battler.pbCheckForm(true) } + # Switch Pokémon in if possible + pbEORSwitch + return if @decision > 0 + # In battles with at least one side of size 3+, move battlers around if none + # are near to any foes + pbEORShiftDistantBattlers + # Try to make Trace work, check for end of primordial weather + priority.each { |battler| battler.pbContinualAbilityChecks } + # Reset/count down battler-specific effects (no messages) + allBattlers.each do |battler| + battler.effects[PBEffects::BanefulBunker] = false + battler.effects[PBEffects::Charge] -= 1 if battler.effects[PBEffects::Charge] > 0 + battler.effects[PBEffects::Counter] = -1 + battler.effects[PBEffects::CounterTarget] = -1 + battler.effects[PBEffects::Electrify] = false + battler.effects[PBEffects::Endure] = false + battler.effects[PBEffects::FirstPledge] = nil + battler.effects[PBEffects::Flinch] = false + battler.effects[PBEffects::FocusPunch] = false + battler.effects[PBEffects::FollowMe] = 0 + battler.effects[PBEffects::HelpingHand] = false + battler.effects[PBEffects::HyperBeam] -= 1 if battler.effects[PBEffects::HyperBeam] > 0 + battler.effects[PBEffects::KingsShield] = false + battler.effects[PBEffects::LaserFocus] -= 1 if battler.effects[PBEffects::LaserFocus] > 0 + if battler.effects[PBEffects::LockOn] > 0 # Also Mind Reader + battler.effects[PBEffects::LockOn] -= 1 + battler.effects[PBEffects::LockOnPos] = -1 if battler.effects[PBEffects::LockOn] == 0 + end + battler.effects[PBEffects::MagicBounce] = false + battler.effects[PBEffects::MagicCoat] = false + battler.effects[PBEffects::MirrorCoat] = -1 + battler.effects[PBEffects::MirrorCoatTarget] = -1 + battler.effects[PBEffects::Obstruct] = false + battler.effects[PBEffects::Powder] = false + battler.effects[PBEffects::Prankster] = false + battler.effects[PBEffects::PriorityAbility] = false + battler.effects[PBEffects::PriorityItem] = false + battler.effects[PBEffects::Protect] = false + battler.effects[PBEffects::RagePowder] = false + battler.effects[PBEffects::Roost] = false + battler.effects[PBEffects::Snatch] = 0 + battler.effects[PBEffects::SpikyShield] = false + battler.effects[PBEffects::Spotlight] = 0 + battler.effects[PBEffects::ThroatChop] -= 1 if battler.effects[PBEffects::ThroatChop] > 0 + battler.lastHPLost = 0 + battler.lastHPLostFromFoe = 0 + battler.droppedBelowHalfHP = false + battler.statsDropped = false + battler.tookDamageThisRound = false + battler.tookPhysicalHit = false + battler.statsRaisedThisRound = false + battler.statsLoweredThisRound = false + battler.canRestoreIceFace = false + battler.lastRoundMoveFailed = battler.lastMoveFailed + battler.lastAttacker.clear + battler.lastFoeAttacker.clear + end + # Reset/count down side-specific effects (no messages) + 2.times do |side| + @sides[side].effects[PBEffects::CraftyShield] = false + if !@sides[side].effects[PBEffects::EchoedVoiceUsed] + @sides[side].effects[PBEffects::EchoedVoiceCounter] = 0 + end + @sides[side].effects[PBEffects::EchoedVoiceUsed] = false + @sides[side].effects[PBEffects::MatBlock] = false + @sides[side].effects[PBEffects::QuickGuard] = false + @sides[side].effects[PBEffects::Round] = false + @sides[side].effects[PBEffects::WideGuard] = false + end + # Reset/count down field-specific effects (no messages) + @field.effects[PBEffects::IonDeluge] = false + @field.effects[PBEffects::FairyLock] -= 1 if @field.effects[PBEffects::FairyLock] > 0 + @field.effects[PBEffects::FusionBolt] = false + @field.effects[PBEffects::FusionFlare] = false + @endOfRound = false + end +end diff --git a/Data/Scripts/011_Battle/001_Battler/005_Battler_StatStages.rb b/Data/Scripts/011_Battle/001_Battler/005_Battler_StatStages.rb deleted file mode 100644 index 17058879cb..0000000000 --- a/Data/Scripts/011_Battle/001_Battler/005_Battler_StatStages.rb +++ /dev/null @@ -1,308 +0,0 @@ -class PokeBattle_Battler - #============================================================================= - # Increase stat stages - #============================================================================= - def statStageAtMax?(stat) - return @stages[stat]>=6 - end - - def pbCanRaiseStatStage?(stat,user=nil,move=nil,showFailMsg=false,ignoreContrary=false) - return false if fainted? - # Contrary - if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker - return pbCanLowerStatStage?(stat,user,move,showFailMsg,true) - end - # Check the stat stage - if statStageAtMax?(stat) - @battle.pbDisplay(_INTL("{1}'s {2} won't go any higher!", - pbThis, GameData::Stat.get(stat).name)) if showFailMsg - return false - end - return true - end - - def pbRaiseStatStageBasic(stat,increment,ignoreContrary=false) - if !@battle.moldBreaker - # Contrary - if hasActiveAbility?(:CONTRARY) && !ignoreContrary - return pbLowerStatStageBasic(stat,increment,true) - end - # Simple - increment *= 2 if hasActiveAbility?(:SIMPLE) - end - # Change the stat stage - increment = [increment,6-@stages[stat]].min - if increment>0 - stat_name = GameData::Stat.get(stat).name - new = @stages[stat]+increment - PBDebug.log("[Stat change] #{pbThis}'s #{stat_name}: #{@stages[stat]} -> #{new} (+#{increment})") - @stages[stat] += increment - end - return increment - end - - def pbRaiseStatStage(stat,increment,user,showAnim=true,ignoreContrary=false) - # Contrary - if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker - return pbLowerStatStage(stat,increment,user,showAnim,true) - end - # Perform the stat stage change - increment = pbRaiseStatStageBasic(stat,increment,ignoreContrary) - return false if increment<=0 - # Stat up animation and message - @battle.pbCommonAnimation("StatUp",self) if showAnim - arrStatTexts = [ - _INTL("{1}'s {2} rose!",pbThis,GameData::Stat.get(stat).name), - _INTL("{1}'s {2} rose sharply!",pbThis,GameData::Stat.get(stat).name), - _INTL("{1}'s {2} rose drastically!",pbThis,GameData::Stat.get(stat).name)] - @battle.pbDisplay(arrStatTexts[[increment-1,2].min]) - # Trigger abilities upon stat gain - if abilityActive? - BattleHandlers.triggerAbilityOnStatGain(self.ability,self,stat,user) - end - return true - end - - def pbRaiseStatStageByCause(stat,increment,user,cause,showAnim=true,ignoreContrary=false) - # Contrary - if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker - return pbLowerStatStageByCause(stat,increment,user,cause,showAnim,true) - end - # Perform the stat stage change - increment = pbRaiseStatStageBasic(stat,increment,ignoreContrary) - return false if increment<=0 - # Stat up animation and message - @battle.pbCommonAnimation("StatUp",self) if showAnim - if user.index==@index - arrStatTexts = [ - _INTL("{1}'s {2} raised its {3}!",pbThis,cause,GameData::Stat.get(stat).name), - _INTL("{1}'s {2} sharply raised its {3}!",pbThis,cause,GameData::Stat.get(stat).name), - _INTL("{1}'s {2} drastically raised its {3}!",pbThis,cause,GameData::Stat.get(stat).name)] - else - arrStatTexts = [ - _INTL("{1}'s {2} raised {3}'s {4}!",user.pbThis,cause,pbThis(true),GameData::Stat.get(stat).name), - _INTL("{1}'s {2} sharply raised {3}'s {4}!",user.pbThis,cause,pbThis(true),GameData::Stat.get(stat).name), - _INTL("{1}'s {2} drastically raised {3}'s {4}!",user.pbThis,cause,pbThis(true),GameData::Stat.get(stat).name)] - end - @battle.pbDisplay(arrStatTexts[[increment-1,2].min]) - # Trigger abilities upon stat gain - if abilityActive? - BattleHandlers.triggerAbilityOnStatGain(self.ability,self,stat,user) - end - return true - end - - def pbRaiseStatStageByAbility(stat,increment,user,splashAnim=true) - return false if fainted? - ret = false - @battle.pbShowAbilitySplash(user) if splashAnim - if pbCanRaiseStatStage?(stat,user,nil,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - ret = pbRaiseStatStage(stat,increment,user) - else - ret = pbRaiseStatStageByCause(stat,increment,user,user.abilityName) - end - end - @battle.pbHideAbilitySplash(user) if splashAnim - return ret - end - - #============================================================================= - # Decrease stat stages - #============================================================================= - def statStageAtMin?(stat) - return @stages[stat]<=-6 - end - - def pbCanLowerStatStage?(stat,user=nil,move=nil,showFailMsg=false,ignoreContrary=false) - return false if fainted? - # Contrary - if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker - return pbCanRaiseStatStage?(stat,user,move,showFailMsg,true) - end - if !user || user.index!=@index # Not self-inflicted - if @effects[PBEffects::Substitute]>0 && !(move && move.ignoresSubstitute?(user)) - @battle.pbDisplay(_INTL("{1} is protected by its substitute!",pbThis)) if showFailMsg - return false - end - if pbOwnSide.effects[PBEffects::Mist]>0 && - !(user && user.hasActiveAbility?(:INFILTRATOR)) - @battle.pbDisplay(_INTL("{1} is protected by Mist!",pbThis)) if showFailMsg - return false - end - if abilityActive? - return false if BattleHandlers.triggerStatLossImmunityAbility( - self.ability,self,stat,@battle,showFailMsg) if !@battle.moldBreaker - return false if BattleHandlers.triggerStatLossImmunityAbilityNonIgnorable( - self.ability,self,stat,@battle,showFailMsg) - end - if !@battle.moldBreaker - eachAlly do |b| - next if !b.abilityActive? - return false if BattleHandlers.triggerStatLossImmunityAllyAbility( - b.ability,b,self,stat,@battle,showFailMsg) - end - end - end - # Check the stat stage - if statStageAtMin?(stat) - @battle.pbDisplay(_INTL("{1}'s {2} won't go any lower!", - pbThis, GameData::Stat.get(stat).name)) if showFailMsg - return false - end - return true - end - - def pbLowerStatStageBasic(stat,increment,ignoreContrary=false) - if !@battle.moldBreaker - # Contrary - if hasActiveAbility?(:CONTRARY) && !ignoreContrary - return pbRaiseStatStageBasic(stat,increment,true) - end - # Simple - increment *= 2 if hasActiveAbility?(:SIMPLE) - end - # Change the stat stage - increment = [increment,6+@stages[stat]].min - if increment>0 - stat_name = GameData::Stat.get(stat).name - new = @stages[stat]-increment - PBDebug.log("[Stat change] #{pbThis}'s #{stat_name}: #{@stages[stat]} -> #{new} (-#{increment})") - @stages[stat] -= increment - end - return increment - end - - def pbLowerStatStage(stat,increment,user,showAnim=true,ignoreContrary=false) - # Contrary - if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker - return pbRaiseStatStage(stat,increment,user,showAnim,true) - end - # Perform the stat stage change - increment = pbLowerStatStageBasic(stat,increment,ignoreContrary) - return false if increment<=0 - # Stat down animation and message - @battle.pbCommonAnimation("StatDown",self) if showAnim - arrStatTexts = [ - _INTL("{1}'s {2} fell!",pbThis,GameData::Stat.get(stat).name), - _INTL("{1}'s {2} harshly fell!",pbThis,GameData::Stat.get(stat).name), - _INTL("{1}'s {2} severely fell!",pbThis,GameData::Stat.get(stat).name)] - @battle.pbDisplay(arrStatTexts[[increment-1,2].min]) - # Trigger abilities upon stat loss - if abilityActive? - BattleHandlers.triggerAbilityOnStatLoss(self.ability,self,stat,user) - end - return true - end - - def pbLowerStatStageByCause(stat,increment,user,cause,showAnim=true,ignoreContrary=false) - # Contrary - if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker - return pbRaiseStatStageByCause(stat,increment,user,cause,showAnim,true) - end - # Perform the stat stage change - increment = pbLowerStatStageBasic(stat,increment,ignoreContrary) - return false if increment<=0 - # Stat down animation and message - @battle.pbCommonAnimation("StatDown",self) if showAnim - if user.index==@index - arrStatTexts = [ - _INTL("{1}'s {2} lowered its {3}!",pbThis,cause,GameData::Stat.get(stat).name), - _INTL("{1}'s {2} harshly lowered its {3}!",pbThis,cause,GameData::Stat.get(stat).name), - _INTL("{1}'s {2} severely lowered its {3}!",pbThis,cause,GameData::Stat.get(stat).name)] - else - arrStatTexts = [ - _INTL("{1}'s {2} lowered {3}'s {4}!",user.pbThis,cause,pbThis(true),GameData::Stat.get(stat).name), - _INTL("{1}'s {2} harshly lowered {3}'s {4}!",user.pbThis,cause,pbThis(true),GameData::Stat.get(stat).name), - _INTL("{1}'s {2} severely lowered {3}'s {4}!",user.pbThis,cause,pbThis(true),GameData::Stat.get(stat).name)] - end - @battle.pbDisplay(arrStatTexts[[increment-1,2].min]) - # Trigger abilities upon stat loss - if abilityActive? - BattleHandlers.triggerAbilityOnStatLoss(self.ability,self,stat,user) - end - return true - end - - def pbLowerStatStageByAbility(stat,increment,user,splashAnim=true,checkContact=false) - ret = false - @battle.pbShowAbilitySplash(user) if splashAnim - if pbCanLowerStatStage?(stat,user,nil,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) && - (!checkContact || affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH)) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - ret = pbLowerStatStage(stat,increment,user) - else - ret = pbLowerStatStageByCause(stat,increment,user,user.abilityName) - end - end - @battle.pbHideAbilitySplash(user) if splashAnim - return ret - end - - def pbLowerAttackStatStageIntimidate(user) - return false if fainted? - # NOTE: Substitute intentially blocks Intimidate even if self has Contrary. - if @effects[PBEffects::Substitute]>0 - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} is protected by its substitute!",pbThis)) - else - @battle.pbDisplay(_INTL("{1}'s substitute protected it from {2}'s {3}!", - pbThis,user.pbThis(true),user.abilityName)) - end - return false - end - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - return pbLowerStatStageByAbility(:ATTACK,1,user,false) - end - # NOTE: These checks exist to ensure appropriate messages are shown if - # Intimidate is blocked somehow (i.e. the messages should mention the - # Intimidate ability by name). - if !hasActiveAbility?(:CONTRARY) - if pbOwnSide.effects[PBEffects::Mist]>0 - @battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by Mist!", - pbThis,user.pbThis(true),user.abilityName)) - return false - end - if abilityActive? - if BattleHandlers.triggerStatLossImmunityAbility(self.ability,self,:ATTACK,@battle,false) || - BattleHandlers.triggerStatLossImmunityAbilityNonIgnorable(self.ability,self,:ATTACK,@battle,false) - @battle.pbDisplay(_INTL("{1}'s {2} prevented {3}'s {4} from working!", - pbThis,abilityName,user.pbThis(true),user.abilityName)) - return false - end - end - eachAlly do |b| - next if !b.abilityActive? - if BattleHandlers.triggerStatLossImmunityAllyAbility(b.ability,b,self,:ATTACK,@battle,false) - @battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by {4}'s {5}!", - pbThis,user.pbThis(true),user.abilityName,b.pbThis(true),b.abilityName)) - return false - end - end - end - return false if !pbCanLowerStatStage?(:ATTACK,user) - return pbLowerStatStageByCause(:ATTACK,1,user,user.abilityName) - end - - #============================================================================= - # Reset stat stages - #============================================================================= - def hasAlteredStatStages? - GameData::Stat.each_battle { |s| return true if @stages[s.id] != 0 } - return false - end - - def hasRaisedStatStages? - GameData::Stat.each_battle { |s| return true if @stages[s.id] > 0 } - return false - end - - def hasLoweredStatStages? - GameData::Stat.each_battle { |s| return true if @stages[s.id] < 0 } - return false - end - - def pbResetStatStages - GameData::Stat.each_battle { |s| @stages[s.id] = 0 } - end -end diff --git a/Data/Scripts/011_Battle/001_Battler/006_Battler_AbilityAndItem.rb b/Data/Scripts/011_Battle/001_Battler/006_Battler_AbilityAndItem.rb deleted file mode 100644 index b7d1390037..0000000000 --- a/Data/Scripts/011_Battle/001_Battler/006_Battler_AbilityAndItem.rb +++ /dev/null @@ -1,296 +0,0 @@ -class PokeBattle_Battler - #============================================================================= - # Called when a Pokémon (self) is sent into battle or its ability changes. - #============================================================================= - def pbEffectsOnSwitchIn(switchIn=false) - # Healing Wish/Lunar Dance/entry hazards - @battle.pbOnActiveOne(self) if switchIn - # Primal Revert upon entering battle - @battle.pbPrimalReversion(@index) if !fainted? - # Ending primordial weather, checking Trace - pbContinualAbilityChecks(true) - # Abilities that trigger upon switching in - if (!fainted? && unstoppableAbility?) || abilityActive? - BattleHandlers.triggerAbilityOnSwitchIn(self.ability,self,@battle) - end - # Check for end of primordial weather - @battle.pbEndPrimordialWeather - # Items that trigger upon switching in (Air Balloon message) - if switchIn && itemActive? - BattleHandlers.triggerItemOnSwitchIn(self.item,self,@battle) - end - # Berry check, status-curing ability check - pbHeldItemTriggerCheck if switchIn - pbAbilityStatusCureCheck - end - - #============================================================================= - # Ability effects - #============================================================================= - def pbAbilitiesOnSwitchOut - if abilityActive? - BattleHandlers.triggerAbilityOnSwitchOut(self.ability,self,false) - end - # Reset form - @battle.peer.pbOnLeavingBattle(@battle,@pokemon,@battle.usedInBattle[idxOwnSide][@index/2]) - # Treat self as fainted - @hp = 0 - @fainted = true - # Check for end of primordial weather - @battle.pbEndPrimordialWeather - end - - def pbAbilitiesOnFainting - # Self fainted; check all other battlers to see if their abilities trigger - @battle.pbPriority(true).each do |b| - next if !b || !b.abilityActive? - BattleHandlers.triggerAbilityChangeOnBattlerFainting(b.ability,b,self,@battle) - end - @battle.pbPriority(true).each do |b| - next if !b || !b.abilityActive? - BattleHandlers.triggerAbilityOnBattlerFainting(b.ability,b,self,@battle) - end - end - - # Used for Emergency Exit/Wimp Out. - def pbAbilitiesOnDamageTaken(oldHP,newHP=-1) - return false if !abilityActive? - newHP = @hp if newHP<0 - return false if oldHP<@totalhp/2 || newHP>=@totalhp/2 # Didn't drop below half - ret = BattleHandlers.triggerAbilityOnHPDroppedBelowHalf(self.ability,self,@battle) - return ret # Whether self has switched out - end - - # Called when a Pokémon (self) enters battle, at the end of each move used, - # and at the end of each round. - def pbContinualAbilityChecks(onSwitchIn=false) - # Check for end of primordial weather - @battle.pbEndPrimordialWeather - # Trace - if hasActiveAbility?(:TRACE) - # NOTE: In Gen 5 only, Trace only triggers upon the Trace bearer switching - # in and not at any later times, even if a traceable ability turns - # up later. Essentials ignores this, and allows Trace to trigger - # whenever it can even in the old battle mechanics. - choices = [] - @battle.eachOtherSideBattler(@index) do |b| - next if b.ungainableAbility? || - [:POWEROFALCHEMY, :RECEIVER, :TRACE].include?(b.ability_id) - choices.push(b) - end - if choices.length>0 - choice = choices[@battle.pbRandom(choices.length)] - @battle.pbShowAbilitySplash(self) - self.ability = choice.ability - @battle.pbDisplay(_INTL("{1} traced {2}'s {3}!",pbThis,choice.pbThis(true),choice.abilityName)) - @battle.pbHideAbilitySplash(self) - if !onSwitchIn && (unstoppableAbility? || abilityActive?) - BattleHandlers.triggerAbilityOnSwitchIn(self.ability,self,@battle) - end - end - end - end - - #============================================================================= - # Ability curing - #============================================================================= - # Cures status conditions, confusion and infatuation. - def pbAbilityStatusCureCheck - if abilityActive? - BattleHandlers.triggerStatusCureAbility(self.ability,self) - end - end - - #============================================================================= - # Ability change - #============================================================================= - def pbOnAbilityChanged(oldAbil) - if @effects[PBEffects::Illusion] && oldAbil == :ILLUSION - @effects[PBEffects::Illusion] = nil - if !@effects[PBEffects::Transform] - @battle.scene.pbChangePokemon(self, @pokemon) - @battle.pbDisplay(_INTL("{1}'s {2} wore off!", pbThis, GameData::Ability.get(oldAbil).name)) - @battle.pbSetSeen(self) - end - end - @effects[PBEffects::GastroAcid] = false if unstoppableAbility? - @effects[PBEffects::SlowStart] = 0 if self.ability != :SLOWSTART - # Revert form if Flower Gift/Forecast was lost - pbCheckFormOnWeatherChange - # Check for end of primordial weather - @battle.pbEndPrimordialWeather - end - - #============================================================================= - # Held item consuming/removing - #============================================================================= - def canConsumeBerry? - return false if @battle.pbCheckOpposingAbility(:UNNERVE, @index) - return true - end - - def canConsumePinchBerry?(check_gluttony = true) - return false if !canConsumeBerry? - return true if @hp <= @totalhp / 4 - return true if @hp <= @totalhp / 2 && (!check_gluttony || hasActiveAbility?(:GLUTTONY)) - return false - end - - # permanent is whether the item is lost even after battle. Is false for Knock - # Off. - def pbRemoveItem(permanent = true) - @effects[PBEffects::ChoiceBand] = nil - @effects[PBEffects::Unburden] = true if self.item - setInitialItem(nil) if permanent && self.item == self.initialItem - self.item = nil - end - - def pbConsumeItem(recoverable=true,symbiosis=true,belch=true) - PBDebug.log("[Item consumed] #{pbThis} consumed its held #{itemName}") - if recoverable - setRecycleItem(@item_id) - @effects[PBEffects::PickupItem] = @item_id - @effects[PBEffects::PickupUse] = @battle.nextPickupUse - end - setBelched if belch && self.item.is_berry? - pbRemoveItem - pbSymbiosis if symbiosis - end - - def pbSymbiosis - return if fainted? - return if self.item - @battle.pbPriority(true).each do |b| - next if b.opposes? - next if !b.hasActiveAbility?(:SYMBIOSIS) - next if !b.item || b.unlosableItem?(b.item) - next if unlosableItem?(b.item) - @battle.pbShowAbilitySplash(b) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} shared its {2} with {3}!", - b.pbThis,b.itemName,pbThis(true))) - else - @battle.pbDisplay(_INTL("{1}'s {2} let it share its {3} with {4}!", - b.pbThis,b.abilityName,b.itemName,pbThis(true))) - end - self.item = b.item - b.item = nil - b.effects[PBEffects::Unburden] = true - @battle.pbHideAbilitySplash(b) - pbHeldItemTriggerCheck - break - end - end - - # item_to_use is an item ID or GameData::Item object. own_item is whether the - # item is held by self. fling is for Fling only. - def pbHeldItemTriggered(item_to_use, own_item = true, fling = false) - # Cheek Pouch - if hasActiveAbility?(:CHEEKPOUCH) && GameData::Item.get(item_to_use).is_berry? && canHeal? - @battle.pbShowAbilitySplash(self) - pbRecoverHP(@totalhp / 3) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1}'s HP was restored.", pbThis)) - else - @battle.pbDisplay(_INTL("{1}'s {2} restored its HP.", pbThis, abilityName)) - end - @battle.pbHideAbilitySplash(self) - end - pbConsumeItem if own_item - pbSymbiosis if !own_item && !fling # Bug Bite/Pluck users trigger Symbiosis - end - - #============================================================================= - # Held item trigger checks - #============================================================================= - # NOTE: A Pokémon using Bug Bite/Pluck, and a Pokémon having an item thrown at - # it via Fling, will gain the effect of the item even if the Pokémon is - # affected by item-negating effects. - # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. - # fling is for Fling only. - def pbHeldItemTriggerCheck(item_to_use = nil, fling = false) - return if fainted? - return if !item_to_use && !itemActive? - pbItemHPHealCheck(item_to_use, fling) - pbItemStatusCureCheck(item_to_use, fling) - pbItemEndOfMoveCheck(item_to_use, fling) - # For Enigma Berry, Kee Berry and Maranga Berry, which have their effects - # when forcibly consumed by Pluck/Fling. - if item_to_use - itm = item_to_use || self.item - if BattleHandlers.triggerTargetItemOnHitPositiveBerry(itm, self, @battle, true) - pbHeldItemTriggered(itm, false, fling) - end - end - end - - # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. - # fling is for Fling only. - def pbItemHPHealCheck(item_to_use = nil, fling = false) - return if !item_to_use && !itemActive? - itm = item_to_use || self.item - if BattleHandlers.triggerHPHealItem(itm, self, @battle, !item_to_use.nil?) - pbHeldItemTriggered(itm, item_to_use.nil?, fling) - elsif !item_to_use - pbItemTerrainStatBoostCheck - end - end - - # Cures status conditions, confusion, infatuation and the other effects cured - # by Mental Herb. - # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. - # fling is for Fling only. - def pbItemStatusCureCheck(item_to_use = nil, fling = false) - return if fainted? - return if !item_to_use && !itemActive? - itm = item_to_use || self.item - if BattleHandlers.triggerStatusCureItem(itm, self, @battle, !item_to_use.nil?) - pbHeldItemTriggered(itm, item_to_use.nil?, fling) - end - end - - # Called at the end of using a move. - # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. - # fling is for Fling only. - def pbItemEndOfMoveCheck(item_to_use = nil, fling = false) - return if fainted? - return if !item_to_use && !itemActive? - itm = item_to_use || self.item - if BattleHandlers.triggerEndOfMoveItem(itm, self, @battle, !item_to_use.nil?) - pbHeldItemTriggered(itm, item_to_use.nil?, fling) - elsif BattleHandlers.triggerEndOfMoveStatRestoreItem(itm, self, @battle, !item_to_use.nil?) - pbHeldItemTriggered(itm, item_to_use.nil?, fling) - end - end - - # Used for White Herb (restore lowered stats). Only called by Moody and Sticky - # Web, as all other stat reduction happens because of/during move usage and - # this handler is also called at the end of each move's usage. - # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. - # fling is for Fling only. - def pbItemStatRestoreCheck(item_to_use = nil, fling = false) - return if fainted? - return if !item_to_use && !itemActive? - itm = item_to_use || self.item - if BattleHandlers.triggerEndOfMoveStatRestoreItem(itm, self, @battle, !item_to_use.nil?) - pbHeldItemTriggered(itm, item_to_use.nil?, fling) - end - end - - # Called when the battle terrain changes and when a Pokémon loses HP. - def pbItemTerrainStatBoostCheck - return if !itemActive? - if BattleHandlers.triggerTerrainStatBoostItem(self.item, self, @battle) - pbHeldItemTriggered(self.item) - end - end - - # Used for Adrenaline Orb. Called when Intimidate is triggered (even if - # Intimidate has no effect on the Pokémon). - def pbItemOnIntimidatedCheck - return if !itemActive? - if BattleHandlers.triggerItemOnIntimidated(self.item, self, @battle) - pbHeldItemTriggered(self.item) - end - end -end diff --git a/Data/Scripts/011_Battle/001_Battler/008_Battler_UseMove_Targeting.rb b/Data/Scripts/011_Battle/001_Battler/008_Battler_UseMove_Targeting.rb deleted file mode 100644 index c3cd1a6df2..0000000000 --- a/Data/Scripts/011_Battle/001_Battler/008_Battler_UseMove_Targeting.rb +++ /dev/null @@ -1,193 +0,0 @@ -class PokeBattle_Battler - #============================================================================= - # Get move's user - #============================================================================= - def pbFindUser(_choice,_move) - return self - end - - def pbChangeUser(choice,move,user) - # Snatch - move.snatched = false - if move.canSnatch? - newUser = nil; strength = 100 - @battle.eachBattler do |b| - next if b.effects[PBEffects::Snatch]==0 || - b.effects[PBEffects::Snatch]>=strength - next if b.effects[PBEffects::SkyDrop]>=0 - newUser = b - strength = b.effects[PBEffects::Snatch] - end - if newUser - user = newUser - user.effects[PBEffects::Snatch] = 0 - move.snatched = true - @battle.moldBreaker = user.hasMoldBreaker? - choice[3] = -1 # Clear pre-chosen target - end - end - return user - end - - #============================================================================= - # Get move's default target(s) - #============================================================================= - def pbFindTargets(choice,move,user) - preTarget = choice[3] # A target that was already chosen - targets = [] - # Get list of targets - case move.pbTarget(user).id # Curse can change its target type - when :NearAlly - targetBattler = (preTarget>=0) ? @battle.battlers[preTarget] : nil - if !pbAddTarget(targets,user,targetBattler,move) - pbAddTargetRandomAlly(targets,user,move) - end - when :UserOrNearAlly - targetBattler = (preTarget>=0) ? @battle.battlers[preTarget] : nil - if !pbAddTarget(targets,user,targetBattler,move,true,true) - pbAddTarget(targets,user,user,move,true,true) - end - when :UserAndAllies - pbAddTarget(targets,user,user,move,true,true) - @battle.eachSameSideBattler(user.index) { |b| pbAddTarget(targets,user,b,move,false,true) } - when :NearFoe, :NearOther - targetBattler = (preTarget>=0) ? @battle.battlers[preTarget] : nil - if !pbAddTarget(targets,user,targetBattler,move) - if preTarget>=0 && !user.opposes?(preTarget) - pbAddTargetRandomAlly(targets,user,move) - else - pbAddTargetRandomFoe(targets,user,move) - end - end - when :RandomNearFoe - pbAddTargetRandomFoe(targets,user,move) - when :AllNearFoes - @battle.eachOtherSideBattler(user.index) { |b| pbAddTarget(targets,user,b,move) } - when :Foe, :Other - targetBattler = (preTarget>=0) ? @battle.battlers[preTarget] : nil - if !pbAddTarget(targets,user,targetBattler,move,false) - if preTarget>=0 && !user.opposes?(preTarget) - pbAddTargetRandomAlly(targets,user,move,false) - else - pbAddTargetRandomFoe(targets,user,move,false) - end - end - when :AllFoes - @battle.eachOtherSideBattler(user.index) { |b| pbAddTarget(targets,user,b,move,false) } - when :AllNearOthers - @battle.eachBattler { |b| pbAddTarget(targets,user,b,move) } - when :AllBattlers - @battle.eachBattler { |b| pbAddTarget(targets,user,b,move,false,true) } - else - # Used by Counter/Mirror Coat/Metal Burst/Bide - move.pbAddTarget(targets,user) # Move-specific pbAddTarget, not the def below - end - return targets - end - - #============================================================================= - # Redirect attack to another target - #============================================================================= - def pbChangeTargets(move,user,targets) - target_data = move.pbTarget(user) - return targets if @battle.switching # For Pursuit interrupting a switch - return targets if move.cannotRedirect? - return targets if !target_data.can_target_one_foe? || targets.length != 1 - priority = @battle.pbPriority(true) - nearOnly = !target_data.can_choose_distant_target? - # Spotlight (takes priority over Follow Me/Rage Powder/Lightning Rod/Storm Drain) - newTarget = nil; strength = 100 # Lower strength takes priority - priority.each do |b| - next if b.fainted? || b.effects[PBEffects::SkyDrop]>=0 - next if b.effects[PBEffects::Spotlight]==0 || - b.effects[PBEffects::Spotlight]>=strength - next if !b.opposes?(user) - next if nearOnly && !b.near?(user) - newTarget = b - strength = b.effects[PBEffects::Spotlight] - end - if newTarget - PBDebug.log("[Move target changed] #{newTarget.pbThis}'s Spotlight made it the target") - targets = [] - pbAddTarget(targets,user,newTarget,move,nearOnly) - return targets - end - # Follow Me/Rage Powder (takes priority over Lightning Rod/Storm Drain) - newTarget = nil; strength = 100 # Lower strength takes priority - priority.each do |b| - next if b.fainted? || b.effects[PBEffects::SkyDrop]>=0 - next if b.effects[PBEffects::RagePowder] && !user.affectedByPowder? - next if b.effects[PBEffects::FollowMe]==0 || - b.effects[PBEffects::FollowMe]>=strength - next if !b.opposes?(user) - next if nearOnly && !b.near?(user) - newTarget = b - strength = b.effects[PBEffects::FollowMe] - end - if newTarget - PBDebug.log("[Move target changed] #{newTarget.pbThis}'s Follow Me/Rage Powder made it the target") - targets = [] - pbAddTarget(targets,user,newTarget,move,nearOnly) - return targets - end - # Lightning Rod - targets = pbChangeTargetByAbility(:LIGHTNINGROD,:ELECTRIC,move,user,targets,priority,nearOnly) - # Storm Drain - targets = pbChangeTargetByAbility(:STORMDRAIN,:WATER,move,user,targets,priority,nearOnly) - return targets - end - - def pbChangeTargetByAbility(drawingAbility,drawnType,move,user,targets,priority,nearOnly) - return targets if move.calcType != drawnType - return targets if targets[0].hasActiveAbility?(drawingAbility) - priority.each do |b| - next if b.index==user.index || b.index==targets[0].index - next if !b.hasActiveAbility?(drawingAbility) - next if nearOnly && !b.near?(user) - @battle.pbShowAbilitySplash(b) - targets.clear - pbAddTarget(targets,user,b,move,nearOnly) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} took the attack!",b.pbThis)) - else - @battle.pbDisplay(_INTL("{1} took the attack with its {2}!",b.pbThis,b.abilityName)) - end - @battle.pbHideAbilitySplash(b) - break - end - return targets - end - - #============================================================================= - # Register target - #============================================================================= - def pbAddTarget(targets,user,target,move,nearOnly=true,allowUser=false) - return false if !target || (target.fainted? && !move.cannotRedirect?) - return false if !(allowUser && user==target) && nearOnly && !user.near?(target) - targets.each { |b| return true if b.index==target.index } # Already added - targets.push(target) - return true - end - - def pbAddTargetRandomAlly(targets,user,_move,nearOnly=true) - choices = [] - user.eachAlly do |b| - next if nearOnly && !user.near?(b) - pbAddTarget(choices,user,b,nearOnly) - end - if choices.length>0 - pbAddTarget(targets,user,choices[@battle.pbRandom(choices.length)],nearOnly) - end - end - - def pbAddTargetRandomFoe(targets,user,_move,nearOnly=true) - choices = [] - user.eachOpposing do |b| - next if nearOnly && !user.near?(b) - pbAddTarget(choices,user,b,nearOnly) - end - if choices.length>0 - pbAddTarget(targets,user,choices[@battle.pbRandom(choices.length)],nearOnly) - end - end -end diff --git a/Data/Scripts/011_Battle/001_Battler/009_Battler_UseMove_SuccessChecks.rb b/Data/Scripts/011_Battle/001_Battler/009_Battler_UseMove_SuccessChecks.rb deleted file mode 100644 index 33e23c6769..0000000000 --- a/Data/Scripts/011_Battle/001_Battler/009_Battler_UseMove_SuccessChecks.rb +++ /dev/null @@ -1,538 +0,0 @@ -class PokeBattle_Battler - #============================================================================= - # Decide whether the trainer is allowed to tell the Pokémon to use the given - # move. Called when choosing a command for the round. - # Also called when processing the Pokémon's action, because these effects also - # prevent Pokémon action. Relevant because these effects can become active - # earlier in the same round (after choosing the command but before using the - # move) or an unusable move may be called by another move such as Metronome. - #============================================================================= - def pbCanChooseMove?(move,commandPhase,showMessages=true,specialUsage=false) - # Disable - if @effects[PBEffects::DisableMove]==move.id && !specialUsage - if showMessages - msg = _INTL("{1}'s {2} is disabled!",pbThis,move.name) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - # Heal Block - if @effects[PBEffects::HealBlock]>0 && move.healingMove? - if showMessages - msg = _INTL("{1} can't use {2} because of Heal Block!",pbThis,move.name) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - # Gravity - if @battle.field.effects[PBEffects::Gravity]>0 && move.unusableInGravity? - if showMessages - msg = _INTL("{1} can't use {2} because of gravity!",pbThis,move.name) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - # Throat Chop - if @effects[PBEffects::ThroatChop]>0 && move.soundMove? - if showMessages - msg = _INTL("{1} can't use {2} because of Throat Chop!",pbThis,move.name) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - # Choice Band - if @effects[PBEffects::ChoiceBand] - if hasActiveItem?([:CHOICEBAND,:CHOICESPECS,:CHOICESCARF]) && - pbHasMove?(@effects[PBEffects::ChoiceBand]) - if move.id!=@effects[PBEffects::ChoiceBand] - if showMessages - msg = _INTL("{1} allows the use of only {2}!",itemName, - GameData::Move.get(@effects[PBEffects::ChoiceBand]).name) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - else - @effects[PBEffects::ChoiceBand] = nil - end - end - # Taunt - if @effects[PBEffects::Taunt]>0 && move.statusMove? - if showMessages - msg = _INTL("{1} can't use {2} after the taunt!",pbThis,move.name) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - # Torment - if @effects[PBEffects::Torment] && !@effects[PBEffects::Instructed] && - @lastMoveUsed && move.id==@lastMoveUsed && move.id!=@battle.struggle.id - if showMessages - msg = _INTL("{1} can't use the same move twice in a row due to the torment!",pbThis) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - # Imprison - @battle.eachOtherSideBattler(@index) do |b| - next if !b.effects[PBEffects::Imprison] || !b.pbHasMove?(move.id) - if showMessages - msg = _INTL("{1} can't use its sealed {2}!",pbThis,move.name) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - # Assault Vest (prevents choosing status moves but doesn't prevent - # executing them) - if hasActiveItem?(:ASSAULTVEST) && move.statusMove? && commandPhase - if showMessages - msg = _INTL("The effects of the {1} prevent status moves from being used!", - itemName) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - # Belch - return false if !move.pbCanChooseMove?(self,commandPhase,showMessages) - return true - end - - #============================================================================= - # Obedience check - #============================================================================= - # Return true if Pokémon continues attacking (although it may have chosen to - # use a different move in disobedience), or false if attack stops. - def pbObedienceCheck?(choice) - return true if usingMultiTurnAttack? - return true if choice[0]!=:UseMove - return true if !@battle.internalBattle - return true if !@battle.pbOwnedByPlayer?(@index) - disobedient = false - # Pokémon may be disobedient; calculate if it is - badgeLevel = 10 * (@battle.pbPlayer.badge_count + 1) - badgeLevel = GameData::GrowthRate.max_level if @battle.pbPlayer.badge_count >= 8 - if @pokemon.foreign?(@battle.pbPlayer) && @level>badgeLevel - a = ((@level+badgeLevel)*@battle.pbRandom(256)/256).floor - disobedient |= (a>=badgeLevel) - end - disobedient |= !pbHyperModeObedience(choice[2]) - return true if !disobedient - # Pokémon is disobedient; make it do something else - return pbDisobey(choice,badgeLevel) - end - - def pbDisobey(choice,badgeLevel) - move = choice[2] - PBDebug.log("[Disobedience] #{pbThis} disobeyed") - @effects[PBEffects::Rage] = false - # Do nothing if using Snore/Sleep Talk - if @status == :SLEEP && move.usableWhenAsleep? - @battle.pbDisplay(_INTL("{1} ignored orders and kept sleeping!",pbThis)) - return false - end - b = ((@level+badgeLevel)*@battle.pbRandom(256)/256).floor - # Use another move - if b=0 # Intentionally no message here - PBDebug.log("[Move failed] #{pbThis} can't use #{move.name} because of being Sky Dropped") - return false - end - if @effects[PBEffects::HyperBeam]>0 # Intentionally before Truant - @battle.pbDisplay(_INTL("{1} must recharge!",pbThis)) - return false - end - if choice[1]==-2 # Battle Palace - @battle.pbDisplay(_INTL("{1} appears incapable of using its power!",pbThis)) - return false - end - # Skip checking all applied effects that could make self fail doing something - return true if skipAccuracyCheck - # Check status problems and continue their effects/cure them - case @status - when :SLEEP - self.statusCount -= 1 - if @statusCount<=0 - pbCureStatus - else - pbContinueStatus - if !move.usableWhenAsleep? # Snore/Sleep Talk - @lastMoveFailed = true - return false - end - end - when :FROZEN - if !move.thawsUser? - if @battle.pbRandom(100)<20 - pbCureStatus - else - pbContinueStatus - @lastMoveFailed = true - return false - end - end - end - # Obedience check - return false if !pbObedienceCheck?(choice) - # Truant - if hasActiveAbility?(:TRUANT) - @effects[PBEffects::Truant] = !@effects[PBEffects::Truant] - if !@effects[PBEffects::Truant] # True means loafing, but was just inverted - @battle.pbShowAbilitySplash(self) - @battle.pbDisplay(_INTL("{1} is loafing around!",pbThis)) - @lastMoveFailed = true - @battle.pbHideAbilitySplash(self) - return false - end - end - # Flinching - if @effects[PBEffects::Flinch] - @battle.pbDisplay(_INTL("{1} flinched and couldn't move!",pbThis)) - if abilityActive? - BattleHandlers.triggerAbilityOnFlinch(self.ability,self,@battle) - end - @lastMoveFailed = true - return false - end - # Confusion - if @effects[PBEffects::Confusion]>0 - @effects[PBEffects::Confusion] -= 1 - if @effects[PBEffects::Confusion]<=0 - pbCureConfusion - @battle.pbDisplay(_INTL("{1} snapped out of its confusion.",pbThis)) - else - @battle.pbCommonAnimation("Confusion",self) - @battle.pbDisplay(_INTL("{1} is confused!",pbThis)) - threshold = (Settings::MECHANICS_GENERATION >= 7) ? 33 : 50 # % chance - if @battle.pbRandom(100)=0 - @battle.pbCommonAnimation("Attract",self) - @battle.pbDisplay(_INTL("{1} is in love with {2}!",pbThis, - @battle.battlers[@effects[PBEffects::Attract]].pbThis(true))) - if @battle.pbRandom(100)<50 - @battle.pbDisplay(_INTL("{1} is immobilized by love!",pbThis)) - @lastMoveFailed = true - return false - end - end - return true - end - - #============================================================================= - # Initial success check against the target. Done once before the first hit. - # Includes move-specific failure conditions, protections and type immunities. - #============================================================================= - def pbSuccessCheckAgainstTarget(move,user,target) - typeMod = move.pbCalcTypeMod(move.calcType,user,target) - target.damageState.typeMod = typeMod - # Two-turn attacks can't fail here in the charging turn - return true if user.effects[PBEffects::TwoTurnAttack] - # Move-specific failures - return false if move.pbFailsAgainstTarget?(user,target) - # Immunity to priority moves because of Psychic Terrain - if @battle.field.terrain == :Psychic && target.affectedByTerrain? && target.opposes?(user) && - @battle.choices[user.index][4]>0 # Move priority saved from pbCalculatePriority - @battle.pbDisplay(_INTL("{1} surrounds itself with psychic terrain!",target.pbThis)) - return false - end - # Crafty Shield - if target.pbOwnSide.effects[PBEffects::CraftyShield] && user.index!=target.index && - move.statusMove? && !move.pbTarget(user).targets_all - @battle.pbCommonAnimation("CraftyShield",target) - @battle.pbDisplay(_INTL("Crafty Shield protected {1}!",target.pbThis(true))) - target.damageState.protected = true - @battle.successStates[user.index].protected = true - return false - end - # Wide Guard - if target.pbOwnSide.effects[PBEffects::WideGuard] && user.index!=target.index && - move.pbTarget(user).num_targets > 1 && - (Settings::MECHANICS_GENERATION >= 7 || move.damagingMove?) - @battle.pbCommonAnimation("WideGuard",target) - @battle.pbDisplay(_INTL("Wide Guard protected {1}!",target.pbThis(true))) - target.damageState.protected = true - @battle.successStates[user.index].protected = true - return false - end - if move.canProtectAgainst? - # Quick Guard - if target.pbOwnSide.effects[PBEffects::QuickGuard] && - @battle.choices[user.index][4]>0 # Move priority saved from pbCalculatePriority - @battle.pbCommonAnimation("QuickGuard",target) - @battle.pbDisplay(_INTL("Quick Guard protected {1}!",target.pbThis(true))) - target.damageState.protected = true - @battle.successStates[user.index].protected = true - return false - end - # Protect - if target.effects[PBEffects::Protect] - @battle.pbCommonAnimation("Protect",target) - @battle.pbDisplay(_INTL("{1} protected itself!",target.pbThis)) - target.damageState.protected = true - @battle.successStates[user.index].protected = true - return false - end - # King's Shield - if target.effects[PBEffects::KingsShield] && move.damagingMove? - @battle.pbCommonAnimation("KingsShield",target) - @battle.pbDisplay(_INTL("{1} protected itself!",target.pbThis)) - target.damageState.protected = true - @battle.successStates[user.index].protected = true - if move.pbContactMove?(user) && user.affectedByContactEffect? - if user.pbCanLowerStatStage?(:ATTACK) - user.pbLowerStatStage(:ATTACK,2,nil) - end - end - return false - end - # Spiky Shield - if target.effects[PBEffects::SpikyShield] - @battle.pbCommonAnimation("SpikyShield",target) - @battle.pbDisplay(_INTL("{1} protected itself!",target.pbThis)) - target.damageState.protected = true - @battle.successStates[user.index].protected = true - if move.pbContactMove?(user) && user.affectedByContactEffect? - @battle.scene.pbDamageAnimation(user) - user.pbReduceHP(user.totalhp/8,false) - @battle.pbDisplay(_INTL("{1} was hurt!",user.pbThis)) - user.pbItemHPHealCheck - end - return false - end - # Baneful Bunker - if target.effects[PBEffects::BanefulBunker] - @battle.pbCommonAnimation("BanefulBunker",target) - @battle.pbDisplay(_INTL("{1} protected itself!",target.pbThis)) - target.damageState.protected = true - @battle.successStates[user.index].protected = true - if move.pbContactMove?(user) && user.affectedByContactEffect? - user.pbPoison(target) if user.pbCanPoison?(target,false) - end - return false - end - # Mat Block - if target.pbOwnSide.effects[PBEffects::MatBlock] && move.damagingMove? - # NOTE: Confirmed no common animation for this effect. - @battle.pbDisplay(_INTL("{1} was blocked by the kicked-up mat!",move.name)) - target.damageState.protected = true - @battle.successStates[user.index].protected = true - return false - end - end - # Magic Coat/Magic Bounce - if move.canMagicCoat? && !target.semiInvulnerable? && target.opposes?(user) - if target.effects[PBEffects::MagicCoat] - target.damageState.magicCoat = true - target.effects[PBEffects::MagicCoat] = false - return false - end - if target.hasActiveAbility?(:MAGICBOUNCE) && !@battle.moldBreaker && - !target.effects[PBEffects::MagicBounce] - target.damageState.magicBounce = true - target.effects[PBEffects::MagicBounce] = true - return false - end - end - # Immunity because of ability (intentionally before type immunity check) - return false if move.pbImmunityByAbility(user,target) - # Type immunity - if move.pbDamagingMove? && Effectiveness.ineffective?(typeMod) - PBDebug.log("[Target immune] #{target.pbThis}'s type immunity") - @battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - return false - end - # Dark-type immunity to moves made faster by Prankster - if Settings::MECHANICS_GENERATION >= 7 && user.effects[PBEffects::Prankster] && - target.pbHasType?(:DARK) && target.opposes?(user) - PBDebug.log("[Target immune] #{target.pbThis} is Dark-type and immune to Prankster-boosted moves") - @battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - return false - end - # Airborne-based immunity to Ground moves - if move.damagingMove? && move.calcType == :GROUND && - target.airborne? && !move.hitsFlyingTargets? - if target.hasActiveAbility?(:LEVITATE) && !@battle.moldBreaker - @battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} avoided the attack!",target.pbThis)) - else - @battle.pbDisplay(_INTL("{1} avoided the attack with {2}!",target.pbThis,target.abilityName)) - end - @battle.pbHideAbilitySplash(target) - return false - end - if target.hasActiveItem?(:AIRBALLOON) - @battle.pbDisplay(_INTL("{1}'s {2} makes Ground moves miss!",target.pbThis,target.itemName)) - return false - end - if target.effects[PBEffects::MagnetRise]>0 - @battle.pbDisplay(_INTL("{1} makes Ground moves miss with Magnet Rise!",target.pbThis)) - return false - end - if target.effects[PBEffects::Telekinesis]>0 - @battle.pbDisplay(_INTL("{1} makes Ground moves miss with Telekinesis!",target.pbThis)) - return false - end - end - # Immunity to powder-based moves - if move.powderMove? - if target.pbHasType?(:GRASS) && Settings::MORE_TYPE_EFFECTS - PBDebug.log("[Target immune] #{target.pbThis} is Grass-type and immune to powder-based moves") - @battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - return false - end - if Settings::MECHANICS_GENERATION >= 6 - if target.hasActiveAbility?(:OVERCOAT) && !@battle.moldBreaker - @battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - else - @battle.pbDisplay(_INTL("It doesn't affect {1} because of its {2}.",target.pbThis(true),target.abilityName)) - end - @battle.pbHideAbilitySplash(target) - return false - end - if target.hasActiveItem?(:SAFETYGOGGLES) - PBDebug.log("[Item triggered] #{target.pbThis} has Safety Goggles and is immune to powder-based moves") - @battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - return false - end - end - end - # Substitute - if target.effects[PBEffects::Substitute]>0 && move.statusMove? && - !move.ignoresSubstitute?(user) && user.index!=target.index - PBDebug.log("[Target immune] #{target.pbThis} is protected by its Substitute") - @battle.pbDisplay(_INTL("{1} avoided the attack!",target.pbThis(true))) - return false - end - return true - end - - #============================================================================= - # Per-hit success check against the target. - # Includes semi-invulnerable move use and accuracy calculation. - #============================================================================= - def pbSuccessCheckPerHit(move,user,target,skipAccuracyCheck) - # Two-turn attacks can't fail here in the charging turn - return true if user.effects[PBEffects::TwoTurnAttack] - # Lock-On - return true if user.effects[PBEffects::LockOn]>0 && - user.effects[PBEffects::LockOnPos]==target.index - # Toxic - return true if move.pbOverrideSuccessCheckPerHit(user,target) - miss = false; hitsInvul = false - # No Guard - hitsInvul = true if user.hasActiveAbility?(:NOGUARD) || - target.hasActiveAbility?(:NOGUARD) - # Future Sight - hitsInvul = true if @battle.futureSight - # Helping Hand - hitsInvul = true if move.function=="09C" - if !hitsInvul - # Semi-invulnerable moves - if target.effects[PBEffects::TwoTurnAttack] - if target.inTwoTurnAttack?("0C9","0CC","0CE") # Fly, Bounce, Sky Drop - miss = true if !move.hitsFlyingTargets? - elsif target.inTwoTurnAttack?("0CA") # Dig - miss = true if !move.hitsDiggingTargets? - elsif target.inTwoTurnAttack?("0CB") # Dive - miss = true if !move.hitsDivingTargets? - elsif target.inTwoTurnAttack?("0CD","14D") # Shadow Force, Phantom Force - miss = true - end - end - if target.effects[PBEffects::SkyDrop]>=0 && - target.effects[PBEffects::SkyDrop]!=user.index - miss = true if !move.hitsFlyingTargets? - end - end - if !miss - # Called by another move - return true if skipAccuracyCheck - # Accuracy check - return true if move.pbAccuracyCheck(user,target) # Includes Counter/Mirror Coat - end - # Missed - PBDebug.log("[Move failed] Failed pbAccuracyCheck or target is semi-invulnerable") - return false - end - - #============================================================================= - # Message shown when a move fails the per-hit success check above. - #============================================================================= - def pbMissMessage(move,user,target) - if move.pbTarget(user).num_targets > 1 - @battle.pbDisplay(_INTL("{1} avoided the attack!",target.pbThis)) - elsif target.effects[PBEffects::TwoTurnAttack] - @battle.pbDisplay(_INTL("{1} avoided the attack!",target.pbThis)) - elsif !move.pbMissMessage(user,target) - @battle.pbDisplay(_INTL("{1}'s attack missed!",user.pbThis)) - end - end -end diff --git a/Data/Scripts/011_Battle/001_Battler/010_Battler_UseMove_TriggerEffects.rb b/Data/Scripts/011_Battle/001_Battler/010_Battler_UseMove_TriggerEffects.rb deleted file mode 100644 index e6f729bc82..0000000000 --- a/Data/Scripts/011_Battle/001_Battler/010_Battler_UseMove_TriggerEffects.rb +++ /dev/null @@ -1,186 +0,0 @@ -class PokeBattle_Battler - #============================================================================= - # Effect per hit - #============================================================================= - def pbEffectsOnMakingHit(move,user,target) - if target.damageState.calcDamage>0 && !target.damageState.substitute - # Target's ability - if target.abilityActive?(true) - oldHP = user.hp - BattleHandlers.triggerTargetAbilityOnHit(target.ability,user,target,move,@battle) - user.pbItemHPHealCheck if user.hp0 && !target.damageState.substitute && move.physicalMove? - target.tookPhysicalHit = true - target.effects[PBEffects::MoveNext] = true - target.effects[PBEffects::Quash] = 0 - end - end - # Grudge - if target.effects[PBEffects::Grudge] && target.fainted? - move.pp = 0 - @battle.pbDisplay(_INTL("{1}'s {2} lost all of its PP due to the grudge!", - user.pbThis,move.name)) - end - # Destiny Bond (recording that it should apply) - if target.effects[PBEffects::DestinyBond] && target.fainted? - if user.effects[PBEffects::DestinyBondTarget]<0 - user.effects[PBEffects::DestinyBondTarget] = target.index - end - end - end - end - - #============================================================================= - # Effects after all hits (i.e. at end of move usage) - #============================================================================= - def pbEffectsAfterMove(user,targets,move,numHits) - # Defrost - if move.damagingMove? - targets.each do |b| - next if b.damageState.unaffected || b.damageState.substitute - next if b.status != :FROZEN - # NOTE: Non-Fire-type moves that thaw the user will also thaw the - # target (in Gen 6+). - if move.calcType == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && move.thawsUser?) - b.pbCureStatus - end - end - end - # Destiny Bond - # NOTE: Although Destiny Bond is similar to Grudge, they don't apply at - # the same time (although Destiny Bond does check whether it's going - # to trigger at the same time as Grudge). - if user.effects[PBEffects::DestinyBondTarget]>=0 && !user.fainted? - dbName = @battle.battlers[user.effects[PBEffects::DestinyBondTarget]].pbThis - @battle.pbDisplay(_INTL("{1} took its attacker down with it!",dbName)) - user.pbReduceHP(user.hp,false) - user.pbItemHPHealCheck - user.pbFaint - @battle.pbJudgeCheckpoint(user) - end - # User's ability - if user.abilityActive? - BattleHandlers.triggerUserAbilityEndOfMove(user.ability,user,targets,move,@battle) - end - # Greninja - Battle Bond - if !user.fainted? && !user.effects[PBEffects::Transform] && - user.isSpecies?(:GRENINJA) && user.ability == :BATTLEBOND - if !@battle.pbAllFainted?(user.idxOpposingSide) && - !@battle.battleBond[user.index&1][user.pokemonIndex] - numFainted = 0 - targets.each { |b| numFainted += 1 if b.damageState.fainted } - if numFainted>0 && user.form==1 - @battle.battleBond[user.index&1][user.pokemonIndex] = true - @battle.pbDisplay(_INTL("{1} became fully charged due to its bond with its Trainer!",user.pbThis)) - @battle.pbShowAbilitySplash(user,true) - @battle.pbHideAbilitySplash(user) - user.pbChangeForm(2,_INTL("{1} became Ash-Greninja!",user.pbThis)) - end - end - end - # Consume user's Gem - if user.effects[PBEffects::GemConsumed] - # NOTE: The consume animation and message for Gems are shown immediately - # after the move's animation, but the item is only consumed now. - user.pbConsumeItem - end - # Pokémon switching caused by Roar, Whirlwind, Circle Throw, Dragon Tail - switchedBattlers = [] - move.pbSwitchOutTargetsEffect(user,targets,numHits,switchedBattlers) - # Target's item, user's item, target's ability (all negated by Sheer Force) - if move.addlEffect==0 || !user.hasActiveAbility?(:SHEERFORCE) - pbEffectsAfterMove2(user,targets,move,numHits,switchedBattlers) - end - # Some move effects that need to happen here, i.e. U-turn/Volt Switch - # switching, Baton Pass switching, Parting Shot switching, Relic Song's form - # changing, Fling/Natural Gift consuming item. - if !switchedBattlers.include?(user.index) - move.pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers) - end - if numHits>0 - @battle.eachBattler { |b| b.pbItemEndOfMoveCheck } - end - end - - # Everything in this method is negated by Sheer Force. - def pbEffectsAfterMove2(user,targets,move,numHits,switchedBattlers) - hpNow = user.hp # Intentionally determined now, before Shell Bell - # Target's held item (Eject Button, Red Card) - switchByItem = [] - @battle.pbPriority(true).each do |b| - next if !targets.any? { |targetB| targetB.index==b.index } - next if b.damageState.unaffected || b.damageState.calcDamage==0 || - switchedBattlers.include?(b.index) - next if !b.itemActive? - BattleHandlers.triggerTargetItemAfterMoveUse(b.item,b,user,move,switchByItem,@battle) - end - @battle.moldBreaker = false if switchByItem.include?(user.index) - @battle.pbPriority(true).each do |b| - b.pbEffectsOnSwitchIn(true) if switchByItem.include?(b.index) - end - switchByItem.each { |idxB| switchedBattlers.push(idxB) } - # User's held item (Life Orb, Shell Bell) - if !switchedBattlers.include?(user.index) && user.itemActive? - BattleHandlers.triggerUserItemAfterMoveUse(user.item,user,targets,move,numHits,@battle) - end - # Target's ability (Berserk, Color Change, Emergency Exit, Pickpocket, Wimp Out) - switchWimpOut = [] - @battle.pbPriority(true).each do |b| - next if !targets.any? { |targetB| targetB.index==b.index } - next if b.damageState.unaffected || switchedBattlers.include?(b.index) - next if !b.abilityActive? - BattleHandlers.triggerTargetAbilityAfterMoveUse(b.ability,b,user,move,switchedBattlers,@battle) - if !switchedBattlers.include?(b.index) && move.damagingMove? - if b.pbAbilitiesOnDamageTaken(b.damageState.initialHP) # Emergency Exit, Wimp Out - switchWimpOut.push(b.index) - end - end - end - @battle.moldBreaker = false if switchWimpOut.include?(user.index) - @battle.pbPriority(true).each do |b| - next if b.index==user.index - b.pbEffectsOnSwitchIn(true) if switchWimpOut.include?(b.index) - end - switchWimpOut.each { |idxB| switchedBattlers.push(idxB) } - # User's ability (Emergency Exit, Wimp Out) - if !switchedBattlers.include?(user.index) && move.damagingMove? - hpNow = user.hp if user.hp= 7) - itemName = GameData::Item.get(item).name - battle.pbCommonAnimation("EatBerry",battler) if !forced - fraction_to_heal = 8 # Gens 6 and lower - if Settings::MECHANICS_GENERATION == 7; fraction_to_heal = 2 - elsif Settings::MECHANICS_GENERATION >= 8; fraction_to_heal = 3 - end - amt = battler.pbRecoverHP(battler.totalhp / fraction_to_heal) - if amt>0 - if forced - PBDebug.log("[Item triggered] Forced consuming of #{itemName}") - battle.pbDisplay(_INTL("{1}'s HP was restored.",battler.pbThis)) - else - battle.pbDisplay(_INTL("{1} restored its health using its {2}!",battler.pbThis,itemName)) - end - end - flavor_stat = [:ATTACK, :DEFENSE, :SPEED, :SPECIAL_ATTACK, :SPECIAL_DEFENSE][flavor] - battler.nature.stat_changes.each do |change| - next if change[1] > 0 || change[0] != flavor_stat - battle.pbDisplay(confuseMsg) - battler.pbConfuse if battler.pbCanConfuseSelf?(false) - break - end - return true -end - -def pbBattleStatIncreasingBerry(battler,battle,item,forced,stat,increment=1) - return false if !forced && !battler.canConsumePinchBerry? - return false if !battler.pbCanRaiseStatStage?(stat,battler) - itemName = GameData::Item.get(item).name - if forced - PBDebug.log("[Item triggered] Forced consuming of #{itemName}") - return battler.pbRaiseStatStage(stat,increment,battler) - end - battle.pbCommonAnimation("EatBerry",battler) - return battler.pbRaiseStatStageByCause(stat,increment,battler,itemName) -end - -# For abilities that grant immunity to moves of a particular type, and raises -# one of the ability's bearer's stats instead. -def pbBattleMoveImmunityStatAbility(user,target,move,moveType,immuneType,stat,increment,battle) - return false if user.index==target.index - return false if moveType != immuneType - battle.pbShowAbilitySplash(target) - if target.pbCanRaiseStatStage?(stat,target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - target.pbRaiseStatStage(stat,increment,target) - else - target.pbRaiseStatStageByCause(stat,increment,target,target.abilityName) - end - else - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - else - battle.pbDisplay(_INTL("{1}'s {2} made {3} ineffective!", - target.pbThis,target.abilityName,move.name)) - end - end - battle.pbHideAbilitySplash(target) - return true -end - -# For abilities that grant immunity to moves of a particular type, and heals the -# ability's bearer by 1/4 of its total HP instead. -def pbBattleMoveImmunityHealAbility(user,target,move,moveType,immuneType,battle) - return false if user.index==target.index - return false if moveType != immuneType - battle.pbShowAbilitySplash(target) - if target.canHeal? && target.pbRecoverHP(target.totalhp/4)>0 - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s HP was restored.",target.pbThis)) - else - battle.pbDisplay(_INTL("{1}'s {2} restored its HP.",target.pbThis,target.abilityName)) - end - else - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - else - battle.pbDisplay(_INTL("{1}'s {2} made {3} ineffective!", - target.pbThis,target.abilityName,move.name)) - end - end - battle.pbHideAbilitySplash(target) - return true -end - -def pbBattleGem(user,type,move,mults,moveType) - # Pledge moves never consume Gems - return if move.is_a?(PokeBattle_PledgeMove) - return if moveType != type - user.effects[PBEffects::GemConsumed] = user.item_id - if Settings::MECHANICS_GENERATION >= 6 - mults[:base_damage_multiplier] *= 1.3 - else - mults[:base_damage_multiplier] *= 1.5 - end -end - -def pbBattleTypeWeakingBerry(type,moveType,target,mults) - return if moveType != type - return if Effectiveness.resistant?(target.damageState.typeMod) && moveType != :NORMAL - mults[:final_damage_multiplier] /= 2 - target.damageState.berryWeakened = true - target.battle.pbCommonAnimation("EatBerry",target) -end - -def pbBattleWeatherAbility(weather,battler,battle,ignorePrimal=false) - return if !ignorePrimal && [:HarshSun, :HeavyRain, :StrongWinds].include?(battle.field.weather) - return if battle.field.weather==weather - battle.pbShowAbilitySplash(battler) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s {2} activated!",battler.pbThis,battler.abilityName)) - end - fixedDuration = false - fixedDuration = true if Settings::FIXED_DURATION_WEATHER_FROM_ABILITY && - ![:HarshSun, :HeavyRain, :StrongWinds].include?(weather) - battle.pbStartWeather(battler,weather,fixedDuration) - # NOTE: The ability splash is hidden again in def pbStartWeather. -end diff --git a/Data/Scripts/011_Battle/001_Battler/001_PokeBattle_Battler.rb b/Data/Scripts/011_Battle/002_Battler/001_Battle_Battler.rb similarity index 68% rename from Data/Scripts/011_Battle/001_Battler/001_PokeBattle_Battler.rb rename to Data/Scripts/011_Battle/002_Battler/001_Battle_Battler.rb index 575e71af3a..09185db96e 100644 --- a/Data/Scripts/011_Battle/001_Battler/001_PokeBattle_Battler.rb +++ b/Data/Scripts/011_Battle/002_Battler/001_Battle_Battler.rb @@ -1,4 +1,4 @@ -class PokeBattle_Battler +class Battle::Battler # Fundamental to this object attr_reader :battle attr_accessor :index @@ -6,13 +6,10 @@ class PokeBattle_Battler attr_reader :pokemon attr_accessor :pokemonIndex attr_accessor :species - attr_accessor :type1 - attr_accessor :type2 + attr_accessor :types attr_accessor :ability_id attr_accessor :item_id attr_accessor :moves - attr_accessor :gender - attr_accessor :iv attr_accessor :attack attr_accessor :spatk attr_accessor :speed @@ -38,10 +35,14 @@ class PokeBattle_Battler attr_accessor :lastRoundMoveFailed # For Stomping Tantrum attr_accessor :movesUsed attr_accessor :currentMove # ID of multi-turn move currently being used - attr_accessor :tookDamage # Boolean for whether self took damage this round + attr_accessor :droppedBelowHalfHP # Used for Emergency Exit/Wimp Out + attr_accessor :statsDropped # Used for Eject Pack + attr_accessor :tookDamageThisRound # Boolean for whether self took damage this round attr_accessor :tookPhysicalHit + attr_accessor :statsRaisedThisRound # Boolean for whether self's stat(s) raised this round + attr_accessor :statsLoweredThisRound # Boolean for whether self's stat(s) lowered this round + attr_accessor :canRestoreIceFace # Whether Hail started in the round attr_accessor :damageState - attr_accessor :initialHP # Set at the start of each move's usage #============================================================================= # Complex accessors @@ -80,14 +81,14 @@ def item=(value) end def defense - return @spdef if @battle.field.effects[PBEffects::WonderRoom]>0 + return @spdef if @battle.field.effects[PBEffects::WonderRoom] > 0 return @defense end attr_writer :defense def spdef - return @defense if @battle.field.effects[PBEffects::WonderRoom]>0 + return @defense if @battle.field.effects[PBEffects::WonderRoom] > 0 return @spdef end @@ -100,14 +101,13 @@ def hp=(value) @pokemon.hp = value.to_i if @pokemon end - def fainted?; return @hp<=0; end - alias isFainted? fainted? + def fainted?; return @hp <= 0; end attr_reader :status def status=(value) @effects[PBEffects::Truant] = false if @status == :SLEEP && value != :SLEEP - @effects[PBEffects::Toxic] = 0 if value != :POISON + @effects[PBEffects::Toxic] = 0 if value != :POISON || self.statusCount == 0 @status = value @pokemon.status = value if @pokemon self.statusCount = 0 if value != :POISON && value != :SLEEP @@ -125,31 +125,30 @@ def statusCount=(value) #============================================================================= # Properties from Pokémon #============================================================================= - def happiness; return @pokemon ? @pokemon.happiness : 0; end - def nature; return @pokemon ? @pokemon.nature : 0; end - def pokerusStage; return @pokemon ? @pokemon.pokerusStage : 0; end + def happiness; return @pokemon ? @pokemon.happiness : 0; end + def affection_level; return @pokemon ? @pokemon.affection_level : 2; end + def gender; return @pokemon ? @pokemon.gender : 0; end + def nature; return @pokemon ? @pokemon.nature : nil; end + def pokerusStage; return @pokemon ? @pokemon.pokerusStage : 0; end #============================================================================= # Mega Evolution, Primal Reversion, Shadow Pokémon #============================================================================= def hasMega? return false if @effects[PBEffects::Transform] - return @pokemon && @pokemon.hasMegaForm? + return @pokemon&.hasMegaForm? end - def mega?; return @pokemon && @pokemon.mega?; end - alias isMega? mega? + def mega?; return @pokemon&.mega?; end def hasPrimal? return false if @effects[PBEffects::Transform] - return @pokemon && @pokemon.hasPrimalForm? + return @pokemon&.hasPrimalForm? end - def primal?; return @pokemon && @pokemon.primal?; end - alias isPrimal? primal? + def primal?; return @pokemon&.primal?; end def shadowPokemon?; return false; end - alias isShadow? shadowPokemon? def inHyperMode?; return false; end @@ -185,13 +184,16 @@ def displayForm def shiny? return @effects[PBEffects::Illusion].shiny? if @effects[PBEffects::Illusion] - return @pokemon && @pokemon.shiny? + return @pokemon&.shiny? + end + + def super_shiny? + return @pokemon&.super_shiny? end - alias isShiny? shiny? def owned? return false if !@battle.wildBattle? - return $Trainer.owned?(displaySpecies) + return $player.owned?(displaySpecies) end alias owned owned? @@ -205,27 +207,27 @@ def itemName return (itm) ? itm.name : "" end - def pbThis(lowerCase=false) + def pbThis(lowerCase = false) if opposes? if @battle.trainerBattle? - return lowerCase ? _INTL("the opposing {1}",name) : _INTL("The opposing {1}",name) + return lowerCase ? _INTL("the opposing {1}", name) : _INTL("The opposing {1}", name) else - return lowerCase ? _INTL("the wild {1}",name) : _INTL("The wild {1}",name) + return lowerCase ? _INTL("the wild {1}", name) : _INTL("The wild {1}", name) end elsif !pbOwnedByPlayer? - return lowerCase ? _INTL("the ally {1}",name) : _INTL("The ally {1}",name) + return lowerCase ? _INTL("the ally {1}", name) : _INTL("The ally {1}", name) end return name end - def pbTeam(lowerCase=false) + def pbTeam(lowerCase = false) if opposes? return lowerCase ? _INTL("the opposing team") : _INTL("The opposing team") end return lowerCase ? _INTL("your team") : _INTL("Your team") end - def pbOpposingTeam(lowerCase=false) + def pbOpposingTeam(lowerCase = false) if opposes? return lowerCase ? _INTL("your team") : _INTL("Your team") end @@ -237,22 +239,22 @@ def pbOpposingTeam(lowerCase=false) #============================================================================= def pbSpeed return 1 if fainted? - stageMul = [2,2,2,2,2,2, 2, 3,4,5,6,7,8] - stageDiv = [8,7,6,5,4,3, 2, 2,2,2,2,2,2] + stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8] + stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2] stage = @stages[:SPEED] + 6 - speed = @speed*stageMul[stage]/stageDiv[stage] + speed = @speed * stageMul[stage] / stageDiv[stage] speedMult = 1.0 # Ability effects that alter calculated Speed if abilityActive? - speedMult = BattleHandlers.triggerSpeedCalcAbility(self.ability,self,speedMult) + speedMult = Battle::AbilityEffects.triggerSpeedCalc(self.ability, self, speedMult) end # Item effects that alter calculated Speed if itemActive? - speedMult = BattleHandlers.triggerSpeedCalcItem(self.item,self,speedMult) + speedMult = Battle::ItemEffects.triggerSpeedCalc(self.item, self, speedMult) end # Other effects - speedMult *= 2 if pbOwnSide.effects[PBEffects::Tailwind]>0 - speedMult /= 2 if pbOwnSide.effects[PBEffects::Swamp]>0 + speedMult *= 2 if pbOwnSide.effects[PBEffects::Tailwind] > 0 + speedMult /= 2 if pbOwnSide.effects[PBEffects::Swamp] > 0 # Paralysis if status == :PARALYSIS && !hasActiveAbility?(:QUICKFEET) speedMult /= (Settings::MECHANICS_GENERATION >= 7) ? 2 : 4 @@ -263,20 +265,20 @@ def pbSpeed speedMult *= 1.1 end # Calculation - return [(speed*speedMult).round,1].max + return [(speed * speedMult).round, 1].max end def pbWeight ret = (@pokemon) ? @pokemon.weight : 500 ret += @effects[PBEffects::WeightChange] - ret = 1 if ret<1 + ret = 1 if ret < 1 if abilityActive? && !@battle.moldBreaker - ret = BattleHandlers.triggerWeightCalcAbility(self.ability,self,ret) + ret = Battle::AbilityEffects.triggerWeightCalc(self.ability, self, ret) end if itemActive? - ret = BattleHandlers.triggerWeightCalcItem(self.item,self,ret) + ret = Battle::ItemEffects.triggerWeightCalc(self.item, self, ret) end - return [ret,1].max + return [ret, 1].max end #============================================================================= @@ -293,15 +295,13 @@ def plainStats end def isSpecies?(species) - return @pokemon && @pokemon.isSpecies?(species) + return @pokemon&.isSpecies?(species) end # Returns the active types of this Pokémon. The array should not include the - # same type more than once, and should not include any invalid type numbers - # (e.g. -1). - def pbTypes(withType3=false) - ret = [@type1] - ret.push(@type2) if @type2!=@type1 + # same type more than once, and should not include any invalid types. + def pbTypes(withType3 = false) + ret = @types.uniq # Burn Up erases the Fire-type. ret.delete(:FIRE) if @effects[PBEffects::BurnUp] # Roost erases the Flying-type. If there are no types left, adds the Normal- @@ -311,8 +311,8 @@ def pbTypes(withType3=false) ret.push(:NORMAL) if ret.length == 0 end # Add the third type specially. - if withType3 && @effects[PBEffects::Type3] - ret.push(@effects[PBEffects::Type3]) if !ret.include?(@effects[PBEffects::Type3]) + if withType3 && @effects[PBEffects::Type3] && !ret.include?(@effects[PBEffects::Type3]) + ret.push(@effects[PBEffects::Type3]) end return ret end @@ -333,16 +333,18 @@ def pbHasOtherType?(type) # NOTE: Do not create any held item which affects whether a Pokémon's ability # is active. The ability Klutz affects whether a Pokémon's item is # active, and the code for the two combined would cause an infinite loop - # (regardless of whether any Pokémon actualy has either the ability or + # (regardless of whether any Pokémon actually has either the ability or # the item - the code existing is enough to cause the loop). - def abilityActive?(ignore_fainted = false) + def abilityActive?(ignore_fainted = false, check_ability = nil) return false if fainted? && !ignore_fainted return false if @effects[PBEffects::GastroAcid] + return false if check_ability != :NEUTRALIZINGGAS && self.ability != :NEUTRALIZINGGAS && + @battle.pbCheckGlobalAbility(:NEUTRALIZINGGAS) return true end def hasActiveAbility?(check_ability, ignore_fainted = false) - return false if !abilityActive?(ignore_fainted) + return false if !abilityActive?(ignore_fainted, check_ability) return check_ability.include?(@ability_id) if check_ability.is_a?(Array) return self.ability == check_ability end @@ -360,6 +362,8 @@ def unstoppableAbility?(abil = nil) :DISGUISE, # :FLOWERGIFT, # This can be stopped # :FORECAST, # This can be stopped + :GULPMISSILE, + :ICEFACE, :MULTITYPE, :POWERCONSTRUCT, :SCHOOLING, @@ -367,6 +371,8 @@ def unstoppableAbility?(abil = nil) :STANCECHANGE, :ZENMODE, # Abilities intended to be inherent properties of a certain species + :ASONECHILLINGNEIGH, + :ASONEGRIMNEIGH, :COMATOSE, :RKSSYSTEM ] @@ -384,6 +390,8 @@ def ungainableAbility?(abil = nil) :DISGUISE, :FLOWERGIFT, :FORECAST, + :GULPMISSILE, + :ICEFACE, :MULTITYPE, :POWERCONSTRUCT, :SCHOOLING, @@ -394,17 +402,22 @@ def ungainableAbility?(abil = nil) :ILLUSION, :IMPOSTER, # Abilities intended to be inherent properties of a certain species + :ASONECHILLINGNEIGH, + :ASONEGRIMNEIGH, :COMATOSE, - :RKSSYSTEM + :RKSSYSTEM, + # Abilities that can't be negated + :NEUTRALIZINGGAS ] return ability_blacklist.include?(abil.id) end - def itemActive?(ignoreFainted=false) + def itemActive?(ignoreFainted = false) return false if fainted? && !ignoreFainted - return false if @effects[PBEffects::Embargo]>0 - return false if @battle.field.effects[PBEffects::MagicRoom]>0 - return false if hasActiveAbility?(:KLUTZ,ignoreFainted) + return false if @effects[PBEffects::Embargo] > 0 + return false if @battle.field.effects[PBEffects::MagicRoom] > 0 + return false if @battle.corrosiveGas[@index % 2][@pokemonIndex] + return false if hasActiveAbility?(:KLUTZ, ignoreFainted) return true end @@ -418,19 +431,20 @@ def hasActiveItem?(check_item, ignore_fainted = false) # Returns whether the specified item will be unlosable for this Pokémon. def unlosableItem?(check_item) return false if !check_item - return true if GameData::Item.get(check_item).is_mail? + item_data = GameData::Item.get(check_item) + return true if item_data.is_mail? return false if @effects[PBEffects::Transform] # Items that change a Pokémon's form if mega? # Check if item was needed for this Mega Evolution - return true if @pokemon.species_data.mega_stone == check_item + return true if @pokemon.species_data.mega_stone == item_data.id else # Check if item could cause a Mega Evolution GameData::Species.each do |data| next if data.species != @species || data.unmega_form != @form - return true if data.mega_stone == check_item + return true if data.mega_stone == item_data.id end end # Other unlosable items - return GameData::Item.get(check_item).unlosable?(@species, self.ability) + return item_data.unlosable?(@species, self.ability) end def eachMove @@ -462,6 +476,12 @@ def pbHasMoveFunction?(*arg) return false end + def pbGetMoveWithID(move_id) + return nil if !move_id + eachMove { |m| return m if m.id == move_id } + return nil + end + def hasMoldBreaker? return hasActiveAbility?([:MOLDBREAKER, :TERAVOLT, :TURBOBLAZE]) end @@ -489,15 +509,15 @@ def affectedByTerrain? return true end - def takesIndirectDamage?(showMsg=false) + def takesIndirectDamage?(showMsg = false) return false if fainted? if hasActiveAbility?(:MAGICGUARD) if showMsg @battle.pbShowAbilitySplash(self) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1} is unaffected!", pbThis)) else - @battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",pbThis,abilityName)) + @battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!", pbThis, abilityName)) end @battle.pbHideAbilitySplash(self) end @@ -509,8 +529,9 @@ def takesIndirectDamage?(showMsg=false) def takesSandstormDamage? return false if !takesIndirectDamage? return false if pbHasType?(:GROUND) || pbHasType?(:ROCK) || pbHasType?(:STEEL) - return false if inTwoTurnAttack?("0CA","0CB") # Dig, Dive - return false if hasActiveAbility?([:OVERCOAT,:SANDFORCE,:SANDRUSH,:SANDVEIL]) + return false if inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground", + "TwoTurnAttackInvulnerableUnderwater") + return false if hasActiveAbility?([:OVERCOAT, :SANDFORCE, :SANDRUSH, :SANDVEIL]) return false if hasActiveItem?(:SAFETYGOGGLES) return true end @@ -518,8 +539,9 @@ def takesSandstormDamage? def takesHailDamage? return false if !takesIndirectDamage? return false if pbHasType?(:ICE) - return false if inTwoTurnAttack?("0CA","0CB") # Dig, Dive - return false if hasActiveAbility?([:OVERCOAT,:ICEBODY,:SNOWCLOAK]) + return false if inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground", + "TwoTurnAttackInvulnerableUnderwater") + return false if hasActiveAbility?([:OVERCOAT, :ICEBODY, :SNOWCLOAK]) return false if hasActiveItem?(:SAFETYGOGGLES) return true end @@ -530,20 +552,26 @@ def takesShadowSkyDamage? return true end - def affectedByPowder?(showMsg=false) + def effectiveWeather + ret = @battle.pbWeather + ret = :None if [:Sun, :Rain, :HarshSun, :HeavyRain].include?(ret) && hasActiveItem?(:UTILITYUMBRELLA) + return ret + end + + def affectedByPowder?(showMsg = false) return false if fainted? if pbHasType?(:GRASS) && Settings::MORE_TYPE_EFFECTS - @battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) if showMsg + @battle.pbDisplay(_INTL("{1} is unaffected!", pbThis)) if showMsg return false end if Settings::MECHANICS_GENERATION >= 6 if hasActiveAbility?(:OVERCOAT) && !@battle.moldBreaker if showMsg @battle.pbShowAbilitySplash(self) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1} is unaffected!", pbThis)) else - @battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",pbThis,abilityName)) + @battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!", pbThis, abilityName)) end @battle.pbHideAbilitySplash(self) end @@ -551,7 +579,7 @@ def affectedByPowder?(showMsg=false) end if hasActiveItem?(:SAFETYGOGGLES) if showMsg - @battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",pbThis,itemName)) + @battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!", pbThis, itemName)) end return false end @@ -560,50 +588,67 @@ def affectedByPowder?(showMsg=false) end def canHeal? - return false if fainted? || @hp>=@totalhp - return false if @effects[PBEffects::HealBlock]>0 + return false if fainted? || @hp >= @totalhp + return false if @effects[PBEffects::HealBlock] > 0 return true end - def affectedByContactEffect?(showMsg=false) + def affectedByContactEffect?(showMsg = false) return false if fainted? if hasActiveItem?(:PROTECTIVEPADS) - @battle.pbDisplay(_INTL("{1} protected itself with the {2}!",pbThis,itemName)) if showMsg + @battle.pbDisplay(_INTL("{1} protected itself with the {2}!", pbThis, itemName)) if showMsg return false end return true end + def trappedInBattle? + return true if @effects[PBEffects::Trapping] > 0 + return true if @effects[PBEffects::MeanLook] >= 0 + return true if @effects[PBEffects::JawLock] >= 0 + return true if @battle.allBattlers.any? { |b| b.effects[PBEffects::JawLock] == @index } + return true if @effects[PBEffects::Octolock] >= 0 + return true if @effects[PBEffects::Ingrain] + return true if @effects[PBEffects::NoRetreat] + return true if @battle.field.effects[PBEffects::FairyLock] > 0 + return false + end + def movedThisRound? - return @lastRoundMoved && @lastRoundMoved==@battle.turnCount + return @lastRoundMoved && @lastRoundMoved == @battle.turnCount end def usingMultiTurnAttack? return true if @effects[PBEffects::TwoTurnAttack] - return true if @effects[PBEffects::HyperBeam]>0 - return true if @effects[PBEffects::Rollout]>0 - return true if @effects[PBEffects::Outrage]>0 - return true if @effects[PBEffects::Uproar]>0 - return true if @effects[PBEffects::Bide]>0 + return true if @effects[PBEffects::HyperBeam] > 0 + return true if @effects[PBEffects::Rollout] > 0 + return true if @effects[PBEffects::Outrage] > 0 + return true if @effects[PBEffects::Uproar] > 0 + return true if @effects[PBEffects::Bide] > 0 return false end def inTwoTurnAttack?(*arg) return false if !@effects[PBEffects::TwoTurnAttack] ttaFunction = GameData::Move.get(@effects[PBEffects::TwoTurnAttack]).function_code - arg.each { |a| return true if a==ttaFunction } + arg.each { |a| return true if a == ttaFunction } return false end def semiInvulnerable? - return inTwoTurnAttack?("0C9","0CA","0CB","0CC","0CD","0CE","14D") + return inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky", + "TwoTurnAttackInvulnerableUnderground", + "TwoTurnAttackInvulnerableUnderwater", + "TwoTurnAttackInvulnerableInSkyParalyzeTarget", + "TwoTurnAttackInvulnerableRemoveProtections", + "TwoTurnAttackInvulnerableInSkyTargetCannotAct") end def pbEncoredMoveIndex - return -1 if @effects[PBEffects::Encore]==0 || !@effects[PBEffects::EncoreMove] + return -1 if @effects[PBEffects::Encore] == 0 || !@effects[PBEffects::EncoreMove] ret = -1 - eachMoveWithIndex do |m,i| - next if m.id!=@effects[PBEffects::EncoreMove] + eachMoveWithIndex do |m, i| + next if m.id != @effects[PBEffects::EncoreMove] ret = i break end @@ -611,46 +656,46 @@ def pbEncoredMoveIndex end def initialItem - return @battle.initialItems[@index&1][@pokemonIndex] + return @battle.initialItems[@index & 1][@pokemonIndex] end def setInitialItem(value) item_data = GameData::Item.try_get(value) new_item = (item_data) ? item_data.id : nil - @battle.initialItems[@index&1][@pokemonIndex] = new_item + @battle.initialItems[@index & 1][@pokemonIndex] = new_item end def recycleItem - return @battle.recycleItems[@index&1][@pokemonIndex] + return @battle.recycleItems[@index & 1][@pokemonIndex] end def setRecycleItem(value) item_data = GameData::Item.try_get(value) new_item = (item_data) ? item_data.id : nil - @battle.recycleItems[@index&1][@pokemonIndex] = new_item + @battle.recycleItems[@index & 1][@pokemonIndex] = new_item end def belched? - return @battle.belch[@index&1][@pokemonIndex] + return @battle.belch[@index & 1][@pokemonIndex] end def setBelched - @battle.belch[@index&1][@pokemonIndex] = true + @battle.belch[@index & 1][@pokemonIndex] = true end #============================================================================= # Methods relating to this battler's position on the battlefield #============================================================================= # Returns whether the given position belongs to the opposing Pokémon's side. - def opposes?(i=0) + def opposes?(i = 0) i = i.index if i.respond_to?("index") - return (@index&1)!=(i&1) + return (@index & 1) != (i & 1) end # Returns whether the given position/battler is near to self. def near?(i) i = i.index if i.respond_to?("index") - return @battle.nearBattlers?(@index,i) + return @battle.nearBattlers?(@index, i) end # Returns whether self is owned by the player. @@ -658,16 +703,20 @@ def pbOwnedByPlayer? return @battle.pbOwnedByPlayer?(@index) end + def wild? + return @battle.wildBattle? && opposes? + end + # Returns 0 if self is on the player's side, or 1 if self is on the opposing # side. def idxOwnSide - return @index&1 + return @index & 1 end # Returns 1 if self is on the player's side, or 0 if self is on the opposing # side. def idxOpposingSide - return (@index&1)^1 + return (@index & 1) ^ 1 end # Returns the data structure for this battler's side. @@ -681,20 +730,32 @@ def pbOpposingSide end # Yields each unfainted ally Pokémon. + # Unused def eachAlly @battle.battlers.each do |b| - yield b if b && !b.fainted? && !b.opposes?(@index) && b.index!=@index + yield b if b && !b.fainted? && !b.opposes?(@index) && b.index != @index end end + # Returns an array containing all unfainted ally Pokémon. + def allAllies + return @battle.allSameSideBattlers(@index).reject { |b| b.index == @index } + end + # Yields each unfainted opposing Pokémon. + # Unused def eachOpposing @battle.battlers.each { |b| yield b if b && !b.fainted? && b.opposes?(@index) } end + # Returns an array containing all unfainted opposing Pokémon. + def allOpposing + return @battle.allOtherSideBattlers(@index) + end + # Returns the battler that is most directly opposite to self. unfaintedOnly is # whether it should prefer to return a non-fainted battler. - def pbDirectOpposing(unfaintedOnly=false) + def pbDirectOpposing(unfaintedOnly = false) @battle.pbGetOpposingIndicesInOrder(@index).each do |i| next if !@battle.battlers[i] break if unfaintedOnly && @battle.battlers[i].fainted? @@ -705,6 +766,6 @@ def pbDirectOpposing(unfaintedOnly=false) @battle.pbGetOpposingIndicesInOrder(@index).each do |i| return @battle.battlers[i] if @battle.battlers[i] end - return @battle.battlers[(@index^1)] + return @battle.battlers[(@index ^ 1)] end end diff --git a/Data/Scripts/011_Battle/001_Battler/002_Battler_Initialize.rb b/Data/Scripts/011_Battle/002_Battler/002_Battler_Initialize.rb similarity index 82% rename from Data/Scripts/011_Battle/001_Battler/002_Battler_Initialize.rb rename to Data/Scripts/011_Battle/002_Battler/002_Battler_Initialize.rb index f64c1962c8..53ae3b3279 100644 --- a/Data/Scripts/011_Battle/001_Battler/002_Battler_Initialize.rb +++ b/Data/Scripts/011_Battle/002_Battler/002_Battler_Initialize.rb @@ -1,15 +1,15 @@ -class PokeBattle_Battler +class Battle::Battler #============================================================================= # Creating a battler #============================================================================= - def initialize(btl,idxBattler) + def initialize(btl, idxBattler) @battle = btl @index = idxBattler @captured = false @dummy = false @stages = {} @effects = [] - @damageState = PokeBattle_DamageState.new + @damageState = Battle::DamageState.new pbInitBlank pbInitEffects(false) end @@ -20,10 +20,9 @@ def pbInitBlank @form = 0 @level = 0 @hp = @totalhp = 0 - @type1 = @type2 = nil + @types = [] @ability_id = nil @item_id = nil - @gender = 0 @attack = @defense = @spatk = @spdef = @speed = 0 @status = :NONE @statusCount = 0 @@ -31,12 +30,10 @@ def pbInitBlank @pokemonIndex = -1 @participants = [] @moves = [] - @iv = {} - GameData::Stat.each_main { |s| @iv[s.id] = 0 } end # Used by Future Sight only, when Future Sight's user is no longer in battle. - def pbInitDummyPokemon(pkmn,idxParty) + def pbInitDummyPokemon(pkmn, idxParty) raise _INTL("An egg can't be an active Pokémon.") if pkmn.egg? @name = pkmn.name @species = pkmn.species @@ -44,10 +41,8 @@ def pbInitDummyPokemon(pkmn,idxParty) @level = pkmn.level @hp = pkmn.hp @totalhp = pkmn.totalhp - @type1 = pkmn.type1 - @type2 = pkmn.type2 + @types = pkmn.types # ability and item intentionally not copied across here - @gender = pkmn.gender @attack = pkmn.attack @defense = pkmn.defense @spatk = pkmn.spatk @@ -59,18 +54,16 @@ def pbInitDummyPokemon(pkmn,idxParty) @pokemonIndex = idxParty @participants = [] # moves intentionally not copied across here - @iv = {} - GameData::Stat.each_main { |s| @iv[s.id] = pkmn.iv[s.id] } @dummy = true end - def pbInitialize(pkmn,idxParty,batonPass=false) - pbInitPokemon(pkmn,idxParty) + def pbInitialize(pkmn, idxParty, batonPass = false) + pbInitPokemon(pkmn, idxParty) pbInitEffects(batonPass) @damageState.reset end - def pbInitPokemon(pkmn,idxParty) + def pbInitPokemon(pkmn, idxParty) raise _INTL("An egg can't be an active Pokémon.") if pkmn.egg? @name = pkmn.name @species = pkmn.species @@ -78,11 +71,9 @@ def pbInitPokemon(pkmn,idxParty) @level = pkmn.level @hp = pkmn.hp @totalhp = pkmn.totalhp - @type1 = pkmn.type1 - @type2 = pkmn.type2 + @types = pkmn.types @ability_id = pkmn.ability_id @item_id = pkmn.item_id - @gender = pkmn.gender @attack = pkmn.attack @defense = pkmn.defense @spatk = pkmn.spatk @@ -94,21 +85,19 @@ def pbInitPokemon(pkmn,idxParty) @pokemonIndex = idxParty @participants = [] # Participants earn Exp. if this battler is defeated @moves = [] - pkmn.moves.each_with_index do |m,i| - @moves[i] = PokeBattle_Move.from_pokemon_move(@battle,m) + pkmn.moves.each_with_index do |m, i| + @moves[i] = Battle::Move.from_pokemon_move(@battle, m) end - @iv = {} - GameData::Stat.each_main { |s| @iv[s.id] = pkmn.iv[s.id] } end def pbInitEffects(batonPass) if batonPass # These effects are passed on if Baton Pass is used, but they need to be # reapplied - @effects[PBEffects::LaserFocus] = (@effects[PBEffects::LaserFocus]>0) ? 2 : 0 - @effects[PBEffects::LockOn] = (@effects[PBEffects::LockOn]>0) ? 2 : 0 + @effects[PBEffects::LaserFocus] = (@effects[PBEffects::LaserFocus] > 0) ? 2 : 0 + @effects[PBEffects::LockOn] = (@effects[PBEffects::LockOn] > 0) ? 2 : 0 if @effects[PBEffects::PowerTrick] - @attack,@defense = @defense,@attack + @attack, @defense = @defense, @attack end # These effects are passed on if Baton Pass is used, but they need to be # cancelled in certain circumstances anyway @@ -116,13 +105,7 @@ def pbInitEffects(batonPass) @effects[PBEffects::GastroAcid] = false if unstoppableAbility? else # These effects are passed on if Baton Pass is used - @stages[:ATTACK] = 0 - @stages[:DEFENSE] = 0 - @stages[:SPEED] = 0 - @stages[:SPECIAL_ATTACK] = 0 - @stages[:SPECIAL_DEFENSE] = 0 - @stages[:ACCURACY] = 0 - @stages[:EVASION] = 0 + GameData::Stat.each_battle { |stat| @stages[stat.id] = 0 } @effects[PBEffects::AquaRing] = false @effects[PBEffects::Confusion] = 0 @effects[PBEffects::Curse] = false @@ -142,14 +125,18 @@ def pbInitEffects(batonPass) @effects[PBEffects::Substitute] = 0 @effects[PBEffects::Telekinesis] = 0 end - @fainted = (@hp==0) - @initialHP = 0 + @fainted = (@hp == 0) @lastAttacker = [] @lastFoeAttacker = [] @lastHPLost = 0 @lastHPLostFromFoe = 0 - @tookDamage = false + @droppedBelowHalfHP = false + @statsDropped = false + @tookDamageThisRound = false @tookPhysicalHit = false + @statsRaisedThisRound = false + @statsLoweredThisRound = false + @canRestoreIceFace = false @lastMoveUsed = nil @lastMoveUsedType = nil @lastRegularMoveUsed = nil @@ -160,8 +147,8 @@ def pbInitEffects(batonPass) @movesUsed = [] @turnCount = 0 @effects[PBEffects::Attract] = -1 - @battle.eachBattler do |b| # Other battlers no longer attracted to self - b.effects[PBEffects::Attract] = -1 if b.effects[PBEffects::Attract]==@index + @battle.allBattlers.each do |b| # Other battlers no longer attracted to self + b.effects[PBEffects::Attract] = -1 if b.effects[PBEffects::Attract] == @index end @effects[PBEffects::BanefulBunker] = false @effects[PBEffects::BeakBlast] = false @@ -184,7 +171,7 @@ def pbInitEffects(batonPass) @effects[PBEffects::Encore] = 0 @effects[PBEffects::EncoreMove] = nil @effects[PBEffects::Endure] = false - @effects[PBEffects::FirstPledge] = 0 + @effects[PBEffects::FirstPledge] = nil @effects[PBEffects::FlashFire] = false @effects[PBEffects::Flinch] = false @effects[PBEffects::FocusPunch] = false @@ -205,18 +192,22 @@ def pbInitEffects(batonPass) @effects[PBEffects::Imprison] = false @effects[PBEffects::Instruct] = false @effects[PBEffects::Instructed] = false + @effects[PBEffects::JawLock] = -1 + @battle.allBattlers.each do |b| # Other battlers no longer blocked by self + b.effects[PBEffects::JawLock] = -1 if b.effects[PBEffects::JawLock] == @index + end @effects[PBEffects::KingsShield] = false - @battle.eachBattler do |b| # Other battlers lose their lock-on against self - next if b.effects[PBEffects::LockOn]==0 - next if b.effects[PBEffects::LockOnPos]!=@index + @battle.allBattlers.each do |b| # Other battlers lose their lock-on against self + next if b.effects[PBEffects::LockOn] == 0 + next if b.effects[PBEffects::LockOnPos] != @index b.effects[PBEffects::LockOn] = 0 b.effects[PBEffects::LockOnPos] = -1 end @effects[PBEffects::MagicBounce] = false @effects[PBEffects::MagicCoat] = false @effects[PBEffects::MeanLook] = -1 - @battle.eachBattler do |b| # Other battlers no longer blocked by self - b.effects[PBEffects::MeanLook] = -1 if b.effects[PBEffects::MeanLook]==@index + @battle.allBattlers.each do |b| # Other battlers no longer blocked by self + b.effects[PBEffects::MeanLook] = -1 if b.effects[PBEffects::MeanLook] == @index end @effects[PBEffects::MeFirst] = false @effects[PBEffects::Metronome] = 0 @@ -228,6 +219,12 @@ def pbInitEffects(batonPass) @effects[PBEffects::MoveNext] = false @effects[PBEffects::MudSport] = false @effects[PBEffects::Nightmare] = false + @effects[PBEffects::NoRetreat] = false + @effects[PBEffects::Obstruct] = false + @effects[PBEffects::Octolock] = -1 + @battle.allBattlers.each do |b| # Other battlers no longer locked by self + b.effects[PBEffects::Octolock] = -1 if b.effects[PBEffects::Octolock] == @index + end @effects[PBEffects::Outrage] = 0 @effects[PBEffects::ParentalBond] = 0 @effects[PBEffects::PickupItem] = nil @@ -246,8 +243,8 @@ def pbInitEffects(batonPass) @effects[PBEffects::Rollout] = 0 @effects[PBEffects::Roost] = false @effects[PBEffects::SkyDrop] = -1 - @battle.eachBattler do |b| # Other battlers no longer Sky Dropped by self - b.effects[PBEffects::SkyDrop] = -1 if b.effects[PBEffects::SkyDrop]==@index + @battle.allBattlers.each do |b| # Other battlers no longer Sky Dropped by self + b.effects[PBEffects::SkyDrop] = -1 if b.effects[PBEffects::SkyDrop] == @index end @effects[PBEffects::SlowStart] = 0 @effects[PBEffects::SmackDown] = false @@ -257,17 +254,18 @@ def pbInitEffects(batonPass) @effects[PBEffects::Stockpile] = 0 @effects[PBEffects::StockpileDef] = 0 @effects[PBEffects::StockpileSpDef] = 0 + @effects[PBEffects::TarShot] = false @effects[PBEffects::Taunt] = 0 @effects[PBEffects::ThroatChop] = 0 @effects[PBEffects::Torment] = false @effects[PBEffects::Toxic] = 0 @effects[PBEffects::Transform] = false - @effects[PBEffects::TransformSpecies] = 0 + @effects[PBEffects::TransformSpecies] = nil @effects[PBEffects::Trapping] = 0 @effects[PBEffects::TrappingMove] = nil @effects[PBEffects::TrappingUser] = -1 - @battle.eachBattler do |b| # Other battlers no longer trapped by self - next if b.effects[PBEffects::TrappingUser]!=@index + @battle.allBattlers.each do |b| # Other battlers no longer trapped by self + next if b.effects[PBEffects::TrappingUser] != @index b.effects[PBEffects::Trapping] = 0 b.effects[PBEffects::TrappingUser] = -1 end @@ -284,7 +282,7 @@ def pbInitEffects(batonPass) #============================================================================= # Refreshing a battler's properties #============================================================================= - def pbUpdate(fullChange=false) + def pbUpdate(fullChange = false) return if !@pokemon @pokemon.calc_stats @level = @pokemon.level @@ -297,8 +295,7 @@ def pbUpdate(fullChange=false) @spdef = @pokemon.spdef @speed = @pokemon.speed if fullChange - @type1 = @pokemon.type1 - @type2 = @pokemon.type2 + @types = @pokemon.types @ability_id = @pokemon.ability_id end end @@ -321,7 +318,7 @@ def pbReset # Update which Pokémon will gain Exp if this battler is defeated. def pbUpdateParticipants return if fainted? || !@battle.opposes?(@index) - eachOpposing do |b| + allOpposing.each do |b| @participants.push(b.pokemonIndex) if !@participants.include?(b.pokemonIndex) end end diff --git a/Data/Scripts/011_Battle/001_Battler/003_Battler_ChangeSelf.rb b/Data/Scripts/011_Battle/002_Battler/003_Battler_ChangeSelf.rb similarity index 52% rename from Data/Scripts/011_Battle/001_Battler/003_Battler_ChangeSelf.rb rename to Data/Scripts/011_Battle/002_Battler/003_Battler_ChangeSelf.rb index 6f61d21dfd..cfaaf60de0 100644 --- a/Data/Scripts/011_Battle/001_Battler/003_Battler_ChangeSelf.rb +++ b/Data/Scripts/011_Battle/002_Battler/003_Battler_ChangeSelf.rb @@ -1,79 +1,96 @@ -class PokeBattle_Battler +class Battle::Battler #============================================================================= # Change HP #============================================================================= - def pbReduceHP(amt,anim=true,registerDamage=true,anyAnim=true) + def pbReduceHP(amt, anim = true, registerDamage = true, anyAnim = true) amt = amt.round - amt = @hp if amt>@hp - amt = 1 if amt<1 && !fainted? + amt = @hp if amt > @hp + amt = 1 if amt < 1 && !fainted? oldHP = @hp self.hp -= amt - PBDebug.log("[HP change] #{pbThis} lost #{amt} HP (#{oldHP}=>#{@hp})") if amt>0 - raise _INTL("HP less than 0") if @hp<0 - raise _INTL("HP greater than total HP") if @hp>@totalhp - @battle.scene.pbHPChanged(self,oldHP,anim) if anyAnim && amt>0 - @tookDamage = true if amt>0 && registerDamage + PBDebug.log("[HP change] #{pbThis} lost #{amt} HP (#{oldHP}=>#{@hp})") if amt > 0 + raise _INTL("HP less than 0") if @hp < 0 + raise _INTL("HP greater than total HP") if @hp > @totalhp + @battle.scene.pbHPChanged(self, oldHP, anim) if anyAnim && amt > 0 + if amt > 0 && registerDamage + @droppedBelowHalfHP = true if @hp < @totalhp / 2 && @hp + amt >= @totalhp / 2 + @tookDamageThisRound = true + end return amt end - def pbRecoverHP(amt,anim=true,anyAnim=true) + def pbRecoverHP(amt, anim = true, anyAnim = true) amt = amt.round - amt = @totalhp-@hp if amt>@totalhp-@hp - amt = 1 if amt<1 && @hp<@totalhp + amt = @totalhp - @hp if amt > @totalhp - @hp + amt = 1 if amt < 1 && @hp < @totalhp oldHP = @hp self.hp += amt - PBDebug.log("[HP change] #{pbThis} gained #{amt} HP (#{oldHP}=>#{@hp})") if amt>0 - raise _INTL("HP less than 0") if @hp<0 - raise _INTL("HP greater than total HP") if @hp>@totalhp - @battle.scene.pbHPChanged(self,oldHP,anim) if anyAnim && amt>0 + PBDebug.log("[HP change] #{pbThis} gained #{amt} HP (#{oldHP}=>#{@hp})") if amt > 0 + raise _INTL("HP less than 0") if @hp < 0 + raise _INTL("HP greater than total HP") if @hp > @totalhp + @battle.scene.pbHPChanged(self, oldHP, anim) if anyAnim && amt > 0 + @droppedBelowHalfHP = false if @hp >= @totalhp / 2 return amt end - def pbRecoverHPFromDrain(amt,target,msg=nil) + def pbRecoverHPFromDrain(amt, target, msg = nil) if target.hasActiveAbility?(:LIQUIDOOZE) @battle.pbShowAbilitySplash(target) pbReduceHP(amt) - @battle.pbDisplay(_INTL("{1} sucked up the liquid ooze!",pbThis)) + @battle.pbDisplay(_INTL("{1} sucked up the liquid ooze!", pbThis)) @battle.pbHideAbilitySplash(target) pbItemHPHealCheck else - msg = _INTL("{1} had its energy drained!",target.pbThis) if nil_or_empty?(msg) + msg = _INTL("{1} had its energy drained!", target.pbThis) if nil_or_empty?(msg) @battle.pbDisplay(msg) if canHeal? - amt = (amt*1.3).floor if hasActiveItem?(:BIGROOT) + amt = (amt * 1.3).floor if hasActiveItem?(:BIGROOT) pbRecoverHP(amt) end end end - def pbFaint(showMessage=true) + def pbTakeEffectDamage(amt, show_anim = true) + @droppedBelowHalfHP = false + hp_lost = pbReduceHP(amt, show_anim) + yield hp_lost if block_given? # Show message + pbItemHPHealCheck + pbAbilitiesOnDamageTaken + pbFaint if fainted? + @droppedBelowHalfHP = false + end + + def pbFaint(showMessage = true) if !fainted? PBDebug.log("!!!***Can't faint with HP greater than 0") return end return if @fainted # Has already fainted properly - @battle.pbDisplayBrief(_INTL("{1} fainted!",pbThis)) if showMessage + @battle.pbDisplayBrief(_INTL("{1} fainted!", pbThis)) if showMessage PBDebug.log("[Pokémon fainted] #{pbThis} (#{@index})") if !showMessage @battle.scene.pbFaintBattler(self) + @battle.pbSetDefeated(self) if opposes? pbInitEffects(false) # Reset status self.status = :NONE self.statusCount = 0 # Lose happiness if @pokemon && @battle.internalBattle - badLoss = false - @battle.eachOtherSideBattler(@index) do |b| - badLoss = true if b.level>=self.level+30 - end + badLoss = @battle.allOtherSideBattlers(@index).any? { |b| b.level >= self.level + 30 } @pokemon.changeHappiness((badLoss) ? "faintbad" : "faint") end # Reset form - @battle.peer.pbOnLeavingBattle(@battle,@pokemon,@battle.usedInBattle[idxOwnSide][@index/2]) + @battle.peer.pbOnLeavingBattle(@battle, @pokemon, @battle.usedInBattle[idxOwnSide][@index / 2]) @pokemon.makeUnmega if mega? @pokemon.makeUnprimal if primal? # Do other things @battle.pbClearChoice(@index) # Reset choice pbOwnSide.effects[PBEffects::LastRoundFainted] = @battle.turnCount + if $game_temp.party_direct_damage_taken && + $game_temp.party_direct_damage_taken[@pokemonIndex] && + pbOwnedByPlayer? + $game_temp.party_direct_damage_taken[@pokemonIndex] = 0 + end # Check other battlers' abilities that trigger upon a battler fainting pbAbilitiesOnFainting # Check for end of primordial weather @@ -83,64 +100,69 @@ def pbFaint(showMessage=true) #============================================================================= # Move PP #============================================================================= - def pbSetPP(move,pp) + def pbSetPP(move, pp) move.pp = pp # No need to care about @effects[PBEffects::Mimic], since Mimic can't copy # Mimic - if move.realMove && move.id==move.realMove.id && !@effects[PBEffects::Transform] + if move.realMove && move.id == move.realMove.id && !@effects[PBEffects::Transform] move.realMove.pp = pp end end def pbReducePP(move) return true if usingMultiTurnAttack? - return true if move.pp<0 # Don't reduce PP for special calls of moves - return true if move.total_pp<=0 # Infinite PP, can always be used - return false if move.pp==0 # Ran out of PP, couldn't reduce - pbSetPP(move,move.pp-1) if move.pp>0 + return true if move.pp < 0 # Don't reduce PP for special calls of moves + return true if move.total_pp <= 0 # Infinite PP, can always be used + return false if move.pp == 0 # Ran out of PP, couldn't reduce + pbSetPP(move, move.pp - 1) if move.pp > 0 return true end def pbReducePPOther(move) - pbSetPP(move,move.pp-1) if move.pp>0 + pbSetPP(move, move.pp - 1) if move.pp > 0 end #============================================================================= # Change type #============================================================================= def pbChangeTypes(newType) - if newType.is_a?(PokeBattle_Battler) + if newType.is_a?(Battle::Battler) newTypes = newType.pbTypes newTypes.push(:NORMAL) if newTypes.length == 0 newType3 = newType.effects[PBEffects::Type3] newType3 = nil if newTypes.include?(newType3) - @type1 = newTypes[0] - @type2 = (newTypes.length == 1) ? newTypes[0] : newTypes[1] + @types = newTypes.clone @effects[PBEffects::Type3] = newType3 else newType = GameData::Type.get(newType).id - @type1 = newType - @type2 = newType + @types = [newType] @effects[PBEffects::Type3] = nil end @effects[PBEffects::BurnUp] = false @effects[PBEffects::Roost] = false end + def pbResetTypes + @types = @pokemon.types + @effects[PBEffects::Type3] = nil + @effects[PBEffects::BurnUp] = false + @effects[PBEffects::Roost] = false + end + #============================================================================= # Forms #============================================================================= - def pbChangeForm(newForm,msg) - return if fainted? || @effects[PBEffects::Transform] || @form==newForm + def pbChangeForm(newForm, msg) + return if fainted? || @effects[PBEffects::Transform] || @form == newForm oldForm = @form - oldDmg = @totalhp-@hp + oldDmg = @totalhp - @hp self.form = newForm pbUpdate(true) - @hp = @totalhp-oldDmg + @hp = @totalhp - oldDmg @effects[PBEffects::WeightChange] = 0 if Settings::MECHANICS_GENERATION >= 6 - @battle.scene.pbChangePokemon(self,@pokemon) + @battle.scene.pbChangePokemon(self, @pokemon) @battle.scene.pbRefreshOne(@index) - @battle.pbDisplay(msg) if msg && msg!="" + @battle.pbDisplay(msg) if msg && msg != "" PBDebug.log("[Form changed] #{pbThis} changed from form #{oldForm} to form #{newForm}") @battle.pbSetSeen(self) end @@ -149,7 +171,7 @@ def pbCheckFormOnStatusChange return if fainted? || @effects[PBEffects::Transform] # Shaymin - reverts if frozen if isSpecies?(:SHAYMIN) && frozen? - pbChangeForm(0,_INTL("{1} transformed!",pbThis)) + pbChangeForm(0, _INTL("{1} transformed!", pbThis)) end end @@ -159,107 +181,117 @@ def pbCheckFormOnMovesetChange if isSpecies?(:KELDEO) newForm = 0 newForm = 1 if pbHasMove?(:SECRETSWORD) - pbChangeForm(newForm,_INTL("{1} transformed!",pbThis)) + pbChangeForm(newForm, _INTL("{1} transformed!", pbThis)) end end - def pbCheckFormOnWeatherChange + def pbCheckFormOnWeatherChange(ability_changed = false) return if fainted? || @effects[PBEffects::Transform] # Castform - Forecast if isSpecies?(:CASTFORM) if hasActiveAbility?(:FORECAST) newForm = 0 - case @battle.pbWeather + case effectiveWeather when :Sun, :HarshSun then newForm = 1 when :Rain, :HeavyRain then newForm = 2 when :Hail then newForm = 3 end - if @form!=newForm - @battle.pbShowAbilitySplash(self,true) + if @form != newForm + @battle.pbShowAbilitySplash(self, true) @battle.pbHideAbilitySplash(self) - pbChangeForm(newForm,_INTL("{1} transformed!",pbThis)) + pbChangeForm(newForm, _INTL("{1} transformed!", pbThis)) end else - pbChangeForm(0,_INTL("{1} transformed!",pbThis)) + pbChangeForm(0, _INTL("{1} transformed!", pbThis)) end end # Cherrim - Flower Gift if isSpecies?(:CHERRIM) if hasActiveAbility?(:FLOWERGIFT) newForm = 0 - newForm = 1 if [:Sun, :HarshSun].include?(@battle.pbWeather) - if @form!=newForm - @battle.pbShowAbilitySplash(self,true) + newForm = 1 if [:Sun, :HarshSun].include?(effectiveWeather) + if @form != newForm + @battle.pbShowAbilitySplash(self, true) @battle.pbHideAbilitySplash(self) - pbChangeForm(newForm,_INTL("{1} transformed!",pbThis)) + pbChangeForm(newForm, _INTL("{1} transformed!", pbThis)) end else - pbChangeForm(0,_INTL("{1} transformed!",pbThis)) + pbChangeForm(0, _INTL("{1} transformed!", pbThis)) end end + # Eiscue - Ice Face + if !ability_changed && isSpecies?(:EISCUE) && self.ability == :ICEFACE && + @form == 1 && effectiveWeather == :Hail + @canRestoreIceFace = true # Changed form at end of round + end end # Checks the Pokémon's form and updates it if necessary. Used for when a # Pokémon enters battle (endOfRound=false) and at the end of each round # (endOfRound=true). - def pbCheckForm(endOfRound=false) + def pbCheckForm(endOfRound = false) return if fainted? || @effects[PBEffects::Transform] # Form changes upon entering battle and when the weather changes pbCheckFormOnWeatherChange if !endOfRound # Darmanitan - Zen Mode if isSpecies?(:DARMANITAN) && self.ability == :ZENMODE - if @hp<=@totalhp/2 - if @form!=1 - @battle.pbShowAbilitySplash(self,true) + if @hp <= @totalhp / 2 + if @form.even? + @battle.pbShowAbilitySplash(self, true) @battle.pbHideAbilitySplash(self) - pbChangeForm(1,_INTL("{1} triggered!",abilityName)) + pbChangeForm(@form + 1, _INTL("{1} triggered!", abilityName)) end - elsif @form!=0 - @battle.pbShowAbilitySplash(self,true) + elsif @form.odd? + @battle.pbShowAbilitySplash(self, true) @battle.pbHideAbilitySplash(self) - pbChangeForm(0,_INTL("{1} triggered!",abilityName)) + pbChangeForm(@form - 1, _INTL("{1} triggered!", abilityName)) end end # Minior - Shields Down if isSpecies?(:MINIOR) && self.ability == :SHIELDSDOWN - if @hp>@totalhp/2 # Turn into Meteor form - newForm = (@form>=7) ? @form-7 : @form - if @form!=newForm - @battle.pbShowAbilitySplash(self,true) + if @hp > @totalhp / 2 # Turn into Meteor form + newForm = (@form >= 7) ? @form - 7 : @form + if @form != newForm + @battle.pbShowAbilitySplash(self, true) @battle.pbHideAbilitySplash(self) - pbChangeForm(newForm,_INTL("{1} deactivated!",abilityName)) + pbChangeForm(newForm, _INTL("{1} deactivated!", abilityName)) elsif !endOfRound - @battle.pbDisplay(_INTL("{1} deactivated!",abilityName)) + @battle.pbDisplay(_INTL("{1} deactivated!", abilityName)) end - elsif @form<7 # Turn into Core form - @battle.pbShowAbilitySplash(self,true) + elsif @form < 7 # Turn into Core form + @battle.pbShowAbilitySplash(self, true) @battle.pbHideAbilitySplash(self) - pbChangeForm(@form+7,_INTL("{1} activated!",abilityName)) + pbChangeForm(@form + 7, _INTL("{1} activated!", abilityName)) end end # Wishiwashi - Schooling if isSpecies?(:WISHIWASHI) && self.ability == :SCHOOLING - if @level>=20 && @hp>@totalhp/4 - if @form!=1 - @battle.pbShowAbilitySplash(self,true) + if @level >= 20 && @hp > @totalhp / 4 + if @form != 1 + @battle.pbShowAbilitySplash(self, true) @battle.pbHideAbilitySplash(self) - pbChangeForm(1,_INTL("{1} formed a school!",pbThis)) + pbChangeForm(1, _INTL("{1} formed a school!", pbThis)) end - elsif @form!=0 - @battle.pbShowAbilitySplash(self,true) + elsif @form != 0 + @battle.pbShowAbilitySplash(self, true) @battle.pbHideAbilitySplash(self) - pbChangeForm(0,_INTL("{1} stopped schooling!",pbThis)) + pbChangeForm(0, _INTL("{1} stopped schooling!", pbThis)) end end # Zygarde - Power Construct - if isSpecies?(:ZYGARDE) && self.ability == :POWERCONSTRUCT && endOfRound - if @hp<=@totalhp/2 && @form<2 # Turn into Complete Forme - newForm = @form+2 - @battle.pbDisplay(_INTL("You sense the presence of many!")) - @battle.pbShowAbilitySplash(self,true) - @battle.pbHideAbilitySplash(self) - pbChangeForm(newForm,_INTL("{1} transformed into its Complete Forme!",pbThis)) - end + if isSpecies?(:ZYGARDE) && self.ability == :POWERCONSTRUCT && endOfRound && + @hp <= @totalhp / 2 && @form < 2 # Turn into Complete Forme + newForm = @form + 2 + @battle.pbDisplay(_INTL("You sense the presence of many!")) + @battle.pbShowAbilitySplash(self, true) + @battle.pbHideAbilitySplash(self) + pbChangeForm(newForm, _INTL("{1} transformed into its Complete Forme!", pbThis)) + end + # Morpeko - Hunger Switch + if isSpecies?(:MORPEKO) && hasActiveAbility?(:HUNGERSWITCH) && endOfRound + # Intentionally doesn't show the ability splash or a message + newForm = (@form + 1) % 2 + pbChangeForm(newForm, nil) end end @@ -280,8 +312,8 @@ def pbTransform(target) @effects[PBEffects::LaserFocus] = target.effects[PBEffects::LaserFocus] end @moves.clear - target.moves.each_with_index do |m,i| - @moves[i] = PokeBattle_Move.from_pokemon_move(@battle, Pokemon::Move.new(m.id)) + target.moves.each_with_index do |m, i| + @moves[i] = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(m.id)) @moves[i].pp = 5 @moves[i].total_pp = 5 end @@ -289,8 +321,8 @@ def pbTransform(target) @effects[PBEffects::DisableMove] = nil @effects[PBEffects::WeightChange] = target.effects[PBEffects::WeightChange] @battle.scene.pbRefreshOne(@index) - @battle.pbDisplay(_INTL("{1} transformed into {2}!",pbThis,target.pbThis(true))) - pbOnAbilityChanged(oldAbil) + @battle.pbDisplay(_INTL("{1} transformed into {2}!", pbThis, target.pbThis(true))) + pbOnLosingAbility(oldAbil) end def pbHyperMode; end diff --git a/Data/Scripts/011_Battle/001_Battler/004_Battler_Statuses.rb b/Data/Scripts/011_Battle/002_Battler/004_Battler_Statuses.rb similarity index 73% rename from Data/Scripts/011_Battle/001_Battler/004_Battler_Statuses.rb rename to Data/Scripts/011_Battle/002_Battler/004_Battler_Statuses.rb index 733192ec3f..27d35437a3 100644 --- a/Data/Scripts/011_Battle/001_Battler/004_Battler_Statuses.rb +++ b/Data/Scripts/011_Battle/002_Battler/004_Battler_Statuses.rb @@ -1,4 +1,4 @@ -class PokeBattle_Battler +class Battle::Battler #============================================================================= # Generalised checks for whether a status problem can be inflicted #============================================================================= @@ -9,24 +9,24 @@ class PokeBattle_Battler # "counts as having that status", which includes Comatose which can't be # cured. def pbHasStatus?(checkStatus) - if BattleHandlers.triggerStatusCheckAbilityNonIgnorable(self.ability,self,checkStatus) + if Battle::AbilityEffects.triggerStatusCheckNonIgnorable(self.ability, self, checkStatus) return true end - return @status==checkStatus + return @status == checkStatus end def pbHasAnyStatus? - if BattleHandlers.triggerStatusCheckAbilityNonIgnorable(self.ability,self,nil) + if Battle::AbilityEffects.triggerStatusCheckNonIgnorable(self.ability, self, nil) return true end return @status != :NONE end - def pbCanInflictStatus?(newStatus,user,showMessages,move=nil,ignoreStatus=false) + def pbCanInflictStatus?(newStatus, user, showMessages, move = nil, ignoreStatus = false) return false if fainted? - selfInflicted = (user && user.index==@index) + selfInflicted = (user && user.index == @index) # Already have that status problem - if self.status==newStatus && !ignoreStatus + if self.status == newStatus && !ignoreStatus if showMessages msg = "" case self.status @@ -42,18 +42,18 @@ def pbCanInflictStatus?(newStatus,user,showMessages,move=nil,ignoreStatus=false) end # Trying to replace a status problem with another one if self.status != :NONE && !ignoreStatus && !selfInflicted - @battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages + @battle.pbDisplay(_INTL("It doesn't affect {1}...", pbThis(true))) if showMessages return false end # Trying to inflict a status problem on a Pokémon behind a substitute - if @effects[PBEffects::Substitute]>0 && !(move && move.ignoresSubstitute?(user)) && + if @effects[PBEffects::Substitute] > 0 && !(move && move.ignoresSubstitute?(user)) && !selfInflicted - @battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages + @battle.pbDisplay(_INTL("It doesn't affect {1}...", pbThis(true))) if showMessages return false end # Weather immunity - if newStatus == :FROZEN && [:Sun, :HarshSun].include?(@battle.pbWeather) - @battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages + if newStatus == :FROZEN && [:Sun, :HarshSun].include?(effectiveWeather) + @battle.pbDisplay(_INTL("It doesn't affect {1}...", pbThis(true))) if showMessages return false end # Terrains immunity @@ -61,20 +61,21 @@ def pbCanInflictStatus?(newStatus,user,showMessages,move=nil,ignoreStatus=false) case @battle.field.terrain when :Electric if newStatus == :SLEEP - @battle.pbDisplay(_INTL("{1} surrounds itself with electrified terrain!", - pbThis(true))) if showMessages + if showMessages + @battle.pbDisplay(_INTL("{1} surrounds itself with electrified terrain!", pbThis(true))) + end return false end when :Misty - @battle.pbDisplay(_INTL("{1} surrounds itself with misty terrain!",pbThis(true))) if showMessages + @battle.pbDisplay(_INTL("{1} surrounds itself with misty terrain!", pbThis(true))) if showMessages return false end end # Uproar immunity if newStatus == :SLEEP && !(hasActiveAbility?(:SOUNDPROOF) && !@battle.moldBreaker) - @battle.eachBattler do |b| - next if b.effects[PBEffects::Uproar]==0 - @battle.pbDisplay(_INTL("But the uproar kept {1} awake!",pbThis(true))) if showMessages + @battle.allBattlers.each do |b| + next if b.effects[PBEffects::Uproar] == 0 + @battle.pbDisplay(_INTL("But the uproar kept {1} awake!", pbThis(true))) if showMessages return false end end @@ -96,20 +97,21 @@ def pbCanInflictStatus?(newStatus,user,showMessages,move=nil,ignoreStatus=false) hasImmuneType |= pbHasType?(:ICE) end if hasImmuneType - @battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages + @battle.pbDisplay(_INTL("It doesn't affect {1}...", pbThis(true))) if showMessages return false end # Ability immunity - immuneByAbility = false; immAlly = nil - if BattleHandlers.triggerStatusImmunityAbilityNonIgnorable(self.ability,self,newStatus) + immuneByAbility = false + immAlly = nil + if Battle::AbilityEffects.triggerStatusImmunityNonIgnorable(self.ability, self, newStatus) immuneByAbility = true elsif selfInflicted || !@battle.moldBreaker - if abilityActive? && BattleHandlers.triggerStatusImmunityAbility(self.ability,self,newStatus) + if abilityActive? && Battle::AbilityEffects.triggerStatusImmunity(self.ability, self, newStatus) immuneByAbility = true else - eachAlly do |b| + allAllies.each do |b| next if !b.abilityActive? - next if !BattleHandlers.triggerStatusImmunityAllyAbility(b.ability,self,newStatus) + next if !Battle::AbilityEffects.triggerStatusImmunityFromAlly(b.ability, self, newStatus) immuneByAbility = true immAlly = b break @@ -120,7 +122,7 @@ def pbCanInflictStatus?(newStatus,user,showMessages,move=nil,ignoreStatus=false) if showMessages @battle.pbShowAbilitySplash(immAlly || self) msg = "" - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH + if Battle::Scene::USE_ABILITY_SPLASH case newStatus when :SLEEP then msg = _INTL("{1} stays awake!", pbThis) when :POISON then msg = _INTL("{1} cannot be poisoned!", pbThis) @@ -132,19 +134,19 @@ def pbCanInflictStatus?(newStatus,user,showMessages,move=nil,ignoreStatus=false) case newStatus when :SLEEP msg = _INTL("{1} stays awake because of {2}'s {3}!", - pbThis,immAlly.pbThis(true),immAlly.abilityName) + pbThis, immAlly.pbThis(true), immAlly.abilityName) when :POISON msg = _INTL("{1} cannot be poisoned because of {2}'s {3}!", - pbThis,immAlly.pbThis(true),immAlly.abilityName) + pbThis, immAlly.pbThis(true), immAlly.abilityName) when :BURN msg = _INTL("{1} cannot be burned because of {2}'s {3}!", - pbThis,immAlly.pbThis(true),immAlly.abilityName) + pbThis, immAlly.pbThis(true), immAlly.abilityName) when :PARALYSIS msg = _INTL("{1} cannot be paralyzed because of {2}'s {3}!", - pbThis,immAlly.pbThis(true),immAlly.abilityName) + pbThis, immAlly.pbThis(true), immAlly.abilityName) when :FROZEN msg = _INTL("{1} cannot be frozen solid because of {2}'s {3}!", - pbThis,immAlly.pbThis(true),immAlly.abilityName) + pbThis, immAlly.pbThis(true), immAlly.abilityName) end else case newStatus @@ -161,15 +163,15 @@ def pbCanInflictStatus?(newStatus,user,showMessages,move=nil,ignoreStatus=false) return false end # Safeguard immunity - if pbOwnSide.effects[PBEffects::Safeguard]>0 && !selfInflicted && move && + if pbOwnSide.effects[PBEffects::Safeguard] > 0 && !selfInflicted && move && !(user && user.hasActiveAbility?(:INFILTRATOR)) - @battle.pbDisplay(_INTL("{1}'s team is protected by Safeguard!",pbThis)) if showMessages + @battle.pbDisplay(_INTL("{1}'s team is protected by Safeguard!", pbThis)) if showMessages return false end return true end - def pbCanSynchronizeStatus?(newStatus,target) + def pbCanSynchronizeStatus?(newStatus, target) return false if fainted? # Trying to replace a status problem with another one return false if self.status != :NONE @@ -191,19 +193,19 @@ def pbCanSynchronizeStatus?(newStatus,target) end return false if hasImmuneType # Ability immunity - if BattleHandlers.triggerStatusImmunityAbilityNonIgnorable(self.ability,self,newStatus) + if Battle::AbilityEffects.triggerStatusImmunityNonIgnorable(self.ability, self, newStatus) return false end - if abilityActive? && BattleHandlers.triggerStatusImmunityAbility(self.ability,self,newStatus) + if abilityActive? && Battle::AbilityEffects.triggerStatusImmunity(self.ability, self, newStatus) return false end - eachAlly do |b| + allAllies.each do |b| next if !b.abilityActive? - next if !BattleHandlers.triggerStatusImmunityAllyAbility(b.ability,self,newStatus) + next if !Battle::AbilityEffects.triggerStatusImmunityFromAlly(b.ability, self, newStatus) return false end # Safeguard immunity - if pbOwnSide.effects[PBEffects::Safeguard]>0 && + if pbOwnSide.effects[PBEffects::Safeguard] > 0 && !(user && user.hasActiveAbility?(:INFILTRATOR)) return false end @@ -213,7 +215,7 @@ def pbCanSynchronizeStatus?(newStatus,target) #============================================================================= # Generalised infliction of status problem #============================================================================= - def pbInflictStatus(newStatus,newStatusCount=0,msg=nil,user=nil) + def pbInflictStatus(newStatus, newStatusCount = 0, msg = nil, user = nil) # Inflict the new status self.status = newStatus self.statusCount = newStatusCount @@ -233,7 +235,7 @@ def pbInflictStatus(newStatus,newStatusCount=0,msg=nil,user=nil) when :SLEEP @battle.pbDisplay(_INTL("{1} fell asleep!", pbThis)) when :POISON - if newStatusCount>0 + if newStatusCount > 0 @battle.pbDisplay(_INTL("{1} was badly poisoned!", pbThis)) else @battle.pbDisplay(_INTL("{1} was poisoned!", pbThis)) @@ -251,7 +253,7 @@ def pbInflictStatus(newStatus,newStatusCount=0,msg=nil,user=nil) pbCheckFormOnStatusChange # Synchronize if abilityActive? - BattleHandlers.triggerAbilityOnStatusInflicted(self.ability,self,user,newStatus) + Battle::AbilityEffects.triggerOnStatusInflicted(self.ability, self, user, newStatus) end # Status cures pbItemStatusCureCheck @@ -280,32 +282,30 @@ def pbCanSleep?(user, showMessages, move = nil, ignoreStatus = false) def pbCanSleepYawn? return false if self.status != :NONE - if affectedByTerrain? - return false if [:Electric, :Misty].include?(@battle.field.terrain) + if affectedByTerrain? && [:Electric, :Misty].include?(@battle.field.terrain) + return false end - if !hasActiveAbility?(:SOUNDPROOF) - @battle.eachBattler do |b| - return false if b.effects[PBEffects::Uproar]>0 - end + if !hasActiveAbility?(:SOUNDPROOF) && @battle.allBattlers.any? { |b| b.effects[PBEffects::Uproar] > 0 } + return false end - if BattleHandlers.triggerStatusImmunityAbilityNonIgnorable(self.ability, self, :SLEEP) + if Battle::AbilityEffects.triggerStatusImmunityNonIgnorable(self.ability, self, :SLEEP) return false end # NOTE: Bulbapedia claims that Flower Veil shouldn't prevent sleep due to # drowsiness, but I disagree because that makes no sense. Also, the # comparable Sweet Veil does prevent sleep due to drowsiness. - if abilityActive? && BattleHandlers.triggerStatusImmunityAbility(self.ability, self, :SLEEP) + if abilityActive? && Battle::AbilityEffects.triggerStatusImmunity(self.ability, self, :SLEEP) return false end - eachAlly do |b| + allAllies.each do |b| next if !b.abilityActive? - next if !BattleHandlers.triggerStatusImmunityAllyAbility(b.ability, self, :SLEEP) + next if !Battle::AbilityEffects.triggerStatusImmunityFromAlly(b.ability, self, :SLEEP) return false end # NOTE: Bulbapedia claims that Safeguard shouldn't prevent sleep due to # drowsiness. I disagree with this too. Compare with the other sided # effects Misty/Electric Terrain, which do prevent it. - return false if pbOwnSide.effects[PBEffects::Safeguard]>0 + return false if pbOwnSide.effects[PBEffects::Safeguard] > 0 return true end @@ -421,7 +421,7 @@ def pbContinueStatus PBDebug.log("[Status continues] #{pbThis}'s sleep count is #{@statusCount}") if self.status == :SLEEP end - def pbCureStatus(showMessages=true) + def pbCureStatus(showMessages = true) oldStatus = status self.status = :NONE if showMessages @@ -439,52 +439,50 @@ def pbCureStatus(showMessages=true) #============================================================================= # Confusion #============================================================================= - def pbCanConfuse?(user=nil,showMessages=true,move=nil,selfInflicted=false) + def pbCanConfuse?(user = nil, showMessages = true, move = nil, selfInflicted = false) return false if fainted? - if @effects[PBEffects::Confusion]>0 - @battle.pbDisplay(_INTL("{1} is already confused.",pbThis)) if showMessages + if @effects[PBEffects::Confusion] > 0 + @battle.pbDisplay(_INTL("{1} is already confused.", pbThis)) if showMessages return false end - if @effects[PBEffects::Substitute]>0 && !(move && move.ignoresSubstitute?(user)) && + if @effects[PBEffects::Substitute] > 0 && !(move && move.ignoresSubstitute?(user)) && !selfInflicted @battle.pbDisplay(_INTL("But it failed!")) if showMessages return false end # Terrains immunity - if affectedByTerrain? && @battle.field.terrain == :Misty - @battle.pbDisplay(_INTL("{1} surrounds itself with misty terrain!",pbThis(true))) if showMessages + if affectedByTerrain? && @battle.field.terrain == :Misty && Settings::MECHANICS_GENERATION >= 7 + @battle.pbDisplay(_INTL("{1} surrounds itself with misty terrain!", pbThis(true))) if showMessages return false end - if selfInflicted || !@battle.moldBreaker - if hasActiveAbility?(:OWNTEMPO) - if showMessages - @battle.pbShowAbilitySplash(self) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} doesn't become confused!",pbThis)) - else - @battle.pbDisplay(_INTL("{1}'s {2} prevents confusion!",pbThis,abilityName)) - end - @battle.pbHideAbilitySplash(self) + if (selfInflicted || !@battle.moldBreaker) && hasActiveAbility?(:OWNTEMPO) + if showMessages + @battle.pbShowAbilitySplash(self) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1} doesn't become confused!", pbThis)) + else + @battle.pbDisplay(_INTL("{1}'s {2} prevents confusion!", pbThis, abilityName)) end - return false + @battle.pbHideAbilitySplash(self) end + return false end - if pbOwnSide.effects[PBEffects::Safeguard]>0 && !selfInflicted && + if pbOwnSide.effects[PBEffects::Safeguard] > 0 && !selfInflicted && !(user && user.hasActiveAbility?(:INFILTRATOR)) - @battle.pbDisplay(_INTL("{1}'s team is protected by Safeguard!",pbThis)) if showMessages + @battle.pbDisplay(_INTL("{1}'s team is protected by Safeguard!", pbThis)) if showMessages return false end return true end def pbCanConfuseSelf?(showMessages) - return pbCanConfuse?(nil,showMessages,nil,true) + return pbCanConfuse?(nil, showMessages, nil, true) end - def pbConfuse(msg=nil) + def pbConfuse(msg = nil) @effects[PBEffects::Confusion] = pbConfusionDuration - @battle.pbCommonAnimation("Confusion",self) - msg = _INTL("{1} became confused!",pbThis) if nil_or_empty?(msg) + @battle.pbCommonAnimation("Confusion", self) + msg = _INTL("{1} became confused!", pbThis) if nil_or_empty?(msg) @battle.pbDisplay(msg) PBDebug.log("[Lingering effect] #{pbThis}'s confusion count is #{@effects[PBEffects::Confusion]}") # Confusion cures @@ -492,8 +490,8 @@ def pbConfuse(msg=nil) pbAbilityStatusCureCheck end - def pbConfusionDuration(duration=-1) - duration = 2+@battle.pbRandom(4) if duration<=0 + def pbConfusionDuration(duration = -1) + duration = 2 + @battle.pbRandom(4) if duration <= 0 return duration end @@ -504,40 +502,40 @@ def pbCureConfusion #============================================================================= # Attraction #============================================================================= - def pbCanAttract?(user,showMessages=true) + def pbCanAttract?(user, showMessages = true) return false if fainted? return false if !user || user.fainted? - if @effects[PBEffects::Attract]>=0 - @battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) if showMessages + if @effects[PBEffects::Attract] >= 0 + @battle.pbDisplay(_INTL("{1} is unaffected!", pbThis)) if showMessages return false end agender = user.gender ogender = gender - if agender==2 || ogender==2 || agender==ogender - @battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) if showMessages + if agender == 2 || ogender == 2 || agender == ogender + @battle.pbDisplay(_INTL("{1} is unaffected!", pbThis)) if showMessages return false end if !@battle.moldBreaker - if hasActiveAbility?([:AROMAVEIL,:OBLIVIOUS]) + if hasActiveAbility?([:AROMAVEIL, :OBLIVIOUS]) if showMessages @battle.pbShowAbilitySplash(self) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1} is unaffected!", pbThis)) else - @battle.pbDisplay(_INTL("{1}'s {2} prevents romance!",pbThis,abilityName)) + @battle.pbDisplay(_INTL("{1}'s {2} prevents romance!", pbThis, abilityName)) end @battle.pbHideAbilitySplash(self) end return false else - eachAlly do |b| + allAllies.each do |b| next if !b.hasActiveAbility?(:AROMAVEIL) if showMessages @battle.pbShowAbilitySplash(self) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1} is unaffected!", pbThis)) else - @battle.pbDisplay(_INTL("{1}'s {2} prevents romance!",b.pbThis,b.abilityName)) + @battle.pbDisplay(_INTL("{1}'s {2} prevents romance!", b.pbThis, b.abilityName)) end @battle.pbHideAbilitySplash(self) end @@ -548,14 +546,14 @@ def pbCanAttract?(user,showMessages=true) return true end - def pbAttract(user,msg=nil) + def pbAttract(user, msg = nil) @effects[PBEffects::Attract] = user.index - @battle.pbCommonAnimation("Attract",self) - msg = _INTL("{1} fell in love!",pbThis) if nil_or_empty?(msg) + @battle.pbCommonAnimation("Attract", self) + msg = _INTL("{1} fell in love!", pbThis) if nil_or_empty?(msg) @battle.pbDisplay(msg) # Destiny Knot - if hasActiveItem?(:DESTINYKNOT) && user.pbCanAttract?(self,false) - user.pbAttract(self,_INTL("{1} fell in love from the {2}!",user.pbThis(true),itemName)) + if hasActiveItem?(:DESTINYKNOT) && user.pbCanAttract?(self, false) + user.pbAttract(self, _INTL("{1} fell in love from the {2}!", user.pbThis(true), itemName)) end # Attraction cures pbItemStatusCureCheck @@ -569,7 +567,7 @@ def pbCureAttract #============================================================================= # Flinching #============================================================================= - def pbFlinch(_user=nil) + def pbFlinch(_user = nil) return if hasActiveAbility?(:INNERFOCUS) && !@battle.moldBreaker @effects[PBEffects::Flinch] = true end diff --git a/Data/Scripts/011_Battle/002_Battler/005_Battler_StatStages.rb b/Data/Scripts/011_Battle/002_Battler/005_Battler_StatStages.rb new file mode 100644 index 0000000000..1f6e66d6fd --- /dev/null +++ b/Data/Scripts/011_Battle/002_Battler/005_Battler_StatStages.rb @@ -0,0 +1,387 @@ +class Battle::Battler + #============================================================================= + # Increase stat stages + #============================================================================= + def statStageAtMax?(stat) + return @stages[stat] >= 6 + end + + def pbCanRaiseStatStage?(stat, user = nil, move = nil, showFailMsg = false, ignoreContrary = false) + return false if fainted? + # Contrary + if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker + return pbCanLowerStatStage?(stat, user, move, showFailMsg, true) + end + # Check the stat stage + if statStageAtMax?(stat) + if showFailMsg + @battle.pbDisplay(_INTL("{1}'s {2} won't go any higher!", + pbThis, GameData::Stat.get(stat).name)) + end + return false + end + return true + end + + def pbRaiseStatStageBasic(stat, increment, ignoreContrary = false) + if !@battle.moldBreaker + # Contrary + if hasActiveAbility?(:CONTRARY) && !ignoreContrary + return pbLowerStatStageBasic(stat, increment, true) + end + # Simple + increment *= 2 if hasActiveAbility?(:SIMPLE) + end + # Change the stat stage + increment = [increment, 6 - @stages[stat]].min + if increment > 0 + stat_name = GameData::Stat.get(stat).name + new = @stages[stat] + increment + PBDebug.log("[Stat change] #{pbThis}'s #{stat_name}: #{@stages[stat]} -> #{new} (+#{increment})") + @stages[stat] += increment + @statsRaisedThisRound = true + end + return increment + end + + def pbRaiseStatStage(stat, increment, user, showAnim = true, ignoreContrary = false) + # Contrary + if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker + return pbLowerStatStage(stat, increment, user, showAnim, true) + end + # Perform the stat stage change + increment = pbRaiseStatStageBasic(stat, increment, ignoreContrary) + return false if increment <= 0 + # Stat up animation and message + @battle.pbCommonAnimation("StatUp", self) if showAnim + arrStatTexts = [ + _INTL("{1}'s {2} rose!", pbThis, GameData::Stat.get(stat).name), + _INTL("{1}'s {2} rose sharply!", pbThis, GameData::Stat.get(stat).name), + _INTL("{1}'s {2} rose drastically!", pbThis, GameData::Stat.get(stat).name) + ] + @battle.pbDisplay(arrStatTexts[[increment - 1, 2].min]) + # Trigger abilities upon stat gain + if abilityActive? + Battle::AbilityEffects.triggerOnStatGain(self.ability, self, stat, user) + end + return true + end + + def pbRaiseStatStageByCause(stat, increment, user, cause, showAnim = true, ignoreContrary = false) + # Contrary + if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker + return pbLowerStatStageByCause(stat, increment, user, cause, showAnim, true) + end + # Perform the stat stage change + increment = pbRaiseStatStageBasic(stat, increment, ignoreContrary) + return false if increment <= 0 + # Stat up animation and message + @battle.pbCommonAnimation("StatUp", self) if showAnim + if user.index == @index + arrStatTexts = [ + _INTL("{1}'s {2} raised its {3}!", pbThis, cause, GameData::Stat.get(stat).name), + _INTL("{1}'s {2} sharply raised its {3}!", pbThis, cause, GameData::Stat.get(stat).name), + _INTL("{1}'s {2} drastically raised its {3}!", pbThis, cause, GameData::Stat.get(stat).name) + ] + else + arrStatTexts = [ + _INTL("{1}'s {2} raised {3}'s {4}!", user.pbThis, cause, pbThis(true), GameData::Stat.get(stat).name), + _INTL("{1}'s {2} sharply raised {3}'s {4}!", user.pbThis, cause, pbThis(true), GameData::Stat.get(stat).name), + _INTL("{1}'s {2} drastically raised {3}'s {4}!", user.pbThis, cause, pbThis(true), GameData::Stat.get(stat).name) + ] + end + @battle.pbDisplay(arrStatTexts[[increment - 1, 2].min]) + # Trigger abilities upon stat gain + if abilityActive? + Battle::AbilityEffects.triggerOnStatGain(self.ability, self, stat, user) + end + return true + end + + def pbRaiseStatStageByAbility(stat, increment, user, splashAnim = true) + return false if fainted? + ret = false + @battle.pbShowAbilitySplash(user) if splashAnim + if pbCanRaiseStatStage?(stat, user, nil, Battle::Scene::USE_ABILITY_SPLASH) + if Battle::Scene::USE_ABILITY_SPLASH + ret = pbRaiseStatStage(stat, increment, user) + else + ret = pbRaiseStatStageByCause(stat, increment, user, user.abilityName) + end + end + @battle.pbHideAbilitySplash(user) if splashAnim + return ret + end + + #============================================================================= + # Decrease stat stages + #============================================================================= + def statStageAtMin?(stat) + return @stages[stat] <= -6 + end + + def pbCanLowerStatStage?(stat, user = nil, move = nil, showFailMsg = false, + ignoreContrary = false, ignoreMirrorArmor = false) + return false if fainted? + if !@battle.moldBreaker + # Contrary + if hasActiveAbility?(:CONTRARY) && !ignoreContrary + return pbCanRaiseStatStage?(stat, user, move, showFailMsg, true) + end + # Mirror Armor + if hasActiveAbility?(:MIRRORARMOR) && !ignoreMirrorArmor && + user && user.index != @index && !statStageAtMin?(stat) + return true + end + end + if !user || user.index != @index # Not self-inflicted + if @effects[PBEffects::Substitute] > 0 && + (ignoreMirrorArmor || !(move && move.ignoresSubstitute?(user))) + @battle.pbDisplay(_INTL("{1} is protected by its substitute!", pbThis)) if showFailMsg + return false + end + if pbOwnSide.effects[PBEffects::Mist] > 0 && + !(user && user.hasActiveAbility?(:INFILTRATOR)) + @battle.pbDisplay(_INTL("{1} is protected by Mist!", pbThis)) if showFailMsg + return false + end + if abilityActive? + return false if !@battle.moldBreaker && Battle::AbilityEffects.triggerStatLossImmunity( + self.ability, self, stat, @battle, showFailMsg + ) + return false if Battle::AbilityEffects.triggerStatLossImmunityNonIgnorable( + self.ability, self, stat, @battle, showFailMsg + ) + end + if !@battle.moldBreaker + allAllies.each do |b| + next if !b.abilityActive? + return false if Battle::AbilityEffects.triggerStatLossImmunityFromAlly( + b.ability, b, self, stat, @battle, showFailMsg + ) + end + end + end + # Check the stat stage + if statStageAtMin?(stat) + if showFailMsg + @battle.pbDisplay(_INTL("{1}'s {2} won't go any lower!", + pbThis, GameData::Stat.get(stat).name)) + end + return false + end + return true + end + + def pbLowerStatStageBasic(stat, increment, ignoreContrary = false) + if !@battle.moldBreaker + # Contrary + if hasActiveAbility?(:CONTRARY) && !ignoreContrary + return pbRaiseStatStageBasic(stat, increment, true) + end + # Simple + increment *= 2 if hasActiveAbility?(:SIMPLE) + end + # Change the stat stage + increment = [increment, 6 + @stages[stat]].min + if increment > 0 + stat_name = GameData::Stat.get(stat).name + new = @stages[stat] - increment + PBDebug.log("[Stat change] #{pbThis}'s #{stat_name}: #{@stages[stat]} -> #{new} (-#{increment})") + @stages[stat] -= increment + @statsLoweredThisRound = true + @statsDropped = true + end + return increment + end + + def pbLowerStatStage(stat, increment, user, showAnim = true, ignoreContrary = false, + mirrorArmorSplash = 0, ignoreMirrorArmor = false) + if !@battle.moldBreaker + # Contrary + if hasActiveAbility?(:CONTRARY) && !ignoreContrary + return pbRaiseStatStage(stat, increment, user, showAnim, true) + end + # Mirror Armor + if hasActiveAbility?(:MIRRORARMOR) && !ignoreMirrorArmor && + user && user.index != @index && !statStageAtMin?(stat) + if mirrorArmorSplash < 2 + @battle.pbShowAbilitySplash(self) + if !Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1}'s {2} activated!", pbThis, abilityName)) + end + end + ret = false + if user.pbCanLowerStatStage?(stat, self, nil, true, ignoreContrary, true) + ret = user.pbLowerStatStage(stat, increment, self, showAnim, ignoreContrary, mirrorArmorSplash, true) + end + @battle.pbHideAbilitySplash(self) if mirrorArmorSplash.even? # i.e. not 1 or 3 + return ret + end + end + # Perform the stat stage change + increment = pbLowerStatStageBasic(stat, increment, ignoreContrary) + return false if increment <= 0 + # Stat down animation and message + @battle.pbCommonAnimation("StatDown", self) if showAnim + arrStatTexts = [ + _INTL("{1}'s {2} fell!", pbThis, GameData::Stat.get(stat).name), + _INTL("{1}'s {2} harshly fell!", pbThis, GameData::Stat.get(stat).name), + _INTL("{1}'s {2} severely fell!", pbThis, GameData::Stat.get(stat).name) + ] + @battle.pbDisplay(arrStatTexts[[increment - 1, 2].min]) + # Trigger abilities upon stat loss + if abilityActive? + Battle::AbilityEffects.triggerOnStatLoss(self.ability, self, stat, user) + end + return true + end + + def pbLowerStatStageByCause(stat, increment, user, cause, showAnim = true, + ignoreContrary = false, ignoreMirrorArmor = false) + if !@battle.moldBreaker + # Contrary + if hasActiveAbility?(:CONTRARY) && !ignoreContrary + return pbRaiseStatStageByCause(stat, increment, user, cause, showAnim, true) + end + # Mirror Armor + if hasActiveAbility?(:MIRRORARMOR) && !ignoreMirrorArmor && + user && user.index != @index && !statStageAtMin?(stat) + @battle.pbShowAbilitySplash(self) + if !Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1}'s {2} activated!", pbThis, abilityName)) + end + ret = false + if user.pbCanLowerStatStage?(stat, self, nil, true, ignoreContrary, true) + ret = user.pbLowerStatStageByCause(stat, increment, self, abilityName, showAnim, ignoreContrary, true) + end + @battle.pbHideAbilitySplash(self) + return ret + end + end + # Perform the stat stage change + increment = pbLowerStatStageBasic(stat, increment, ignoreContrary) + return false if increment <= 0 + # Stat down animation and message + @battle.pbCommonAnimation("StatDown", self) if showAnim + if user.index == @index + arrStatTexts = [ + _INTL("{1}'s {2} lowered its {3}!", pbThis, cause, GameData::Stat.get(stat).name), + _INTL("{1}'s {2} harshly lowered its {3}!", pbThis, cause, GameData::Stat.get(stat).name), + _INTL("{1}'s {2} severely lowered its {3}!", pbThis, cause, GameData::Stat.get(stat).name) + ] + else + arrStatTexts = [ + _INTL("{1}'s {2} lowered {3}'s {4}!", user.pbThis, cause, pbThis(true), GameData::Stat.get(stat).name), + _INTL("{1}'s {2} harshly lowered {3}'s {4}!", user.pbThis, cause, pbThis(true), GameData::Stat.get(stat).name), + _INTL("{1}'s {2} severely lowered {3}'s {4}!", user.pbThis, cause, pbThis(true), GameData::Stat.get(stat).name) + ] + end + @battle.pbDisplay(arrStatTexts[[increment - 1, 2].min]) + # Trigger abilities upon stat loss + if abilityActive? + Battle::AbilityEffects.triggerOnStatLoss(self.ability, self, stat, user) + end + return true + end + + def pbLowerStatStageByAbility(stat, increment, user, splashAnim = true, checkContact = false) + ret = false + @battle.pbShowAbilitySplash(user) if splashAnim + if pbCanLowerStatStage?(stat, user, nil, Battle::Scene::USE_ABILITY_SPLASH) && + (!checkContact || affectedByContactEffect?(Battle::Scene::USE_ABILITY_SPLASH)) + if Battle::Scene::USE_ABILITY_SPLASH + ret = pbLowerStatStage(stat, increment, user) + else + ret = pbLowerStatStageByCause(stat, increment, user, user.abilityName) + end + end + @battle.pbHideAbilitySplash(user) if splashAnim + return ret + end + + def pbLowerAttackStatStageIntimidate(user) + return false if fainted? + # NOTE: Substitute intentionally blocks Intimidate even if self has Contrary. + if @effects[PBEffects::Substitute] > 0 + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1} is protected by its substitute!", pbThis)) + else + @battle.pbDisplay(_INTL("{1}'s substitute protected it from {2}'s {3}!", + pbThis, user.pbThis(true), user.abilityName)) + end + return false + end + if Settings::MECHANICS_GENERATION >= 8 && hasActiveAbility?([:OBLIVIOUS, :OWNTEMPO, :INNERFOCUS, :SCRAPPY]) + @battle.pbShowAbilitySplash(self) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1}'s {2} cannot be lowered!", pbThis, GameData::Stat.get(:ATTACK).name)) + else + @battle.pbDisplay(_INTL("{1}'s {2} prevents {3} loss!", pbThis, abilityName, + GameData::Stat.get(:ATTACK).name)) + end + @battle.pbHideAbilitySplash(self) + return false + end + if Battle::Scene::USE_ABILITY_SPLASH + return pbLowerStatStageByAbility(:ATTACK, 1, user, false) + end + # NOTE: These checks exist to ensure appropriate messages are shown if + # Intimidate is blocked somehow (i.e. the messages should mention the + # Intimidate ability by name). + if !hasActiveAbility?(:CONTRARY) + if pbOwnSide.effects[PBEffects::Mist] > 0 + @battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by Mist!", + pbThis, user.pbThis(true), user.abilityName)) + return false + end + if abilityActive? && + (Battle::AbilityEffects.triggerStatLossImmunity(self.ability, self, :ATTACK, @battle, false) || + Battle::AbilityEffects.triggerStatLossImmunityNonIgnorable(self.ability, self, :ATTACK, @battle, false)) + @battle.pbDisplay(_INTL("{1}'s {2} prevented {3}'s {4} from working!", + pbThis, abilityName, user.pbThis(true), user.abilityName)) + return false + end + allAllies.each do |b| + next if !b.abilityActive? + if Battle::AbilityEffects.triggerStatLossImmunityFromAlly(b.ability, b, self, :ATTACK, @battle, false) + @battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by {4}'s {5}!", + pbThis, user.pbThis(true), user.abilityName, b.pbThis(true), b.abilityName)) + return false + end + end + end + return false if !pbCanLowerStatStage?(:ATTACK, user) + return pbLowerStatStageByCause(:ATTACK, 1, user, user.abilityName) + end + + #============================================================================= + # Reset stat stages + #============================================================================= + def hasAlteredStatStages? + GameData::Stat.each_battle { |s| return true if @stages[s.id] != 0 } + return false + end + + def hasRaisedStatStages? + GameData::Stat.each_battle { |s| return true if @stages[s.id] > 0 } + return false + end + + def hasLoweredStatStages? + GameData::Stat.each_battle { |s| return true if @stages[s.id] < 0 } + return false + end + + def pbResetStatStages + GameData::Stat.each_battle do |s| + if @stages[s.id] > 0 + @statsLoweredThisRound = true + @statsDropped = true + elsif @stages[s.id] < 0 + @statsRaisedThisRound = true + end + @stages[s.id] = 0 + end + end +end diff --git a/Data/Scripts/011_Battle/002_Battler/006_Battler_AbilityAndItem.rb b/Data/Scripts/011_Battle/002_Battler/006_Battler_AbilityAndItem.rb new file mode 100644 index 0000000000..02fa3e9fa3 --- /dev/null +++ b/Data/Scripts/011_Battle/002_Battler/006_Battler_AbilityAndItem.rb @@ -0,0 +1,468 @@ +class Battle::Battler + #============================================================================= + # Ability trigger checks + #============================================================================= + def pbAbilitiesOnSwitchOut + if abilityActive? + Battle::AbilityEffects.triggerOnSwitchOut(self.ability, self, false) + end + # Reset form + @battle.peer.pbOnLeavingBattle(@battle, @pokemon, @battle.usedInBattle[idxOwnSide][@index / 2]) + # Treat self as fainted + @hp = 0 + @fainted = true + # Check for end of Neutralizing Gas/Unnerve + pbAbilitiesOnNeutralizingGasEnding if hasActiveAbility?(:NEUTRALIZINGGAS, true) + pbItemsOnUnnerveEnding if hasActiveAbility?(:UNNERVE, true) + # Check for end of primordial weather + @battle.pbEndPrimordialWeather + end + + def pbAbilitiesOnFainting + # Self fainted; check all other battlers to see if their abilities trigger + @battle.pbPriority(true).each do |b| + next if !b || !b.abilityActive? + Battle::AbilityEffects.triggerChangeOnBattlerFainting(b.ability, b, self, @battle) + end + @battle.pbPriority(true).each do |b| + next if !b || !b.abilityActive? + Battle::AbilityEffects.triggerOnBattlerFainting(b.ability, b, self, @battle) + end + pbAbilitiesOnNeutralizingGasEnding if hasActiveAbility?(:NEUTRALIZINGGAS, true) + pbItemsOnUnnerveEnding if hasActiveAbility?(:UNNERVE, true) + end + + # Used for Emergency Exit/Wimp Out. Returns whether self has switched out. + def pbAbilitiesOnDamageTaken(move_user = nil) + return false if !@droppedBelowHalfHP + return false if !abilityActive? + return Battle::AbilityEffects.triggerOnHPDroppedBelowHalf(self.ability, self, move_user, @battle) + end + + def pbAbilityOnTerrainChange(ability_changed = false) + return if !abilityActive? + Battle::AbilityEffects.triggerOnTerrainChange(self.ability, self, @battle, ability_changed) + end + + # Used for Rattled's Gen 8 effect. Called when Intimidate is triggered. + def pbAbilitiesOnIntimidated + return if !abilityActive? + Battle::AbilityEffects.triggerOnIntimidated(self.ability, self, @battle) + end + + def pbAbilitiesOnNeutralizingGasEnding + return if @battle.pbCheckGlobalAbility(:NEUTRALIZINGGAS) + @battle.pbDisplay(_INTL("The effects of the neutralizing gas wore off!")) + @battle.pbEndPrimordialWeather + @battle.pbPriority(true).each do |b| + next if b.fainted? + next if !b.unstoppableAbility? && !b.abilityActive? + Battle::AbilityEffects.triggerOnSwitchIn(b.ability, b, @battle) + end + end + + # Called when a Pokémon (self) enters battle, at the end of each move used, + # and at the end of each round. + def pbContinualAbilityChecks(onSwitchIn = false) + # Check for end of primordial weather + @battle.pbEndPrimordialWeather + # Trace + if hasActiveAbility?(:TRACE) + # NOTE: In Gen 5 only, Trace only triggers upon the Trace bearer switching + # in and not at any later times, even if a traceable ability turns + # up later. Essentials ignores this, and allows Trace to trigger + # whenever it can even in Gen 5 battle mechanics. + choices = @battle.allOtherSideBattlers(@index).select { |b| + next !b.ungainableAbility? && + ![:POWEROFALCHEMY, :RECEIVER, :TRACE].include?(b.ability_id) + } + if choices.length > 0 + choice = choices[@battle.pbRandom(choices.length)] + @battle.pbShowAbilitySplash(self) + self.ability = choice.ability + @battle.pbDisplay(_INTL("{1} traced {2}'s {3}!", pbThis, choice.pbThis(true), choice.abilityName)) + @battle.pbHideAbilitySplash(self) + if !onSwitchIn && (unstoppableAbility? || abilityActive?) + Battle::AbilityEffects.triggerOnSwitchIn(self.ability, self, @battle) + end + end + end + end + + #============================================================================= + # Ability curing + #============================================================================= + # Cures status conditions, confusion and infatuation. + def pbAbilityStatusCureCheck + if abilityActive? + Battle::AbilityEffects.triggerStatusCure(self.ability, self) + end + end + + #============================================================================= + # Ability effects + #============================================================================= + # For abilities that grant immunity to moves of a particular type, and raises + # one of the ability's bearer's stats instead. + def pbMoveImmunityStatRaisingAbility(user, move, moveType, immuneType, stat, increment, show_message) + return false if user.index == @index + return false if moveType != immuneType + # NOTE: If show_message is false (Dragon Darts only), the stat will not be + # raised. This is not how the official games work, but I'm considering + # that a bug because Dragon Darts won't be fired at self in the first + # place if it's immune, so why would this ability be triggered by them? + if show_message + @battle.pbShowAbilitySplash(self) + if pbCanRaiseStatStage?(stat, self) + if Battle::Scene::USE_ABILITY_SPLASH + pbRaiseStatStage(stat, increment, self) + else + pbRaiseStatStageByCause(stat, increment, self, abilityName) + end + elsif Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("It doesn't affect {1}...", pbThis(true))) + else + @battle.pbDisplay(_INTL("{1}'s {2} made {3} ineffective!", pbThis, abilityName, move.name)) + end + @battle.pbHideAbilitySplash(self) + end + return true + end + + # For abilities that grant immunity to moves of a particular type, and heals + # the ability's bearer by 1/4 of its total HP instead. + def pbMoveImmunityHealingAbility(user, move, moveType, immuneType, show_message) + return false if user.index == @index + return false if moveType != immuneType + # NOTE: If show_message is false (Dragon Darts only), HP will not be healed. + # This is not how the official games work, but I'm considering that a + # bug because Dragon Darts won't be fired at self in the first place + # if it's immune, so why would this ability be triggered by them? + if show_message + @battle.pbShowAbilitySplash(self) + if canHeal? && pbRecoverHP(@totalhp / 4) > 0 + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1}'s HP was restored.", pbThis)) + else + @battle.pbDisplay(_INTL("{1}'s {2} restored its HP.", pbThis, abilityName)) + end + elsif Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("It doesn't affect {1}...", pbThis(true))) + else + @battle.pbDisplay(_INTL("{1}'s {2} made {3} ineffective!", pbThis, abilityName, move.name)) + end + @battle.pbHideAbilitySplash(self) + end + return true + end + + #============================================================================= + # Ability change + #============================================================================= + def pbOnLosingAbility(oldAbil, suppressed = false) + if oldAbil == :NEUTRALIZINGGAS && (suppressed || !@effects[PBEffects::GastroAcid]) + pbAbilitiesOnNeutralizingGasEnding + elsif oldAbil == :UNNERVE && (suppressed || !@effects[PBEffects::GastroAcid]) + pbItemsOnUnnerveEnding + elsif oldAbil == :ILLUSION && @effects[PBEffects::Illusion] + @effects[PBEffects::Illusion] = nil + if !@effects[PBEffects::Transform] + @battle.scene.pbChangePokemon(self, @pokemon) + @battle.pbDisplay(_INTL("{1}'s {2} wore off!", pbThis, GameData::Ability.get(oldAbil).name)) + @battle.pbSetSeen(self) + end + end + @effects[PBEffects::GastroAcid] = false if unstoppableAbility? + @effects[PBEffects::SlowStart] = 0 if self.ability != :SLOWSTART + @effects[PBEffects::Truant] = false if self.ability != :TRUANT + # Check for end of primordial weather + @battle.pbEndPrimordialWeather + # Revert form if Flower Gift/Forecast was lost + pbCheckFormOnWeatherChange(true) + # Abilities that trigger when the terrain changes + pbAbilityOnTerrainChange(true) + end + + def pbTriggerAbilityOnGainingIt + # Ending primordial weather, checking Trace + pbContinualAbilityChecks(true) # Don't trigger Traced ability as it's triggered below + # Abilities that trigger upon switching in + if (!fainted? && unstoppableAbility?) || abilityActive? + Battle::AbilityEffects.triggerOnSwitchIn(self.ability, self, @battle) + end + # Status-curing ability check + pbAbilityStatusCureCheck + # Check for end of primordial weather + @battle.pbEndPrimordialWeather + end + + #============================================================================= + # Held item consuming/removing + #============================================================================= + def canConsumeBerry? + return false if @battle.pbCheckOpposingAbility(:UNNERVE, @index) + return true + end + + def canConsumePinchBerry?(check_gluttony = true) + return false if !canConsumeBerry? + return true if @hp <= @totalhp / 4 + return true if @hp <= @totalhp / 2 && (!check_gluttony || hasActiveAbility?(:GLUTTONY)) + return false + end + + # permanent is whether the item is lost even after battle. Is false for Knock + # Off. + def pbRemoveItem(permanent = true) + @effects[PBEffects::ChoiceBand] = nil if !hasActiveAbility?(:GORILLATACTICS) + @effects[PBEffects::Unburden] = true if self.item && hasActiveAbility?(:UNBURDEN) + setInitialItem(nil) if permanent && self.item == self.initialItem + self.item = nil + end + + def pbConsumeItem(recoverable = true, symbiosis = true, belch = true) + PBDebug.log("[Item consumed] #{pbThis} consumed its held #{itemName}") + if recoverable + setRecycleItem(@item_id) + @effects[PBEffects::PickupItem] = @item_id + @effects[PBEffects::PickupUse] = @battle.nextPickupUse + end + setBelched if belch && self.item.is_berry? + pbRemoveItem + pbSymbiosis if symbiosis + end + + def pbSymbiosis + return if fainted? + return if self.item + @battle.pbPriority(true).each do |b| + next if b.opposes?(self) + next if !b.hasActiveAbility?(:SYMBIOSIS) + next if !b.item || b.unlosableItem?(b.item) + next if unlosableItem?(b.item) + @battle.pbShowAbilitySplash(b) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1} shared its {2} with {3}!", + b.pbThis, b.itemName, pbThis(true))) + else + @battle.pbDisplay(_INTL("{1}'s {2} let it share its {3} with {4}!", + b.pbThis, b.abilityName, b.itemName, pbThis(true))) + end + self.item = b.item + b.item = nil + b.effects[PBEffects::Unburden] = true if b.hasActiveAbility?(:UNBURDEN) + @battle.pbHideAbilitySplash(b) + pbHeldItemTriggerCheck + break + end + end + + # item_to_use is an item ID or GameData::Item object. own_item is whether the + # item is held by self. fling is for Fling only. + def pbHeldItemTriggered(item_to_use, own_item = true, fling = false) + # Cheek Pouch + if hasActiveAbility?(:CHEEKPOUCH) && GameData::Item.get(item_to_use).is_berry? && canHeal? + @battle.pbShowAbilitySplash(self) + pbRecoverHP(@totalhp / 3) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1}'s HP was restored.", pbThis)) + else + @battle.pbDisplay(_INTL("{1}'s {2} restored its HP.", pbThis, abilityName)) + end + @battle.pbHideAbilitySplash(self) + end + pbConsumeItem if own_item + pbSymbiosis if !own_item && !fling # Bug Bite/Pluck users trigger Symbiosis + end + + #============================================================================= + # Held item trigger checks + #============================================================================= + # NOTE: A Pokémon using Bug Bite/Pluck, and a Pokémon having an item thrown at + # it via Fling, will gain the effect of the item even if the Pokémon is + # affected by item-negating effects. + # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. + # fling is for Fling only. + def pbHeldItemTriggerCheck(item_to_use = nil, fling = false) + return if fainted? + return if !item_to_use && !itemActive? + pbItemHPHealCheck(item_to_use, fling) + pbItemStatusCureCheck(item_to_use, fling) + pbItemEndOfMoveCheck(item_to_use, fling) + # For Enigma Berry, Kee Berry and Maranga Berry, which have their effects + # when forcibly consumed by Pluck/Fling. + if item_to_use + itm = item_to_use || self.item + if Battle::ItemEffects.triggerOnBeingHitPositiveBerry(itm, self, @battle, true) + pbHeldItemTriggered(itm, false, fling) + end + end + end + + # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. + # fling is for Fling only. + def pbItemHPHealCheck(item_to_use = nil, fling = false) + return if !item_to_use && !itemActive? + itm = item_to_use || self.item + if Battle::ItemEffects.triggerHPHeal(itm, self, @battle, !item_to_use.nil?) + pbHeldItemTriggered(itm, item_to_use.nil?, fling) + elsif !item_to_use + pbItemTerrainStatBoostCheck + end + end + + # Cures status conditions, confusion, infatuation and the other effects cured + # by Mental Herb. + # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. + # fling is for Fling only. + def pbItemStatusCureCheck(item_to_use = nil, fling = false) + return if fainted? + return if !item_to_use && !itemActive? + itm = item_to_use || self.item + if Battle::ItemEffects.triggerStatusCure(itm, self, @battle, !item_to_use.nil?) + pbHeldItemTriggered(itm, item_to_use.nil?, fling) + end + end + + # Called at the end of using a move. + # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. + # fling is for Fling only. + def pbItemEndOfMoveCheck(item_to_use = nil, fling = false) + return if fainted? + return if !item_to_use && !itemActive? + itm = item_to_use || self.item + if Battle::ItemEffects.triggerOnEndOfUsingMove(itm, self, @battle, !item_to_use.nil?) + pbHeldItemTriggered(itm, item_to_use.nil?, fling) + elsif Battle::ItemEffects.triggerOnEndOfUsingMoveStatRestore(itm, self, @battle, !item_to_use.nil?) + pbHeldItemTriggered(itm, item_to_use.nil?, fling) + end + end + + # Used for White Herb (restore lowered stats). Only called by Moody and Sticky + # Web, as all other stat reduction happens because of/during move usage and + # this handler is also called at the end of each move's usage. + # item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise. + # fling is for Fling only. + def pbItemStatRestoreCheck(item_to_use = nil, fling = false) + return if fainted? + return if !item_to_use && !itemActive? + itm = item_to_use || self.item + if Battle::ItemEffects.triggerOnEndOfUsingMoveStatRestore(itm, self, @battle, !item_to_use.nil?) + pbHeldItemTriggered(itm, item_to_use.nil?, fling) + end + end + + # Called when the battle terrain changes and when a Pokémon loses HP. + def pbItemTerrainStatBoostCheck + return if !itemActive? + if Battle::ItemEffects.triggerTerrainStatBoost(self.item, self, @battle) + pbHeldItemTriggered(self.item) + end + end + + # Used for Adrenaline Orb. Called when Intimidate is triggered (even if + # Intimidate has no effect on the Pokémon). + def pbItemOnIntimidatedCheck + return if !itemActive? + if Battle::ItemEffects.triggerOnIntimidated(self.item, self, @battle) + pbHeldItemTriggered(self.item) + end + end + + # Used for Eject Pack. Returns whether self has switched out. + def pbItemOnStatDropped(move_user = nil) + return false if !@statsDropped + return false if !itemActive? + return Battle::ItemEffects.triggerOnStatLoss(self.item, self, move_user, @battle) + end + + def pbItemsOnUnnerveEnding + @battle.pbPriority(true).each do |b| + b.pbHeldItemTriggerCheck if b.item&.is_berry? + end + end + + #============================================================================= + # Item effects + #============================================================================= + def pbConfusionBerry(item_to_use, forced, flavor, confuse_msg) + return false if !forced && !canHeal? + return false if !forced && !canConsumePinchBerry?(Settings::MECHANICS_GENERATION >= 7) + used_item_name = GameData::Item.get(item_to_use).name + fraction_to_heal = 8 # Gens 6 and lower + if Settings::MECHANICS_GENERATION == 7 + fraction_to_heal = 2 + elsif Settings::MECHANICS_GENERATION >= 8 + fraction_to_heal = 3 + end + amt = @totalhp / fraction_to_heal + ripening = false + if hasActiveAbility?(:RIPEN) + @battle.pbShowAbilitySplash(self, forced) + amt *= 2 + ripening = true + end + @battle.pbCommonAnimation("EatBerry", self) if !forced + @battle.pbHideAbilitySplash(self) if ripening + amt = pbRecoverHP(amt) + if amt > 0 + if forced + PBDebug.log("[Item triggered] Forced consuming of #{used_item_name}") + @battle.pbDisplay(_INTL("{1}'s HP was restored.", pbThis)) + else + @battle.pbDisplay(_INTL("{1} restored its health using its {2}!", pbThis, used_item_name)) + end + end + flavor_stat = [:ATTACK, :DEFENSE, :SPEED, :SPECIAL_ATTACK, :SPECIAL_DEFENSE][flavor] + self.nature.stat_changes.each do |change| + next if change[1] > 0 || change[0] != flavor_stat + @battle.pbDisplay(confuse_msg) + pbConfuse if pbCanConfuseSelf?(false) + break + end + return true + end + + def pbStatIncreasingBerry(item_to_use, forced, stat, increment = 1) + return false if !forced && !canConsumePinchBerry? + return false if !pbCanRaiseStatStage?(stat, self) + used_item_name = GameData::Item.get(item_to_use).name + ripening = false + if hasActiveAbility?(:RIPEN) + @battle.pbShowAbilitySplash(self, forced) + increment *= 2 + ripening = true + end + @battle.pbCommonAnimation("EatBerry", self) if !forced + @battle.pbHideAbilitySplash(self) if ripening + return pbRaiseStatStageByCause(stat, increment, self, used_item_name) if !forced + PBDebug.log("[Item triggered] Forced consuming of #{used_item_name}") + return pbRaiseStatStage(stat, increment, self) + end + + def pbMoveTypeWeakeningBerry(berry_type, move_type, mults) + return if move_type != berry_type + return if !Effectiveness.super_effective?(@damageState.typeMod) && move_type != :NORMAL + mults[:final_damage_multiplier] /= 2 + @damageState.berryWeakened = true + ripening = false + if hasActiveAbility?(:RIPEN) + @battle.pbShowAbilitySplash(self) + mults[:final_damage_multiplier] /= 2 + ripening = true + end + @battle.pbCommonAnimation("EatBerry", self) + @battle.pbHideAbilitySplash(self) if ripening + end + + def pbMoveTypePoweringUpGem(gem_type, move, move_type, mults) + return if move.is_a?(Battle::Move::PledgeMove) # Pledge moves never consume Gems + return if move_type != gem_type + @effects[PBEffects::GemConsumed] = @item_id + if Settings::MECHANICS_GENERATION >= 6 + mults[:base_damage_multiplier] *= 1.3 + else + mults[:base_damage_multiplier] *= 1.5 + end + end +end diff --git a/Data/Scripts/011_Battle/001_Battler/007_Battler_UseMove.rb b/Data/Scripts/011_Battle/002_Battler/007_Battler_UseMove.rb similarity index 62% rename from Data/Scripts/011_Battle/001_Battler/007_Battler_UseMove.rb rename to Data/Scripts/011_Battle/002_Battler/007_Battler_UseMove.rb index 1fec2b87b9..d737054797 100644 --- a/Data/Scripts/011_Battle/001_Battler/007_Battler_UseMove.rb +++ b/Data/Scripts/011_Battle/002_Battler/007_Battler_UseMove.rb @@ -1,37 +1,37 @@ -class PokeBattle_Battler +class Battle::Battler #============================================================================= # Turn processing #============================================================================= - def pbProcessTurn(choice,tryFlee=true) + def pbProcessTurn(choice, tryFlee = true) return false if fainted? # Wild roaming Pokémon always flee if possible - if tryFlee && @battle.wildBattle? && opposes? && + if tryFlee && wild? && @battle.rules["alwaysflee"] && @battle.pbCanRun?(@index) pbBeginTurn(choice) pbSEPlay("Battle flee") - @battle.pbDisplay(_INTL("{1} fled from battle!",pbThis)) + @battle.pbDisplay(_INTL("{1} fled from battle!", pbThis)) @battle.decision = 3 pbEndTurn(choice) return true end # Shift with the battler next to this one - if choice[0]==:Shift + if choice[0] == :Shift idxOther = -1 case @battle.pbSideSize(@index) when 2 - idxOther = (@index+2)%4 + idxOther = (@index + 2) % 4 when 3 - if @index!=2 && @index!=3 # If not in middle spot already - idxOther = ((@index%2)==0) ? 2 : 3 + if @index != 2 && @index != 3 # If not in middle spot already + idxOther = (@index.even?) ? 2 : 3 end end - if idxOther>=0 - @battle.pbSwapBattlers(@index,idxOther) + if idxOther >= 0 + @battle.pbSwapBattlers(@index, idxOther) case @battle.pbSideSize(@index) when 2 - @battle.pbDisplay(_INTL("{1} moved across!",pbThis)) + @battle.pbDisplay(_INTL("{1} moved across!", pbThis)) when 3 - @battle.pbDisplay(_INTL("{1} moved to the center!",pbThis)) + @battle.pbDisplay(_INTL("{1} moved to the center!", pbThis)) end end pbBeginTurn(choice) @@ -40,7 +40,7 @@ def pbProcessTurn(choice,tryFlee=true) return true end # If this battler's action for this round wasn't "use a move" - if choice[0]!=:UseMove + if choice[0] != :UseMove # Clean up effects that end at battler's turn pbBeginTurn(choice) pbEndTurn(choice) @@ -56,8 +56,8 @@ def pbProcessTurn(choice,tryFlee=true) end # Use the move PBDebug.log("[Move usage] #{pbThis} started using #{choice[2].name}") - PBDebug.logonerr{ - pbUseMove(choice,choice[2]==@battle.struggle) + PBDebug.logonerr { + pbUseMove(choice, choice[2] == @battle.struggle) } @battle.pbJudge # Update priority order @@ -70,14 +70,13 @@ def pbProcessTurn(choice,tryFlee=true) #============================================================================= def pbBeginTurn(_choice) # Cancel some lingering effects which only apply until the user next moves - @effects[PBEffects::BeakBlast] = false @effects[PBEffects::DestinyBondPrevious] = @effects[PBEffects::DestinyBond] @effects[PBEffects::DestinyBond] = false @effects[PBEffects::Grudge] = false @effects[PBEffects::MoveNext] = false @effects[PBEffects::Quash] = 0 # Encore's effect ends if the encored move is no longer available - if @effects[PBEffects::Encore]>0 && pbEncoredMoveIndex<0 + if @effects[PBEffects::Encore] > 0 && pbEncoredMoveIndex < 0 @effects[PBEffects::Encore] = 0 @effects[PBEffects::EncoreMove] = nil end @@ -91,8 +90,8 @@ def pbBeginTurn(_choice) def pbCancelMoves(full_cancel = false) # Outragers get confused anyway if they are disrupted during their final # turn of using the move - if @effects[PBEffects::Outrage]==1 && pbCanConfuseSelf?(false) && !full_cancel - pbConfuse(_INTL("{1} became confused due to fatigue!",pbThis)) + if @effects[PBEffects::Outrage] == 1 && pbCanConfuseSelf?(false) && !full_cancel + pbConfuse(_INTL("{1} became confused due to fatigue!", pbThis)) end # Cancel usage of most multi-turn moves @effects[PBEffects::TwoTurnAttack] = nil @@ -102,13 +101,14 @@ def pbCancelMoves(full_cancel = false) @effects[PBEffects::Bide] = 0 @currentMove = nil # Reset counters for moves which increase them when used in succession - @effects[PBEffects::FuryCutter] = 0 + @effects[PBEffects::FuryCutter] = 0 end def pbEndTurn(_choice) @lastRoundMoved = @battle.turnCount # Done something this round if !@effects[PBEffects::ChoiceBand] && - hasActiveItem?([:CHOICEBAND,:CHOICESPECS,:CHOICESCARF]) + (hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) || + hasActiveAbility?(:GORILLATACTICS)) if @lastMoveUsed && pbHasMove?(@lastMoveUsed) @effects[PBEffects::ChoiceBand] = @lastMoveUsed elsif @lastRegularMoveUsed && pbHasMove?(@lastRegularMoveUsed) @@ -116,25 +116,24 @@ def pbEndTurn(_choice) end end @effects[PBEffects::BeakBlast] = false - @effects[PBEffects::Charge] = 0 if @effects[PBEffects::Charge]==1 + @effects[PBEffects::Charge] = 0 if @effects[PBEffects::Charge] == 1 @effects[PBEffects::GemConsumed] = nil @effects[PBEffects::ShellTrap] = false - @battle.eachBattler { |b| b.pbContinualAbilityChecks } # Trace, end primordial weathers + @battle.allBattlers.each { |b| b.pbContinualAbilityChecks } # Trace, end primordial weathers end def pbConfusionDamage(msg) @damageState.reset - @damageState.initialHP = @hp - confusionMove = PokeBattle_Confusion.new(@battle,nil) + confusionMove = Battle::Move::Confusion.new(@battle, nil) confusionMove.calcType = confusionMove.pbCalcType(self) # nil - @damageState.typeMod = confusionMove.pbCalcTypeMod(confusionMove.calcType,self,self) # 8 - confusionMove.pbCheckDamageAbsorption(self,self) - confusionMove.pbCalcDamage(self,self) - confusionMove.pbReduceDamage(self,self) + @damageState.typeMod = confusionMove.pbCalcTypeMod(confusionMove.calcType, self, self) # 8 + confusionMove.pbCheckDamageAbsorption(self, self) + confusionMove.pbCalcDamage(self, self) + confusionMove.pbReduceDamage(self, self) self.hp -= @damageState.hpLost - confusionMove.pbAnimateHitAndHPLost(self,[self]) + confusionMove.pbAnimateHitAndHPLost(self, [self]) @battle.pbDisplay(msg) # "It hurt itself in its confusion!" - confusionMove.pbRecordDamageLost(self,self) + confusionMove.pbRecordDamageLost(self, self) confusionMove.pbEndureKOMessage(self) pbFaint if fainted? pbItemHPHealCheck @@ -144,43 +143,42 @@ def pbConfusionDamage(msg) # Simple "use move" method, used when a move calls another move and for Future # Sight's attack #============================================================================= - def pbUseMoveSimple(moveID,target=-1,idxMove=-1,specialUsage=true) + def pbUseMoveSimple(moveID, target = -1, idxMove = -1, specialUsage = true) choice = [] choice[0] = :UseMove # "Use move" choice[1] = idxMove # Index of move to be used in user's moveset - if idxMove>=0 + if idxMove >= 0 choice[2] = @moves[idxMove] else - choice[2] = PokeBattle_Move.from_pokemon_move(@battle, Pokemon::Move.new(moveID)) + choice[2] = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(moveID)) choice[2].pp = -1 end choice[3] = target # Target (-1 means no target yet) PBDebug.log("[Move usage] #{pbThis} started using the called/simple move #{choice[2].name}") - pbUseMove(choice,specialUsage) + pbUseMove(choice, specialUsage) end #============================================================================= # Master "use move" method #============================================================================= - def pbUseMove(choice,specialUsage=false) + def pbUseMove(choice, specialUsage = false) # NOTE: This is intentionally determined before a multi-turn attack can # set specialUsage to true. - skipAccuracyCheck = (specialUsage && choice[2]!=@battle.struggle) + skipAccuracyCheck = (specialUsage && choice[2] != @battle.struggle) # Start using the move pbBeginTurn(choice) # Force the use of certain moves if they're already being used if usingMultiTurnAttack? - choice[2] = PokeBattle_Move.from_pokemon_move(@battle, Pokemon::Move.new(@currentMove)) + choice[2] = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@currentMove)) specialUsage = true - elsif @effects[PBEffects::Encore]>0 && choice[1]>=0 && - @battle.pbCanShowCommands?(@index) + elsif @effects[PBEffects::Encore] > 0 && choice[1] >= 0 && + @battle.pbCanShowCommands?(@index) idxEncoredMove = pbEncoredMoveIndex - if idxEncoredMove>=0 && @battle.pbCanChooseMove?(@index,idxEncoredMove,false) - if choice[1]!=idxEncoredMove # Change move if battler was Encored mid-round - choice[1] = idxEncoredMove - choice[2] = @moves[idxEncoredMove] - choice[3] = -1 # No target chosen - end + if idxEncoredMove >= 0 && choice[1] != idxEncoredMove && + @battle.pbCanChooseMove?(@index, idxEncoredMove, false) # Change move if battler was Encored mid-round + choice[1] = idxEncoredMove + choice[2] = @moves[idxEncoredMove] + choice[3] = -1 # No target chosen end end # Labels the move being used as "move" @@ -188,7 +186,7 @@ def pbUseMove(choice,specialUsage=false) return if !move # if move was not chosen somehow # Try to use the move (inc. disobedience) @lastMoveFailed = false - if !pbTryUseMove(choice,move,specialUsage,skipAccuracyCheck) + if !pbTryUseMove(choice, move, specialUsage, skipAccuracyCheck) @lastMoveUsed = nil @lastMoveUsedType = nil if !specialUsage @@ -203,26 +201,24 @@ def pbUseMove(choice,specialUsage=false) move = choice[2] # In case disobedience changed the move to be used return if !move # if move was not chosen somehow # Subtract PP - if !specialUsage - if !pbReducePP(move) - @battle.pbDisplay(_INTL("{1} used {2}!",pbThis,move.name)) - @battle.pbDisplay(_INTL("But there was no PP left for the move!")) - @lastMoveUsed = nil - @lastMoveUsedType = nil - @lastRegularMoveUsed = nil - @lastRegularMoveTarget = -1 - @lastMoveFailed = true - pbCancelMoves - pbEndTurn(choice) - return - end + if !specialUsage && !pbReducePP(move) + @battle.pbDisplay(_INTL("{1} used {2}!", pbThis, move.name)) + @battle.pbDisplay(_INTL("But there was no PP left for the move!")) + @lastMoveUsed = nil + @lastMoveUsedType = nil + @lastRegularMoveUsed = nil + @lastRegularMoveTarget = -1 + @lastMoveFailed = true + pbCancelMoves + pbEndTurn(choice) + return end # Stance Change if isSpecies?(:AEGISLASH) && self.ability == :STANCECHANGE if move.damagingMove? - pbChangeForm(1,_INTL("{1} changed to Blade Forme!",pbThis)) + pbChangeForm(1, _INTL("{1} changed to Blade Forme!", pbThis)) elsif move.id == :KINGSSHIELD - pbChangeForm(0,_INTL("{1} changed to Shield Forme!",pbThis)) + pbChangeForm(0, _INTL("{1} changed to Shield Forme!", pbThis)) end end # Calculate the move's type during this usage @@ -238,10 +234,10 @@ def pbUseMove(choice,specialUsage=false) @effects[PBEffects::TwoTurnAttack] = nil # Cancel use of two-turn attack end # Add to counters for moves which increase them when used in succession - move.pbChangeUsageCounters(self,specialUsage) + move.pbChangeUsageCounters(self, specialUsage) # Charge up Metronome item if hasActiveItem?(:METRONOME) && !move.callsAnotherMove? - if @lastMoveUsed && @lastMoveUsed==move.id && !@lastMoveFailed + if @lastMoveUsed && @lastMoveUsed == move.id && !@lastMoveFailed @effects[PBEffects::Metronome] += 1 else @effects[PBEffects::Metronome] = 0 @@ -259,10 +255,10 @@ def pbUseMove(choice,specialUsage=false) @battle.lastMoveUser = @index # For "self KO" battle clause to avoid draws @battle.successStates[@index].useState = 1 # Battle Arena - assume failure # Find the default user (self or Snatcher) and target(s) - user = pbFindUser(choice,move) - user = pbChangeUser(choice,move,user) - targets = pbFindTargets(choice,move,user) - targets = pbChangeTargets(move,user,targets) + user = pbFindUser(choice, move) + user = pbChangeUser(choice, move, user) + targets = pbFindTargets(choice, move, user) + targets = pbChangeTargets(move, user, targets) # Pressure if !specialUsage targets.each do |b| @@ -271,7 +267,7 @@ def pbUseMove(choice,specialUsage=false) user.pbReducePP(move) end if move.pbTarget(user).affects_foe_side - @battle.eachOtherSideBattler(user) do |b| + @battle.allOtherSideBattlers(user).each do |b| next unless b.hasActiveAbility?(:PRESSURE) PBDebug.log("[Ability triggered] #{b.pbThis}'s #{b.abilityName}") user.pbReducePP(move) @@ -281,10 +277,10 @@ def pbUseMove(choice,specialUsage=false) # Dazzling/Queenly Majesty make the move fail here @battle.pbPriority(true).each do |b| next if !b || !b.abilityActive? - if BattleHandlers.triggerMoveBlockingAbility(b.ability,b,user,targets,move,@battle) - @battle.pbDisplayBrief(_INTL("{1} used {2}!",user.pbThis,move.name)) + if Battle::AbilityEffects.triggerMoveBlocking(b.ability, b, user, targets, move, @battle) + @battle.pbDisplayBrief(_INTL("{1} used {2}!", user.pbThis, move.name)) @battle.pbShowAbilitySplash(b) - @battle.pbDisplay(_INTL("{1} cannot use {2}!",user.pbThis,move.name)) + @battle.pbDisplay(_INTL("{1} cannot use {2}!", user.pbThis, move.name)) @battle.pbHideAbilitySplash(b) user.lastMoveFailed = true pbCancelMoves @@ -301,11 +297,11 @@ def pbUseMove(choice,specialUsage=false) # Snatch's message (user is the new user, self is the original user) if move.snatched @lastMoveFailed = true # Intentionally applies to self, not user - @battle.pbDisplay(_INTL("{1} snatched {2}'s move!",user.pbThis,pbThis(true))) + @battle.pbDisplay(_INTL("{1} snatched {2}'s move!", user.pbThis, pbThis(true))) end # "But it failed!" checks - if move.pbMoveFailed?(user,targets) - PBDebug.log(sprintf("[Move failed] In function code %s's def pbMoveFailed?",move.function)) + if move.pbMoveFailed?(user, targets) + PBDebug.log(sprintf("[Move failed] In function code %s's def pbMoveFailed?", move.function)) user.lastMoveFailed = true pbCancelMoves pbEndTurn(choice) @@ -313,26 +309,22 @@ def pbUseMove(choice,specialUsage=false) end # Perform set-up actions and display messages # Messages include Magnitude's number and Pledge moves' "it's a combo!" - move.pbOnStartUse(user,targets) + move.pbOnStartUse(user, targets) # Self-thawing due to the move if user.status == :FROZEN && move.thawsUser? user.pbCureStatus(false) - @battle.pbDisplay(_INTL("{1} melted the ice!",user.pbThis)) + @battle.pbDisplay(_INTL("{1} melted the ice!", user.pbThis)) end # Powder if user.effects[PBEffects::Powder] && move.calcType == :FIRE - @battle.pbCommonAnimation("Powder",user) + @battle.pbCommonAnimation("Powder", user) @battle.pbDisplay(_INTL("When the flame touched the powder on the Pokémon, it exploded!")) user.lastMoveFailed = true - if ![:Rain, :HeavyRain].include?(@battle.pbWeather) && user.takesIndirectDamage? - oldHP = user.hp - user.pbReduceHP((user.totalhp/4.0).round,false) - user.pbFaint if user.fainted? + if ![:Rain, :HeavyRain].include?(user.effectiveWeather) && user.takesIndirectDamage? + user.pbTakeEffectDamage((user.totalhp / 4.0).round, false) { |hp_lost| + @battle.pbDisplay(_INTL("{1} is hurt by its {2}!", battler.pbThis, battler.itemName)) + } @battle.pbGainExp # In case user is KO'd by this - user.pbItemHPHealCheck - if user.pbAbilitiesOnDamageTaken(oldHP) - user.pbEffectsOnSwitchIn(true) - end end pbCancelMoves pbEndTurn(choice) @@ -360,21 +352,21 @@ def pbUseMove(choice,specialUsage=false) end end # Protean - if user.hasActiveAbility?(:PROTEAN) && !move.callsAnotherMove? && !move.snatched - if user.pbHasOtherType?(move.calcType) && !GameData::Type.get(move.calcType).pseudo_type - @battle.pbShowAbilitySplash(user) - user.pbChangeTypes(move.calcType) - typeName = GameData::Type.get(move.calcType).name - @battle.pbDisplay(_INTL("{1} transformed into the {2} type!",user.pbThis,typeName)) - @battle.pbHideAbilitySplash(user) - # NOTE: The GF games say that if Curse is used by a non-Ghost-type - # Pokémon which becomes Ghost-type because of Protean, it should - # target and curse itself. I think this is silly, so I'm making it - # choose a random opponent to curse instead. - if move.function=="10D" && targets.length==0 # Curse - choice[3] = -1 - targets = pbFindTargets(choice,move,user) - end + if user.hasActiveAbility?([:LIBERO, :PROTEAN]) && + !move.callsAnotherMove? && !move.snatched && + user.pbHasOtherType?(move.calcType) && !GameData::Type.get(move.calcType).pseudo_type + @battle.pbShowAbilitySplash(user) + user.pbChangeTypes(move.calcType) + typeName = GameData::Type.get(move.calcType).name + @battle.pbDisplay(_INTL("{1}'s type changed to {2}!", user.pbThis, typeName)) + @battle.pbHideAbilitySplash(user) + # NOTE: The GF games say that if Curse is used by a non-Ghost-type + # Pokémon which becomes Ghost-type because of Protean, it should + # target and curse itself. I think this is silly, so I'm making it + # choose a random opponent to curse instead. + if move.function == "CurseTargetOrLowerUserSpd1RaiseUserAtkDef1" && targets.length == 0 + choice[3] = -1 + targets = pbFindTargets(choice, move, user) end end #--------------------------------------------------------------------------- @@ -388,16 +380,17 @@ def pbUseMove(choice,specialUsage=false) user.lastMoveFailed = true else # We have targets, or move doesn't use targets # Reset whole damage state, perform various success checks (not accuracy) - user.initialHP = user.hp + @battle.allBattlers.each do |b| + b.droppedBelowHalfHP = false + b.statsDropped = false + end targets.each do |b| b.damageState.reset - b.damageState.initialHP = b.hp - if !pbSuccessCheckAgainstTarget(move,user,b) - b.damageState.unaffected = true - end + next if pbSuccessCheckAgainstTarget(move, user, b, targets) + b.damageState.unaffected = true end # Magic Coat/Magic Bounce checks (for moves which don't target Pokémon) - if targets.length==0 && move.canMagicCoat? + if targets.length == 0 && move.statusMove? && move.canMagicCoat? @battle.pbPriority(true).each do |b| next if b.fainted? || !b.opposes?(user) next if b.semiInvulnerable? @@ -406,7 +399,7 @@ def pbUseMove(choice,specialUsage=false) b.effects[PBEffects::MagicCoat] = false break elsif b.hasActiveAbility?(:MAGICBOUNCE) && !@battle.moldBreaker && - !b.effects[PBEffects::MagicBounce] + !b.effects[PBEffects::MagicBounce] magicBouncer = b.index b.effects[PBEffects::MagicBounce] = true break @@ -414,14 +407,14 @@ def pbUseMove(choice,specialUsage=false) end end # Get the number of hits - numHits = move.pbNumHits(user,targets) + numHits = move.pbNumHits(user, targets) # Process each hit in turn realNumHits = 0 - for i in 0...numHits - break if magicCoater>=0 || magicBouncer>=0 - success = pbProcessMoveHit(move,user,targets,i,skipAccuracyCheck) + numHits.times do |i| + break if magicCoater >= 0 || magicBouncer >= 0 + success = pbProcessMoveHit(move, user, targets, i, skipAccuracyCheck) if !success - if i==0 && targets.length>0 + if i == 0 && targets.length > 0 hasFailed = false targets.each do |t| next if t.damageState.protected @@ -438,11 +431,11 @@ def pbUseMove(choice,specialUsage=false) # NOTE: If a multi-hit move becomes disabled partway through doing those # hits (e.g. by Cursed Body), the rest of the hits continue as # normal. - break if !targets.any? { |t| !t.fainted? } # All targets are fainted + break if targets.none? { |t| !t.fainted? } # All targets are fainted end # Battle Arena only - attack is successful @battle.successStates[user.index].useState = 2 - if targets.length>0 + if targets.length > 0 @battle.successStates[user.index].typeMod = 0 targets.each do |b| next if b.damageState.unaffected @@ -452,17 +445,17 @@ def pbUseMove(choice,specialUsage=false) # Effectiveness message for multi-hit moves # NOTE: No move is both multi-hit and multi-target, and the messages below # aren't quite right for such a hypothetical move. - if numHits>1 + if numHits > 1 if move.damagingMove? targets.each do |b| next if b.damageState.unaffected || b.damageState.substitute - move.pbEffectivenessMessage(user,b,targets.length) + move.pbEffectivenessMessage(user, b, targets.length) end end - if realNumHits==1 + if realNumHits == 1 @battle.pbDisplay(_INTL("Hit 1 time!")) - elsif realNumHits>1 - @battle.pbDisplay(_INTL("Hit {1} times!",realNumHits)) + elsif realNumHits > 1 + @battle.pbDisplay(_INTL("Hit {1} times!", realNumHits)) end end # Magic Coat's bouncing back (move has targets) @@ -470,102 +463,124 @@ def pbUseMove(choice,specialUsage=false) next if b.fainted? next if !b.damageState.magicCoat && !b.damageState.magicBounce @battle.pbShowAbilitySplash(b) if b.damageState.magicBounce - @battle.pbDisplay(_INTL("{1} bounced the {2} back!",b.pbThis,move.name)) + @battle.pbDisplay(_INTL("{1} bounced the {2} back!", b.pbThis, move.name)) @battle.pbHideAbilitySplash(b) if b.damageState.magicBounce newChoice = choice.clone newChoice[3] = user.index - newTargets = pbFindTargets(newChoice,move,b) - newTargets = pbChangeTargets(move,b,newTargets) - success = pbProcessMoveHit(move,b,newTargets,0,false) + newTargets = pbFindTargets(newChoice, move, b) + newTargets = pbChangeTargets(move, b, newTargets) + success = false + if !move.pbMoveFailed?(b, newTargets) + newTargets.each_with_index do |newTarget, idx| + if pbSuccessCheckAgainstTarget(move, b, newTarget, newTargets) + success = true + next + end + newTargets[idx] = nil + end + newTargets.compact! + end + pbProcessMoveHit(move, b, newTargets, 0, false) if success b.lastMoveFailed = true if !success - targets.each { |otherB| otherB.pbFaint if otherB && otherB.fainted? } + targets.each { |otherB| otherB.pbFaint if otherB&.fainted? } user.pbFaint if user.fainted? end # Magic Coat's bouncing back (move has no targets) - if magicCoater>=0 || magicBouncer>=0 - mc = @battle.battlers[(magicCoater>=0) ? magicCoater : magicBouncer] + if magicCoater >= 0 || magicBouncer >= 0 + mc = @battle.battlers[(magicCoater >= 0) ? magicCoater : magicBouncer] if !mc.fainted? user.lastMoveFailed = true - @battle.pbShowAbilitySplash(mc) if magicBouncer>=0 - @battle.pbDisplay(_INTL("{1} bounced the {2} back!",mc.pbThis,move.name)) - @battle.pbHideAbilitySplash(mc) if magicBouncer>=0 - success = pbProcessMoveHit(move,mc,[],0,false) + @battle.pbShowAbilitySplash(mc) if magicBouncer >= 0 + @battle.pbDisplay(_INTL("{1} bounced the {2} back!", mc.pbThis, move.name)) + @battle.pbHideAbilitySplash(mc) if magicBouncer >= 0 + success = false + if !move.pbMoveFailed?(mc, []) + success = pbProcessMoveHit(move, mc, [], 0, false) + end mc.lastMoveFailed = true if !success - targets.each { |b| b.pbFaint if b && b.fainted? } + targets.each { |b| b.pbFaint if b&.fainted? } user.pbFaint if user.fainted? end end # Move-specific effects after all hits - targets.each { |b| move.pbEffectAfterAllHits(user,b) } + targets.each { |b| move.pbEffectAfterAllHits(user, b) } # Faint if 0 HP - targets.each { |b| b.pbFaint if b && b.fainted? } + targets.each { |b| b.pbFaint if b&.fainted? } user.pbFaint if user.fainted? # External/general effects after all hits. Eject Button, Shell Bell, etc. - pbEffectsAfterMove(user,targets,move,realNumHits) + pbEffectsAfterMove(user, targets, move, realNumHits) + @battle.allBattlers.each do |b| + b.droppedBelowHalfHP = false + b.statsDropped = false + end end # End effect of Mold Breaker @battle.moldBreaker = false # Gain Exp @battle.pbGainExp # Battle Arena only - update skills - @battle.eachBattler { |b| @battle.successStates[b.index].updateSkill } + @battle.allBattlers.each { |b| @battle.successStates[b.index].updateSkill } # Shadow Pokémon triggering Hyper Mode - pbHyperMode if @battle.choices[@index][0]!=:None # Not if self is replaced + pbHyperMode if @battle.choices[@index][0] != :None # Not if self is replaced # End of move usage pbEndTurn(choice) # Instruct - @battle.eachBattler do |b| + @battle.allBattlers.each do |b| next if !b.effects[PBEffects::Instruct] || !b.lastMoveUsed b.effects[PBEffects::Instruct] = false idxMove = -1 - b.eachMoveWithIndex { |m,i| idxMove = i if m.id==b.lastMoveUsed } - next if idxMove<0 + b.eachMoveWithIndex { |m, i| idxMove = i if m.id == b.lastMoveUsed } + next if idxMove < 0 oldLastRoundMoved = b.lastRoundMoved - @battle.pbDisplay(_INTL("{1} used the move instructed by {2}!",b.pbThis,user.pbThis(true))) - PBDebug.logonerr{ - b.effects[PBEffects::Instructed] = true - b.pbUseMoveSimple(b.lastMoveUsed,b.lastRegularMoveTarget,idxMove,false) - b.effects[PBEffects::Instructed] = false - } - b.lastRoundMoved = oldLastRoundMoved - @battle.pbJudge - return if @battle.decision>0 + @battle.pbDisplay(_INTL("{1} used the move instructed by {2}!", b.pbThis, user.pbThis(true))) + b.effects[PBEffects::Instructed] = true + if b.pbCanChooseMove?(@moves[idxMove], false) + PBDebug.logonerr { + b.pbUseMoveSimple(b.lastMoveUsed, b.lastRegularMoveTarget, idxMove, false) + } + b.lastRoundMoved = oldLastRoundMoved + @battle.pbJudge + return if @battle.decision > 0 + end + b.effects[PBEffects::Instructed] = false end # Dancer - if !@effects[PBEffects::Dancer] && !user.lastMoveFailed && realNumHits>0 && - !move.snatched && magicCoater<0 && @battle.pbCheckGlobalAbility(:DANCER) && + if !@effects[PBEffects::Dancer] && !user.lastMoveFailed && realNumHits > 0 && + !move.snatched && magicCoater < 0 && @battle.pbCheckGlobalAbility(:DANCER) && move.danceMove? dancers = [] @battle.pbPriority(true).each do |b| - dancers.push(b) if b.index!=user.index && b.hasActiveAbility?(:DANCER) + dancers.push(b) if b.index != user.index && b.hasActiveAbility?(:DANCER) end - while dancers.length>0 + while dancers.length > 0 nextUser = dancers.pop oldLastRoundMoved = nextUser.lastRoundMoved # NOTE: Petal Dance being used because of Dancer shouldn't lock the # Dancer into using that move, and shouldn't contribute to its # turn counter if it's already locked into Petal Dance. oldOutrage = nextUser.effects[PBEffects::Outrage] - nextUser.effects[PBEffects::Outrage] += 1 if nextUser.effects[PBEffects::Outrage]>0 + nextUser.effects[PBEffects::Outrage] += 1 if nextUser.effects[PBEffects::Outrage] > 0 oldCurrentMove = nextUser.currentMove preTarget = choice[3] preTarget = user.index if nextUser.opposes?(user) || !nextUser.opposes?(preTarget) - @battle.pbShowAbilitySplash(nextUser,true) + @battle.pbShowAbilitySplash(nextUser, true) @battle.pbHideAbilitySplash(nextUser) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH + if !Battle::Scene::USE_ABILITY_SPLASH @battle.pbDisplay(_INTL("{1} kept the dance going with {2}!", - nextUser.pbThis,nextUser.abilityName)) + nextUser.pbThis, nextUser.abilityName)) end - PBDebug.logonerr{ - nextUser.effects[PBEffects::Dancer] = true - nextUser.pbUseMoveSimple(move.id,preTarget) - nextUser.effects[PBEffects::Dancer] = false - } - nextUser.lastRoundMoved = oldLastRoundMoved - nextUser.effects[PBEffects::Outrage] = oldOutrage - nextUser.currentMove = oldCurrentMove - @battle.pbJudge - return if @battle.decision>0 + nextUser.effects[PBEffects::Dancer] = true + if nextUser.pbCanChooseMove?(move, false) + PBDebug.logonerr { + nextUser.pbUseMoveSimple(move.id, preTarget) + } + nextUser.lastRoundMoved = oldLastRoundMoved + nextUser.effects[PBEffects::Outrage] = oldOutrage + nextUser.currentMove = oldCurrentMove + @battle.pbJudge + return if @battle.decision > 0 + end + nextUser.effects[PBEffects::Dancer] = false end end end @@ -573,19 +588,19 @@ def pbUseMove(choice,specialUsage=false) #============================================================================= # Attack a single target #============================================================================= - def pbProcessMoveHit(move,user,targets,hitNum,skipAccuracyCheck) + def pbProcessMoveHit(move, user, targets, hitNum, skipAccuracyCheck) return false if user.fainted? # For two-turn attacks being used in a single turn - move.pbInitialEffect(user,targets,hitNum) + move.pbInitialEffect(user, targets, hitNum) numTargets = 0 # Number of targets that are affected by this hit - targets.each { |b| b.damageState.resetPerHit } # Count a hit for Parental Bond (if it applies) - user.effects[PBEffects::ParentalBond] -= 1 if user.effects[PBEffects::ParentalBond]>0 + user.effects[PBEffects::ParentalBond] -= 1 if user.effects[PBEffects::ParentalBond] > 0 # Accuracy check (accuracy/evasion calc) - if hitNum==0 || move.successCheckPerHit? + if hitNum == 0 || move.successCheckPerHit? targets.each do |b| + b.damageState.missed = false next if b.damageState.unaffected - if pbSuccessCheckPerHit(move,user,b,skipAccuracyCheck) + if pbSuccessCheckPerHit(move, user, b, skipAccuracyCheck) numTargets += 1 else b.damageState.missed = true @@ -593,10 +608,14 @@ def pbProcessMoveHit(move,user,targets,hitNum,skipAccuracyCheck) end end # If failed against all targets - if targets.length>0 && numTargets==0 && !move.worksWithNoTargets? + if targets.length > 0 && numTargets == 0 && !move.worksWithNoTargets? targets.each do |b| next if !b.damageState.missed || b.damageState.magicCoat - pbMissMessage(move,user,b) + pbMissMessage(move, user, b) + if user.itemActive? + Battle::ItemEffects.triggerOnMissingTarget(user.item, user, b, move, hitNum, @battle) + end + break if move.pbRepeatHit? # Dragon Darts only shows one failure message end move.pbCrashDamage(user) user.pbItemHPHealCheck @@ -605,69 +624,74 @@ def pbProcessMoveHit(move,user,targets,hitNum,skipAccuracyCheck) end end # If we get here, this hit will happen and do something + all_targets = targets + targets = move.pbDesignateTargetsForHit(targets, hitNum) # For Dragon Darts + targets.each { |b| b.damageState.resetPerHit } #--------------------------------------------------------------------------- # Calculate damage to deal if move.pbDamagingMove? targets.each do |b| next if b.damageState.unaffected # Check whether Substitute/Disguise will absorb the damage - move.pbCheckDamageAbsorption(user,b) + move.pbCheckDamageAbsorption(user, b) # Calculate the damage against b # pbCalcDamage shows the "eat berry" animation for SE-weakening # berries, although the message about it comes after the additional # effect below - move.pbCalcDamage(user,b,targets.length) # Stored in damageState.calcDamage + move.pbCalcDamage(user, b, targets.length) # Stored in damageState.calcDamage # Lessen damage dealt because of False Swipe/Endure/etc. - move.pbReduceDamage(user,b) # Stored in damageState.hpLost + move.pbReduceDamage(user, b) # Stored in damageState.hpLost end end # Show move animation (for this hit) - move.pbShowAnimation(move.id,user,targets,hitNum) + move.pbShowAnimation(move.id, user, targets, hitNum) # Type-boosting Gem consume animation/message - if user.effects[PBEffects::GemConsumed] && hitNum==0 + if user.effects[PBEffects::GemConsumed] && hitNum == 0 # NOTE: The consume animation and message for Gems are shown now, but the # actual removal of the item happens in def pbEffectsAfterMove. - @battle.pbCommonAnimation("UseItem",user) + @battle.pbCommonAnimation("UseItem", user) @battle.pbDisplay(_INTL("The {1} strengthened {2}'s power!", - GameData::Item.get(user.effects[PBEffects::GemConsumed]).name,move.name)) + GameData::Item.get(user.effects[PBEffects::GemConsumed]).name, move.name)) end # Messages about missed target(s) (relevant for multi-target moves only) - targets.each do |b| - next if !b.damageState.missed - pbMissMessage(move,user,b) + if !move.pbRepeatHit? + targets.each do |b| + next if !b.damageState.missed + pbMissMessage(move, user, b) + if user.itemActive? + Battle::ItemEffects.triggerOnMissingTarget(user.item, user, b, move, hitNum, @battle) + end + end end # Deal the damage (to all allies first simultaneously, then all foes # simultaneously) if move.pbDamagingMove? # This just changes the HP amounts and does nothing else - targets.each do |b| - next if b.damageState.unaffected - move.pbInflictHPDamage(b) - end + targets.each { |b| move.pbInflictHPDamage(b) if !b.damageState.unaffected } # Animate the hit flashing and HP bar changes - move.pbAnimateHitAndHPLost(user,targets) + move.pbAnimateHitAndHPLost(user, targets) end # Self-Destruct/Explosion's damaging and fainting of user - move.pbSelfKO(user) if hitNum==0 + move.pbSelfKO(user) if hitNum == 0 user.pbFaint if user.fainted? if move.pbDamagingMove? targets.each do |b| next if b.damageState.unaffected - # NOTE: This method is also used for the OKHO special message. - move.pbHitEffectivenessMessages(user,b,targets.length) + # NOTE: This method is also used for the OHKO special message. + move.pbHitEffectivenessMessages(user, b, targets.length) # Record data about the hit for various effects' purposes - move.pbRecordDamageLost(user,b) + move.pbRecordDamageLost(user, b) end # Close Combat/Superpower's stat-lowering, Flame Burst's splash damage, # and Incinerate's berry destruction targets.each do |b| next if b.damageState.unaffected - move.pbEffectWhenDealingDamage(user,b) + move.pbEffectWhenDealingDamage(user, b) end # Ability/item effects such as Static/Rocky Helmet, and Grudge, etc. targets.each do |b| next if b.damageState.unaffected - pbEffectsOnMakingHit(move,user,b) + pbEffectsOnMakingHit(move, user, b) end # Disguise/Endure/Sturdy/Focus Sash/Focus Band messages targets.each do |b| @@ -679,35 +703,35 @@ def pbProcessMoveHit(move,user,targets,hitNum,skipAccuracyCheck) @battle.pbPriority(true).each { |b| b.pbItemHPHealCheck } # Animate battlers fainting (checks all battlers rather than just targets # because Flame Burst's splash damage affects non-targets) - @battle.pbPriority(true).each { |b| b.pbFaint if b && b.fainted? } + @battle.pbPriority(true).each { |b| b.pbFaint if b&.fainted? } end - @battle.pbJudgeCheckpoint(user,move) + @battle.pbJudgeCheckpoint(user, move) # Main effect (recoil/drain, etc.) targets.each do |b| next if b.damageState.unaffected - move.pbEffectAgainstTarget(user,b) + move.pbEffectAgainstTarget(user, b) end move.pbEffectGeneral(user) - targets.each { |b| b.pbFaint if b && b.fainted? } + targets.each { |b| b.pbFaint if b&.fainted? } user.pbFaint if user.fainted? # Additional effect if !user.hasActiveAbility?(:SHEERFORCE) targets.each do |b| - next if b.damageState.calcDamage==0 - chance = move.pbAdditionalEffectChance(user,b) - next if chance<=0 - if @battle.pbRandom(100)= strength + next if b.effects[PBEffects::SkyDrop] >= 0 + newUser = b + strength = b.effects[PBEffects::Snatch] + end + if newUser + user = newUser + user.effects[PBEffects::Snatch] = 0 + move.snatched = true + @battle.moldBreaker = user.hasMoldBreaker? + choice[3] = -1 # Clear pre-chosen target + end + end + return user + end + + #============================================================================= + # Get move's default target(s) + #============================================================================= + def pbFindTargets(choice, move, user) + preTarget = choice[3] # A target that was already chosen + targets = [] + # Get list of targets + case move.pbTarget(user).id # Curse can change its target type + when :NearAlly + targetBattler = (preTarget >= 0) ? @battle.battlers[preTarget] : nil + if !pbAddTarget(targets, user, targetBattler, move) + pbAddTargetRandomAlly(targets, user, move) + end + when :UserOrNearAlly + targetBattler = (preTarget >= 0) ? @battle.battlers[preTarget] : nil + if !pbAddTarget(targets, user, targetBattler, move, true, true) + pbAddTarget(targets, user, user, move, true, true) + end + when :AllAllies + @battle.allSameSideBattlers(user.index).each do |b| + pbAddTarget(targets, user, b, move, false, true) if b.index != user.index + end + when :UserAndAllies + pbAddTarget(targets, user, user, move, true, true) + @battle.allSameSideBattlers(user.index).each { |b| pbAddTarget(targets, user, b, move, false, true) } + when :NearFoe, :NearOther + targetBattler = (preTarget >= 0) ? @battle.battlers[preTarget] : nil + if !pbAddTarget(targets, user, targetBattler, move) + if preTarget >= 0 && !user.opposes?(preTarget) + pbAddTargetRandomAlly(targets, user, move) + else + pbAddTargetRandomFoe(targets, user, move) + end + end + when :RandomNearFoe + pbAddTargetRandomFoe(targets, user, move) + when :AllNearFoes + @battle.allOtherSideBattlers(user.index).each { |b| pbAddTarget(targets, user, b, move) } + when :Foe, :Other + targetBattler = (preTarget >= 0) ? @battle.battlers[preTarget] : nil + if !pbAddTarget(targets, user, targetBattler, move, false) + if preTarget >= 0 && !user.opposes?(preTarget) + pbAddTargetRandomAlly(targets, user, move, false) + else + pbAddTargetRandomFoe(targets, user, move, false) + end + end + when :AllFoes + @battle.allOtherSideBattlers(user.index).each { |b| pbAddTarget(targets, user, b, move, false) } + when :AllNearOthers + @battle.allBattlers.each { |b| pbAddTarget(targets, user, b, move) } + when :AllBattlers + @battle.allBattlers.each { |b| pbAddTarget(targets, user, b, move, false, true) } + else + # Used by Counter/Mirror Coat/Metal Burst/Bide + move.pbAddTarget(targets, user) # Move-specific pbAddTarget, not the def below + end + return targets + end + + #============================================================================= + # Redirect attack to another target + #============================================================================= + def pbChangeTargets(move, user, targets) + target_data = move.pbTarget(user) + return targets if @battle.switching # For Pursuit interrupting a switch + return targets if move.cannotRedirect? || move.targetsPosition? + return targets if !target_data.can_target_one_foe? || targets.length != 1 + move.pbModifyTargets(targets, user) # For Dragon Darts + return targets if user.hasActiveAbility?([:PROPELLERTAIL, :STALWART]) + priority = @battle.pbPriority(true) + nearOnly = !target_data.can_choose_distant_target? + # Spotlight (takes priority over Follow Me/Rage Powder/Lightning Rod/Storm Drain) + newTarget = nil + strength = 100 # Lower strength takes priority + priority.each do |b| + next if b.fainted? || b.effects[PBEffects::SkyDrop] >= 0 + next if b.effects[PBEffects::Spotlight] == 0 || + b.effects[PBEffects::Spotlight] >= strength + next if !b.opposes?(user) + next if nearOnly && !b.near?(user) + newTarget = b + strength = b.effects[PBEffects::Spotlight] + end + if newTarget + PBDebug.log("[Move target changed] #{newTarget.pbThis}'s Spotlight made it the target") + targets = [] + pbAddTarget(targets, user, newTarget, move, nearOnly) + return targets + end + # Follow Me/Rage Powder (takes priority over Lightning Rod/Storm Drain) + newTarget = nil + strength = 100 # Lower strength takes priority + priority.each do |b| + next if b.fainted? || b.effects[PBEffects::SkyDrop] >= 0 + next if b.effects[PBEffects::RagePowder] && !user.affectedByPowder? + next if b.effects[PBEffects::FollowMe] == 0 || + b.effects[PBEffects::FollowMe] >= strength + next if !b.opposes?(user) + next if nearOnly && !b.near?(user) + newTarget = b + strength = b.effects[PBEffects::FollowMe] + end + if newTarget + PBDebug.log("[Move target changed] #{newTarget.pbThis}'s Follow Me/Rage Powder made it the target") + targets = [] + pbAddTarget(targets, user, newTarget, move, nearOnly) + return targets + end + # Lightning Rod + targets = pbChangeTargetByAbility(:LIGHTNINGROD, :ELECTRIC, move, user, targets, priority, nearOnly) + # Storm Drain + targets = pbChangeTargetByAbility(:STORMDRAIN, :WATER, move, user, targets, priority, nearOnly) + return targets + end + + def pbChangeTargetByAbility(drawingAbility, drawnType, move, user, targets, priority, nearOnly) + return targets if move.calcType != drawnType + return targets if targets[0].hasActiveAbility?(drawingAbility) + priority.each do |b| + next if b.index == user.index || b.index == targets[0].index + next if !b.hasActiveAbility?(drawingAbility) + next if nearOnly && !b.near?(user) + @battle.pbShowAbilitySplash(b) + targets.clear + pbAddTarget(targets, user, b, move, nearOnly) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1} took the attack!", b.pbThis)) + else + @battle.pbDisplay(_INTL("{1} took the attack with its {2}!", b.pbThis, b.abilityName)) + end + @battle.pbHideAbilitySplash(b) + break + end + return targets + end + + #============================================================================= + # Register target + #============================================================================= + def pbAddTarget(targets, user, target, move, nearOnly = true, allowUser = false) + return false if !target || (target.fainted? && !move.targetsPosition?) + return false if !allowUser && target == user + return false if nearOnly && !user.near?(target) && target != user + targets.each { |b| return true if b.index == target.index } # Already added + targets.push(target) + return true + end + + def pbAddTargetRandomAlly(targets, user, move, nearOnly = true) + choices = [] + user.allAllies.each do |b| + next if nearOnly && !user.near?(b) + pbAddTarget(choices, user, b, move, nearOnly) + end + if choices.length > 0 + pbAddTarget(targets, user, choices[@battle.pbRandom(choices.length)], move, nearOnly) + end + end + + def pbAddTargetRandomFoe(targets, user, move, nearOnly = true) + choices = [] + user.allOpposing.each do |b| + next if nearOnly && !user.near?(b) + pbAddTarget(choices, user, b, move, nearOnly) + end + if choices.length > 0 + pbAddTarget(targets, user, choices[@battle.pbRandom(choices.length)], move, nearOnly) + end + end +end diff --git a/Data/Scripts/011_Battle/002_Battler/009_Battler_UseMoveSuccessChecks.rb b/Data/Scripts/011_Battle/002_Battler/009_Battler_UseMoveSuccessChecks.rb new file mode 100644 index 0000000000..b7bc076f74 --- /dev/null +++ b/Data/Scripts/011_Battle/002_Battler/009_Battler_UseMoveSuccessChecks.rb @@ -0,0 +1,578 @@ +class Battle::Battler + #============================================================================= + # Decide whether the trainer is allowed to tell the Pokémon to use the given + # move. Called when choosing a command for the round. + # Also called when processing the Pokémon's action, because these effects also + # prevent Pokémon action. Relevant because these effects can become active + # earlier in the same round (after choosing the command but before using the + # move) or an unusable move may be called by another move such as Metronome. + #============================================================================= + def pbCanChooseMove?(move, commandPhase, showMessages = true, specialUsage = false) + # Disable + if @effects[PBEffects::DisableMove] == move.id && !specialUsage + if showMessages + msg = _INTL("{1}'s {2} is disabled!", pbThis, move.name) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + end + # Heal Block + if @effects[PBEffects::HealBlock] > 0 && move.healingMove? + if showMessages + msg = _INTL("{1} can't use {2} because of Heal Block!", pbThis, move.name) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + end + # Gravity + if @battle.field.effects[PBEffects::Gravity] > 0 && move.unusableInGravity? + if showMessages + msg = _INTL("{1} can't use {2} because of gravity!", pbThis, move.name) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + end + # Throat Chop + if @effects[PBEffects::ThroatChop] > 0 && move.soundMove? + if showMessages + msg = _INTL("{1} can't use {2} because of Throat Chop!", pbThis, move.name) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + end + # Choice Band/Gorilla Tactics + @effects[PBEffects::ChoiceBand] = nil if !pbHasMove?(@effects[PBEffects::ChoiceBand]) + if @effects[PBEffects::ChoiceBand] && move.id != @effects[PBEffects::ChoiceBand] + choiced_move_name = GameData::Move.get(@effects[PBEffects::ChoiceBand]).name + if hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) + if showMessages + msg = _INTL("The {1} only allows the use of {2}!", itemName, choiced_move_name) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + elsif hasActiveAbility?(:GORILLATACTICS) + if showMessages + msg = _INTL("{1} can only use {2}!", pbThis, choiced_move_name) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + end + end + # Taunt + if @effects[PBEffects::Taunt] > 0 && move.statusMove? + if showMessages + msg = _INTL("{1} can't use {2} after the taunt!", pbThis, move.name) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + end + # Torment + if @effects[PBEffects::Torment] && !@effects[PBEffects::Instructed] && + @lastMoveUsed && move.id == @lastMoveUsed && move.id != @battle.struggle.id + if showMessages + msg = _INTL("{1} can't use the same move twice in a row due to the torment!", pbThis) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + end + # Imprison + if @battle.allOtherSideBattlers(@index).any? { |b| b.effects[PBEffects::Imprison] && b.pbHasMove?(move.id) } + if showMessages + msg = _INTL("{1} can't use its sealed {2}!", pbThis, move.name) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + end + # Assault Vest (prevents choosing status moves but doesn't prevent + # executing them) + if hasActiveItem?(:ASSAULTVEST) && move.statusMove? && move.id != :MEFIRST && commandPhase + if showMessages + msg = _INTL("The effects of the {1} prevent status moves from being used!", itemName) + (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) + end + return false + end + # Belch + return false if !move.pbCanChooseMove?(self, commandPhase, showMessages) + return true + end + + #============================================================================= + # Obedience check + #============================================================================= + # Return true if Pokémon continues attacking (although it may have chosen to + # use a different move in disobedience), or false if attack stops. + def pbObedienceCheck?(choice) + return true if usingMultiTurnAttack? + return true if choice[0] != :UseMove + return true if !@battle.internalBattle + return true if !@battle.pbOwnedByPlayer?(@index) + disobedient = false + # Pokémon may be disobedient; calculate if it is + badge_level = 10 * (@battle.pbPlayer.badge_count + 1) + badge_level = GameData::GrowthRate.max_level if @battle.pbPlayer.badge_count >= 8 + if Settings::ANY_HIGH_LEVEL_POKEMON_CAN_DISOBEY || + (Settings::FOREIGN_HIGH_LEVEL_POKEMON_CAN_DISOBEY && @pokemon.foreign?(@battle.pbPlayer)) + if @level > badge_level + a = ((@level + badge_level) * @battle.pbRandom(256) / 256).floor + disobedient |= (a >= badge_level) + end + end + disobedient |= !pbHyperModeObedience(choice[2]) + return true if !disobedient + # Pokémon is disobedient; make it do something else + return pbDisobey(choice, badge_level) + end + + def pbDisobey(choice, badge_level) + move = choice[2] + PBDebug.log("[Disobedience] #{pbThis} disobeyed") + @effects[PBEffects::Rage] = false + # Do nothing if using Snore/Sleep Talk + if @status == :SLEEP && move.usableWhenAsleep? + @battle.pbDisplay(_INTL("{1} ignored orders and kept sleeping!", pbThis)) + return false + end + b = ((@level + badge_level) * @battle.pbRandom(256) / 256).floor + # Use another move + if b < badge_level + @battle.pbDisplay(_INTL("{1} ignored orders!", pbThis)) + return false if !@battle.pbCanShowFightMenu?(@index) + otherMoves = [] + eachMoveWithIndex do |_m, i| + next if i == choice[1] + otherMoves.push(i) if @battle.pbCanChooseMove?(@index, i, false) + end + return false if otherMoves.length == 0 # No other move to use; do nothing + newChoice = otherMoves[@battle.pbRandom(otherMoves.length)] + choice[1] = newChoice + choice[2] = @moves[newChoice] + choice[3] = -1 + return true + end + c = @level - badge_level + r = @battle.pbRandom(256) + # Fall asleep + if r < c && pbCanSleep?(self, false) + pbSleepSelf(_INTL("{1} began to nap!", pbThis)) + return false + end + # Hurt self in confusion + r -= c + if r < c && @status != :SLEEP + pbConfusionDamage(_INTL("{1} won't obey! It hurt itself in its confusion!", pbThis)) + return false + end + # Show refusal message and do nothing + case @battle.pbRandom(4) + when 0 then @battle.pbDisplay(_INTL("{1} won't obey!", pbThis)) + when 1 then @battle.pbDisplay(_INTL("{1} turned away!", pbThis)) + when 2 then @battle.pbDisplay(_INTL("{1} is loafing around!", pbThis)) + when 3 then @battle.pbDisplay(_INTL("{1} pretended not to notice!", pbThis)) + end + return false + end + + #============================================================================= + # Check whether the user (self) is able to take action at all. + # If this returns true, and if PP isn't a problem, the move will be considered + # to have been used (even if it then fails for whatever reason). + #============================================================================= + def pbTryUseMove(choice, move, specialUsage, skipAccuracyCheck) + # Check whether it's possible for self to use the given move + # NOTE: Encore has already changed the move being used, no need to have a + # check for it here. + if !pbCanChooseMove?(move, false, true, specialUsage) + @lastMoveFailed = true + return false + end + # Check whether it's possible for self to do anything at all + if @effects[PBEffects::SkyDrop] >= 0 # Intentionally no message here + PBDebug.log("[Move failed] #{pbThis} can't use #{move.name} because of being Sky Dropped") + return false + end + if @effects[PBEffects::HyperBeam] > 0 # Intentionally before Truant + @battle.pbDisplay(_INTL("{1} must recharge!", pbThis)) + return false + end + if choice[1] == -2 # Battle Palace + @battle.pbDisplay(_INTL("{1} appears incapable of using its power!", pbThis)) + return false + end + # Skip checking all applied effects that could make self fail doing something + return true if skipAccuracyCheck + # Check status problems and continue their effects/cure them + case @status + when :SLEEP + self.statusCount -= 1 + if @statusCount <= 0 + pbCureStatus + else + pbContinueStatus + if !move.usableWhenAsleep? # Snore/Sleep Talk + @lastMoveFailed = true + return false + end + end + when :FROZEN + if !move.thawsUser? + if @battle.pbRandom(100) < 20 + pbCureStatus + else + pbContinueStatus + @lastMoveFailed = true + return false + end + end + end + # Obedience check + return false if !pbObedienceCheck?(choice) + # Truant + if hasActiveAbility?(:TRUANT) + @effects[PBEffects::Truant] = !@effects[PBEffects::Truant] + if !@effects[PBEffects::Truant] # True means loafing, but was just inverted + @battle.pbShowAbilitySplash(self) + @battle.pbDisplay(_INTL("{1} is loafing around!", pbThis)) + @lastMoveFailed = true + @battle.pbHideAbilitySplash(self) + return false + end + end + # Flinching + if @effects[PBEffects::Flinch] + @battle.pbDisplay(_INTL("{1} flinched and couldn't move!", pbThis)) + if abilityActive? + Battle::AbilityEffects.triggerOnFlinch(self.ability, self, @battle) + end + @lastMoveFailed = true + return false + end + # Confusion + if @effects[PBEffects::Confusion] > 0 + @effects[PBEffects::Confusion] -= 1 + if @effects[PBEffects::Confusion] <= 0 + pbCureConfusion + @battle.pbDisplay(_INTL("{1} snapped out of its confusion.", pbThis)) + else + @battle.pbCommonAnimation("Confusion", self) + @battle.pbDisplay(_INTL("{1} is confused!", pbThis)) + threshold = (Settings::MECHANICS_GENERATION >= 7) ? 33 : 50 # % chance + if @battle.pbRandom(100) < threshold + pbConfusionDamage(_INTL("It hurt itself in its confusion!")) + @lastMoveFailed = true + return false + end + end + end + # Paralysis + if @status == :PARALYSIS && @battle.pbRandom(100) < 25 + pbContinueStatus + @lastMoveFailed = true + return false + end + # Infatuation + if @effects[PBEffects::Attract] >= 0 + @battle.pbCommonAnimation("Attract", self) + @battle.pbDisplay(_INTL("{1} is in love with {2}!", pbThis, + @battle.battlers[@effects[PBEffects::Attract]].pbThis(true))) + if @battle.pbRandom(100) < 50 + @battle.pbDisplay(_INTL("{1} is immobilized by love!", pbThis)) + @lastMoveFailed = true + return false + end + end + return true + end + + #============================================================================= + # Initial success check against the target. Done once before the first hit. + # Includes move-specific failure conditions, protections and type immunities. + #============================================================================= + def pbSuccessCheckAgainstTarget(move, user, target, targets) + show_message = move.pbShowFailMessages?(targets) + typeMod = move.pbCalcTypeMod(move.calcType, user, target) + target.damageState.typeMod = typeMod + # Two-turn attacks can't fail here in the charging turn + return true if user.effects[PBEffects::TwoTurnAttack] + # Move-specific failures + return false if move.pbFailsAgainstTarget?(user, target, show_message) + # Immunity to priority moves because of Psychic Terrain + if @battle.field.terrain == :Psychic && target.affectedByTerrain? && target.opposes?(user) && + @battle.choices[user.index][4] > 0 # Move priority saved from pbCalculatePriority + @battle.pbDisplay(_INTL("{1} surrounds itself with psychic terrain!", target.pbThis)) if show_message + return false + end + # Crafty Shield + if target.pbOwnSide.effects[PBEffects::CraftyShield] && user.index != target.index && + move.statusMove? && !move.pbTarget(user).targets_all + if show_message + @battle.pbCommonAnimation("CraftyShield", target) + @battle.pbDisplay(_INTL("Crafty Shield protected {1}!", target.pbThis(true))) + end + target.damageState.protected = true + @battle.successStates[user.index].protected = true + return false + end + if !(user.hasActiveAbility?(:UNSEENFIST) && move.contactMove?) + # Wide Guard + if target.pbOwnSide.effects[PBEffects::WideGuard] && user.index != target.index && + move.pbTarget(user).num_targets > 1 && + (Settings::MECHANICS_GENERATION >= 7 || move.damagingMove?) + if show_message + @battle.pbCommonAnimation("WideGuard", target) + @battle.pbDisplay(_INTL("Wide Guard protected {1}!", target.pbThis(true))) + end + target.damageState.protected = true + @battle.successStates[user.index].protected = true + return false + end + if move.canProtectAgainst? + # Quick Guard + if target.pbOwnSide.effects[PBEffects::QuickGuard] && + @battle.choices[user.index][4] > 0 # Move priority saved from pbCalculatePriority + if show_message + @battle.pbCommonAnimation("QuickGuard", target) + @battle.pbDisplay(_INTL("Quick Guard protected {1}!", target.pbThis(true))) + end + target.damageState.protected = true + @battle.successStates[user.index].protected = true + return false + end + # Protect + if target.effects[PBEffects::Protect] + if show_message + @battle.pbCommonAnimation("Protect", target) + @battle.pbDisplay(_INTL("{1} protected itself!", target.pbThis)) + end + target.damageState.protected = true + @battle.successStates[user.index].protected = true + return false + end + # King's Shield + if target.effects[PBEffects::KingsShield] && move.damagingMove? + if show_message + @battle.pbCommonAnimation("KingsShield", target) + @battle.pbDisplay(_INTL("{1} protected itself!", target.pbThis)) + end + target.damageState.protected = true + @battle.successStates[user.index].protected = true + if move.pbContactMove?(user) && user.affectedByContactEffect? && + user.pbCanLowerStatStage?(:ATTACK, target) + user.pbLowerStatStage(:ATTACK, (Settings::MECHANICS_GENERATION >= 8) ? 1 : 2, target) + end + return false + end + # Spiky Shield + if target.effects[PBEffects::SpikyShield] + if show_message + @battle.pbCommonAnimation("SpikyShield", target) + @battle.pbDisplay(_INTL("{1} protected itself!", target.pbThis)) + end + target.damageState.protected = true + @battle.successStates[user.index].protected = true + if move.pbContactMove?(user) && user.affectedByContactEffect? + @battle.scene.pbDamageAnimation(user) + user.pbReduceHP(user.totalhp / 8, false) + @battle.pbDisplay(_INTL("{1} was hurt!", user.pbThis)) + user.pbItemHPHealCheck + end + return false + end + # Baneful Bunker + if target.effects[PBEffects::BanefulBunker] + if show_message + @battle.pbCommonAnimation("BanefulBunker", target) + @battle.pbDisplay(_INTL("{1} protected itself!", target.pbThis)) + end + target.damageState.protected = true + @battle.successStates[user.index].protected = true + if move.pbContactMove?(user) && user.affectedByContactEffect? && + user.pbCanPoison?(target, false) + user.pbPoison(target) + end + return false + end + # Obstruct + if target.effects[PBEffects::Obstruct] && move.damagingMove? + if show_message + @battle.pbCommonAnimation("Obstruct", target) + @battle.pbDisplay(_INTL("{1} protected itself!", target.pbThis)) + end + target.damageState.protected = true + @battle.successStates[user.index].protected = true + if move.pbContactMove?(user) && user.affectedByContactEffect? && + user.pbCanLowerStatStage?(:DEFENSE, target) + user.pbLowerStatStage(:DEFENSE, 2, target) + end + return false + end + # Mat Block + if target.pbOwnSide.effects[PBEffects::MatBlock] && move.damagingMove? + # NOTE: Confirmed no common animation for this effect. + @battle.pbDisplay(_INTL("{1} was blocked by the kicked-up mat!", move.name)) if show_message + target.damageState.protected = true + @battle.successStates[user.index].protected = true + return false + end + end + end + # Magic Coat/Magic Bounce + if move.statusMove? && move.canMagicCoat? && !target.semiInvulnerable? && target.opposes?(user) + if target.effects[PBEffects::MagicCoat] + target.damageState.magicCoat = true + target.effects[PBEffects::MagicCoat] = false + return false + end + if target.hasActiveAbility?(:MAGICBOUNCE) && !@battle.moldBreaker && + !target.effects[PBEffects::MagicBounce] + target.damageState.magicBounce = true + target.effects[PBEffects::MagicBounce] = true + return false + end + end + # Immunity because of ability (intentionally before type immunity check) + return false if move.pbImmunityByAbility(user, target, show_message) + # Type immunity + if move.pbDamagingMove? && Effectiveness.ineffective?(typeMod) + PBDebug.log("[Target immune] #{target.pbThis}'s type immunity") + @battle.pbDisplay(_INTL("It doesn't affect {1}...", target.pbThis(true))) if show_message + return false + end + # Dark-type immunity to moves made faster by Prankster + if Settings::MECHANICS_GENERATION >= 7 && user.effects[PBEffects::Prankster] && + target.pbHasType?(:DARK) && target.opposes?(user) + PBDebug.log("[Target immune] #{target.pbThis} is Dark-type and immune to Prankster-boosted moves") + @battle.pbDisplay(_INTL("It doesn't affect {1}...", target.pbThis(true))) if show_message + return false + end + # Airborne-based immunity to Ground moves + if move.damagingMove? && move.calcType == :GROUND && + target.airborne? && !move.hitsFlyingTargets? + if target.hasActiveAbility?(:LEVITATE) && !@battle.moldBreaker + if show_message + @battle.pbShowAbilitySplash(target) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("{1} avoided the attack!", target.pbThis)) + else + @battle.pbDisplay(_INTL("{1} avoided the attack with {2}!", target.pbThis, target.abilityName)) + end + @battle.pbHideAbilitySplash(target) + end + return false + end + if target.hasActiveItem?(:AIRBALLOON) + @battle.pbDisplay(_INTL("{1}'s {2} makes Ground moves miss!", target.pbThis, target.itemName)) if show_message + return false + end + if target.effects[PBEffects::MagnetRise] > 0 + @battle.pbDisplay(_INTL("{1} makes Ground moves miss with Magnet Rise!", target.pbThis)) if show_message + return false + end + if target.effects[PBEffects::Telekinesis] > 0 + @battle.pbDisplay(_INTL("{1} makes Ground moves miss with Telekinesis!", target.pbThis)) if show_message + return false + end + end + # Immunity to powder-based moves + if move.powderMove? + if target.pbHasType?(:GRASS) && Settings::MORE_TYPE_EFFECTS + PBDebug.log("[Target immune] #{target.pbThis} is Grass-type and immune to powder-based moves") + @battle.pbDisplay(_INTL("It doesn't affect {1}...", target.pbThis(true))) if show_message + return false + end + if Settings::MECHANICS_GENERATION >= 6 + if target.hasActiveAbility?(:OVERCOAT) && !@battle.moldBreaker + if show_message + @battle.pbShowAbilitySplash(target) + if Battle::Scene::USE_ABILITY_SPLASH + @battle.pbDisplay(_INTL("It doesn't affect {1}...", target.pbThis(true))) + else + @battle.pbDisplay(_INTL("It doesn't affect {1} because of its {2}.", target.pbThis(true), target.abilityName)) + end + @battle.pbHideAbilitySplash(target) + end + return false + end + if target.hasActiveItem?(:SAFETYGOGGLES) + PBDebug.log("[Item triggered] #{target.pbThis} has Safety Goggles and is immune to powder-based moves") + @battle.pbDisplay(_INTL("It doesn't affect {1}...", target.pbThis(true))) if show_message + return false + end + end + end + # Substitute + if target.effects[PBEffects::Substitute] > 0 && move.statusMove? && + !move.ignoresSubstitute?(user) && user.index != target.index + PBDebug.log("[Target immune] #{target.pbThis} is protected by its Substitute") + @battle.pbDisplay(_INTL("{1} avoided the attack!", target.pbThis(true))) if show_message + return false + end + return true + end + + #============================================================================= + # Per-hit success check against the target. + # Includes semi-invulnerable move use and accuracy calculation. + #============================================================================= + def pbSuccessCheckPerHit(move, user, target, skipAccuracyCheck) + # Two-turn attacks can't fail here in the charging turn + return true if user.effects[PBEffects::TwoTurnAttack] + # Lock-On + return true if user.effects[PBEffects::LockOn] > 0 && + user.effects[PBEffects::LockOnPos] == target.index + # Toxic + return true if move.pbOverrideSuccessCheckPerHit(user, target) + miss = false + hitsInvul = false + # No Guard + hitsInvul = true if user.hasActiveAbility?(:NOGUARD) || + target.hasActiveAbility?(:NOGUARD) + # Future Sight + hitsInvul = true if @battle.futureSight + # Helping Hand + hitsInvul = true if move.function == "PowerUpAllyMove" + if !hitsInvul + # Semi-invulnerable moves + if target.effects[PBEffects::TwoTurnAttack] + if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky", + "TwoTurnAttackInvulnerableInSkyParalyzeTarget", + "TwoTurnAttackInvulnerableInSkyTargetCannotAct") + miss = true if !move.hitsFlyingTargets? + elsif target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground") + miss = true if !move.hitsDiggingTargets? + elsif target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderwater") + miss = true if !move.hitsDivingTargets? + elsif target.inTwoTurnAttack?("TwoTurnAttackInvulnerableRemoveProtections") + miss = true + end + end + if target.effects[PBEffects::SkyDrop] >= 0 && + target.effects[PBEffects::SkyDrop] != user.index && !move.hitsFlyingTargets? + miss = true + end + end + target.damageState.invulnerable = true if miss + if !miss + # Called by another move + return true if skipAccuracyCheck + # Accuracy check + return true if move.pbAccuracyCheck(user, target) # Includes Counter/Mirror Coat + end + # Missed + PBDebug.log("[Move failed] Failed pbAccuracyCheck or target is semi-invulnerable") + return false + end + + #============================================================================= + # Message shown when a move fails the per-hit success check above. + #============================================================================= + def pbMissMessage(move, user, target) + if target.damageState.affection_missed + @battle.pbDisplay(_INTL("{1} avoided the move in time with your shout!", target.pbThis)) + elsif move.pbTarget(user).num_targets > 1 || target.effects[PBEffects::TwoTurnAttack] + @battle.pbDisplay(_INTL("{1} avoided the attack!", target.pbThis)) + elsif !move.pbMissMessage(user, target) + @battle.pbDisplay(_INTL("{1}'s attack missed!", user.pbThis)) + end + end +end diff --git a/Data/Scripts/011_Battle/002_Battler/010_Battler_UseMoveTriggerEffects.rb b/Data/Scripts/011_Battle/002_Battler/010_Battler_UseMoveTriggerEffects.rb new file mode 100644 index 0000000000..982df6037f --- /dev/null +++ b/Data/Scripts/011_Battle/002_Battler/010_Battler_UseMoveTriggerEffects.rb @@ -0,0 +1,217 @@ +class Battle::Battler + #============================================================================= + # Effect per hit + #============================================================================= + def pbEffectsOnMakingHit(move, user, target) + if target.damageState.calcDamage > 0 && !target.damageState.substitute + # Target's ability + if target.abilityActive?(true) + oldHP = user.hp + Battle::AbilityEffects.triggerOnBeingHit(target.ability, user, target, move, @battle) + user.pbItemHPHealCheck if user.hp < oldHP + end + # Cramorant - Gulp Missile + if target.isSpecies?(:CRAMORANT) && target.ability == :GULPMISSILE && + target.form > 0 && !target.effects[PBEffects::Transform] + oldHP = user.hp + # NOTE: Strictly speaking, an attack animation should be shown (the + # target Cramorant attacking the user) and the ability splash + # shouldn't be shown. + @battle.pbShowAbilitySplash(target) + if user.takesIndirectDamage?(Battle::Scene::USE_ABILITY_SPLASH) + @battle.scene.pbDamageAnimation(user) + user.pbReduceHP(user.totalhp / 4, false) + end + case target.form + when 1 # Gulping Form + user.pbLowerStatStageByAbility(:DEFENSE, 1, target, false) + when 2 # Gorging Form + target.pbParalyze(user) if target.pbCanParalyze?(user, false) + end + @battle.pbHideAbilitySplash(target) + user.pbItemHPHealCheck if user.hp < oldHP + end + # User's ability + if user.abilityActive?(true) + Battle::AbilityEffects.triggerOnDealingHit(user.ability, user, target, move, @battle) + user.pbItemHPHealCheck + end + # Target's item + if target.itemActive?(true) + oldHP = user.hp + Battle::ItemEffects.triggerOnBeingHit(target.item, user, target, move, @battle) + user.pbItemHPHealCheck if user.hp < oldHP + end + end + if target.opposes?(user) + # Rage + if target.effects[PBEffects::Rage] && !target.fainted? && + target.pbCanRaiseStatStage?(:ATTACK, target) + @battle.pbDisplay(_INTL("{1}'s rage is building!", target.pbThis)) + target.pbRaiseStatStage(:ATTACK, 1, target) + end + # Beak Blast + if target.effects[PBEffects::BeakBlast] + PBDebug.log("[Lingering effect] #{target.pbThis}'s Beak Blast") + if move.pbContactMove?(user) && user.affectedByContactEffect? && + target.pbCanBurn?(user, false, self) + target.pbBurn(user) + end + end + # Shell Trap (make the trapper move next if the trap was triggered) + if target.effects[PBEffects::ShellTrap] && move.physicalMove? && + @battle.choices[target.index][0] == :UseMove && !target.movedThisRound? && + target.damageState.hpLost > 0 && !target.damageState.substitute + target.tookPhysicalHit = true + target.effects[PBEffects::MoveNext] = true + target.effects[PBEffects::Quash] = 0 + end + # Grudge + if target.effects[PBEffects::Grudge] && target.fainted? + move.pp = 0 + @battle.pbDisplay(_INTL("{1}'s {2} lost all of its PP due to the grudge!", + user.pbThis, move.name)) + end + # Destiny Bond (recording that it should apply) + if target.effects[PBEffects::DestinyBond] && target.fainted? && + user.effects[PBEffects::DestinyBondTarget] < 0 + user.effects[PBEffects::DestinyBondTarget] = target.index + end + end + end + + #============================================================================= + # Effects after all hits (i.e. at end of move usage) + #============================================================================= + def pbEffectsAfterMove(user, targets, move, numHits) + # Defrost + if move.damagingMove? + targets.each do |b| + next if b.damageState.unaffected || b.damageState.substitute + next if b.status != :FROZEN + # NOTE: Non-Fire-type moves that thaw the user will also thaw the + # target (in Gen 6+). + if move.calcType == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && move.thawsUser?) + b.pbCureStatus + end + end + end + # Destiny Bond + # NOTE: Although Destiny Bond is similar to Grudge, they don't apply at + # the same time (however, Destiny Bond does check whether it's going + # to trigger at the same time as Grudge). + if user.effects[PBEffects::DestinyBondTarget] >= 0 && !user.fainted? + dbName = @battle.battlers[user.effects[PBEffects::DestinyBondTarget]].pbThis + @battle.pbDisplay(_INTL("{1} took its attacker down with it!", dbName)) + user.pbReduceHP(user.hp, false) + user.pbItemHPHealCheck + user.pbFaint + @battle.pbJudgeCheckpoint(user) + end + # User's ability + if user.abilityActive? + Battle::AbilityEffects.triggerOnEndOfUsingMove(user.ability, user, targets, move, @battle) + end + if !user.fainted? && !user.effects[PBEffects::Transform] && + !@battle.pbAllFainted?(user.idxOpposingSide) + # Greninja - Battle Bond + if user.isSpecies?(:GRENINJA) && user.ability == :BATTLEBOND && + !@battle.battleBond[user.index & 1][user.pokemonIndex] + numFainted = 0 + targets.each { |b| numFainted += 1 if b.damageState.fainted } + if numFainted > 0 && user.form == 1 + @battle.battleBond[user.index & 1][user.pokemonIndex] = true + @battle.pbDisplay(_INTL("{1} became fully charged due to its bond with its Trainer!", user.pbThis)) + @battle.pbShowAbilitySplash(user, true) + @battle.pbHideAbilitySplash(user) + user.pbChangeForm(2, _INTL("{1} became Ash-Greninja!", user.pbThis)) + end + end + # Cramorant = Gulp Missile + if user.isSpecies?(:CRAMORANT) && user.ability == :GULPMISSILE && user.form == 0 && + ((move.id == :SURF && numHits > 0) || (move.id == :DIVE && move.chargingTurn)) + # NOTE: Intentionally no ability splash or message here. + user.pbChangeForm((user.hp > user.totalhp / 2) ? 1 : 2, nil) + end + end + # Room Service + if move.function == "StartSlowerBattlersActFirst" && @battle.field.effects[PBEffects::TrickRoom] > 0 + @battle.allBattlers.each do |b| + next if !b.hasActiveItem?(:ROOMSERVICE) + next if !b.pbCanLowerStatStage?(:SPEED) + @battle.pbCommonAnimation("UseItem", b) + b.pbLowerStatStage(:SPEED, 1, nil) + b.pbConsumeItem + end + end + # Consume user's Gem + if user.effects[PBEffects::GemConsumed] + # NOTE: The consume animation and message for Gems are shown immediately + # after the move's animation, but the item is only consumed now. + user.pbConsumeItem + end + switched_battlers = [] # Indices of battlers that were switched out somehow + # Target switching caused by Roar, Whirlwind, Circle Throw, Dragon Tail + move.pbSwitchOutTargetEffect(user, targets, numHits, switched_battlers) + # Target's item, user's item, target's ability (all negated by Sheer Force) + if !(user.hasActiveAbility?(:SHEERFORCE) && move.addlEffect > 0) + pbEffectsAfterMove2(user, targets, move, numHits, switched_battlers) + end + # Some move effects that need to happen here, i.e. user switching caused by + # U-turn/Volt Switch/Baton Pass/Parting Shot, Relic Song's form changing, + # Fling/Natural Gift consuming item. + if !switched_battlers.include?(user.index) + move.pbEndOfMoveUsageEffect(user, targets, numHits, switched_battlers) + end + # User's ability/item that switches the user out (all negated by Sheer Force) + if !(user.hasActiveAbility?(:SHEERFORCE) && move.addlEffect > 0) + pbEffectsAfterMove3(user, targets, move, numHits, switched_battlers) + end + if numHits > 0 + @battle.allBattlers.each { |b| b.pbItemEndOfMoveCheck } + end + end + + # Everything in this method is negated by Sheer Force. + def pbEffectsAfterMove2(user, targets, move, numHits, switched_battlers) + # Target's held item (Eject Button, Red Card, Eject Pack) + @battle.pbPriority(true).each do |b| + if targets.any? { |targetB| targetB.index == b.index } && + !b.damageState.unaffected && b.damageState.calcDamage > 0 && b.itemActive? + Battle::ItemEffects.triggerAfterMoveUseFromTarget(b.item, b, user, move, switched_battlers, @battle) + end + # Target's Eject Pack + if switched_battlers.empty? && b.index != user.index && b.pbItemOnStatDropped(user) + switched_battlers.push(b.index) + end + end + # User's held item (Life Orb, Shell Bell, Throat Spray, Eject Pack) + if !switched_battlers.include?(user.index) && user.itemActive? # Only if user hasn't switched out + Battle::ItemEffects.triggerAfterMoveUseFromUser(user.item, user, targets, move, numHits, @battle) + end + # Target's ability (Berserk, Color Change, Emergency Exit, Pickpocket, Wimp Out) + @battle.pbPriority(true).each do |b| + if targets.any? { |targetB| targetB.index == b.index } && + !b.damageState.unaffected && !switched_battlers.include?(b.index) && b.abilityActive? + Battle::AbilityEffects.triggerAfterMoveUseFromTarget(b.ability, b, user, move, switched_battlers, @battle) + end + # Target's Emergency Exit, Wimp Out (including for Pokémon hurt by Flame Burst) + if switched_battlers.empty? && move.damagingMove? && + b.index != user.index && b.pbAbilitiesOnDamageTaken(user) + switched_battlers.push(b.index) + end + end + end + + # Everything in this method is negated by Sheer Force. + def pbEffectsAfterMove3(user, targets, move, numHits, switched_battlers) + # User's held item that switches it out (Eject Pack) + if switched_battlers.empty? && user.pbItemOnStatDropped(user) + switched_battlers.push(user.index) + end + # User's ability (Emergency Exit, Wimp Out) + if switched_battlers.empty? && move.damagingMove? && user.pbAbilitiesOnDamageTaken(user) + switched_battlers.push(user.index) + end + end +end diff --git a/Data/Scripts/011_Battle/002_Move/001_PokeBattle_Move.rb b/Data/Scripts/011_Battle/002_Move/001_PokeBattle_Move.rb deleted file mode 100644 index 964c48e069..0000000000 --- a/Data/Scripts/011_Battle/002_Move/001_PokeBattle_Move.rb +++ /dev/null @@ -1,141 +0,0 @@ -class PokeBattle_Move - attr_reader :battle - attr_reader :realMove - attr_accessor :id - attr_reader :name - attr_reader :function - attr_reader :baseDamage - attr_reader :type - attr_reader :category - attr_reader :accuracy - attr_accessor :pp - attr_writer :total_pp - attr_reader :addlEffect - attr_reader :target - attr_reader :priority - attr_reader :flags - attr_accessor :calcType - attr_accessor :powerBoost - attr_accessor :snatched - - def to_int; return @id; end - - #============================================================================= - # Creating a move - #============================================================================= - def initialize(battle, move) - @battle = battle - @realMove = move - @id = move.id - @name = move.name # Get the move's name - # Get data on the move - @function = move.function_code - @baseDamage = move.base_damage - @type = move.type - @category = move.category - @accuracy = move.accuracy - @pp = move.pp # Can be changed with Mimic/Transform - @addlEffect = move.effect_chance - @target = move.target - @priority = move.priority - @flags = move.flags - @calcType = nil - @powerBoost = false # For Aerilate, Pixilate, Refrigerate, Galvanize - @snatched = false - end - - # This is the code actually used to generate a PokeBattle_Move object. The - # object generated is a subclass of this one which depends on the move's - # function code (found in the script section PokeBattle_MoveEffect). - def PokeBattle_Move.from_pokemon_move(battle, move) - validate move => Pokemon::Move - moveFunction = move.function_code || "000" - className = sprintf("PokeBattle_Move_%s", moveFunction) - if Object.const_defined?(className) - return Object.const_get(className).new(battle, move) - end - return PokeBattle_UnimplementedMove.new(battle, move) - end - - #============================================================================= - # About the move - #============================================================================= - def pbTarget(_user); return GameData::Target.get(@target); end - - def total_pp - return @total_pp if @total_pp && @total_pp>0 # Usually undefined - return @realMove.total_pp if @realMove - return 0 - end - - # NOTE: This method is only ever called while using a move (and also by the - # AI), so using @calcType here is acceptable. - def physicalMove?(thisType=nil) - return (@category==0) if Settings::MOVE_CATEGORY_PER_MOVE - thisType ||= @calcType - thisType ||= @type - return true if !thisType - return GameData::Type.get(thisType).physical? - end - - # NOTE: This method is only ever called while using a move (and also by the - # AI), so using @calcType here is acceptable. - def specialMove?(thisType=nil) - return (@category==1) if Settings::MOVE_CATEGORY_PER_MOVE - thisType ||= @calcType - thisType ||= @type - return false if !thisType - return GameData::Type.get(thisType).special? - end - - def damagingMove?; return @category!=2; end - def statusMove?; return @category==2; end - - def usableWhenAsleep?; return false; end - def unusableInGravity?; return false; end - def healingMove?; return false; end - def recoilMove?; return false; end - def flinchingMove?; return false; end - def callsAnotherMove?; return false; end - # Whether the move can/will hit more than once in the same turn (including - # Beat Up which may instead hit just once). Not the same as pbNumHits>1. - def multiHitMove?; return false; end - def chargingTurnMove?; return false; end - def successCheckPerHit?; return false; end - def hitsFlyingTargets?; return false; end - def hitsDiggingTargets?; return false; end - def hitsDivingTargets?; return false; end - def ignoresReflect?; return false; end # For Brick Break - def cannotRedirect?; return false; end # For Future Sight/Doom Desire - def worksWithNoTargets?; return false; end # For Explosion - def damageReducedByBurn?; return true; end # For Facade - def triggersHyperMode?; return false; end - - def contactMove?; return @flags[/a/]; end - def canProtectAgainst?; return @flags[/b/]; end - def canMagicCoat?; return @flags[/c/]; end - def canSnatch?; return @flags[/d/]; end - def canMirrorMove?; return @flags[/e/]; end - def canKingsRock?; return @flags[/f/]; end - def thawsUser?; return @flags[/g/]; end - def highCriticalRate?; return @flags[/h/]; end - def bitingMove?; return @flags[/i/]; end - def punchingMove?; return @flags[/j/]; end - def soundMove?; return @flags[/k/]; end - def powderMove?; return @flags[/l/]; end - def pulseMove?; return @flags[/m/]; end - def bombMove?; return @flags[/n/]; end - def danceMove?; return @flags[/o/]; end - - # Causes perfect accuracy (param=1) and double damage (param=2). - def tramplesMinimize?(_param=1); return false; end - def nonLethal?(_user,_target); return false; end # For False Swipe - - def ignoresSubstitute?(user) # user is the Pokémon using this move - if Settings::MECHANICS_GENERATION >= 6 - return true if soundMove? - return true if user && user.hasActiveAbility?(:INFILTRATOR) - end - return false - end -end diff --git a/Data/Scripts/011_Battle/002_Move/005_Move_Effects_000-07F.rb b/Data/Scripts/011_Battle/002_Move/005_Move_Effects_000-07F.rb deleted file mode 100644 index 78fc2f164f..0000000000 --- a/Data/Scripts/011_Battle/002_Move/005_Move_Effects_000-07F.rb +++ /dev/null @@ -1,2714 +0,0 @@ -#=============================================================================== -# No additional effect. -#=============================================================================== -class PokeBattle_Move_000 < PokeBattle_Move -end - - - -#=============================================================================== -# Does absolutely nothing. (Splash) -#=============================================================================== -class PokeBattle_Move_001 < PokeBattle_Move - def unusableInGravity?; return true; end - - def pbEffectGeneral(user) - @battle.pbDisplay(_INTL("But nothing happened!")) - end -end - - - -#=============================================================================== -# Struggle, if defined as a move in moves.txt. Typically it won't be. -#=============================================================================== -class PokeBattle_Move_002 < PokeBattle_Struggle -end - - - -#=============================================================================== -# Puts the target to sleep. -#=============================================================================== -class PokeBattle_Move_003 < PokeBattle_SleepMove - def pbMoveFailed?(user,targets) - if Settings::MECHANICS_GENERATION >= 7 && @id == :DARKVOID - if !user.isSpecies?(:DARKRAI) && user.effects[PBEffects::TransformSpecies] != :DARKRAI - @battle.pbDisplay(_INTL("But {1} can't use the move!",user.pbThis)) - return true - end - end - return false - end - - def pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers) - return if numHits==0 - return if user.fainted? || user.effects[PBEffects::Transform] - return if @id != :RELICSONG - return if !user.isSpecies?(:MELOETTA) - return if user.hasActiveAbility?(:SHEERFORCE) && @addlEffect>0 - newForm = (user.form+1)%2 - user.pbChangeForm(newForm,_INTL("{1} transformed!",user.pbThis)) - end -end - - - -#=============================================================================== -# Makes the target drowsy; it falls asleep at the end of the next turn. (Yawn) -#=============================================================================== -class PokeBattle_Move_004 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Yawn]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if !target.pbCanSleep?(user,true,self) - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Yawn] = 2 - @battle.pbDisplay(_INTL("{1} made {2} drowsy!",user.pbThis,target.pbThis(true))) - end -end - - - -#=============================================================================== -# Poisons the target. -#=============================================================================== -class PokeBattle_Move_005 < PokeBattle_PoisonMove -end - - - -#=============================================================================== -# Badly poisons the target. (Poison Fang, Toxic) -#=============================================================================== -class PokeBattle_Move_006 < PokeBattle_PoisonMove - def initialize(battle,move) - super - @toxic = true - end - - def pbOverrideSuccessCheckPerHit(user,target) - return (Settings::MORE_TYPE_EFFECTS && statusMove? && user.pbHasType?(:POISON)) - end -end - - - -#=============================================================================== -# Paralyzes the target. -# Thunder Wave: Doesn't affect target if move's type has no effect on it. -# Body Slam: Does double damage and has perfect accuracy if target is Minimized. -#=============================================================================== -class PokeBattle_Move_007 < PokeBattle_ParalysisMove - def tramplesMinimize?(param=1) - # Perfect accuracy and double damage (for Body Slam only) - return Settings::MECHANICS_GENERATION >= 6 if @id == :BODYSLAM - return super - end - - def pbFailsAgainstTarget?(user,target) - if @id == :THUNDERWAVE && Effectiveness.ineffective?(target.damageState.typeMod) - @battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - return true - end - return super - end -end - - - -#=============================================================================== -# Paralyzes the target. Accuracy perfect in rain, 50% in sunshine. Hits some -# semi-invulnerable targets. (Thunder) -#=============================================================================== -class PokeBattle_Move_008 < PokeBattle_ParalysisMove - def hitsFlyingTargets?; return true; end - - def pbBaseAccuracy(user,target) - case @battle.pbWeather - when :Sun, :HarshSun - return 50 - when :Rain, :HeavyRain - return 0 - end - return super - end -end - - - -#=============================================================================== -# Paralyzes the target. May cause the target to flinch. (Thunder Fang) -#=============================================================================== -class PokeBattle_Move_009 < PokeBattle_Move - def flinchingMove?; return true; end - - def pbAdditionalEffect(user,target) - return if target.damageState.substitute - chance = pbAdditionalEffectChance(user,target,10) - return if chance==0 - if @battle.pbRandom(100)= 6 # Perfect accuracy - return true if param==2 # Double damage - return super - end -end - - - -#=============================================================================== -# Causes the target to flinch. Fails if the user is not asleep. (Snore) -#=============================================================================== -class PokeBattle_Move_011 < PokeBattle_FlinchMove - def usableWhenAsleep?; return true; end - - def pbMoveFailed?(user,targets) - if !user.asleep? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end -end - - - -#=============================================================================== -# Causes the target to flinch. Fails if this isn't the user's first turn. -# (Fake Out) -#=============================================================================== -class PokeBattle_Move_012 < PokeBattle_FlinchMove - def pbMoveFailed?(user,targets) - if user.turnCount > 1 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end -end - - - -#=============================================================================== -# Confuses the target. -#=============================================================================== -class PokeBattle_Move_013 < PokeBattle_ConfuseMove -end - - - -#=============================================================================== -# Confuses the target. (Chatter) -#=============================================================================== -class PokeBattle_Move_014 < PokeBattle_Move_013 -end - - - -#=============================================================================== -# Confuses the target. Accuracy perfect in rain, 50% in sunshine. Hits some -# semi-invulnerable targets. (Hurricane) -#=============================================================================== -class PokeBattle_Move_015 < PokeBattle_ConfuseMove - def hitsFlyingTargets?; return true; end - - def pbBaseAccuracy(user,target) - case @battle.pbWeather - when :Sun, :HarshSun - return 50 - when :Rain, :HeavyRain - return 0 - end - return super - end -end - - - -#=============================================================================== -# Attracts the target. (Attract) -#=============================================================================== -class PokeBattle_Move_016 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - return false if damagingMove? - return true if !target.pbCanAttract?(user) - return true if pbMoveFailedAromaVeil?(user,target) - return false - end - - def pbEffectAgainstTarget(user,target) - return if damagingMove? - target.pbAttract(user) - end - - def pbAdditionalEffect(user,target) - return if target.damageState.substitute - target.pbAttract(user) if target.pbCanAttract?(user,false) - end -end - - - -#=============================================================================== -# Burns, freezes or paralyzes the target. (Tri Attack) -#=============================================================================== -class PokeBattle_Move_017 < PokeBattle_Move - def pbAdditionalEffect(user,target) - return if target.damageState.substitute - case @battle.pbRandom(3) - when 0 then target.pbBurn(user) if target.pbCanBurn?(user, false, self) - when 1 then target.pbFreeze if target.pbCanFreeze?(user, false, self) - when 2 then target.pbParalyze(user) if target.pbCanParalyze?(user, false, self) - end - end -end - - - -#=============================================================================== -# Cures user of burn, poison and paralysis. (Refresh) -#=============================================================================== -class PokeBattle_Move_018 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if ![:BURN, :POISON, :PARALYSIS].include?(user.status) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - old_status = user.status - user.pbCureStatus(false) - case old_status - when :BURN - @battle.pbDisplay(_INTL("{1} healed its burn!",user.pbThis)) - when :POISON - @battle.pbDisplay(_INTL("{1} cured its poisoning!",user.pbThis)) - when :PARALYSIS - @battle.pbDisplay(_INTL("{1} cured its paralysis!",user.pbThis)) - end - end -end - - - -#=============================================================================== -# Cures all party Pokémon of permanent status problems. (Aromatherapy, Heal Bell) -#=============================================================================== -# NOTE: In Gen 5, this move should have a target of UserSide, while in Gen 6+ it -# should have a target of UserAndAllies. This is because, in Gen 5, this -# move shouldn't call def pbSuccessCheckAgainstTarget for each Pokémon -# currently in battle that will be affected by this move (i.e. allies -# aren't protected by their substitute/ability/etc., but they are in Gen -# 6+). We achieve this by not targeting any battlers in Gen 5, since -# pbSuccessCheckAgainstTarget is only called for targeted battlers. -class PokeBattle_Move_019 < PokeBattle_Move - def worksWithNoTargets?; return true; end - - def pbMoveFailed?(user,targets) - failed = true - @battle.eachSameSideBattler(user) do |b| - next if b.status == :NONE - failed = false - break - end - if !failed - @battle.pbParty(user.index).each do |pkmn| - next if !pkmn || !pkmn.able? || pkmn.status == :NONE - failed = false - break - end - end - if failed - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - return target.status == :NONE - end - - def pbAromatherapyHeal(pkmn,battler=nil) - oldStatus = (battler) ? battler.status : pkmn.status - curedName = (battler) ? battler.pbThis : pkmn.name - if battler - battler.pbCureStatus(false) - else - pkmn.status = :NONE - pkmn.statusCount = 0 - end - case oldStatus - when :SLEEP - @battle.pbDisplay(_INTL("{1} was woken from sleep.",curedName)) - when :POISON - @battle.pbDisplay(_INTL("{1} was cured of its poisoning.",curedName)) - when :BURN - @battle.pbDisplay(_INTL("{1}'s burn was healed.",curedName)) - when :PARALYSIS - @battle.pbDisplay(_INTL("{1} was cured of paralysis.",curedName)) - when :FROZEN - @battle.pbDisplay(_INTL("{1} was thawed out.",curedName)) - end - end - - def pbEffectAgainstTarget(user,target) - # Cure all Pokémon in battle on the user's side. - pbAromatherapyHeal(target.pokemon,target) - end - - def pbEffectGeneral(user) - # Cure all Pokémon in battle on the user's side. For the benefit of the Gen - # 5 version of this move, to make Pokémon out in battle get cured first. - if pbTarget(user) == :UserSide - @battle.eachSameSideBattler(user) do |b| - next if b.status == :NONE - pbAromatherapyHeal(b.pokemon,b) - end - end - # Cure all Pokémon in the user's and partner trainer's party. - # NOTE: This intentionally affects the partner trainer's inactive Pokémon - # too. - @battle.pbParty(user.index).each_with_index do |pkmn,i| - next if !pkmn || !pkmn.able? || pkmn.status == :NONE - next if @battle.pbFindBattler(i,user) # Skip Pokémon in battle - pbAromatherapyHeal(pkmn) - end - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - super - if @id == :AROMATHERAPY - @battle.pbDisplay(_INTL("A soothing aroma wafted through the area!")) - elsif @id == :HEALBELL - @battle.pbDisplay(_INTL("A bell chimed!")) - end - end -end - - - -#=============================================================================== -# Safeguards the user's side from being inflicted with status problems. -# (Safeguard) -#=============================================================================== -class PokeBattle_Move_01A < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOwnSide.effects[PBEffects::Safeguard]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::Safeguard] = 5 - @battle.pbDisplay(_INTL("{1} became cloaked in a mystical veil!",user.pbTeam)) - end -end - - - -#=============================================================================== -# User passes its status problem to the target. (Psycho Shift) -#=============================================================================== -class PokeBattle_Move_01B < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.status == :NONE - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if !target.pbCanInflictStatus?(user.status,user,false,self) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - msg = "" - case user.status - when :SLEEP - target.pbSleep - msg = _INTL("{1} woke up.",user.pbThis) - when :POISON - target.pbPoison(user,nil,user.statusCount!=0) - msg = _INTL("{1} was cured of its poisoning.",user.pbThis) - when :BURN - target.pbBurn(user) - msg = _INTL("{1}'s burn was healed.",user.pbThis) - when :PARALYSIS - target.pbParalyze(user) - msg = _INTL("{1} was cured of paralysis.",user.pbThis) - when :FROZEN - target.pbFreeze - msg = _INTL("{1} was thawed out.",user.pbThis) - end - if msg!="" - user.pbCureStatus(false) - @battle.pbDisplay(msg) - end - end -end - - - -#=============================================================================== -# Increases the user's Attack by 1 stage. -#=============================================================================== -class PokeBattle_Move_01C < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:ATTACK,1] - end -end - - - -#=============================================================================== -# Increases the user's Defense by 1 stage. (Harden, Steel Wing, Withdraw) -#=============================================================================== -class PokeBattle_Move_01D < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:DEFENSE,1] - end -end - - - -#=============================================================================== -# Increases the user's Defense by 1 stage. User curls up. (Defense Curl) -#=============================================================================== -class PokeBattle_Move_01E < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:DEFENSE,1] - end - - def pbEffectGeneral(user) - user.effects[PBEffects::DefenseCurl] = true - super - end -end - - - -#=============================================================================== -# Increases the user's Speed by 1 stage. (Flame Charge) -#=============================================================================== -class PokeBattle_Move_01F < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:SPEED,1] - end -end - - - -#=============================================================================== -# Increases the user's Special Attack by 1 stage. (Charge Beam, Fiery Dance) -#=============================================================================== -class PokeBattle_Move_020 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:SPECIAL_ATTACK,1] - end -end - - - -#=============================================================================== -# Increases the user's Special Defense by 1 stage. -# Charges up user's next attack if it is Electric-type. (Charge) -#=============================================================================== -class PokeBattle_Move_021 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:SPECIAL_DEFENSE,1] - end - - def pbEffectGeneral(user) - user.effects[PBEffects::Charge] = 2 - @battle.pbDisplay(_INTL("{1} began charging power!",user.pbThis)) - super - end -end - - - -#=============================================================================== -# Increases the user's evasion by 1 stage. (Double Team) -#=============================================================================== -class PokeBattle_Move_022 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:EVASION,1] - end -end - - - -#=============================================================================== -# Increases the user's critical hit rate. (Focus Energy) -#=============================================================================== -class PokeBattle_Move_023 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::FocusEnergy]>=2 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.effects[PBEffects::FocusEnergy] = 2 - @battle.pbDisplay(_INTL("{1} is getting pumped!",user.pbThis)) - end -end - - - -#=============================================================================== -# Increases the user's Attack and Defense by 1 stage each. (Bulk Up) -#=============================================================================== -class PokeBattle_Move_024 < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:ATTACK,1,:DEFENSE,1] - end -end - - - -#=============================================================================== -# Increases the user's Attack, Defense and accuracy by 1 stage each. (Coil) -#=============================================================================== -class PokeBattle_Move_025 < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:ATTACK,1,:DEFENSE,1,:ACCURACY,1] - end -end - - - -#=============================================================================== -# Increases the user's Attack and Speed by 1 stage each. (Dragon Dance) -#=============================================================================== -class PokeBattle_Move_026 < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:ATTACK,1,:SPEED,1] - end -end - - - -#=============================================================================== -# Increases the user's Attack and Special Attack by 1 stage each. (Work Up) -#=============================================================================== -class PokeBattle_Move_027 < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:ATTACK,1,:SPECIAL_ATTACK,1] - end -end - - - -#=============================================================================== -# Increases the user's Attack and Sp. Attack by 1 stage each. -# In sunny weather, increases are 2 stages each instead. (Growth) -#=============================================================================== -class PokeBattle_Move_028 < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:ATTACK,1,:SPECIAL_ATTACK,1] - end - - def pbOnStartUse(user,targets) - increment = 1 - increment = 2 if [:Sun, :HarshSun].include?(@battle.pbWeather) - @statUp[1] = @statUp[3] = increment - end -end - - - -#=============================================================================== -# Increases the user's Attack and accuracy by 1 stage each. (Hone Claws) -#=============================================================================== -class PokeBattle_Move_029 < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:ATTACK,1,:ACCURACY,1] - end -end - - - -#=============================================================================== -# Increases the user's Defense and Special Defense by 1 stage each. -# (Cosmic Power, Defend Order) -#=============================================================================== -class PokeBattle_Move_02A < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:DEFENSE,1,:SPECIAL_DEFENSE,1] - end -end - - - -#=============================================================================== -# Increases the user's Sp. Attack, Sp. Defense and Speed by 1 stage each. -# (Quiver Dance) -#=============================================================================== -class PokeBattle_Move_02B < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:SPECIAL_ATTACK,1,:SPECIAL_DEFENSE,1,:SPEED,1] - end -end - - - -#=============================================================================== -# Increases the user's Sp. Attack and Sp. Defense by 1 stage each. (Calm Mind) -#=============================================================================== -class PokeBattle_Move_02C < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:SPECIAL_ATTACK,1,:SPECIAL_DEFENSE,1] - end -end - - - -#=============================================================================== -# Increases the user's Attack, Defense, Speed, Special Attack and Special Defense -# by 1 stage each. (Ancient Power, Ominous Wind, Silver Wind) -#=============================================================================== -class PokeBattle_Move_02D < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:ATTACK,1,:DEFENSE,1,:SPECIAL_ATTACK,1,:SPECIAL_DEFENSE,1,:SPEED,1] - end -end - - - -#=============================================================================== -# Increases the user's Attack by 2 stages. (Swords Dance) -#=============================================================================== -class PokeBattle_Move_02E < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:ATTACK,2] - end -end - - - -#=============================================================================== -# Increases the user's Defense by 2 stages. (Acid Armor, Barrier, Iron Defense) -#=============================================================================== -class PokeBattle_Move_02F < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:DEFENSE,2] - end -end - - - -#=============================================================================== -# Increases the user's Speed by 2 stages. (Agility, Rock Polish) -#=============================================================================== -class PokeBattle_Move_030 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:SPEED,2] - end -end - - - -#=============================================================================== -# Increases the user's Speed by 2 stages. Lowers user's weight by 100kg. -# (Autotomize) -#=============================================================================== -class PokeBattle_Move_031 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:SPEED,2] - end - - def pbEffectGeneral(user) - if user.pbWeight+user.effects[PBEffects::WeightChange]>1 - user.effects[PBEffects::WeightChange] -= 1000 - @battle.pbDisplay(_INTL("{1} became nimble!",user.pbThis)) - end - super - end -end - - - -#=============================================================================== -# Increases the user's Special Attack by 2 stages. (Nasty Plot) -#=============================================================================== -class PokeBattle_Move_032 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:SPECIAL_ATTACK,2] - end -end - - - -#=============================================================================== -# Increases the user's Special Defense by 2 stages. (Amnesia) -#=============================================================================== -class PokeBattle_Move_033 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:SPECIAL_DEFENSE,2] - end -end - - - -#=============================================================================== -# Increases the user's evasion by 2 stages. Minimizes the user. (Minimize) -#=============================================================================== -class PokeBattle_Move_034 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:EVASION,2] - end - - def pbEffectGeneral(user) - user.effects[PBEffects::Minimize] = true - super - end -end - - - -#=============================================================================== -# Decreases the user's Defense and Special Defense by 1 stage each. -# Increases the user's Attack, Speed and Special Attack by 2 stages each. -# (Shell Smash) -#=============================================================================== -class PokeBattle_Move_035 < PokeBattle_Move - def initialize(battle,move) - super - @statUp = [:ATTACK,2,:SPECIAL_ATTACK,2,:SPEED,2] - @statDown = [:DEFENSE,1,:SPECIAL_DEFENSE,1] - end - - def pbMoveFailed?(user,targets) - failed = true - for i in 0...@statUp.length/2 - if user.pbCanRaiseStatStage?(@statUp[i*2],user,self) - failed = false; break - end - end - for i in 0...@statDown.length/2 - if user.pbCanLowerStatStage?(@statDown[i*2],user,self) - failed = false; break - end - end - if failed - @battle.pbDisplay(_INTL("{1}'s stats can't be changed further!",user.pbThis)) - return true - end - return false - end - - def pbEffectGeneral(user) - showAnim = true - for i in 0...@statDown.length/2 - next if !user.pbCanLowerStatStage?(@statDown[i*2],user,self) - if user.pbLowerStatStage(@statDown[i*2],@statDown[i*2+1],user,showAnim) - showAnim = false - end - end - showAnim = true - for i in 0...@statUp.length/2 - next if !user.pbCanRaiseStatStage?(@statUp[i*2],user,self) - if user.pbRaiseStatStage(@statUp[i*2],@statUp[i*2+1],user,showAnim) - showAnim = false - end - end - end -end - - - -#=============================================================================== -# Increases the user's Speed by 2 stages, and its Attack by 1 stage. (Shift Gear) -#=============================================================================== -class PokeBattle_Move_036 < PokeBattle_MultiStatUpMove - def initialize(battle,move) - super - @statUp = [:SPEED,2,:ATTACK,1] - end -end - - - -#=============================================================================== -# Increases one random stat of the target by 2 stages (except HP). (Acupressure) -#=============================================================================== -class PokeBattle_Move_037 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - @statArray = [] - GameData::Stat.each_battle do |s| - @statArray.push(s.id) if target.pbCanRaiseStatStage?(s.id,user,self) - end - if @statArray.length==0 - @battle.pbDisplay(_INTL("{1}'s stats won't go any higher!",target.pbThis)) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - stat = @statArray[@battle.pbRandom(@statArray.length)] - target.pbRaiseStatStage(stat,2,user) - end -end - - - -#=============================================================================== -# Increases the user's Defense by 3 stages. (Cotton Guard) -#=============================================================================== -class PokeBattle_Move_038 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:DEFENSE,3] - end -end - - - -#=============================================================================== -# Increases the user's Special Attack by 3 stages. (Tail Glow) -#=============================================================================== -class PokeBattle_Move_039 < PokeBattle_StatUpMove - def initialize(battle,move) - super - @statUp = [:SPECIAL_ATTACK,3] - end -end - - - -#=============================================================================== -# Reduces the user's HP by half of max, and sets its Attack to maximum. -# (Belly Drum) -#=============================================================================== -class PokeBattle_Move_03A < PokeBattle_Move - def pbMoveFailed?(user,targets) - hpLoss = [user.totalhp/2,1].max - if user.hp<=hpLoss - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if !user.pbCanRaiseStatStage?(:ATTACK,user,self,true) - return false - end - - def pbEffectGeneral(user) - hpLoss = [user.totalhp/2,1].max - user.pbReduceHP(hpLoss,false) - if user.hasActiveAbility?(:CONTRARY) - user.stages[:ATTACK] = -6 - @battle.pbCommonAnimation("StatDown",user) - @battle.pbDisplay(_INTL("{1} cut its own HP and minimized its Attack!",user.pbThis)) - else - user.stages[:ATTACK] = 6 - @battle.pbCommonAnimation("StatUp",user) - @battle.pbDisplay(_INTL("{1} cut its own HP and maximized its Attack!",user.pbThis)) - end - user.pbItemHPHealCheck - end -end - - - -#=============================================================================== -# Decreases the user's Attack and Defense by 1 stage each. (Superpower) -#=============================================================================== -class PokeBattle_Move_03B < PokeBattle_StatDownMove - def initialize(battle,move) - super - @statDown = [:ATTACK,1,:DEFENSE,1] - end -end - - - -#=============================================================================== -# Decreases the user's Defense and Special Defense by 1 stage each. -# (Close Combat, Dragon Ascent) -#=============================================================================== -class PokeBattle_Move_03C < PokeBattle_StatDownMove - def initialize(battle,move) - super - @statDown = [:DEFENSE,1,:SPECIAL_DEFENSE,1] - end -end - - - -#=============================================================================== -# Decreases the user's Defense, Special Defense and Speed by 1 stage each. -# (V-create) -#=============================================================================== -class PokeBattle_Move_03D < PokeBattle_StatDownMove - def initialize(battle,move) - super - @statDown = [:SPEED,1,:DEFENSE,1,:SPECIAL_DEFENSE,1] - end -end - - - -#=============================================================================== -# Decreases the user's Speed by 1 stage. (Hammer Arm, Ice Hammer) -#=============================================================================== -class PokeBattle_Move_03E < PokeBattle_StatDownMove - def initialize(battle,move) - super - @statDown = [:SPEED,1] - end -end - - - -#=============================================================================== -# Decreases the user's Special Attack by 2 stages. -#=============================================================================== -class PokeBattle_Move_03F < PokeBattle_StatDownMove - def initialize(battle,move) - super - @statDown = [:SPECIAL_ATTACK,2] - end -end - - - -#=============================================================================== -# Increases the target's Special Attack by 1 stage. Confuses the target. (Flatter) -#=============================================================================== -class PokeBattle_Move_040 < PokeBattle_Move - def pbMoveFailed?(user,targets) - failed = true - targets.each do |b| - next if !b.pbCanRaiseStatStage?(:SPECIAL_ATTACK,user,self) && - !b.pbCanConfuse?(user,false,self) - failed = false - break - end - if failed - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - if target.pbCanRaiseStatStage?(:SPECIAL_ATTACK,user,self) - target.pbRaiseStatStage(:SPECIAL_ATTACK,1,user) - end - target.pbConfuse if target.pbCanConfuse?(user,false,self) - end -end - - - -#=============================================================================== -# Increases the target's Attack by 2 stages. Confuses the target. (Swagger) -#=============================================================================== -class PokeBattle_Move_041 < PokeBattle_Move - def pbMoveFailed?(user,targets) - failed = true - targets.each do |b| - next if !b.pbCanRaiseStatStage?(:ATTACK,user,self) && - !b.pbCanConfuse?(user,false,self) - failed = false - break - end - if failed - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - if target.pbCanRaiseStatStage?(:ATTACK,user,self) - target.pbRaiseStatStage(:ATTACK,2,user) - end - target.pbConfuse if target.pbCanConfuse?(user,false,self) - end -end - - - -#=============================================================================== -# Decreases the target's Attack by 1 stage. -#=============================================================================== -class PokeBattle_Move_042 < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:ATTACK,1] - end -end - - -#=============================================================================== -# Decreases the target's Defense by 1 stage. -#=============================================================================== -class PokeBattle_Move_043 < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:DEFENSE,1] - end -end - - - -#=============================================================================== -# Decreases the target's Speed by 1 stage. -#=============================================================================== -class PokeBattle_Move_044 < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:SPEED,1] - end - - def pbBaseDamage(baseDmg,user,target) - if @id == :BULLDOZE && @battle.field.terrain == :Grassy - baseDmg = (baseDmg/2.0).round - end - return baseDmg - end -end - - - -#=============================================================================== -# Decreases the target's Special Attack by 1 stage. -#=============================================================================== -class PokeBattle_Move_045 < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:SPECIAL_ATTACK,1] - end -end - - - -#=============================================================================== -# Decreases the target's Special Defense by 1 stage. -#=============================================================================== -class PokeBattle_Move_046 < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:SPECIAL_DEFENSE,1] - end -end - - - -#=============================================================================== -# Decreases the target's accuracy by 1 stage. -#=============================================================================== -class PokeBattle_Move_047 < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:ACCURACY,1] - end -end - - - -#=============================================================================== -# Decreases the target's evasion by 1 stage OR 2 stages. (Sweet Scent) -#=============================================================================== -class PokeBattle_Move_048 < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:EVASION, (Settings::MECHANICS_GENERATION >= 6) ? 2 : 1] - end -end - - - -#=============================================================================== -# Decreases the target's evasion by 1 stage. Ends all barriers and entry -# hazards for the target's side OR on both sides. (Defog) -#=============================================================================== -class PokeBattle_Move_049 < PokeBattle_TargetStatDownMove - def ignoresSubstitute?(user); return true; end - - def initialize(battle,move) - super - @statDown = [:EVASION,1] - end - - def pbFailsAgainstTarget?(user,target) - targetSide = target.pbOwnSide - targetOpposingSide = target.pbOpposingSide - return false if targetSide.effects[PBEffects::AuroraVeil]>0 || - targetSide.effects[PBEffects::LightScreen]>0 || - targetSide.effects[PBEffects::Reflect]>0 || - targetSide.effects[PBEffects::Mist]>0 || - targetSide.effects[PBEffects::Safeguard]>0 - return false if targetSide.effects[PBEffects::StealthRock] || - targetSide.effects[PBEffects::Spikes]>0 || - targetSide.effects[PBEffects::ToxicSpikes]>0 || - targetSide.effects[PBEffects::StickyWeb] - return false if Settings::MECHANICS_GENERATION >= 6 && - (targetOpposingSide.effects[PBEffects::StealthRock] || - targetOpposingSide.effects[PBEffects::Spikes]>0 || - targetOpposingSide.effects[PBEffects::ToxicSpikes]>0 || - targetOpposingSide.effects[PBEffects::StickyWeb]) - return false if Settings::MECHANICS_GENERATION >= 8 && @battle.field.terrain != :None - return super - end - - def pbEffectAgainstTarget(user,target) - if target.pbCanLowerStatStage?(@statDown[0],user,self) - target.pbLowerStatStage(@statDown[0],@statDown[1],user) - end - if target.pbOwnSide.effects[PBEffects::AuroraVeil]>0 - target.pbOwnSide.effects[PBEffects::AuroraVeil] = 0 - @battle.pbDisplay(_INTL("{1}'s Aurora Veil wore off!",target.pbTeam)) - end - if target.pbOwnSide.effects[PBEffects::LightScreen]>0 - target.pbOwnSide.effects[PBEffects::LightScreen] = 0 - @battle.pbDisplay(_INTL("{1}'s Light Screen wore off!",target.pbTeam)) - end - if target.pbOwnSide.effects[PBEffects::Reflect]>0 - target.pbOwnSide.effects[PBEffects::Reflect] = 0 - @battle.pbDisplay(_INTL("{1}'s Reflect wore off!",target.pbTeam)) - end - if target.pbOwnSide.effects[PBEffects::Mist]>0 - target.pbOwnSide.effects[PBEffects::Mist] = 0 - @battle.pbDisplay(_INTL("{1}'s Mist faded!",target.pbTeam)) - end - if target.pbOwnSide.effects[PBEffects::Safeguard]>0 - target.pbOwnSide.effects[PBEffects::Safeguard] = 0 - @battle.pbDisplay(_INTL("{1} is no longer protected by Safeguard!!",target.pbTeam)) - end - if target.pbOwnSide.effects[PBEffects::StealthRock] || - (Settings::MECHANICS_GENERATION >= 6 && - target.pbOpposingSide.effects[PBEffects::StealthRock]) - target.pbOwnSide.effects[PBEffects::StealthRock] = false - target.pbOpposingSide.effects[PBEffects::StealthRock] = false if Settings::MECHANICS_GENERATION >= 6 - @battle.pbDisplay(_INTL("{1} blew away stealth rocks!",user.pbThis)) - end - if target.pbOwnSide.effects[PBEffects::Spikes]>0 || - (Settings::MECHANICS_GENERATION >= 6 && - target.pbOpposingSide.effects[PBEffects::Spikes]>0) - target.pbOwnSide.effects[PBEffects::Spikes] = 0 - target.pbOpposingSide.effects[PBEffects::Spikes] = 0 if Settings::MECHANICS_GENERATION >= 6 - @battle.pbDisplay(_INTL("{1} blew away spikes!",user.pbThis)) - end - if target.pbOwnSide.effects[PBEffects::ToxicSpikes]>0 || - (Settings::MECHANICS_GENERATION >= 6 && - target.pbOpposingSide.effects[PBEffects::ToxicSpikes]>0) - target.pbOwnSide.effects[PBEffects::ToxicSpikes] = 0 - target.pbOpposingSide.effects[PBEffects::ToxicSpikes] = 0 if Settings::MECHANICS_GENERATION >= 6 - @battle.pbDisplay(_INTL("{1} blew away poison spikes!",user.pbThis)) - end - if target.pbOwnSide.effects[PBEffects::StickyWeb] || - (Settings::MECHANICS_GENERATION >= 6 && - target.pbOpposingSide.effects[PBEffects::StickyWeb]) - target.pbOwnSide.effects[PBEffects::StickyWeb] = false - target.pbOpposingSide.effects[PBEffects::StickyWeb] = false if Settings::MECHANICS_GENERATION >= 6 - @battle.pbDisplay(_INTL("{1} blew away sticky webs!",user.pbThis)) - end - if Settings::MECHANICS_GENERATION >= 8 && @battle.field.terrain != :None - case @battle.field.terrain - when :Electric - @battle.pbDisplay(_INTL("The electricity disappeared from the battlefield.")) - when :Grassy - @battle.pbDisplay(_INTL("The grass disappeared from the battlefield.")) - when :Misty - @battle.pbDisplay(_INTL("The mist disappeared from the battlefield.")) - when :Psychic - @battle.pbDisplay(_INTL("The weirdness disappeared from the battlefield.")) - end - @battle.field.terrain = :None - end - end -end - - - -#=============================================================================== -# Decreases the target's Attack and Defense by 1 stage each. (Tickle) -#=============================================================================== -class PokeBattle_Move_04A < PokeBattle_TargetMultiStatDownMove - def initialize(battle,move) - super - @statDown = [:ATTACK,1,:DEFENSE,1] - end -end - - - -#=============================================================================== -# Decreases the target's Attack by 2 stages. (Charm, Feather Dance) -#=============================================================================== -class PokeBattle_Move_04B < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:ATTACK,2] - end -end - - - -#=============================================================================== -# Decreases the target's Defense by 2 stages. (Screech) -#=============================================================================== -class PokeBattle_Move_04C < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:DEFENSE,2] - end -end - - - -#=============================================================================== -# Decreases the target's Speed by 2 stages. (Cotton Spore, Scary Face, String Shot) -#=============================================================================== -class PokeBattle_Move_04D < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - inc = 2 - inc = 1 if @id == :STRINGSHOT && Settings::MECHANICS_GENERATION <= 5 - @statDown = [:SPEED,inc] - end -end - - - -#=============================================================================== -# Decreases the target's Special Attack by 2 stages. Only works on the opposite -# gender. (Captivate) -#=============================================================================== -class PokeBattle_Move_04E < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:SPECIAL_ATTACK,2] - end - - def pbFailsAgainstTarget?(user,target) - return true if super - return false if damagingMove? - if user.gender==2 || target.gender==2 || user.gender==target.gender - @battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis)) - return true - end - if target.hasActiveAbility?(:OBLIVIOUS) && !@battle.moldBreaker - @battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis)) - else - @battle.pbDisplay(_INTL("{1}'s {2} prevents romance!",target.pbThis,target.abilityName)) - end - @battle.pbHideAbilitySplash(target) - return true - end - return false - end - - def pbAdditionalEffect(user,target) - return if user.gender==2 || target.gender==2 || user.gender==target.gender - return if target.hasActiveAbility?(:OBLIVIOUS) && !@battle.moldBreaker - super - end -end - - - -#=============================================================================== -# Decreases the target's Special Defense by 2 stages. -#=============================================================================== -class PokeBattle_Move_04F < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:SPECIAL_DEFENSE,2] - end -end - - - -#=============================================================================== -# Resets all target's stat stages to 0. (Clear Smog) -#=============================================================================== -class PokeBattle_Move_050 < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - if target.damageState.calcDamage>0 && !target.damageState.substitute && - target.hasAlteredStatStages? - target.pbResetStatStages - @battle.pbDisplay(_INTL("{1}'s stat changes were removed!",target.pbThis)) - end - end -end - - - -#=============================================================================== -# Resets all stat stages for all battlers to 0. (Haze) -#=============================================================================== -class PokeBattle_Move_051 < PokeBattle_Move - def pbMoveFailed?(user,targets) - failed = true - @battle.eachBattler do |b| - failed = false if b.hasAlteredStatStages? - break if !failed - end - if failed - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - @battle.eachBattler { |b| b.pbResetStatStages } - @battle.pbDisplay(_INTL("All stat changes were eliminated!")) - end -end - - - -#=============================================================================== -# User and target swap their Attack and Special Attack stat stages. (Power Swap) -#=============================================================================== -class PokeBattle_Move_052 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbEffectAgainstTarget(user,target) - [:ATTACK,:SPECIAL_ATTACK].each do |s| - user.stages[s],target.stages[s] = target.stages[s],user.stages[s] - end - @battle.pbDisplay(_INTL("{1} switched all changes to its Attack and Sp. Atk with the target!",user.pbThis)) - end -end - - - -#=============================================================================== -# User and target swap their Defense and Special Defense stat stages. (Guard Swap) -#=============================================================================== -class PokeBattle_Move_053 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbEffectAgainstTarget(user,target) - [:DEFENSE,:SPECIAL_DEFENSE].each do |s| - user.stages[s],target.stages[s] = target.stages[s],user.stages[s] - end - @battle.pbDisplay(_INTL("{1} switched all changes to its Defense and Sp. Def with the target!",user.pbThis)) - end -end - - - -#=============================================================================== -# User and target swap all their stat stages. (Heart Swap) -#=============================================================================== -class PokeBattle_Move_054 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbEffectAgainstTarget(user,target) - GameData::Stat.each_battle do |s| - user.stages[s.id],target.stages[s.id] = target.stages[s.id],user.stages[s.id] - end - @battle.pbDisplay(_INTL("{1} switched stat changes with the target!",user.pbThis)) - end -end - - - -#=============================================================================== -# User copies the target's stat stages. (Psych Up) -#=============================================================================== -class PokeBattle_Move_055 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbEffectAgainstTarget(user,target) - GameData::Stat.each_battle { |s| user.stages[s.id] = target.stages[s.id] } - if Settings::NEW_CRITICAL_HIT_RATE_MECHANICS - user.effects[PBEffects::FocusEnergy] = target.effects[PBEffects::FocusEnergy] - user.effects[PBEffects::LaserFocus] = target.effects[PBEffects::LaserFocus] - end - @battle.pbDisplay(_INTL("{1} copied {2}'s stat changes!",user.pbThis,target.pbThis(true))) - end -end - - - -#=============================================================================== -# For 5 rounds, user's and ally's stat stages cannot be lowered by foes. (Mist) -#=============================================================================== -class PokeBattle_Move_056 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOwnSide.effects[PBEffects::Mist]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::Mist] = 5 - @battle.pbDisplay(_INTL("{1} became shrouded in mist!",user.pbTeam)) - end -end - - - -#=============================================================================== -# Swaps the user's Attack and Defense stats. (Power Trick) -#=============================================================================== -class PokeBattle_Move_057 < PokeBattle_Move - def pbEffectGeneral(user) - user.attack,user.defense = user.defense,user.attack - user.effects[PBEffects::PowerTrick] = !user.effects[PBEffects::PowerTrick] - @battle.pbDisplay(_INTL("{1} switched its Attack and Defense!",user.pbThis)) - end -end - - - -#=============================================================================== -# Averages the user's and target's Attack. -# Averages the user's and target's Special Attack. (Power Split) -#=============================================================================== -class PokeBattle_Move_058 < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - newatk = ((user.attack+target.attack)/2).floor - newspatk = ((user.spatk+target.spatk)/2).floor - user.attack = target.attack = newatk - user.spatk = target.spatk = newspatk - @battle.pbDisplay(_INTL("{1} shared its power with the target!",user.pbThis)) - end -end - - - -#=============================================================================== -# Averages the user's and target's Defense. -# Averages the user's and target's Special Defense. (Guard Split) -#=============================================================================== -class PokeBattle_Move_059 < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - newdef = ((user.defense+target.defense)/2).floor - newspdef = ((user.spdef+target.spdef)/2).floor - user.defense = target.defense = newdef - user.spdef = target.spdef = newspdef - @battle.pbDisplay(_INTL("{1} shared its guard with the target!",user.pbThis)) - end -end - - - -#=============================================================================== -# Averages the user's and target's current HP. (Pain Split) -#=============================================================================== -class PokeBattle_Move_05A < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - newHP = (user.hp+target.hp)/2 - if user.hp>newHP; user.pbReduceHP(user.hp-newHP,false,false) - elsif user.hpnewHP; target.pbReduceHP(target.hp-newHP,false,false) - elsif target.hp0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::Tailwind] = 4 - @battle.pbDisplay(_INTL("The Tailwind blew from behind {1}!",user.pbTeam(true))) - end -end - - - -#=============================================================================== -# This move turns into the last move used by the target, until user switches -# out. (Mimic) -#=============================================================================== -class PokeBattle_Move_05C < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def initialize(battle,move) - super - @moveBlacklist = [ - "014", # Chatter - "0B6", # Metronome - # Struggle - "002", # Struggle - # Moves that affect the moveset - "05C", # Mimic - "05D", # Sketch - "069" # Transform - ] - end - - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Transform] || !user.pbHasMove?(@id) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - lastMoveData = GameData::Move.try_get(target.lastRegularMoveUsed) - if !lastMoveData || - user.pbHasMove?(target.lastRegularMoveUsed) || - @moveBlacklist.include?(lastMoveData.function_code) || - lastMoveData.type == :SHADOW - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - user.eachMoveWithIndex do |m,i| - next if m.id!=@id - newMove = Pokemon::Move.new(target.lastRegularMoveUsed) - user.moves[i] = PokeBattle_Move.from_pokemon_move(@battle,newMove) - @battle.pbDisplay(_INTL("{1} learned {2}!",user.pbThis,newMove.name)) - user.pbCheckFormOnMovesetChange - break - end - end -end - - - -#=============================================================================== -# This move permanently turns into the last move used by the target. (Sketch) -#=============================================================================== -class PokeBattle_Move_05D < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def initialize(battle,move) - super - @moveBlacklist = [ - "014", # Chatter - "05D", # Sketch (this move) - # Struggle - "002" # Struggle - ] - end - - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Transform] || !user.pbHasMove?(@id) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - lastMoveData = GameData::Move.try_get(target.lastRegularMoveUsed) - if !lastMoveData || - user.pbHasMove?(target.lastRegularMoveUsed) || - @moveBlacklist.include?(lastMoveData.function_code) || - lastMoveData.type == :SHADOW - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - user.eachMoveWithIndex do |m,i| - next if m.id!=@id - newMove = Pokemon::Move.new(target.lastRegularMoveUsed) - user.pokemon.moves[i] = newMove - user.moves[i] = PokeBattle_Move.from_pokemon_move(@battle,newMove) - @battle.pbDisplay(_INTL("{1} learned {2}!",user.pbThis,newMove.name)) - user.pbCheckFormOnMovesetChange - break - end - end -end - - - -#=============================================================================== -# Changes user's type to that of a random user's move, except a type the user -# already has (even partially), OR changes to the user's first move's type. -# (Conversion) -#=============================================================================== -class PokeBattle_Move_05E < PokeBattle_Move - def pbMoveFailed?(user,targets) - if !user.canChangeType? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - userTypes = user.pbTypes(true) - @newTypes = [] - user.eachMoveWithIndex do |m,i| - break if Settings::MECHANICS_GENERATION >= 6 && i>0 - next if GameData::Type.get(m.type).pseudo_type - next if userTypes.include?(m.type) - @newTypes.push(m.type) if !@newTypes.include?(m.type) - end - if @newTypes.length==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - newType = @newTypes[@battle.pbRandom(@newTypes.length)] - user.pbChangeTypes(newType) - typeName = GameData::Type.get(newType).name - @battle.pbDisplay(_INTL("{1} transformed into the {2} type!",user.pbThis,typeName)) - end -end - - - -#=============================================================================== -# Changes user's type to a random one that resists/is immune to the last move -# used by the target. (Conversion 2) -#=============================================================================== -class PokeBattle_Move_05F < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbMoveFailed?(user, targets) - if !user.canChangeType? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user, target) - if !target.lastMoveUsed || !target.lastMoveUsedType || - GameData::Type.get(target.lastMoveUsedType).pseudo_type - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - @newTypes = [] - GameData::Type.each do |t| - next if t.pseudo_type || user.pbHasType?(t.id) || - !Effectiveness.resistant_type?(target.lastMoveUsedType, t.id) - @newTypes.push(t.id) - end - if @newTypes.length == 0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - newType = @newTypes[@battle.pbRandom(@newTypes.length)] - user.pbChangeTypes(newType) - typeName = GameData::Type.get(newType).name - @battle.pbDisplay(_INTL("{1} transformed into the {2} type!", user.pbThis, typeName)) - end -end - - - -#=============================================================================== -# Changes user's type depending on the environment. (Camouflage) -#=============================================================================== -class PokeBattle_Move_060 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if !user.canChangeType? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - @newType = :NORMAL - checkedTerrain = false - case @battle.field.terrain - when :Electric - if GameData::Type.exists?(:ELECTRIC) - @newType = :ELECTRIC - checkedTerrain = true - end - when :Grassy - if GameData::Type.exists?(:GRASS) - @newType = :GRASS - checkedTerrain = true - end - when :Misty - if GameData::Type.exists?(:FAIRY) - @newType = :FAIRY - checkedTerrain = true - end - when :Psychic - if GameData::Type.exists?(:PSYCHIC) - @newType = :PSYCHIC - checkedTerrain = true - end - end - if !checkedTerrain - case @battle.environment - when :Grass, :TallGrass - @newType = :GRASS - when :MovingWater, :StillWater, :Puddle, :Underwater - @newType = :WATER - when :Cave - @newType = :ROCK - when :Rock, :Sand - @newType = :GROUND - when :Forest, :ForestGrass - @newType = :BUG - when :Snow, :Ice - @newType = :ICE - when :Volcano - @newType = :FIRE - when :Graveyard - @newType = :GHOST - when :Sky - @newType = :FLYING - when :Space - @newType = :DRAGON - when :UltraSpace - @newType = :PSYCHIC - end - end - @newType = :NORMAL if !GameData::Type.exists?(@newType) - if !GameData::Type.exists?(@newType) || !user.pbHasOtherType?(@newType) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbChangeTypes(@newType) - typeName = GameData::Type.get(@newType).name - @battle.pbDisplay(_INTL("{1} transformed into the {2} type!",user.pbThis,typeName)) - end -end - - - -#=============================================================================== -# Target becomes Water type. (Soak) -#=============================================================================== -class PokeBattle_Move_061 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if !target.canChangeType? || !GameData::Type.exists?(:WATER) || - !target.pbHasOtherType?(:WATER) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.pbChangeTypes(:WATER) - typeName = GameData::Type.get(:WATER).name - @battle.pbDisplay(_INTL("{1} transformed into the {2} type!",target.pbThis,typeName)) - end -end - - - -#=============================================================================== -# User copes target's types. (Reflect Type) -#=============================================================================== -class PokeBattle_Move_062 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbMoveFailed?(user,targets) - if !user.canChangeType? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - newTypes = target.pbTypes(true) - if newTypes.length==0 # Target has no type to copy - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if user.pbTypes==target.pbTypes && - user.effects[PBEffects::Type3]==target.effects[PBEffects::Type3] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - user.pbChangeTypes(target) - @battle.pbDisplay(_INTL("{1}'s type changed to match {2}'s!", - user.pbThis,target.pbThis(true))) - end -end - - - -#=============================================================================== -# Target's ability becomes Simple. (Simple Beam) -#=============================================================================== -class PokeBattle_Move_063 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if !GameData::Ability.exists?(:SIMPLE) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if target.unstoppableAbility? || [:TRUANT, :SIMPLE].include?(target.ability) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - @battle.pbShowAbilitySplash(target,true,false) - oldAbil = target.ability - target.ability = :SIMPLE - @battle.pbReplaceAbilitySplash(target) - @battle.pbDisplay(_INTL("{1} acquired {2}!",target.pbThis,target.abilityName)) - @battle.pbHideAbilitySplash(target) - target.pbOnAbilityChanged(oldAbil) - end -end - - - -#=============================================================================== -# Target's ability becomes Insomnia. (Worry Seed) -#=============================================================================== -class PokeBattle_Move_064 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if !GameData::Ability.exists?(:INSOMNIA) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if target.unstoppableAbility? || [:TRUANT, :INSOMNIA].include?(target.ability_id) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - @battle.pbShowAbilitySplash(target,true,false) - oldAbil = target.ability - target.ability = :INSOMNIA - @battle.pbReplaceAbilitySplash(target) - @battle.pbDisplay(_INTL("{1} acquired {2}!",target.pbThis,target.abilityName)) - @battle.pbHideAbilitySplash(target) - target.pbOnAbilityChanged(oldAbil) - end -end - - - -#=============================================================================== -# User copies target's ability. (Role Play) -#=============================================================================== -class PokeBattle_Move_065 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbMoveFailed?(user,targets) - if user.unstoppableAbility? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if !target.ability || user.ability==target.ability - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.ungainableAbility? || - [:POWEROFALCHEMY, :RECEIVER, :TRACE, :WONDERGUARD].include?(target.ability_id) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - @battle.pbShowAbilitySplash(user,true,false) - oldAbil = user.ability - user.ability = target.ability - @battle.pbReplaceAbilitySplash(user) - @battle.pbDisplay(_INTL("{1} copied {2}'s {3}!", - user.pbThis,target.pbThis(true),target.abilityName)) - @battle.pbHideAbilitySplash(user) - user.pbOnAbilityChanged(oldAbil) - user.pbEffectsOnSwitchIn - end -end - - - -#=============================================================================== -# Target copies user's ability. (Entrainment) -#=============================================================================== -class PokeBattle_Move_066 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if !user.ability - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if user.ungainableAbility? || - [:POWEROFALCHEMY, :RECEIVER, :TRACE].include?(user.ability_id) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if target.unstoppableAbility? || target.ability == :TRUANT - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - @battle.pbShowAbilitySplash(target,true,false) - oldAbil = target.ability - target.ability = user.ability - @battle.pbReplaceAbilitySplash(target) - @battle.pbDisplay(_INTL("{1} acquired {2}!",target.pbThis,target.abilityName)) - @battle.pbHideAbilitySplash(target) - target.pbOnAbilityChanged(oldAbil) - target.pbEffectsOnSwitchIn - end -end - - - -#=============================================================================== -# User and target swap abilities. (Skill Swap) -#=============================================================================== -class PokeBattle_Move_067 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbMoveFailed?(user,targets) - if !user.ability - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if user.unstoppableAbility? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if user.ungainableAbility? || user.ability == :WONDERGUARD - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if !target.ability || - (user.ability == target.ability && Settings::MECHANICS_GENERATION <= 5) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.unstoppableAbility? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.ungainableAbility? || target.ability == :WONDERGUARD - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - if user.opposes?(target) - @battle.pbShowAbilitySplash(user,false,false) - @battle.pbShowAbilitySplash(target,true,false) - end - oldUserAbil = user.ability - oldTargetAbil = target.ability - user.ability = oldTargetAbil - target.ability = oldUserAbil - if user.opposes?(target) - @battle.pbReplaceAbilitySplash(user) - @battle.pbReplaceAbilitySplash(target) - end - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} swapped Abilities with its target!",user.pbThis)) - else - @battle.pbDisplay(_INTL("{1} swapped its {2} Ability with its target's {3} Ability!", - user.pbThis,target.abilityName,user.abilityName)) - end - if user.opposes?(target) - @battle.pbHideAbilitySplash(user) - @battle.pbHideAbilitySplash(target) - end - user.pbOnAbilityChanged(oldUserAbil) - target.pbOnAbilityChanged(oldTargetAbil) - user.pbEffectsOnSwitchIn - target.pbEffectsOnSwitchIn - end -end - - - -#=============================================================================== -# Target's ability is negated. (Gastro Acid) -#=============================================================================== -class PokeBattle_Move_068 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if target.unstoppableAbility? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::GastroAcid] = true - target.effects[PBEffects::Truant] = false - @battle.pbDisplay(_INTL("{1}'s Ability was suppressed!",target.pbThis)) - target.pbOnAbilityChanged(target.ability) - end -end - - - -#=============================================================================== -# User transforms into the target. (Transform) -#=============================================================================== -class PokeBattle_Move_069 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Transform] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Transform] || - target.effects[PBEffects::Illusion] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - user.pbTransform(target) - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - super - @battle.scene.pbChangePokemon(user,targets[0].pokemon) - end -end - - - -#=============================================================================== -# Inflicts a fixed 20HP damage. (Sonic Boom) -#=============================================================================== -class PokeBattle_Move_06A < PokeBattle_FixedDamageMove - def pbFixedDamage(user,target) - return 20 - end -end - - - -#=============================================================================== -# Inflicts a fixed 40HP damage. (Dragon Rage) -#=============================================================================== -class PokeBattle_Move_06B < PokeBattle_FixedDamageMove - def pbFixedDamage(user,target) - return 40 - end -end - - - -#=============================================================================== -# Halves the target's current HP. (Nature's Madness, Super Fang) -#=============================================================================== -class PokeBattle_Move_06C < PokeBattle_FixedDamageMove - def pbFixedDamage(user,target) - return (target.hp/2.0).round - end -end - - - -#=============================================================================== -# Inflicts damage equal to the user's level. (Night Shade, Seismic Toss) -#=============================================================================== -class PokeBattle_Move_06D < PokeBattle_FixedDamageMove - def pbFixedDamage(user,target) - return user.level - end -end - - - -#=============================================================================== -# Inflicts damage to bring the target's HP down to equal the user's HP. (Endeavor) -#=============================================================================== -class PokeBattle_Move_06E < PokeBattle_FixedDamageMove - def pbFailsAgainstTarget?(user,target) - if user.hp>=target.hp - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbNumHits(user,targets); return 1; end - - def pbFixedDamage(user,target) - return target.hp-user.hp - end -end - - - -#=============================================================================== -# Inflicts damage between 0.5 and 1.5 times the user's level. (Psywave) -#=============================================================================== -class PokeBattle_Move_06F < PokeBattle_FixedDamageMove - def pbFixedDamage(user,target) - min = (user.level/2).floor - max = (user.level*3/2).floor - return min+@battle.pbRandom(max-min+1) - end -end - - - -#=============================================================================== -# OHKO. Accuracy increases by difference between levels of user and target. -#=============================================================================== -class PokeBattle_Move_070 < PokeBattle_FixedDamageMove - def hitsDiggingTargets?; return @id == :FISSURE; end - - def pbFailsAgainstTarget?(user,target) - if target.level>user.level - @battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis)) - return true - end - if target.hasActiveAbility?(:STURDY) && !@battle.moldBreaker - @battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("But it failed to affect {1}!",target.pbThis(true))) - else - @battle.pbDisplay(_INTL("But it failed to affect {1} because of its {2}!", - target.pbThis(true),target.abilityName)) - end - @battle.pbHideAbilitySplash(target) - return true - end - if Settings::MECHANICS_GENERATION >= 7 && @id == :SHEERCOLD && target.pbHasType?(:ICE) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbAccuracyCheck(user,target) - acc = @accuracy+user.level-target.level - acc -= 10 if Settings::MECHANICS_GENERATION >= 7 && @id == :SHEERCOLD && !user.pbHasType?(:ICE) - return @battle.pbRandom(100)0 - hitAlly.each do |b| - @battle.pbDisplay(_INTL("The bursting flame hit {1}!", - @battle.battlers[b[0]].pbThis(true))) - end - end - switchedAlly = [] - hitAlly.each do |b| - @battle.battlers[b[0]].pbItemHPHealCheck - if @battle.battlers[b[0]].pbAbilitiesOnDamageTaken(b[1]) - switchedAlly.push(@battle.battlers[b[0]]) - end - end - switchedAlly.each { |b| b.pbEffectsOnSwitchIn(true) } - end -end - - - -#=============================================================================== -# Power is doubled if the target is using Dive. Hits some semi-invulnerable -# targets. (Surf) -#=============================================================================== -class PokeBattle_Move_075 < PokeBattle_Move - def hitsDivingTargets?; return true; end - - def pbModifyDamage(damageMult,user,target) - damageMult *= 2 if target.inTwoTurnAttack?("0CB") # Dive - return damageMult - end -end - - - -#=============================================================================== -# Power is doubled if the target is using Dig. Power is halved if Grassy Terrain -# is in effect. Hits some semi-invulnerable targets. (Earthquake) -#=============================================================================== -class PokeBattle_Move_076 < PokeBattle_Move - def hitsDiggingTargets?; return true; end - - def pbModifyDamage(damageMult,user,target) - damageMult *= 2 if target.inTwoTurnAttack?("0CA") # Dig - damageMult /= 2 if @battle.field.terrain == :Grassy - return damageMult - end -end - - - -#=============================================================================== -# Power is doubled if the target is using Bounce, Fly or Sky Drop. Hits some -# semi-invulnerable targets. (Gust) -#=============================================================================== -class PokeBattle_Move_077 < PokeBattle_Move - def hitsFlyingTargets?; return true; end - - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if target.inTwoTurnAttack?("0C9","0CC","0CE") || # Fly/Bounce/Sky Drop - target.effects[PBEffects::SkyDrop]>=0 - return baseDmg - end -end - - - -#=============================================================================== -# Power is doubled if the target is using Bounce, Fly or Sky Drop. Hits some -# semi-invulnerable targets. May make the target flinch. (Twister) -#=============================================================================== -class PokeBattle_Move_078 < PokeBattle_FlinchMove - def hitsFlyingTargets?; return true; end - - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if target.inTwoTurnAttack?("0C9","0CC","0CE") || # Fly/Bounce/Sky Drop - target.effects[PBEffects::SkyDrop]>=0 - return baseDmg - end -end - - - -#=============================================================================== -# Power is doubled if Fusion Flare has already been used this round. (Fusion Bolt) -#=============================================================================== -class PokeBattle_Move_079 < PokeBattle_Move - def pbChangeUsageCounters(user,specialUsage) - @doublePower = @battle.field.effects[PBEffects::FusionFlare] - super - end - - def pbBaseDamageMultiplier(damageMult,user,target) - damageMult *= 2 if @doublePower - return damageMult - end - - def pbEffectGeneral(user) - @battle.field.effects[PBEffects::FusionBolt] = true - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - hitNum = 1 if (targets.length>0 && targets[0].damageState.critical) || - @doublePower # Charged anim - super - end -end - - - -#=============================================================================== -# Power is doubled if Fusion Bolt has already been used this round. (Fusion Flare) -#=============================================================================== -class PokeBattle_Move_07A < PokeBattle_Move - def pbChangeUsageCounters(user,specialUsage) - @doublePower = @battle.field.effects[PBEffects::FusionBolt] - super - end - - def pbBaseDamageMultiplier(damageMult,user,target) - damageMult *= 2 if @doublePower - return damageMult - end - - def pbEffectGeneral(user) - @battle.field.effects[PBEffects::FusionFlare] = true - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - hitNum = 1 if (targets.length>0 && targets[0].damageState.critical) || - @doublePower # Charged anim - super - end -end - - - -#=============================================================================== -# Power is doubled if the target is poisoned. (Venoshock) -#=============================================================================== -class PokeBattle_Move_07B < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - if target.poisoned? && - (target.effects[PBEffects::Substitute]==0 || ignoresSubstitute?(user)) - baseDmg *= 2 - end - return baseDmg - end -end - - - -#=============================================================================== -# Power is doubled if the target is paralyzed. Cures the target of paralysis. -# (Smelling Salts) -#=============================================================================== -class PokeBattle_Move_07C < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - if target.paralyzed? && - (target.effects[PBEffects::Substitute]==0 || ignoresSubstitute?(user)) - baseDmg *= 2 - end - return baseDmg - end - - def pbEffectAfterAllHits(user,target) - return if target.fainted? - return if target.damageState.unaffected || target.damageState.substitute - return if target.status != :PARALYSIS - target.pbCureStatus - end -end - - - -#=============================================================================== -# Power is doubled if the target is asleep. Wakes the target up. (Wake-Up Slap) -#=============================================================================== -class PokeBattle_Move_07D < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - if target.asleep? && - (target.effects[PBEffects::Substitute]==0 || ignoresSubstitute?(user)) - baseDmg *= 2 - end - return baseDmg - end - - def pbEffectAfterAllHits(user,target) - return if target.fainted? - return if target.damageState.unaffected || target.damageState.substitute - return if target.status != :SLEEP - target.pbCureStatus - end -end - - - -#=============================================================================== -# Power is doubled if the user is burned, poisoned or paralyzed. (Facade) -# Burn's halving of Attack is negated (new mechanics). -#=============================================================================== -class PokeBattle_Move_07E < PokeBattle_Move - def damageReducedByBurn?; return Settings::MECHANICS_GENERATION <= 5; end - - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if user.poisoned? || user.burned? || user.paralyzed? - return baseDmg - end -end - - - -#=============================================================================== -# Power is doubled if the target has a status problem. (Hex) -#=============================================================================== -class PokeBattle_Move_07F < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - if target.pbHasAnyStatus? && - (target.effects[PBEffects::Substitute]==0 || ignoresSubstitute?(user)) - baseDmg *= 2 - end - return baseDmg - end -end diff --git a/Data/Scripts/011_Battle/002_Move/006_Move_Effects_080-0FF.rb b/Data/Scripts/011_Battle/002_Move/006_Move_Effects_080-0FF.rb deleted file mode 100644 index c6a7a59582..0000000000 --- a/Data/Scripts/011_Battle/002_Move/006_Move_Effects_080-0FF.rb +++ /dev/null @@ -1,3722 +0,0 @@ -#=============================================================================== -# Power is doubled if the target's HP is down to 1/2 or less. (Brine) -#=============================================================================== -class PokeBattle_Move_080 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if target.hp<=target.totalhp/2 - return baseDmg - end -end - - - -#=============================================================================== -# Power is doubled if the user has lost HP due to the target's move this round. -# (Avalanche, Revenge) -#=============================================================================== -class PokeBattle_Move_081 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if user.lastAttacker.include?(target.index) - return baseDmg - end -end - - - -#=============================================================================== -# Power is doubled if the target has already lost HP this round. (Assurance) -#=============================================================================== -class PokeBattle_Move_082 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if target.tookDamage - return baseDmg - end -end - - - -#=============================================================================== -# Power is doubled if a user's ally has already used this move this round. (Round) -# If an ally is about to use the same move, make it go next, ignoring priority. -#=============================================================================== -class PokeBattle_Move_083 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if user.pbOwnSide.effects[PBEffects::Round] - return baseDmg - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::Round] = true - user.eachAlly do |b| - next if @battle.choices[b.index][0]!=:UseMove || b.movedThisRound? - next if @battle.choices[b.index][2].function!=@function - b.effects[PBEffects::MoveNext] = true - b.effects[PBEffects::Quash] = 0 - break - end - end -end - - - -#=============================================================================== -# Power is doubled if the target has already moved this round. (Payback) -#=============================================================================== -class PokeBattle_Move_084 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - if @battle.choices[target.index][0]!=:None && - ((@battle.choices[target.index][0]!=:UseMove && - @battle.choices[target.index][0]!=:Shift) || target.movedThisRound?) - baseDmg *= 2 - end - return baseDmg - end -end - - - -#=============================================================================== -# Power is doubled if a user's teammate fainted last round. (Retaliate) -#=============================================================================== -class PokeBattle_Move_085 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - lrf = user.pbOwnSide.effects[PBEffects::LastRoundFainted] - baseDmg *= 2 if lrf>=0 && lrf==@battle.turnCount-1 - return baseDmg - end -end - - - -#=============================================================================== -# Power is doubled if the user has no held item. (Acrobatics) -#=============================================================================== -class PokeBattle_Move_086 < PokeBattle_Move - def pbBaseDamageMultiplier(damageMult,user,target) - damageMult *= 2 if !user.item - return damageMult - end -end - - - -#=============================================================================== -# Power is doubled in weather. Type changes depending on the weather. (Weather Ball) -#=============================================================================== -class PokeBattle_Move_087 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if @battle.pbWeather != :None - return baseDmg - end - - def pbBaseType(user) - ret = :NORMAL - case @battle.pbWeather - when :Sun, :HarshSun - ret = :FIRE if GameData::Type.exists?(:FIRE) - when :Rain, :HeavyRain - ret = :WATER if GameData::Type.exists?(:WATER) - when :Sandstorm - ret = :ROCK if GameData::Type.exists?(:ROCK) - when :Hail - ret = :ICE if GameData::Type.exists?(:ICE) - end - return ret - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - t = pbBaseType(user) - hitNum = 1 if t == :FIRE # Type-specific anims - hitNum = 2 if t == :WATER - hitNum = 3 if t == :ROCK - hitNum = 4 if t == :ICE - super - end -end - - - -#=============================================================================== -# Interrupts a foe switching out or using U-turn/Volt Switch/Parting Shot. Power -# is doubled in that case. (Pursuit) -# (Handled in Battle's pbAttackPhase): Makes this attack happen before switching. -#=============================================================================== -class PokeBattle_Move_088 < PokeBattle_Move - def pbAccuracyCheck(user,target) - return true if @battle.switching - return super - end - - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if @battle.switching - return baseDmg - end -end - - - -#=============================================================================== -# Power increases with the user's happiness. (Return) -#=============================================================================== -class PokeBattle_Move_089 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - return [(user.happiness*2/5).floor,1].max - end -end - - - -#=============================================================================== -# Power decreases with the user's happiness. (Frustration) -#=============================================================================== -class PokeBattle_Move_08A < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - return [((255-user.happiness)*2/5).floor,1].max - end -end - - - -#=============================================================================== -# Power increases with the user's HP. (Eruption, Water Spout) -#=============================================================================== -class PokeBattle_Move_08B < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - return [150*user.hp/user.totalhp,1].max - end -end - - - -#=============================================================================== -# Power increases with the target's HP. (Crush Grip, Wring Out) -#=============================================================================== -class PokeBattle_Move_08C < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - return [120*target.hp/target.totalhp,1].max - end -end - - - -#=============================================================================== -# Power increases the quicker the target is than the user. (Gyro Ball) -#=============================================================================== -class PokeBattle_Move_08D < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - return [[(25*target.pbSpeed/user.pbSpeed).floor,150].min,1].max - end -end - - - -#=============================================================================== -# Power increases with the user's positive stat changes (ignores negative ones). -# (Power Trip, Stored Power) -#=============================================================================== -class PokeBattle_Move_08E < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - mult = 1 - GameData::Stat.each_battle { |s| mult += user.stages[s.id] if user.stages[s.id] > 0 } - return 20 * mult - end -end - - - -#=============================================================================== -# Power increases with the target's positive stat changes (ignores negative ones). -# (Punishment) -#=============================================================================== -class PokeBattle_Move_08F < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - mult = 3 - GameData::Stat.each_battle { |s| mult += target.stages[s.id] if target.stages[s.id] > 0 } - return [20 * mult, 200].min - end -end - - - -#=============================================================================== -# Power and type depends on the user's IVs. (Hidden Power) -#=============================================================================== -class PokeBattle_Move_090 < PokeBattle_Move - def pbBaseType(user) - hp = pbHiddenPower(user) - return hp[0] - end - - def pbBaseDamage(baseDmg,user,target) - return super if Settings::MECHANICS_GENERATION >= 6 - hp = pbHiddenPower(user) - return hp[1] - end -end - - - -def pbHiddenPower(pkmn) - # NOTE: This allows Hidden Power to be Fairy-type (if you have that type in - # your game). I don't care that the official games don't work like that. - iv = pkmn.iv - idxType = 0; power = 60 - types = [] - GameData::Type.each { |t| types.push(t.id) if !t.pseudo_type && ![:NORMAL, :SHADOW].include?(t.id)} - types.sort! { |a, b| GameData::Type.get(a).id_number <=> GameData::Type.get(b).id_number } - idxType |= (iv[:HP]&1) - idxType |= (iv[:ATTACK]&1)<<1 - idxType |= (iv[:DEFENSE]&1)<<2 - idxType |= (iv[:SPEED]&1)<<3 - idxType |= (iv[:SPECIAL_ATTACK]&1)<<4 - idxType |= (iv[:SPECIAL_DEFENSE]&1)<<5 - idxType = (types.length-1)*idxType/63 - type = types[idxType] - if Settings::MECHANICS_GENERATION <= 5 - powerMin = 30 - powerMax = 70 - power |= (iv[:HP]&2)>>1 - power |= (iv[:ATTACK]&2) - power |= (iv[:DEFENSE]&2)<<1 - power |= (iv[:SPEED]&2)<<2 - power |= (iv[:SPECIAL_ATTACK]&2)<<3 - power |= (iv[:SPECIAL_DEFENSE]&2)<<4 - power = powerMin+(powerMax-powerMin)*power/63 - end - return [type,power] -end - - - -#=============================================================================== -# Power doubles for each consecutive use. (Fury Cutter) -#=============================================================================== -class PokeBattle_Move_091 < PokeBattle_Move - def pbChangeUsageCounters(user,specialUsage) - oldVal = user.effects[PBEffects::FuryCutter] - super - maxMult = 1 - while (@baseDamage<<(maxMult-1))<160 - maxMult += 1 # 1-4 for base damage of 20, 1-3 for base damage of 40 - end - user.effects[PBEffects::FuryCutter] = (oldVal>=maxMult) ? maxMult : oldVal+1 - end - - def pbBaseDamage(baseDmg,user,target) - return baseDmg<<(user.effects[PBEffects::FuryCutter]-1) - end -end - - - -#=============================================================================== -# Power is multiplied by the number of consecutive rounds in which this move was -# used by any Pokémon on the user's side. (Echoed Voice) -#=============================================================================== -class PokeBattle_Move_092 < PokeBattle_Move - def pbChangeUsageCounters(user,specialUsage) - oldVal = user.pbOwnSide.effects[PBEffects::EchoedVoiceCounter] - super - if !user.pbOwnSide.effects[PBEffects::EchoedVoiceUsed] - user.pbOwnSide.effects[PBEffects::EchoedVoiceCounter] = (oldVal>=5) ? 5 : oldVal+1 - end - user.pbOwnSide.effects[PBEffects::EchoedVoiceUsed] = true - end - - def pbBaseDamage(baseDmg,user,target) - return baseDmg*user.pbOwnSide.effects[PBEffects::EchoedVoiceCounter] # 1-5 - end -end - - - -#=============================================================================== -# User rages until the start of a round in which they don't use this move. (Rage) -# (Handled in Battler's pbProcessMoveAgainstTarget): Ups rager's Attack by 1 -# stage each time it loses HP due to a move. -#=============================================================================== -class PokeBattle_Move_093 < PokeBattle_Move - def pbEffectGeneral(user) - user.effects[PBEffects::Rage] = true - end -end - - - -#=============================================================================== -# Randomly damages or heals the target. (Present) -# NOTE: Apparently a Normal Gem should be consumed even if this move will heal, -# but I think that's silly so I've omitted that effect. -#=============================================================================== -class PokeBattle_Move_094 < PokeBattle_Move - def pbOnStartUse(user,targets) - @presentDmg = 0 # 0 = heal, >0 = damage - r = @battle.pbRandom(100) - if r<40; @presentDmg = 40 - elsif r<70; @presentDmg = 80 - elsif r<80; @presentDmg = 120 - end - end - - def pbFailsAgainstTarget?(user,target) - return false if @presentDmg>0 - if !target.canHeal? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbDamagingMove? - return false if @presentDmg==0 - return super - end - - def pbBaseDamage(baseDmg,user,target) - return @presentDmg - end - - def pbEffectAgainstTarget(user,target) - return if @presentDmg>0 - target.pbRecoverHP(target.totalhp/4) - @battle.pbDisplay(_INTL("{1}'s HP was restored.",target.pbThis)) - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - hitNum = 1 if @presentDmg==0 # Healing anim - super - end -end - - - -#=============================================================================== -# Power is chosen at random. Power is doubled if the target is using Dig. Hits -# some semi-invulnerable targets. (Magnitude) -#=============================================================================== -class PokeBattle_Move_095 < PokeBattle_Move - def hitsDiggingTargets?; return true; end - - def pbOnStartUse(user,targets) - baseDmg = [10,30,50,70,90,110,150] - magnitudes = [ - 4, - 5,5, - 6,6,6,6, - 7,7,7,7,7,7, - 8,8,8,8, - 9,9, - 10 - ] - magni = magnitudes[@battle.pbRandom(magnitudes.length)] - @magnitudeDmg = baseDmg[magni-4] - @battle.pbDisplay(_INTL("Magnitude {1}!",magni)) - end - - def pbBaseDamage(baseDmg,user,target) - return @magnitudeDmg - end - - def pbModifyDamage(damageMult,user,target) - damageMult *= 2 if target.inTwoTurnAttack?("0CA") # Dig - damageMult /= 2 if @battle.field.terrain == :Grassy - return damageMult - end -end - - - -#=============================================================================== -# Power and type depend on the user's held berry. Destroys the berry. -# (Natural Gift) -#=============================================================================== -class PokeBattle_Move_096 < PokeBattle_Move - def initialize(battle,move) - super - @typeArray = { - :NORMAL => [:CHILANBERRY], - :FIRE => [:CHERIBERRY, :BLUKBERRY, :WATMELBERRY, :OCCABERRY], - :WATER => [:CHESTOBERRY, :NANABBERRY, :DURINBERRY, :PASSHOBERRY], - :ELECTRIC => [:PECHABERRY, :WEPEARBERRY, :BELUEBERRY, :WACANBERRY], - :GRASS => [:RAWSTBERRY, :PINAPBERRY, :RINDOBERRY, :LIECHIBERRY], - :ICE => [:ASPEARBERRY, :POMEGBERRY, :YACHEBERRY, :GANLONBERRY], - :FIGHTING => [:LEPPABERRY, :KELPSYBERRY, :CHOPLEBERRY, :SALACBERRY], - :POISON => [:ORANBERRY, :QUALOTBERRY, :KEBIABERRY, :PETAYABERRY], - :GROUND => [:PERSIMBERRY, :HONDEWBERRY, :SHUCABERRY, :APICOTBERRY], - :FLYING => [:LUMBERRY, :GREPABERRY, :COBABERRY, :LANSATBERRY], - :PSYCHIC => [:SITRUSBERRY, :TAMATOBERRY, :PAYAPABERRY, :STARFBERRY], - :BUG => [:FIGYBERRY, :CORNNBERRY, :TANGABERRY, :ENIGMABERRY], - :ROCK => [:WIKIBERRY, :MAGOSTBERRY, :CHARTIBERRY, :MICLEBERRY], - :GHOST => [:MAGOBERRY, :RABUTABERRY, :KASIBBERRY, :CUSTAPBERRY], - :DRAGON => [:AGUAVBERRY, :NOMELBERRY, :HABANBERRY, :JABOCABERRY], - :DARK => [:IAPAPABERRY, :SPELONBERRY, :COLBURBERRY, :ROWAPBERRY, :MARANGABERRY], - :STEEL => [:RAZZBERRY, :PAMTREBERRY, :BABIRIBERRY], - :FAIRY => [:ROSELIBERRY, :KEEBERRY] - } - @damageArray = { - 60 => [:CHERIBERRY, :CHESTOBERRY, :PECHABERRY, :RAWSTBERRY, :ASPEARBERRY, - :LEPPABERRY, :ORANBERRY, :PERSIMBERRY, :LUMBERRY, :SITRUSBERRY, - :FIGYBERRY, :WIKIBERRY, :MAGOBERRY, :AGUAVBERRY, :IAPAPABERRY, - :RAZZBERRY, :OCCABERRY, :PASSHOBERRY, :WACANBERRY, :RINDOBERRY, - :YACHEBERRY, :CHOPLEBERRY, :KEBIABERRY, :SHUCABERRY, :COBABERRY, - :PAYAPABERRY, :TANGABERRY, :CHARTIBERRY, :KASIBBERRY, :HABANBERRY, - :COLBURBERRY, :BABIRIBERRY, :CHILANBERRY, :ROSELIBERRY], - 70 => [:BLUKBERRY, :NANABBERRY, :WEPEARBERRY, :PINAPBERRY, :POMEGBERRY, - :KELPSYBERRY, :QUALOTBERRY, :HONDEWBERRY, :GREPABERRY, :TAMATOBERRY, - :CORNNBERRY, :MAGOSTBERRY, :RABUTABERRY, :NOMELBERRY, :SPELONBERRY, - :PAMTREBERRY], - 80 => [:WATMELBERRY, :DURINBERRY, :BELUEBERRY, :LIECHIBERRY, :GANLONBERRY, - :SALACBERRY, :PETAYABERRY, :APICOTBERRY, :LANSATBERRY, :STARFBERRY, - :ENIGMABERRY, :MICLEBERRY, :CUSTAPBERRY, :JABOCABERRY, :ROWAPBERRY, - :KEEBERRY, :MARANGABERRY] - } - end - - def pbMoveFailed?(user,targets) - # NOTE: Unnerve does not stop a Pokémon using this move. - item = user.item - if !item || !item.is_berry? || !user.itemActive? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - # NOTE: The AI calls this method via pbCalcType, but it involves user.item - # which here is assumed to be not nil (because item.id is called). Since - # the AI won't want to use it if the user has no item anyway, perhaps - # this is good enough. - def pbBaseType(user) - item = user.item - ret = :NORMAL - if item - @typeArray.each do |type, items| - next if !items.include?(item.id) - ret = type if GameData::Type.exists?(type) - break - end - end - return ret - end - - # This is a separate method so that the AI can use it as well - def pbNaturalGiftBaseDamage(heldItem) - ret = 1 - @damageArray.each do |dmg, items| - next if !items.include?(heldItem) - ret = dmg - ret += 20 if Settings::MECHANICS_GENERATION >= 6 - break - end - return ret - end - - def pbBaseDamage(baseDmg,user,target) - return pbNaturalGiftBaseDamage(user.item.id) - end - - def pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers) - # NOTE: The item is consumed even if this move was Protected against or it - # missed. The item is not consumed if the target was switched out by - # an effect like a target's Red Card. - # NOTE: There is no item consumption animation. - user.pbConsumeItem(true,true,false) if user.item - end -end - - - -#=============================================================================== -# Power increases the less PP this move has. (Trump Card) -#=============================================================================== -class PokeBattle_Move_097 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - dmgs = [200,80,60,50,40] - ppLeft = [@pp,dmgs.length-1].min # PP is reduced before the move is used - return dmgs[ppLeft] - end -end - - - -#=============================================================================== -# Power increases the less HP the user has. (Flail, Reversal) -#=============================================================================== -class PokeBattle_Move_098 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - ret = 20 - n = 48*user.hp/user.totalhp - if n<2; ret = 200 - elsif n<5; ret = 150 - elsif n<10; ret = 100 - elsif n<17; ret = 80 - elsif n<33; ret = 40 - end - return ret - end -end - - - -#=============================================================================== -# Power increases the quicker the user is than the target. (Electro Ball) -#=============================================================================== -class PokeBattle_Move_099 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - ret = 40 - n = user.pbSpeed/target.pbSpeed - if n>=4; ret = 150 - elsif n>=3; ret = 120 - elsif n>=2; ret = 80 - elsif n>=1; ret = 60 - end - return ret - end -end - - - -#=============================================================================== -# Power increases the heavier the target is. (Grass Knot, Low Kick) -#=============================================================================== -class PokeBattle_Move_09A < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - ret = 20 - weight = target.pbWeight - if weight>=2000; ret = 120 - elsif weight>=1000; ret = 100 - elsif weight>=500; ret = 80 - elsif weight>=250; ret = 60 - elsif weight>=100; ret = 40 - end - return ret - end -end - - - -#=============================================================================== -# Power increases the heavier the user is than the target. (Heat Crash, Heavy Slam) -# Does double damage and has perfect accuracy if the target is Minimized. -#=============================================================================== -class PokeBattle_Move_09B < PokeBattle_Move - def tramplesMinimize?(param=1) - return true if Settings::MECHANICS_GENERATION >= 7 # Perfect accuracy and double damage - return super - end - - def pbBaseDamage(baseDmg,user,target) - ret = 40 - n = (user.pbWeight/target.pbWeight).floor - if n>=5; ret = 120 - elsif n>=4; ret = 100 - elsif n>=3; ret = 80 - elsif n>=2; ret = 60 - end - return ret - end -end - - - -#=============================================================================== -# Powers up the ally's attack this round by 1.5. (Helping Hand) -#=============================================================================== -class PokeBattle_Move_09C < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - if target.fainted? || target.effects[PBEffects::HelpingHand] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedTargetAlreadyMoved?(target) - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::HelpingHand] = true - @battle.pbDisplay(_INTL("{1} is ready to help {2}!",user.pbThis,target.pbThis(true))) - end -end - - - -#=============================================================================== -# Weakens Electric attacks. (Mud Sport) -#=============================================================================== -class PokeBattle_Move_09D < PokeBattle_Move - def pbMoveFailed?(user,targets) - if Settings::MECHANICS_GENERATION >= 6 - if @battle.field.effects[PBEffects::MudSportField]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - else - @battle.eachBattler do |b| - next if !b.effects[PBEffects::MudSport] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - end - return false - end - - def pbEffectGeneral(user) - if Settings::MECHANICS_GENERATION >= 6 - @battle.field.effects[PBEffects::MudSportField] = 5 - else - user.effects[PBEffects::MudSport] = true - end - @battle.pbDisplay(_INTL("Electricity's power was weakened!")) - end -end - - - -#=============================================================================== -# Weakens Fire attacks. (Water Sport) -#=============================================================================== -class PokeBattle_Move_09E < PokeBattle_Move - def pbMoveFailed?(user,targets) - if Settings::MECHANICS_GENERATION >= 6 - if @battle.field.effects[PBEffects::WaterSportField]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - else - @battle.eachBattler do |b| - next if !b.effects[PBEffects::WaterSport] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - end - return false - end - - def pbEffectGeneral(user) - if Settings::MECHANICS_GENERATION >= 6 - @battle.field.effects[PBEffects::WaterSportField] = 5 - else - user.effects[PBEffects::WaterSport] = true - end - @battle.pbDisplay(_INTL("Fire's power was weakened!")) - end -end - - - -#=============================================================================== -# Type depends on the user's held item. (Judgment, Multi-Attack, Techno Blast) -#=============================================================================== -class PokeBattle_Move_09F < PokeBattle_Move - def initialize(battle,move) - super - if @id == :JUDGMENT - @itemTypes = { - :FISTPLATE => :FIGHTING, - :SKYPLATE => :FLYING, - :TOXICPLATE => :POISON, - :EARTHPLATE => :GROUND, - :STONEPLATE => :ROCK, - :INSECTPLATE => :BUG, - :SPOOKYPLATE => :GHOST, - :IRONPLATE => :STEEL, - :FLAMEPLATE => :FIRE, - :SPLASHPLATE => :WATER, - :MEADOWPLATE => :GRASS, - :ZAPPLATE => :ELECTRIC, - :MINDPLATE => :PSYCHIC, - :ICICLEPLATE => :ICE, - :DRACOPLATE => :DRAGON, - :DREADPLATE => :DARK, - :PIXIEPLATE => :FAIRY - } - elsif @id == :TECHNOBLAST - @itemTypes = { - :SHOCKDRIVE => :ELECTRIC, - :BURNDRIVE => :FIRE, - :CHILLDRIVE => :ICE, - :DOUSEDRIVE => :WATER - } - elsif @id == :MULTIATTACK - @itemTypes = { - :FIGHTINGMEMORY => :FIGHTING, - :FLYINGMEMORY => :FLYING, - :POISONMEMORY => :POISON, - :GROUNDMEMORY => :GROUND, - :ROCKMEMORY => :ROCK, - :BUGMEMORY => :BUG, - :GHOSTMEMORY => :GHOST, - :STEELMEMORY => :STEEL, - :FIREMEMORY => :FIRE, - :WATERMEMORY => :WATER, - :GRASSMEMORY => :GRASS, - :ELECTRICMEMORY => :ELECTRIC, - :PSYCHICMEMORY => :PSYCHIC, - :ICEMEMORY => :ICE, - :DRAGONMEMORY => :DRAGON, - :DARKMEMORY => :DARK, - :FAIRYMEMORY => :FAIRY - } - end - end - - def pbBaseType(user) - ret = :NORMAL - if user.itemActive? - @itemTypes.each do |item, itemType| - next if user.item != item - ret = itemType if GameData::Type.exists?(itemType) - break - end - end - return ret - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - if @id == :TECHNOBLAST # Type-specific anim - t = pbBaseType(user) - hitNum = 0 - hitNum = 1 if t == :ELECTRIC - hitNum = 2 if t == :FIRE - hitNum = 3 if t == :ICE - hitNum = 4 if t == :WATER - end - super - end -end - - - -#=============================================================================== -# This attack is always a critical hit. (Frost Breath, Storm Throw) -#=============================================================================== -class PokeBattle_Move_0A0 < PokeBattle_Move - def pbCritialOverride(user,target); return 1; end -end - - - -#=============================================================================== -# For 5 rounds, foes' attacks cannot become critical hits. (Lucky Chant) -#=============================================================================== -class PokeBattle_Move_0A1 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOwnSide.effects[PBEffects::LuckyChant]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::LuckyChant] = 5 - @battle.pbDisplay(_INTL("The Lucky Chant shielded {1} from critical hits!",user.pbTeam(true))) - end -end - - - -#=============================================================================== -# For 5 rounds, lowers power of physical attacks against the user's side. -# (Reflect) -#=============================================================================== -class PokeBattle_Move_0A2 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOwnSide.effects[PBEffects::Reflect]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::Reflect] = 5 - user.pbOwnSide.effects[PBEffects::Reflect] = 8 if user.hasActiveItem?(:LIGHTCLAY) - @battle.pbDisplay(_INTL("{1} raised {2}'s Defense!",@name,user.pbTeam(true))) - end -end - - - -#=============================================================================== -# For 5 rounds, lowers power of special attacks against the user's side. (Light Screen) -#=============================================================================== -class PokeBattle_Move_0A3 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOwnSide.effects[PBEffects::LightScreen]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::LightScreen] = 5 - user.pbOwnSide.effects[PBEffects::LightScreen] = 8 if user.hasActiveItem?(:LIGHTCLAY) - @battle.pbDisplay(_INTL("{1} raised {2}'s Special Defense!",@name,user.pbTeam(true))) - end -end - - - -#=============================================================================== -# Effect depends on the environment. (Secret Power) -#=============================================================================== -class PokeBattle_Move_0A4 < PokeBattle_Move - def flinchingMove?; return [6,10,12].include?(@secretPower); end - - def pbOnStartUse(user,targets) - # NOTE: This is Gen 7's list plus some of Gen 6 plus a bit of my own. - @secretPower = 0 # Body Slam, paralysis - case @battle.field.terrain - when :Electric - @secretPower = 1 # Thunder Shock, paralysis - when :Grassy - @secretPower = 2 # Vine Whip, sleep - when :Misty - @secretPower = 3 # Fairy Wind, lower Sp. Atk by 1 - when :Psychic - @secretPower = 4 # Confusion, lower Speed by 1 - else - case @battle.environment - when :Grass, :TallGrass, :Forest, :ForestGrass - @secretPower = 2 # (Same as Grassy Terrain) - when :MovingWater, :StillWater, :Underwater - @secretPower = 5 # Water Pulse, lower Attack by 1 - when :Puddle - @secretPower = 6 # Mud Shot, lower Speed by 1 - when :Cave - @secretPower = 7 # Rock Throw, flinch - when :Rock, :Sand - @secretPower = 8 # Mud-Slap, lower Acc by 1 - when :Snow, :Ice - @secretPower = 9 # Ice Shard, freeze - when :Volcano - @secretPower = 10 # Incinerate, burn - when :Graveyard - @secretPower = 11 # Shadow Sneak, flinch - when :Sky - @secretPower = 12 # Gust, lower Speed by 1 - when :Space - @secretPower = 13 # Swift, flinch - when :UltraSpace - @secretPower = 14 # Psywave, lower Defense by 1 - end - end - end - - # NOTE: This intentionally doesn't use def pbAdditionalEffect, because that - # method is called per hit and this move's additional effect only occurs - # once per use, after all the hits have happened (two hits are possible - # via Parental Bond). - def pbEffectAfterAllHits(user,target) - return if target.fainted? - return if target.damageState.unaffected || target.damageState.substitute - chance = pbAdditionalEffectChance(user,target) - return if @battle.pbRandom(100)>=chance - case @secretPower - when 2 - target.pbSleep if target.pbCanSleep?(user,false,self) - when 10 - target.pbBurn(user) if target.pbCanBurn?(user,false,self) - when 0, 1 - target.pbParalyze(user) if target.pbCanParalyze?(user,false,self) - when 9 - target.pbFreeze if target.pbCanFreeze?(user,false,self) - when 5 - if target.pbCanLowerStatStage?(:ATTACK,user,self) - target.pbLowerStatStage(:ATTACK,1,user) - end - when 14 - if target.pbCanLowerStatStage?(:DEFENSE,user,self) - target.pbLowerStatStage(:DEFENSE,1,user) - end - when 3 - if target.pbCanLowerStatStage?(:SPECIAL_ATTACK,user,self) - target.pbLowerStatStage(:SPECIAL_ATTACK,1,user) - end - when 4, 6, 12 - if target.pbCanLowerStatStage?(:SPEED,user,self) - target.pbLowerStatStage(:SPEED,1,user) - end - when 8 - if target.pbCanLowerStatStage?(:ACCURACY,user,self) - target.pbLowerStatStage(:ACCURACY,1,user) - end - when 7, 11, 13 - target.pbFlinch(user) - end - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - id = :BODYSLAM # Environment-specific anim - case @secretPower - when 1 then id = :THUNDERSHOCK if GameData::Move.exists?(:THUNDERSHOCK) - when 2 then id = :VINEWHIP if GameData::Move.exists?(:VINEWHIP) - when 3 then id = :FAIRYWIND if GameData::Move.exists?(:FAIRYWIND) - when 4 then id = :CONFUSIO if GameData::Move.exists?(:CONFUSION) - when 5 then id = :WATERPULSE if GameData::Move.exists?(:WATERPULSE) - when 6 then id = :MUDSHOT if GameData::Move.exists?(:MUDSHOT) - when 7 then id = :ROCKTHROW if GameData::Move.exists?(:ROCKTHROW) - when 8 then id = :MUDSLAP if GameData::Move.exists?(:MUDSLAP) - when 9 then id = :ICESHARD if GameData::Move.exists?(:ICESHARD) - when 10 then id = :INCINERATE if GameData::Move.exists?(:INCINERATE) - when 11 then id = :SHADOWSNEAK if GameData::Move.exists?(:SHADOWSNEAK) - when 12 then id = :GUST if GameData::Move.exists?(:GUST) - when 13 then id = :SWIFT if GameData::Move.exists?(:SWIFT) - when 14 then id = :PSYWAVE if GameData::Move.exists?(:PSYWAVE) - end - super - end -end - - - -#=============================================================================== -# Always hits. -#=============================================================================== -class PokeBattle_Move_0A5 < PokeBattle_Move - def pbAccuracyCheck(user,target); return true; end -end - - - -#=============================================================================== -# User's attack next round against the target will definitely hit. -# (Lock-On, Mind Reader) -#=============================================================================== -class PokeBattle_Move_0A6 < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - user.effects[PBEffects::LockOn] = 2 - user.effects[PBEffects::LockOnPos] = target.index - @battle.pbDisplay(_INTL("{1} took aim at {2}!",user.pbThis,target.pbThis(true))) - end -end - - - -#=============================================================================== -# Target's evasion stat changes are ignored from now on. (Foresight, Odor Sleuth) -# Normal and Fighting moves have normal effectiveness against the Ghost-type target. -#=============================================================================== -class PokeBattle_Move_0A7 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Foresight] = true - @battle.pbDisplay(_INTL("{1} was identified!",target.pbThis)) - end -end - - - -#=============================================================================== -# Target's evasion stat changes are ignored from now on. (Miracle Eye) -# Psychic moves have normal effectiveness against the Dark-type target. -#=============================================================================== -class PokeBattle_Move_0A8 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::MiracleEye] = true - @battle.pbDisplay(_INTL("{1} was identified!",target.pbThis)) - end -end - - - -#=============================================================================== -# This move ignores target's Defense, Special Defense and evasion stat changes. -# (Chip Away, Darkest Lariat, Sacred Sword) -#=============================================================================== -class PokeBattle_Move_0A9 < PokeBattle_Move - def pbCalcAccuracyMultipliers(user,target,multipliers) - super - modifiers[:evasion_stage] = 0 - end - - def pbGetDefenseStats(user,target) - ret1, _ret2 = super - return ret1, 6 # Def/SpDef stat stage - end -end - - - -#=============================================================================== -# User is protected against moves with the "B" flag this round. (Detect, Protect) -#=============================================================================== -class PokeBattle_Move_0AA < PokeBattle_ProtectMove - def initialize(battle,move) - super - @effect = PBEffects::Protect - end -end - - - -#=============================================================================== -# User's side is protected against moves with priority greater than 0 this round. -# (Quick Guard) -#=============================================================================== -class PokeBattle_Move_0AB < PokeBattle_ProtectMove - def initialize(battle,move) - super - @effect = PBEffects::QuickGuard - @sidedEffect = true - end -end - - - -#=============================================================================== -# User's side is protected against moves that target multiple battlers this round. -# (Wide Guard) -#=============================================================================== -class PokeBattle_Move_0AC < PokeBattle_ProtectMove - def initialize(battle,move) - super - @effect = PBEffects::WideGuard - @sidedEffect = true - end -end - - - -#=============================================================================== -# Ends target's protections immediately. (Feint) -#=============================================================================== -class PokeBattle_Move_0AD < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::BanefulBunker] = false - target.effects[PBEffects::KingsShield] = false - target.effects[PBEffects::Protect] = false - target.effects[PBEffects::SpikyShield] = false - target.pbOwnSide.effects[PBEffects::CraftyShield] = false - target.pbOwnSide.effects[PBEffects::MatBlock] = false - target.pbOwnSide.effects[PBEffects::QuickGuard] = false - target.pbOwnSide.effects[PBEffects::WideGuard] = false - end -end - - - -#=============================================================================== -# Uses the last move that the target used. (Mirror Move) -#=============================================================================== -class PokeBattle_Move_0AE < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - def callsAnotherMove?; return true; end - - def pbFailsAgainstTarget?(user,target) - if !target.lastRegularMoveUsed || - !GameData::Move.get(target.lastRegularMoveUsed).flags[/e/] # Not copyable by Mirror Move - @battle.pbDisplay(_INTL("The mirror move failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - user.pbUseMoveSimple(target.lastRegularMoveUsed,target.index) - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - # No animation - end -end - - - -#=============================================================================== -# Uses the last move that was used. (Copycat) -#=============================================================================== -class PokeBattle_Move_0AF < PokeBattle_Move - def callsAnotherMove?; return true; end - - def initialize(battle,move) - super - @moveBlacklist = [ - # Struggle, Chatter, Belch - "002", # Struggle - "014", # Chatter - "158", # Belch # Not listed on Bulbapedia - # Moves that affect the moveset - "05C", # Mimic - "05D", # Sketch - "069", # Transform - # Counter moves - "071", # Counter - "072", # Mirror Coat - "073", # Metal Burst # Not listed on Bulbapedia - # Helping Hand, Feint (always blacklisted together, don't know why) - "09C", # Helping Hand - "0AD", # Feint - # Protection moves - "0AA", # Detect, Protect - "0AB", # Quick Guard # Not listed on Bulbapedia - "0AC", # Wide Guard # Not listed on Bulbapedia - "0E8", # Endure - "149", # Mat Block - "14A", # Crafty Shield # Not listed on Bulbapedia - "14B", # King's Shield - "14C", # Spiky Shield - "168", # Baneful Bunker - # Moves that call other moves - "0AE", # Mirror Move - "0AF", # Copycat (this move) - "0B0", # Me First - "0B3", # Nature Power # Not listed on Bulbapedia - "0B4", # Sleep Talk - "0B5", # Assist - "0B6", # Metronome - # Move-redirecting and stealing moves - "0B1", # Magic Coat # Not listed on Bulbapedia - "0B2", # Snatch - "117", # Follow Me, Rage Powder - "16A", # Spotlight - # Set up effects that trigger upon KO - "0E6", # Grudge # Not listed on Bulbapedia - "0E7", # Destiny Bond - # Held item-moving moves - "0F1", # Covet, Thief - "0F2", # Switcheroo, Trick - "0F3", # Bestow - # Moves that start focussing at the start of the round - "115", # Focus Punch - "171", # Shell Trap - "172", # Beak Blast - # Event moves that do nothing - "133", # Hold Hands - "134" # Celebrate - ] - if Settings::MECHANICS_GENERATION >= 6 - @moveBlacklist += [ - # Target-switching moves - "0EB", # Roar, Whirlwind - "0EC" # Circle Throw, Dragon Tail - ] - end - end - - def pbChangeUsageCounters(user,specialUsage) - super - @copied_move = @battle.lastMoveUsed - end - - def pbMoveFailed?(user,targets) - if !@copied_move || - @moveBlacklist.include?(GameData::Move.get(@copied_move).function_code) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbUseMoveSimple(@copied_move) - end -end - - - -#=============================================================================== -# Uses the move the target was about to use this round, with 1.5x power. -# (Me First) -#=============================================================================== -class PokeBattle_Move_0B0 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - def callsAnotherMove?; return true; end - - def initialize(battle,move) - super - @moveBlacklist = [ - "0F1", # Covet, Thief - # Struggle, Chatter, Belch - "002", # Struggle - "014", # Chatter - "158", # Belch - # Counter moves - "071", # Counter - "072", # Mirror Coat - "073", # Metal Burst - # Moves that start focussing at the start of the round - "115", # Focus Punch - "171", # Shell Trap - "172" # Beak Blast - ] - end - - def pbFailsAgainstTarget?(user,target) - return true if pbMoveFailedTargetAlreadyMoved?(target) - oppMove = @battle.choices[target.index][2] - if !oppMove || oppMove.statusMove? || @moveBlacklist.include?(oppMove.function) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - user.effects[PBEffects::MeFirst] = true - user.pbUseMoveSimple(@battle.choices[target.index][2].id) - user.effects[PBEffects::MeFirst] = false - end -end - - - -#=============================================================================== -# This round, reflects all moves with the "C" flag targeting the user back at -# their origin. (Magic Coat) -#=============================================================================== -class PokeBattle_Move_0B1 < PokeBattle_Move - def pbEffectGeneral(user) - user.effects[PBEffects::MagicCoat] = true - @battle.pbDisplay(_INTL("{1} shrouded itself with Magic Coat!",user.pbThis)) - end -end - - - -#=============================================================================== -# This round, snatches all used moves with the "D" flag. (Snatch) -#=============================================================================== -class PokeBattle_Move_0B2 < PokeBattle_Move - def pbEffectGeneral(user) - user.effects[PBEffects::Snatch] = 1 - @battle.eachBattler do |b| - next if b.effects[PBEffects::Snatch]= 6 - @npMove = :ENERGYBALL if GameData::Move.exists?(:ENERGYBALL) - else - @npMove = :SEEDBOMB if GameData::Move.exists?(:SEEDBOMB) - end - when :MovingWater, :StillWater, :Underwater - @npMove = :HYDROPUMP if GameData::Move.exists?(:HYDROPUMP) - when :Puddle - @npMove = :MUDBOMB if GameData::Move.exists?(:MUDBOMB) - when :Cave - if Settings::MECHANICS_GENERATION >= 6 - @npMove = :POWERGEM if GameData::Move.exists?(:POWERGEM) - else - @npMove = :ROCKSLIDE if GameData::Move.exists?(:ROCKSLIDE) - end - when :Rock - if Settings::MECHANICS_GENERATION >= 6 - @npMove = :EARTHPOWER if GameData::Move.exists?(:EARTHPOWER) - else - @npMove = :ROCKSLIDE if GameData::Move.exists?(:ROCKSLIDE) - end - when :Sand - if Settings::MECHANICS_GENERATION >= 6 - @npMove = :EARTHPOWER if GameData::Move.exists?(:EARTHPOWER) - else - @npMove = :EARTHQUAKE if GameData::Move.exists?(:EARTHQUAKE) - end - when :Snow - if Settings::MECHANICS_GENERATION >= 6 - @npMove = :FROSTBREATH if GameData::Move.exists?(:FROSTBREATH) - else - @npMove = :BLIZZARD if GameData::Move.exists?(:BLIZZARD) - end - when :Ice - @npMove = :ICEBEAM if GameData::Move.exists?(:ICEBEAM) - when :Volcano - @npMove = :LAVAPLUME if GameData::Move.exists?(:LAVAPLUME) - when :Graveyard - @npMove = :SHADOWBALL if GameData::Move.exists?(:SHADOWBALL) - when :Sky - @npMove = :AIRSLASH if GameData::Move.exists?(:AIRSLASH) - when :Space - @npMove = :DRACOMETEOR if GameData::Move.exists?(:DRACOMETEOR) - when :UltraSpace - @npMove = :PSYSHOCK if GameData::Move.exists?(:PSYSHOCK) - end - end - end - - def pbEffectAgainstTarget(user,target) - @battle.pbDisplay(_INTL("{1} turned into {2}!", @name, GameData::Move.get(@npMove).name)) - user.pbUseMoveSimple(@npMove, target.index) - end -end - - - -#=============================================================================== -# Uses a random move the user knows. Fails if user is not asleep. (Sleep Talk) -#=============================================================================== -class PokeBattle_Move_0B4 < PokeBattle_Move - def usableWhenAsleep?; return true; end - def callsAnotherMove?; return true; end - - def initialize(battle,move) - super - @moveBlacklist = [ - "0D1", # Uproar - "0D4", # Bide - # Struggle, Chatter, Belch - "002", # Struggle # Not listed on Bulbapedia - "014", # Chatter # Not listed on Bulbapedia - "158", # Belch - # Moves that affect the moveset (except Transform) - "05C", # Mimic - "05D", # Sketch - # Moves that call other moves - "0AE", # Mirror Move - "0AF", # Copycat - "0B0", # Me First - "0B3", # Nature Power # Not listed on Bulbapedia - "0B4", # Sleep Talk - "0B5", # Assist - "0B6", # Metronome - # Two-turn attacks - "0C3", # Razor Wind - "0C4", # Solar Beam, Solar Blade - "0C5", # Freeze Shock - "0C6", # Ice Burn - "0C7", # Sky Attack - "0C8", # Skull Bash - "0C9", # Fly - "0CA", # Dig - "0CB", # Dive - "0CC", # Bounce - "0CD", # Shadow Force - "0CE", # Sky Drop - "12E", # Shadow Half - "14D", # Phantom Force - "14E", # Geomancy - # Moves that start focussing at the start of the round - "115", # Focus Punch - "171", # Shell Trap - "172" # Beak Blast - ] - end - - def pbMoveFailed?(user,targets) - @sleepTalkMoves = [] - user.eachMoveWithIndex do |m,i| - next if @moveBlacklist.include?(m.function) - next if !@battle.pbCanChooseMove?(user.index,i,false,true) - @sleepTalkMoves.push(i) - end - if !user.asleep? || @sleepTalkMoves.length==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - choice = @sleepTalkMoves[@battle.pbRandom(@sleepTalkMoves.length)] - user.pbUseMoveSimple(user.moves[choice].id,user.pbDirectOpposing.index) - end -end - - - -#=============================================================================== -# Uses a random move known by any non-user Pokémon in the user's party. (Assist) -#=============================================================================== -class PokeBattle_Move_0B5 < PokeBattle_Move - def callsAnotherMove?; return true; end - - def initialize(battle,move) - super - @moveBlacklist = [ - # Struggle, Chatter, Belch - "002", # Struggle - "014", # Chatter - "158", # Belch - # Moves that affect the moveset - "05C", # Mimic - "05D", # Sketch - "069", # Transform - # Counter moves - "071", # Counter - "072", # Mirror Coat - "073", # Metal Burst # Not listed on Bulbapedia - # Helping Hand, Feint (always blacklisted together, don't know why) - "09C", # Helping Hand - "0AD", # Feint - # Protection moves - "0AA", # Detect, Protect - "0AB", # Quick Guard # Not listed on Bulbapedia - "0AC", # Wide Guard # Not listed on Bulbapedia - "0E8", # Endure - "149", # Mat Block - "14A", # Crafty Shield # Not listed on Bulbapedia - "14B", # King's Shield - "14C", # Spiky Shield - "168", # Baneful Bunker - # Moves that call other moves - "0AE", # Mirror Move - "0AF", # Copycat - "0B0", # Me First -# "0B3", # Nature Power # See below - "0B4", # Sleep Talk - "0B5", # Assist - "0B6", # Metronome - # Move-redirecting and stealing moves - "0B1", # Magic Coat # Not listed on Bulbapedia - "0B2", # Snatch - "117", # Follow Me, Rage Powder - "16A", # Spotlight - # Set up effects that trigger upon KO - "0E6", # Grudge # Not listed on Bulbapedia - "0E7", # Destiny Bond - # Target-switching moves -# "0EB", # Roar, Whirlwind # See below - "0EC", # Circle Throw, Dragon Tail - # Held item-moving moves - "0F1", # Covet, Thief - "0F2", # Switcheroo, Trick - "0F3", # Bestow - # Moves that start focussing at the start of the round - "115", # Focus Punch - "171", # Shell Trap - "172", # Beak Blast - # Event moves that do nothing - "133", # Hold Hands - "134" # Celebrate - ] - if Settings::MECHANICS_GENERATION >= 6 - @moveBlacklist += [ - # Moves that call other moves - "0B3", # Nature Power - # Two-turn attacks - "0C3", # Razor Wind # Not listed on Bulbapedia - "0C4", # Solar Beam, Solar Blade # Not listed on Bulbapedia - "0C5", # Freeze Shock # Not listed on Bulbapedia - "0C6", # Ice Burn # Not listed on Bulbapedia - "0C7", # Sky Attack # Not listed on Bulbapedia - "0C8", # Skull Bash # Not listed on Bulbapedia - "0C9", # Fly - "0CA", # Dig - "0CB", # Dive - "0CC", # Bounce - "0CD", # Shadow Force - "0CE", # Sky Drop - "12E", # Shadow Half - "14D", # Phantom Force - "14E", # Geomancy # Not listed on Bulbapedia - # Target-switching moves - "0EB" # Roar, Whirlwind - ] - end - end - - def pbMoveFailed?(user,targets) - @assistMoves = [] - # NOTE: This includes the Pokémon of ally trainers in multi battles. - @battle.pbParty(user.index).each_with_index do |pkmn,i| - next if !pkmn || i==user.pokemonIndex - next if Settings::MECHANICS_GENERATION >= 6 && pkmn.egg? - pkmn.moves.each do |move| - next if @moveBlacklist.include?(move.function_code) - next if move.type == :SHADOW - @assistMoves.push(move.id) - end - end - if @assistMoves.length==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - move = @assistMoves[@battle.pbRandom(@assistMoves.length)] - user.pbUseMoveSimple(move) - end -end - - - -#=============================================================================== -# Uses a random move that exists. (Metronome) -#=============================================================================== -class PokeBattle_Move_0B6 < PokeBattle_Move - def callsAnotherMove?; return true; end - - def initialize(battle,move) - super - @moveBlacklist = [ - "011", # Snore - "11D", # After You - "11E", # Quash - "16C", # Instruct - # Struggle, Chatter, Belch - "002", # Struggle - "014", # Chatter - "158", # Belch - # Moves that affect the moveset - "05C", # Mimic - "05D", # Sketch - "069", # Transform - # Counter moves - "071", # Counter - "072", # Mirror Coat - "073", # Metal Burst # Not listed on Bulbapedia - # Helping Hand, Feint (always blacklisted together, don't know why) - "09C", # Helping Hand - "0AD", # Feint - # Protection moves - "0AA", # Detect, Protect - "0AB", # Quick Guard - "0AC", # Wide Guard - "0E8", # Endure - "149", # Mat Block - "14A", # Crafty Shield - "14B", # King's Shield - "14C", # Spiky Shield - "168", # Baneful Bunker - # Moves that call other moves - "0AE", # Mirror Move - "0AF", # Copycat - "0B0", # Me First - "0B3", # Nature Power - "0B4", # Sleep Talk - "0B5", # Assist - "0B6", # Metronome - # Move-redirecting and stealing moves - "0B1", # Magic Coat # Not listed on Bulbapedia - "0B2", # Snatch - "117", # Follow Me, Rage Powder - "16A", # Spotlight - # Set up effects that trigger upon KO - "0E6", # Grudge # Not listed on Bulbapedia - "0E7", # Destiny Bond - # Held item-moving moves - "0F1", # Covet, Thief - "0F2", # Switcheroo, Trick - "0F3", # Bestow - # Moves that start focussing at the start of the round - "115", # Focus Punch - "171", # Shell Trap - "172", # Beak Blast - # Event moves that do nothing - "133", # Hold Hands - "134" # Celebrate - ] - @moveBlacklistSignatures = [ - :SNARL, - # Signature moves - :DIAMONDSTORM, # Diancie (Gen 6) - :FLEURCANNON, # Magearna (Gen 7) - :FREEZESHOCK, # Black Kyurem (Gen 5) - :HYPERSPACEFURY, # Hoopa Unbound (Gen 6) - :HYPERSPACEHOLE, # Hoopa Confined (Gen 6) - :ICEBURN, # White Kyurem (Gen 5) - :LIGHTOFRUIN, # Eternal Flower Floette (Gen 6) - :MINDBLOWN, # Blacephalon (Gen 7) - :PHOTONGEYSER, # Necrozma (Gen 7) - :PLASMAFISTS, # Zeraora (Gen 7) - :RELICSONG, # Meloetta (Gen 5) - :SECRETSWORD, # Keldeo (Gen 5) - :SPECTRALTHIEF, # Marshadow (Gen 7) - :STEAMERUPTION, # Volcanion (Gen 6) - :TECHNOBLAST, # Genesect (Gen 5) - :THOUSANDARROWS, # Zygarde (Gen 6) - :THOUSANDWAVES, # Zygarde (Gen 6) - :VCREATE # Victini (Gen 5) - ] - end - - def pbMoveFailed?(user,targets) - @metronomeMove = nil - move_keys = GameData::Move::DATA.keys - # NOTE: You could be really unlucky and roll blacklisted moves 1000 times in - # a row. This is too unlikely to care about, though. - 1000.times do - move_id = move_keys[@battle.pbRandom(move_keys.length)] - move_data = GameData::Move.get(move_id) - next if @moveBlacklist.include?(move_data.function_code) - next if @moveBlacklistSignatures.include?(move_data.id) - next if move_data.type == :SHADOW - @metronomeMove = move_data.id - break - end - if !@metronomeMove - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbUseMoveSimple(@metronomeMove) - end -end - - - -#=============================================================================== -# The target can no longer use the same move twice in a row. (Torment) -#=============================================================================== -class PokeBattle_Move_0B7 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Torment] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedAromaVeil?(user,target) - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Torment] = true - @battle.pbDisplay(_INTL("{1} was subjected to torment!",target.pbThis)) - target.pbItemStatusCureCheck - end -end - - - -#=============================================================================== -# Disables all target's moves that the user also knows. (Imprison) -#=============================================================================== -class PokeBattle_Move_0B8 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Imprison] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.effects[PBEffects::Imprison] = true - @battle.pbDisplay(_INTL("{1} sealed any moves its target shares with it!",user.pbThis)) - end -end - - - -#=============================================================================== -# For 5 rounds, disables the last move the target used. (Disable) -#=============================================================================== -class PokeBattle_Move_0B9 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Disable]>0 || !target.lastRegularMoveUsed - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedAromaVeil?(user,target) - canDisable = false - target.eachMove do |m| - next if m.id!=target.lastRegularMoveUsed - next if m.pp==0 && m.total_pp>0 - canDisable = true - break - end - if !canDisable - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Disable] = 5 - target.effects[PBEffects::DisableMove] = target.lastRegularMoveUsed - @battle.pbDisplay(_INTL("{1}'s {2} was disabled!",target.pbThis, - GameData::Move.get(target.lastRegularMoveUsed).name)) - target.pbItemStatusCureCheck - end -end - - - -#=============================================================================== -# For 4 rounds, disables the target's non-damaging moves. (Taunt) -#=============================================================================== -class PokeBattle_Move_0BA < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Taunt]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedAromaVeil?(user,target) - if Settings::MECHANICS_GENERATION >= 6 && target.hasActiveAbility?(:OBLIVIOUS) && - !@battle.moldBreaker - @battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("But it failed!")) - else - @battle.pbDisplay(_INTL("But it failed because of {1}'s {2}!", - target.pbThis(true),target.abilityName)) - end - @battle.pbHideAbilitySplash(target) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Taunt] = 4 - @battle.pbDisplay(_INTL("{1} fell for the taunt!",target.pbThis)) - target.pbItemStatusCureCheck - end -end - - - -#=============================================================================== -# For 5 rounds, disables the target's healing moves. (Heal Block) -#=============================================================================== -class PokeBattle_Move_0BB < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::HealBlock]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedAromaVeil?(user,target) - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::HealBlock] = 5 - @battle.pbDisplay(_INTL("{1} was prevented from healing!",target.pbThis)) - target.pbItemStatusCureCheck - end -end - - - -#=============================================================================== -# For 4 rounds, the target must use the same move each round. (Encore) -#=============================================================================== -class PokeBattle_Move_0BC < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def initialize(battle,move) - super - @moveBlacklist = [ - "0BC", # Encore - # Struggle - "002", # Struggle - # Moves that affect the moveset - "05C", # Mimic - "05D", # Sketch - "069", # Transform - # Moves that call other moves (see also below) - "0AE" # Mirror Move - ] - if Settings::MECHANICS_GENERATION >= 7 - @moveBlacklist += [ - # Moves that call other moves -# "0AE", # Mirror Move # See above - "0AF", # Copycat - "0B0", # Me First - "0B3", # Nature Power - "0B4", # Sleep Talk - "0B5", # Assist - "0B6" # Metronome - ] - end - end - - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Encore]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if !target.lastRegularMoveUsed || - @moveBlacklist.include?(GameData::Move.get(target.lastRegularMoveUsed).function_code) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.effects[PBEffects::ShellTrap] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedAromaVeil?(user,target) - canEncore = false - target.eachMove do |m| - next if m.id!=target.lastRegularMoveUsed - next if m.pp==0 && m.total_pp>0 - canEncore = true - break - end - if !canEncore - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Encore] = 4 - target.effects[PBEffects::EncoreMove] = target.lastRegularMoveUsed - @battle.pbDisplay(_INTL("{1} received an encore!",target.pbThis)) - target.pbItemStatusCureCheck - end -end - - - -#=============================================================================== -# Hits twice. -#=============================================================================== -class PokeBattle_Move_0BD < PokeBattle_Move - def multiHitMove?; return true; end - def pbNumHits(user,targets); return 2; end -end - - - -#=============================================================================== -# Hits twice. May poison the target on each hit. (Twineedle) -#=============================================================================== -class PokeBattle_Move_0BE < PokeBattle_PoisonMove - def multiHitMove?; return true; end - def pbNumHits(user,targets); return 2; end -end - - - -#=============================================================================== -# Hits 3 times. Power is multiplied by the hit number. (Triple Kick) -# An accuracy check is performed for each hit. -#=============================================================================== -class PokeBattle_Move_0BF < PokeBattle_Move - def multiHitMove?; return true; end - def pbNumHits(user,targets); return 3; end - - def successCheckPerHit? - return @accCheckPerHit - end - - def pbOnStartUse(user,targets) - @calcBaseDmg = 0 - @accCheckPerHit = !user.hasActiveAbility?(:SKILLLINK) - end - - def pbBaseDamage(baseDmg,user,target) - @calcBaseDmg += baseDmg - return @calcBaseDmg - end -end - - - -#=============================================================================== -# Hits 2-5 times. -#=============================================================================== -class PokeBattle_Move_0C0 < PokeBattle_Move - def multiHitMove?; return true; end - - def pbNumHits(user,targets) - if @id == :WATERSHURIKEN && user.isSpecies?(:GRENINJA) && user.form == 2 - return 3 - end - hitChances = [2,2,3,3,4,5] - r = @battle.pbRandom(hitChances.length) - r = hitChances.length-1 if user.hasActiveAbility?(:SKILLLINK) - return hitChances[r] - end - - def pbBaseDamage(baseDmg,user,target) - if @id == :WATERSHURIKEN && user.isSpecies?(:GRENINJA) && user.form == 2 - return 20 - end - return super - end -end - - - -#=============================================================================== -# Hits X times, where X is the number of non-user unfainted status-free Pokémon -# in the user's party (not including partner trainers). Fails if X is 0. -# Base power of each hit depends on the base Attack stat for the species of that -# hit's participant. (Beat Up) -#=============================================================================== -class PokeBattle_Move_0C1 < PokeBattle_Move - def multiHitMove?; return true; end - - def pbMoveFailed?(user,targets) - @beatUpList = [] - @battle.eachInTeamFromBattlerIndex(user.index) do |pkmn,i| - next if !pkmn.able? || pkmn.status != :NONE - @beatUpList.push(i) - end - if @beatUpList.length==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbNumHits(user,targets) - return @beatUpList.length - end - - def pbBaseDamage(baseDmg,user,target) - i = @beatUpList.shift # First element in array, and removes it from array - atk = @battle.pbParty(user.index)[i].baseStats[:ATTACK] - return 5+(atk/10) - end -end - - - -#=============================================================================== -# Two turn attack. Attacks first turn, skips second turn (if successful). -#=============================================================================== -class PokeBattle_Move_0C2 < PokeBattle_Move - def pbEffectGeneral(user) - user.effects[PBEffects::HyperBeam] = 2 - user.currentMove = @id - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Razor Wind) -#=============================================================================== -class PokeBattle_Move_0C3 < PokeBattle_TwoTurnMove - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} whipped up a whirlwind!",user.pbThis)) - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Solar Beam, Solar Blade) -# Power halved in all weather except sunshine. In sunshine, takes 1 turn instead. -#=============================================================================== -class PokeBattle_Move_0C4 < PokeBattle_TwoTurnMove - def pbIsChargingTurn?(user) - ret = super - if !user.effects[PBEffects::TwoTurnAttack] - if [:Sun, :HarshSun].include?(@battle.pbWeather) - @powerHerb = false - @chargingTurn = true - @damagingTurn = true - return false - end - end - return ret - end - - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} took in sunlight!",user.pbThis)) - end - - def pbBaseDamageMultiplier(damageMult,user,target) - damageMult /= 2 if ![:None, :Sun, :HarshSun].include?(@battle.pbWeather) - return damageMult - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Freeze Shock) -# May paralyze the target. -#=============================================================================== -class PokeBattle_Move_0C5 < PokeBattle_TwoTurnMove - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} became cloaked in a freezing light!",user.pbThis)) - end - - def pbAdditionalEffect(user,target) - return if target.damageState.substitute - target.pbParalyze(user) if target.pbCanParalyze?(user,false,self) - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Ice Burn) -# May burn the target. -#=============================================================================== -class PokeBattle_Move_0C6 < PokeBattle_TwoTurnMove - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} became cloaked in freezing air!",user.pbThis)) - end - - def pbAdditionalEffect(user,target) - return if target.damageState.substitute - target.pbBurn(user) if target.pbCanBurn?(user,false,self) - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Sky Attack) -# May make the target flinch. -#=============================================================================== -class PokeBattle_Move_0C7 < PokeBattle_TwoTurnMove - def flinchingMove?; return true; end - - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} became cloaked in a harsh light!",user.pbThis)) - end - - def pbAdditionalEffect(user,target) - return if target.damageState.substitute - target.pbFlinch(user) - end -end - - - -#=============================================================================== -# Two turn attack. Ups user's Defense by 1 stage first turn, attacks second turn. -# (Skull Bash) -#=============================================================================== -class PokeBattle_Move_0C8 < PokeBattle_TwoTurnMove - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} tucked in its head!",user.pbThis)) - end - - def pbChargingTurnEffect(user,target) - if user.pbCanRaiseStatStage?(:DEFENSE,user,self) - user.pbRaiseStatStage(:DEFENSE,1,user) - end - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Fly) -# (Handled in Battler's pbSuccessCheckPerHit): Is semi-invulnerable during use. -#=============================================================================== -class PokeBattle_Move_0C9 < PokeBattle_TwoTurnMove - def unusableInGravity?; return true; end - - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} flew up high!",user.pbThis)) - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Dig) -# (Handled in Battler's pbSuccessCheckPerHit): Is semi-invulnerable during use. -#=============================================================================== -class PokeBattle_Move_0CA < PokeBattle_TwoTurnMove - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} burrowed its way under the ground!",user.pbThis)) - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Dive) -# (Handled in Battler's pbSuccessCheckPerHit): Is semi-invulnerable during use. -#=============================================================================== -class PokeBattle_Move_0CB < PokeBattle_TwoTurnMove - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} hid underwater!",user.pbThis)) - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Bounce) -# May paralyze the target. -# (Handled in Battler's pbSuccessCheckPerHit): Is semi-invulnerable during use. -#=============================================================================== -class PokeBattle_Move_0CC < PokeBattle_TwoTurnMove - def unusableInGravity?; return true; end - - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} sprang up!",user.pbThis)) - end - - def pbAdditionalEffect(user,target) - return if target.damageState.substitute - target.pbParalyze(user) if target.pbCanParalyze?(user,false,self) - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Shadow Force) -# Is invulnerable during use. Ends target's protections upon hit. -#=============================================================================== -class PokeBattle_Move_0CD < PokeBattle_TwoTurnMove - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} vanished instantly!",user.pbThis)) - end - - def pbAttackingTurnEffect(user,target) - target.effects[PBEffects::BanefulBunker] = false - target.effects[PBEffects::KingsShield] = false - target.effects[PBEffects::Protect] = false - target.effects[PBEffects::SpikyShield] = false - target.pbOwnSide.effects[PBEffects::CraftyShield] = false - target.pbOwnSide.effects[PBEffects::MatBlock] = false - target.pbOwnSide.effects[PBEffects::QuickGuard] = false - target.pbOwnSide.effects[PBEffects::WideGuard] = false - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Sky Drop) -# (Handled in Battler's pbSuccessCheckPerHit): Is semi-invulnerable during use. -# Target is also semi-invulnerable during use, and can't take any action. -# Doesn't damage airborne Pokémon (but still makes them unable to move during). -#=============================================================================== -class PokeBattle_Move_0CE < PokeBattle_TwoTurnMove - def unusableInGravity?; return true; end - - def pbIsChargingTurn?(user) - # NOTE: Sky Drop doesn't benefit from Power Herb, probably because it works - # differently (i.e. immobilises the target during use too). - @powerHerb = false - @chargingTurn = (user.effects[PBEffects::TwoTurnAttack].nil?) - @damagingTurn = (!user.effects[PBEffects::TwoTurnAttack].nil?) - return !@damagingTurn - end - - def pbFailsAgainstTarget?(user,target) - if !target.opposes?(user) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.effects[PBEffects::Substitute]>0 && !ignoresSubstitute?(user) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if Settings::MECHANICS_GENERATION >= 6 && target.pbWeight>=2000 # 200.0kg - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.semiInvulnerable? || - (target.effects[PBEffects::SkyDrop]>=0 && @chargingTurn) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.effects[PBEffects::SkyDrop]!=user.index && @damagingTurn - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbCalcTypeMod(movetype,user,target) - return Effectiveness::INEFFECTIVE if target.pbHasType?(:FLYING) - return super - end - - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} took {2} into the sky!",user.pbThis,targets[0].pbThis(true))) - end - - def pbAttackingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} was freed from the Sky Drop!",targets[0].pbThis)) - end - - def pbChargingTurnEffect(user,target) - target.effects[PBEffects::SkyDrop] = user.index - end - - def pbAttackingTurnEffect(user,target) - target.effects[PBEffects::SkyDrop] = -1 - end -end - - - -#=============================================================================== -# Trapping move. Traps for 5 or 6 rounds. Trapped Pokémon lose 1/16 of max HP -# at end of each round. -#=============================================================================== -class PokeBattle_Move_0CF < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - return if target.fainted? || target.damageState.substitute - return if target.effects[PBEffects::Trapping]>0 - # Set trapping effect duration and info - if user.hasActiveItem?(:GRIPCLAW) - target.effects[PBEffects::Trapping] = (Settings::MECHANICS_GENERATION >= 5) ? 8 : 6 - else - target.effects[PBEffects::Trapping] = 5+@battle.pbRandom(2) - end - target.effects[PBEffects::TrappingMove] = @id - target.effects[PBEffects::TrappingUser] = user.index - # Message - msg = _INTL("{1} was trapped in the vortex!",target.pbThis) - case @id - when :BIND - msg = _INTL("{1} was squeezed by {2}!",target.pbThis,user.pbThis(true)) - when :CLAMP - msg = _INTL("{1} clamped {2}!",user.pbThis,target.pbThis(true)) - when :FIRESPIN - msg = _INTL("{1} was trapped in the fiery vortex!",target.pbThis) - when :INFESTATION - msg = _INTL("{1} has been afflicted with an infestation by {2}!",target.pbThis,user.pbThis(true)) - when :MAGMASTORM - msg = _INTL("{1} became trapped by Magma Storm!",target.pbThis) - when :SANDTOMB - msg = _INTL("{1} became trapped by Sand Tomb!",target.pbThis) - when :WHIRLPOOL - msg = _INTL("{1} became trapped in the vortex!",target.pbThis) - when :WRAP - msg = _INTL("{1} was wrapped by {2}!",target.pbThis,user.pbThis(true)) - end - @battle.pbDisplay(msg) - end -end - - - -#=============================================================================== -# Trapping move. Traps for 5 or 6 rounds. Trapped Pokémon lose 1/16 of max HP -# at end of each round. (Whirlpool) -# Power is doubled if target is using Dive. Hits some semi-invulnerable targets. -#=============================================================================== -class PokeBattle_Move_0D0 < PokeBattle_Move_0CF - def hitsDivingTargets?; return true; end - - def pbModifyDamage(damageMult,user,target) - damageMult *= 2 if target.inTwoTurnAttack?("0CB") # Dive - return damageMult - end -end - - - -#=============================================================================== -# User must use this move for 2 more rounds. No battlers can sleep. (Uproar) -# NOTE: Bulbapedia claims that an uproar will wake up Pokémon even if they have -# Soundproof, and will not allow Pokémon to fall asleep even if they have -# Soundproof. I think this is an oversight, so I've let Soundproof Pokémon -# be unaffected by Uproar waking/non-sleeping effects. -#=============================================================================== -class PokeBattle_Move_0D1 < PokeBattle_Move - def pbEffectGeneral(user) - return if user.effects[PBEffects::Uproar]>0 - user.effects[PBEffects::Uproar] = 3 - user.currentMove = @id - @battle.pbDisplay(_INTL("{1} caused an uproar!",user.pbThis)) - @battle.pbPriority(true).each do |b| - next if b.fainted? || b.status != :SLEEP - next if b.hasActiveAbility?(:SOUNDPROOF) - b.pbCureStatus - end - end -end - - - -#=============================================================================== -# User must use this move for 1 or 2 more rounds. At end, user becomes confused. -# (Outrage, Petal Dange, Thrash) -#=============================================================================== -class PokeBattle_Move_0D2 < PokeBattle_Move - def pbEffectAfterAllHits(user,target) - if !target.damageState.unaffected && user.effects[PBEffects::Outrage]==0 - user.effects[PBEffects::Outrage] = 2+@battle.pbRandom(2) - user.currentMove = @id - end - if user.effects[PBEffects::Outrage]>0 - user.effects[PBEffects::Outrage] -= 1 - if user.effects[PBEffects::Outrage]==0 && user.pbCanConfuseSelf?(false) - user.pbConfuse(_INTL("{1} became confused due to fatigue!",user.pbThis)) - end - end - end -end - - - -#=============================================================================== -# User must use this move for 4 more rounds. Power doubles each round. -# Power is also doubled if user has curled up. (Ice Ball, Rollout) -#=============================================================================== -class PokeBattle_Move_0D3 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - shift = (5 - user.effects[PBEffects::Rollout]) # 0-4, where 0 is most powerful - shift = 0 if user.effects[PBEffects::Rollout] == 0 # For first turn - shift += 1 if user.effects[PBEffects::DefenseCurl] - baseDmg *= 2**shift - return baseDmg - end - - def pbEffectAfterAllHits(user,target) - if !target.damageState.unaffected && user.effects[PBEffects::Rollout] == 0 - user.effects[PBEffects::Rollout] = 5 - user.currentMove = @id - end - user.effects[PBEffects::Rollout] -= 1 if user.effects[PBEffects::Rollout] > 0 - end -end - - - -#=============================================================================== -# User bides its time this round and next round. The round after, deals 2x the -# total direct damage it took while biding to the last battler that damaged it. -# (Bide) -#=============================================================================== -class PokeBattle_Move_0D4 < PokeBattle_FixedDamageMove - def pbAddTarget(targets,user) - return if user.effects[PBEffects::Bide]!=1 # Not the attack turn - idxTarget = user.effects[PBEffects::BideTarget] - t = (idxTarget>=0) ? @battle.battlers[idxTarget] : nil - if !user.pbAddTarget(targets,user,t,self,false) - user.pbAddTargetRandomFoe(targets,user,self,false) - end - end - - def pbMoveFailed?(user,targets) - return false if user.effects[PBEffects::Bide]!=1 # Not the attack turn - if user.effects[PBEffects::BideDamage]==0 - @battle.pbDisplay(_INTL("But it failed!")) - user.effects[PBEffects::Bide] = 0 # No need to reset other Bide variables - return true - end - if targets.length==0 - @battle.pbDisplay(_INTL("But there was no target...")) - user.effects[PBEffects::Bide] = 0 # No need to reset other Bide variables - return true - end - return false - end - - def pbOnStartUse(user,targets) - @damagingTurn = (user.effects[PBEffects::Bide]==1) # If attack turn - end - - def pbDisplayUseMessage(user) - if @damagingTurn # Attack turn - @battle.pbDisplayBrief(_INTL("{1} unleashed energy!",user.pbThis)) - elsif user.effects[PBEffects::Bide]>1 # Charging turns - @battle.pbDisplayBrief(_INTL("{1} is storing energy!",user.pbThis)) - else - super # Start using Bide - end - end - - def pbDamagingMove? # Stops damage being dealt in the charging turns - return false if !@damagingTurn - return super - end - - def pbFixedDamage(user,target) - return user.effects[PBEffects::BideDamage]*2 - end - - def pbEffectGeneral(user) - if user.effects[PBEffects::Bide]==0 # Starting using Bide - user.effects[PBEffects::Bide] = 3 - user.effects[PBEffects::BideDamage] = 0 - user.effects[PBEffects::BideTarget] = -1 - user.currentMove = @id - end - user.effects[PBEffects::Bide] -= 1 - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - hitNum = 1 if !@damagingTurn # Charging anim - super - end -end - - - -#=============================================================================== -# Heals user by 1/2 of its max HP. -#=============================================================================== -class PokeBattle_Move_0D5 < PokeBattle_HealingMove - def pbHealAmount(user) - return (user.totalhp/2.0).round - end -end - - - -#=============================================================================== -# Heals user by 1/2 of its max HP. (Roost) -# User roosts, and its Flying type is ignored for attacks used against it. -#=============================================================================== -class PokeBattle_Move_0D6 < PokeBattle_HealingMove - def pbHealAmount(user) - return (user.totalhp/2.0).round - end - - def pbEffectGeneral(user) - super - user.effects[PBEffects::Roost] = true - end -end - - - -#=============================================================================== -# Battler in user's position is healed by 1/2 of its max HP, at the end of the -# next round. (Wish) -#=============================================================================== -class PokeBattle_Move_0D7 < PokeBattle_Move - def healingMove?; return true; end - - def pbMoveFailed?(user,targets) - if @battle.positions[user.index].effects[PBEffects::Wish]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - @battle.positions[user.index].effects[PBEffects::Wish] = 2 - @battle.positions[user.index].effects[PBEffects::WishAmount] = (user.totalhp/2.0).round - @battle.positions[user.index].effects[PBEffects::WishMaker] = user.pokemonIndex - end -end - - - -#=============================================================================== -# Heals user by an amount depending on the weather. (Moonlight, Morning Sun, -# Synthesis) -#=============================================================================== -class PokeBattle_Move_0D8 < PokeBattle_HealingMove - def pbOnStartUse(user,targets) - case @battle.pbWeather - when :Sun, :HarshSun - @healAmount = (user.totalhp*2/3.0).round - when :None, :StrongWinds - @healAmount = (user.totalhp/2.0).round - else - @healAmount = (user.totalhp/4.0).round - end - end - - def pbHealAmount(user) - return @healAmount - end -end - - - -#=============================================================================== -# Heals user to full HP. User falls asleep for 2 more rounds. (Rest) -#=============================================================================== -class PokeBattle_Move_0D9 < PokeBattle_HealingMove - def pbMoveFailed?(user,targets) - if user.asleep? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if !user.pbCanSleep?(user,true,self,true) - return true if super - return false - end - - def pbHealAmount(user) - return user.totalhp-user.hp - end - - def pbEffectGeneral(user) - user.pbSleepSelf(_INTL("{1} slept and became healthy!",user.pbThis),3) - super - end -end - - - -#=============================================================================== -# Rings the user. Ringed Pokémon gain 1/16 of max HP at the end of each round. -# (Aqua Ring) -#=============================================================================== -class PokeBattle_Move_0DA < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::AquaRing] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.effects[PBEffects::AquaRing] = true - @battle.pbDisplay(_INTL("{1} surrounded itself with a veil of water!",user.pbThis)) - end -end - - - -#=============================================================================== -# Ingrains the user. Ingrained Pokémon gain 1/16 of max HP at the end of each -# round, and cannot flee or switch out. (Ingrain) -#=============================================================================== -class PokeBattle_Move_0DB < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Ingrain] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.effects[PBEffects::Ingrain] = true - @battle.pbDisplay(_INTL("{1} planted its roots!",user.pbThis)) - end -end - - - -#=============================================================================== -# Seeds the target. Seeded Pokémon lose 1/8 of max HP at the end of each round, -# and the Pokémon in the user's position gains the same amount. (Leech Seed) -#=============================================================================== -class PokeBattle_Move_0DC < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::LeechSeed]>=0 - @battle.pbDisplay(_INTL("{1} evaded the attack!",target.pbThis)) - return true - end - if target.pbHasType?(:GRASS) - @battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - return true - end - return false - end - - def pbMissMessage(user,target) - @battle.pbDisplay(_INTL("{1} evaded the attack!",target.pbThis)) - return true - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::LeechSeed] = user.index - @battle.pbDisplay(_INTL("{1} was seeded!",target.pbThis)) - end -end - - - -#=============================================================================== -# User gains half the HP it inflicts as damage. -#=============================================================================== -class PokeBattle_Move_0DD < PokeBattle_Move - def healingMove?; return Settings::MECHANICS_GENERATION >= 6; end - - def pbEffectAgainstTarget(user,target) - return if target.damageState.hpLost<=0 - hpGain = (target.damageState.hpLost/2.0).round - user.pbRecoverHPFromDrain(hpGain,target) - end -end - - - -#=============================================================================== -# User gains half the HP it inflicts as damage. Fails if target is not asleep. -# (Dream Eater) -#=============================================================================== -class PokeBattle_Move_0DE < PokeBattle_Move - def healingMove?; return Settings::MECHANICS_GENERATION >= 6; end - - def pbFailsAgainstTarget?(user,target) - if !target.asleep? - @battle.pbDisplay(_INTL("{1} wasn't affected!",target.pbThis)) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - return if target.damageState.hpLost<=0 - hpGain = (target.damageState.hpLost/2.0).round - user.pbRecoverHPFromDrain(hpGain,target) - end -end - - - -#=============================================================================== -# Heals target by 1/2 of its max HP. (Heal Pulse) -#=============================================================================== -class PokeBattle_Move_0DF < PokeBattle_Move - def healingMove?; return true; end - - def pbFailsAgainstTarget?(user,target) - if target.hp==target.totalhp - @battle.pbDisplay(_INTL("{1}'s HP is full!",target.pbThis)) - return true - elsif !target.canHeal? - @battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis)) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - hpGain = (target.totalhp/2.0).round - if pulseMove? && user.hasActiveAbility?(:MEGALAUNCHER) - hpGain = (target.totalhp*3/4.0).round - end - target.pbRecoverHP(hpGain) - @battle.pbDisplay(_INTL("{1}'s HP was restored.",target.pbThis)) - end -end - - - -#=============================================================================== -# User faints, even if the move does nothing else. (Explosion, Self-Destruct) -#=============================================================================== -class PokeBattle_Move_0E0 < PokeBattle_Move - def worksWithNoTargets?; return true; end - def pbNumHits(user,targets); return 1; end - - def pbMoveFailed?(user,targets) - if !@battle.moldBreaker - bearer = @battle.pbCheckGlobalAbility(:DAMP) - if bearer!=nil - @battle.pbShowAbilitySplash(bearer) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} cannot use {2}!",user.pbThis,@name)) - else - @battle.pbDisplay(_INTL("{1} cannot use {2} because of {3}'s {4}!", - user.pbThis,@name,bearer.pbThis(true),bearer.abilityName)) - end - @battle.pbHideAbilitySplash(bearer) - return true - end - end - return false - end - - def pbSelfKO(user) - return if user.fainted? - user.pbReduceHP(user.hp,false) - user.pbItemHPHealCheck - end -end - - - -#=============================================================================== -# Inflicts fixed damage equal to user's current HP. (Final Gambit) -# User faints (if successful). -#=============================================================================== -class PokeBattle_Move_0E1 < PokeBattle_FixedDamageMove - def pbNumHits(user,targets); return 1; end - - def pbOnStartUse(user,targets) - @finalGambitDamage = user.hp - end - - def pbFixedDamage(user,target) - return @finalGambitDamage - end - - def pbSelfKO(user) - return if user.fainted? - user.pbReduceHP(user.hp,false) - user.pbItemHPHealCheck - end -end - - - -#=============================================================================== -# Decreases the target's Attack and Special Attack by 2 stages each. (Memento) -# User faints (if successful). -#=============================================================================== -class PokeBattle_Move_0E2 < PokeBattle_TargetMultiStatDownMove - def initialize(battle,move) - super - @statDown = [:ATTACK,2,:SPECIAL_ATTACK,2] - end - - # NOTE: The user faints even if the target's stats cannot be changed, so this - # method must always return false to allow the move's usage to continue. - def pbFailsAgainstTarget?(user,target) - return false - end - - def pbSelfKO(user) - return if user.fainted? - user.pbReduceHP(user.hp,false) - user.pbItemHPHealCheck - end -end - - - -#=============================================================================== -# User faints. The Pokémon that replaces the user is fully healed (HP and -# status). Fails if user won't be replaced. (Healing Wish) -#=============================================================================== -class PokeBattle_Move_0E3 < PokeBattle_Move - def healingMove?; return true; end - - def pbMoveFailed?(user,targets) - if !@battle.pbCanChooseNonActive?(user.index) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbSelfKO(user) - return if user.fainted? - user.pbReduceHP(user.hp,false) - user.pbItemHPHealCheck - @battle.positions[user.index].effects[PBEffects::HealingWish] = true - end -end - - - -#=============================================================================== -# User faints. The Pokémon that replaces the user is fully healed (HP, PP and -# status). Fails if user won't be replaced. (Lunar Dance) -#=============================================================================== -class PokeBattle_Move_0E4 < PokeBattle_Move - def healingMove?; return true; end - - def pbMoveFailed?(user,targets) - if !@battle.pbCanChooseNonActive?(user.index) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbSelfKO(user) - return if user.fainted? - user.pbReduceHP(user.hp,false) - user.pbItemHPHealCheck - @battle.positions[user.index].effects[PBEffects::LunarDance] = true - end -end - - - -#=============================================================================== -# All current battlers will perish after 3 more rounds. (Perish Song) -#=============================================================================== -class PokeBattle_Move_0E5 < PokeBattle_Move - def pbMoveFailed?(user,targets) - failed = true - targets.each do |b| - next if b.effects[PBEffects::PerishSong]>0 # Heard it before - failed = false - break - end - if failed - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - return target.effects[PBEffects::PerishSong]>0 # Heard it before - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::PerishSong] = 4 - target.effects[PBEffects::PerishSongUser] = user.index - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - super - @battle.pbDisplay(_INTL("All Pokémon that hear the song will faint in three turns!")) - end -end - - - -#=============================================================================== -# If user is KO'd before it next moves, the attack that caused it loses all PP. -# (Grudge) -#=============================================================================== -class PokeBattle_Move_0E6 < PokeBattle_Move - def pbEffectGeneral(user) - user.effects[PBEffects::Grudge] = true - @battle.pbDisplay(_INTL("{1} wants its target to bear a grudge!",user.pbThis)) - end -end - - - -#=============================================================================== -# If user is KO'd before it next moves, the battler that caused it also faints. -# (Destiny Bond) -#=============================================================================== -class PokeBattle_Move_0E7 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if Settings::MECHANICS_GENERATION >= 7 && user.effects[PBEffects::DestinyBondPrevious] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.effects[PBEffects::DestinyBond] = true - @battle.pbDisplay(_INTL("{1} is hoping to take its attacker down with it!",user.pbThis)) - end -end - - - -#=============================================================================== -# If user would be KO'd this round, it survives with 1HP instead. (Endure) -#=============================================================================== -class PokeBattle_Move_0E8 < PokeBattle_ProtectMove - def initialize(battle,move) - super - @effect = PBEffects::Endure - end - - def pbProtectMessage(user) - @battle.pbDisplay(_INTL("{1} braced itself!",user.pbThis)) - end -end - - - -#=============================================================================== -# If target would be KO'd by this attack, it survives with 1HP instead. -# (False Swipe, Hold Back) -#=============================================================================== -class PokeBattle_Move_0E9 < PokeBattle_Move - def nonLethal?(user,target); return true; end -end - - - -#=============================================================================== -# User flees from battle. Fails in trainer battles. (Teleport) -#=============================================================================== -class PokeBattle_Move_0EA < PokeBattle_Move - def pbMoveFailed?(user,targets) - if !@battle.pbCanRun?(user.index) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - @battle.pbDisplay(_INTL("{1} fled from battle!",user.pbThis)) - @battle.decision = 3 # Escaped - end -end - - - -#=============================================================================== -# In wild battles, makes target flee. Fails if target is a higher level than the -# user. -# In trainer battles, target switches out. -# For status moves. (Roar, Whirlwind) -#=============================================================================== -class PokeBattle_Move_0EB < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - if target.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker - @battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} anchors itself!",target.pbThis)) - else - @battle.pbDisplay(_INTL("{1} anchors itself with {2}!",target.pbThis,target.abilityName)) - end - @battle.pbHideAbilitySplash(target) - return true - end - if target.effects[PBEffects::Ingrain] - @battle.pbDisplay(_INTL("{1} anchored itself with its roots!",target.pbThis)) - return true - end - if !@battle.canRun - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if @battle.wildBattle? && target.level>user.level - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if @battle.trainerBattle? - canSwitch = false - @battle.eachInTeamFromBattlerIndex(target.index) do |_pkmn,i| - next if !@battle.pbCanSwitchLax?(target.index,i) - canSwitch = true - break - end - if !canSwitch - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - end - return false - end - - def pbEffectGeneral(user) - @battle.decision = 3 if @battle.wildBattle? # Escaped from battle - end - - def pbSwitchOutTargetsEffect(user,targets,numHits,switchedBattlers) - return if @battle.wildBattle? - return if user.fainted? || numHits==0 - roarSwitched = [] - targets.each do |b| - next if b.fainted? || b.damageState.unaffected || switchedBattlers.include?(b.index) - newPkmn = @battle.pbGetReplacementPokemonIndex(b.index,true) # Random - next if newPkmn<0 - @battle.pbRecallAndReplace(b.index, newPkmn, true) - @battle.pbDisplay(_INTL("{1} was dragged out!",b.pbThis)) - @battle.pbClearChoice(b.index) # Replacement Pokémon does nothing this round - switchedBattlers.push(b.index) - roarSwitched.push(b.index) - end - if roarSwitched.length>0 - @battle.moldBreaker = false if roarSwitched.include?(user.index) - @battle.pbPriority(true).each do |b| - b.pbEffectsOnSwitchIn(true) if roarSwitched.include?(b.index) - end - end - end -end - - - -#=============================================================================== -# In wild battles, makes target flee. Fails if target is a higher level than the -# user. -# In trainer battles, target switches out. -# For damaging moves. (Circle Throw, Dragon Tail) -#=============================================================================== -class PokeBattle_Move_0EC < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - if @battle.wildBattle? && target.level<=user.level && @battle.canRun && - (target.effects[PBEffects::Substitute]==0 || ignoresSubstitute?(user)) - @battle.decision = 3 - end - end - - def pbSwitchOutTargetsEffect(user,targets,numHits,switchedBattlers) - return if @battle.wildBattle? - return if user.fainted? || numHits==0 - roarSwitched = [] - targets.each do |b| - next if b.fainted? || b.damageState.unaffected || b.damageState.substitute - next if switchedBattlers.include?(b.index) - next if b.effects[PBEffects::Ingrain] - next if b.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker - newPkmn = @battle.pbGetReplacementPokemonIndex(b.index,true) # Random - next if newPkmn<0 - @battle.pbRecallAndReplace(b.index, newPkmn, true) - @battle.pbDisplay(_INTL("{1} was dragged out!",b.pbThis)) - @battle.pbClearChoice(b.index) # Replacement Pokémon does nothing this round - switchedBattlers.push(b.index) - roarSwitched.push(b.index) - end - if roarSwitched.length>0 - @battle.moldBreaker = false if roarSwitched.include?(user.index) - @battle.pbPriority(true).each do |b| - b.pbEffectsOnSwitchIn(true) if roarSwitched.include?(b.index) - end - end - end -end - - - -#=============================================================================== -# User switches out. Various effects affecting the user are passed to the -# replacement. (Baton Pass) -#=============================================================================== -class PokeBattle_Move_0ED < PokeBattle_Move - def pbMoveFailed?(user,targets) - if !@battle.pbCanChooseNonActive?(user.index) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers) - return if user.fainted? || numHits==0 - return if !@battle.pbCanChooseNonActive?(user.index) - @battle.pbPursuit(user.index) - return if user.fainted? - newPkmn = @battle.pbGetReplacementPokemonIndex(user.index) # Owner chooses - return if newPkmn<0 - @battle.pbRecallAndReplace(user.index, newPkmn, false, true) - @battle.pbClearChoice(user.index) # Replacement Pokémon does nothing this round - @battle.moldBreaker = false - switchedBattlers.push(user.index) - user.pbEffectsOnSwitchIn(true) - end -end - - - -#=============================================================================== -# After inflicting damage, user switches out. Ignores trapping moves. -# (U-turn, Volt Switch) -#=============================================================================== -class PokeBattle_Move_0EE < PokeBattle_Move - def pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers) - return if user.fainted? || numHits==0 - targetSwitched = true - targets.each do |b| - targetSwitched = false if !switchedBattlers.include?(b.index) - end - return if targetSwitched - return if !@battle.pbCanChooseNonActive?(user.index) - @battle.pbDisplay(_INTL("{1} went back to {2}!",user.pbThis, - @battle.pbGetOwnerName(user.index))) - @battle.pbPursuit(user.index) - return if user.fainted? - newPkmn = @battle.pbGetReplacementPokemonIndex(user.index) # Owner chooses - return if newPkmn<0 - @battle.pbRecallAndReplace(user.index,newPkmn) - @battle.pbClearChoice(user.index) # Replacement Pokémon does nothing this round - @battle.moldBreaker = false - switchedBattlers.push(user.index) - user.pbEffectsOnSwitchIn(true) - end -end - - - -#=============================================================================== -# Target can no longer switch out or flee, as long as the user remains active. -# (Anchor Shot, Block, Mean Look, Spider Web, Spirit Shackle, Thousand Waves) -#=============================================================================== -class PokeBattle_Move_0EF < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - return false if damagingMove? - if target.effects[PBEffects::MeanLook]>=0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if Settings::MORE_TYPE_EFFECTS && target.pbHasType?(:GHOST) - @battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - return if damagingMove? - target.effects[PBEffects::MeanLook] = user.index - @battle.pbDisplay(_INTL("{1} can no longer escape!",target.pbThis)) - end - - def pbAdditionalEffect(user,target) - return if target.fainted? || target.damageState.substitute - return if target.effects[PBEffects::MeanLook]>=0 - return if Settings::MORE_TYPE_EFFECTS && target.pbHasType?(:GHOST) - target.effects[PBEffects::MeanLook] = user.index - @battle.pbDisplay(_INTL("{1} can no longer escape!",target.pbThis)) - end -end - - - -#=============================================================================== -# Target drops its item. It regains the item at the end of the battle. (Knock Off) -# If target has a losable item, damage is multiplied by 1.5. -#=============================================================================== -class PokeBattle_Move_0F0 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - if Settings::MECHANICS_GENERATION >= 6 && - target.item && !target.unlosableItem?(target.item) - # NOTE: Damage is still boosted even if target has Sticky Hold or a - # substitute. - baseDmg = (baseDmg*1.5).round - end - return baseDmg - end - - def pbEffectAfterAllHits(user,target) - return if @battle.wildBattle? && user.opposes? # Wild Pokémon can't knock off - return if user.fainted? - return if target.damageState.unaffected || target.damageState.substitute - return if !target.item || target.unlosableItem?(target.item) - return if target.hasActiveAbility?(:STICKYHOLD) && !@battle.moldBreaker - itemName = target.itemName - target.pbRemoveItem(false) - @battle.pbDisplay(_INTL("{1} dropped its {2}!",target.pbThis,itemName)) - end -end - - - -#=============================================================================== -# User steals the target's item, if the user has none itself. (Covet, Thief) -# Items stolen from wild Pokémon are kept after the battle. -#=============================================================================== -class PokeBattle_Move_0F1 < PokeBattle_Move - def pbEffectAfterAllHits(user,target) - return if @battle.wildBattle? && user.opposes? # Wild Pokémon can't thieve - return if user.fainted? - return if target.damageState.unaffected || target.damageState.substitute - return if !target.item || user.item - return if target.unlosableItem?(target.item) - return if user.unlosableItem?(target.item) - return if target.hasActiveAbility?(:STICKYHOLD) && !@battle.moldBreaker - itemName = target.itemName - user.item = target.item - # Permanently steal the item from wild Pokémon - if @battle.wildBattle? && target.opposes? && !user.initialItem && - target.item == target.initialItem - user.setInitialItem(target.item) - target.pbRemoveItem - else - target.pbRemoveItem(false) - end - @battle.pbDisplay(_INTL("{1} stole {2}'s {3}!",user.pbThis,target.pbThis(true),itemName)) - user.pbHeldItemTriggerCheck - end -end - - - -#=============================================================================== -# User and target swap items. They remain swapped after wild battles. -# (Switcheroo, Trick) -#=============================================================================== -class PokeBattle_Move_0F2 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if @battle.wildBattle? && user.opposes? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if !user.item && !target.item - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.unlosableItem?(target.item) || - target.unlosableItem?(user.item) || - user.unlosableItem?(user.item) || - user.unlosableItem?(target.item) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.hasActiveAbility?(:STICKYHOLD) && !@battle.moldBreaker - @battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("But it failed to affect {1}!",target.pbThis(true))) - else - @battle.pbDisplay(_INTL("But it failed to affect {1} because of its {2}!", - target.pbThis(true),target.abilityName)) - end - @battle.pbHideAbilitySplash(target) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - oldUserItem = user.item; oldUserItemName = user.itemName - oldTargetItem = target.item; oldTargetItemName = target.itemName - user.item = oldTargetItem - user.effects[PBEffects::ChoiceBand] = nil - user.effects[PBEffects::Unburden] = (!user.item && oldUserItem) - target.item = oldUserItem - target.effects[PBEffects::ChoiceBand] = nil - target.effects[PBEffects::Unburden] = (!target.item && oldTargetItem) - # Permanently steal the item from wild Pokémon - if @battle.wildBattle? && target.opposes? && !user.initialItem && - oldTargetItem == target.initialItem - user.setInitialItem(oldTargetItem) - end - @battle.pbDisplay(_INTL("{1} switched items with its opponent!",user.pbThis)) - @battle.pbDisplay(_INTL("{1} obtained {2}.",user.pbThis,oldTargetItemName)) if oldTargetItem - @battle.pbDisplay(_INTL("{1} obtained {2}.",target.pbThis,oldUserItemName)) if oldUserItem - user.pbHeldItemTriggerCheck - target.pbHeldItemTriggerCheck - end -end - - - -#=============================================================================== -# User gives its item to the target. The item remains given after wild battles. -# (Bestow) -#=============================================================================== -class PokeBattle_Move_0F3 < PokeBattle_Move - def ignoresSubstitute?(user) - return true if Settings::MECHANICS_GENERATION >= 6 - return super - end - - def pbMoveFailed?(user,targets) - if !user.item || user.unlosableItem?(user.item) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if target.item || target.unlosableItem?(user.item) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - itemName = user.itemName - target.item = user.item - # Permanently steal the item from wild Pokémon - if @battle.wildBattle? && user.opposes? && !target.initialItem && - user.item == user.initialItem - target.setInitialItem(user.item) - user.pbRemoveItem - else - user.pbRemoveItem(false) - end - @battle.pbDisplay(_INTL("{1} received {2} from {3}!",target.pbThis,itemName,user.pbThis(true))) - target.pbHeldItemTriggerCheck - end -end - - - -#=============================================================================== -# User consumes target's berry and gains its effect. (Bug Bite, Pluck) -#=============================================================================== -class PokeBattle_Move_0F4 < PokeBattle_Move - def pbEffectAfterAllHits(user,target) - return if user.fainted? || target.fainted? - return if target.damageState.unaffected || target.damageState.substitute - return if !target.item || !target.item.is_berry? - return if target.hasActiveAbility?(:STICKYHOLD) && !@battle.moldBreaker - item = target.item - itemName = target.itemName - target.pbRemoveItem - @battle.pbDisplay(_INTL("{1} stole and ate its target's {2}!",user.pbThis,itemName)) - user.pbHeldItemTriggerCheck(item,false) - end -end - - - -#=============================================================================== -# Target's berry/Gem is destroyed. (Incinerate) -#=============================================================================== -class PokeBattle_Move_0F5 < PokeBattle_Move - def pbEffectWhenDealingDamage(user,target) - return if target.damageState.substitute || target.damageState.berryWeakened - return if !target.item || (!target.item.is_berry? && - !(Settings::MECHANICS_GENERATION >= 6 && target.item.is_gem?)) - target.pbRemoveItem - @battle.pbDisplay(_INTL("{1}'s {2} was incinerated!",target.pbThis,target.itemName)) - end -end - - - -#=============================================================================== -# User recovers the last item it held and consumed. (Recycle) -#=============================================================================== -class PokeBattle_Move_0F6 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if !user.recycleItem - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - item = user.recycleItem - user.item = item - user.setInitialItem(item) if @battle.wildBattle? && !user.initialItem - user.setRecycleItem(nil) - user.effects[PBEffects::PickupItem] = nil - user.effects[PBEffects::PickupUse] = 0 - itemName = GameData::Item.get(item).name - if itemName.starts_with_vowel? - @battle.pbDisplay(_INTL("{1} found an {2}!",user.pbThis,itemName)) - else - @battle.pbDisplay(_INTL("{1} found a {2}!",user.pbThis,itemName)) - end - user.pbHeldItemTriggerCheck - end -end - - - -#=============================================================================== -# User flings its item at the target. Power/effect depend on the item. (Fling) -#=============================================================================== -class PokeBattle_Move_0F7 < PokeBattle_Move - def initialize(battle,move) - super - # 80 => all Mega Stones - # 10 => all Berries - @flingPowers = { - 130 => [:IRONBALL - ], - 100 => [:HARDSTONE,:RAREBONE, - # Fossils - :ARMORFOSSIL,:CLAWFOSSIL,:COVERFOSSIL,:DOMEFOSSIL,:HELIXFOSSIL, - :JAWFOSSIL,:OLDAMBER,:PLUMEFOSSIL,:ROOTFOSSIL,:SAILFOSSIL, - :SKULLFOSSIL - ], - 90 => [:DEEPSEATOOTH,:GRIPCLAW,:THICKCLUB, - # Plates - :DRACOPLATE,:DREADPLATE,:EARTHPLATE,:FISTPLATE,:FLAMEPLATE, - :ICICLEPLATE,:INSECTPLATE,:IRONPLATE,:MEADOWPLATE,:MINDPLATE, - :PIXIEPLATE,:SKYPLATE,:SPLASHPLATE,:SPOOKYPLATE,:STONEPLATE, - :TOXICPLATE,:ZAPPLATE - ], - 80 => [:ASSAULTVEST,:DAWNSTONE,:DUSKSTONE,:ELECTIRIZER,:MAGMARIZER, - :ODDKEYSTONE,:OVALSTONE,:PROTECTOR,:QUICKCLAW,:RAZORCLAW,:SACHET, - :SAFETYGOGGLES,:SHINYSTONE,:STICKYBARB,:WEAKNESSPOLICY, - :WHIPPEDDREAM - ], - 70 => [:DRAGONFANG,:POISONBARB, - # EV-training items (Macho Brace is 60) - :POWERANKLET,:POWERBAND,:POWERBELT,:POWERBRACER,:POWERLENS, - :POWERWEIGHT, - # Drives - :BURNDRIVE,:CHILLDRIVE,:DOUSEDRIVE,:SHOCKDRIVE - ], - 60 => [:ADAMANTORB,:DAMPROCK,:GRISEOUSORB,:HEATROCK,:LUSTROUSORB, - :MACHOBRACE,:ROCKYHELMET,:STICK,:TERRAINEXTENDER - ], - 50 => [:DUBIOUSDISC,:SHARPBEAK, - # Memories - :BUGMEMORY,:DARKMEMORY,:DRAGONMEMORY,:ELECTRICMEMORY,:FAIRYMEMORY, - :FIGHTINGMEMORY,:FIREMEMORY,:FLYINGMEMORY,:GHOSTMEMORY, - :GRASSMEMORY,:GROUNDMEMORY,:ICEMEMORY,:POISONMEMORY, - :PSYCHICMEMORY,:ROCKMEMORY,:STEELMEMORY,:WATERMEMORY - ], - 40 => [:EVIOLITE,:ICYROCK,:LUCKYPUNCH - ], - 30 => [:ABSORBBULB,:ADRENALINEORB,:AMULETCOIN,:BINDINGBAND,:BLACKBELT, - :BLACKGLASSES,:BLACKSLUDGE,:BOTTLECAP,:CELLBATTERY,:CHARCOAL, - :CLEANSETAG,:DEEPSEASCALE,:DRAGONSCALE,:EJECTBUTTON,:ESCAPEROPE, - :EXPSHARE,:FLAMEORB,:FLOATSTONE,:FLUFFYTAIL,:GOLDBOTTLECAP, - :HEARTSCALE,:HONEY,:KINGSROCK,:LIFEORB,:LIGHTBALL,:LIGHTCLAY, - :LUCKYEGG,:LUMINOUSMOSS,:MAGNET,:METALCOAT,:METRONOME, - :MIRACLESEED,:MYSTICWATER,:NEVERMELTICE,:PASSORB,:POKEDOLL, - :POKETOY,:PRISMSCALE,:PROTECTIVEPADS,:RAZORFANG,:SACREDASH, - :SCOPELENS,:SHELLBELL,:SHOALSALT,:SHOALSHELL,:SMOKEBALL,:SNOWBALL, - :SOULDEW,:SPELLTAG,:TOXICORB,:TWISTEDSPOON,:UPGRADE, - # Healing items - :ANTIDOTE,:AWAKENING,:BERRYJUICE,:BIGMALASADA,:BLUEFLUTE, - :BURNHEAL,:CASTELIACONE,:ELIXIR,:ENERGYPOWDER,:ENERGYROOT,:ETHER, - :FRESHWATER,:FULLHEAL,:FULLRESTORE,:HEALPOWDER,:HYPERPOTION, - :ICEHEAL,:LAVACOOKIE,:LEMONADE,:LUMIOSEGALETTE,:MAXELIXIR, - :MAXETHER,:MAXPOTION,:MAXREVIVE,:MOOMOOMILK,:OLDGATEAU, - :PARALYZEHEAL,:PARLYZHEAL,:PEWTERCRUNCHIES,:POTION,:RAGECANDYBAR, - :REDFLUTE,:REVIVALHERB,:REVIVE,:SHALOURSABLE,:SODAPOP, - :SUPERPOTION,:SWEETHEART,:YELLOWFLUTE, - # Battle items - :XACCURACY,:XACCURACY2,:XACCURACY3,:XACCURACY6, - :XATTACK,:XATTACK2,:XATTACK3,:XATTACK6, - :XDEFEND,:XDEFEND2,:XDEFEND3,:XDEFEND6, - :XDEFENSE,:XDEFENSE2,:XDEFENSE3,:XDEFENSE6, - :XSPATK,:XSPATK2,:XSPATK3,:XSPATK6, - :XSPECIAL,:XSPECIAL2,:XSPECIAL3,:XSPECIAL6, - :XSPDEF,:XSPDEF2,:XSPDEF3,:XSPDEF6, - :XSPEED,:XSPEED2,:XSPEED3,:XSPEED6, - :DIREHIT,:DIREHIT2,:DIREHIT3, - :ABILITYURGE,:GUARDSPEC,:ITEMDROP,:ITEMURGE,:RESETURGE, - # Vitamins - :CALCIUM,:CARBOS,:HPUP,:IRON,:PPUP,:PPMAX,:PROTEIN,:ZINC, - :RARECANDY, - # Most evolution stones (see also 80) - :EVERSTONE,:FIRESTONE,:ICESTONE,:LEAFSTONE,:MOONSTONE,:SUNSTONE, - :THUNDERSTONE,:WATERSTONE, - # Repels - :MAXREPEL,:REPEL,:SUPERREPEL, - # Mulches - :AMAZEMULCH,:BOOSTMULCH,:DAMPMULCH,:GOOEYMULCH,:GROWTHMULCH, - :RICHMULCH,:STABLEMULCH,:SURPRISEMULCH, - # Shards - :BLUESHARD,:GREENSHARD,:REDSHARD,:YELLOWSHARD, - # Valuables - :BALMMUSHROOM,:BIGMUSHROOM,:BIGNUGGET,:BIGPEARL,:COMETSHARD, - :NUGGET,:PEARL,:PEARLSTRING,:RELICBAND,:RELICCOPPER,:RELICCROWN, - :RELICGOLD,:RELICSILVER,:RELICSTATUE,:RELICVASE,:STARDUST, - :STARPIECE,:STRANGESOUVENIR,:TINYMUSHROOM - ], - 20 => [# Wings - :CLEVERWING,:GENIUSWING,:HEALTHWING,:MUSCLEWING,:PRETTYWING, - :RESISTWING,:SWIFTWING - ], - 10 => [:AIRBALLOON,:BIGROOT,:BRIGHTPOWDER,:CHOICEBAND,:CHOICESCARF, - :CHOICESPECS,:DESTINYKNOT,:DISCOUNTCOUPON,:EXPERTBELT,:FOCUSBAND, - :FOCUSSASH,:LAGGINGTAIL,:LEFTOVERS,:MENTALHERB,:METALPOWDER, - :MUSCLEBAND,:POWERHERB,:QUICKPOWDER,:REAPERCLOTH,:REDCARD, - :RINGTARGET,:SHEDSHELL,:SILKSCARF,:SILVERPOWDER,:SMOOTHROCK, - :SOFTSAND,:SOOTHEBELL,:WHITEHERB,:WIDELENS,:WISEGLASSES,:ZOOMLENS, - # Terrain seeds - :ELECTRICSEED,:GRASSYSEED,:MISTYSEED,:PSYCHICSEED, - # Nectar - :PINKNECTAR,:PURPLENECTAR,:REDNECTAR,:YELLOWNECTAR, - # Incenses - :FULLINCENSE,:LAXINCENSE,:LUCKINCENSE,:ODDINCENSE,:PUREINCENSE, - :ROCKINCENSE,:ROSEINCENSE,:SEAINCENSE,:WAVEINCENSE, - # Scarves - :BLUESCARF,:GREENSCARF,:PINKSCARF,:REDSCARF,:YELLOWSCARF - ] - } - end - - def pbCheckFlingSuccess(user) - @willFail = false - @willFail = true if !user.item || !user.itemActive? || user.unlosableItem?(user.item) - return if @willFail - @willFail = true if user.item.is_berry? && !user.canConsumeBerry? - return if @willFail - return if user.item.is_mega_stone? - flingableItem = false - @flingPowers.each do |_power, items| - next if !items.include?(user.item_id) - flingableItem = true - break - end - @willFail = true if !flingableItem - end - - def pbMoveFailed?(user,targets) - if @willFail - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbDisplayUseMessage(user) - super - pbCheckFlingSuccess(user) - if !@willFail - @battle.pbDisplay(_INTL("{1} flung its {2}!",user.pbThis,user.itemName)) - end - end - - def pbNumHits(user,targets); return 1; end - - def pbBaseDamage(baseDmg,user,target) - return 10 if user.item && user.item.is_berry? - return 80 if user.item && user.item.is_mega_stone? - @flingPowers.each do |power,items| - return power if items.include?(user.item_id) - end - return 10 - end - - def pbEffectAgainstTarget(user,target) - return if target.damageState.substitute - return if target.hasActiveAbility?(:SHIELDDUST) && !@battle.moldBreaker - case user.item_id - when :POISONBARB - target.pbPoison(user) if target.pbCanPoison?(user,false,self) - when :TOXICORB - target.pbPoison(user,nil,true) if target.pbCanPoison?(user,false,self) - when :FLAMEORB - target.pbBurn(user) if target.pbCanBurn?(user,false,self) - when :LIGHTBALL - target.pbParalyze(user) if target.pbCanParalyze?(user,false,self) - when :KINGSROCK, :RAZORFANG - target.pbFlinch(user) - else - target.pbHeldItemTriggerCheck(user.item,true) - end - end - - def pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers) - # NOTE: The item is consumed even if this move was Protected against or it - # missed. The item is not consumed if the target was switched out by - # an effect like a target's Red Card. - # NOTE: There is no item consumption animation. - user.pbConsumeItem(true,true,false) if user.item - end -end - - - -#=============================================================================== -# For 5 rounds, the target cannnot use its held item, its held item has no -# effect, and no items can be used on it. (Embargo) -#=============================================================================== -class PokeBattle_Move_0F8 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Embargo]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Embargo] = 5 - @battle.pbDisplay(_INTL("{1} can't use items anymore!",target.pbThis)) - end -end - - - -#=============================================================================== -# For 5 rounds, all held items cannot be used in any way and have no effect. -# Held items can still change hands, but can't be thrown. (Magic Room) -#=============================================================================== -class PokeBattle_Move_0F9 < PokeBattle_Move - def pbEffectGeneral(user) - if @battle.field.effects[PBEffects::MagicRoom]>0 - @battle.field.effects[PBEffects::MagicRoom] = 0 - @battle.pbDisplay(_INTL("The area returned to normal!")) - else - @battle.field.effects[PBEffects::MagicRoom] = 5 - @battle.pbDisplay(_INTL("It created a bizarre area in which Pokémon's held items lose their effects!")) - end - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - return if @battle.field.effects[PBEffects::MagicRoom]>0 # No animation - super - end -end - - - -#=============================================================================== -# User takes recoil damage equal to 1/4 of the damage this move dealt. -#=============================================================================== -class PokeBattle_Move_0FA < PokeBattle_RecoilMove - def pbRecoilDamage(user,target) - return (target.damageState.totalHPLost/4.0).round - end -end - - - -#=============================================================================== -# User takes recoil damage equal to 1/3 of the damage this move dealt. -#=============================================================================== -class PokeBattle_Move_0FB < PokeBattle_RecoilMove - def pbRecoilDamage(user,target) - return (target.damageState.totalHPLost/3.0).round - end -end - - - -#=============================================================================== -# User takes recoil damage equal to 1/2 of the damage this move dealt. -# (Head Smash, Light of Ruin) -#=============================================================================== -class PokeBattle_Move_0FC < PokeBattle_RecoilMove - def pbRecoilDamage(user,target) - return (target.damageState.totalHPLost/2.0).round - end -end - - - -#=============================================================================== -# User takes recoil damage equal to 1/3 of the damage this move dealt. -# May paralyze the target. (Volt Tackle) -#=============================================================================== -class PokeBattle_Move_0FD < PokeBattle_RecoilMove - def pbRecoilDamage(user,target) - return (target.damageState.totalHPLost/3.0).round - end - - def pbAdditionalEffect(user,target) - return if target.damageState.substitute - target.pbParalyze(user) if target.pbCanParalyze?(user,false,self) - end -end - - - -#=============================================================================== -# User takes recoil damage equal to 1/3 of the damage this move dealt. -# May burn the target. (Flare Blitz) -#=============================================================================== -class PokeBattle_Move_0FE < PokeBattle_RecoilMove - def pbRecoilDamage(user,target) - return (target.damageState.totalHPLost/3.0).round - end - - def pbAdditionalEffect(user,target) - return if target.damageState.substitute - target.pbBurn(user) if target.pbCanBurn?(user,false,self) - end -end - - - -#=============================================================================== -# Starts sunny weather. (Sunny Day) -#=============================================================================== -class PokeBattle_Move_0FF < PokeBattle_WeatherMove - def initialize(battle,move) - super - @weatherType = :Sun - end -end diff --git a/Data/Scripts/011_Battle/002_Move/007_Move_Effects_100-17F.rb b/Data/Scripts/011_Battle/002_Move/007_Move_Effects_100-17F.rb deleted file mode 100644 index a6b515ae87..0000000000 --- a/Data/Scripts/011_Battle/002_Move/007_Move_Effects_100-17F.rb +++ /dev/null @@ -1,2596 +0,0 @@ -#=============================================================================== -# Starts rainy weather. (Rain Dance) -#=============================================================================== -class PokeBattle_Move_100 < PokeBattle_WeatherMove - def initialize(battle,move) - super - @weatherType = :Rain - end -end - - - -#=============================================================================== -# Starts sandstorm weather. (Sandstorm) -#=============================================================================== -class PokeBattle_Move_101 < PokeBattle_WeatherMove - def initialize(battle,move) - super - @weatherType = :Sandstorm - end -end - - - -#=============================================================================== -# Starts hail weather. (Hail) -#=============================================================================== -class PokeBattle_Move_102 < PokeBattle_WeatherMove - def initialize(battle,move) - super - @weatherType = :Hail - end -end - - - -#=============================================================================== -# Entry hazard. Lays spikes on the opposing side (max. 3 layers). (Spikes) -#=============================================================================== -class PokeBattle_Move_103 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOpposingSide.effects[PBEffects::Spikes]>=3 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOpposingSide.effects[PBEffects::Spikes] += 1 - @battle.pbDisplay(_INTL("Spikes were scattered all around {1}'s feet!", - user.pbOpposingTeam(true))) - end -end - - - -#=============================================================================== -# Entry hazard. Lays poison spikes on the opposing side (max. 2 layers). -# (Toxic Spikes) -#=============================================================================== -class PokeBattle_Move_104 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOpposingSide.effects[PBEffects::ToxicSpikes]>=2 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOpposingSide.effects[PBEffects::ToxicSpikes] += 1 - @battle.pbDisplay(_INTL("Poison spikes were scattered all around {1}'s feet!", - user.pbOpposingTeam(true))) - end -end - - - -#=============================================================================== -# Entry hazard. Lays stealth rocks on the opposing side. (Stealth Rock) -#=============================================================================== -class PokeBattle_Move_105 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOpposingSide.effects[PBEffects::StealthRock] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOpposingSide.effects[PBEffects::StealthRock] = true - @battle.pbDisplay(_INTL("Pointed stones float in the air around {1}!", - user.pbOpposingTeam(true))) - end -end - - - -#=============================================================================== -# Combos with another Pledge move used by the ally. (Grass Pledge) -# If the move is a combo, power is doubled and causes either a sea of fire or a -# swamp on the opposing side. -#=============================================================================== -class PokeBattle_Move_106 < PokeBattle_PledgeMove - def initialize(battle,move) - super - # [Function code to combo with, effect, override type, override animation] - @combos = [["107", :SeaOfFire, :FIRE, :FIREPLEDGE], - ["108", :Swamp, nil, nil]] - end -end - - - -#=============================================================================== -# Combos with another Pledge move used by the ally. (Fire Pledge) -# If the move is a combo, power is doubled and causes either a rainbow on the -# user's side or a sea of fire on the opposing side. -#=============================================================================== -class PokeBattle_Move_107 < PokeBattle_PledgeMove - def initialize(battle,move) - super - # [Function code to combo with, effect, override type, override animation] - @combos = [["108", :Rainbow, :WATER, :WATERPLEDGE], - ["106", :SeaOfFire, nil, nil]] - end -end - - - -#=============================================================================== -# Combos with another Pledge move used by the ally. (Water Pledge) -# If the move is a combo, power is doubled and causes either a swamp on the -# opposing side or a rainbow on the user's side. -#=============================================================================== -class PokeBattle_Move_108 < PokeBattle_PledgeMove - def initialize(battle,move) - super - # [Function code to combo with, effect, override type, override animation] - @combos = [["106", :Swamp, :GRASS, :GRASSPLEDGE], - ["107", :Rainbow, nil, nil]] - end -end - - - -#=============================================================================== -# Scatters coins that the player picks up after winning the battle. (Pay Day) -# NOTE: In Gen 6+, if the user levels up after this move is used, the amount of -# money picked up depends on the user's new level rather than its level -# when it used the move. I think this is silly, so I haven't coded this -# effect. -#=============================================================================== -class PokeBattle_Move_109 < PokeBattle_Move - def pbEffectGeneral(user) - if user.pbOwnedByPlayer? - @battle.field.effects[PBEffects::PayDay] += 5*user.level - end - @battle.pbDisplay(_INTL("Coins were scattered everywhere!")) - end -end - - - -#=============================================================================== -# Ends the opposing side's Light Screen, Reflect and Aurora Break. (Brick Break, -# Psychic Fangs) -#=============================================================================== -class PokeBattle_Move_10A < PokeBattle_Move - def ignoresReflect?; return true; end - - def pbEffectGeneral(user) - if user.pbOpposingSide.effects[PBEffects::LightScreen]>0 - user.pbOpposingSide.effects[PBEffects::LightScreen] = 0 - @battle.pbDisplay(_INTL("{1}'s Light Screen wore off!",user.pbOpposingTeam)) - end - if user.pbOpposingSide.effects[PBEffects::Reflect]>0 - user.pbOpposingSide.effects[PBEffects::Reflect] = 0 - @battle.pbDisplay(_INTL("{1}'s Reflect wore off!",user.pbOpposingTeam)) - end - if user.pbOpposingSide.effects[PBEffects::AuroraVeil]>0 - user.pbOpposingSide.effects[PBEffects::AuroraVeil] = 0 - @battle.pbDisplay(_INTL("{1}'s Aurora Veil wore off!",user.pbOpposingTeam)) - end - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - if user.pbOpposingSide.effects[PBEffects::LightScreen]>0 || - user.pbOpposingSide.effects[PBEffects::Reflect]>0 || - user.pbOpposingSide.effects[PBEffects::AuroraVeil]>0 - hitNum = 1 # Wall-breaking anim - end - super - end -end - - - -#=============================================================================== -# If attack misses, user takes crash damage of 1/2 of max HP. -# (High Jump Kick, Jump Kick) -#=============================================================================== -class PokeBattle_Move_10B < PokeBattle_Move - def recoilMove?; return true; end - def unusableInGravity?; return true; end - - def pbCrashDamage(user) - return if !user.takesIndirectDamage? - @battle.pbDisplay(_INTL("{1} kept going and crashed!",user.pbThis)) - @battle.scene.pbDamageAnimation(user) - user.pbReduceHP(user.totalhp/2,false) - user.pbItemHPHealCheck - user.pbFaint if user.fainted? - end -end - - - -#=============================================================================== -# User turns 1/4 of max HP into a substitute. (Substitute) -#=============================================================================== -class PokeBattle_Move_10C < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Substitute]>0 - @battle.pbDisplay(_INTL("{1} already has a substitute!",user.pbThis)) - return true - end - @subLife = user.totalhp/4 - @subLife = 1 if @subLife<1 - if user.hp<=@subLife - @battle.pbDisplay(_INTL("But it does not have enough HP left to make a substitute!")) - return true - end - return false - end - - def pbOnStartUse(user,targets) - user.pbReduceHP(@subLife,false,false) - user.pbItemHPHealCheck - end - - def pbEffectGeneral(user) - user.effects[PBEffects::Trapping] = 0 - user.effects[PBEffects::TrappingMove] = nil - user.effects[PBEffects::Substitute] = @subLife - @battle.pbDisplay(_INTL("{1} put in a substitute!",user.pbThis)) - end -end - - - -#=============================================================================== -# User is Ghost: User loses 1/2 of max HP, and curses the target. -# Cursed Pokémon lose 1/4 of their max HP at the end of each round. -# User is not Ghost: Decreases the user's Speed by 1 stage, and increases the -# user's Attack and Defense by 1 stage each. (Curse) -#=============================================================================== -class PokeBattle_Move_10D < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbTarget(user) - return GameData::Target.get(:NearFoe) if user.pbHasType?(:GHOST) - return super - end - - def pbMoveFailed?(user,targets) - return false if user.pbHasType?(:GHOST) - if !user.pbCanLowerStatStage?(:SPEED,user,self) && - !user.pbCanRaiseStatStage?(:ATTACK,user,self) && - !user.pbCanRaiseStatStage?(:DEFENSE,user,self) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - if user.pbHasType?(:GHOST) && target.effects[PBEffects::Curse] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - return if user.pbHasType?(:GHOST) - # Non-Ghost effect - if user.pbCanLowerStatStage?(:SPEED,user,self) - user.pbLowerStatStage(:SPEED,1,user) - end - showAnim = true - if user.pbCanRaiseStatStage?(:ATTACK,user,self) - if user.pbRaiseStatStage(:ATTACK,1,user,showAnim) - showAnim = false - end - end - if user.pbCanRaiseStatStage?(:DEFENSE,user,self) - user.pbRaiseStatStage(:DEFENSE,1,user,showAnim) - end - end - - def pbEffectAgainstTarget(user,target) - return if !user.pbHasType?(:GHOST) - # Ghost effect - @battle.pbDisplay(_INTL("{1} cut its own HP and laid a curse on {2}!",user.pbThis,target.pbThis(true))) - target.effects[PBEffects::Curse] = true - user.pbReduceHP(user.totalhp/2,false) - user.pbItemHPHealCheck - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - hitNum = 1 if !user.pbHasType?(:GHOST) # Non-Ghost anim - super - end -end - - - -#=============================================================================== -# Target's last move used loses 4 PP. (Spite) -#=============================================================================== -class PokeBattle_Move_10E < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - failed = true - if target.lastRegularMoveUsed - target.eachMove do |m| - next if m.id!=target.lastRegularMoveUsed || m.pp==0 || m.total_pp<=0 - failed = false; break - end - end - if failed - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.eachMove do |m| - next if m.id!=target.lastRegularMoveUsed - reduction = [4,m.pp].min - target.pbSetPP(m,m.pp-reduction) - @battle.pbDisplay(_INTL("It reduced the PP of {1}'s {2} by {3}!", - target.pbThis(true),m.name,reduction)) - break - end - end -end - - - -#=============================================================================== -# Target will lose 1/4 of max HP at end of each round, while asleep. (Nightmare) -#=============================================================================== -class PokeBattle_Move_10F < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if !target.asleep? || target.effects[PBEffects::Nightmare] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Nightmare] = true - @battle.pbDisplay(_INTL("{1} began having a nightmare!",target.pbThis)) - end -end - - - -#=============================================================================== -# Removes trapping moves, entry hazards and Leech Seed on user/user's side. -# (Rapid Spin) -#=============================================================================== -class PokeBattle_Move_110 < PokeBattle_Move - def pbEffectAfterAllHits(user,target) - return if user.fainted? || target.damageState.unaffected - if user.effects[PBEffects::Trapping]>0 - trapMove = GameData::Move.get(user.effects[PBEffects::TrappingMove]).name - trapUser = @battle.battlers[user.effects[PBEffects::TrappingUser]] - @battle.pbDisplay(_INTL("{1} got free of {2}'s {3}!",user.pbThis,trapUser.pbThis(true),trapMove)) - user.effects[PBEffects::Trapping] = 0 - user.effects[PBEffects::TrappingMove] = nil - user.effects[PBEffects::TrappingUser] = -1 - end - if user.effects[PBEffects::LeechSeed]>=0 - user.effects[PBEffects::LeechSeed] = -1 - @battle.pbDisplay(_INTL("{1} shed Leech Seed!",user.pbThis)) - end - if user.pbOwnSide.effects[PBEffects::StealthRock] - user.pbOwnSide.effects[PBEffects::StealthRock] = false - @battle.pbDisplay(_INTL("{1} blew away stealth rocks!",user.pbThis)) - end - if user.pbOwnSide.effects[PBEffects::Spikes]>0 - user.pbOwnSide.effects[PBEffects::Spikes] = 0 - @battle.pbDisplay(_INTL("{1} blew away spikes!",user.pbThis)) - end - if user.pbOwnSide.effects[PBEffects::ToxicSpikes]>0 - user.pbOwnSide.effects[PBEffects::ToxicSpikes] = 0 - @battle.pbDisplay(_INTL("{1} blew away poison spikes!",user.pbThis)) - end - if user.pbOwnSide.effects[PBEffects::StickyWeb] - user.pbOwnSide.effects[PBEffects::StickyWeb] = false - @battle.pbDisplay(_INTL("{1} blew away sticky webs!",user.pbThis)) - end - end -end - - - -#=============================================================================== -# Attacks 2 rounds in the future. (Doom Desire, Future Sight) -#=============================================================================== -class PokeBattle_Move_111 < PokeBattle_Move - def cannotRedirect?; return true; end - - def pbDamagingMove? # Stops damage being dealt in the setting-up turn - return false if !@battle.futureSight - return super - end - - def pbAccuracyCheck(user,target) - return true if !@battle.futureSight - return super - end - - def pbDisplayUseMessage(user) - super if !@battle.futureSight - end - - def pbFailsAgainstTarget?(user,target) - if !@battle.futureSight && - @battle.positions[target.index].effects[PBEffects::FutureSightCounter]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - return if @battle.futureSight # Attack is hitting - effects = @battle.positions[target.index].effects - effects[PBEffects::FutureSightCounter] = 3 - effects[PBEffects::FutureSightMove] = @id - effects[PBEffects::FutureSightUserIndex] = user.index - effects[PBEffects::FutureSightUserPartyIndex] = user.pokemonIndex - if @id == :DOOMDESIRE - @battle.pbDisplay(_INTL("{1} chose Doom Desire as its destiny!",user.pbThis)) - else - @battle.pbDisplay(_INTL("{1} foresaw an attack!",user.pbThis)) - end - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - hitNum = 1 if !@battle.futureSight # Charging anim - super - end -end - - - -#=============================================================================== -# Increases the user's Defense and Special Defense by 1 stage each. Ups the -# user's stockpile by 1 (max. 3). (Stockpile) -#=============================================================================== -class PokeBattle_Move_112 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Stockpile]>=3 - @battle.pbDisplay(_INTL("{1} can't stockpile any more!",user.pbThis)) - return true - end - return false - end - - def pbEffectGeneral(user) - user.effects[PBEffects::Stockpile] += 1 - @battle.pbDisplay(_INTL("{1} stockpiled {2}!", - user.pbThis,user.effects[PBEffects::Stockpile])) - showAnim = true - if user.pbCanRaiseStatStage?(:DEFENSE,user,self) - if user.pbRaiseStatStage(:DEFENSE,1,user,showAnim) - user.effects[PBEffects::StockpileDef] += 1 - showAnim = false - end - end - if user.pbCanRaiseStatStage?(:SPECIAL_DEFENSE,user,self) - if user.pbRaiseStatStage(:SPECIAL_DEFENSE,1,user,showAnim) - user.effects[PBEffects::StockpileSpDef] += 1 - end - end - end -end - - - -#=============================================================================== -# Power is 100 multiplied by the user's stockpile (X). Resets the stockpile to -# 0. Decreases the user's Defense and Special Defense by X stages each. (Spit Up) -#=============================================================================== -class PokeBattle_Move_113 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Stockpile]==0 - @battle.pbDisplay(_INTL("But it failed to spit up a thing!")) - return true - end - return false - end - - def pbBaseDamage(baseDmg,user,target) - return 100*user.effects[PBEffects::Stockpile] - end - - def pbEffectAfterAllHits(user,target) - return if user.fainted? || user.effects[PBEffects::Stockpile]==0 - return if target.damageState.unaffected - @battle.pbDisplay(_INTL("{1}'s stockpiled effect wore off!",user.pbThis)) - return if @battle.pbAllFainted?(target.idxOwnSide) - showAnim = true - if user.effects[PBEffects::StockpileDef]>0 && - user.pbCanLowerStatStage?(:DEFENSE,user,self) - if user.pbLowerStatStage(:DEFENSE,user.effects[PBEffects::StockpileDef],user,showAnim) - showAnim = false - end - end - if user.effects[PBEffects::StockpileSpDef]>0 && - user.pbCanLowerStatStage?(:SPECIAL_DEFENSE,user,self) - user.pbLowerStatStage(:SPECIAL_DEFENSE,user.effects[PBEffects::StockpileSpDef],user,showAnim) - end - user.effects[PBEffects::Stockpile] = 0 - user.effects[PBEffects::StockpileDef] = 0 - user.effects[PBEffects::StockpileSpDef] = 0 - end -end - - - -#=============================================================================== -# Heals user depending on the user's stockpile (X). Resets the stockpile to 0. -# Decreases the user's Defense and Special Defense by X stages each. (Swallow) -#=============================================================================== -class PokeBattle_Move_114 < PokeBattle_Move - def healingMove?; return true; end - - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Stockpile]==0 - @battle.pbDisplay(_INTL("But it failed to swallow a thing!")) - return true - end - if !user.canHeal? && - user.effects[PBEffects::StockpileDef]==0 && - user.effects[PBEffects::StockpileSpDef]==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - hpGain = 0 - case [user.effects[PBEffects::Stockpile],1].max - when 1 then hpGain = user.totalhp/4 - when 2 then hpGain = user.totalhp/2 - when 3 then hpGain = user.totalhp - end - if user.pbRecoverHP(hpGain)>0 - @battle.pbDisplay(_INTL("{1}'s HP was restored.",user.pbThis)) - end - @battle.pbDisplay(_INTL("{1}'s stockpiled effect wore off!",user.pbThis)) - showAnim = true - if user.effects[PBEffects::StockpileDef]>0 && - user.pbCanLowerStatStage?(:DEFENSE,user,self) - if user.pbLowerStatStage(:DEFENSE,user.effects[PBEffects::StockpileDef],user,showAnim) - showAnim = false - end - end - if user.effects[PBEffects::StockpileSpDef]>0 && - user.pbCanLowerStatStage?(:SPECIAL_DEFENSE,user,self) - user.pbLowerStatStage(:SPECIAL_DEFENSE,user.effects[PBEffects::StockpileSpDef],user,showAnim) - end - user.effects[PBEffects::Stockpile] = 0 - user.effects[PBEffects::StockpileDef] = 0 - user.effects[PBEffects::StockpileSpDef] = 0 - end -end - - - -#=============================================================================== -# Fails if user was hit by a damaging move this round. (Focus Punch) -#=============================================================================== -class PokeBattle_Move_115 < PokeBattle_Move - def pbDisplayChargeMessage(user) - user.effects[PBEffects::FocusPunch] = true - @battle.pbCommonAnimation("FocusPunch",user) - @battle.pbDisplay(_INTL("{1} is tightening its focus!",user.pbThis)) - end - - def pbDisplayUseMessage(user) - super if !user.effects[PBEffects::FocusPunch] || user.lastHPLost==0 - end - - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::FocusPunch] && user.lastHPLost>0 - @battle.pbDisplay(_INTL("{1} lost its focus and couldn't move!",user.pbThis)) - return true - end - return false - end -end - - - -#=============================================================================== -# Fails if the target didn't chose a damaging move to use this round, or has -# already moved. (Sucker Punch) -#=============================================================================== -class PokeBattle_Move_116 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if @battle.choices[target.index][0]!=:UseMove - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - oppMove = @battle.choices[target.index][2] - if !oppMove || - (oppMove.function!="0B0" && # Me First - (target.movedThisRound? || oppMove.statusMove?)) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end -end - - - -#=============================================================================== -# This round, user becomes the target of attacks that have single targets. -# (Follow Me, Rage Powder) -#=============================================================================== -class PokeBattle_Move_117 < PokeBattle_Move - def pbEffectGeneral(user) - user.effects[PBEffects::FollowMe] = 1 - user.eachAlly do |b| - next if b.effects[PBEffects::FollowMe]0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - @battle.field.effects[PBEffects::Gravity] = 5 - @battle.pbDisplay(_INTL("Gravity intensified!")) - @battle.eachBattler do |b| - showMessage = false - if b.inTwoTurnAttack?("0C9","0CC","0CE") # Fly/Bounce/Sky Drop - b.effects[PBEffects::TwoTurnAttack] = nil - @battle.pbClearChoice(b.index) if !b.movedThisRound? - showMessage = true - end - if b.effects[PBEffects::MagnetRise]>0 || - b.effects[PBEffects::Telekinesis]>0 || - b.effects[PBEffects::SkyDrop]>=0 - b.effects[PBEffects::MagnetRise] = 0 - b.effects[PBEffects::Telekinesis] = 0 - b.effects[PBEffects::SkyDrop] = -1 - showMessage = true - end - @battle.pbDisplay(_INTL("{1} couldn't stay airborne because of gravity!", - b.pbThis)) if showMessage - end - end -end - - - -#=============================================================================== -# For 5 rounds, user becomes airborne. (Magnet Rise) -#=============================================================================== -class PokeBattle_Move_119 < PokeBattle_Move - def unusableInGravity?; return true; end - - def pbMoveFailed?(user,targets) - if user.effects[PBEffects::Ingrain] || - user.effects[PBEffects::SmackDown] || - user.effects[PBEffects::MagnetRise]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.effects[PBEffects::MagnetRise] = 5 - @battle.pbDisplay(_INTL("{1} levitated with electromagnetism!",user.pbThis)) - end -end - - - -#=============================================================================== -# For 3 rounds, target becomes airborne and can always be hit. (Telekinesis) -#=============================================================================== -class PokeBattle_Move_11A < PokeBattle_Move - def unusableInGravity?; return true; end - - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Ingrain] || - target.effects[PBEffects::SmackDown] || - target.effects[PBEffects::Telekinesis]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if target.isSpecies?(:DIGLETT) || - target.isSpecies?(:DUGTRIO) || - target.isSpecies?(:SANDYGAST) || - target.isSpecies?(:PALOSSAND) || - (target.isSpecies?(:GENGAR) && target.mega?) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Telekinesis] = 3 - @battle.pbDisplay(_INTL("{1} was hurled into the air!",target.pbThis)) - end -end - - - -#=============================================================================== -# Hits airborne semi-invulnerable targets. (Sky Uppercut) -#=============================================================================== -class PokeBattle_Move_11B < PokeBattle_Move - def hitsFlyingTargets?; return true; end -end - - - -#=============================================================================== -# Grounds the target while it remains active. Hits some semi-invulnerable -# targets. (Smack Down, Thousand Arrows) -#=============================================================================== -class PokeBattle_Move_11C < PokeBattle_Move - def hitsFlyingTargets?; return true; end - - def pbCalcTypeModSingle(moveType,defType,user,target) - return Effectiveness::NORMAL_EFFECTIVE_ONE if moveType == :GROUND && defType == :FLYING - return super - end - - def pbEffectAfterAllHits(user,target) - return if target.fainted? - return if target.damageState.unaffected || target.damageState.substitute - return if target.inTwoTurnAttack?("0CE") || target.effects[PBEffects::SkyDrop]>=0 # Sky Drop - return if !target.airborne? && !target.inTwoTurnAttack?("0C9","0CC") # Fly/Bounce - target.effects[PBEffects::SmackDown] = true - if target.inTwoTurnAttack?("0C9","0CC") # Fly/Bounce. NOTE: Not Sky Drop. - target.effects[PBEffects::TwoTurnAttack] = nil - @battle.pbClearChoice(target.index) if !target.movedThisRound? - end - target.effects[PBEffects::MagnetRise] = 0 - target.effects[PBEffects::Telekinesis] = 0 - @battle.pbDisplay(_INTL("{1} fell straight down!",target.pbThis)) - end -end - - - -#=============================================================================== -# Target moves immediately after the user, ignoring priority/speed. (After You) -#=============================================================================== -class PokeBattle_Move_11D < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - # Target has already moved this round - return true if pbMoveFailedTargetAlreadyMoved?(target) - # Target was going to move next anyway (somehow) - if target.effects[PBEffects::MoveNext] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - # Target didn't choose to use a move this round - oppMove = @battle.choices[target.index][2] - if !oppMove - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::MoveNext] = true - target.effects[PBEffects::Quash] = 0 - @battle.pbDisplay(_INTL("{1} took the kind offer!",target.pbThis)) - end -end - - - -#=============================================================================== -# Target moves last this round, ignoring priority/speed. (Quash) -#=============================================================================== -class PokeBattle_Move_11E < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - return true if pbMoveFailedTargetAlreadyMoved?(target) - # Target isn't going to use a move - oppMove = @battle.choices[target.index][2] - if !oppMove - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - # Target is already maximally Quashed and will move last anyway - highestQuash = 0 - @battle.battlers.each do |b| - next if b.effects[PBEffects::Quash]<=highestQuash - highestQuash = b.effects[PBEffects::Quash] - end - if highestQuash>0 && target.effects[PBEffects::Quash]==highestQuash - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - # Target was already going to move last - if highestQuash==0 && @battle.pbPriority.last.index==target.index - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - highestQuash = 0 - @battle.battlers.each do |b| - next if b.effects[PBEffects::Quash]<=highestQuash - highestQuash = b.effects[PBEffects::Quash] - end - target.effects[PBEffects::Quash] = highestQuash+1 - target.effects[PBEffects::MoveNext] = false - @battle.pbDisplay(_INTL("{1}'s move was postponed!",target.pbThis)) - end -end - - - -#=============================================================================== -# For 5 rounds, for each priority bracket, slow Pokémon move before fast ones. -# (Trick Room) -#=============================================================================== -class PokeBattle_Move_11F < PokeBattle_Move - def pbEffectGeneral(user) - if @battle.field.effects[PBEffects::TrickRoom]>0 - @battle.field.effects[PBEffects::TrickRoom] = 0 - @battle.pbDisplay(_INTL("{1} reverted the dimensions!",user.pbThis)) - else - @battle.field.effects[PBEffects::TrickRoom] = 5 - @battle.pbDisplay(_INTL("{1} twisted the dimensions!",user.pbThis)) - end - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - return if @battle.field.effects[PBEffects::TrickRoom]>0 # No animation - super - end -end - - - -#=============================================================================== -# User switches places with its ally. (Ally Switch) -#=============================================================================== -class PokeBattle_Move_120 < PokeBattle_Move - def pbMoveFailed?(user,targets) - numTargets = 0 - @idxAlly = -1 - idxUserOwner = @battle.pbGetOwnerIndexFromBattlerIndex(user.index) - user.eachAlly do |b| - next if @battle.pbGetOwnerIndexFromBattlerIndex(b.index)!=idxUserOwner - next if !b.near?(user) - numTargets += 1 - @idxAlly = b.index - end - if numTargets!=1 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - idxA = user.index - idxB = @idxAlly - if @battle.pbSwapBattlers(idxA,idxB) - @battle.pbDisplay(_INTL("{1} and {2} switched places!", - @battle.battlers[idxB].pbThis,@battle.battlers[idxA].pbThis(true))) - end - end -end - - - -#=============================================================================== -# Target's Attack is used instead of user's Attack for this move's calculations. -# (Foul Play) -#=============================================================================== -class PokeBattle_Move_121 < PokeBattle_Move - def pbGetAttackStats(user,target) - if specialMove? - return target.spatk, target.stages[:SPECIAL_ATTACK]+6 - end - return target.attack, target.stages[:ATTACK]+6 - end -end - - - -#=============================================================================== -# Target's Defense is used instead of its Special Defense for this move's -# calculations. (Psyshock, Psystrike, Secret Sword) -#=============================================================================== -class PokeBattle_Move_122 < PokeBattle_Move - def pbGetDefenseStats(user,target) - return target.defense, target.stages[:DEFENSE]+6 - end -end - - - -#=============================================================================== -# Only damages Pokémon that share a type with the user. (Synchronoise) -#=============================================================================== -class PokeBattle_Move_123 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - userTypes = user.pbTypes(true) - targetTypes = target.pbTypes(true) - sharesType = false - userTypes.each do |t| - next if !targetTypes.include?(t) - sharesType = true - break - end - if !sharesType - @battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis)) - return true - end - return false - end -end - - - -#=============================================================================== -# For 5 rounds, swaps all battlers' base Defense with base Special Defense. -# (Wonder Room) -#=============================================================================== -class PokeBattle_Move_124 < PokeBattle_Move - def pbEffectGeneral(user) - if @battle.field.effects[PBEffects::WonderRoom]>0 - @battle.field.effects[PBEffects::WonderRoom] = 0 - @battle.pbDisplay(_INTL("Wonder Room wore off, and the Defense and Sp. Def stats returned to normal!")) - else - @battle.field.effects[PBEffects::WonderRoom] = 5 - @battle.pbDisplay(_INTL("It created a bizarre area in which the Defense and Sp. Def stats are swapped!")) - end - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - return if @battle.field.effects[PBEffects::WonderRoom]>0 # No animation - super - end -end - - - -#=============================================================================== -# Fails unless user has already used all other moves it knows. (Last Resort) -#=============================================================================== -class PokeBattle_Move_125 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - hasThisMove = false; hasOtherMoves = false; hasUnusedMoves = false - user.eachMove do |m| - hasThisMove = true if m.id==@id - hasOtherMoves = true if m.id!=@id - hasUnusedMoves = true if m.id!=@id && !user.movesUsed.include?(m.id) - end - if !hasThisMove || !hasOtherMoves || hasUnusedMoves - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end -end - - - -#=============================================================================== -# NOTE: Shadow moves use function codes 126-132 inclusive. -#=============================================================================== - - - -#=============================================================================== -# Does absolutely nothing. (Hold Hands) -#=============================================================================== -class PokeBattle_Move_133 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbMoveFailed?(user,targets) - hasAlly = false - user.eachAlly do |_b| - hasAlly = true - break - end - if !hasAlly - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end -end - - - -#=============================================================================== -# Does absolutely nothing. Shows a special message. (Celebrate) -#=============================================================================== -class PokeBattle_Move_134 < PokeBattle_Move - def pbEffectGeneral(user) - if @battle.wildBattle? && user.opposes? - @battle.pbDisplay(_INTL("Congratulations from {1}!",user.pbThis(true))) - else - @battle.pbDisplay(_INTL("Congratulations, {1}!",@battle.pbGetOwnerName(user.index))) - end - end -end - - - -#=============================================================================== -# Freezes the target. Effectiveness against Water-type is 2x. (Freeze-Dry) -#=============================================================================== -class PokeBattle_Move_135 < PokeBattle_FreezeMove - def pbCalcTypeModSingle(moveType,defType,user,target) - return Effectiveness::SUPER_EFFECTIVE_ONE if defType == :WATER - return super - end -end - - - -#=============================================================================== -# Increases the user's Defense by 2 stages. (Diamond Storm) -#=============================================================================== -class PokeBattle_Move_136 < PokeBattle_Move_02F - # NOTE: In Gen 6, this move increased the user's Defense by 1 stage for each - # target it hit. This effect changed in Gen 7 and is now identical to - # function code 02F. -end - - - -#=============================================================================== -# Increases the user's and its ally's Defense and Special Defense by 1 stage -# each, if they have Plus or Minus. (Magnetic Flux) -#=============================================================================== -# NOTE: In Gen 5, this move should have a target of UserSide, while in Gen 6+ it -# should have a target of UserAndAllies. This is because, in Gen 5, this -# move shouldn't call def pbSuccessCheckAgainstTarget for each Pokémon -# currently in battle that will be affected by this move (i.e. allies -# aren't protected by their substitute/ability/etc., but they are in Gen -# 6+). We achieve this by not targeting any battlers in Gen 5, since -# pbSuccessCheckAgainstTarget is only called for targeted battlers. -class PokeBattle_Move_137 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbMoveFailed?(user,targets) - @validTargets = [] - @battle.eachSameSideBattler(user) do |b| - next if !b.hasActiveAbility?([:MINUS,:PLUS]) - next if !b.pbCanRaiseStatStage?(:DEFENSE,user,self) && - !b.pbCanRaiseStatStage?(:SPECIAL_DEFENSE,user,self) - @validTargets.push(b) - end - if @validTargets.length==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - return false if @validTargets.any? { |b| b.index==target.index } - return true if !target.hasActiveAbility?([:MINUS,:PLUS]) - @battle.pbDisplay(_INTL("{1}'s stats can't be raised further!",target.pbThis)) - return true - end - - - def pbEffectAgainstTarget(user,target) - showAnim = true - if target.pbCanRaiseStatStage?(:DEFENSE,user,self) - if target.pbRaiseStatStage(:DEFENSE,1,user,showAnim) - showAnim = false - end - end - if target.pbCanRaiseStatStage?(:SPECIAL_DEFENSE,user,self) - target.pbRaiseStatStage(:SPECIAL_DEFENSE,1,user,showAnim) - end - end - - def pbEffectGeneral(user) - return if pbTarget(user) != :UserSide - @validTargets.each { |b| pbEffectAgainstTarget(user,b) } - end -end - - - -#=============================================================================== -# Increases target's Special Defense by 1 stage. (Aromatic Mist) -#=============================================================================== -class PokeBattle_Move_138 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - return true if !target.pbCanRaiseStatStage?(:SPECIAL_DEFENSE,user,self,true) - return false - end - - def pbEffectAgainstTarget(user,target) - target.pbRaiseStatStage(:SPECIAL_DEFENSE,1,user) - end -end - - - -#=============================================================================== -# Decreases the target's Attack by 1 stage. Always hits. (Play Nice) -#=============================================================================== -class PokeBattle_Move_139 < PokeBattle_TargetStatDownMove - def ignoresSubstitute?(user); return true; end - - def initialize(battle,move) - super - @statDown = [:ATTACK,1] - end - - def pbAccuracyCheck(user,target); return true; end -end - - - -#=============================================================================== -# Decreases the target's Attack and Special Attack by 1 stage each. Always hits. -# (Noble Roar) -#=============================================================================== -class PokeBattle_Move_13A < PokeBattle_TargetMultiStatDownMove - def ignoresSubstitute?(user); return true; end - - def initialize(battle,move) - super - @statDown = [:ATTACK,1,:SPECIAL_ATTACK,1] - end - - def pbAccuracyCheck(user,target); return true; end -end - - - -#=============================================================================== -# Decreases the user's Defense by 1 stage. Always hits. Ends target's -# protections immediately. (Hyperspace Fury) -#=============================================================================== -class PokeBattle_Move_13B < PokeBattle_StatDownMove - def ignoresSubstitute?(user); return true; end - - def initialize(battle,move) - super - @statDown = [:DEFENSE,1] - end - - def pbMoveFailed?(user,targets) - if !user.isSpecies?(:HOOPA) - @battle.pbDisplay(_INTL("But {1} can't use the move!",user.pbThis(true))) - return true - elsif user.form!=1 - @battle.pbDisplay(_INTL("But {1} can't use it the way it is now!",user.pbThis(true))) - return true - end - return false - end - - def pbAccuracyCheck(user,target); return true; end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::BanefulBunker] = false - target.effects[PBEffects::KingsShield] = false - target.effects[PBEffects::Protect] = false - target.effects[PBEffects::SpikyShield] = false - target.pbOwnSide.effects[PBEffects::CraftyShield] = false - target.pbOwnSide.effects[PBEffects::MatBlock] = false - target.pbOwnSide.effects[PBEffects::QuickGuard] = false - target.pbOwnSide.effects[PBEffects::WideGuard] = false - end -end - - - -#=============================================================================== -# Decreases the target's Special Attack by 1 stage. Always hits. (Confide) -#=============================================================================== -class PokeBattle_Move_13C < PokeBattle_TargetStatDownMove - def ignoresSubstitute?(user); return true; end - - def initialize(battle,move) - super - @statDown = [:SPECIAL_ATTACK,1] - end - - def pbAccuracyCheck(user,target); return true; end -end - - - -#=============================================================================== -# Decreases the target's Special Attack by 2 stages. (Eerie Impulse) -#=============================================================================== -class PokeBattle_Move_13D < PokeBattle_TargetStatDownMove - def initialize(battle,move) - super - @statDown = [:SPECIAL_ATTACK,2] - end -end - - - -#=============================================================================== -# Increases the Attack and Special Attack of all Grass-type Pokémon in battle by -# 1 stage each. Doesn't affect airborne Pokémon. (Rototiller) -#=============================================================================== -class PokeBattle_Move_13E < PokeBattle_Move - def pbMoveFailed?(user,targets) - @validTargets = [] - @battle.eachBattler do |b| - next if !b.pbHasType?(:GRASS) - next if b.airborne? || b.semiInvulnerable? - next if !b.pbCanRaiseStatStage?(:ATTACK,user,self) && - !b.pbCanRaiseStatStage?(:SPECIAL_ATTACK,user,self) - @validTargets.push(b.index) - end - if @validTargets.length==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - return false if @validTargets.include?(target.index) - return true if !target.pbHasType?(:GRASS) - return true if target.airborne? || target.semiInvulnerable? - @battle.pbDisplay(_INTL("{1}'s stats can't be raised further!",target.pbThis)) - return true - end - - def pbEffectAgainstTarget(user,target) - showAnim = true - if target.pbCanRaiseStatStage?(:ATTACK,user,self) - if target.pbRaiseStatStage(:ATTACK,1,user,showAnim) - showAnim = false - end - end - if target.pbCanRaiseStatStage?(:SPECIAL_ATTACK,user,self) - target.pbRaiseStatStage(:SPECIAL_ATTACK,1,user,showAnim) - end - end -end - - - -#=============================================================================== -# Increases the Defense of all Grass-type Pokémon on the field by 1 stage each. -# (Flower Shield) -#=============================================================================== -class PokeBattle_Move_13F < PokeBattle_Move - def pbMoveFailed?(user,targets) - @validTargets = [] - @battle.eachBattler do |b| - next if !b.pbHasType?(:GRASS) - next if b.semiInvulnerable? - next if !b.pbCanRaiseStatStage?(:DEFENSE,user,self) - @validTargets.push(b.index) - end - if @validTargets.length==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - return false if @validTargets.include?(target.index) - return true if !target.pbHasType?(:GRASS) || target.semiInvulnerable? - return !target.pbCanRaiseStatStage?(:DEFENSE,user,self,true) - end - - def pbEffectAgainstTarget(user,target) - target.pbRaiseStatStage(:DEFENSE,1,user) - end -end - - - -#=============================================================================== -# Decreases the Attack, Special Attack and Speed of all poisoned targets by 1 -# stage each. (Venom Drench) -#=============================================================================== -class PokeBattle_Move_140 < PokeBattle_Move - def pbMoveFailed?(user,targets) - @validTargets = [] - targets.each do |b| - next if !b || b.fainted? - next if !b.poisoned? - next if !b.pbCanLowerStatStage?(:ATTACK,user,self) && - !b.pbCanLowerStatStage?(:SPECIAL_ATTACK,user,self) && - !b.pbCanLowerStatStage?(:SPEED,user,self) - @validTargets.push(b.index) - end - if @validTargets.length==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - return if !@validTargets.include?(target.index) - showAnim = true - [:ATTACK,:SPECIAL_ATTACK,:SPEED].each do |s| - next if !target.pbCanLowerStatStage?(s,user,self) - if target.pbLowerStatStage(s,1,user,showAnim) - showAnim = false - end - end - end -end - - - -#=============================================================================== -# Reverses all stat changes of the target. (Topsy-Turvy) -#=============================================================================== -class PokeBattle_Move_141 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - failed = true - GameData::Stat.each_battle do |s| - next if target.stages[s.id] == 0 - failed = false - break - end - if failed - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - GameData::Stat.each_battle { |s| target.stages[s.id] *= -1 } - @battle.pbDisplay(_INTL("{1}'s stats were reversed!",target.pbThis)) - end -end - - - -#=============================================================================== -# Gives target the Ghost type. (Trick-or-Treat) -#=============================================================================== -class PokeBattle_Move_142 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if !GameData::Type.exists?(:GHOST) || target.pbHasType?(:GHOST) || !target.canChangeType? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Type3] = :GHOST - typeName = GameData::Type.get(:GHOST).name - @battle.pbDisplay(_INTL("{1} transformed into the {2} type!",target.pbThis,typeName)) - end -end - - - -#=============================================================================== -# Gives target the Grass type. (Forest's Curse) -#=============================================================================== -class PokeBattle_Move_143 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if !GameData::Type.exists?(:GRASS) || target.pbHasType?(:GRASS) || !target.canChangeType? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Type3] = :GRASS - typeName = GameData::Type.get(:GRASS).name - @battle.pbDisplay(_INTL("{1} transformed into the {2} type!",target.pbThis,typeName)) - end -end - - - -#=============================================================================== -# Type effectiveness is multiplied by the Flying-type's effectiveness against -# the target. Does double damage and has perfect accuracy if the target is -# Minimized. (Flying Press) -#=============================================================================== -class PokeBattle_Move_144 < PokeBattle_Move - def tramplesMinimize?(param=1) - return true if param==1 && Settings::MECHANICS_GENERATION >= 6 # Perfect accuracy - return true if param==2 # Double damage - return super - end - - def pbCalcTypeModSingle(moveType,defType,user,target) - ret = super - if GameData::Type.exists?(:FLYING) - flyingEff = Effectiveness.calculate_one(:FLYING, defType) - ret *= flyingEff.to_f / Effectiveness::NORMAL_EFFECTIVE_ONE - end - return ret - end -end - - - -#=============================================================================== -# Target's moves become Electric-type for the rest of the round. (Electrify) -#=============================================================================== -class PokeBattle_Move_145 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Electrify] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedTargetAlreadyMoved?(target) - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Electrify] = true - @battle.pbDisplay(_INTL("{1}'s moves have been electrified!",target.pbThis)) - end -end - - - -#=============================================================================== -# All Normal-type moves become Electric-type for the rest of the round. -# (Ion Deluge, Plasma Fists) -#=============================================================================== -class PokeBattle_Move_146 < PokeBattle_Move - def pbMoveFailed?(user,targets) - return false if damagingMove? - if @battle.field.effects[PBEffects::IonDeluge] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedLastInRound?(user) - return false - end - - def pbEffectGeneral(user) - return if @battle.field.effects[PBEffects::IonDeluge] - @battle.field.effects[PBEffects::IonDeluge] = true - @battle.pbDisplay(_INTL("A deluge of ions showers the battlefield!")) - end -end - - - -#=============================================================================== -# Always hits. Ends target's protections immediately. (Hyperspace Hole) -#=============================================================================== -class PokeBattle_Move_147 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - def pbAccuracyCheck(user,target); return true; end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::BanefulBunker] = false - target.effects[PBEffects::KingsShield] = false - target.effects[PBEffects::Protect] = false - target.effects[PBEffects::SpikyShield] = false - target.pbOwnSide.effects[PBEffects::CraftyShield] = false - target.pbOwnSide.effects[PBEffects::MatBlock] = false - target.pbOwnSide.effects[PBEffects::QuickGuard] = false - target.pbOwnSide.effects[PBEffects::WideGuard] = false - end -end - - - -#=============================================================================== -# Powders the foe. This round, if it uses a Fire move, it loses 1/4 of its max -# HP instead. (Powder) -#=============================================================================== -class PokeBattle_Move_148 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbFailsAgainstTarget?(user,target) - if target.effects[PBEffects::Powder] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Powder] = true - @battle.pbDisplay(_INTL("{1} is covered in powder!",user.pbThis)) - end -end - - - -#=============================================================================== -# This round, the user's side is unaffected by damaging moves. (Mat Block) -#=============================================================================== -class PokeBattle_Move_149 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.turnCount > 1 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedLastInRound?(user) - return false - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::MatBlock] = true - @battle.pbDisplay(_INTL("{1} intends to flip up a mat and block incoming attacks!",user.pbThis)) - end -end - - - -#=============================================================================== -# User's side is protected against status moves this round. (Crafty Shield) -#=============================================================================== -class PokeBattle_Move_14A < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOwnSide.effects[PBEffects::CraftyShield] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return true if pbMoveFailedLastInRound?(user) - return false - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::CraftyShield] = true - @battle.pbDisplay(_INTL("Crafty Shield protected {1}!",user.pbTeam(true))) - end -end - - - -#=============================================================================== -# User is protected against damaging moves this round. Decreases the Attack of -# the user of a stopped contact move by 2 stages. (King's Shield) -#=============================================================================== -class PokeBattle_Move_14B < PokeBattle_ProtectMove - def initialize(battle,move) - super - @effect = PBEffects::KingsShield - end -end - - - -#=============================================================================== -# User is protected against moves that target it this round. Damages the user of -# a stopped contact move by 1/8 of its max HP. (Spiky Shield) -#=============================================================================== -class PokeBattle_Move_14C < PokeBattle_ProtectMove - def initialize(battle,move) - super - @effect = PBEffects::SpikyShield - end -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, attacks second turn. (Phantom Force) -# Is invulnerable during use. Ends target's protections upon hit. -#=============================================================================== -class PokeBattle_Move_14D < PokeBattle_Move_0CD - # NOTE: This move is identical to function code 0CD (Shadow Force). -end - - - -#=============================================================================== -# Two turn attack. Skips first turn, and increases the user's Special Attack, -# Special Defense and Speed by 2 stages each in the second turn. (Geomancy) -#=============================================================================== -class PokeBattle_Move_14E < PokeBattle_TwoTurnMove - def pbMoveFailed?(user,targets) - return false if user.effects[PBEffects::TwoTurnAttack] # Charging turn - if !user.pbCanRaiseStatStage?(:SPECIAL_ATTACK,user,self) && - !user.pbCanRaiseStatStage?(:SPECIAL_DEFENSE,user,self) && - !user.pbCanRaiseStatStage?(:SPEED,user,self) - @battle.pbDisplay(_INTL("{1}'s stats won't go any higher!",user.pbThis)) - return true - end - return false - end - - def pbChargingTurnMessage(user,targets) - @battle.pbDisplay(_INTL("{1} is absorbing power!",user.pbThis)) - end - - def pbEffectGeneral(user) - return if !@damagingTurn - showAnim = true - [:SPECIAL_ATTACK,:SPECIAL_DEFENSE,:SPEED].each do |s| - next if !user.pbCanRaiseStatStage?(s,user,self) - if user.pbRaiseStatStage(s,2,user,showAnim) - showAnim = false - end - end - end -end - - - -#=============================================================================== -# User gains 3/4 the HP it inflicts as damage. (Draining Kiss, Oblivion Wing) -#=============================================================================== -class PokeBattle_Move_14F < PokeBattle_Move - def healingMove?; return Settings::MECHANICS_GENERATION >= 6; end - - def pbEffectAgainstTarget(user,target) - return if target.damageState.hpLost<=0 - hpGain = (target.damageState.hpLost*0.75).round - user.pbRecoverHPFromDrain(hpGain,target) - end -end - - - -#=============================================================================== -# If this move KO's the target, increases the user's Attack by 3 stages. -# (Fell Stinger) -#=============================================================================== -class PokeBattle_Move_150 < PokeBattle_Move - def pbEffectAfterAllHits(user,target) - return if !target.damageState.fainted - return if !user.pbCanRaiseStatStage?(:ATTACK,user,self) - user.pbRaiseStatStage(:ATTACK,3,user) - end -end - - - -#=============================================================================== -# Decreases the target's Attack and Special Attack by 1 stage each. Then, user -# switches out. Ignores trapping moves. (Parting Shot) -#=============================================================================== -class PokeBattle_Move_151 < PokeBattle_TargetMultiStatDownMove - def initialize(battle,move) - super - @statDown = [:ATTACK,1,:SPECIAL_ATTACK,1] - end - - def pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers) - switcher = user - targets.each do |b| - next if switchedBattlers.include?(b.index) - switcher = b if b.effects[PBEffects::MagicCoat] || b.effects[PBEffects::MagicBounce] - end - return if switcher.fainted? || numHits==0 - return if !@battle.pbCanChooseNonActive?(switcher.index) - @battle.pbDisplay(_INTL("{1} went back to {2}!",switcher.pbThis, - @battle.pbGetOwnerName(switcher.index))) - @battle.pbPursuit(switcher.index) - return if switcher.fainted? - newPkmn = @battle.pbGetReplacementPokemonIndex(switcher.index) # Owner chooses - return if newPkmn<0 - @battle.pbRecallAndReplace(switcher.index,newPkmn) - @battle.pbClearChoice(switcher.index) # Replacement Pokémon does nothing this round - @battle.moldBreaker = false if switcher.index==user.index - switchedBattlers.push(switcher.index) - switcher.pbEffectsOnSwitchIn(true) - end -end - - - -#=============================================================================== -# No Pokémon can switch out or flee until the end of the next round, as long as -# the user remains active. (Fairy Lock) -#=============================================================================== -class PokeBattle_Move_152 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if @battle.field.effects[PBEffects::FairyLock]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - @battle.field.effects[PBEffects::FairyLock] = 2 - @battle.pbDisplay(_INTL("No one will be able to run away during the next turn!")) - end -end - - - -#=============================================================================== -# Entry hazard. Lays stealth rocks on the opposing side. (Sticky Web) -#=============================================================================== -class PokeBattle_Move_153 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.pbOpposingSide.effects[PBEffects::StickyWeb] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOpposingSide.effects[PBEffects::StickyWeb] = true - @battle.pbDisplay(_INTL("A sticky web has been laid out beneath {1}'s feet!", - user.pbOpposingTeam(true))) - end -end - - - -#=============================================================================== -# For 5 rounds, creates an electric terrain which boosts Electric-type moves and -# prevents Pokémon from falling asleep. Affects non-airborne Pokémon only. -# (Electric Terrain) -#=============================================================================== -class PokeBattle_Move_154 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if @battle.field.terrain == :Electric - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - @battle.pbStartTerrain(user, :Electric) - end -end - - - -#=============================================================================== -# For 5 rounds, creates a grassy terrain which boosts Grass-type moves and heals -# Pokémon at the end of each round. Affects non-airborne Pokémon only. -# (Grassy Terrain) -#=============================================================================== -class PokeBattle_Move_155 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if @battle.field.terrain == :Grassy - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - @battle.pbStartTerrain(user, :Grassy) - end -end - - - -#=============================================================================== -# For 5 rounds, creates a misty terrain which weakens Dragon-type moves and -# protects Pokémon from status problems. Affects non-airborne Pokémon only. -# (Misty Terrain) -#=============================================================================== -class PokeBattle_Move_156 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if @battle.field.terrain == :Misty - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - @battle.pbStartTerrain(user, :Misty) - end -end - - - -#=============================================================================== -# Doubles the prize money the player gets after winning the battle. (Happy Hour) -#=============================================================================== -class PokeBattle_Move_157 < PokeBattle_Move - def pbEffectGeneral(user) - @battle.field.effects[PBEffects::HappyHour] = true if !user.opposes? - @battle.pbDisplay(_INTL("Everyone is caught up in the happy atmosphere!")) - end -end - - - -#=============================================================================== -# Fails unless user has consumed a berry at some point. (Belch) -#=============================================================================== -class PokeBattle_Move_158 < PokeBattle_Move - def pbCanChooseMove?(user,commandPhase,showMessages) - if !user.belched? - if showMessages - msg = _INTL("{1} hasn't eaten any held berry, so it can't possibly belch!",user.pbThis) - (commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg) - end - return false - end - return true - end - - def pbMoveFailed?(user,targets) - if !user.belched? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end -end - - - -#=============================================================================== -# Poisons the target and decreases its Speed by 1 stage. (Toxic Thread) -#=============================================================================== -class PokeBattle_Move_159 < PokeBattle_Move - def pbFailsAgainstTarget?(user,target) - if !target.pbCanPoison?(user,false,self) && - !target.pbCanLowerStatStage?(:SPEED,user,self) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.pbPoison(user) if target.pbCanPoison?(user,false,self) - if target.pbCanLowerStatStage?(:SPEED,user,self) - target.pbLowerStatStage(:SPEED,1,user) - end - end -end - - - -#=============================================================================== -# Cures the target's burn. (Sparkling Aria) -#=============================================================================== -class PokeBattle_Move_15A < PokeBattle_Move - def pbAdditionalEffect(user,target) - return if target.fainted? || target.damageState.substitute - return if target.status != :BURN - target.pbCureStatus - end -end - - - -#=============================================================================== -# Cures the target's permanent status problems. Heals user by 1/2 of its max HP. -# (Purify) -#=============================================================================== -class PokeBattle_Move_15B < PokeBattle_HealingMove - def pbFailsAgainstTarget?(user,target) - if target.status == :NONE - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbHealAmount(user) - return (user.totalhp/2.0).round - end - - def pbEffectAgainstTarget(user,target) - target.pbCureStatus - super - end -end - - - -#=============================================================================== -# Increases the user's and its ally's Attack and Special Attack by 1 stage each, -# if they have Plus or Minus. (Gear Up) -#=============================================================================== -# NOTE: In Gen 5, this move should have a target of UserSide, while in Gen 6+ it -# should have a target of UserAndAllies. This is because, in Gen 5, this -# move shouldn't call def pbSuccessCheckAgainstTarget for each Pokémon -# currently in battle that will be affected by this move (i.e. allies -# aren't protected by their substitute/ability/etc., but they are in Gen -# 6+). We achieve this by not targeting any battlers in Gen 5, since -# pbSuccessCheckAgainstTarget is only called for targeted battlers. -class PokeBattle_Move_15C < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbMoveFailed?(user,targets) - @validTargets = [] - @battle.eachSameSideBattler(user) do |b| - next if !b.hasActiveAbility?([:MINUS,:PLUS]) - next if !b.pbCanRaiseStatStage?(:ATTACK,user,self) && - !b.pbCanRaiseStatStage?(:SPECIAL_ATTACK,user,self) - @validTargets.push(b) - end - if @validTargets.length==0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbFailsAgainstTarget?(user,target) - return false if @validTargets.any? { |b| b.index==target.index } - return true if !target.hasActiveAbility?([:MINUS,:PLUS]) - @battle.pbDisplay(_INTL("{1}'s stats can't be raised further!",target.pbThis)) - return true - end - - def pbEffectAgainstTarget(user,target) - showAnim = true - if target.pbCanRaiseStatStage?(:ATTACK,user,self) - if target.pbRaiseStatStage(:ATTACK,1,user,showAnim) - showAnim = false - end - end - if target.pbCanRaiseStatStage?(:SPECIAL_ATTACK,user,self) - target.pbRaiseStatStage(:SPECIAL_ATTACK,1,user,showAnim) - end - end - - def pbEffectGeneral(user) - return if pbTarget(user) != :UserSide - @validTargets.each { |b| pbEffectAgainstTarget(user,b) } - end -end - - - -#=============================================================================== -# User gains stat stages equal to each of the target's positive stat stages, -# and target's positive stat stages become 0, before damage calculation. -# (Spectral Thief) -#=============================================================================== -class PokeBattle_Move_15D < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbCalcDamage(user,target,numTargets=1) - if target.hasRaisedStatStages? - pbShowAnimation(@id,user,target,1) # Stat stage-draining animation - @battle.pbDisplay(_INTL("{1} stole the target's boosted stats!",user.pbThis)) - showAnim = true - GameData::Stat.each_battle do |s| - next if target.stages[s.id] <= 0 - if user.pbCanRaiseStatStage?(s.id,user,self) - if user.pbRaiseStatStage(s.id,target.stages[s.id],user,showAnim) - showAnim = false - end - end - target.stages[s.id] = 0 - end - end - super - end -end - - - -#=============================================================================== -# Until the end of the next round, the user's moves will always be critical hits. -# (Laser Focus) -#=============================================================================== -class PokeBattle_Move_15E < PokeBattle_Move - def pbEffectGeneral(user) - user.effects[PBEffects::LaserFocus] = 2 - @battle.pbDisplay(_INTL("{1} concentrated intensely!",user.pbThis)) - end -end - - - -#=============================================================================== -# Decreases the user's Defense by 1 stage. (Clanging Scales) -#=============================================================================== -class PokeBattle_Move_15F < PokeBattle_StatDownMove - def initialize(battle,move) - super - @statDown = [:DEFENSE,1] - end -end - - - -#=============================================================================== -# Decreases the target's Attack by 1 stage. Heals user by an amount equal to the -# target's Attack stat (after applying stat stages, before this move decreases -# it). (Strength Sap) -#=============================================================================== -class PokeBattle_Move_160 < PokeBattle_Move - def healingMove?; return true; end - - def pbFailsAgainstTarget?(user,target) - # NOTE: The official games appear to just check whether the target's Attack - # stat stage is -6 and fail if so, but I've added the "fail if target - # has Contrary and is at +6" check too for symmetry. This move still - # works even if the stat stage cannot be changed due to an ability or - # other effect. - if !@battle.moldBreaker && target.hasActiveAbility?(:CONTRARY) && - target.statStageAtMax?(:ATTACK) - @battle.pbDisplay(_INTL("But it failed!")) - return true - elsif target.statStageAtMin?(:ATTACK) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - # Calculate target's effective attack value - stageMul = [2,2,2,2,2,2, 2, 3,4,5,6,7,8] - stageDiv = [8,7,6,5,4,3, 2, 2,2,2,2,2,2] - atk = target.attack - atkStage = target.stages[:ATTACK]+6 - healAmt = (atk.to_f*stageMul[atkStage]/stageDiv[atkStage]).floor - # Reduce target's Attack stat - if target.pbCanLowerStatStage?(:ATTACK,user,self) - target.pbLowerStatStage(:ATTACK,1,user) - end - # Heal user - if target.hasActiveAbility?(:LIQUIDOOZE) - @battle.pbShowAbilitySplash(target) - user.pbReduceHP(healAmt) - @battle.pbDisplay(_INTL("{1} sucked up the liquid ooze!",user.pbThis)) - @battle.pbHideAbilitySplash(target) - user.pbItemHPHealCheck - elsif user.canHeal? - healAmt = (healAmt*1.3).floor if user.hasActiveItem?(:BIGROOT) - user.pbRecoverHP(healAmt) - @battle.pbDisplay(_INTL("{1}'s HP was restored.",user.pbThis)) - end - end -end - - - -#=============================================================================== -# User and target swap their Speed stats (not their stat stages). (Speed Swap) -#=============================================================================== -class PokeBattle_Move_161 < PokeBattle_Move - def ignoresSubstitute?(user); return true; end - - def pbEffectAgainstTarget(user,target) - user.speed, target.speed = target.speed, user.speed - @battle.pbDisplay(_INTL("{1} switched Speed with its target!",user.pbThis)) - end -end - - - -#=============================================================================== -# User loses their Fire type. Fails if user is not Fire-type. (Burn Up) -#=============================================================================== -class PokeBattle_Move_162 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if !user.pbHasType?(:FIRE) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAfterAllHits(user,target) - if !user.effects[PBEffects::BurnUp] - user.effects[PBEffects::BurnUp] = true - @battle.pbDisplay(_INTL("{1} burned itself out!",user.pbThis)) - end - end -end - - - -#=============================================================================== -# Ignores all abilities that alter this move's success or damage. -# (Moongeist Beam, Sunsteel Strike) -#=============================================================================== -class PokeBattle_Move_163 < PokeBattle_Move - def pbChangeUsageCounters(user,specialUsage) - super - @battle.moldBreaker = true if !specialUsage - end -end - - - -#=============================================================================== -# Ignores all abilities that alter this move's success or damage. This move is -# physical if user's Attack is higher than its Special Attack (after applying -# stat stages), and special otherwise. (Photon Geyser) -#=============================================================================== -class PokeBattle_Move_164 < PokeBattle_Move_163 - def initialize(battle,move) - super - @calcCategory = 1 - end - - def physicalMove?(thisType=nil); return (@calcCategory==0); end - def specialMove?(thisType=nil); return (@calcCategory==1); end - - def pbOnStartUse(user,targets) - # Calculate user's effective attacking value - stageMul = [2,2,2,2,2,2, 2, 3,4,5,6,7,8] - stageDiv = [8,7,6,5,4,3, 2, 2,2,2,2,2,2] - atk = user.attack - atkStage = user.stages[:ATTACK]+6 - realAtk = (atk.to_f*stageMul[atkStage]/stageDiv[atkStage]).floor - spAtk = user.spatk - spAtkStage = user.stages[:SPECIAL_ATTACK]+6 - realSpAtk = (spAtk.to_f*stageMul[spAtkStage]/stageDiv[spAtkStage]).floor - # Determine move's category - @calcCategory = (realAtk>realSpAtk) ? 0 : 1 - end -end - - - -#=============================================================================== -# Negates the target's ability while it remains on the field, if it has already -# performed its action this round. (Core Enforcer) -#=============================================================================== -class PokeBattle_Move_165 < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - return if target.damageState.substitute || target.effects[PBEffects::GastroAcid] - return if target.unstoppableAbility? - return if @battle.choices[target.index][0]!=:UseItem && - !((@battle.choices[target.index][0]==:UseMove || - @battle.choices[target.index][0]==:Shift) && target.movedThisRound?) - target.effects[PBEffects::GastroAcid] = true - target.effects[PBEffects::Truant] = false - @battle.pbDisplay(_INTL("{1}'s Ability was suppressed!",target.pbThis)) - target.pbOnAbilityChanged(target.ability) - end -end - - - -#=============================================================================== -# Power is doubled if the user's last move failed. (Stomping Tantrum) -#=============================================================================== -class PokeBattle_Move_166 < PokeBattle_Move - def pbBaseDamage(baseDmg,user,target) - baseDmg *= 2 if user.lastRoundMoveFailed - return baseDmg - end -end - - - -#=============================================================================== -# For 5 rounds, lowers power of attacks against the user's side. Fails if -# weather is not hail. (Aurora Veil) -#=============================================================================== -class PokeBattle_Move_167 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if @battle.pbWeather != :Hail - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if user.pbOwnSide.effects[PBEffects::AuroraVeil]>0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - user.pbOwnSide.effects[PBEffects::AuroraVeil] = 5 - user.pbOwnSide.effects[PBEffects::AuroraVeil] = 8 if user.hasActiveItem?(:LIGHTCLAY) - @battle.pbDisplay(_INTL("{1} made {2} stronger against physical and special moves!", - @name,user.pbTeam(true))) - end -end - - - -#=============================================================================== -# User is protected against moves with the "B" flag this round. If a Pokémon -# makes contact with the user while this effect applies, that Pokémon is -# poisoned. (Baneful Bunker) -#=============================================================================== -class PokeBattle_Move_168 < PokeBattle_ProtectMove - def initialize(battle,move) - super - @effect = PBEffects::BanefulBunker - end -end - - - -#=============================================================================== -# This move's type is the same as the user's first type. (Revelation Dance) -#=============================================================================== -class PokeBattle_Move_169 < PokeBattle_Move - def pbBaseType(user) - userTypes = user.pbTypes(true) - return userTypes[0] - end -end - - - -#=============================================================================== -# This round, target becomes the target of attacks that have single targets. -# (Spotlight) -#=============================================================================== -class PokeBattle_Move_16A < PokeBattle_Move - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Spotlight] = 1 - target.eachAlly do |b| - next if b.effects[PBEffects::Spotlight]0 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - target.effects[PBEffects::Instruct] = true - end -end - - - -#=============================================================================== -# Target cannot use sound-based moves for 2 more rounds. (Throat Chop) -#=============================================================================== -class PokeBattle_Move_16C < PokeBattle_Move - def pbAdditionalEffect(user,target) - return if target.fainted? || target.damageState.substitute - @battle.pbDisplay(_INTL("The effects of {1} prevent {2} from using certain moves!", - @name,target.pbThis(true))) if target.effects[PBEffects::ThroatChop]==0 - target.effects[PBEffects::ThroatChop] = 3 - end -end - - - -#=============================================================================== -# Heals user by 1/2 of its max HP, or 2/3 of its max HP in a sandstorm. (Shore Up) -#=============================================================================== -class PokeBattle_Move_16D < PokeBattle_HealingMove - def pbHealAmount(user) - return (user.totalhp*2/3.0).round if @battle.pbWeather == :Sandstorm - return (user.totalhp/2.0).round - end -end - - - -#=============================================================================== -# Heals target by 1/2 of its max HP, or 2/3 of its max HP in Grassy Terrain. -# (Floral Healing) -#=============================================================================== -class PokeBattle_Move_16E < PokeBattle_Move - def healingMove?; return true; end - - def pbFailsAgainstTarget?(user,target) - if target.hp==target.totalhp - @battle.pbDisplay(_INTL("{1}'s HP is full!",target.pbThis)) - return true - elsif !target.canHeal? - @battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis)) - return true - end - return false - end - - def pbEffectAgainstTarget(user,target) - hpGain = (target.totalhp/2.0).round - hpGain = (target.totalhp*2/3.0).round if @battle.field.terrain == :Grassy - target.pbRecoverHP(hpGain) - @battle.pbDisplay(_INTL("{1}'s HP was restored.",target.pbThis)) - end -end - - - -#=============================================================================== -# Damages target if target is a foe, or heals target by 1/2 of its max HP if -# target is an ally. (Pollen Puff) -#=============================================================================== -class PokeBattle_Move_16F < PokeBattle_Move - def pbTarget(user) - return GameData::Target.get(:NearFoe) if user.effects[PBEffects::HealBlock]>0 - return super - end - - def pbOnStartUse(user,targets) - @healing = false - @healing = !user.opposes?(targets[0]) if targets.length>0 - end - - def pbFailsAgainstTarget?(user,target) - return false if !@healing - if target.effects[PBEffects::Substitute]>0 && !ignoresSubstitute?(user) - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if !target.canHeal? - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbDamagingMove? - return false if @healing - return super - end - - def pbEffectAgainstTarget(user,target) - return if !@healing - target.pbRecoverHP(target.totalhp/2) - @battle.pbDisplay(_INTL("{1}'s HP was restored.",target.pbThis)) - end - - def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true) - hitNum = 1 if @healing # Healing anim - super - end -end - - - -#=============================================================================== -# Damages user by 1/2 of its max HP, even if this move misses. (Mind Blown) -#=============================================================================== -class PokeBattle_Move_170 < PokeBattle_Move - def worksWithNoTargets?; return true; end - - def pbMoveFailed?(user,targets) - if !@battle.moldBreaker - bearer = @battle.pbCheckGlobalAbility(:DAMP) - if bearer!=nil - @battle.pbShowAbilitySplash(bearer) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - @battle.pbDisplay(_INTL("{1} cannot use {2}!",user.pbThis,@name)) - else - @battle.pbDisplay(_INTL("{1} cannot use {2} because of {3}'s {4}!", - user.pbThis,@name,bearer.pbThis(true),bearer.abilityName)) - end - @battle.pbHideAbilitySplash(bearer) - return true - end - end - return false - end - - def pbSelfKO(user) - return if !user.takesIndirectDamage? - user.pbReduceHP((user.totalhp/2.0).round,false) - user.pbItemHPHealCheck - end -end - - - -#=============================================================================== -# Fails if user has not been hit by an opponent's physical move this round. -# (Shell Trap) -#=============================================================================== -class PokeBattle_Move_171 < PokeBattle_Move - def pbDisplayChargeMessage(user) - user.effects[PBEffects::ShellTrap] = true - @battle.pbCommonAnimation("ShellTrap",user) - @battle.pbDisplay(_INTL("{1} set a shell trap!",user.pbThis)) - end - - def pbDisplayUseMessage(user) - super if user.tookPhysicalHit - end - - def pbMoveFailed?(user,targets) - if !user.effects[PBEffects::ShellTrap] - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - if !user.tookPhysicalHit - @battle.pbDisplay(_INTL("{1}'s shell trap didn't work!",user.pbThis)) - return true - end - return false - end -end - - - -#=============================================================================== -# If a Pokémon makes contact with the user before it uses this move, the -# attacker is burned. (Beak Blast) -#=============================================================================== -class PokeBattle_Move_172 < PokeBattle_Move - def pbDisplayChargeMessage(user) - user.effects[PBEffects::BeakBlast] = true - @battle.pbCommonAnimation("BeakBlast",user) - @battle.pbDisplay(_INTL("{1} started heating up its beak!",user.pbThis)) - end -end - - - -#=============================================================================== -# For 5 rounds, creates a psychic terrain which boosts Psychic-type moves and -# prevents Pokémon from being hit by >0 priority moves. Affects non-airborne -# Pokémon only. (Psychic Terrain) -#=============================================================================== -class PokeBattle_Move_173 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if @battle.field.terrain == :Psychic - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end - - def pbEffectGeneral(user) - @battle.pbStartTerrain(user, :Psychic) - end -end - - - -#=============================================================================== -# Fails if this isn't the user's first turn. (First Impression) -#=============================================================================== -class PokeBattle_Move_174 < PokeBattle_Move - def pbMoveFailed?(user,targets) - if user.turnCount > 1 - @battle.pbDisplay(_INTL("But it failed!")) - return true - end - return false - end -end - - - -#=============================================================================== -# Hits twice. Causes the target to flinch. Does double damage and has perfect -# accuracy if the target is Minimized. (Double Iron Bash) -#=============================================================================== -class PokeBattle_Move_175 < PokeBattle_FlinchMove - def multiHitMove?; return true; end - def pbNumHits(user,targets); return 2; end - def tramplesMinimize?(param=1); return true; end -end - - - -# NOTE: If you're inventing new move effects, use function code 176 and onwards. -# Actually, you might as well use high numbers like 500+ (up to FFFF), -# just to make sure later additions to Essentials don't clash with your -# new effects. diff --git a/Data/Scripts/011_Battle/003_Battle/001_PokeBattle_BattleCommon.rb b/Data/Scripts/011_Battle/003_Battle/001_PokeBattle_BattleCommon.rb deleted file mode 100644 index 62cfb31089..0000000000 --- a/Data/Scripts/011_Battle/003_Battle/001_PokeBattle_BattleCommon.rb +++ /dev/null @@ -1,215 +0,0 @@ -module PokeBattle_BattleCommon - #============================================================================= - # Store caught Pokémon - #============================================================================= - def pbStorePokemon(pkmn) - # Nickname the Pokémon (unless it's a Shadow Pokémon) - if !pkmn.shadowPokemon? - if pbDisplayConfirm(_INTL("Would you like to give a nickname to {1}?", pkmn.name)) - nickname = @scene.pbNameEntry(_INTL("{1}'s nickname?", pkmn.speciesName), pkmn) - pkmn.name = nickname - end - end - # Store the Pokémon - currentBox = @peer.pbCurrentBox - storedBox = @peer.pbStorePokemon(pbPlayer,pkmn) - if storedBox<0 - pbDisplayPaused(_INTL("{1} has been added to your party.",pkmn.name)) - @initialItems[0][pbPlayer.party.length-1] = pkmn.item_id if @initialItems - return - end - # Messages saying the Pokémon was stored in a PC box - creator = @peer.pbGetStorageCreatorName - curBoxName = @peer.pbBoxName(currentBox) - boxName = @peer.pbBoxName(storedBox) - if storedBox!=currentBox - if creator - pbDisplayPaused(_INTL("Box \"{1}\" on {2}'s PC was full.",curBoxName,creator)) - else - pbDisplayPaused(_INTL("Box \"{1}\" on someone's PC was full.",curBoxName)) - end - pbDisplayPaused(_INTL("{1} was transferred to box \"{2}\".",pkmn.name,boxName)) - else - if creator - pbDisplayPaused(_INTL("{1} was transferred to {2}'s PC.",pkmn.name,creator)) - else - pbDisplayPaused(_INTL("{1} was transferred to someone's PC.",pkmn.name)) - end - pbDisplayPaused(_INTL("It was stored in box \"{1}\".",boxName)) - end - end - - # Register all caught Pokémon in the Pokédex, and store them. - def pbRecordAndStoreCaughtPokemon - @caughtPokemon.each do |pkmn| - pbPlayer.pokedex.register(pkmn) # In case the form changed upon leaving battle - # Record the Pokémon's species as owned in the Pokédex - if !pbPlayer.owned?(pkmn.species) - pbPlayer.pokedex.set_owned(pkmn.species) - if $Trainer.has_pokedex - pbDisplayPaused(_INTL("{1}'s data was added to the Pokédex.",pkmn.name)) - pbPlayer.pokedex.register_last_seen(pkmn) - @scene.pbShowPokedex(pkmn.species) - end - end - # Record a Shadow Pokémon's species as having been caught - pbPlayer.pokedex.set_shadow_pokemon_owned(pkmn.species) if pkmn.shadowPokemon? - # Store caught Pokémon - pbStorePokemon(pkmn) - end - @caughtPokemon.clear - end - - #============================================================================= - # Throw a Poké Ball - #============================================================================= - def pbThrowPokeBall(idxBattler,ball,catch_rate=nil,showPlayer=false) - # Determine which Pokémon you're throwing the Poké Ball at - battler = nil - if opposes?(idxBattler) - battler = @battlers[idxBattler] - else - battler = @battlers[idxBattler].pbDirectOpposing(true) - end - if battler.fainted? - battler.eachAlly do |b| - battler = b - break - end - end - # Messages - itemName = GameData::Item.get(ball).name - if battler.fainted? - if itemName.starts_with_vowel? - pbDisplay(_INTL("{1} threw an {2}!",pbPlayer.name,itemName)) - else - pbDisplay(_INTL("{1} threw a {2}!",pbPlayer.name,itemName)) - end - pbDisplay(_INTL("But there was no target...")) - return - end - if itemName.starts_with_vowel? - pbDisplayBrief(_INTL("{1} threw an {2}!",pbPlayer.name,itemName)) - else - pbDisplayBrief(_INTL("{1} threw a {2}!",pbPlayer.name,itemName)) - end - # Animation of opposing trainer blocking Poké Balls (unless it's a Snag Ball - # at a Shadow Pokémon) - if trainerBattle? && !(GameData::Item.get(ball).is_snag_ball? && battler.shadowPokemon?) - @scene.pbThrowAndDeflect(ball,1) - pbDisplay(_INTL("The Trainer blocked your Poké Ball! Don't be a thief!")) - return - end - # Calculate the number of shakes (4=capture) - pkmn = battler.pokemon - @criticalCapture = false - numShakes = pbCaptureCalc(pkmn,battler,catch_rate,ball) - PBDebug.log("[Threw Poké Ball] #{itemName}, #{numShakes} shakes (4=capture)") - # Animation of Ball throw, absorb, shake and capture/burst out - @scene.pbThrow(ball,numShakes,@criticalCapture,battler.index,showPlayer) - # Outcome message - case numShakes - when 0 - pbDisplay(_INTL("Oh no! The Pokémon broke free!")) - BallHandlers.onFailCatch(ball,self,battler) - when 1 - pbDisplay(_INTL("Aww! It appeared to be caught!")) - BallHandlers.onFailCatch(ball,self,battler) - when 2 - pbDisplay(_INTL("Aargh! Almost had it!")) - BallHandlers.onFailCatch(ball,self,battler) - when 3 - pbDisplay(_INTL("Gah! It was so close, too!")) - BallHandlers.onFailCatch(ball,self,battler) - when 4 - pbDisplayBrief(_INTL("Gotcha! {1} was caught!",pkmn.name)) - @scene.pbThrowSuccess # Play capture success jingle - pbRemoveFromParty(battler.index,battler.pokemonIndex) - # Gain Exp - if Settings::GAIN_EXP_FOR_CAPTURE - battler.captured = true - pbGainExp - battler.captured = false - end - battler.pbReset - if pbAllFainted?(battler.index) - @decision = (trainerBattle?) ? 1 : 4 # Battle ended by win/capture - end - # Modify the Pokémon's properties because of the capture - if GameData::Item.get(ball).is_snag_ball? - pkmn.owner = Pokemon::Owner.new_from_trainer(pbPlayer) - end - BallHandlers.onCatch(ball,self,pkmn) - pkmn.poke_ball = ball - pkmn.makeUnmega if pkmn.mega? - pkmn.makeUnprimal - pkmn.update_shadow_moves if pkmn.shadowPokemon? - pkmn.record_first_moves - # Reset form - pkmn.forced_form = nil if MultipleForms.hasFunction?(pkmn.species,"getForm") - @peer.pbOnLeavingBattle(self,pkmn,true,true) - # Make the Poké Ball and data box disappear - @scene.pbHideCaptureBall(idxBattler) - # Save the Pokémon for storage at the end of battle - @caughtPokemon.push(pkmn) - end - end - - #============================================================================= - # Calculate how many shakes a thrown Poké Ball will make (4 = capture) - #============================================================================= - def pbCaptureCalc(pkmn,battler,catch_rate,ball) - return 4 if $DEBUG && Input.press?(Input::CTRL) - # Get a catch rate if one wasn't provided - catch_rate = pkmn.species_data.catch_rate if !catch_rate - # Modify catch_rate depending on the Poké Ball's effect - ultraBeast = [:NIHILEGO, :BUZZWOLE, :PHEROMOSA, :XURKITREE, :CELESTEELA, - :KARTANA, :GUZZLORD, :POIPOLE, :NAGANADEL, :STAKATAKA, - :BLACEPHALON].include?(pkmn.species) - if !ultraBeast || ball == :BEASTBALL - catch_rate = BallHandlers.modifyCatchRate(ball,catch_rate,self,battler,ultraBeast) - else - catch_rate /= 10 - end - # First half of the shakes calculation - a = battler.totalhp - b = battler.hp - x = ((3*a-2*b)*catch_rate.to_f)/(3*a) - # Calculation modifiers - if battler.status == :SLEEP || battler.status == :FROZEN - x *= 2.5 - elsif battler.status != :NONE - x *= 1.5 - end - x = x.floor - x = 1 if x<1 - # Definite capture, no need to perform randomness checks - return 4 if x>=255 || BallHandlers.isUnconditional?(ball,self,battler) - # Second half of the shakes calculation - y = ( 65536 / ((255.0/x)**0.1875) ).floor - # Critical capture check - if Settings::ENABLE_CRITICAL_CAPTURES - c = 0 - numOwned = $Trainer.pokedex.owned_count - if numOwned>600; c = x*5/12 - elsif numOwned>450; c = x*4/12 - elsif numOwned>300; c = x*3/12 - elsif numOwned>150; c = x*2/12 - elsif numOwned>30; c = x/12 - end - # Calculate the number of shakes - if c>0 && pbRandom(256)0 && !sleepTalk - pbDisplayPaused(_INTL("There's no PP left for this move!")) if showMessages - return false - end - if battler.effects[PBEffects::Encore]>0 - idxEncoredMove = battler.pbEncoredMoveIndex - return false if idxEncoredMove>=0 && idxMove!=idxEncoredMove - end - return battler.pbCanChooseMove?(move,true,showMessages,sleepTalk) - end - - def pbCanChooseAnyMove?(idxBattler,sleepTalk=false) - battler = @battlers[idxBattler] - battler.eachMoveWithIndex do |m,i| - next if m.pp==0 && m.total_pp>0 && !sleepTalk - if battler.effects[PBEffects::Encore]>0 - idxEncoredMove = battler.pbEncoredMoveIndex - next if idxEncoredMove>=0 && i!=idxEncoredMove - end - next if !battler.pbCanChooseMove?(m,true,false,sleepTalk) - return true - end - return false - end - - # Called when the Pokémon is Encored, or if it can't use any of its moves. - # Makes the Pokémon use the Encored move (if Encored), or Struggle. - def pbAutoChooseMove(idxBattler,showMessages=true) - battler = @battlers[idxBattler] - if battler.fainted? - pbClearChoice(idxBattler) - return true - end - # Encore - idxEncoredMove = battler.pbEncoredMoveIndex - if idxEncoredMove>=0 && pbCanChooseMove?(idxBattler,idxEncoredMove,false) - encoreMove = battler.moves[idxEncoredMove] - @choices[idxBattler][0] = :UseMove # "Use move" - @choices[idxBattler][1] = idxEncoredMove # Index of move to be used - @choices[idxBattler][2] = encoreMove # PokeBattle_Move object - @choices[idxBattler][3] = -1 # No target chosen yet - return true if singleBattle? - if pbOwnedByPlayer?(idxBattler) - if showMessages - pbDisplayPaused(_INTL("{1} has to use {2}!",battler.name,encoreMove.name)) - end - return pbChooseTarget(battler,encoreMove) - end - return true - end - # Struggle - if pbOwnedByPlayer?(idxBattler) && showMessages - pbDisplayPaused(_INTL("{1} has no moves left!",battler.name)) - end - @choices[idxBattler][0] = :UseMove # "Use move" - @choices[idxBattler][1] = -1 # Index of move to be used - @choices[idxBattler][2] = @struggle # Struggle PokeBattle_Move object - @choices[idxBattler][3] = -1 # No target chosen yet - return true - end - - def pbRegisterMove(idxBattler,idxMove,showMessages=true) - battler = @battlers[idxBattler] - move = battler.moves[idxMove] - return false if !pbCanChooseMove?(idxBattler,idxMove,showMessages) - @choices[idxBattler][0] = :UseMove # "Use move" - @choices[idxBattler][1] = idxMove # Index of move to be used - @choices[idxBattler][2] = move # PokeBattle_Move object - @choices[idxBattler][3] = -1 # No target chosen yet - return true - end - - def pbChoseMove?(idxBattler,moveID) - return false if !@battlers[idxBattler] || @battlers[idxBattler].fainted? - if @choices[idxBattler][0]==:UseMove && @choices[idxBattler][1] - return @choices[idxBattler][2].id == moveID - end - return false - end - - def pbChoseMoveFunctionCode?(idxBattler,code) - return false if @battlers[idxBattler].fainted? - if @choices[idxBattler][0]==:UseMove && @choices[idxBattler][1] - return @choices[idxBattler][2].function == code - end - return false - end - - def pbRegisterTarget(idxBattler,idxTarget) - @choices[idxBattler][3] = idxTarget # Set target of move - end - - # Returns whether the idxTarget will be targeted by a move with target_data - # used by a battler in idxUser. - def pbMoveCanTarget?(idxUser,idxTarget,target_data) - return false if target_data.num_targets == 0 - case target_data.id - when :NearAlly - return false if opposes?(idxUser,idxTarget) - return false if !nearBattlers?(idxUser,idxTarget) - when :UserOrNearAlly - return true if idxUser==idxTarget - return false if opposes?(idxUser,idxTarget) - return false if !nearBattlers?(idxUser,idxTarget) - when :UserAndAllies - return false if opposes?(idxUser,idxTarget) - when :NearFoe, :RandomNearFoe, :AllNearFoes - return false if !opposes?(idxUser,idxTarget) - return false if !nearBattlers?(idxUser,idxTarget) - when :Foe - return false if !opposes?(idxUser,idxTarget) - when :AllFoes - return false if !opposes?(idxUser,idxTarget) - when :NearOther, :AllNearOthers - return false if !nearBattlers?(idxUser,idxTarget) - when :Other - return false if idxUser==idxTarget - end - return true - end - - #============================================================================= - # Turn order calculation (priority) - #============================================================================= - def pbCalculatePriority(fullCalc=false,indexArray=nil) - needRearranging = false - if fullCalc - @priorityTrickRoom = (@field.effects[PBEffects::TrickRoom]>0) - # Recalculate everything from scratch - randomOrder = Array.new(maxBattlerIndex+1) { |i| i } - (randomOrder.length-1).times do |i| # Can't use shuffle! here - r = i+pbRandom(randomOrder.length-i) - randomOrder[i], randomOrder[r] = randomOrder[r], randomOrder[i] - end - @priority.clear - for i in 0..maxBattlerIndex - b = @battlers[i] - next if !b - # [battler, speed, sub-priority, priority, tie-breaker order] - bArray = [b,b.pbSpeed,0,0,randomOrder[i]] - if @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift - # Calculate move's priority - if @choices[b.index][0]==:UseMove - move = @choices[b.index][2] - pri = move.priority - if b.abilityActive? - pri = BattleHandlers.triggerPriorityChangeAbility(b.ability,b,move,pri) - end - bArray[3] = pri - @choices[b.index][4] = pri - end - # Calculate sub-priority (first/last within priority bracket) - # NOTE: Going fast beats going slow. A Pokémon with Stall and Quick - # Claw will go first in its priority bracket if Quick Claw - # triggers, regardless of Stall. - subPri = 0 - # Abilities (Stall) - if b.abilityActive? - newSubPri = BattleHandlers.triggerPriorityBracketChangeAbility(b.ability, - b,subPri,self) - if subPri!=newSubPri - subPri = newSubPri - b.effects[PBEffects::PriorityAbility] = true - b.effects[PBEffects::PriorityItem] = false - end - end - # Items (Quick Claw, Custap Berry, Lagging Tail, Full Incense) - if b.itemActive? - newSubPri = BattleHandlers.triggerPriorityBracketChangeItem(b.item, - b,subPri,self) - if subPri!=newSubPri - subPri = newSubPri - b.effects[PBEffects::PriorityAbility] = false - b.effects[PBEffects::PriorityItem] = true - end - end - bArray[2] = subPri - end - @priority.push(bArray) - end - needRearranging = true - else - if (@field.effects[PBEffects::TrickRoom]>0)!=@priorityTrickRoom - needRearranging = true - @priorityTrickRoom = (@field.effects[PBEffects::TrickRoom]>0) - end - # Just recheck all battler speeds - @priority.each do |orderArray| - next if !orderArray - next if indexArray && !indexArray.include?(orderArray[0].index) - oldSpeed = orderArray[1] - orderArray[1] = orderArray[0].pbSpeed - needRearranging = true if orderArray[1]!=oldSpeed - end - end - # Reorder the priority array - if needRearranging - @priority.sort! { |a,b| - if a[3]!=b[3] - # Sort by priority (highest value first) - b[3]<=>a[3] - elsif a[2]!=b[2] - # Sort by sub-priority (highest value first) - b[2]<=>a[2] - elsif @priorityTrickRoom - # Sort by speed (lowest first), and use tie-breaker if necessary - (a[1]==b[1]) ? b[4]<=>a[4] : a[1]<=>b[1] - else - # Sort by speed (highest first), and use tie-breaker if necessary - (a[1]==b[1]) ? b[4]<=>a[4] : b[1]<=>a[1] - end - } - # Write the priority order to the debug log - logMsg = (fullCalc) ? "[Round order] " : "[Round order recalculated] " - comma = false - @priority.each do |orderArray| - logMsg += ", " if comma - logMsg += "#{orderArray[0].pbThis(comma)} (#{orderArray[0].index})" - comma = true - end - PBDebug.log(logMsg) - end - end - - def pbPriority(onlySpeedSort=false) - ret = [] - if onlySpeedSort - # Sort battlers by their speed stats and tie-breaker order only. - tempArray = [] - @priority.each { |pArray| tempArray.push([pArray[0],pArray[1],pArray[4]]) } - tempArray.sort! { |a,b| (a[1]==b[1]) ? b[2]<=>a[2] : b[1]<=>a[1] } - tempArray.each { |tArray| ret.push(tArray[0]) } - else - # Sort battlers by priority, sub-priority and their speed. Ties are - # resolved in the same way each time this method is called in a round. - @priority.each { |pArray| ret.push(pArray[0]) if !pArray[0].fainted? } - end - return ret - end -end diff --git a/Data/Scripts/011_Battle/003_Battle/006_Battle_Action_Switching.rb b/Data/Scripts/011_Battle/003_Battle/006_Battle_Action_Switching.rb deleted file mode 100644 index 006f7c55b5..0000000000 --- a/Data/Scripts/011_Battle/003_Battle/006_Battle_Action_Switching.rb +++ /dev/null @@ -1,412 +0,0 @@ -class PokeBattle_Battle - #============================================================================= - # Choosing Pokémon to switch - #============================================================================= - # Checks whether the replacement Pokémon (at party index idxParty) can enter - # battle. - # NOTE: Messages are only shown while in the party screen when choosing a - # command for the next round. - def pbCanSwitchLax?(idxBattler,idxParty,partyScene=nil) - return true if idxParty<0 - party = pbParty(idxBattler) - return false if idxParty>=party.length - return false if !party[idxParty] - if party[idxParty].egg? - partyScene.pbDisplay(_INTL("An Egg can't battle!")) if partyScene - return false - end - if !pbIsOwner?(idxBattler,idxParty) - owner = pbGetOwnerFromPartyIndex(idxBattler,idxParty) - partyScene.pbDisplay(_INTL("You can't switch {1}'s Pokémon with one of yours!", - owner.name)) if partyScene - return false - end - if party[idxParty].fainted? - partyScene.pbDisplay(_INTL("{1} has no energy left to battle!", - party[idxParty].name)) if partyScene - return false - end - if pbFindBattler(idxParty,idxBattler) - partyScene.pbDisplay(_INTL("{1} is already in battle!", - party[idxParty].name)) if partyScene - return false - end - return true - end - - # Check whether the currently active Pokémon (at battler index idxBattler) can - # switch out (and that its replacement at party index idxParty can switch in). - # NOTE: Messages are only shown while in the party screen when choosing a - # command for the next round. - def pbCanSwitch?(idxBattler,idxParty=-1,partyScene=nil) - # Check whether party Pokémon can switch in - return false if !pbCanSwitchLax?(idxBattler,idxParty,partyScene) - # Make sure another battler isn't already choosing to switch to the party - # Pokémon - eachSameSideBattler(idxBattler) do |b| - next if choices[b.index][0]!=:SwitchOut || choices[b.index][1]!=idxParty - partyScene.pbDisplay(_INTL("{1} has already been selected.", - pbParty(idxBattler)[idxParty].name)) if partyScene - return false - end - # Check whether battler can switch out - battler = @battlers[idxBattler] - return true if battler.fainted? - # Ability/item effects that allow switching no matter what - if battler.abilityActive? - if BattleHandlers.triggerCertainSwitchingUserAbility(battler.ability,battler,self) - return true - end - end - if battler.itemActive? - if BattleHandlers.triggerCertainSwitchingUserItem(battler.item,battler,self) - return true - end - end - # Other certain switching effects - return true if Settings::MORE_TYPE_EFFECTS && battler.pbHasType?(:GHOST) - # Other certain trapping effects - if battler.effects[PBEffects::Trapping]>0 || - battler.effects[PBEffects::MeanLook]>=0 || - battler.effects[PBEffects::Ingrain] || - @field.effects[PBEffects::FairyLock]>0 - partyScene.pbDisplay(_INTL("{1} can't be switched out!",battler.pbThis)) if partyScene - return false - end - # Trapping abilities/items - eachOtherSideBattler(idxBattler) do |b| - next if !b.abilityActive? - if BattleHandlers.triggerTrappingTargetAbility(b.ability,battler,b,self) - partyScene.pbDisplay(_INTL("{1}'s {2} prevents switching!", - b.pbThis,b.abilityName)) if partyScene - return false - end - end - eachOtherSideBattler(idxBattler) do |b| - next if !b.itemActive? - if BattleHandlers.triggerTrappingTargetItem(b.item,battler,b,self) - partyScene.pbDisplay(_INTL("{1}'s {2} prevents switching!", - b.pbThis,b.itemName)) if partyScene - return false - end - end - return true - end - - def pbCanChooseNonActive?(idxBattler) - pbParty(idxBattler).each_with_index do |_pkmn,i| - return true if pbCanSwitchLax?(idxBattler,i) - end - return false - end - - def pbRegisterSwitch(idxBattler,idxParty) - return false if !pbCanSwitch?(idxBattler,idxParty) - @choices[idxBattler][0] = :SwitchOut - @choices[idxBattler][1] = idxParty # Party index of Pokémon to switch in - @choices[idxBattler][2] = nil - return true - end - - #============================================================================= - # Open the party screen and potentially pick a replacement Pokémon (or AI - # chooses replacement) - #============================================================================= - # Open party screen and potentially choose a Pokémon to switch with. Used in - # all instances where the party screen is opened. - def pbPartyScreen(idxBattler,checkLaxOnly=false,canCancel=false,shouldRegister=false) - ret = -1 - @scene.pbPartyScreen(idxBattler,canCancel) { |idxParty,partyScene| - if checkLaxOnly - next false if !pbCanSwitchLax?(idxBattler,idxParty,partyScene) - else - next false if !pbCanSwitch?(idxBattler,idxParty,partyScene) - end - if shouldRegister - next false if idxParty<0 || !pbRegisterSwitch(idxBattler,idxParty) - end - ret = idxParty - next true - } - return ret - end - - # For choosing a replacement Pokémon when prompted in the middle of other - # things happening (U-turn, Baton Pass, in def pbSwitch). - def pbSwitchInBetween(idxBattler,checkLaxOnly=false,canCancel=false) - return pbPartyScreen(idxBattler,checkLaxOnly,canCancel) if pbOwnedByPlayer?(idxBattler) - return @battleAI.pbDefaultChooseNewEnemy(idxBattler,pbParty(idxBattler)) - end - - #============================================================================= - # Switching Pokémon - #============================================================================= - # General switching method that checks if any Pokémon need to be sent out and, - # if so, does. Called at the end of each round. - def pbEORSwitch(favorDraws=false) - return if @decision>0 && !favorDraws - return if @decision==5 && favorDraws - pbJudge - return if @decision>0 - # Check through each fainted battler to see if that spot can be filled. - switched = [] - loop do - switched.clear - @battlers.each do |b| - next if !b || !b.fainted? - idxBattler = b.index - next if !pbCanChooseNonActive?(idxBattler) - if !pbOwnedByPlayer?(idxBattler) # Opponent/ally is switching in - next if wildBattle? && opposes?(idxBattler) # Wild Pokémon can't switch - idxPartyNew = pbSwitchInBetween(idxBattler) - opponent = pbGetOwnerFromBattlerIndex(idxBattler) - # NOTE: The player is only offered the chance to switch their own - # Pokémon when an opponent replaces a fainted Pokémon in single - # battles. In double battles, etc. there is no such offer. - if @internalBattle && @switchStyle && trainerBattle? && pbSideSize(0)==1 && - opposes?(idxBattler) && !@battlers[0].fainted? && !switched.include?(0) && - pbCanChooseNonActive?(0) && @battlers[0].effects[PBEffects::Outrage]==0 - idxPartyForName = idxPartyNew - enemyParty = pbParty(idxBattler) - if enemyParty[idxPartyNew].ability == :ILLUSION - new_index = pbLastInTeam(idxBattler) - idxPartyForName = new_index if new_index >= 0 && new_index != idxPartyNew - end - if pbDisplayConfirm(_INTL("{1} is about to send in {2}. Will you switch your Pokémon?", - opponent.full_name, enemyParty[idxPartyForName].name)) - idxPlayerPartyNew = pbSwitchInBetween(0,false,true) - if idxPlayerPartyNew>=0 - pbMessageOnRecall(@battlers[0]) - pbRecallAndReplace(0,idxPlayerPartyNew) - switched.push(0) - end - end - end - pbRecallAndReplace(idxBattler,idxPartyNew) - switched.push(idxBattler) - elsif trainerBattle? # Player switches in in a trainer battle - idxPlayerPartyNew = pbGetReplacementPokemonIndex(idxBattler) # Owner chooses - pbRecallAndReplace(idxBattler,idxPlayerPartyNew) - switched.push(idxBattler) - else # Player's Pokémon has fainted in a wild battle - switch = false - if !pbDisplayConfirm(_INTL("Use next Pokémon?")) - switch = (pbRun(idxBattler,true)<=0) - else - switch = true - end - if switch - idxPlayerPartyNew = pbGetReplacementPokemonIndex(idxBattler) # Owner chooses - pbRecallAndReplace(idxBattler,idxPlayerPartyNew) - switched.push(idxBattler) - end - end - end - break if switched.length==0 - pbPriority(true).each do |b| - b.pbEffectsOnSwitchIn(true) if switched.include?(b.index) - end - end - end - - def pbGetReplacementPokemonIndex(idxBattler,random=false) - if random - return -1 if !pbCanSwitch?(idxBattler) # Can battler switch out? - choices = [] # Find all Pokémon that can switch in - eachInTeamFromBattlerIndex(idxBattler) do |_pkmn,i| - choices.push(i) if pbCanSwitchLax?(idxBattler,i) - end - return -1 if choices.length==0 - return choices[pbRandom(choices.length)] - else - return pbSwitchInBetween(idxBattler,true) - end - end - - # Actually performs the recalling and sending out in all situations. - def pbRecallAndReplace(idxBattler,idxParty,randomReplacement=false,batonPass=false) - @scene.pbRecall(idxBattler) if !@battlers[idxBattler].fainted? - @battlers[idxBattler].pbAbilitiesOnSwitchOut # Inc. primordial weather check - @scene.pbShowPartyLineup(idxBattler&1) if pbSideSize(idxBattler)==1 - pbMessagesOnReplace(idxBattler,idxParty) if !randomReplacement - pbReplace(idxBattler,idxParty,batonPass) - end - - def pbMessageOnRecall(battler) - if battler.pbOwnedByPlayer? - if battler.hp<=battler.totalhp/4 - pbDisplayBrief(_INTL("Good job, {1}! Come back!",battler.name)) - elsif battler.hp<=battler.totalhp/2 - pbDisplayBrief(_INTL("OK, {1}! Come back!",battler.name)) - elsif battler.turnCount>=5 - pbDisplayBrief(_INTL("{1}, that's enough! Come back!",battler.name)) - elsif battler.turnCount>=2 - pbDisplayBrief(_INTL("{1}, come back!",battler.name)) - else - pbDisplayBrief(_INTL("{1}, switch out! Come back!",battler.name)) - end - else - owner = pbGetOwnerName(battler.index) - pbDisplayBrief(_INTL("{1} withdrew {2}!",owner,battler.name)) - end - end - - # Only called from def pbRecallAndReplace and Battle Arena's def pbSwitch. - def pbMessagesOnReplace(idxBattler,idxParty) - party = pbParty(idxBattler) - newPkmnName = party[idxParty].name - if party[idxParty].ability == :ILLUSION - new_index = pbLastInTeam(idxBattler) - newPkmnName = party[new_index].name if new_index >= 0 && new_index != idxParty - end - if pbOwnedByPlayer?(idxBattler) - opposing = @battlers[idxBattler].pbDirectOpposing - if opposing.fainted? || opposing.hp==opposing.totalhp - pbDisplayBrief(_INTL("You're in charge, {1}!",newPkmnName)) - elsif opposing.hp>=opposing.totalhp/2 - pbDisplayBrief(_INTL("Go for it, {1}!",newPkmnName)) - elsif opposing.hp>=opposing.totalhp/4 - pbDisplayBrief(_INTL("Just a little more! Hang in there, {1}!",newPkmnName)) - else - pbDisplayBrief(_INTL("Your opponent's weak! Get 'em, {1}!",newPkmnName)) - end - else - owner = pbGetOwnerFromBattlerIndex(idxBattler) - pbDisplayBrief(_INTL("{1} sent out {2}!",owner.full_name,newPkmnName)) - end - end - - # Only called from def pbRecallAndReplace above and Battle Arena's def - # pbSwitch. - def pbReplace(idxBattler,idxParty,batonPass=false) - party = pbParty(idxBattler) - idxPartyOld = @battlers[idxBattler].pokemonIndex - # Initialise the new Pokémon - @battlers[idxBattler].pbInitialize(party[idxParty],idxParty,batonPass) - # Reorder the party for this battle - partyOrder = pbPartyOrder(idxBattler) - partyOrder[idxParty],partyOrder[idxPartyOld] = partyOrder[idxPartyOld],partyOrder[idxParty] - # Send out the new Pokémon - pbSendOut([[idxBattler,party[idxParty]]]) - pbCalculatePriority(false,[idxBattler]) if Settings::RECALCULATE_TURN_ORDER_AFTER_SPEED_CHANGES - end - - # Called from def pbReplace above and at the start of battle. - # sendOuts is an array; each element is itself an array: [idxBattler,pkmn] - def pbSendOut(sendOuts,startBattle=false) - sendOuts.each { |b| @peer.pbOnEnteringBattle(self,b[1]) } - @scene.pbSendOutBattlers(sendOuts,startBattle) - sendOuts.each do |b| - @scene.pbResetMoveIndex(b[0]) - pbSetSeen(@battlers[b[0]]) - @usedInBattle[b[0]&1][b[0]/2] = true - end - end - - #============================================================================= - # Effects upon a Pokémon entering battle - #============================================================================= - # Called at the start of battle only. - def pbOnActiveAll - # Weather-inducing abilities, Trace, Imposter, etc. - pbCalculatePriority(true) - pbPriority(true).each { |b| b.pbEffectsOnSwitchIn(true) } - pbCalculatePriority - # Check forms are correct - eachBattler { |b| b.pbCheckForm } - end - - # Called when a Pokémon switches in (entry effects, entry hazards). - def pbOnActiveOne(battler) - return false if battler.fainted? - # Introduce Shadow Pokémon - if battler.opposes? && battler.shadowPokemon? - pbCommonAnimation("Shadow",battler) - pbDisplay(_INTL("Oh!\nA Shadow Pokémon!")) - end - # Record money-doubling effect of Amulet Coin/Luck Incense - if !battler.opposes? && [:AMULETCOIN, :LUCKINCENSE].include?(battler.item_id) - @field.effects[PBEffects::AmuletCoin] = true - end - # Update battlers' participants (who will gain Exp/EVs when a battler faints) - eachBattler { |b| b.pbUpdateParticipants } - # Healing Wish - if @positions[battler.index].effects[PBEffects::HealingWish] - pbCommonAnimation("HealingWish",battler) - pbDisplay(_INTL("The healing wish came true for {1}!",battler.pbThis(true))) - battler.pbRecoverHP(battler.totalhp) - battler.pbCureStatus(false) - @positions[battler.index].effects[PBEffects::HealingWish] = false - end - # Lunar Dance - if @positions[battler.index].effects[PBEffects::LunarDance] - pbCommonAnimation("LunarDance",battler) - pbDisplay(_INTL("{1} became cloaked in mystical moonlight!",battler.pbThis)) - battler.pbRecoverHP(battler.totalhp) - battler.pbCureStatus(false) - battler.eachMove { |m| m.pp = m.total_pp } - @positions[battler.index].effects[PBEffects::LunarDance] = false - end - # Entry hazards - # Stealth Rock - if battler.pbOwnSide.effects[PBEffects::StealthRock] && battler.takesIndirectDamage? && - GameData::Type.exists?(:ROCK) - bTypes = battler.pbTypes(true) - eff = Effectiveness.calculate(:ROCK, bTypes[0], bTypes[1], bTypes[2]) - if !Effectiveness.ineffective?(eff) - eff = eff.to_f / Effectiveness::NORMAL_EFFECTIVE - oldHP = battler.hp - battler.pbReduceHP(battler.totalhp*eff/8,false) - pbDisplay(_INTL("Pointed stones dug into {1}!",battler.pbThis)) - battler.pbItemHPHealCheck - if battler.pbAbilitiesOnDamageTaken(oldHP) # Switched out - return pbOnActiveOne(battler) # For replacement battler - end - end - end - # Spikes - if battler.pbOwnSide.effects[PBEffects::Spikes]>0 && battler.takesIndirectDamage? && - !battler.airborne? - spikesDiv = [8,6,4][battler.pbOwnSide.effects[PBEffects::Spikes]-1] - oldHP = battler.hp - battler.pbReduceHP(battler.totalhp/spikesDiv,false) - pbDisplay(_INTL("{1} is hurt by the spikes!",battler.pbThis)) - battler.pbItemHPHealCheck - if battler.pbAbilitiesOnDamageTaken(oldHP) # Switched out - return pbOnActiveOne(battler) # For replacement battler - end - end - # Toxic Spikes - if battler.pbOwnSide.effects[PBEffects::ToxicSpikes]>0 && !battler.fainted? && - !battler.airborne? - if battler.pbHasType?(:POISON) - battler.pbOwnSide.effects[PBEffects::ToxicSpikes] = 0 - pbDisplay(_INTL("{1} absorbed the poison spikes!",battler.pbThis)) - elsif battler.pbCanPoison?(nil,false) - if battler.pbOwnSide.effects[PBEffects::ToxicSpikes]==2 - battler.pbPoison(nil,_INTL("{1} was badly poisoned by the poison spikes!",battler.pbThis),true) - else - battler.pbPoison(nil,_INTL("{1} was poisoned by the poison spikes!",battler.pbThis)) - end - end - end - # Sticky Web - if battler.pbOwnSide.effects[PBEffects::StickyWeb] && !battler.fainted? && - !battler.airborne? - pbDisplay(_INTL("{1} was caught in a sticky web!",battler.pbThis)) - if battler.pbCanLowerStatStage?(:SPEED) - battler.pbLowerStatStage(:SPEED,1,nil) - battler.pbItemStatRestoreCheck - end - end - # Battler faints if it is knocked out because of an entry hazard above - if battler.fainted? - battler.pbFaint - pbGainExp - pbJudge - return false - end - battler.pbCheckForm - return true - end -end diff --git a/Data/Scripts/011_Battle/003_Battle/012_Battle_Phase_EndOfRound.rb b/Data/Scripts/011_Battle/003_Battle/012_Battle_Phase_EndOfRound.rb deleted file mode 100644 index ab90bcf9ec..0000000000 --- a/Data/Scripts/011_Battle/003_Battle/012_Battle_Phase_EndOfRound.rb +++ /dev/null @@ -1,666 +0,0 @@ -class PokeBattle_Battle - #============================================================================= - # Decrement effect counters - #============================================================================= - def pbEORCountDownBattlerEffect(priority,effect) - priority.each do |b| - next if b.fainted? || b.effects[effect]==0 - b.effects[effect] -= 1 - yield b if block_given? && b.effects[effect]==0 - end - end - - def pbEORCountDownSideEffect(side,effect,msg) - if @sides[side].effects[effect]>0 - @sides[side].effects[effect] -= 1 - pbDisplay(msg) if @sides[side].effects[effect]==0 - end - end - - def pbEORCountDownFieldEffect(effect,msg) - if @field.effects[effect]>0 - @field.effects[effect] -= 1 - if @field.effects[effect]==0 - pbDisplay(msg) - if effect==PBEffects::MagicRoom - pbPriority(true).each { |b| b.pbItemTerrainStatBoostCheck } - end - end - end - end - - #============================================================================= - # End Of Round weather - #============================================================================= - def pbEORWeather(priority) - # NOTE: Primordial weather doesn't need to be checked here, because if it - # could wear off here, it will have worn off already. - # Count down weather duration - @field.weatherDuration -= 1 if @field.weatherDuration>0 - # Weather wears off - if @field.weatherDuration==0 - case @field.weather - when :Sun then pbDisplay(_INTL("The sunlight faded.")) - when :Rain then pbDisplay(_INTL("The rain stopped.")) - when :Sandstorm then pbDisplay(_INTL("The sandstorm subsided.")) - when :Hail then pbDisplay(_INTL("The hail stopped.")) - when :ShadowSky then pbDisplay(_INTL("The shadow sky faded.")) - end - @field.weather = :None - # Check for form changes caused by the weather changing - eachBattler { |b| b.pbCheckFormOnWeatherChange } - # Start up the default weather - pbStartWeather(nil,@field.defaultWeather) if @field.defaultWeather != :None - return if @field.weather == :None - end - # Weather continues - weather_data = GameData::BattleWeather.try_get(@field.weather) - pbCommonAnimation(weather_data.animation) if weather_data - case @field.weather -# when :Sun then pbDisplay(_INTL("The sunlight is strong.")) -# when :Rain then pbDisplay(_INTL("Rain continues to fall.")) - when :Sandstorm then pbDisplay(_INTL("The sandstorm is raging.")) - when :Hail then pbDisplay(_INTL("The hail is crashing down.")) -# when :HarshSun then pbDisplay(_INTL("The sunlight is extremely harsh.")) -# when :HeavyRain then pbDisplay(_INTL("It is raining heavily.")) -# when :StrongWinds then pbDisplay(_INTL("The wind is strong.")) - when :ShadowSky then pbDisplay(_INTL("The shadow sky continues.")) - end - # Effects due to weather - curWeather = pbWeather - priority.each do |b| - # Weather-related abilities - if b.abilityActive? - BattleHandlers.triggerEORWeatherAbility(b.ability,curWeather,b,self) - b.pbFaint if b.fainted? - end - # Weather damage - # NOTE: - case curWeather - when :Sandstorm - next if !b.takesSandstormDamage? - pbDisplay(_INTL("{1} is buffeted by the sandstorm!",b.pbThis)) - @scene.pbDamageAnimation(b) - b.pbReduceHP(b.totalhp/16,false) - b.pbItemHPHealCheck - b.pbFaint if b.fainted? - when :Hail - next if !b.takesHailDamage? - pbDisplay(_INTL("{1} is buffeted by the hail!",b.pbThis)) - @scene.pbDamageAnimation(b) - b.pbReduceHP(b.totalhp/16,false) - b.pbItemHPHealCheck - b.pbFaint if b.fainted? - when :ShadowSky - next if !b.takesShadowSkyDamage? - pbDisplay(_INTL("{1} is hurt by the shadow sky!",b.pbThis)) - @scene.pbDamageAnimation(b) - b.pbReduceHP(b.totalhp/16,false) - b.pbItemHPHealCheck - b.pbFaint if b.fainted? - end - end - end - - #============================================================================= - # End Of Round terrain - #============================================================================= - def pbEORTerrain - # Count down terrain duration - @field.terrainDuration -= 1 if @field.terrainDuration>0 - # Terrain wears off - if @field.terrain != :None && @field.terrainDuration == 0 - case @field.terrain - when :Electric - pbDisplay(_INTL("The electric current disappeared from the battlefield!")) - when :Grassy - pbDisplay(_INTL("The grass disappeared from the battlefield!")) - when :Misty - pbDisplay(_INTL("The mist disappeared from the battlefield!")) - when :Psychic - pbDisplay(_INTL("The weirdness disappeared from the battlefield!")) - end - @field.terrain = :None - # Start up the default terrain - pbStartTerrain(nil, @field.defaultTerrain, false) if @field.defaultTerrain != :None - return if @field.terrain == :None - end - # Terrain continues - terrain_data = GameData::BattleTerrain.try_get(@field.terrain) - pbCommonAnimation(terrain_data.animation) if terrain_data - case @field.terrain - when :Electric then pbDisplay(_INTL("An electric current is running across the battlefield.")) - when :Grassy then pbDisplay(_INTL("Grass is covering the battlefield.")) - when :Misty then pbDisplay(_INTL("Mist is swirling about the battlefield.")) - when :Psychic then pbDisplay(_INTL("The battlefield is weird.")) - end - end - - #============================================================================= - # End Of Round shift distant battlers to middle positions - #============================================================================= - def pbEORShiftDistantBattlers - # Move battlers around if none are near to each other - # NOTE: This code assumes each side has a maximum of 3 battlers on it, and - # is not generalised to larger side sizes. - if !singleBattle? - swaps = [] # Each element is an array of two battler indices to swap - for side in 0...2 - next if pbSideSize(side)==1 # Only battlers on sides of size 2+ need to move - # Check if any battler on this side is near any battler on the other side - anyNear = false - eachSameSideBattler(side) do |b| - eachOtherSideBattler(b) do |otherB| - next if !nearBattlers?(otherB.index,b.index) - anyNear = true - break - end - break if anyNear - end - break if anyNear - # No battlers on this side are near any battlers on the other side; try - # to move them - # NOTE: If we get to here (assuming both sides are of size 3 or less), - # there is definitely only 1 able battler on this side, so we - # don't need to worry about multiple battlers trying to move into - # the same position. If you add support for a side of size 4+, - # this code will need revising to account for that, as well as to - # add more complex code to ensure battlers will end up near each - # other. - eachSameSideBattler(side) do |b| - # Get the position to move to - pos = -1 - case pbSideSize(side) - when 2 then pos = [2,3,0,1][b.index] # The unoccupied position - when 3 then pos = (side==0) ? 2 : 3 # The centre position - end - next if pos<0 - # Can't move if the same trainer doesn't control both positions - idxOwner = pbGetOwnerIndexFromBattlerIndex(b.index) - next if pbGetOwnerIndexFromBattlerIndex(pos)!=idxOwner - swaps.push([b.index,pos]) - end - end - # Move battlers around - swaps.each do |pair| - next if pbSideSize(pair[0])==2 && swaps.length>1 - next if !pbSwapBattlers(pair[0],pair[1]) - case pbSideSize(side) - when 2 - pbDisplay(_INTL("{1} moved across!",@battlers[pair[1]].pbThis)) - when 3 - pbDisplay(_INTL("{1} moved to the center!",@battlers[pair[1]].pbThis)) - end - end - end - end - - #============================================================================= - # End Of Round phase - #============================================================================= - def pbEndOfRoundPhase - PBDebug.log("") - PBDebug.log("[End of round]") - @endOfRound = true - @scene.pbBeginEndOfRoundPhase - pbCalculatePriority # recalculate speeds - priority = pbPriority(true) # in order of fastest -> slowest speeds only - # Weather - pbEORWeather(priority) - # Future Sight/Doom Desire - @positions.each_with_index do |pos,idxPos| - next if !pos || pos.effects[PBEffects::FutureSightCounter]==0 - pos.effects[PBEffects::FutureSightCounter] -= 1 - next if pos.effects[PBEffects::FutureSightCounter]>0 - next if !@battlers[idxPos] || @battlers[idxPos].fainted? # No target - moveUser = nil - eachBattler do |b| - next if b.opposes?(pos.effects[PBEffects::FutureSightUserIndex]) - next if b.pokemonIndex!=pos.effects[PBEffects::FutureSightUserPartyIndex] - moveUser = b - break - end - next if moveUser && moveUser.index==idxPos # Target is the user - if !moveUser # User isn't in battle, get it from the party - party = pbParty(pos.effects[PBEffects::FutureSightUserIndex]) - pkmn = party[pos.effects[PBEffects::FutureSightUserPartyIndex]] - if pkmn && pkmn.able? - moveUser = PokeBattle_Battler.new(self,pos.effects[PBEffects::FutureSightUserIndex]) - moveUser.pbInitDummyPokemon(pkmn,pos.effects[PBEffects::FutureSightUserPartyIndex]) - end - end - next if !moveUser # User is fainted - move = pos.effects[PBEffects::FutureSightMove] - pbDisplay(_INTL("{1} took the {2} attack!",@battlers[idxPos].pbThis, - GameData::Move.get(move).name)) - # NOTE: Future Sight failing against the target here doesn't count towards - # Stomping Tantrum. - userLastMoveFailed = moveUser.lastMoveFailed - @futureSight = true - moveUser.pbUseMoveSimple(move,idxPos) - @futureSight = false - moveUser.lastMoveFailed = userLastMoveFailed - @battlers[idxPos].pbFaint if @battlers[idxPos].fainted? - pos.effects[PBEffects::FutureSightCounter] = 0 - pos.effects[PBEffects::FutureSightMove] = nil - pos.effects[PBEffects::FutureSightUserIndex] = -1 - pos.effects[PBEffects::FutureSightUserPartyIndex] = -1 - end - # Wish - @positions.each_with_index do |pos,idxPos| - next if !pos || pos.effects[PBEffects::Wish]==0 - pos.effects[PBEffects::Wish] -= 1 - next if pos.effects[PBEffects::Wish]>0 - next if !@battlers[idxPos] || !@battlers[idxPos].canHeal? - wishMaker = pbThisEx(idxPos,pos.effects[PBEffects::WishMaker]) - @battlers[idxPos].pbRecoverHP(pos.effects[PBEffects::WishAmount]) - pbDisplay(_INTL("{1}'s wish came true!",wishMaker)) - end - # Sea of Fire damage (Fire Pledge + Grass Pledge combination) - curWeather = pbWeather - for side in 0...2 - next if sides[side].effects[PBEffects::SeaOfFire]==0 - next if [:Rain, :HeavyRain].include?(curWeather) - @battle.pbCommonAnimation("SeaOfFire") if side==0 - @battle.pbCommonAnimation("SeaOfFireOpp") if side==1 - priority.each do |b| - next if b.opposes?(side) - next if !b.takesIndirectDamage? || b.pbHasType?(:FIRE) - oldHP = b.hp - @scene.pbDamageAnimation(b) - b.pbReduceHP(b.totalhp/8,false) - pbDisplay(_INTL("{1} is hurt by the sea of fire!",b.pbThis)) - b.pbItemHPHealCheck - b.pbAbilitiesOnDamageTaken(oldHP) - b.pbFaint if b.fainted? - end - end - # Status-curing effects/abilities and HP-healing items - priority.each do |b| - next if b.fainted? - # Grassy Terrain (healing) - if @field.terrain == :Grassy && b.affectedByTerrain? && b.canHeal? - PBDebug.log("[Lingering effect] Grassy Terrain heals #{b.pbThis(true)}") - b.pbRecoverHP(b.totalhp/16) - pbDisplay(_INTL("{1}'s HP was restored.",b.pbThis)) - end - # Healer, Hydration, Shed Skin - BattleHandlers.triggerEORHealingAbility(b.ability,b,self) if b.abilityActive? - # Black Sludge, Leftovers - BattleHandlers.triggerEORHealingItem(b.item,b,self) if b.itemActive? - end - # Aqua Ring - priority.each do |b| - next if !b.effects[PBEffects::AquaRing] - next if !b.canHeal? - hpGain = b.totalhp/16 - hpGain = (hpGain*1.3).floor if b.hasActiveItem?(:BIGROOT) - b.pbRecoverHP(hpGain) - pbDisplay(_INTL("Aqua Ring restored {1}'s HP!",b.pbThis(true))) - end - # Ingrain - priority.each do |b| - next if !b.effects[PBEffects::Ingrain] - next if !b.canHeal? - hpGain = b.totalhp/16 - hpGain = (hpGain*1.3).floor if b.hasActiveItem?(:BIGROOT) - b.pbRecoverHP(hpGain) - pbDisplay(_INTL("{1} absorbed nutrients with its roots!",b.pbThis)) - end - # Leech Seed - priority.each do |b| - next if b.effects[PBEffects::LeechSeed]<0 - next if !b.takesIndirectDamage? - recipient = @battlers[b.effects[PBEffects::LeechSeed]] - next if !recipient || recipient.fainted? - oldHP = b.hp - oldHPRecipient = recipient.hp - pbCommonAnimation("LeechSeed",recipient,b) - hpLoss = b.pbReduceHP(b.totalhp/8) - recipient.pbRecoverHPFromDrain(hpLoss,b, - _INTL("{1}'s health is sapped by Leech Seed!",b.pbThis)) - recipient.pbAbilitiesOnDamageTaken(oldHPRecipient) if recipient.hp0 - b.effects[PBEffects::Toxic] += 1 - b.effects[PBEffects::Toxic] = 15 if b.effects[PBEffects::Toxic]>15 - end - if b.hasActiveAbility?(:POISONHEAL) - if b.canHeal? - anim_name = GameData::Status.get(:POISON).animation - pbCommonAnimation(anim_name, b) if anim_name - pbShowAbilitySplash(b) - b.pbRecoverHP(b.totalhp/8) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - pbDisplay(_INTL("{1}'s HP was restored.",b.pbThis)) - else - pbDisplay(_INTL("{1}'s {2} restored its HP.",b.pbThis,b.abilityName)) - end - pbHideAbilitySplash(b) - end - elsif b.takesIndirectDamage? - oldHP = b.hp - dmg = (b.statusCount==0) ? b.totalhp/8 : b.totalhp*b.effects[PBEffects::Toxic]/16 - b.pbContinueStatus { b.pbReduceHP(dmg,false) } - b.pbItemHPHealCheck - b.pbAbilitiesOnDamageTaken(oldHP) - b.pbFaint if b.fainted? - end - end - # Damage from burn - priority.each do |b| - next if b.status != :BURN || !b.takesIndirectDamage? - oldHP = b.hp - dmg = (Settings::MECHANICS_GENERATION >= 7) ? b.totalhp/16 : b.totalhp/8 - dmg = (dmg/2.0).round if b.hasActiveAbility?(:HEATPROOF) - b.pbContinueStatus { b.pbReduceHP(dmg,false) } - b.pbItemHPHealCheck - b.pbAbilitiesOnDamageTaken(oldHP) - b.pbFaint if b.fainted? - end - # Damage from sleep (Nightmare) - priority.each do |b| - b.effects[PBEffects::Nightmare] = false if !b.asleep? - next if !b.effects[PBEffects::Nightmare] || !b.takesIndirectDamage? - oldHP = b.hp - b.pbReduceHP(b.totalhp/4) - pbDisplay(_INTL("{1} is locked in a nightmare!",b.pbThis)) - b.pbItemHPHealCheck - b.pbAbilitiesOnDamageTaken(oldHP) - b.pbFaint if b.fainted? - end - # Curse - priority.each do |b| - next if !b.effects[PBEffects::Curse] || !b.takesIndirectDamage? - oldHP = b.hp - b.pbReduceHP(b.totalhp/4) - pbDisplay(_INTL("{1} is afflicted by the curse!",b.pbThis)) - b.pbItemHPHealCheck - b.pbAbilitiesOnDamageTaken(oldHP) - b.pbFaint if b.fainted? - end - # Trapping attacks (Bind/Clamp/Fire Spin/Magma Storm/Sand Tomb/Whirlpool/Wrap) - priority.each do |b| - next if b.fainted? || b.effects[PBEffects::Trapping]==0 - b.effects[PBEffects::Trapping] -= 1 - moveName = GameData::Move.get(b.effects[PBEffects::TrappingMove]).name - if b.effects[PBEffects::Trapping]==0 - pbDisplay(_INTL("{1} was freed from {2}!",b.pbThis,moveName)) - else - case b.effects[PBEffects::TrappingMove] - when :BIND then pbCommonAnimation("Bind", b) - when :CLAMP then pbCommonAnimation("Clamp", b) - when :FIRESPIN then pbCommonAnimation("FireSpin", b) - when :MAGMASTORM then pbCommonAnimation("MagmaStorm", b) - when :SANDTOMB then pbCommonAnimation("SandTomb", b) - when :WRAP then pbCommonAnimation("Wrap", b) - when :INFESTATION then pbCommonAnimation("Infestation", b) - else pbCommonAnimation("Wrap", b) - end - if b.takesIndirectDamage? - hpLoss = (Settings::MECHANICS_GENERATION >= 6) ? b.totalhp/8 : b.totalhp/16 - if @battlers[b.effects[PBEffects::TrappingUser]].hasActiveItem?(:BINDINGBAND) - hpLoss = (Settings::MECHANICS_GENERATION >= 6) ? b.totalhp/6 : b.totalhp/8 - end - @scene.pbDamageAnimation(b) - b.pbReduceHP(hpLoss,false) - pbDisplay(_INTL("{1} is hurt by {2}!",b.pbThis,moveName)) - b.pbItemHPHealCheck - # NOTE: No need to call pbAbilitiesOnDamageTaken as b can't switch out. - b.pbFaint if b.fainted? - end - end - end - # Taunt - pbEORCountDownBattlerEffect(priority,PBEffects::Taunt) { |battler| - pbDisplay(_INTL("{1}'s taunt wore off!",battler.pbThis)) - } - # Encore - priority.each do |b| - next if b.fainted? || b.effects[PBEffects::Encore]==0 - idxEncoreMove = b.pbEncoredMoveIndex - if idxEncoreMove>=0 - b.effects[PBEffects::Encore] -= 1 - if b.effects[PBEffects::Encore]==0 || b.moves[idxEncoreMove].pp==0 - b.effects[PBEffects::Encore] = 0 - pbDisplay(_INTL("{1}'s encore ended!",b.pbThis)) - end - else - PBDebug.log("[End of effect] #{b.pbThis}'s encore ended (encored move no longer known)") - b.effects[PBEffects::Encore] = 0 - b.effects[PBEffects::EncoreMove] = nil - end - end - # Disable/Cursed Body - pbEORCountDownBattlerEffect(priority,PBEffects::Disable) { |battler| - battler.effects[PBEffects::DisableMove] = nil - pbDisplay(_INTL("{1} is no longer disabled!",battler.pbThis)) - } - # Magnet Rise - pbEORCountDownBattlerEffect(priority,PBEffects::MagnetRise) { |battler| - pbDisplay(_INTL("{1}'s electromagnetism wore off!",battler.pbThis)) - } - # Telekinesis - pbEORCountDownBattlerEffect(priority,PBEffects::Telekinesis) { |battler| - pbDisplay(_INTL("{1} was freed from the telekinesis!",battler.pbThis)) - } - # Heal Block - pbEORCountDownBattlerEffect(priority,PBEffects::HealBlock) { |battler| - pbDisplay(_INTL("{1}'s Heal Block wore off!",battler.pbThis)) - } - # Embargo - pbEORCountDownBattlerEffect(priority,PBEffects::Embargo) { |battler| - pbDisplay(_INTL("{1} can use items again!",battler.pbThis)) - battler.pbItemTerrainStatBoostCheck - } - # Yawn - pbEORCountDownBattlerEffect(priority,PBEffects::Yawn) { |battler| - if battler.pbCanSleepYawn? - PBDebug.log("[Lingering effect] #{battler.pbThis} fell asleep because of Yawn") - battler.pbSleep - end - } - # Perish Song - perishSongUsers = [] - priority.each do |b| - next if b.fainted? || b.effects[PBEffects::PerishSong]==0 - b.effects[PBEffects::PerishSong] -= 1 - pbDisplay(_INTL("{1}'s perish count fell to {2}!",b.pbThis,b.effects[PBEffects::PerishSong])) - if b.effects[PBEffects::PerishSong]==0 - perishSongUsers.push(b.effects[PBEffects::PerishSongUser]) - b.pbReduceHP(b.hp) - end - b.pbItemHPHealCheck - b.pbFaint if b.fainted? - end - if perishSongUsers.length>0 - # If all remaining Pokemon fainted by a Perish Song triggered by a single side - if (perishSongUsers.find_all { |idxBattler| opposes?(idxBattler) }.length==perishSongUsers.length) || - (perishSongUsers.find_all { |idxBattler| !opposes?(idxBattler) }.length==perishSongUsers.length) - pbJudgeCheckpoint(@battlers[perishSongUsers[0]]) - end - end - # Check for end of battle - if @decision>0 - pbGainExp - return - end - for side in 0...2 - # Reflect - pbEORCountDownSideEffect(side,PBEffects::Reflect, - _INTL("{1}'s Reflect wore off!",@battlers[side].pbTeam)) - # Light Screen - pbEORCountDownSideEffect(side,PBEffects::LightScreen, - _INTL("{1}'s Light Screen wore off!",@battlers[side].pbTeam)) - # Safeguard - pbEORCountDownSideEffect(side,PBEffects::Safeguard, - _INTL("{1} is no longer protected by Safeguard!",@battlers[side].pbTeam)) - # Mist - pbEORCountDownSideEffect(side,PBEffects::Mist, - _INTL("{1} is no longer protected by mist!",@battlers[side].pbTeam)) - # Tailwind - pbEORCountDownSideEffect(side,PBEffects::Tailwind, - _INTL("{1}'s Tailwind petered out!",@battlers[side].pbTeam)) - # Lucky Chant - pbEORCountDownSideEffect(side,PBEffects::LuckyChant, - _INTL("{1}'s Lucky Chant wore off!",@battlers[side].pbTeam)) - # Pledge Rainbow - pbEORCountDownSideEffect(side,PBEffects::Rainbow, - _INTL("The rainbow on {1}'s side disappeared!",@battlers[side].pbTeam(true))) - # Pledge Sea of Fire - pbEORCountDownSideEffect(side,PBEffects::SeaOfFire, - _INTL("The sea of fire around {1} disappeared!",@battlers[side].pbTeam(true))) - # Pledge Swamp - pbEORCountDownSideEffect(side,PBEffects::Swamp, - _INTL("The swamp around {1} disappeared!",@battlers[side].pbTeam(true))) - # Aurora Veil - pbEORCountDownSideEffect(side,PBEffects::AuroraVeil, - _INTL("{1}'s Aurora Veil wore off!",@battlers[side].pbTeam(true))) - end - # Trick Room - pbEORCountDownFieldEffect(PBEffects::TrickRoom, - _INTL("The twisted dimensions returned to normal!")) - # Gravity - pbEORCountDownFieldEffect(PBEffects::Gravity, - _INTL("Gravity returned to normal!")) - # Water Sport - pbEORCountDownFieldEffect(PBEffects::WaterSportField, - _INTL("The effects of Water Sport have faded.")) - # Mud Sport - pbEORCountDownFieldEffect(PBEffects::MudSportField, - _INTL("The effects of Mud Sport have faded.")) - # Wonder Room - pbEORCountDownFieldEffect(PBEffects::WonderRoom, - _INTL("Wonder Room wore off, and Defense and Sp. Def stats returned to normal!")) - # Magic Room - pbEORCountDownFieldEffect(PBEffects::MagicRoom, - _INTL("Magic Room wore off, and held items' effects returned to normal!")) - # End of terrains - pbEORTerrain - priority.each do |b| - next if b.fainted? - # Hyper Mode (Shadow Pokémon) - if b.inHyperMode? - if pbRandom(100)<10 - b.pokemon.hyper_mode = false - b.pokemon.adjustHeart(-50) - pbDisplay(_INTL("{1} came to its senses!",b.pbThis)) - else - pbDisplay(_INTL("{1} is in Hyper Mode!",b.pbThis)) - end - end - # Uproar - if b.effects[PBEffects::Uproar]>0 - b.effects[PBEffects::Uproar] -= 1 - if b.effects[PBEffects::Uproar]==0 - pbDisplay(_INTL("{1} calmed down.",b.pbThis)) - else - pbDisplay(_INTL("{1} is making an uproar!",b.pbThis)) - end - end - # Slow Start's end message - if b.effects[PBEffects::SlowStart]>0 - b.effects[PBEffects::SlowStart] -= 1 - if b.effects[PBEffects::SlowStart]==0 - pbDisplay(_INTL("{1} finally got its act together!",b.pbThis)) - end - end - # Bad Dreams, Moody, Speed Boost - BattleHandlers.triggerEOREffectAbility(b.ability,b,self) if b.abilityActive? - # Flame Orb, Sticky Barb, Toxic Orb - BattleHandlers.triggerEOREffectItem(b.item,b,self) if b.itemActive? - # Harvest, Pickup - BattleHandlers.triggerEORGainItemAbility(b.ability,b,self) if b.abilityActive? - end - pbGainExp - return if @decision>0 - # Form checks - priority.each { |b| b.pbCheckForm(true) } - # Switch Pokémon in if possible - pbEORSwitch - return if @decision>0 - # In battles with at least one side of size 3+, move battlers around if none - # are near to any foes - pbEORShiftDistantBattlers - # Try to make Trace work, check for end of primordial weather - priority.each { |b| b.pbContinualAbilityChecks } - # Reset/count down battler-specific effects (no messages) - eachBattler do |b| - b.effects[PBEffects::BanefulBunker] = false - b.effects[PBEffects::Charge] -= 1 if b.effects[PBEffects::Charge]>0 - b.effects[PBEffects::Counter] = -1 - b.effects[PBEffects::CounterTarget] = -1 - b.effects[PBEffects::Electrify] = false - b.effects[PBEffects::Endure] = false - b.effects[PBEffects::FirstPledge] = 0 - b.effects[PBEffects::Flinch] = false - b.effects[PBEffects::FocusPunch] = false - b.effects[PBEffects::FollowMe] = 0 - b.effects[PBEffects::HelpingHand] = false - b.effects[PBEffects::HyperBeam] -= 1 if b.effects[PBEffects::HyperBeam]>0 - b.effects[PBEffects::KingsShield] = false - b.effects[PBEffects::LaserFocus] -= 1 if b.effects[PBEffects::LaserFocus]>0 - if b.effects[PBEffects::LockOn]>0 # Also Mind Reader - b.effects[PBEffects::LockOn] -= 1 - b.effects[PBEffects::LockOnPos] = -1 if b.effects[PBEffects::LockOn]==0 - end - b.effects[PBEffects::MagicBounce] = false - b.effects[PBEffects::MagicCoat] = false - b.effects[PBEffects::MirrorCoat] = -1 - b.effects[PBEffects::MirrorCoatTarget] = -1 - b.effects[PBEffects::Powder] = false - b.effects[PBEffects::Prankster] = false - b.effects[PBEffects::PriorityAbility] = false - b.effects[PBEffects::PriorityItem] = false - b.effects[PBEffects::Protect] = false - b.effects[PBEffects::RagePowder] = false - b.effects[PBEffects::Roost] = false - b.effects[PBEffects::Snatch] = 0 - b.effects[PBEffects::SpikyShield] = false - b.effects[PBEffects::Spotlight] = 0 - b.effects[PBEffects::ThroatChop] -= 1 if b.effects[PBEffects::ThroatChop]>0 - b.lastHPLost = 0 - b.lastHPLostFromFoe = 0 - b.tookDamage = false - b.tookPhysicalHit = false - b.lastRoundMoveFailed = b.lastMoveFailed - b.lastAttacker.clear - b.lastFoeAttacker.clear - end - # Reset/count down side-specific effects (no messages) - for side in 0...2 - @sides[side].effects[PBEffects::CraftyShield] = false - if !@sides[side].effects[PBEffects::EchoedVoiceUsed] - @sides[side].effects[PBEffects::EchoedVoiceCounter] = 0 - end - @sides[side].effects[PBEffects::EchoedVoiceUsed] = false - @sides[side].effects[PBEffects::MatBlock] = false - @sides[side].effects[PBEffects::QuickGuard] = false - @sides[side].effects[PBEffects::Round] = false - @sides[side].effects[PBEffects::WideGuard] = false - end - # Reset/count down field-specific effects (no messages) - @field.effects[PBEffects::IonDeluge] = false - @field.effects[PBEffects::FairyLock] -= 1 if @field.effects[PBEffects::FairyLock]>0 - @field.effects[PBEffects::FusionBolt] = false - @field.effects[PBEffects::FusionFlare] = false - @endOfRound = false - end -end diff --git a/Data/Scripts/011_Battle/003_BattleHandlers_Abilities.rb b/Data/Scripts/011_Battle/003_BattleHandlers_Abilities.rb deleted file mode 100644 index 802929fb1b..0000000000 --- a/Data/Scripts/011_Battle/003_BattleHandlers_Abilities.rb +++ /dev/null @@ -1,2425 +0,0 @@ -#=============================================================================== -# SpeedCalcAbility handlers -#=============================================================================== - -BattleHandlers::SpeedCalcAbility.add(:CHLOROPHYLL, - proc { |ability,battler,mult| - next mult * 2 if [:Sun, :HarshSun].include?(battler.battle.pbWeather) - } -) - -BattleHandlers::SpeedCalcAbility.add(:QUICKFEET, - proc { |ability,battler,mult| - next mult*1.5 if battler.pbHasAnyStatus? - } -) - -BattleHandlers::SpeedCalcAbility.add(:SANDRUSH, - proc { |ability,battler,mult| - next mult * 2 if [:Sandstorm].include?(battler.battle.pbWeather) - } -) - -BattleHandlers::SpeedCalcAbility.add(:SLOWSTART, - proc { |ability,battler,mult| - next mult/2 if battler.effects[PBEffects::SlowStart]>0 - } -) - -BattleHandlers::SpeedCalcAbility.add(:SLUSHRUSH, - proc { |ability,battler,mult| - next mult * 2 if [:Hail].include?(battler.battle.pbWeather) - } -) - -BattleHandlers::SpeedCalcAbility.add(:SURGESURFER, - proc { |ability,battler,mult| - next mult*2 if battler.battle.field.terrain == :Electric - } -) - -BattleHandlers::SpeedCalcAbility.add(:SWIFTSWIM, - proc { |ability,battler,mult| - next mult * 2 if [:Rain, :HeavyRain].include?(battler.battle.pbWeather) - } -) - -BattleHandlers::SpeedCalcAbility.add(:UNBURDEN, - proc { |ability,battler,mult| - next mult*2 if battler.effects[PBEffects::Unburden] && !battler.item - } -) - -#=============================================================================== -# WeightCalcAbility handlers -#=============================================================================== - -BattleHandlers::WeightCalcAbility.add(:HEAVYMETAL, - proc { |ability,battler,w| - next w*2 - } -) - -BattleHandlers::WeightCalcAbility.add(:LIGHTMETAL, - proc { |ability,battler,w| - next [w/2,1].max - } -) - -#=============================================================================== -# AbilityOnHPDroppedBelowHalf handlers -#=============================================================================== - -BattleHandlers::AbilityOnHPDroppedBelowHalf.add(:EMERGENCYEXIT, - proc { |ability,battler,battle| - next false if battler.effects[PBEffects::SkyDrop]>=0 || battler.inTwoTurnAttack?("0CE") # Sky Drop - # In wild battles - if battle.wildBattle? - next false if battler.opposes? && battle.pbSideBattlerCount(battler.index)>1 - next false if !battle.pbCanRun?(battler.index) - battle.pbShowAbilitySplash(battler,true) - battle.pbHideAbilitySplash(battler) - pbSEPlay("Battle flee") - battle.pbDisplay(_INTL("{1} fled from battle!",battler.pbThis)) - battle.decision = 3 # Escaped - next true - end - # In trainer battles - next false if battle.pbAllFainted?(battler.idxOpposingSide) - next false if !battle.pbCanSwitch?(battler.index) # Battler can't switch out - next false if !battle.pbCanChooseNonActive?(battler.index) # No Pokémon can switch in - battle.pbShowAbilitySplash(battler,true) - battle.pbHideAbilitySplash(battler) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s {2} activated!",battler.pbThis,battler.abilityName)) - end - battle.pbDisplay(_INTL("{1} went back to {2}!", - battler.pbThis,battle.pbGetOwnerName(battler.index))) - if battle.endOfRound # Just switch out - battle.scene.pbRecall(battler.index) if !battler.fainted? - battler.pbAbilitiesOnSwitchOut # Inc. primordial weather check - next true - end - newPkmn = battle.pbGetReplacementPokemonIndex(battler.index) # Owner chooses - next false if newPkmn<0 # Shouldn't ever do this - battle.pbRecallAndReplace(battler.index,newPkmn) - battle.pbClearChoice(battler.index) # Replacement Pokémon does nothing this round - next true - } -) - -BattleHandlers::AbilityOnHPDroppedBelowHalf.copy(:EMERGENCYEXIT,:WIMPOUT) - -#=============================================================================== -# StatusCheckAbilityNonIgnorable handlers -#=============================================================================== - -BattleHandlers::StatusCheckAbilityNonIgnorable.add(:COMATOSE, - proc { |ability,battler,status| - next false if !battler.isSpecies?(:KOMALA) - next true if status.nil? || status == :SLEEP - } -) - -#=============================================================================== -# StatusImmunityAbility handlers -#=============================================================================== - -BattleHandlers::StatusImmunityAbility.add(:FLOWERVEIL, - proc { |ability,battler,status| - next true if battler.pbHasType?(:GRASS) - } -) - -BattleHandlers::StatusImmunityAbility.add(:IMMUNITY, - proc { |ability,battler,status| - next true if status == :POISON - } -) - -BattleHandlers::StatusImmunityAbility.add(:INSOMNIA, - proc { |ability,battler,status| - next true if status == :SLEEP - } -) - -BattleHandlers::StatusImmunityAbility.copy(:INSOMNIA,:SWEETVEIL,:VITALSPIRIT) - -BattleHandlers::StatusImmunityAbility.add(:LEAFGUARD, - proc { |ability,battler,status| - next true if [:Sun, :HarshSun].include?(battler.battle.pbWeather) - } -) - -BattleHandlers::StatusImmunityAbility.add(:LIMBER, - proc { |ability,battler,status| - next true if status == :PARALYSIS - } -) - -BattleHandlers::StatusImmunityAbility.add(:MAGMAARMOR, - proc { |ability,battler,status| - next true if status == :FROZEN - } -) - -BattleHandlers::StatusImmunityAbility.add(:WATERVEIL, - proc { |ability,battler,status| - next true if status == :BURN - } -) - -BattleHandlers::StatusImmunityAbility.copy(:WATERVEIL,:WATERBUBBLE) - -#=============================================================================== -# StatusImmunityAbilityNonIgnorable handlers -#=============================================================================== - -BattleHandlers::StatusImmunityAbilityNonIgnorable.add(:COMATOSE, - proc { |ability,battler,status| - next true if battler.isSpecies?(:KOMALA) - } -) - -BattleHandlers::StatusImmunityAbilityNonIgnorable.add(:SHIELDSDOWN, - proc { |ability,battler,status| - next true if battler.isSpecies?(:MINIOR) && battler.form<7 - } -) - -#=============================================================================== -# StatusImmunityAllyAbility handlers -#=============================================================================== - -BattleHandlers::StatusImmunityAllyAbility.add(:FLOWERVEIL, - proc { |ability,battler,status| - next true if battler.pbHasType?(:GRASS) - } -) - -BattleHandlers::StatusImmunityAbility.add(:SWEETVEIL, - proc { |ability,battler,status| - next true if status == :SLEEP - } -) - -#=============================================================================== -# AbilityOnStatusInflicted handlers -#=============================================================================== - -BattleHandlers::AbilityOnStatusInflicted.add(:SYNCHRONIZE, - proc { |ability,battler,user,status| - next if !user || user.index==battler.index - case status - when :POISON - if user.pbCanPoisonSynchronize?(battler) - battler.battle.pbShowAbilitySplash(battler) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} poisoned {3}!",battler.pbThis,battler.abilityName,user.pbThis(true)) - end - user.pbPoison(nil,msg,(battler.statusCount>0)) - battler.battle.pbHideAbilitySplash(battler) - end - when :BURN - if user.pbCanBurnSynchronize?(battler) - battler.battle.pbShowAbilitySplash(battler) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} burned {3}!",battler.pbThis,battler.abilityName,user.pbThis(true)) - end - user.pbBurn(nil,msg) - battler.battle.pbHideAbilitySplash(battler) - end - when :PARALYSIS - if user.pbCanParalyzeSynchronize?(battler) - battler.battle.pbShowAbilitySplash(battler) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} paralyzed {3}! It may be unable to move!", - battler.pbThis,battler.abilityName,user.pbThis(true)) - end - user.pbParalyze(nil,msg) - battler.battle.pbHideAbilitySplash(battler) - end - end - } -) - -#=============================================================================== -# StatusCureAbility handlers -#=============================================================================== - -BattleHandlers::StatusCureAbility.add(:IMMUNITY, - proc { |ability,battler| - next if battler.status != :POISON - battler.battle.pbShowAbilitySplash(battler) - battler.pbCureStatus(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battler.battle.pbDisplay(_INTL("{1}'s {2} cured its poisoning!",battler.pbThis,battler.abilityName)) - end - battler.battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::StatusCureAbility.add(:INSOMNIA, - proc { |ability,battler| - next if battler.status != :SLEEP - battler.battle.pbShowAbilitySplash(battler) - battler.pbCureStatus(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battler.battle.pbDisplay(_INTL("{1}'s {2} woke it up!",battler.pbThis,battler.abilityName)) - end - battler.battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::StatusCureAbility.copy(:INSOMNIA,:VITALSPIRIT) - -BattleHandlers::StatusCureAbility.add(:LIMBER, - proc { |ability,battler| - next if battler.status != :PARALYSIS - battler.battle.pbShowAbilitySplash(battler) - battler.pbCureStatus(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battler.battle.pbDisplay(_INTL("{1}'s {2} cured its paralysis!",battler.pbThis,battler.abilityName)) - end - battler.battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::StatusCureAbility.add(:MAGMAARMOR, - proc { |ability,battler| - next if battler.status != :FROZEN - battler.battle.pbShowAbilitySplash(battler) - battler.pbCureStatus(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battler.battle.pbDisplay(_INTL("{1}'s {2} defrosted it!",battler.pbThis,battler.abilityName)) - end - battler.battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::StatusCureAbility.add(:OBLIVIOUS, - proc { |ability,battler| - next if battler.effects[PBEffects::Attract]<0 && - (battler.effects[PBEffects::Taunt]==0 || Settings::MECHANICS_GENERATION <= 5) - battler.battle.pbShowAbilitySplash(battler) - if battler.effects[PBEffects::Attract]>=0 - battler.pbCureAttract - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battler.battle.pbDisplay(_INTL("{1} got over its infatuation.",battler.pbThis)) - else - battler.battle.pbDisplay(_INTL("{1}'s {2} cured its infatuation status!", - battler.pbThis,battler.abilityName)) - end - end - if battler.effects[PBEffects::Taunt]>0 && Settings::MECHANICS_GENERATION >= 6 - battler.effects[PBEffects::Taunt] = 0 - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battler.battle.pbDisplay(_INTL("{1}'s Taunt wore off!",battler.pbThis)) - else - battler.battle.pbDisplay(_INTL("{1}'s {2} made its taunt wear off!", - battler.pbThis,battler.abilityName)) - end - end - battler.battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::StatusCureAbility.add(:OWNTEMPO, - proc { |ability,battler| - next if battler.effects[PBEffects::Confusion]==0 - battler.battle.pbShowAbilitySplash(battler) - battler.pbCureConfusion - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battler.battle.pbDisplay(_INTL("{1} snapped out of its confusion.",battler.pbThis)) - else - battler.battle.pbDisplay(_INTL("{1}'s {2} snapped it out of its confusion!", - battler.pbThis,battler.abilityName)) - end - battler.battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::StatusCureAbility.add(:WATERVEIL, - proc { |ability,battler| - next if battler.status != :BURN - battler.battle.pbShowAbilitySplash(battler) - battler.pbCureStatus(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battler.battle.pbDisplay(_INTL("{1}'s {2} healed its burn!",battler.pbThis,battler.abilityName)) - end - battler.battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::StatusCureAbility.copy(:WATERVEIL,:WATERBUBBLE) - -#=============================================================================== -# StatLossImmunityAbility handlers -#=============================================================================== - -BattleHandlers::StatLossImmunityAbility.add(:BIGPECKS, - proc { |ability,battler,stat,battle,showMessages| - next false if stat!=:DEFENSE - if showMessages - battle.pbShowAbilitySplash(battler) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s {2} cannot be lowered!",battler.pbThis,GameData::Stat.get(stat).name)) - else - battle.pbDisplay(_INTL("{1}'s {2} prevents {3} loss!",battler.pbThis, - battler.abilityName,GameData::Stat.get(stat).name)) - end - battle.pbHideAbilitySplash(battler) - end - next true - } -) - -BattleHandlers::StatLossImmunityAbility.add(:CLEARBODY, - proc { |ability,battler,stat,battle,showMessages| - if showMessages - battle.pbShowAbilitySplash(battler) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s stats cannot be lowered!",battler.pbThis)) - else - battle.pbDisplay(_INTL("{1}'s {2} prevents stat loss!",battler.pbThis,battler.abilityName)) - end - battle.pbHideAbilitySplash(battler) - end - next true - } -) - -BattleHandlers::StatLossImmunityAbility.copy(:CLEARBODY,:WHITESMOKE) - -BattleHandlers::StatLossImmunityAbility.add(:FLOWERVEIL, - proc { |ability,battler,stat,battle,showMessages| - next false if !battler.pbHasType?(:GRASS) - if showMessages - battle.pbShowAbilitySplash(battler) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s stats cannot be lowered!",battler.pbThis)) - else - battle.pbDisplay(_INTL("{1}'s {2} prevents stat loss!",battler.pbThis,battler.abilityName)) - end - battle.pbHideAbilitySplash(battler) - end - next true - } -) - -BattleHandlers::StatLossImmunityAbility.add(:HYPERCUTTER, - proc { |ability,battler,stat,battle,showMessages| - next false if stat!=:ATTACK - if showMessages - battle.pbShowAbilitySplash(battler) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s {2} cannot be lowered!",battler.pbThis,GameData::Stat.get(stat).name)) - else - battle.pbDisplay(_INTL("{1}'s {2} prevents {3} loss!",battler.pbThis, - battler.abilityName,GameData::Stat.get(stat).name)) - end - battle.pbHideAbilitySplash(battler) - end - next true - } -) - -BattleHandlers::StatLossImmunityAbility.add(:KEENEYE, - proc { |ability,battler,stat,battle,showMessages| - next false if stat!=:ACCURACY - if showMessages - battle.pbShowAbilitySplash(battler) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s {2} cannot be lowered!",battler.pbThis,GameData::Stat.get(stat).name)) - else - battle.pbDisplay(_INTL("{1}'s {2} prevents {3} loss!",battler.pbThis, - battler.abilityName,GameData::Stat.get(stat).name)) - end - battle.pbHideAbilitySplash(battler) - end - next true - } -) - -#=============================================================================== -# StatLossImmunityAbilityNonIgnorable handlers -#=============================================================================== - -BattleHandlers::StatLossImmunityAbilityNonIgnorable.add(:FULLMETALBODY, - proc { |ability,battler,stat,battle,showMessages| - if showMessages - battle.pbShowAbilitySplash(battler) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s stats cannot be lowered!",battler.pbThis)) - else - battle.pbDisplay(_INTL("{1}'s {2} prevents stat loss!",battler.pbThis,battler.abilityName)) - end - battle.pbHideAbilitySplash(battler) - end - next true - } -) - -#=============================================================================== -# StatLossImmunityAllyAbility handlers -#=============================================================================== - -BattleHandlers::StatLossImmunityAllyAbility.add(:FLOWERVEIL, - proc { |ability,bearer,battler,stat,battle,showMessages| - next false if !battler.pbHasType?(:GRASS) - if showMessages - battle.pbShowAbilitySplash(bearer) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s stats cannot be lowered!",battler.pbThis)) - else - battle.pbDisplay(_INTL("{1}'s {2} prevents {3}'s stat loss!", - bearer.pbThis,bearer.abilityName,battler.pbThis(true))) - end - battle.pbHideAbilitySplash(bearer) - end - next true - } -) - -#=============================================================================== -# AbilityOnStatGain handlers -#=============================================================================== - -# There aren't any! - -#=============================================================================== -# AbilityOnStatLoss handlers -#=============================================================================== - -BattleHandlers::AbilityOnStatLoss.add(:COMPETITIVE, - proc { |ability,battler,stat,user| - next if user && !user.opposes?(battler) - battler.pbRaiseStatStageByAbility(:SPECIAL_ATTACK,2,battler) - } -) - -BattleHandlers::AbilityOnStatLoss.add(:DEFIANT, - proc { |ability,battler,stat,user| - next if user && !user.opposes?(battler) - battler.pbRaiseStatStageByAbility(:ATTACK,2,battler) - } -) - -#=============================================================================== -# PriorityChangeAbility handlers -#=============================================================================== - -BattleHandlers::PriorityChangeAbility.add(:GALEWINGS, - proc { |ability,battler,move,pri| - next pri+1 if battler.hp==battler.totalhp && move.type == :FLYING - } -) - -BattleHandlers::PriorityChangeAbility.add(:PRANKSTER, - proc { |ability,battler,move,pri| - if move.statusMove? - battler.effects[PBEffects::Prankster] = true - next pri+1 - end - } -) - -BattleHandlers::PriorityChangeAbility.add(:TRIAGE, - proc { |ability,battler,move,pri| - next pri+3 if move.healingMove? - } -) - -#=============================================================================== -# PriorityBracketChangeAbility handlers -#=============================================================================== - -BattleHandlers::PriorityBracketChangeAbility.add(:STALL, - proc { |ability,battler,subPri,battle| - next -1 if subPri==0 - } -) - -#=============================================================================== -# PriorityBracketUseAbility handlers -#=============================================================================== - -# There aren't any! - -#=============================================================================== -# AbilityOnFlinch handlers -#=============================================================================== - -BattleHandlers::AbilityOnFlinch.add(:STEADFAST, - proc { |ability,battler,battle| - battler.pbRaiseStatStageByAbility(:SPEED,1,battler) - } -) - -#=============================================================================== -# MoveBlockingAbility handlers -#=============================================================================== - -BattleHandlers::MoveBlockingAbility.add(:DAZZLING, - proc { |ability,bearer,user,targets,move,battle| - next false if battle.choices[user.index][4]<=0 - next false if !bearer.opposes?(user) - ret = false - targets.each do |b| - next if !b.opposes?(user) - ret = true - end - next ret - } -) - -BattleHandlers::MoveBlockingAbility.copy(:DAZZLING,:QUEENLYMAJESTY) - -#=============================================================================== -# MoveImmunityTargetAbility handlers -#=============================================================================== - -BattleHandlers::MoveImmunityTargetAbility.add(:BULLETPROOF, - proc { |ability,user,target,move,type,battle| - next false if !move.bombMove? - battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - else - battle.pbDisplay(_INTL("{1}'s {2} made {3} ineffective!", - target.pbThis,target.abilityName,move.name)) - end - battle.pbHideAbilitySplash(target) - next true - } -) - -BattleHandlers::MoveImmunityTargetAbility.add(:FLASHFIRE, - proc { |ability,user,target,move,type,battle| - next false if user.index==target.index - next false if type != :FIRE - battle.pbShowAbilitySplash(target) - if !target.effects[PBEffects::FlashFire] - target.effects[PBEffects::FlashFire] = true - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("The power of {1}'s Fire-type moves rose!",target.pbThis(true))) - else - battle.pbDisplay(_INTL("The power of {1}'s Fire-type moves rose because of its {2}!", - target.pbThis(true),target.abilityName)) - end - else - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - else - battle.pbDisplay(_INTL("{1}'s {2} made {3} ineffective!", - target.pbThis,target.abilityName,move.name)) - end - end - battle.pbHideAbilitySplash(target) - next true - } -) - -BattleHandlers::MoveImmunityTargetAbility.add(:LIGHTNINGROD, - proc { |ability,user,target,move,type,battle| - next pbBattleMoveImmunityStatAbility(user,target,move,type,:ELECTRIC,:SPECIAL_ATTACK,1,battle) - } -) - -BattleHandlers::MoveImmunityTargetAbility.add(:MOTORDRIVE, - proc { |ability,user,target,move,type,battle| - next pbBattleMoveImmunityStatAbility(user,target,move,type,:ELECTRIC,:SPEED,1,battle) - } -) - -BattleHandlers::MoveImmunityTargetAbility.add(:SAPSIPPER, - proc { |ability,user,target,move,type,battle| - next pbBattleMoveImmunityStatAbility(user,target,move,type,:GRASS,:ATTACK,1,battle) - } -) - -BattleHandlers::MoveImmunityTargetAbility.add(:SOUNDPROOF, - proc { |ability,user,target,move,type,battle| - next false if !move.soundMove? - battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - else - battle.pbDisplay(_INTL("{1}'s {2} blocks {3}!",target.pbThis,target.abilityName,move.name)) - end - battle.pbHideAbilitySplash(target) - next true - - } -) - -BattleHandlers::MoveImmunityTargetAbility.add(:STORMDRAIN, - proc { |ability,user,target,move,type,battle| - next pbBattleMoveImmunityStatAbility(user,target,move,type,:WATER,:SPECIAL_ATTACK,1,battle) - } -) - -BattleHandlers::MoveImmunityTargetAbility.add(:TELEPATHY, - proc { |ability,user,target,move,type,battle| - next false if move.statusMove? - next false if user.index==target.index || target.opposes?(user) - battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1} avoids attacks by its ally Pokémon!",target.pbThis(true))) - else - battle.pbDisplay(_INTL("{1} avoids attacks by its ally Pokémon with {2}!", - target.pbThis,target.abilityName)) - end - battle.pbHideAbilitySplash(target) - next true - } -) - -BattleHandlers::MoveImmunityTargetAbility.add(:VOLTABSORB, - proc { |ability,user,target,move,type,battle| - next pbBattleMoveImmunityHealAbility(user,target,move,type,:ELECTRIC,battle) - } -) - -BattleHandlers::MoveImmunityTargetAbility.add(:WATERABSORB, - proc { |ability,user,target,move,type,battle| - next pbBattleMoveImmunityHealAbility(user,target,move,type,:WATER,battle) - } -) - -BattleHandlers::MoveImmunityTargetAbility.copy(:WATERABSORB,:DRYSKIN) - -BattleHandlers::MoveImmunityTargetAbility.add(:WONDERGUARD, - proc { |ability,user,target,move,type,battle| - next false if move.statusMove? - next false if !type || Effectiveness.super_effective?(target.damageState.typeMod) - battle.pbShowAbilitySplash(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true))) - else - battle.pbDisplay(_INTL("{1} avoided damage with {2}!",target.pbThis,target.abilityName)) - end - battle.pbHideAbilitySplash(target) - next true - } -) - -#=============================================================================== -# MoveBaseTypeModifierAbility handlers -#=============================================================================== - -BattleHandlers::MoveBaseTypeModifierAbility.add(:AERILATE, - proc { |ability,user,move,type| - next if type != :NORMAL || !GameData::Type.exists?(:FLYING) - move.powerBoost = true - next :FLYING - } -) - -BattleHandlers::MoveBaseTypeModifierAbility.add(:GALVANIZE, - proc { |ability,user,move,type| - next if type != :NORMAL || !GameData::Type.exists?(:ELECTRIC) - move.powerBoost = true - next :ELECTRIC - } -) - -BattleHandlers::MoveBaseTypeModifierAbility.add(:LIQUIDVOICE, - proc { |ability,user,move,type| - next :WATER if GameData::Type.exists?(:WATER) && move.soundMove? - } -) - -BattleHandlers::MoveBaseTypeModifierAbility.add(:NORMALIZE, - proc { |ability,user,move,type| - next if !GameData::Type.exists?(:NORMAL) - move.powerBoost = true if Settings::MECHANICS_GENERATION >= 7 - next :NORMAL - } -) - -BattleHandlers::MoveBaseTypeModifierAbility.add(:PIXILATE, - proc { |ability,user,move,type| - next if type != :NORMAL || !GameData::Type.exists?(:FAIRY) - move.powerBoost = true - next :FAIRY - } -) - -BattleHandlers::MoveBaseTypeModifierAbility.add(:REFRIGERATE, - proc { |ability,user,move,type| - next if type != :NORMAL || !GameData::Type.exists?(:ICE) - move.powerBoost = true - next :ICE - } -) - -#=============================================================================== -# AccuracyCalcUserAbility handlers -#=============================================================================== - -BattleHandlers::AccuracyCalcUserAbility.add(:COMPOUNDEYES, - proc { |ability,mods,user,target,move,type| - mods[:accuracy_multiplier] *= 1.3 - } -) - -BattleHandlers::AccuracyCalcUserAbility.add(:HUSTLE, - proc { |ability,mods,user,target,move,type| - mods[:accuracy_multiplier] *= 0.8 if move.physicalMove? - } -) - -BattleHandlers::AccuracyCalcUserAbility.add(:KEENEYE, - proc { |ability,mods,user,target,move,type| - mods[:evasion_stage] = 0 if mods[:evasion_stage] > 0 && Settings::MECHANICS_GENERATION >= 6 - } -) - -BattleHandlers::AccuracyCalcUserAbility.add(:NOGUARD, - proc { |ability,mods,user,target,move,type| - mods[:base_accuracy] = 0 - } -) - -BattleHandlers::AccuracyCalcUserAbility.add(:UNAWARE, - proc { |ability,mods,user,target,move,type| - mods[:evasion_stage] = 0 if move.damagingMove? - } -) - -BattleHandlers::AccuracyCalcUserAbility.add(:VICTORYSTAR, - proc { |ability,mods,user,target,move,type| - mods[:accuracy_multiplier] *= 1.1 - } -) - -#=============================================================================== -# AccuracyCalcUserAllyAbility handlers -#=============================================================================== - -BattleHandlers::AccuracyCalcUserAllyAbility.add(:VICTORYSTAR, - proc { |ability,mods,user,target,move,type| - mods[:accuracy_multiplier] *= 1.1 - } -) - -#=============================================================================== -# AccuracyCalcTargetAbility handlers -#=============================================================================== - -BattleHandlers::AccuracyCalcTargetAbility.add(:LIGHTNINGROD, - proc { |ability,mods,user,target,move,type| - mods[:base_accuracy] = 0 if type == :ELECTRIC - } -) - -BattleHandlers::AccuracyCalcTargetAbility.add(:NOGUARD, - proc { |ability,mods,user,target,move,type| - mods[:base_accuracy] = 0 - } -) - -BattleHandlers::AccuracyCalcTargetAbility.add(:SANDVEIL, - proc { |ability,mods,user,target,move,type| - mods[:evasion_multiplier] *= 1.25 if target.battle.pbWeather == :Sandstorm - } -) - -BattleHandlers::AccuracyCalcTargetAbility.add(:SNOWCLOAK, - proc { |ability,mods,user,target,move,type| - mods[:evasion_multiplier] *= 1.25 if target.battle.pbWeather == :Hail - } -) - -BattleHandlers::AccuracyCalcTargetAbility.add(:STORMDRAIN, - proc { |ability,mods,user,target,move,type| - mods[:base_accuracy] = 0 if type == :WATER - } -) - -BattleHandlers::AccuracyCalcTargetAbility.add(:TANGLEDFEET, - proc { |ability,mods,user,target,move,type| - mods[:accuracy_multiplier] /= 2 if target.effects[PBEffects::Confusion] > 0 - } -) - -BattleHandlers::AccuracyCalcTargetAbility.add(:UNAWARE, - proc { |ability,mods,user,target,move,type| - mods[:accuracy_stage] = 0 if move.damagingMove? - } -) - -BattleHandlers::AccuracyCalcTargetAbility.add(:WONDERSKIN, - proc { |ability,mods,user,target,move,type| - if move.statusMove? && user.opposes?(target) - mods[:base_accuracy] = 50 if mods[:base_accuracy] > 50 - end - } -) - -#=============================================================================== -# DamageCalcUserAbility handlers -#=============================================================================== - -BattleHandlers::DamageCalcUserAbility.add(:AERILATE, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] *= 1.2 if move.powerBoost - } -) - -BattleHandlers::DamageCalcUserAbility.copy(:AERILATE, :PIXILATE, :REFRIGERATE, :GALVANIZE, :NORMALIZE) - -BattleHandlers::DamageCalcUserAbility.add(:ANALYTIC, - proc { |ability,user,target,move,mults,baseDmg,type| - if (target.battle.choices[target.index][0]!=:UseMove && - target.battle.choices[target.index][0]!=:Shift) || - target.movedThisRound? - mults[:base_damage_multiplier] *= 1.3 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:BLAZE, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.hp <= user.totalhp / 3 && type == :FIRE - mults[:attack_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:DEFEATIST, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:attack_multiplier] /= 2 if user.hp <= user.totalhp / 2 - } -) - -BattleHandlers::DamageCalcUserAbility.add(:FLAREBOOST, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.burned? && move.specialMove? - mults[:base_damage_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:FLASHFIRE, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.effects[PBEffects::FlashFire] && type == :FIRE - mults[:attack_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:FLOWERGIFT, - proc { |ability,user,target,move,mults,baseDmg,type| - if move.physicalMove? && [:Sun, :HarshSun].include?(user.battle.pbWeather) - mults[:attack_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:GUTS, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.pbHasAnyStatus? && move.physicalMove? - mults[:attack_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:HUGEPOWER, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:attack_multiplier] *= 2 if move.physicalMove? - } -) - -BattleHandlers::DamageCalcUserAbility.copy(:HUGEPOWER,:PUREPOWER) - -BattleHandlers::DamageCalcUserAbility.add(:HUSTLE, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:attack_multiplier] *= 1.5 if move.physicalMove? - } -) - -BattleHandlers::DamageCalcUserAbility.add(:IRONFIST, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] *= 1.2 if move.punchingMove? - } -) - -BattleHandlers::DamageCalcUserAbility.add(:MEGALAUNCHER, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] *= 1.5 if move.pulseMove? - } -) - -BattleHandlers::DamageCalcUserAbility.add(:MINUS, - proc { |ability,user,target,move,mults,baseDmg,type| - next if !move.specialMove? - user.eachAlly do |b| - next if !b.hasActiveAbility?([:MINUS, :PLUS]) - mults[:attack_multiplier] *= 1.5 - break - end - } -) - -BattleHandlers::DamageCalcUserAbility.copy(:MINUS,:PLUS) - -BattleHandlers::DamageCalcUserAbility.add(:NEUROFORCE, - proc { |ability,user,target,move,mults,baseDmg,type| - if Effectiveness.super_effective?(target.damageState.typeMod) - mults[:final_damage_multiplier] *= 1.25 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:OVERGROW, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.hp <= user.totalhp / 3 && type == :GRASS - mults[:attack_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:RECKLESS, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] *= 1.2 if move.recoilMove? - } -) - -BattleHandlers::DamageCalcUserAbility.add(:RIVALRY, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.gender!=2 && target.gender!=2 - if user.gender==target.gender - mults[:base_damage_multiplier] *= 1.25 - else - mults[:base_damage_multiplier] *= 0.75 - end - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:SANDFORCE, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.battle.pbWeather == :Sandstorm && - [:ROCK, :GROUND, :STEEL].include?(type) - mults[:base_damage_multiplier] *= 1.3 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:SHEERFORCE, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] *= 1.3 if move.addlEffect > 0 - } -) - -BattleHandlers::DamageCalcUserAbility.add(:SLOWSTART, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:attack_multiplier] /= 2 if user.effects[PBEffects::SlowStart] > 0 && move.physicalMove? - } -) - -BattleHandlers::DamageCalcUserAbility.add(:SOLARPOWER, - proc { |ability,user,target,move,mults,baseDmg,type| - if move.specialMove? && [:Sun, :HarshSun].include?(user.battle.pbWeather) - mults[:attack_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:SNIPER, - proc { |ability,user,target,move,mults,baseDmg,type| - if target.damageState.critical - mults[:final_damage_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:STAKEOUT, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:attack_multiplier] *= 2 if target.battle.choices[target.index][0] == :SwitchOut - } -) - -BattleHandlers::DamageCalcUserAbility.add(:STEELWORKER, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:attack_multiplier] *= 1.5 if type == :STEEL - } -) - -BattleHandlers::DamageCalcUserAbility.add(:STRONGJAW, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] *= 1.5 if move.bitingMove? - } -) - -BattleHandlers::DamageCalcUserAbility.add(:SWARM, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.hp <= user.totalhp / 3 && type == :BUG - mults[:attack_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:TECHNICIAN, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.index != target.index && move && move.id != :STRUGGLE && - baseDmg * mults[:base_damage_multiplier] <= 60 - mults[:base_damage_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:TINTEDLENS, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:final_damage_multiplier] *= 2 if Effectiveness.resistant?(target.damageState.typeMod) - } -) - -BattleHandlers::DamageCalcUserAbility.add(:TORRENT, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.hp <= user.totalhp / 3 && type == :WATER - mults[:attack_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:TOUGHCLAWS, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] *= 4 / 3.0 if move.contactMove? - } -) - -BattleHandlers::DamageCalcUserAbility.add(:TOXICBOOST, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.poisoned? && move.physicalMove? - mults[:base_damage_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcUserAbility.add(:WATERBUBBLE, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:attack_multiplier] *= 2 if type == :WATER - } -) - -#=============================================================================== -# DamageCalcUserAllyAbility handlers -#=============================================================================== - -BattleHandlers::DamageCalcUserAllyAbility.add(:BATTERY, - proc { |ability,user,target,move,mults,baseDmg,type| - next if !move.specialMove? - mults[:final_damage_multiplier] *= 1.3 - } -) - -BattleHandlers::DamageCalcUserAllyAbility.add(:FLOWERGIFT, - proc { |ability,user,target,move,mults,baseDmg,type| - if move.physicalMove? && [:Sun, :HarshSun].include?(user.battle.pbWeather) - mults[:attack_multiplier] *= 1.5 - end - } -) - -#=============================================================================== -# DamageCalcTargetAbility handlers -#=============================================================================== - -BattleHandlers::DamageCalcTargetAbility.add(:DRYSKIN, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] *= 1.25 if type == :FIRE - } -) - -BattleHandlers::DamageCalcTargetAbility.add(:FILTER, - proc { |ability,user,target,move,mults,baseDmg,type| - if Effectiveness.super_effective?(target.damageState.typeMod) - mults[:final_damage_multiplier] *= 0.75 - end - } -) - -BattleHandlers::DamageCalcTargetAbility.copy(:FILTER,:SOLIDROCK) - -BattleHandlers::DamageCalcTargetAbility.add(:FLOWERGIFT, - proc { |ability,user,target,move,mults,baseDmg,type| - if move.specialMove? && [:Sun, :HarshSun].include?(user.battle.pbWeather) - mults[:defense_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcTargetAbility.add(:FLUFFY, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:final_damage_multiplier] *= 2 if move.calcType == :FIRE - mults[:final_damage_multiplier] /= 2 if move.contactMove? - } -) - -BattleHandlers::DamageCalcTargetAbility.add(:FURCOAT, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:defense_multiplier] *= 2 if move.physicalMove? || move.function == "122" # Psyshock - } -) - -BattleHandlers::DamageCalcTargetAbility.add(:GRASSPELT, - proc { |ability,user,target,move,mults,baseDmg,type| - if user.battle.field.terrain == :Grassy - mults[:defense_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcTargetAbility.add(:HEATPROOF, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] /= 2 if type == :FIRE - } -) - -BattleHandlers::DamageCalcTargetAbility.add(:MARVELSCALE, - proc { |ability,user,target,move,mults,baseDmg,type| - if target.pbHasAnyStatus? && move.physicalMove? - mults[:defense_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcTargetAbility.add(:MULTISCALE, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:final_damage_multiplier] /= 2 if target.hp == target.totalhp - } -) - -BattleHandlers::DamageCalcTargetAbility.add(:THICKFAT, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:base_damage_multiplier] /= 2 if type == :FIRE || type == :ICE - } -) - -BattleHandlers::DamageCalcTargetAbility.add(:WATERBUBBLE, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:final_damage_multiplier] /= 2 if type == :FIRE - } -) - -#=============================================================================== -# DamageCalcTargetAbilityNonIgnorable handlers -#=============================================================================== - -BattleHandlers::DamageCalcTargetAbilityNonIgnorable.add(:PRISMARMOR, - proc { |ability,user,target,move,mults,baseDmg,type| - if Effectiveness.super_effective?(target.damageState.typeMod) - mults[:final_damage_multiplier] *= 0.75 - end - } -) - -BattleHandlers::DamageCalcTargetAbilityNonIgnorable.add(:SHADOWSHIELD, - proc { |ability,user,target,move,mults,baseDmg,type| - if target.hp==target.totalhp - mults[:final_damage_multiplier] /= 2 - end - } -) - -#=============================================================================== -# DamageCalcTargetAllyAbility handlers -#=============================================================================== - -BattleHandlers::DamageCalcTargetAllyAbility.add(:FLOWERGIFT, - proc { |ability,user,target,move,mults,baseDmg,type| - if move.specialMove? && [:Sun, :HarshSun].include?(user.battle.pbWeather) - mults[:defense_multiplier] *= 1.5 - end - } -) - -BattleHandlers::DamageCalcTargetAllyAbility.add(:FRIENDGUARD, - proc { |ability,user,target,move,mults,baseDmg,type| - mults[:final_damage_multiplier] *= 0.75 - } -) - -#=============================================================================== -# CriticalCalcUserAbility handlers -#=============================================================================== - -BattleHandlers::CriticalCalcUserAbility.add(:MERCILESS, - proc { |ability,user,target,c| - next 99 if target.poisoned? - } -) - -BattleHandlers::CriticalCalcUserAbility.add(:SUPERLUCK, - proc { |ability,user,target,c| - next c+1 - } -) - -#=============================================================================== -# CriticalCalcTargetAbility handlers -#=============================================================================== - -BattleHandlers::CriticalCalcTargetAbility.add(:BATTLEARMOR, - proc { |ability,user,target,c| - next -1 - } -) - -BattleHandlers::CriticalCalcTargetAbility.copy(:BATTLEARMOR,:SHELLARMOR) - -#=============================================================================== -# TargetAbilityOnHit handlers -#=============================================================================== - -BattleHandlers::TargetAbilityOnHit.add(:AFTERMATH, - proc { |ability,user,target,move,battle| - next if !target.fainted? - next if !move.pbContactMove?(user) - battle.pbShowAbilitySplash(target) - if !battle.moldBreaker - dampBattler = battle.pbCheckGlobalAbility(:DAMP) - if dampBattler - battle.pbShowAbilitySplash(dampBattler) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1} cannot use {2}!",target.pbThis,target.abilityName)) - else - battle.pbDisplay(_INTL("{1} cannot use {2} because of {3}'s {4}!", - target.pbThis,target.abilityName,dampBattler.pbThis(true),dampBattler.abilityName)) - end - battle.pbHideAbilitySplash(dampBattler) - battle.pbHideAbilitySplash(target) - next - end - end - if user.takesIndirectDamage?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) && - user.affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - battle.scene.pbDamageAnimation(user) - user.pbReduceHP(user.totalhp/4,false) - battle.pbDisplay(_INTL("{1} was caught in the aftermath!",user.pbThis)) - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:ANGERPOINT, - proc { |ability,user,target,move,battle| - next if !target.damageState.critical - next if !target.pbCanRaiseStatStage?(:ATTACK,target) - battle.pbShowAbilitySplash(target) - target.stages[:ATTACK] = 6 - battle.pbCommonAnimation("StatUp",target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1} maxed its {2}!",target.pbThis,GameData::Stat.get(:ATTACK).name)) - else - battle.pbDisplay(_INTL("{1}'s {2} maxed its {3}!", - target.pbThis,target.abilityName,GameData::Stat.get(:ATTACK).name)) - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:CURSEDBODY, - proc { |ability,user,target,move,battle| - next if user.fainted? - next if user.effects[PBEffects::Disable]>0 - regularMove = nil - user.eachMove do |m| - next if m.id!=user.lastRegularMoveUsed - regularMove = m - break - end - next if !regularMove || (regularMove.pp==0 && regularMove.total_pp>0) - next if battle.pbRandom(100)>=30 - battle.pbShowAbilitySplash(target) - if !move.pbMoveFailedAromaVeil?(target,user,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - user.effects[PBEffects::Disable] = 3 - user.effects[PBEffects::DisableMove] = regularMove.id - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s {2} was disabled!",user.pbThis,regularMove.name)) - else - battle.pbDisplay(_INTL("{1}'s {2} was disabled by {3}'s {4}!", - user.pbThis,regularMove.name,target.pbThis(true),target.abilityName)) - end - battle.pbHideAbilitySplash(target) - user.pbItemStatusCureCheck - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:CUTECHARM, - proc { |ability,user,target,move,battle| - next if target.fainted? - next if !move.pbContactMove?(user) - next if battle.pbRandom(100)>=30 - battle.pbShowAbilitySplash(target) - if user.pbCanAttract?(target,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) && - user.affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} made {3} fall in love!",target.pbThis, - target.abilityName,user.pbThis(true)) - end - user.pbAttract(target,msg) - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:EFFECTSPORE, - proc { |ability,user,target,move,battle| - # NOTE: This ability has a 30% chance of triggering, not a 30% chance of - # inflicting a status condition. It can try (and fail) to inflict a - # status condition that the user is immune to. - next if !move.pbContactMove?(user) - next if battle.pbRandom(100)>=30 - r = battle.pbRandom(3) - next if r==0 && user.asleep? - next if r==1 && user.poisoned? - next if r==2 && user.paralyzed? - battle.pbShowAbilitySplash(target) - if user.affectedByPowder?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) && - user.affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - case r - when 0 - if user.pbCanSleep?(target,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} made {3} fall asleep!",target.pbThis, - target.abilityName,user.pbThis(true)) - end - user.pbSleep(msg) - end - when 1 - if user.pbCanPoison?(target,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} poisoned {3}!",target.pbThis, - target.abilityName,user.pbThis(true)) - end - user.pbPoison(target,msg) - end - when 2 - if user.pbCanParalyze?(target,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} paralyzed {3}! It may be unable to move!", - target.pbThis,target.abilityName,user.pbThis(true)) - end - user.pbParalyze(target,msg) - end - end - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:FLAMEBODY, - proc { |ability,user,target,move,battle| - next if !move.pbContactMove?(user) - next if user.burned? || battle.pbRandom(100)>=30 - battle.pbShowAbilitySplash(target) - if user.pbCanBurn?(target,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) && - user.affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} burned {3}!",target.pbThis,target.abilityName,user.pbThis(true)) - end - user.pbBurn(target,msg) - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:GOOEY, - proc { |ability,user,target,move,battle| - next if !move.pbContactMove?(user) - user.pbLowerStatStageByAbility(:SPEED,1,target,true,true) - } -) - -BattleHandlers::TargetAbilityOnHit.copy(:GOOEY,:TANGLINGHAIR) - -BattleHandlers::TargetAbilityOnHit.add(:ILLUSION, - proc { |ability,user,target,move,battle| - # NOTE: This intentionally doesn't show the ability splash. - next if !target.effects[PBEffects::Illusion] - target.effects[PBEffects::Illusion] = nil - battle.scene.pbChangePokemon(target,target.pokemon) - battle.pbDisplay(_INTL("{1}'s illusion wore off!",target.pbThis)) - battle.pbSetSeen(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:INNARDSOUT, - proc { |ability,user,target,move,battle| - next if !target.fainted? || user.dummy - battle.pbShowAbilitySplash(target) - if user.takesIndirectDamage?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - battle.scene.pbDamageAnimation(user) - user.pbReduceHP(target.damageState.hpLost,false) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1} is hurt!",user.pbThis)) - else - battle.pbDisplay(_INTL("{1} is hurt by {2}'s {3}!",user.pbThis, - target.pbThis(true),target.abilityName)) - end - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:IRONBARBS, - proc { |ability,user,target,move,battle| - next if !move.pbContactMove?(user) - battle.pbShowAbilitySplash(target) - if user.takesIndirectDamage?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) && - user.affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - battle.scene.pbDamageAnimation(user) - user.pbReduceHP(user.totalhp/8,false) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1} is hurt!",user.pbThis)) - else - battle.pbDisplay(_INTL("{1} is hurt by {2}'s {3}!",user.pbThis, - target.pbThis(true),target.abilityName)) - end - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.copy(:IRONBARBS,:ROUGHSKIN) - -BattleHandlers::TargetAbilityOnHit.add(:JUSTIFIED, - proc { |ability,user,target,move,battle| - next if move.calcType != :DARK - target.pbRaiseStatStageByAbility(:ATTACK,1,target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:MUMMY, - proc { |ability,user,target,move,battle| - next if !move.pbContactMove?(user) - next if user.fainted? - next if user.unstoppableAbility? || user.ability == ability - oldAbil = nil - battle.pbShowAbilitySplash(target) if user.opposes?(target) - if user.affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - oldAbil = user.ability - battle.pbShowAbilitySplash(user,true,false) if user.opposes?(target) - user.ability = ability - battle.pbReplaceAbilitySplash(user) if user.opposes?(target) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s Ability became {2}!",user.pbThis,user.abilityName)) - else - battle.pbDisplay(_INTL("{1}'s Ability became {2} because of {3}!", - user.pbThis,user.abilityName,target.pbThis(true))) - end - battle.pbHideAbilitySplash(user) if user.opposes?(target) - end - battle.pbHideAbilitySplash(target) if user.opposes?(target) - user.pbOnAbilityChanged(oldAbil) if oldAbil != nil - } -) - -BattleHandlers::TargetAbilityOnHit.add(:POISONPOINT, - proc { |ability,user,target,move,battle| - next if !move.pbContactMove?(user) - next if user.poisoned? || battle.pbRandom(100)>=30 - battle.pbShowAbilitySplash(target) - if user.pbCanPoison?(target,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) && - user.affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} poisoned {3}!",target.pbThis,target.abilityName,user.pbThis(true)) - end - user.pbPoison(target,msg) - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:RATTLED, - proc { |ability,user,target,move,battle| - next if ![:BUG, :DARK, :GHOST].include?(move.calcType) - target.pbRaiseStatStageByAbility(:SPEED,1,target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:STAMINA, - proc { |ability,user,target,move,battle| - target.pbRaiseStatStageByAbility(:DEFENSE,1,target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:STATIC, - proc { |ability,user,target,move,battle| - next if !move.pbContactMove?(user) - next if user.paralyzed? || battle.pbRandom(100)>=30 - battle.pbShowAbilitySplash(target) - if user.pbCanParalyze?(target,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) && - user.affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} paralyzed {3}! It may be unable to move!", - target.pbThis,target.abilityName,user.pbThis(true)) - end - user.pbParalyze(target,msg) - end - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:WATERCOMPACTION, - proc { |ability,user,target,move,battle| - next if move.calcType != :WATER - target.pbRaiseStatStageByAbility(:DEFENSE,2,target) - } -) - -BattleHandlers::TargetAbilityOnHit.add(:WEAKARMOR, - proc { |ability,user,target,move,battle| - next if !move.physicalMove? - next if !target.pbCanLowerStatStage?(:DEFENSE, target) && - !target.pbCanRaiseStatStage?(:SPEED, target) - battle.pbShowAbilitySplash(target) - target.pbLowerStatStageByAbility(:DEFENSE, 1, target, false) - target.pbRaiseStatStageByAbility(:SPEED, - (Settings::MECHANICS_GENERATION >= 7) ? 2 : 1, target, false) - battle.pbHideAbilitySplash(target) - } -) - -#=============================================================================== -# UserAbilityOnHit handlers -#=============================================================================== - -BattleHandlers::UserAbilityOnHit.add(:POISONTOUCH, - proc { |ability,user,target,move,battle| - next if !move.contactMove? - next if battle.pbRandom(100)>=30 - battle.pbShowAbilitySplash(user) - if target.hasActiveAbility?(:SHIELDDUST) && !battle.moldBreaker - battle.pbShowAbilitySplash(target) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis)) - end - battle.pbHideAbilitySplash(target) - elsif target.pbCanPoison?(user,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - msg = nil - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - msg = _INTL("{1}'s {2} poisoned {3}!",user.pbThis,user.abilityName,target.pbThis(true)) - end - target.pbPoison(user,msg) - end - battle.pbHideAbilitySplash(user) - } -) - -#=============================================================================== -# UserAbilityEndOfMove handlers -#=============================================================================== - -BattleHandlers::UserAbilityEndOfMove.add(:BEASTBOOST, - proc { |ability,user,targets,move,battle| - next if battle.pbAllFainted?(user.idxOpposingSide) - numFainted = 0 - targets.each { |b| numFainted += 1 if b.damageState.fainted } - next if numFainted == 0 - userStats = user.plainStats - highestStatValue = 0 - userStats.each_value { |value| highestStatValue = value if highestStatValue < value } - GameData::Stat.each_main_battle do |s| - next if userStats[s.id] < highestStatValue - if user.pbCanRaiseStatStage?(s.id, user) - user.pbRaiseStatStageByAbility(s.id, numFainted, user) - end - break - end - } -) - -BattleHandlers::UserAbilityEndOfMove.add(:MAGICIAN, - proc { |ability,user,targets,move,battle| - next if battle.futureSight - next if !move.pbDamagingMove? - next if user.item - next if battle.wildBattle? && user.opposes? - targets.each do |b| - next if b.damageState.unaffected || b.damageState.substitute - next if !b.item - next if b.unlosableItem?(b.item) || user.unlosableItem?(b.item) - battle.pbShowAbilitySplash(user) - if b.hasActiveAbility?(:STICKYHOLD) - battle.pbShowAbilitySplash(b) if user.opposes?(b) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s item cannot be stolen!",b.pbThis)) - end - battle.pbHideAbilitySplash(b) if user.opposes?(b) - next - end - user.item = b.item - b.item = nil - b.effects[PBEffects::Unburden] = true - if battle.wildBattle? && !user.initialItem && user.item == b.initialItem - user.setInitialItem(user.item) - b.setInitialItem(nil) - end - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1} stole {2}'s {3}!",user.pbThis, - b.pbThis(true),user.itemName)) - else - battle.pbDisplay(_INTL("{1} stole {2}'s {3} with {4}!",user.pbThis, - b.pbThis(true),user.itemName,user.abilityName)) - end - battle.pbHideAbilitySplash(user) - user.pbHeldItemTriggerCheck - break - end - } -) - -BattleHandlers::UserAbilityEndOfMove.add(:MOXIE, - proc { |ability,user,targets,move,battle| - next if battle.pbAllFainted?(user.idxOpposingSide) - numFainted = 0 - targets.each { |b| numFainted += 1 if b.damageState.fainted } - next if numFainted==0 || !user.pbCanRaiseStatStage?(:ATTACK,user) - user.pbRaiseStatStageByAbility(:ATTACK,numFainted,user) - } -) - -#=============================================================================== -# TargetAbilityAfterMoveUse handlers -#=============================================================================== - -BattleHandlers::TargetAbilityAfterMoveUse.add(:BERSERK, - proc { |ability,target,user,move,switched,battle| - next if !move.damagingMove? - next if target.damageState.initialHP=target.totalhp/2 - next if !target.pbCanRaiseStatStage?(:SPECIAL_ATTACK,target) - target.pbRaiseStatStageByAbility(:SPECIAL_ATTACK,1,target) - } -) - -BattleHandlers::TargetAbilityAfterMoveUse.add(:COLORCHANGE, - proc { |ability,target,user,move,switched,battle| - next if target.damageState.calcDamage==0 || target.damageState.substitute - next if !move.calcType || GameData::Type.get(move.calcType).pseudo_type - next if target.pbHasType?(move.calcType) && !target.pbHasOtherType?(move.calcType) - typeName = GameData::Type.get(move.calcType).name - battle.pbShowAbilitySplash(target) - target.pbChangeTypes(move.calcType) - battle.pbDisplay(_INTL("{1}'s {2} made it the {3} type!",target.pbThis, - target.abilityName,typeName)) - battle.pbHideAbilitySplash(target) - } -) - -BattleHandlers::TargetAbilityAfterMoveUse.add(:PICKPOCKET, - proc { |ability,target,user,move,switched,battle| - # NOTE: According to Bulbapedia, this can still trigger to steal the user's - # item even if it was switched out by a Red Card. This doesn't make - # sense, so this code doesn't do it. - next if battle.wildBattle? && target.opposes? - next if !move.contactMove? - next if switched.include?(user.index) - next if user.effects[PBEffects::Substitute]>0 || target.damageState.substitute - next if target.item || !user.item - next if user.unlosableItem?(user.item) || target.unlosableItem?(user.item) - battle.pbShowAbilitySplash(target) - if user.hasActiveAbility?(:STICKYHOLD) - battle.pbShowAbilitySplash(user) if target.opposes?(user) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s item cannot be stolen!",user.pbThis)) - end - battle.pbHideAbilitySplash(user) if target.opposes?(user) - battle.pbHideAbilitySplash(target) - next - end - target.item = user.item - user.item = nil - user.effects[PBEffects::Unburden] = true - if battle.wildBattle? && !target.initialItem && target.item == user.initialItem - target.setInitialItem(target.item) - user.setInitialItem(nil) - end - battle.pbDisplay(_INTL("{1} pickpocketed {2}'s {3}!",target.pbThis, - user.pbThis(true),target.itemName)) - battle.pbHideAbilitySplash(target) - target.pbHeldItemTriggerCheck - } -) - -#=============================================================================== -# EORWeatherAbility handlers -#=============================================================================== - -BattleHandlers::EORWeatherAbility.add(:DRYSKIN, - proc { |ability,weather,battler,battle| - case weather - when :Sun, :HarshSun - battle.pbShowAbilitySplash(battler) - battle.scene.pbDamageAnimation(battler) - battler.pbReduceHP(battler.totalhp/8,false) - battle.pbDisplay(_INTL("{1} was hurt by the sunlight!",battler.pbThis)) - battle.pbHideAbilitySplash(battler) - battler.pbItemHPHealCheck - when :Rain, :HeavyRain - next if !battler.canHeal? - battle.pbShowAbilitySplash(battler) - battler.pbRecoverHP(battler.totalhp/8) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s HP was restored.",battler.pbThis)) - else - battle.pbDisplay(_INTL("{1}'s {2} restored its HP.",battler.pbThis,battler.abilityName)) - end - battle.pbHideAbilitySplash(battler) - end - } -) - -BattleHandlers::EORWeatherAbility.add(:ICEBODY, - proc { |ability,weather,battler,battle| - next unless weather == :Hail - next if !battler.canHeal? - battle.pbShowAbilitySplash(battler) - battler.pbRecoverHP(battler.totalhp/16) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s HP was restored.",battler.pbThis)) - else - battle.pbDisplay(_INTL("{1}'s {2} restored its HP.",battler.pbThis,battler.abilityName)) - end - battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::EORWeatherAbility.add(:RAINDISH, - proc { |ability,weather,battler,battle| - next unless [:Rain, :HeavyRain].include?(weather) - next if !battler.canHeal? - battle.pbShowAbilitySplash(battler) - battler.pbRecoverHP(battler.totalhp/16) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1}'s HP was restored.",battler.pbThis)) - else - battle.pbDisplay(_INTL("{1}'s {2} restored its HP.",battler.pbThis,battler.abilityName)) - end - battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::EORWeatherAbility.add(:SOLARPOWER, - proc { |ability,weather,battler,battle| - next unless [:Sun, :HarshSun].include?(weather) - battle.pbShowAbilitySplash(battler) - battle.scene.pbDamageAnimation(battler) - battler.pbReduceHP(battler.totalhp/8,false) - battle.pbDisplay(_INTL("{1} was hurt by the sunlight!",battler.pbThis)) - battle.pbHideAbilitySplash(battler) - battler.pbItemHPHealCheck - } -) - -#=============================================================================== -# EORHealingAbility handlers -#=============================================================================== - -BattleHandlers::EORHealingAbility.add(:HEALER, - proc { |ability,battler,battle| - next unless battle.pbRandom(100)<30 - battler.eachAlly do |b| - next if b.status == :NONE - battle.pbShowAbilitySplash(battler) - oldStatus = b.status - b.pbCureStatus(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - case oldStatus - when :SLEEP - battle.pbDisplay(_INTL("{1}'s {2} woke its partner up!",battler.pbThis,battler.abilityName)) - when :POISON - battle.pbDisplay(_INTL("{1}'s {2} cured its partner's poison!",battler.pbThis,battler.abilityName)) - when :BURN - battle.pbDisplay(_INTL("{1}'s {2} healed its partner's burn!",battler.pbThis,battler.abilityName)) - when :PARALYSIS - battle.pbDisplay(_INTL("{1}'s {2} cured its partner's paralysis!",battler.pbThis,battler.abilityName)) - when :FROZEN - battle.pbDisplay(_INTL("{1}'s {2} defrosted its partner!",battler.pbThis,battler.abilityName)) - end - end - battle.pbHideAbilitySplash(battler) - end - } -) - -BattleHandlers::EORHealingAbility.add(:HYDRATION, - proc { |ability,battler,battle| - next if battler.status == :NONE - next if ![:Rain, :HeavyRain].include?(battle.pbWeather) - battle.pbShowAbilitySplash(battler) - oldStatus = battler.status - battler.pbCureStatus(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - case oldStatus - when :SLEEP - battle.pbDisplay(_INTL("{1}'s {2} woke it up!",battler.pbThis,battler.abilityName)) - when :POISON - battle.pbDisplay(_INTL("{1}'s {2} cured its poison!",battler.pbThis,battler.abilityName)) - when :BURN - battle.pbDisplay(_INTL("{1}'s {2} healed its burn!",battler.pbThis,battler.abilityName)) - when :PARALYSIS - battle.pbDisplay(_INTL("{1}'s {2} cured its paralysis!",battler.pbThis,battler.abilityName)) - when :FROZEN - battle.pbDisplay(_INTL("{1}'s {2} defrosted it!",battler.pbThis,battler.abilityName)) - end - end - battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::EORHealingAbility.add(:SHEDSKIN, - proc { |ability,battler,battle| - next if battler.status == :NONE - next unless battle.pbRandom(100)<30 - battle.pbShowAbilitySplash(battler) - oldStatus = battler.status - battler.pbCureStatus(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - case oldStatus - when :SLEEP - battle.pbDisplay(_INTL("{1}'s {2} woke it up!",battler.pbThis,battler.abilityName)) - when :POISON - battle.pbDisplay(_INTL("{1}'s {2} cured its poison!",battler.pbThis,battler.abilityName)) - when :BURN - battle.pbDisplay(_INTL("{1}'s {2} healed its burn!",battler.pbThis,battler.abilityName)) - when :PARALYSIS - battle.pbDisplay(_INTL("{1}'s {2} cured its paralysis!",battler.pbThis,battler.abilityName)) - when :FROZEN - battle.pbDisplay(_INTL("{1}'s {2} defrosted it!",battler.pbThis,battler.abilityName)) - end - end - battle.pbHideAbilitySplash(battler) - } -) - -#=============================================================================== -# EOREffectAbility handlers -#=============================================================================== - -BattleHandlers::EOREffectAbility.add(:BADDREAMS, - proc { |ability,battler,battle| - battle.eachOtherSideBattler(battler.index) do |b| - next if !b.near?(battler) || !b.asleep? - battle.pbShowAbilitySplash(battler) - next if !b.takesIndirectDamage?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH) - oldHP = b.hp - b.pbReduceHP(b.totalhp/8) - if PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1} is tormented!",b.pbThis)) - else - battle.pbDisplay(_INTL("{1} is tormented by {2}'s {3}!",b.pbThis, - battler.pbThis(true),battler.abilityName)) - end - battle.pbHideAbilitySplash(battler) - b.pbItemHPHealCheck - b.pbAbilitiesOnDamageTaken(oldHP) - b.pbFaint if b.fainted? - end - } -) - -BattleHandlers::EOREffectAbility.add(:MOODY, - proc { |ability,battler,battle| - randomUp = [] - randomDown = [] - GameData::Stat.each_battle do |s| - randomUp.push(s.id) if battler.pbCanRaiseStatStage?(s.id, battler) - randomDown.push(s.id) if battler.pbCanLowerStatStage?(s.id, battler) - end - next if randomUp.length==0 && randomDown.length==0 - battle.pbShowAbilitySplash(battler) - if randomUp.length>0 - r = battle.pbRandom(randomUp.length) - battler.pbRaiseStatStageByAbility(randomUp[r],2,battler,false) - randomDown.delete(randomUp[r]) - end - if randomDown.length>0 - r = battle.pbRandom(randomDown.length) - battler.pbLowerStatStageByAbility(randomDown[r],1,battler,false) - end - battle.pbHideAbilitySplash(battler) - battler.pbItemStatRestoreCheck if randomDown.length>0 - } -) - -BattleHandlers::EOREffectAbility.add(:SPEEDBOOST, - proc { |ability,battler,battle| - # A Pokémon's turnCount is 0 if it became active after the beginning of a - # round - if battler.turnCount>0 && battler.pbCanRaiseStatStage?(:SPEED,battler) - battler.pbRaiseStatStageByAbility(:SPEED,1,battler) - end - } -) - -#=============================================================================== -# EORGainItemAbility handlers -#=============================================================================== - -BattleHandlers::EORGainItemAbility.add(:HARVEST, - proc { |ability,battler,battle| - next if battler.item - next if !battler.recycleItem || !GameData::Item.get(battler.recycleItem).is_berry? - if ![:Sun, :HarshSun].include?(battle.pbWeather) - next unless battle.pbRandom(100)<50 - end - battle.pbShowAbilitySplash(battler) - battler.item = battler.recycleItem - battler.setRecycleItem(nil) - battler.setInitialItem(battler.item) if !battler.initialItem - battle.pbDisplay(_INTL("{1} harvested one {2}!",battler.pbThis,battler.itemName)) - battle.pbHideAbilitySplash(battler) - battler.pbHeldItemTriggerCheck - } -) - -BattleHandlers::EORGainItemAbility.add(:PICKUP, - proc { |ability,battler,battle| - next if battler.item - foundItem = nil; fromBattler = nil; use = 0 - battle.eachBattler do |b| - next if b.index==battler.index - next if b.effects[PBEffects::PickupUse]<=use - foundItem = b.effects[PBEffects::PickupItem] - fromBattler = b - use = b.effects[PBEffects::PickupUse] - end - next if !foundItem - battle.pbShowAbilitySplash(battler) - battler.item = foundItem - fromBattler.effects[PBEffects::PickupItem] = nil - fromBattler.effects[PBEffects::PickupUse] = 0 - fromBattler.setRecycleItem(nil) if fromBattler.recycleItem==foundItem - if battle.wildBattle? && !battler.initialItem && fromBattler.initialItem==foundItem - battler.setInitialItem(foundItem) - fromBattler.setInitialItem(nil) - end - battle.pbDisplay(_INTL("{1} found one {2}!",battler.pbThis,battler.itemName)) - battle.pbHideAbilitySplash(battler) - battler.pbHeldItemTriggerCheck - } -) - -#=============================================================================== -# CertainSwitchingUserAbility handlers -#=============================================================================== - -# There aren't any! - -#=============================================================================== -# TrappingTargetAbility handlers -#=============================================================================== - -BattleHandlers::TrappingTargetAbility.add(:ARENATRAP, - proc { |ability,switcher,bearer,battle| - next true if !switcher.airborne? - } -) - -BattleHandlers::TrappingTargetAbility.add(:MAGNETPULL, - proc { |ability,switcher,bearer,battle| - next true if switcher.pbHasType?(:STEEL) - } -) - -BattleHandlers::TrappingTargetAbility.add(:SHADOWTAG, - proc { |ability,switcher,bearer,battle| - next true if !switcher.hasActiveAbility?(:SHADOWTAG) - } -) - -#=============================================================================== -# AbilityOnSwitchIn handlers -#=============================================================================== - -BattleHandlers::AbilityOnSwitchIn.add(:AIRLOCK, - proc { |ability,battler,battle| - battle.pbShowAbilitySplash(battler) - if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH - battle.pbDisplay(_INTL("{1} has {2}!",battler.pbThis,battler.abilityName)) - end - battle.pbDisplay(_INTL("The effects of the weather disappeared.")) - battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::AbilityOnSwitchIn.copy(:AIRLOCK,:CLOUDNINE) - -BattleHandlers::AbilityOnSwitchIn.add(:ANTICIPATION, - proc { |ability,battler,battle| - next if !battler.pbOwnedByPlayer? - battlerTypes = battler.pbTypes(true) - type1 = battlerTypes[0] - type2 = battlerTypes[1] || type1 - type3 = battlerTypes[2] || type2 - found = false - battle.eachOtherSideBattler(battler.index) do |b| - b.eachMove do |m| - next if m.statusMove? - if type1 - moveType = m.type - if Settings::MECHANICS_GENERATION >= 6 && m.function == "090" # Hidden Power - moveType = pbHiddenPower(b.pokemon)[0] - end - eff = Effectiveness.calculate(moveType,type1,type2,type3) - next if Effectiveness.ineffective?(eff) - next if !Effectiveness.super_effective?(eff) && m.function != "070" # OHKO - else - next if m.function != "070" # OHKO - end - found = true - break - end - break if found - end - if found - battle.pbShowAbilitySplash(battler) - battle.pbDisplay(_INTL("{1} shuddered with anticipation!",battler.pbThis)) - battle.pbHideAbilitySplash(battler) - end - } -) - -BattleHandlers::AbilityOnSwitchIn.add(:AURABREAK, - proc { |ability,battler,battle| - battle.pbShowAbilitySplash(battler) - battle.pbDisplay(_INTL("{1} reversed all other Pokémon's auras!",battler.pbThis)) - battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::AbilityOnSwitchIn.add(:COMATOSE, - proc { |ability,battler,battle| - battle.pbShowAbilitySplash(battler) - battle.pbDisplay(_INTL("{1} is drowsing!",battler.pbThis)) - battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::AbilityOnSwitchIn.add(:DARKAURA, - proc { |ability,battler,battle| - battle.pbShowAbilitySplash(battler) - battle.pbDisplay(_INTL("{1} is radiating a dark aura!",battler.pbThis)) - battle.pbHideAbilitySplash(battler) - } -) - -BattleHandlers::AbilityOnSwitchIn.add(:DELTASTREAM, - proc { |ability,battler,battle| - pbBattleWeatherAbility(:StrongWinds, battler, battle, true) - } -) - -BattleHandlers::AbilityOnSwitchIn.add(:DESOLATELAND, - proc { |ability,battler,battle| - pbBattleWeatherAbility(:HarshSun, battler, battle, true) - } -) - -BattleHandlers::AbilityOnSwitchIn.add(:DOWNLOAD, - proc { |ability,battler,battle| - oDef = oSpDef = 0 - battle.eachOtherSideBattler(battler.index) do |b| - oDef += b.defense - oSpDef += b.spdef - end - stat = (oDef