update 6.7

This commit is contained in:
chardub
2025-09-28 15:53:01 -04:00
parent ef5e023ae0
commit 318ff90d8d
696 changed files with 111759 additions and 198230 deletions

BIN
Audio/BGM/SSANNE.ogg Normal file

Binary file not shown.

BIN
Audio/BGM/bicycle.mp3 Normal file

Binary file not shown.

View File

@@ -107,9 +107,9 @@ casinoluck<s>ymirbot
###################################
### Custom Pok?dex entries ###
### Custom Pokedex entries ###
###################################
### Pok?dex entries quality control (Unown)
### Pokedex entries quality control (Unown)
luvischlo<s>char_latte3412<s>strawbearycandy
bobosmith01<s>griddle<s>.izik
knilk<s>lordjoostmeister<s>.realthree
@@ -127,6 +127,10 @@ Maruno (http://pokemonessentials.wikia.com/wiki/Pok%C3%A9mon_Essentials_Wiki)
###########################################################
Playtesting was done by various members of the Discord channel.
Special thanks to all of you!
Elite 4 rematch teams
duskrd<s>anaconja
###########################################################
### Graphics #
###########################################################
@@ -140,6 +144,14 @@ Nintendo
GameFreak
########################
### Translations #
########################
French translation:
anthonygourmand
locpic_
blood.wolf58 (Willi)
#######################################################################
The following ressources were also used
with their respective authors' consent

BIN
Data/.DS_Store vendored

Binary file not shown.

11
Data/.idea/Data.iml generated Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="RUBY_MODULE" version="4">
<component name="ModuleRunConfigurationManager">
<shared />
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
Data/.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Data.iml" filepath="$PROJECT_DIR$/.idea/Data.iml" />
</modules>
</component>
</project>

7
Data/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$/Scripts" vcs="Git" />
</component>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -5,10 +5,13 @@
#==============================================================================#
module Settings
# The version of your game. It has to adhere to the MAJOR.MINOR.PATCH format.
GAME_VERSION = '6.6.2'
GAME_VERSION_NUMBER = "6.6.2"
GAME_VERSION = '6.7.0'
GAME_VERSION_NUMBER = "6.7.0"
LATEST_GAME_RELEASE = "6.6"
KANTO = GAME_ID == :IF_KANTO
HOENN = GAME_ID == :IF_HOENN
POKERADAR_LIGHT_ANIMATION_RED_ID = 17
POKERADAR_LIGHT_ANIMATION_GREEN_ID = 18
POKERADAR_HIDDEN_ABILITY_POKE_CHANCE = 32
@@ -33,7 +36,7 @@ module Settings
BATTLERS_FOLDER = "Graphics/Battlers/Autogens/"
DOWNLOADED_SPRITES_FOLDER = "Graphics/temp/"
DEFAULT_SPRITE_PATH = "Graphics/Battlers/Special/000.png"
CREDITS_FILE_PATH = "Data/sprites/Sprite Credits.csv"
CREDITS_FILE_PATH = "Data/sprites/Sprite_Credits.csv"
VERSION_FILE_PATH = "Data/VERSION"
CUSTOM_SPRITES_FILE_PATH = "Data/sprites/CUSTOM_SPRITES"
BASE_SPRITES_FILE_PATH = "Data/sprites/BASE_SPRITES"
@@ -66,9 +69,13 @@ module Settings
SPRITES_FILE_URL = "https://raw.githubusercontent.com/infinitefusion/infinitefusion-e18/main/Data/sprites/CUSTOM_SPRITES"
BASE_SPRITES_FILE_URL = "https://raw.githubusercontent.com/infinitefusion/infinitefusion-e18/main/Data/sprites/BASE_SPRITES"
CREDITS_FILE_URL = "https://infinitefusion.net/Sprite Credits.csv"
CREDITS_FILE_URL = "https://infinitefusion.net/customsprites/Sprite_Credits.csv"
CUSTOM_DEX_FILE_URL = "https://raw.githubusercontent.com/infinitefusion/pif-downloadables/refs/heads/master/dex.json"
SECRETBASE_UPLOAD_URL = "http://secretbases-upload.pkmninfinitefusion.workers.dev"
SECRETBASE_DOWNLOAD_URL = "https://secretbase-download.pkmninfinitefusion.workers.dev"
STARTUP_MESSAGES = ""
LEVEL_CAPS=[12,22,26,35,38,45,51,54,62,62,63,64,64,65,67,68]
@@ -175,12 +182,19 @@ module Settings
# always inherit egg moves from its father.
BREEDING_CAN_INHERIT_EGG_MOVES_FROM_MOTHER = (MECHANICS_GENERATION >= 6)
KANTO_STARTERS = [:BULBASAUR, :CHARMANDER, :SQUIRTLE]
JOHTO_STARTERS = [:CHIKORITA, :CYNDAQUIL, :TOTODILE]
HOENN_STARTERS = [:TREECKO, :TORCHIC, :MUDKIP]
SINNOH_STARTERS = [:TURTWIG, :CHIMCHAR, :PIPLUP]
KALOS_STARTERS = [:CHESPIN, :FENNEKIN, :FROAKIE]
DEFAULT_STARTERS = Settings::GAME_ID == :IF_KANTO ? KANTO_STARTERS : HOENN_STARTERS
GRASS_STARTERS = [:BULBASAUR,:CHIKORITA,:TREECKO,:TURTWIG,:CHESPIN]
FIRE_STARTERS = [:CHARMANDER,:CYNDAQUIL, :TORCHIC, :CHIMCHAR, :FENNEKIN]
WATER_STARTERS = [:SQUIRTLE, :TOTODILE, :MUDKIP, :PIPLUP, :FROAKIE]
#=============================================================================
@@ -345,7 +359,7 @@ module Settings
# Dex list, no matter which region the player is currently in.
def self.pokedex_names
return [
# [_INTL("Kanto Pokédex"), 0]
# ["Kanto Pokédex", 0]
]
end
@@ -543,11 +557,15 @@ module Settings
# file in the Data folder. Edit only if you have 2 or more languages to choose
# from.
LANGUAGES = [
# ["English", "english.dat"],
# ["Deutsch", "deutsch.dat"]
["English", "english.dat"],
["Français", "french.dat"]
]
#Experimental
REMOTE_BATTLES_CONTROL = false
REMOTE_NPC_DIALOG = false
REMOTE_BATTLE_CONTROL_SERVER_URL = "http://127.0.0.1:5000/choose_move"
REMOTE_NPC_DIALOG_SERVER_URL = "http://127.0.0.1:5000"
#Technical
SPRITE_CACHE_MAX_NB=100
NEWEST_SPRITEPACK_MONTH = 12
@@ -576,7 +594,7 @@ module Settings
"speech hgss 18",
"speech hgss 19",
"speech hgss 20",
"speech pl 18"
"speech pl 18",
]
# Available menu frames. These are graphic files in "Graphics/Windowskins/".

View File

@@ -3,42 +3,42 @@
# HTTP utility functions
#
#############################
#
def pbPostData(url, postdata, filename=nil, depth=0)
if url[/^http:\/\/([^\/]+)(.*)$/]
host = $1
path = $2
path = "/" if path.length==0
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
body = postdata.map { |key, value|
keyString = key.to_s
valueString = value.to_s
keyString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
valueString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
next "#{keyString}=#{valueString}"
}.join('&')
ret = HTTPLite.post_body(
url,
body,
"application/x-www-form-urlencoded",
{
"Host" => host, # might not be necessary
"Proxy-Connection" => "Close",
"Content-Length" => body.bytesize.to_s,
"Pragma" => "no-cache",
"User-Agent" => userAgent
}
) rescue ""
return ret if !ret.is_a?(Hash)
return "" if ret[:status] != 200
return ret[:body] if !filename
File.open(filename, "wb"){|f|f.write(ret[:body])}
return "" unless url =~ /^https?:\/\/([^\/]+)(.*)$/
host = $1
path = $2
path = "/" if path.empty?
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
# Serialize as JSON
body = serialize_json(postdata)
ret = HTTPLite.post_body(
url,
body,
"application/json",
{
"Host" => host,
"Proxy-Connection" => "Close",
"Content-Length" => body.bytesize.to_s,
"Pragma" => "no-cache",
"User-Agent" => userAgent
}
) rescue ""
return "" if !ret.is_a?(Hash)
return "" if ret[:status] != 200
if filename
File.open(filename, "wb") { |f| f.write(ret[:body]) }
return ""
end
return ""
ret[:body]
end
def pbDownloadData(url, filename = nil, authorization = nil, depth = 0, &block)
return nil if !downloadAllowed?()
echoln "downloading data from #{url}"
@@ -73,15 +73,21 @@ def pbDownloadToFile(url, file)
end
end
def pbPostToString(url, postdata)
def pbPostToString(url, postdata, timeout = 30)
safe_postdata = postdata.transform_values(&:to_s)
begin
data = pbPostData(url, postdata)
return data
rescue
data = pbPostData(url, safe_postdata)
return data || ""
rescue MKXPError => e
echoln("[Remote AI] Exception: #{e.message}")
return ""
end
end
def pbPostToFile(url, postdata, file)
begin
pbPostData(url, postdata,file)
@@ -89,7 +95,7 @@ def pbPostToFile(url, postdata, file)
end
end
def serialize_value(value)
def serialize_value_legacy(value)
if value.is_a?(Hash)
serialize_json(value)
elsif value.is_a?(String)
@@ -102,17 +108,47 @@ end
def serialize_json(data)
#echoln data
# Manually serialize the JSON data into a string
parts = ["{"]
data.each_with_index do |(key, value), index|
parts << "\"#{key}\":#{serialize_value(value)}"
parts << "," unless index == data.size - 1
if data.is_a?(Hash)
parts = ["{"]
data.each_with_index do |(key, value), index|
parts << "\"#{key}\":#{serialize_value(value)}"
parts << "," unless index == data.size - 1
end
parts << "}"
return parts.join
else
return serialize_value(data)
end
parts << "}"
return parts.join
end
def serialize_value(value)
case value
when String
"\"#{escape_json_string(value)}\""
when Numeric
value.to_s
when TrueClass, FalseClass
value.to_s
when NilClass
"null"
when Array
"[" + value.map { |v| serialize_value(v) }.join(",") + "]"
when Hash
serialize_json(value)
else
raise "Unsupported type: #{value.class}"
end
end
def escape_json_string(str)
# Minimal escape handling
str.gsub(/["\\]/) { |m| "\\#{m}" }
.gsub("\n", "\\n")
.gsub("\t", "\\t")
.gsub("\r", "\\r")
end
def downloadAllowed?()
return $PokemonSystem.download_sprites==0
@@ -141,6 +177,42 @@ def clean_json_string(str)
end
# json.rb - lightweight JSON parser for MKXP/RGSS XP
# Lightweight JSON for MKXP/RGSS XP
module JSON
module_function
# Convert Ruby object (hash/array/etc) into JSON string
def generate(obj)
case obj
when Hash
"{" + obj.map { |k, v| "\"#{k}\":#{generate(v)}" }.join(",") + "}"
when Array
"[" + obj.map { |v| generate(v) }.join(",") + "]"
when String, Symbol
"\"#{obj.to_s}\""
when TrueClass, FalseClass
obj.to_s
when NilClass
"null"
when Numeric
obj.to_s
else
raise "Unsupported type #{obj.class}"
end
end
# Simple parser (not full JSON) — optional
def parse(str)
return nil if str.nil? || str.strip.empty?
eval(str)
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -202,6 +202,7 @@ module Game
$PokemonMap.updateMap
$scene = Scene_Map.new
onLoadExistingGame()
end
# Loads and validates the map. Called when loading a saved game.
@@ -234,9 +235,15 @@ module Game
end
$PokemonEncounters = PokemonEncounters.new
$PokemonEncounters.setup($game_map.map_id)
self.load_secret_bases if Settings::HOENN
pbUpdateVehicle
end
def self.load_secret_bases
loader = SecretBaseLoader.new
loader.load_visitor_bases
end
# Saves the game. Returns whether the operation was successful.
# @param save_file [String] the save file path
# @param safe [Boolean] whether $PokemonGlobal.safesave should be set to true

View File

@@ -218,6 +218,12 @@ class Scene_Map
end
return if $game_temp.message_window_showing
if !pbMapInterpreterRunning?
if $game_temp.moving_furniture
placeFurnitureMenu() if Input.trigger?(Input::USE)
rotate__held_furniture_left if Input.trigger?(Input::JUMPDOWN)
rotate_held_furniture_right if Input.trigger?(Input::JUMPUP)
end
if Input.trigger?(Input::USE)
$PokemonTemp.hiddenMoveEventCalling = true
elsif Input.trigger?(Input::BACK)
@@ -225,7 +231,7 @@ class Scene_Map
$game_temp.menu_calling = true
$game_temp.menu_beep = true
dayOfWeek = getDayOfTheWeek().to_s
$scene.spriteset.addUserSprite(LocationWindow.new($game_map.name+ "\n"+ pbGetTimeNow.strftime("%I:%M %p") + "\n" + dayOfWeek))
$scene.spriteset.addUserSprite(LocationWindow.new($game_map.name+ "\n"+ pbGetTimeNow.strftime(_INTL("%I:%M %p")) + "\n" + dayOfWeek))
end
elsif Input.trigger?(Input::SPECIAL)
unless $game_system.menu_disabled || $game_player.moving?

View File

@@ -25,11 +25,6 @@ class Game_Temp
attr_accessor :fadestate # for sprite hashes
attr_accessor :background_bitmap
attr_accessor :mart_prices
attr_accessor :unimportedSprites
attr_accessor :nb_imported_sprites
attr_accessor :loading_screen
attr_accessor :custom_sprites_list
attr_accessor :base_sprites_list
#-----------------------------------------------------------------------------
# * Object Initialization
@@ -57,9 +52,6 @@ class Game_Temp
@message_window_showing = false
@transition_processing = false
@mart_prices = {}
@custom_sprites_list ={}
@base_sprites_list ={}
end
def clear_mart_prices

View File

@@ -12,8 +12,8 @@ class Game_Picture
attr_reader :number # picture number
attr_accessor :name # file name
attr_reader :origin # starting point
attr_reader :x # x-coordinate
attr_reader :y # y-coordinate
attr_accessor :x # x-coordinate
attr_accessor :y # y-coordinate
attr_accessor :zoom_x # x directional zoom rate
attr_accessor :zoom_y # y directional zoom rate
attr_accessor :opacity # opacity level

View File

@@ -25,7 +25,10 @@ class Game_Character
attr_reader :move_speed
attr_accessor :walk_anime
attr_writer :bob_height
attr_accessor :under_everything
attr_accessor :under_everything #under even grass
attr_accessor :under_player #always under the player, but over grass, etc.
attr_accessor :direction_fix
attr_accessor :always_on_top
def initialize(map = nil)
@map = map
@@ -77,6 +80,7 @@ class Game_Character
@locked = false
@prelock_direction = 0
@under_everything=false
@under_player = false
@forced_z=nil
end
@@ -339,6 +343,7 @@ class Game_Character
def screen_z(height = 0)
return -1 if @under_everything
return 0 if @under_player
return 999 if @always_on_top
return @forced_z if @forced_z
z = screen_y_ground
@@ -354,6 +359,17 @@ class Game_Character
return z + ((height > Game_Map::TILE_HEIGHT) ? Game_Map::TILE_HEIGHT - 1 : 0)
end
def opposite_direction
case @direction
when DIRECTION_LEFT; return DIRECTION_RIGHT
when DIRECTION_RIGHT; return DIRECTION_LEFT
when DIRECTION_UP; return DIRECTION_DOWN
when DIRECTION_DOWN; return DIRECTION_UP
else
return DIRECTION_ALL
end
end
#=============================================================================
# Movement
#=============================================================================
@@ -899,6 +915,27 @@ class Game_Character
@direction_fix = last_direction_fix
end
def jump_forward
case $game_player.direction
when DIRECTION_DOWN
x_direction = 0
y_direction = 1
when DIRECTION_UP
x_direction = 0
y_direction = -1
when DIRECTION_LEFT
x_direction = -1
y_direction = 0
when DIRECTION_RIGHT
x_direction = 1
y_direction = 0
else
x_direction = 0
y_direction = 0
end
jump(x_direction, y_direction)
end
def jump(x_plus, y_plus)
if x_plus != 0 || y_plus != 0
if x_plus.abs > y_plus.abs

View File

@@ -1,10 +1,13 @@
class Game_Event < Game_Character
attr_reader :map_id
attr_reader :trigger
attr_accessor :trigger
attr_reader :list
attr_reader :starting
attr_reader :tempSwitches # Temporary self-switches
attr_reader :character_name
attr_accessor :need_refresh
attr_accessor :opacity
attr_accessor :through
def initialize(map_id, event, map=nil)
super(map)

View File

@@ -33,40 +33,34 @@ class Game_Player < Game_Character
return moving? && !@move_route_forcing && pbCanRun?
end
#Override the player's graphics
# Path from Graphics/Characters/player/
def setPlayerGraphicsOverride(path)
@defaultCharacterName=path
end
def removeGraphicsOverride
@defaultCharacterName = ""
end
def hasGraphicsOverride?
return @defaultCharacterName!=""
end
def character_name
@defaultCharacterName = "" if !@defaultCharacterName
return @defaultCharacterName if @defaultCharacterName!=""
return @defaultCharacterName if hasGraphicsOverride?
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
player_is_moving = moving?
if pbCanRun? && (player_is_moving || @wasmoving) && Input.dir4!=0 && meta[4] && meta[4]!=""
charset = 4 # Display running character sprite
end
newCharName = pbGetPlayerCharset(meta,charset)
if newCharName
# echoln caller
# echoln newCharName
# echoln "moving: " + moving?.to_s
# echoln "was moving: " + @wasmoving.to_s
#
# echoln "can run: " + pbCanRun?.to_s
# echoln "Input.dir4 " + Input.dir4.to_s
#
#
# echoln (moving? || @wasmoving)
# echoln charset
# echoln ""
end
@character_name = newCharName if newCharName
@wasmoving = player_is_moving
end

View File

@@ -127,7 +127,9 @@ class Sprite_Wearable < RPG::Sprite
end
def update(action, filename,color)
@sprite.opacity = @player_sprite.opacity if @wearableBitmap
@sprite.opacity=0 if $game_player.hasGraphicsOverride?
if filename != @filename || color != @color
if pbResolveBitmap(filename)
#echoln pbResolveBitmap(filename)

View File

@@ -60,6 +60,13 @@ class Sprite_Player < Sprite_Character
@charbitmap.bitmap.clone #nekkid sprite
baseBitmap = @charbitmap.bitmap.clone #nekkid sprite
if $game_player.hasGraphicsOverride?
@hair.update(@character_name, "", 0) if @hair
@hat.update(@character_name, "", 0) if @hat
@hat2.update(@character_name, "", 0) if @hat2
return baseBitmap
end
outfitFilename = getOverworldOutfitFilename($Trainer.clothes, @character_name) #
outfitFilename = getOverworldOutfitFilename(Settings::PLAYER_TEMP_OUTFIT_FALLBACK) if !pbResolveBitmap(outfitFilename)
hairFilename = getOverworldHairFilename($Trainer.hair)

View File

@@ -78,6 +78,18 @@ class TilemapRenderer
},
5 => { #Rustboro
996 => "flowers_orange[10]",
991 => "flowers_pink[10]",
999 => "flowers_yellow[10]",
1007 => "flowers_blue[10]",
1015 => "flowers_purple[10]",
1023 => "flowers_red[10]",
1031 => "flowers_grey[10]",
1039 => "flowers_white[10]",
},
6 => { #Dewford Town
#water cliffs
@@ -92,7 +104,8 @@ class TilemapRenderer
},
7 => { #Sea Route
#water rocks
1173 => "water_rock_medium[15]",
#water cliffs
1363 => "water_rock10", 1364 => "water_rock11",
1389 => "water_rock01", 1391 => "water_rock09",
@@ -154,6 +167,38 @@ class TilemapRenderer
881 => "tree_sway_single_6",
866 => "tree_sway_group_1",
867 => "tree_sway_group_2",
874 => "tree_sway_group_3",
875 => "tree_sway_group_4",
},
5 => { #Rustboro
#trees
864 => "tree_sway_single_1",
865 => "tree_sway_single_2",
872 => "tree_sway_single_3",
873 => "tree_sway_single_4",
880 => "tree_sway_single_5",
881 => "tree_sway_single_6",
866 => "tree_sway_group_1",
867 => "tree_sway_group_2",
874 => "tree_sway_group_3",
875 => "tree_sway_group_4",
},
9 => { #Route Forest
#trees
864 => "tree_sway_single_1",
865 => "tree_sway_single_2",
872 => "tree_sway_single_3",
873 => "tree_sway_single_4",
880 => "tree_sway_single_5",
881 => "tree_sway_single_6",
866 => "tree_sway_group_1",
867 => "tree_sway_group_2",
874 => "tree_sway_group_3",

View File

@@ -129,22 +129,49 @@ class AnimatedBitmap
@bitmap.bitmap = new_bitmap
end
# def mirror
# for x in 0..@bitmap.bitmap.width / 2
# for y in 0..@bitmap.bitmap.height - 2
# temp = @bitmap.bitmap.get_pixel(x, y)
# newPix = @bitmap.bitmap.get_pixel((@bitmap.bitmap.width - x), y)
#
# @bitmap.bitmap.set_pixel(x, y, newPix)
# @bitmap.bitmap.set_pixel((@bitmap.bitmap.width - x), y, temp)
# end
# end
# end
def mirror
@bitmap.bitmap
mirror_horizontally
end
def mirror_horizontally
bmp = @bitmap.bitmap
half_width = bmp.width / 2
height = bmp.height
(0...half_width).each do |x|
(0...height).each do |y|
left_pixel = bmp.get_pixel(x, y)
right_pixel = bmp.get_pixel(bmp.width - 1 - x, y)
bmp.set_pixel(x, y, right_pixel)
bmp.set_pixel(bmp.width - 1 - x, y, left_pixel)
end
end
end
def mirror_vertically
bmp = @bitmap.bitmap
width = bmp.width
half_height = bmp.height / 2
(0...half_height).each do |y|
(0...width).each do |x|
top_pixel = bmp.get_pixel(x, y)
bottom_pixel = bmp.get_pixel(x, bmp.height - 1 - y)
bmp.set_pixel(x, y, bottom_pixel)
bmp.set_pixel(x, bmp.height - 1 - y, top_pixel)
end
end
end
# def mirror
# @bitmap.bitmap
# end
end

View File

@@ -396,9 +396,9 @@ def pbGetGoldString
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)
moneyString = "#{$game_party.gold} #{$data_system.words.gold}"
else
moneyString = _INTL("{1} {2}", $game_party.gold, Vocab.gold)
moneyString = "#{$game_party.gold} #{Vocab.gold}"
end
end
return moneyString
@@ -474,6 +474,8 @@ def pbDisplayHeartScalesWindow(msgwindow)
return pointswindow
end
def pbDisplayCoinsWindow(msgwindow, goldwindow)
coinString = ($Trainer) ? $Trainer.coins.to_s_formatted : "0"
coinwindow = Window_AdvancedTextPokemon.new(_INTL("Coins:\n<ar>{1}</ar>", coinString))
@@ -506,6 +508,22 @@ def pbDisplayBattlePointsWindow(msgwindow)
return pointswindow
end
def pbDisplayQuestPointsWindow(msgwindow)
pointsString = ($Trainer) ? $Trainer.quest_points.to_s_formatted : "0"
pointswindow = Window_AdvancedTextPokemon.new(_INTL("Quest Points:\n<ar>{1}</ar>", 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
else
pointswindow.y = 0
end
pointswindow.viewport = msgwindow.viewport
pointswindow.z = msgwindow.z
return pointswindow
end
#===============================================================================
#
#===============================================================================
@@ -594,6 +612,12 @@ def pbMessageDisplay(msgwindow, message, letterbyletter = true, commandProc = ni
text.gsub!(/\\pog/i, "")
text.gsub!(/\\b/i, "<c3=3050C8,D0D0C8>")
text.gsub!(/\\r/i, "<c3=E00808,D0D0C8>")
text.gsub!(/\\mu\[(.*?)\]/i) do
$Trainer && isPlayerMale() ? $1 : ""
end
text.gsub!(/\\fu\[(.*?)\]/i) do
$Trainer && isPlayerFemale() ? $1 : ""
end
text.gsub!(/\\[Ww]\[([^\]]*)\]/) {
w = $1.to_s
if w == ""
@@ -632,7 +656,7 @@ def pbMessageDisplay(msgwindow, message, letterbyletter = true, commandProc = ni
### Controls
textchunks = []
controls = []
while text[/(?:\\(f|ff|ts|cl|me|se|wt|wtnp|ch)\[([^\]]*)\]|\\(g|cn|pt|ft|hs|wd|wm|op|cl|wu|\.|\||\!|\^))/i]
while text[/(?:\\(f|ff|ts|cl|me|se|wt|wtnp|ch)\[([^\]]*)\]|\\(g|cn|pt|ft|qp|hs|wd|wm|op|cl|wu|\.|\||\!|\^))/i]
textchunks.push($~.pre_match)
if $~[1]
controls.push([$~[1].downcase, $~[2], -1])
@@ -757,6 +781,9 @@ def pbMessageDisplay(msgwindow, message, letterbyletter = true, commandProc = ni
when "hs" # Display heartscakes
goldwindow.dispose if goldwindow
goldwindow = pbDisplayHeartScalesWindow(msgwindow)
when "qp" # Display quest points
goldwindow.dispose if goldwindow
goldwindow = pbDisplayQuestPointsWindow(msgwindow)
when "cn" # Display coins window
coinwindow.dispose if coinwindow
coinwindow = pbDisplayCoinsWindow(msgwindow, goldwindow)

View File

@@ -101,7 +101,6 @@ end
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbMEPlay(param,volume=nil,pitch=nil)
echoln param
return if !param
param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name!=""

View File

@@ -54,14 +54,12 @@ module GameData
end
if !self::DATA.has_key?(other)
#echoln _INTL("Unknown ID {1}.", other)
return self::get(:PIKACHU)
if self == GameData::Item
return nil
else
return self::get(:PIKACHU)
end
end
#if other == :Species
# end
return self::DATA[other]
end

View File

@@ -27,10 +27,16 @@ module GameData
attr_reader :flowerYellow
attr_reader :flowerBlue
attr_reader :flower
attr_reader :trashcan
attr_reader :sharpedoObstacle
attr_reader :underwater #only visible when diving
attr_reader :secretBase_tree
attr_reader :secretBase_cave
attr_reader :secretBase_bush
DATA = {}
extend ClassMethods
@@ -82,11 +88,20 @@ module GameData
@sharpedoObstacle = hash[:sharpedoObstacle] || false
@underwater = hash[:underwater] || false
@secretBase_tree = hash[:secretBase_tree] || false
@secretBase_cave = hash[:secretBase_cave] || false
@secretBase_bush = hash[:secretBase_bush] || false
end
def can_surf_freely
return @can_surf && !@waterfall && !@waterfall_crest
end
def can_secret_base
return false if Settings::GAME_ID != :IF_HOENN
return @secretBase_tree || @secretBase_cave || @secretBase_bush
end
end
end
@@ -299,4 +314,23 @@ GameData::TerrainTag.register({
:id_number => 28,
:battle_environment => :underwater,
:underwater => true,
})
GameData::TerrainTag.register({
:id => :Secretbase_Tree,
:id_number => 29,
:secretBase_tree => true,
})
GameData::TerrainTag.register({
:id => :Secretbase_Cave,
:id_number => 30,
:secretBase_cave => true,
})
GameData::TerrainTag.register({
:id => :Secretbase_Bush,
:id_number => 31,
:secretBase_bush => true,
})

View File

@@ -63,7 +63,7 @@ module GameData
# return (ret) ? ret : pbResolveBitmap("Graphics/Pokemon/Eggs/000")
# end
def self.egg_sprite_filename(species, form)
return "Graphics/Battlers/Eggs/000" if $PokemonSystem.use_custom_eggs
return "Graphics/Battlers/Eggs/000" if $PokemonSystem.hide_custom_eggs
dexNum = getDexNumberForSpecies(species)
bitmapFileName = sprintf("Graphics/Battlers/Eggs/%d", dexNum) rescue nil
if !pbResolveBitmap(bitmapFileName)

View File

@@ -197,7 +197,8 @@ module GameData
gym_type = GameData::Type.get(type_id)
while true
new_species = $game_switches[SWITCH_RANDOM_GYM_CUSTOMS] ? getSpecies(getNewCustomSpecies(old_species, customsList, bst_range)) : getSpecies(getNewSpecies(old_species, bst_range))
if new_species.hasType?(gym_type)
if new_species.hasType?(gym_type) || $game_switches[SWITCH_RANDOM_GYM_CUSTOMS] || $game_switches[SWITCH_LEGENDARY_MODE]
# Note: gym Type validation is handled in-house for legendary mode
return new_species
end
end
@@ -218,6 +219,9 @@ module GameData
end
end
new_species = generateRandomGymSpecies(species)
if !new_species
return species
end
if $game_switches[SWITCH_RANDOM_GYM_PERSIST_TEAMS]
add_generated_species_to_gym_array(new_species, trainerId)
end
@@ -392,7 +396,7 @@ module GameData
secondary_ability_index = pkmn.ability_index == 0 ? 1 : 0
pkmn.ability2_index = secondary_ability_index
pkmn.ability2 = pkmn.getAbilityList[secondary_ability_index][0]
#print _INTL("Primary: {1}, Secondary: {2}",pkmn.ability.id, pkmn.ability2.id)
#print "Primary: {1}, Secondary: {2}",pkmn.ability.id, pkmn.ability2.id
end
pkmn.gender = pkmn_data[:gender] || ((trainer.male?) ? 0 : 1)

View File

@@ -1,369 +1,5 @@
module GameData
class TrainerModern
attr_reader :id
attr_reader :id_number
attr_reader :trainer_type
attr_reader :real_name
attr_reader :version
attr_reader :items
attr_reader :real_lose_text
attr_reader :pokemon
DATA = {}
class TrainerModern < Trainer
DATA_FILENAME = "trainers_remix.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"],
}
extend ClassMethods
include InstanceMethods
# @param tr_type [Symbol, String]
# @param tr_name [String]
# @param tr_version [Integer, nil]
# @return [Boolean] whether the given other is defined as a self
def self.exists?(tr_type, tr_name, tr_version = 0)
validate tr_type => [Symbol, String]
validate tr_name => [String]
key = [tr_type.to_sym, tr_name, tr_version]
return !self::DATA[key].nil?
end
# @param tr_type [Symbol, String]
# @param tr_name [String]
# @param tr_version [Integer, nil]
# @return [self]
def self.get(tr_type, tr_name, tr_version = 0)
validate tr_type => [Symbol, String]
validate tr_name => [String]
key = [tr_type.to_sym, tr_name, tr_version]
raise "Unknown trainer #{tr_type} #{tr_name} #{tr_version}." unless self::DATA.has_key?(key)
return self::DATA[key]
end
# @param tr_type [Symbol, String]
# @param tr_name [String]
# @param tr_version [Integer, nil]
# @return [self, nil]
def self.try_get(tr_type, tr_name, tr_version = 0)
validate tr_type => [Symbol, String]
validate tr_name => [String]
key = [tr_type.to_sym, tr_name, tr_version]
return (self::DATA.has_key?(key)) ? self::DATA[key] : nil
end
def self.list_all()
return self::DATA
end
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
@items = hash[:items] || []
@real_lose_text = hash[:lose_text] || "..."
@pokemon = hash[:pokemon] || []
@pokemon.each do |pkmn|
GameData::Stat.each_main do |s|
pkmn[:iv][s.id] ||= 0 if pkmn[:iv]
pkmn[:ev][s.id] ||= 0 if pkmn[:ev]
end
end
end
# @return [String] the translated name of this trainer
def name
return pbGetMessageFromHash(MessageTypes::TrainerNames, @real_name)
end
# @return [String] the translated in-battle lose message of this trainer
def lose_text
return pbGetMessageFromHash(MessageTypes::TrainerLoseText, @real_lose_text)
end
def replace_species_with_placeholder(species)
case species
when Settings::RIVAL_STARTER_PLACEHOLDER_SPECIES
return pbGet(Settings::RIVAL_STARTER_PLACEHOLDER_VARIABLE)
when Settings::VAR_1_PLACEHOLDER_SPECIES
return pbGet(1)
when Settings::VAR_2_PLACEHOLDER_SPECIES
return pbGet(2)
when Settings::VAR_3_PLACEHOLDER_SPECIES
return pbGet(3)
end
end
def generateRandomChampionSpecies(old_species)
customsList = getCustomSpeciesList()
bst_range = pbGet(VAR_RANDOMIZER_TRAINER_BST)
new_species = $game_switches[SWITCH_RANDOM_GYM_CUSTOMS] ? getSpecies(getNewCustomSpecies(old_species, customsList, bst_range)) : getSpecies(getNewSpecies(old_species, bst_range))
#every pokemon should be fully evolved
evolved_species_id = getEvolution(new_species)
evolved_species_id = getEvolution(evolved_species_id)
evolved_species_id = getEvolution(evolved_species_id)
evolved_species_id = getEvolution(evolved_species_id)
return getSpecies(evolved_species_id)
end
def generateRandomGymSpecies(old_species)
gym_index = pbGet(VAR_CURRENT_GYM_TYPE)
return old_species if gym_index == -1
return generateRandomChampionSpecies(old_species) if gym_index == 999
type_id = pbGet(VAR_GYM_TYPES_ARRAY)[gym_index]
return old_species if type_id == -1
customsList = getCustomSpeciesList()
bst_range = pbGet(VAR_RANDOMIZER_TRAINER_BST)
gym_type = GameData::Type.get(type_id)
while true
new_species = $game_switches[SWITCH_RANDOM_GYM_CUSTOMS] ? getSpecies(getNewCustomSpecies(old_species, customsList, bst_range)) : getSpecies(getNewSpecies(old_species, bst_range))
if new_species.hasType?(gym_type)
return new_species
end
end
end
def replace_species_to_randomized_gym(species, trainerId, pokemonIndex)
if $PokemonGlobal.randomGymTrainersHash == nil
$PokemonGlobal.randomGymTrainersHash = {}
end
if $game_switches[SWITCH_RANDOM_GYM_PERSIST_TEAMS] && $PokemonGlobal.randomGymTrainersHash != nil
if $PokemonGlobal.randomGymTrainersHash[trainerId] != nil && $PokemonGlobal.randomGymTrainersHash[trainerId].length >= $PokemonGlobal.randomTrainersHash[trainerId].length
return getSpecies($PokemonGlobal.randomGymTrainersHash[trainerId][pokemonIndex])
end
end
new_species = generateRandomGymSpecies(species)
if $game_switches[SWITCH_RANDOM_GYM_PERSIST_TEAMS]
add_generated_species_to_gym_array(new_species, trainerId)
end
return new_species
end
def add_generated_species_to_gym_array(new_species, trainerId)
if (new_species.is_a?(Symbol))
id = new_species
else
id = new_species.id_number
end
expected_team_length = 1
expected_team_length = $PokemonGlobal.randomTrainersHash[trainerId].length if $PokemonGlobal.randomTrainersHash[trainerId]
new_team = []
if $PokemonGlobal.randomGymTrainersHash[trainerId]
new_team = $PokemonGlobal.randomGymTrainersHash[trainerId]
end
if new_team.length < expected_team_length
new_team << id
end
$PokemonGlobal.randomGymTrainersHash[trainerId] = new_team
end
def replace_species_to_randomized_regular(species, trainerId, pokemonIndex)
if $PokemonGlobal.randomTrainersHash[trainerId] == nil
Kernel.pbMessage(_INTL("The trainers need to be re-shuffled."))
Kernel.pbShuffleTrainers()
end
new_species_dex = $PokemonGlobal.randomTrainersHash[trainerId][pokemonIndex]
return getSpecies(new_species_dex)
end
def isGymBattle
return ($game_switches[SWITCH_RANDOM_TRAINERS] && ($game_variables[VAR_CURRENT_GYM_TYPE] != -1) || ($game_switches[SWITCH_FIRST_RIVAL_BATTLE] && $game_switches[SWITCH_RANDOM_STARTERS]))
end
def replace_species_to_randomized(species, trainerId, pokemonIndex)
return species if $game_switches[SWITCH_FIRST_RIVAL_BATTLE]
return species if $game_switches[SWITCH_DONT_RANDOMIZE]
if isGymBattle() && $game_switches[SWITCH_RANDOMIZE_GYMS_SEPARATELY]
return replace_species_to_randomized_gym(species, trainerId, pokemonIndex)
end
return replace_species_to_randomized_regular(species, trainerId, pokemonIndex)
end
def replaceSingleSpeciesModeIfApplicable(species)
if $game_switches[SWITCH_SINGLE_POKEMON_MODE]
if $game_switches[SWITCH_SINGLE_POKEMON_MODE_HEAD]
return replaceFusionsHeadWithSpecies(species)
elsif $game_switches[SWITCH_SINGLE_POKEMON_MODE_BODY]
return replaceFusionsBodyWithSpecies(species)
elsif $game_switches[SWITCH_SINGLE_POKEMON_MODE_RANDOM]
if (rand(2) == 0)
return replaceFusionsHeadWithSpecies(species)
else
return replaceFusionsBodyWithSpecies(species)
end
end
end
return species
end
def replaceFusionsHeadWithSpecies(species)
speciesId = getDexNumberForSpecies(species)
if speciesId > NB_POKEMON
bodyPoke = getBodyID(speciesId)
headPoke = pbGet(VAR_SINGLE_POKEMON_MODE)
newSpecies = bodyPoke * NB_POKEMON + headPoke
return getPokemon(newSpecies)
end
return species
end
def replaceFusionsBodyWithSpecies(species)
speciesId = getDexNumberForSpecies(species)
if speciesId > NB_POKEMON
bodyPoke = pbGet(VAR_SINGLE_POKEMON_MODE)
headPoke = getHeadID(species)
newSpecies = bodyPoke * NB_POKEMON + headPoke
return getPokemon(newSpecies)
end
return species
end
def to_trainer
placeholder_species = [Settings::RIVAL_STARTER_PLACEHOLDER_SPECIES,
Settings::VAR_1_PLACEHOLDER_SPECIES,
Settings::VAR_2_PLACEHOLDER_SPECIES,
Settings::VAR_3_PLACEHOLDER_SPECIES]
# Determine trainer's name
tr_name = self.name
Settings::RIVAL_NAMES.each do |rival|
next if rival[0] != @trainer_type || !$game_variables[rival[1]].is_a?(String)
tr_name = $game_variables[rival[1]]
break
end
# Create trainer object
trainer = NPCTrainer.new(tr_name, @trainer_type)
trainer.id = $Trainer.make_foreign_ID
trainer.items = @items.clone
trainer.lose_text = self.lose_text
isRematch = $game_switches[SWITCH_IS_REMATCH]
isPlayingRandomized = $game_switches[SWITCH_RANDOM_TRAINERS] && !$game_switches[SWITCH_FIRST_RIVAL_BATTLE]
rematchId = getRematchId(trainer.name, trainer.trainer_type)
# Create each Pokémon owned by the trainer
index = 0
@pokemon.each do |pkmn_data|
#replace placeholder species infinite fusion edit
species = GameData::Species.get(pkmn_data[:species]).species
original_species = species
if placeholder_species.include?(species)
species = replace_species_with_placeholder(species)
else
species = replace_species_to_randomized(species, self.id, index) if isPlayingRandomized
end
species = replaceSingleSpeciesModeIfApplicable(species)
if $game_switches[SWITCH_REVERSED_MODE]
species = reverseFusionSpecies(species)
end
level = pkmn_data[:level]
if $game_switches[SWITCH_GAME_DIFFICULTY_HARD]
level = (level * Settings::HARD_MODE_LEVEL_MODIFIER).ceil
if level > Settings::MAXIMUM_LEVEL
level = Settings::MAXIMUM_LEVEL
end
end
if $game_switches[Settings::OVERRIDE_BATTLE_LEVEL_SWITCH]
override_level = $game_variables[Settings::OVERRIDE_BATTLE_LEVEL_VALUE_VAR]
if override_level.is_a?(Integer)
level = override_level
end
end
####
#trainer rematch infinite fusion edit
if isRematch
nbRematch = getNumberRematch(rematchId)
level = getRematchLevel(level, nbRematch)
species = evolveRematchPokemon(nbRematch, species)
end
pkmn = Pokemon.new(species, level, trainer, false)
trainer.party.push(pkmn)
# Set Pokémon's properties if defined
if pkmn_data[:form]
pkmn.forced_form = pkmn_data[:form] if MultipleForms.hasFunction?(species, "getForm")
pkmn.form_simple = pkmn_data[:form]
end
if $game_switches[SWITCH_RANDOM_HELD_ITEMS]
pkmn.item = pbGetRandomHeldItem().id
else
pkmn.item = pkmn_data[:item]
end
if pkmn_data[:moves] && pkmn_data[:moves].length > 0 && original_species == species
pkmn_data[:moves].each { |move| pkmn.learn_move(move) }
else
pkmn.reset_moves
end
pkmn.ability_index = pkmn_data[:ability_index]
pkmn.ability = pkmn_data[:ability]
pkmn.gender = pkmn_data[:gender] || ((trainer.male?) ? 0 : 1)
pkmn.shiny = (pkmn_data[: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)
end
GameData::Stat.each_main do |s|
if pkmn_data[:iv]
pkmn.iv[s.id] = pkmn_data[:iv][s.id]
else
pkmn.iv[s.id] = [pkmn_data[:level] / 2, Pokemon::IV_STAT_LIMIT].min
end
if pkmn_data[:ev]
pkmn.ev[s.id] = pkmn_data[:ev][s.id]
else
pkmn.ev[s.id] = [pkmn_data[:level] * 3 / 2, Pokemon::EV_LIMIT / 6].min
end
end
pkmn.happiness = pkmn_data[:happiness] if pkmn_data[:happiness]
pkmn.name = pkmn_data[:name] if pkmn_data[:name] && !pkmn_data[:name].empty?
if pkmn_data[:shadowness]
pkmn.makeShadow
pkmn.update_shadow_moves(true)
pkmn.shiny = false
end
pkmn.poke_ball = pkmn_data[:poke_ball] if pkmn_data[:poke_ball]
pkmn.calc_stats
index += 1
end
return 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
end

View File

@@ -2542,7 +2542,7 @@ end
# class PokeBattle_Move_XXX < PokeBattle_Move
# def pbMoveFailed?(user,targets)
# if targets[0].effects[PBEffects::Transform]
# @battle.pbDisplay(_INTL("But it failed!"))
# @battle.pbDisplay("But it failed!")
# return true
# end
# return false
@@ -2552,7 +2552,7 @@ end
# if target.effects[PBEffects::Transform] ||
# target.effects[PBEffects::Illusion] ||
# !target.pokemon.isFusion?
# @battle.pbDisplay(_INTL("But it failed!"))
# @battle.pbDisplay("But it failed!")
# return true
# end
# return false

View File

@@ -275,7 +275,7 @@ def pbHiddenPower(pkmn,forcedType=nil)
if Settings::MECHANICS_GENERATION <= 5
powerMin = 30
powerMax = 70
power |= (iv[:HP]&2)>>1
power = (iv[:HP]&2)>>1
power |= (iv[:ATTACK]&2)
power |= (iv[:DEFENSE]&2)<<1
power |= (iv[:SPEED]&2)<<2
@@ -2995,7 +2995,7 @@ class PokeBattle_Move_0EB < PokeBattle_Move
return true
end
# if @battle.wildBattle? && target.level>user.level
# @battle.pbDisplay(_INTL("But it failed!"))
# @battle.pbDisplay("But it failed!")
# return true
# end
if @battle.trainerBattle?

View File

@@ -70,7 +70,10 @@ module PokeBattle_BattleCommon
# 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
promptCaughtPokemonAction(pkmn)
gave_away_pokemon = promptGiveToPartner(pkmn) if isPartneredWithAnyTrainer()
promptCaughtPokemonAction(pkmn) if !gave_away_pokemon
if $game_switches[AUTOSAVE_CATCH_SWITCH]
Kernel.tryAutosave()
end
@@ -84,8 +87,8 @@ module PokeBattle_BattleCommon
# return pbStorePokemon(pokemon) if !$Trainer.party_full?
#
# while !pickedOption
# command = pbMessage(_INTL("\\ts[]Your team is full!"),
# [_INTL("Add to your party"), _INTL("Store to PC"),], 2)
# command = pbMessage("\\ts[]Your team is full!"),
# ["Add to your party", "Store to PC",], 2)
# echoln ("command " + command.to_s)
# case command
# when 0 #SWAP

View File

@@ -99,7 +99,9 @@ class PokeBattle_Battle
end
@scene = scene
@peer = PokeBattle_BattlePeer.create
@battleAI = PokeBattle_AI.new(self)
@battleAI = Settings::REMOTE_BATTLES_CONTROL ? RemotePokeBattle_AI.new(self) : PokeBattle_AI.new(self)
#TODO
@field = PokeBattle_ActiveField.new # Whole field (gravity/rooms)
@sides = [PokeBattle_ActiveSide.new, # Player's side
PokeBattle_ActiveSide.new] # Foe's side

View File

@@ -55,10 +55,10 @@ class PokeBattle_Battle
requireds[idxTrainer] += 1
end
# Compare the have values with the need values
if requireds.length>sideCounts.length
raise _INTL("Error: def pbGetOwnerIndexFromBattlerIndex gives invalid owner index ({1} for battle type {2}v{3}, trainers {4}v{5})",
requireds.length-1,@sideSizes[0],@sideSizes[1],side1counts.length,side2counts.length)
end
# if requireds.length>sideCounts.length
# raise "Error: def pbGetOwnerIndexFromBattlerIndex gives invalid owner index ({1} for battle type {2}v{3}, trainers {4}v{5}",
# requireds.length-1,@sideSizes[0],@sideSizes[1],side1counts.length,side2counts.length)
# end
sideCounts.each_with_index do |_count,i|
if !requireds[i] || requireds[i]==0
raise _INTL("Player-side trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)",

View File

@@ -179,7 +179,7 @@ class PokeBattle_Battle
dontAnimate = true
# debugInfo = "Levels: #{curLevel}->#{newLevel} | Exp: #{pkmn.exp}->#{expFinal} | gain: #{expGained}"
# raise RuntimeError.new(
# echoln _INTL("{1}'s new level is less than its\r\ncurrent level, which shouldn't happen.\r\n[Debug: {2}]",
# echoln "{1}'s new level is less than its\r\ncurrent level, which shouldn't happen.\r\n[Debug: {2}]",
# pkmn.name, debugInfo)
pbDisplayPaused(_INTL("{1}'s growth rate has changed to '{2}''. Its level will be adjusted to reflect its current exp.", pkmn.name, pkmn.growth_rate.real_name))
end

View File

@@ -74,7 +74,7 @@ class PokeBattle_Battle
end
# NOTE: Add your own Mega objects for particular NPC trainers here.
# if pbGetOwnerFromBattlerIndex(idxBattler).trainer_type == :BUGCATCHER
# return _INTL("Mega Net")
# return "Mega Net"
# end
return _INTL("Mega Ring")
end

View File

@@ -57,13 +57,13 @@ class PokeBattle_Battle
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 :Sun then pbDisplay("The sunlight is strong.")
# when :Rain then pbDisplay("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 :HarshSun then pbDisplay("The sunlight is extremely harsh.")
# when :HeavyRain then pbDisplay("It is raining heavily.")
# when :StrongWinds then pbDisplay("The wind is strong.")
when :ShadowSky then pbDisplay(_INTL("The shadow sky continues."))
end
# Effects due to weather

View File

@@ -8,11 +8,11 @@ class PokeBattle_AI
return false if !item
# Determine target of item (always the Pokémon choosing the action)
useType = GameData::Item.get(item).battle_use
if [1, 2, 3, 6, 7, 8].include?(useType) # Use on Pokémon
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
if [1, 2, 3, 6, 7, 8].include?(useType) # Use on Pokémon
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
end
# Register use of item
@battle.pbRegisterItem(idxBattler,item,idxTarget)
@battle.pbRegisterItem(idxBattler, item, idxTarget)
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use item #{GameData::Item.get(item).name}")
return true
end
@@ -22,33 +22,33 @@ class PokeBattle_AI
def pbEnemyItemToUse(idxBattler)
return nil if !@battle.internalBattle
items = @battle.pbGetOwnerItems(idxBattler)
return nil if !items || items.length==0
return nil if !items || items.length == 0
# Determine target of item (always the Pokémon choosing the action)
idxTarget = idxBattler # Battler using the item
idxTarget = idxBattler # Battler using the item
battler = @battle.battlers[idxTarget]
pkmn = battler.pokemon
# Item categories
hpItems = {
:POTION => 20,
:SUPERPOTION => 50,
:HYPERPOTION => 200,
:MAXPOTION => 999,
:BERRYJUICE => 20,
:SWEETHEART => 20,
:FRESHWATER => 50,
:SODAPOP => 60,
:LEMONADE => 80,
:MOOMOOMILK => 100,
:ORANBERRY => 10,
:SITRUSBERRY => battler.totalhp/4,
:ENERGYPOWDER => 50,
:ENERGYROOT => 200
:POTION => 20,
:SUPERPOTION => 50,
:HYPERPOTION => 200,
:MAXPOTION => 999,
:BERRYJUICE => 20,
:SWEETHEART => 20,
:FRESHWATER => 50,
:SODAPOP => 60,
:LEMONADE => 80,
:MOOMOOMILK => 100,
:ORANBERRY => 10,
:SITRUSBERRY => battler.totalhp / 4,
:ENERGYPOWDER => 50,
:ENERGYROOT => 200
}
hpItems[:RAGECANDYBAR] = 20 if !Settings::RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS
fullRestoreItems = [
:FULLRESTORE
:FULLRESTORE
]
oneStatusItems = [ # Preferred over items that heal all status problems
oneStatusItems = [# Preferred over items that heal all status problems
:AWAKENING, :CHESTOBERRY, :BLUEFLUTE,
:ANTIDOTE, :PECHABERRY,
:BURNHEAL, :RAWSTBERRY,
@@ -56,112 +56,112 @@ class PokeBattle_AI
:ICEHEAL, :ASPEARBERRY
]
allStatusItems = [
:FULLHEAL, :LAVACOOKIE, :OLDGATEAU, :CASTELIACONE, :LUMIOSEGALETTE,
:SHALOURSABLE, :BIGMALASADA, :LUMBERRY, :HEALPOWDER
:FULLHEAL, :LAVACOOKIE, :OLDGATEAU, :CASTELIACONE, :LUMIOSEGALETTE,
:SHALOURSABLE, :BIGMALASADA, :LUMBERRY, :HEALPOWDER
]
allStatusItems.push(:RAGECANDYBAR) if Settings::RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS
xItems = {
:XATTACK => [:ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XATTACK2 => [:ATTACK, 2],
:XATTACK3 => [:ATTACK, 3],
:XATTACK6 => [:ATTACK, 6],
:XDEFENSE => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XDEFENSE2 => [:DEFENSE, 2],
:XDEFENSE3 => [:DEFENSE, 3],
:XDEFENSE6 => [:DEFENSE, 6],
:XDEFEND => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XDEFEND2 => [:DEFENSE, 2],
:XDEFEND3 => [:DEFENSE, 3],
:XDEFEND6 => [:DEFENSE, 6],
:XSPATK => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPATK2 => [:SPECIAL_ATTACK, 2],
:XSPATK3 => [:SPECIAL_ATTACK, 3],
:XSPATK6 => [:SPECIAL_ATTACK, 6],
:XSPECIAL => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPECIAL2 => [:SPECIAL_ATTACK, 2],
:XSPECIAL3 => [:SPECIAL_ATTACK, 3],
:XSPECIAL6 => [:SPECIAL_ATTACK, 6],
:XSPDEF => [:SPECIAL_DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPDEF2 => [:SPECIAL_DEFENSE, 2],
:XSPDEF3 => [:SPECIAL_DEFENSE, 3],
:XSPDEF6 => [:SPECIAL_DEFENSE, 6],
:XSPEED => [:SPEED, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPEED2 => [:SPEED, 2],
:XSPEED3 => [:SPEED, 3],
:XSPEED6 => [:SPEED, 6],
:XACCURACY => [:ACCURACY, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XACCURACY2 => [:ACCURACY, 2],
:XACCURACY3 => [:ACCURACY, 3],
:XACCURACY6 => [:ACCURACY, 6]
:XATTACK => [:ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XATTACK2 => [:ATTACK, 2],
:XATTACK3 => [:ATTACK, 3],
:XATTACK6 => [:ATTACK, 6],
:XDEFENSE => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XDEFENSE2 => [:DEFENSE, 2],
:XDEFENSE3 => [:DEFENSE, 3],
:XDEFENSE6 => [:DEFENSE, 6],
:XDEFEND => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XDEFEND2 => [:DEFENSE, 2],
:XDEFEND3 => [:DEFENSE, 3],
:XDEFEND6 => [:DEFENSE, 6],
:XSPATK => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPATK2 => [:SPECIAL_ATTACK, 2],
:XSPATK3 => [:SPECIAL_ATTACK, 3],
:XSPATK6 => [:SPECIAL_ATTACK, 6],
:XSPECIAL => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPECIAL2 => [:SPECIAL_ATTACK, 2],
:XSPECIAL3 => [:SPECIAL_ATTACK, 3],
:XSPECIAL6 => [:SPECIAL_ATTACK, 6],
:XSPDEF => [:SPECIAL_DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPDEF2 => [:SPECIAL_DEFENSE, 2],
:XSPDEF3 => [:SPECIAL_DEFENSE, 3],
:XSPDEF6 => [:SPECIAL_DEFENSE, 6],
:XSPEED => [:SPEED, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPEED2 => [:SPEED, 2],
:XSPEED3 => [:SPEED, 3],
:XSPEED6 => [:SPEED, 6],
:XACCURACY => [:ACCURACY, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XACCURACY2 => [:ACCURACY, 2],
:XACCURACY3 => [:ACCURACY, 3],
:XACCURACY6 => [:ACCURACY, 6]
}
# Determine lost HP
losthp = battler.totalhp - battler.hp
preferFullRestore = (battler.hp <= battler.totalhp * 2 / 3 &&
(battler.status != :NONE || battler.effects[PBEffects::Confusion] > 0))
# Decide if Full Restore is actually preferred
preferFullRestore = (battler.hp <= battler.totalhp / 2 &&
(battler.status != :NONE || battler.effects[PBEffects::Confusion] > 0))
# Find all usable items
usableHPItems = []
usableHPItems = []
usableStatusItems = []
usableXItems = []
usableXItems = []
items.each do |i|
next if !i
next if !@battle.pbCanUseItemOnPokemon?(i,pkmn,battler,@battle.scene,false)
next if !ItemHandlers.triggerCanUseInBattle(i,pkmn,battler,nil,
false,self,@battle.scene,false)
# Log HP healing items
if losthp > 0
power = hpItems[i]
if power
usableHPItems.push([i, 5, power])
next
end
next if !@battle.pbCanUseItemOnPokemon?(i, pkmn, battler, @battle.scene, false)
next if !ItemHandlers.triggerCanUseInBattle(i, pkmn, battler, nil, false, self, @battle.scene, false)
itemQuantity = @battle.pbGetOwnerItems(idxBattler).count(i)
# Healing items (potions, berries, etc.)
if hpItems[i] && losthp > 0
priority = 5
# Use lower priority if only 1 or 2 left
priority += 3 if itemQuantity <= 2
usableHPItems.push([i, priority, hpItems[i]])
next
end
# Log Full Restores (HP healer and status curer)
if losthp > 0 || battler.status != :NONE
if fullRestoreItems.include?(i)
usableHPItems.push([i, (preferFullRestore) ? 3 : 7, 999])
usableStatusItems.push([i, (preferFullRestore) ? 3 : 9])
next
# Full Restore items
if fullRestoreItems.include?(i)
if losthp >= battler.totalhp / 4 || battler.status != :NONE || battler.effects[PBEffects::Confusion] > 0
# Only consider Full Restore if HP is below 25% or has status/confusion
priority = preferFullRestore ? 3 : 7
# Raise priority if stock is low to discourage waste
priority += 5 if itemQuantity <= 2
usableHPItems.push([i, priority, battler.totalhp])
usableStatusItems.push([i, preferFullRestore ? 3 : 9])
end
next
end
# Log single status-curing items
if oneStatusItems.include?(i)
# Single-status curers
if oneStatusItems.include?(i) && battler.status != :NONE
usableStatusItems.push([i, 5])
next
end
# Log Full Heal-type items
if allStatusItems.include?(i)
# Full heal-type items
if allStatusItems.include?(i) && battler.status != :NONE
usableStatusItems.push([i, 7])
next
end
# Log stat-raising items
# Stat-raising items
if xItems[i]
data = xItems[i]
usableXItems.push([i, battler.stages[data[0]], data[1]])
next
end
end
# Prioritise using a HP restoration item
if usableHPItems.length>0 && (battler.hp<=battler.totalhp/4 ||
(battler.hp<=battler.totalhp/2 && pbAIRandom(100)<30))
usableHPItems.sort! { |a,b| (a[1]==b[1]) ? a[2]<=>b[2] : a[1]<=>b[1] }
# Prioritise using HP items (including Full Restore if really needed)
if usableHPItems.length > 0 && (battler.hp <= battler.totalhp / 4 ||
(battler.hp <= battler.totalhp / 2 && pbAIRandom(100) < 30))
usableHPItems.sort! { |a, b| (a[1] == b[1]) ? a[2] <=> b[2] : a[1] <=> b[1] }
prevItem = nil
usableHPItems.each do |i|
return i[0], idxTarget if i[2]>=losthp
prevItem = i
end
return prevItem[0], idxTarget
end
# Next prioritise using a status-curing item
if usableStatusItems.length>0 && pbAIRandom(100)<40
usableStatusItems.sort! { |a,b| a[1]<=>b[1] }
return usableStatusItems[0][0], idxTarget
end
# Next try using an X item
if usableXItems.length>0 && pbAIRandom(100)<30
usableXItems.sort! { |a,b| (a[1]==b[1]) ? a[2]<=>b[2] : a[1]<=>b[1] }
prevItem = nil
usableXItems.each do |i|
break if prevItem && i[1]>prevItem[1]
return i[0], idxTarget if i[1]+i[2]>=6
return i[0], idxTarget if i[2] >= losthp
prevItem = i
end
return prevItem[0], idxTarget

View File

@@ -4,77 +4,77 @@ class PokeBattle_AI
# chosen)
#=============================================================================
def pbChooseMoves(idxBattler)
user = @battle.battlers[idxBattler]
user = @battle.battlers[idxBattler]
wildBattler = (@battle.wildBattle? && @battle.opposes?(idxBattler))
skill = 0
skill = 0
if !wildBattler
skill = @battle.pbGetOwnerFromBattlerIndex(user.index).skill_level || 0
skill = @battle.pbGetOwnerFromBattlerIndex(user.index).skill_level || 0
end
# Get scores and targets for each move
# NOTE: A move is only added to the choices array if it has a non-zero
# score.
choices = []
user.eachMoveWithIndex do |_m,i|
next if !@battle.pbCanChooseMove?(idxBattler,i,false)
choices = []
user.eachMoveWithIndex do |_m, i|
next if !@battle.pbCanChooseMove?(idxBattler, i, false)
if wildBattler
pbRegisterMoveWild(user,i,choices)
pbRegisterMoveWild(user, i, choices)
else
pbRegisterMoveTrainer(user,i,choices,skill)
pbRegisterMoveTrainer(user, i, choices, skill)
end
end
# Figure out useful information about the choices
totalScore = 0
maxScore = 0
maxScore = 0
choices.each do |c|
totalScore += c[1]
maxScore = c[1] if maxScore<c[1]
maxScore = c[1] if maxScore < c[1]
end
# Log the available choices
if $INTERNAL
logMsg = "[AI] Move choices for #{user.pbThis(true)} (#{user.index}): "
choices.each_with_index do |c,i|
choices.each_with_index do |c, i|
logMsg += "#{user.moves[c[0]].name}=#{c[1]}"
logMsg += " (target #{c[2]})" if c[2]>=0
logMsg += ", " if i<choices.length-1
logMsg += " (target #{c[2]})" if c[2] >= 0
logMsg += ", " if i < choices.length - 1
end
PBDebug.log(logMsg)
end
# Find any preferred moves and just choose from them
if !wildBattler && skill>=PBTrainerAI.highSkill && maxScore>100
if !wildBattler && skill >= PBTrainerAI.highSkill && maxScore > 100
stDev = pbStdDev(choices)
if stDev>=40 && pbAIRandom(100)<90
if stDev >= 40 && pbAIRandom(100) < 90
preferredMoves = []
choices.each do |c|
next if c[1]<200 && c[1]<maxScore*0.8
next if c[1] < 200 && c[1] < maxScore * 0.8
preferredMoves.push(c)
preferredMoves.push(c) if c[1]==maxScore # Doubly prefer the best move
preferredMoves.push(c) if c[1] == maxScore # Doubly prefer the best move
end
if preferredMoves.length>0
if preferredMoves.length > 0
m = preferredMoves[pbAIRandom(preferredMoves.length)]
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) prefers #{user.moves[m[0]].name}")
@battle.pbRegisterMove(idxBattler,m[0],false)
@battle.pbRegisterTarget(idxBattler,m[2]) if m[2]>=0
@battle.pbRegisterMove(idxBattler, m[0], false)
@battle.pbRegisterTarget(idxBattler, m[2]) if m[2] >= 0
return
end
end
end
# Decide whether all choices are bad, and if so, try switching instead
if !wildBattler && skill>=PBTrainerAI.highSkill
if !wildBattler && skill >= PBTrainerAI.highSkill
badMoves = false
if (maxScore<=20 && user.turnCount>2) ||
(maxScore<=40 && user.turnCount>5)
badMoves = true if pbAIRandom(100)<80
if (maxScore <= 20 && user.turnCount > 2) ||
(maxScore <= 40 && user.turnCount > 5)
badMoves = true if pbAIRandom(100) < 80
end
if !badMoves && totalScore<100 && user.turnCount>1
if !badMoves && totalScore < 100 && user.turnCount > 1
badMoves = true
choices.each do |c|
next if !user.moves[c[0]].damagingMove?
badMoves = false
break
end
badMoves = false if badMoves && pbAIRandom(100)<10
badMoves = false if badMoves && pbAIRandom(100) < 10
end
if badMoves && pbEnemyShouldWithdrawEx?(idxBattler,true)
if badMoves && pbEnemyShouldWithdrawEx?(idxBattler, true)
if $INTERNAL
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will switch due to terrible moves")
end
@@ -82,13 +82,13 @@ class PokeBattle_AI
end
end
# If there are no calculated choices, pick one at random
if choices.length==0
if choices.length == 0
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) doesn't want to use any moves; picking one at random")
user.eachMoveWithIndex do |_m,i|
next if !@battle.pbCanChooseMove?(idxBattler,i,false)
choices.push([i,100,-1]) # Move index, score, target
user.eachMoveWithIndex do |_m, i|
next if !@battle.pbCanChooseMove?(idxBattler, i, false)
choices.push([i, 100, -1]) # Move index, score, target
end
if choices.length==0 # No moves are physically possible to use; use Struggle
if choices.length == 0 # No moves are physically possible to use; use Struggle
@battle.pbAutoChooseMove(user.index)
end
end
@@ -96,9 +96,9 @@ class PokeBattle_AI
randNum = pbAIRandom(totalScore)
choices.each do |c|
randNum -= c[1]
next if randNum>=0
@battle.pbRegisterMove(idxBattler,c[0],false)
@battle.pbRegisterTarget(idxBattler,c[2]) if c[2]>=0
next if randNum >= 0
@battle.pbRegisterMove(idxBattler, c[0], false)
@battle.pbRegisterTarget(idxBattler, c[2]) if c[2] >= 0
break
end
# Log the result
@@ -111,40 +111,40 @@ class PokeBattle_AI
# Get scores for the given move against each possible target
#=============================================================================
# Wild Pokémon choose their moves randomly.
def pbRegisterMoveWild(_user,idxMove,choices)
choices.push([idxMove,100,-1]) # Move index, score, target
def pbRegisterMoveWild(_user, idxMove, choices)
choices.push([idxMove, 100, -1]) # Move index, score, target
end
# Trainer Pokémon calculate how much they want to use each of their moves.
def pbRegisterMoveTrainer(user,idxMove,choices,skill)
def pbRegisterMoveTrainer(user, idxMove, choices, skill)
move = user.moves[idxMove]
target_data = move.pbTarget(user)
if target_data.num_targets > 1
# If move affects multiple battlers and you don't choose a particular one
totalScore = 0
@battle.eachBattler do |b|
next if !@battle.pbMoveCanTarget?(user.index,b.index,target_data)
score = pbGetMoveScore(move,user,b,skill)
next if !@battle.pbMoveCanTarget?(user.index, b.index, target_data)
score = pbGetMoveScore(move, user, b, skill)
totalScore += ((user.opposes?(b)) ? score : -score)
end
choices.push([idxMove,totalScore,-1]) if totalScore>0
choices.push([idxMove, totalScore, -1]) if totalScore > 0
elsif target_data.num_targets == 0
# If move has no targets, affects the user, a side or the whole field
score = pbGetMoveScore(move,user,user,skill)
choices.push([idxMove,score,-1]) if score>0
score = pbGetMoveScore(move, user, user, skill)
choices.push([idxMove, score, -1]) if score > 0
else
# If move affects one battler and you have to choose which one
scoresAndTargets = []
@battle.eachBattler do |b|
next if !@battle.pbMoveCanTarget?(user.index,b.index,target_data)
next if !@battle.pbMoveCanTarget?(user.index, b.index, target_data)
next if target_data.targets_foe && !user.opposes?(b)
score = pbGetMoveScore(move,user,b,skill)
scoresAndTargets.push([score,b.index]) if score>0
score = pbGetMoveScore(move, user, b, skill)
scoresAndTargets.push([score, b.index]) if score > 0
end
if scoresAndTargets.length>0
if scoresAndTargets.length > 0
# Get the one best target for the move
scoresAndTargets.sort! { |a,b| b[0]<=>a[0] }
choices.push([idxMove,scoresAndTargets[0][0],scoresAndTargets[0][1]])
scoresAndTargets.sort! { |a, b| b[0] <=> a[0] }
choices.push([idxMove, scoresAndTargets[0][0], scoresAndTargets[0][1]])
end
end
end
@@ -152,38 +152,38 @@ class PokeBattle_AI
#=============================================================================
# Get a score for the given move being used against the given target
#=============================================================================
def pbGetMoveScore(move,user,target,skill=100)
skill = PBTrainerAI.minimumSkill if skill<PBTrainerAI.minimumSkill
def pbGetMoveScore(move, user, target, skill = 100)
skill = PBTrainerAI.minimumSkill if skill < PBTrainerAI.minimumSkill
score = 100
score = pbGetMoveScoreFunctionCode(score,move,user,target,skill)
score = pbGetMoveScoreFunctionCode(score, move, user, target, skill)
# A score of 0 here means it absolutely should not be used
return 0 if score<=0
if skill>=PBTrainerAI.mediumSkill
return 0 if score <= 0
if skill >= PBTrainerAI.mediumSkill
# Prefer damaging moves if AI has no more Pokémon or AI is less clever
if @battle.pbAbleNonActiveCount(user.idxOwnSide)==0
if !(skill>=PBTrainerAI.highSkill && @battle.pbAbleNonActiveCount(target.idxOwnSide)>0)
if @battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
if !(skill >= PBTrainerAI.highSkill && @battle.pbAbleNonActiveCount(target.idxOwnSide) > 0)
if move.statusMove?
score /= 1.5
elsif target.hp<=target.totalhp/2
elsif target.hp <= target.totalhp / 2
score *= 1.5
end
end
end
# Don't prefer attacking the target if they'd be semi-invulnerable
if skill>=PBTrainerAI.highSkill && move.accuracy>0 &&
(target.semiInvulnerable? || target.effects[PBEffects::SkyDrop]>=0)
if skill >= PBTrainerAI.highSkill && move.accuracy > 0 &&
(target.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0)
miss = true
miss = false if user.hasActiveAbility?(:NOGUARD) || target.hasActiveAbility?(:NOGUARD)
if miss && pbRoughStat(user,:SPEED,skill)>pbRoughStat(target,:SPEED,skill)
if miss && pbRoughStat(user, :SPEED, skill) > pbRoughStat(target, :SPEED, skill)
# Knows what can get past semi-invulnerability
if target.effects[PBEffects::SkyDrop]>=0
if target.effects[PBEffects::SkyDrop] >= 0
miss = false if move.hitsFlyingTargets?
else
if target.inTwoTurnAttack?("0C9","0CC","0CE") # Fly, Bounce, Sky Drop
if target.inTwoTurnAttack?("0C9", "0CC", "0CE") # Fly, Bounce, Sky Drop
miss = false if move.hitsFlyingTargets?
elsif target.inTwoTurnAttack?("0CA") # Dig
elsif target.inTwoTurnAttack?("0CA") # Dig
miss = false if move.hitsDiggingTargets?
elsif target.inTwoTurnAttack?("0CB") # Dive
elsif target.inTwoTurnAttack?("0CB") # Dive
miss = false if move.hitsDivingTargets?
end
end
@@ -191,11 +191,15 @@ class PokeBattle_AI
score -= 80 if miss
end
# Pick a good move for the Choice items
if user.hasActiveItem?([:CHOICEBAND,:CHOICESPECS,:CHOICESCARF])
if move.baseDamage>=60; score += 60
elsif move.damagingMove?; score += 30
elsif move.function=="0F2"; score += 70 # Trick
else; score -= 60
if user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF])
if move.baseDamage >= 60;
score += 60
elsif move.damagingMove?;
score += 30
elsif move.function == "0F2";
score += 70 # Trick
else
; score -= 60
end
end
# If user is asleep, prefer moves that are usable while asleep
@@ -229,17 +233,18 @@ class PokeBattle_AI
end
# Adjust score based on how much damage it can deal
if move.damagingMove?
score = pbGetMoveScoreDamage(score,move,user,target,skill)
else # Status moves
score = pbGetMoveScoreDamage(score, move, user, target, skill)
else
# Status moves
# Don't prefer attacks which don't deal damage
score -= 10
# Account for accuracy of move
accuracy = pbRoughAccuracy(move,user,target,skill)
score *= accuracy/100.0
score = 0 if score<=10 && skill>=PBTrainerAI.highSkill
accuracy = pbRoughAccuracy(move, user, target, skill)
score *= accuracy / 100.0
score = 0 if score <= 10 && skill >= PBTrainerAI.highSkill
end
score = score.to_i
score = 0 if score<0
score = 0 if score < 0
return score
end
@@ -247,27 +252,27 @@ class PokeBattle_AI
# Add to a move's score based on how much damage it will deal (as a percentage
# of the target's current HP)
#=============================================================================
def pbGetMoveScoreDamage(score,move,user,target,skill)
def pbGetMoveScoreDamage(score, move, user, target, skill)
# Don't prefer moves that are ineffective because of abilities or effects
return 0 if score<=0 || pbCheckMoveImmunity(score,move,user,target,skill)
return 0 if score <= 0 || pbCheckMoveImmunity(score, move, user, target, skill)
# Calculate how much damage the move will do (roughly)
baseDmg = pbMoveBaseDamage(move,user,target,skill)
realDamage = pbRoughDamage(move,user,target,skill,baseDmg)
baseDmg = pbMoveBaseDamage(move, user, target, skill)
realDamage = pbRoughDamage(move, user, target, skill, baseDmg)
# Account for accuracy of move
accuracy = pbRoughAccuracy(move,user,target,skill)
realDamage *= accuracy/100.0
accuracy = pbRoughAccuracy(move, user, target, skill)
realDamage *= accuracy / 100.0
# Two-turn attacks waste 2 turns to deal one lot of damage
if move.chargingTurnMove? || move.function=="0C2" # Hyper Beam
realDamage *= 2/3 # Not halved because semi-invulnerable during use or hits first turn
if move.chargingTurnMove? || move.function == "0C2" # Hyper Beam
realDamage *= 2 / 3 # Not halved because semi-invulnerable during use or hits first turn
end
# Prefer flinching external effects (note that move effects which cause
# flinching are dealt with in the function code part of score calculation)
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
if !target.hasActiveAbility?(:INNERFOCUS) &&
!target.hasActiveAbility?(:SHIELDDUST) &&
target.effects[PBEffects::Substitute]==0
!target.hasActiveAbility?(:SHIELDDUST) &&
target.effects[PBEffects::Substitute] == 0
canFlinch = false
if move.canKingsRock? && user.hasActiveItem?([:KINGSROCK,:RAZORFANG])
if move.canKingsRock? && user.hasActiveItem?([:KINGSROCK, :RAZORFANG])
canFlinch = true
end
if user.hasActiveAbility?(:STENCH) && !move.flinchingMove?
@@ -277,14 +282,14 @@ class PokeBattle_AI
end
end
# Convert damage to percentage of target's remaining HP
damagePercentage = realDamage*100.0/target.hp
damagePercentage = realDamage * 100.0 / target.hp
# Don't prefer weak attacks
# damagePercentage /= 2 if damagePercentage<20
# damagePercentage /= 2 if damagePercentage<20
# Prefer damaging attack if level difference is significantly high
damagePercentage *= 1.2 if user.level-10>target.level
damagePercentage *= 1.2 if user.level - 10 > target.level
# Adjust score
damagePercentage = 120 if damagePercentage>120 # Treat all lethal moves the same
damagePercentage += 40 if damagePercentage>100 # Prefer moves likely to be lethal
damagePercentage = 120 if damagePercentage > 120 # Treat all lethal moves the same
damagePercentage += 40 if damagePercentage > 100 # Prefer moves likely to be lethal
score += damagePercentage.to_i
return score
end

View File

@@ -177,7 +177,8 @@ BallHandlers::ModifyCatchRate.add(:LEVELBALL,proc { |ball,catchRate,battle,battl
BallHandlers::ModifyCatchRate.add(:LUREBALL,proc { |ball,catchRate,battle,battler,ultraBeast|
multiplier = (Settings::NEW_POKE_BALL_CATCH_RATES) ? 5 : 3
catchRate *= multiplier if GameData::EncounterType.get($PokemonTemp.encounterType).type == :fishing
encounterType = GameData::EncounterType.try_get($PokemonTemp.encounterType)
catchRate *= multiplier if encounterType.is_a?(GameData::EncounterType) && encounterType.type == :fishing
next [catchRate,255].min
})

View File

@@ -93,9 +93,9 @@ class PokemonDataBox < SpriteWrapper
def initializeOtherGraphics(viewport)
# Create other bitmaps
@numbersBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/icon_numbers"))
@hpBarBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/overlay_hp"))
@expBarBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/overlay_exp"))
@numbersBitmap = AnimatedBitmap.new("Graphics/Pictures/Battle/icon_numbers")
@hpBarBitmap = AnimatedBitmap.new("Graphics/Pictures/Battle/overlay_hp")
@expBarBitmap = AnimatedBitmap.new("Graphics/Pictures/Battle/overlay_exp")
# Create sprite to draw HP numbers on
@hpNumbers = BitmapSprite.new(124,16,viewport)
pbSetSmallFont(@hpNumbers.bitmap)
@@ -235,9 +235,9 @@ class PokemonDataBox < SpriteWrapper
# Draw Pokémon's gender symbol
case @battler.displayGender
when 0 # Male
textPos.push([_INTL(""),@spriteBaseX+126,0,false,MALE_BASE_COLOR,MALE_SHADOW_COLOR])
textPos.push(["",@spriteBaseX+126,0,false,MALE_BASE_COLOR,MALE_SHADOW_COLOR])
when 1 # Female
textPos.push([_INTL(""),@spriteBaseX+126,0,false,FEMALE_BASE_COLOR,FEMALE_SHADOW_COLOR])
textPos.push(["",@spriteBaseX+126,0,false,FEMALE_BASE_COLOR,FEMALE_SHADOW_COLOR])
end
pbDrawTextPositions(self.bitmap,textPos)
# Draw Pokémon's level
@@ -450,7 +450,7 @@ class AbilitySplashBar < SpriteWrapper
@side = side
@battler = nil
# Create sprite wrapper that displays background graphic
@bgBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/ability_bar"))
@bgBitmap = AnimatedBitmap.new("Graphics/Pictures/Battle/ability_bar")
@bgSprite = SpriteWrapper.new(viewport)
@bgSprite.bitmap = @bgBitmap.bitmap
@bgSprite.src_rect.y = (side==0) ? 0 : @bgBitmap.height/2

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