6.6 update

This commit is contained in:
chardub
2025-06-07 08:16:50 -04:00
parent 295a71dbcd
commit a393ba1137
467 changed files with 171196 additions and 36566 deletions

View File

@@ -0,0 +1,51 @@
# frozen_string_literal: true
class Game_Temp
attr_accessor :water_plane
attr_accessor :water_plane2
end
alias originalCausticsMethod addWaterCausticsEffect
def addWaterCausticsEffect(fog_name = "caustic1", opacity = 16)
originalCausticsMethod(fog_name, 8)
if Settings::GAME_ID == :IF_HOENN && $PokemonGlobal.diving
if $game_temp.water_plane
$game_temp.water_plane.bitmap.dispose if $game_temp.water_plane.bitmap
$game_temp.water_plane.dispose
end
if $game_temp.water_plane2
$game_temp.water_plane2.bitmap.dispose if $game_temp.water_plane2.bitmap
$game_temp.water_plane2.dispose
end
$game_temp.water_plane = AnimatedPlane.new(Spriteset_Map.viewport)
$game_temp.water_plane.bitmap = RPG::Cache.picture("Dive/ocean_dive")
$game_temp.water_plane.z = -2
$game_temp.water_plane.opacity = 230
$game_temp.water_plane2 = AnimatedPlane.new(Spriteset_Map.viewport)
$game_temp.water_plane2.bitmap = RPG::Cache.picture("Dive/dive_dark2") # Different image if needed
$game_temp.water_plane2.z = 2
$game_temp.water_plane2.opacity = 210
end
end
class Spriteset_Map
alias pokemonEssentials_spritesetMap_update update
def update
pokemonEssentials_spritesetMap_update
if Settings::GAME_ID == :IF_HOENN && $PokemonGlobal.diving
@fog.z=-1 if @fog
@fog2.z=-1 if @fog2
end
end
end

View File

@@ -0,0 +1,42 @@
# frozen_string_literal: true
class PokemonEncounters
WEATHER_ENCOUNTER_BASE_CHANCE = 8 #/100 (for weather intensity of 0)
alias pokemonEssentials_PokemonEncounter_choose_wild_pokemon choose_wild_pokemon
def choose_wild_pokemon(enc_type, *args)
return pokemonEssentials_PokemonEncounter_choose_wild_pokemon(enc_type, *args) if !$game_weather
current_weather_type = $game_weather.get_map_weather_type($game_map.map_id)
current_weather_intensity = $game_weather.get_map_weather_intensity($game_map.map_id)
if can_substitute_for_weather_encounter(enc_type, current_weather_type)
#Chance to replace the chosen by one in from the weather pool
if roll_for_weather_encounter(current_weather_intensity)
weather_encounter_type = get_weather_encounter_type(enc_type,current_weather_type)
echoln "weather encounter!"
echoln weather_encounter_type
return pokemonEssentials_PokemonEncounter_choose_wild_pokemon(weather_encounter_type) if(weather_encounter_type)
end
end
return pokemonEssentials_PokemonEncounter_choose_wild_pokemon(enc_type, *args)
end
SUBSTITUTABLE_ENCOUNTER_TYPES = [:Land, :Land1, :Land2, :Land3, :Water]
def can_substitute_for_weather_encounter(encounter_type,current_weather)
return false if Settings::GAME_ID != :IF_HOENN
return false if !SUBSTITUTABLE_ENCOUNTER_TYPES.include?(encounter_type)
return false if current_weather.nil? || current_weather == :None
return true
end
def get_weather_encounter_type(normal_encounter_type, current_weather_type)
base_encounter_type = normal_encounter_type == :Water ? :Water : :Land
weather_encounter_type = "#{base_encounter_type}#{current_weather_type}".to_sym
return weather_encounter_type if GameData::EncounterType.exists?(weather_encounter_type)
return nil
end
def roll_for_weather_encounter(weather_intensity)
weather_encounter_chance = (WEATHER_ENCOUNTER_BASE_CHANCE * weather_intensity)+WEATHER_ENCOUNTER_BASE_CHANCE
return rand(100) < weather_encounter_chance
end
end

View File

@@ -0,0 +1,76 @@
GameData::EncounterType.register({
:id => :LandRain,
:type => :none,
:old_slots => [30,30,10,15,5,10]
})
GameData::EncounterType.register({
:id => :LandSunny,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :LandWind,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :LandFog,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :LandStorm,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :LandSnow,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :WaterRain,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :WaterSunny,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :WaterWind,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :WaterFog,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :WaterStorm,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})
GameData::EncounterType.register({
:id => :WaterSnow,
:type => :none,
:old_slots => [30,30,10,15,5,10],
})

View File

@@ -0,0 +1,96 @@
def update_neighbor_map
@neighbors_maps = generate_neighbor_map_from_town_map
@neighbors_maps = normalize_neighbors(@neighbors_maps)
end
def normalize_neighbors(map)
fixed_map = {}
map.each do |map_id, neighbors|
neighbors.each do |neighbor_id|
fixed_map[map_id] ||= []
fixed_map[neighbor_id] ||= []
fixed_map[map_id] |= [neighbor_id]
fixed_map[neighbor_id] |= [map_id]
end
end
fixed_map
end
def generate_neighbor_map_from_town_map
mapdata = pbLoadTownMapData
neighbor_map = {}
name_to_map_id = {}
name_grid = {}
# First, build:
# - a grid: [x, y] => location_name
# - a lookup: location_name => map_id (if one exists)
mapdata.each do |region|
maps_array = region[2]
maps_array.each do |entry|
x, y, name, _showname, map_id = entry
next unless name.is_a?(String) && !name.empty?
name_grid[[x, y]] = name
if map_id.is_a?(Integer) #&& !is_indoor_map?(map_id)
name_to_map_id[name] ||= map_id # Only keep the first valid one
end
end
end
# Now, check each tile against its neighbors (up/down/left/right)
name_grid.each do |(x, y), name|
[[0, -1], [0, 1], [-1, 0], [1, 0]].each do |dx, dy|
neighbor_coords = [x + dx, y + dy]
neighbor_name = name_grid[neighbor_coords]
map1 = name_to_map_id[name]
map2 = name_to_map_id[neighbor_name]
next unless map1 && map2
next if map1 == map2 # Prevent self-linking
neighbor_map[map1] ||= []
neighbor_map[map2] ||= []
neighbor_map[map1] << map2 unless neighbor_map[map1].include?(map2)
neighbor_map[map2] << map1 unless neighbor_map[map2].include?(map1)
end
end
return neighbor_map
end
def generate_neighbor_map_from_connections
raw_connections = MapFactoryHelper.getMapConnections
neighbor_map = {}
raw_connections.each_with_index do |conns, map_id|
next if conns.nil? || conns.empty?
conns.each do |conn|
next unless conn.is_a?(Array) && conn.length >= 4
map1 = conn[0]
map2 = conn[3]
next unless map1.is_a?(Integer) && map2.is_a?(Integer)
#next if is_indoor_map?(map1) || is_indoor_map?(map2)
neighbor_map[map1] ||= []
neighbor_map[map2] ||= []
neighbor_map[map1] << map2 unless neighbor_map[map1].include?(map2)
neighbor_map[map2] << map1 unless neighbor_map[map2].include?(map1)
end
end
return neighbor_map
end
def is_indoor_map?(map_id)
return false
mapMetadata = GameData::MapMetadata.try_get(map_id)
return true if !mapMetadata
return !mapMetadata.outdoor_map
end

View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
Events.onMapChange+= proc { |_old_map_id|
next if !$game_weather || !$game_weather.current_weather || !$game_weather.last_update_time
next if !$game_map
echoln pbGetTimeNow.to_i
update_overworld_weather($game_map.map_id)
next if $game_weather.last_update_time.to_i + GameWeather::TIME_BETWEEN_WEATHER_UPDATES > pbGetTimeNow.to_i
new_map_id = $game_map.map_id
mapMetadata = GameData::MapMetadata.try_get(new_map_id)
next if mapMetadata.nil?
$game_screen.weather(:None,0,0) if !mapMetadata.outdoor_map
next unless mapMetadata.outdoor_map
$game_weather.update_weather
}
def update_overworld_weather(current_map)
return if current_map.nil?
return if !$game_weather.current_weather
current_weather_array = $game_weather.current_weather[current_map]
return if current_weather_array.nil?
current_weather_type = current_weather_array[0]
current_weather_intensity = current_weather_array[1]
current_weather_type = :None if !current_weather_type
current_weather_intensity=0 if !current_weather_intensity
current_weather_type = :None if PBDayNight.isNight? && current_weather_type == :Sunny
$game_screen.weather(current_weather_type,current_weather_intensity,0)
end

View File

@@ -0,0 +1,131 @@
class BetterRegionMap
DEBUG_WEATHER = true
def update_weather_icon(location)
return
return nil if !location
map_id = location[4]
return nil if !map_id
weather_at_location = $game_weather.current_weather[map_id]
return nil if weather_at_location.nil?
weather_type = weather_at_location[0]
weather_intensity = weather_at_location[1]
icon = get_weather_icon(weather_type,weather_intensity)
return nil if icon.nil?
icon_path = "Graphics/Pictures/Weather/Cursor/" + icon
# @sprites["weather"].visible=true
@sprites["cursor"].bmp(icon_path)
@sprites["cursor"].src_rect.width = @sprites["cursor"].bmp.height
return weather_type
end
def draw_all_weather
processed_locations =[]
n=0
for x in 0...(@window["map"].bmp.width / TileWidth)
for y in 0...(@window["map"].bmp.height / TileHeight)
for location in @data[2]
if location[0] == x && location[1] == y
map_id = location[4]
next if !map_id
next if processed_locations.include?(map_id)
weather_at_location = $game_weather.current_weather[map_id]
next if weather_at_location.nil?
weather_type = weather_at_location[0]
weather_intensity = weather_at_location[1]
weather_icon = get_full_weather_icon_name(weather_type,weather_intensity)
next if weather_icon.nil?
weather_icon_path = "Graphics/Pictures/Weather/" + weather_icon
@weatherIcons["weather#{n}"] = Sprite.new(@mapvp)
@weatherIcons["weather#{n}"].bmp(weather_icon_path)
@weatherIcons["weather#{n}"].src_rect.width = @weatherIcons["weather#{n}"].bmp.height
@weatherIcons["weather#{n}"].x = TileWidth * x + (TileWidth / 2)
@weatherIcons["weather#{n}"].y = TileHeight * y + (TileHeight / 2)
@weatherIcons["weather#{n}"].oy = @weatherIcons["weather#{n}"].bmp.height / 2.0
@weatherIcons["weather#{n}"].ox = @weatherIcons["weather#{n}"].oy
processed_locations << map_id
n= n+1
end
end
end
end
end
def new_weather_cycle
return if !$game_weather
@weatherIcons.dispose
@weatherIcons = SpriteHash.new
$game_weather.update_weather
draw_all_weather
end
end
def get_current_map_weather_icon
return if !$game_weather
current_weather= $game_weather.current_weather[$game_map.map_id]
weather_type = current_weather[0]
weather_intensity = current_weather[1]
icon = get_full_weather_icon_name(weather_type,weather_intensity)
return "Graphics/Pictures/Weather/" +icon if icon
return nil
end
def get_weather_icon(weather_type,intensity)
case weather_type
when :Sunny #&& !PBDayNight.isNight?
icon_name = "mapSun"
when :Rain
icon_name = "mapRain"
when :Fog
icon_name = "mapFog"
when :Wind
icon_name = "mapWind"
when :Storm
icon_name = "mapStorm"
when :Sandstorm
icon_name = "mapSand"
when :Snow
icon_name = "mapSnow"
when :HeavyRain
icon_name = "mapHeavyRain"
when :StrongWinds
icon_name = "mapStrongWinds"
when :HarshSun
icon_name = "mapHarshSun"
else
icon_name = nil
end
return icon_name
end
def get_full_weather_icon_name(weather_type,intensity)
return nil if !weather_type
return nil if !intensity
same_intensity_weather_types = [:Sandstorm,:Snow,:StrongWinds,:HeavyRain,:HarshSun]
base_weather_icon_name = get_weather_icon(weather_type,intensity)
icon_name = base_weather_icon_name
return nil if !icon_name
return icon_name if same_intensity_weather_types.include?(weather_type)
if intensity <= 2
icon_name += "_light"
elsif intensity <=4
icon_name += "_medium"
else
icon_name += "_heavy"
end
return icon_name
end

View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
def isRaining?()
return isWeatherRain? || isWeatherStorm?
end
def isWeatherRain?()
return $game_weather.get_map_weather_type($game_map.map_id) == :Rain || $game_weather.get_map_weather_type($game_map.map_id) == :HeavyRain
end
def isWeatherSunny?()
return $game_weather.get_map_weather_type($game_map.map_id) == :Sunny || $game_weather.get_map_weather_type($game_map.map_id) == :HarshSun
end
def isWeatherStorm?()
return $game_weather.get_map_weather_type($game_map.map_id) == :Storm
end
def isWeatherWind?()
return $game_weather.get_map_weather_type($game_map.map_id) == :Wind || $game_weather.get_map_weather_type($game_map.map_id) == :StrongWinds
end
def isWeatherFog?()
return $game_weather.get_map_weather_type($game_map.map_id) == :Fog
end
def isWeatherSnow?()
return $game_weather.get_map_weather_type($game_map.map_id) == :Fog
end
def changeCurrentWeather(weatherType,intensity)
new_map_id = $game_map.map_id
mapMetadata = GameData::MapMetadata.try_get(new_map_id)
return nil if mapMetadata.nil?
return nil if !mapMetadata.outdoor_map
if $game_weather
$game_weather.set_map_weather($game_map.map_id,weatherType,intensity)
$game_weather.update_overworld_weather($game_map.map_id)
else
$game_screen.weather(weatherType,intensity,5)
end
end

View File

@@ -0,0 +1,376 @@
# Dynamic weather system by Chardub, for Pokemon Infinite Fusion
if Settings::GAME_ID == :IF_HOENN
SaveData.register(:weather) do
ensure_class :GameWeather
save_value { $game_weather }
load_value { |value|
if value.is_a?(GameWeather)
$game_weather = value
else
$game_weather = GameWeather.new
end
$game_weather.update_neighbor_map # reupdate the neighbors map to account for new maps added
$game_weather.initialize_weather unless $game_weather.current_weather
}
new_game_value { GameWeather.new }
end
end
class GameWeather
attr_accessor :current_weather
attr_accessor :last_update_time
TIME_BETWEEN_WEATHER_UPDATES = 12000 # 180 seconds, only actually changes once the player changes map
CHANCE_OF_NEW_WEATHER = 2 # /100 spontaneous new weather popping up somewhere
CHANCE_OF_RAIN = 40 #/100
CHANCE_OF_SUNNY = 30 #/100
CHANCE_OF_WINDY = 30 #/100
CHANCE_OF_FOG = 30 #/100 Only possible in the morning, otherwise, when rain and sun combine
MAX_INTENSITY_ON_NEW_WEATHER = 4
CHANCES_OF_INTENSITY_INCREASE = 30 # /100
CHANCES_OF_INTENSITY_DECREASE = 20 # /100
BASE_CHANCE_OF_WEATHER_SPREAD = 15
BASE_CHANCES_OF_WEATHER_END = 10 #/100 - For a weather intensity of 10. Chances should increase the lower the intensity is
BASE_CHANCES_OF_WEATHER_MOVE = 10
DEBUG_PROPAGATION = false
COLD_MAPS = [444] # Rain is snow on that map (shoal cave)
SNOW_LIMITS = [965,951] # Route 121, Pacifidlog - Snow turns to rain if it reaches these maps
SANDSTORM_MAPS = [555] # Always sandstorm, doesn't spread
SOOT_MAPS = [] # Always soot, doesn't spread
NO_WIND_MAPS = [989] # Sootopolis, Petalburg Forest
def set_weather(map_id, weather_type, intensity)
@current_weather[map_id] = [weather_type, intensity]
update_overworld_weather($game_map.map_id)
end
def map_current_weather_type(map_id)
map_weather = @current_weather[map_id]
return map_weather[0] if map_weather
end
def initialize
@last_update_time = pbGetTimeNow
echoln @last_update_time
# Similar to roaming legendaries: A hash of all the maps accessible from one map
@neighbors_maps = generate_neighbor_map_from_town_map
initialize_weather
end
def initialize_weather
weather = {}
@neighbors_maps.keys.each { |map_id|
weather[map_id] = select_new_weather_spawn
}
@current_weather = weather
end
def set_map_weather(map_id,weather_type,intensity)
@current_weather[map_id] = [weather_type,intensity]
end
def get_map_weather_type(map_id)
if !@current_weather[map_id]
@current_weather[map_id] = [:None,0]
end
return @current_weather[map_id][0]
end
def get_map_weather_intensity(map_id)
if !@current_weather[map_id]
@current_weather[map_id] = [:None,0]
end
return @current_weather[map_id][1]
end
#Legendary weather conditions can't dissapear, so they're treated as their full force counterpart for spreading
def normalize_legendary_weather(type, intensity)
case type
when :HarshSun then [:Sunny, 10]
when :HeavyRain then [:Rain, 10]
when :StrongWinds then [:Wind, 10]
else [type, intensity]
end
end
def update_weather()
return if !$game_weather
new_weather = @current_weather.dup
new_weather.each do |map_id, (type, intensity)|
try_end_weather(map_id,type, get_map_weather_intensity(map_id))
try_spawn_new_weather(map_id,type, intensity)
try_propagate_weather_to_neighbors(map_id,type, intensity)
echoln @current_weather[954] if @debug_you
try_move_weather_to_neighbors(map_id,type, intensity)
try_weather_intensity_decrease(map_id,type, intensity)
try_weather_intensity_increase(map_id,type, intensity)
end
update_overworld_weather($game_map.map_id)
@last_update_time = pbGetTimeNow
end
def try_propagate_weather_to_neighbors(map_id,propagating_map_weather_type,propagating_map_weather_intensity)
propagating_map_neighbors = @neighbors_maps[map_id]
return if propagating_map_weather_type == :None
return unless can_weather_spread(propagating_map_weather_type)
propagating_map_weather_type, propagating_map_weather_intensity = normalize_legendary_weather(propagating_map_weather_type, propagating_map_weather_intensity)
propagating_map_neighbors.each do |neighbor_id|
neighbor_weather_type = get_map_weather_type(neighbor_id)
neighbor_weather_intensity = get_map_weather_intensity(neighbor_id)
should_propagate = roll_for_weather_propagation(propagating_map_weather_type, propagating_map_weather_intensity, neighbor_weather_type, neighbor_weather_intensity)
next if !should_propagate
propagated_weather_type = resolve_weather_interaction(propagating_map_weather_type, neighbor_weather_type, propagating_map_weather_intensity, neighbor_weather_intensity)
propagated_weather_intensity = [propagating_map_weather_intensity - 1, 1].max
new_weather = get_updated_weather(propagated_weather_type, propagated_weather_intensity, neighbor_id)
@current_weather[neighbor_id] = new_weather
end
end
def try_spawn_new_weather(map_id,map_weather_type,weather_intensity)
return if map_weather_type != :None
new_weather = select_new_weather_spawn
@current_weather[map_id] = adjust_weather_for_map(new_weather,map_id)
end
def try_move_weather_to_neighbors(map_id,map_weather_type,weather_intensity)
map_neighbors = @neighbors_maps[map_id]
return if map_weather_type == :None || weather_intensity <= 1
return unless can_weather_spread(map_weather_type)
map_weather_type, weather_intensity = normalize_legendary_weather(map_weather_type, weather_intensity)
map_neighbors.each do |neighbor_id|
neighbor_weather_type = get_map_weather_type(neighbor_id)
neighbor_weather_intensity = get_map_weather_intensity(neighbor_id)
should_move_weather = roll_for_weather_move(map_weather_type)
next if !should_move_weather
next if neighbor_weather_type == map_weather_type && neighbor_weather_intensity >= weather_intensity
result_weather_type = resolve_weather_interaction(map_weather_type, neighbor_weather_type, weather_intensity, neighbor_weather_intensity)
result_weather_intensity = weather_intensity
new_weather = [result_weather_type,result_weather_intensity]
@current_weather[neighbor_id] = adjust_weather_for_map(new_weather,map_id)
end
end
def try_weather_intensity_decrease(map_id,map_weather_type,weather_intensity)
return unless can_weather_decrease(map_weather_type)
should_change_intensity = roll_for_weather_decrease(map_weather_type)
return if !should_change_intensity
new_weather = [map_weather_type,weather_intensity-1]
@current_weather[map_id] = adjust_weather_for_map(new_weather,map_id)
end
def try_weather_intensity_increase(map_id,map_weather_type,weather_intensity)
return unless can_weather_increase(map_weather_type)
should_change_intensity = roll_for_weather_increase(map_weather_type)
return if !should_change_intensity
new_weather = [map_weather_type,weather_intensity+1]
@current_weather[map_id] = adjust_weather_for_map(new_weather,map_id)
end
def try_end_weather(map_id,map_weather_type,weather_intensity)
return unless can_weather_end(map_weather_type)
should_weather_end = roll_for_weather_end(map_weather_type,weather_intensity)
return if !should_weather_end
if weather_intensity >1
map_weather_type = :Rain if map_weather_type == :Storm
new_weather = [map_weather_type,0]
else
new_weather = [:None,0]
end
@current_weather[map_id] = adjust_weather_for_map(new_weather,map_id)
end
def adjust_weather_for_map(map_current_weather,map_id)
type = map_current_weather[0]
intensity = map_current_weather[1]
return get_updated_weather(type,intensity,map_id)
end
def get_updated_weather(type, intensity, map_id)
if COLD_MAPS.include?(map_id)
type = :Snow if type == :Rain
type = :Blizzard if type == :Storm
type = :None if type == :Sunny
end
if SNOW_LIMITS.include?(map_id)
type = :Rain if type == :Snow
end
if SOOT_MAPS.include?(map_id)
type = :SootRain if type == :Rain
end
if NO_WIND_MAPS.include?(map_id)
type = :None if type == :Wind
end
if SANDSTORM_MAPS.include?(map_id)
type = :Sandstorm
intensity = 9
end
if (PBDayNight.isNight? || PBDayNight.isEvening?) && type == :Sunny
type = :None
intensity = 0
end
return [type, intensity]
end
def get_map_name(map_id)
mapinfos = pbLoadMapInfos
if mapinfos[map_id]
neighbor_map_name = mapinfos[map_id].name
else
neighbor_map_name = "Map #{map_id}"
end
return neighbor_map_name
end
def resolve_weather_interaction(incoming, existing, incoming_intensity, existing_intensity)
return existing unless can_weather_end(existing)
return incoming if existing == :None
return :Fog if incoming == :Rain && existing == :Sunny
return :Fog if incoming == :Sunny && existing == :Rain
if incoming == :Rain && existing == :Wind
return :Storm if incoming_intensity >= 5 || existing_intensity >= 5
end
return incoming
end
def print_current_weather()
mapinfos = pbLoadMapInfos
echoln "Current weather :"
@current_weather.each do |map_id, value|
game_map = mapinfos[map_id]
if game_map
map_name = mapinfos[map_id].name
else
map_name = map_id
end
echoln " #{map_name} : #{value}"
end
end
def can_weather_spread(type)
return false if type == :Sandstorm
return true
end
def can_weather_move(type)
return false if type == :Sandstorm
return false if type == :HeavyRain
return false if type == :HarshSun
return false if type == :StrongWinds
return true
end
def can_weather_end(type)
return false if type == :Sandstorm
return false if type == :HeavyRain
return false if type == :HarshSun
return false if type == :StrongWinds
# Sandstorm and special weathers for kyogre/groudon/rayquaza
return true
end
def can_weather_decrease(type)
return false if type == :Sandstorm
return false if type == :HeavyRain
return false if type == :HarshSun
return false if type == :StrongWinds
# Sandstorm and special weathers for kyogre/groudon/rayquaza
return true
end
def can_weather_increase(type)
return false if type == :Sandstorm
return false if type == :HeavyRain
return false if type == :HarshSun
return false if type == :StrongWinds
# Sandstorm and special weathers for kyogre/groudon/rayquaza
return true
end
def roll_for_weather_propagation(propagating_map_weather_type, propagating_map_weather_intensity, destination_map_weather_type, destination_map_weather_intensity)
if propagating_map_weather_type == destination_map_weather_type
# same weather, use highest intensity
intensity_diff = [propagating_map_weather_intensity,destination_map_weather_intensity].max
propagation_chance = (intensity_diff * BASE_CHANCE_OF_WEATHER_SPREAD)
else
intensity_diff = propagating_map_weather_intensity - destination_map_weather_intensity
if intensity_diff > 0
propagation_chance = (intensity_diff * BASE_CHANCE_OF_WEATHER_SPREAD)
else
return false# Other map's weather is stronger
end
end
return rand(100) < propagation_chance
end
def roll_for_weather_end(type, current_intensity)
return false if !can_weather_end(type)
chances = BASE_CHANCES_OF_WEATHER_END
chances += (10 - current_intensity) * 5
return rand(100) <= chances
end
def roll_for_weather_move(type)
return false if !can_weather_spread(type)
return rand(100) <= BASE_CHANCES_OF_WEATHER_MOVE
end
def roll_for_weather_increase(type)
return false if !can_weather_decrease(type)
return rand(100) <= CHANCES_OF_INTENSITY_INCREASE
end
def roll_for_weather_decrease(type)
return false if !can_weather_decrease(type)
return rand(100) <= CHANCES_OF_INTENSITY_DECREASE
end
def select_new_weather_spawn
return [:None, 0] if rand(100) >= CHANCE_OF_NEW_WEATHER
base_intensity = rand(MAX_INTENSITY_ON_NEW_WEATHER) + 1
weights = []
weights << [:Rain, CHANCE_OF_RAIN]
weights << [:Sunny, CHANCE_OF_SUNNY]
weights << [:Wind, CHANCE_OF_WINDY]
weights << [:Fog, CHANCE_OF_FOG] if PBDayNight.isMorning?
total = weights.sum { |w| w[1] }
roll = rand(total)
sum = 0
weights.each do |type, chance|
sum += chance
if roll < sum
intensity = (type == :Fog) ? base_intensity + 2 : base_intensity
return [type, intensity]
end
end
return [:None, 0] # Fallback
end
end

View File

@@ -0,0 +1,27 @@
# available channels
# :NEWS
# :WEATHER
#
def showTVText(channel = :NEWS)
case channel
when :NEWS
pbMessage(getTVNewsCaption())
end
end
SWITCH_REPORTER_AT_PETALBURG = 2026
def getTVNewsCaption()
if $game_switches[SWITCH_REPORTER_AT_PETALBURG]
return _INTL("It's showing the local news. There's a berry-growing contest going on in Petalburg Town!")
else
return _INTL("Its a rerun of PokéChef Deluxe. Nothing important on the news right now.")
end
end
def hoennSelectStarter
starters = [obtainStarter(0), obtainStarter(1), obtainStarter(2)]
selected_starter = StartersSelectionScene.new(starters).startScene
pbAddPokemonSilent(selected_starter)
return selected_starter
end

View File

@@ -0,0 +1,248 @@
# frozen_string_literal: true
HOENN_RIVAL_EVENT_NAME = "HOENN_RIVAL"
TEMPLATE_CHARACTER_FILE = "NPC_template"
class Player < Trainer
attr_accessor :rival_appearance
alias pokemonEssentials_player_initialize initialize
def initialize(*args)
pokemonEssentials_player_initialize(*args)
@rival_appearance = init_rival_appearance
end
def init_rival_appearance
if isPlayerMale
return TrainerAppearance.new(5,
HAT_MAY,
CLOTHES_MAY,
getFullHairId(HAIR_MAY,3) ,
0, 0, 0)
else
return TrainerAppearance.new(5,
HAT_BRENDAN,
CLOTHES_BRENDAN,
getFullHairId(HAIR_BRENDAN,3),
0, 0, 0)
end
end
def rival_appearance
@rival_appearance = init_rival_appearance if !@rival_appearance
return @rival_appearance
end
def rival_appearance=(value)
@rival_appearance = value
end
end
BATTLED_TRAINER_RIVAL_KEY = "rival"
def init_rival_name
rival_name = "Brendan" if isPlayerFemale
rival_name = "May" if isPlayerMale
pbSet(VAR_RIVAL_NAME,rival_name)
end
def set_rival_hat(hat)
$Trainer.rival_appearance = TrainerAppearance.new(
$Trainer.rival_appearance.skin_color,
hat,
$Trainer.rival_appearance.clothes,
$Trainer.rival_appearance.hair,
$Trainer.rival_appearance.hair_color,
$Trainer.rival_appearance.clothes_color,
$Trainer.rival_appearance.hat_color,
)
end
class Sprite_Character
alias PIF_typeExpert_checkModifySpriteGraphics checkModifySpriteGraphics
def checkModifySpriteGraphics(character)
PIF_typeExpert_checkModifySpriteGraphics(character)
return if character == $game_player
setSpriteToAppearance($Trainer.rival_appearance) if isPlayerFemale && character.name == HOENN_RIVAL_EVENT_NAME && character.character_name == TEMPLATE_CHARACTER_FILE
setSpriteToAppearance($Trainer.rival_appearance) if isPlayerMale && character.name == HOENN_RIVAL_EVENT_NAME && character.character_name == TEMPLATE_CHARACTER_FILE
end
end
def get_rival_starter
case get_rival_starter_type()
when :GRASS
return obtainStarter(0)
when :FIRE
return obtainStarter(1)
when :WATER
return obtainStarter(2)
else
#fallback, should not happen
return obtainStarter(0)
end
end
def get_rival_starter_type()
player_chosen_starter_index = pbGet(VAR_HOENN_CHOSEN_STARTER_INDEX)
case player_chosen_starter_index
when 0 #GRASS
return :FIRE
when 1 #FIRE
return :WATER
when 2 #WATER
return :GRASS
end
end
#Rival catches a Pokemon the same type as the player's starter and fuses it with their starter
def updateRivalTeamForSecondBattle()
rival_trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY]
rival_starter = rival_trainer.currentTeam[0]
rival_starter_species= rival_starter.species
player_chosen_starter_index = pbGet(VAR_HOENN_CHOSEN_STARTER_INDEX)
case player_chosen_starter_index
when 0 #GRASS
pokemon_species = getFusionSpeciesSymbol(:LOTAD, rival_starter_species) if isPlayerFemale()
pokemon_species = getFusionSpeciesSymbol(rival_starter_species,:SHROOMISH) if isPlayerMale()
when 1 #FIRE
pokemon_species = getFusionSpeciesSymbol(:SLUGMA,rival_starter_species) if isPlayerFemale()
pokemon_species = getFusionSpeciesSymbol(rival_starter_species,:NUMEL) if isPlayerMale()
when 2 #WATER
pokemon_species = getFusionSpeciesSymbol(rival_starter_species,:WINGULL) if isPlayerFemale()
pokemon_species = getFusionSpeciesSymbol(:WAILMER,rival_starter_species) if isPlayerMale()
end
team = []
team << Pokemon.new(pokemon_species,15)
rival_trainer.currentTeam = team
$PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY] = rival_trainer
end
# This sets up the rival's main team for the game
# Fir further battle, we can just add Pokemon and gain exp the same way as other
# trainer rematches
#
# Basically, rival catches a pokemon the type of their rival's starter - fuses it with their starters
# Has a team composed of fire/grass, water/grass, water/fire pokemon
def updateRivalTeamForThirdBattle()
rival_trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY]
rival_starter = rival_trainer.currentTeam[0]
starter_species= rival_starter.species
rival_starter.level=20
team = []
team << rival_starter
rival_trainer.currentTeam = team
$PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY] = rival_trainer
evolveRivalTeam
evolution_species = rival_starter.check_evolution_on_level_up(false)
if evolution_species
starter_species = evolution_species
end
player_chosen_starter_index = pbGet(VAR_HOENN_CHOSEN_STARTER_INDEX)
case player_chosen_starter_index
when 0 #GRASS
if isPlayerFemale()
fire_grass_pokemon = starter_species
water_fire_pokemon = getFusionSpeciesSymbol(:NUMEL,:WINGULL)
water_grass_pokemon = getFusionSpeciesSymbol(:WAILMER,:SHROOMISH)
end
if isPlayerMale()
fire_grass_pokemon = starter_species
water_fire_pokemon = getFusionSpeciesSymbol(:LOMBRE,:WINGULL)
water_grass_pokemon = getFusionSpeciesSymbol(:SLUGMA,:WAILMER)
end
contains_starter = [fire_grass_pokemon]
other_pokemon = [water_fire_pokemon,water_grass_pokemon]
when 1 #FIRE
if isPlayerFemale()
fire_grass_pokemon = getFusionSpeciesSymbol(:SHROOMISH,:NUMEL)
water_fire_pokemon = getFusionSpeciesSymbol(:LOMBRE,:WAILMER)
water_grass_pokemon = starter_species
end
if isPlayerMale()
fire_grass_pokemon = getFusionSpeciesSymbol(:LOMBRE,:SLUGMA,)
water_fire_pokemon = getFusionSpeciesSymbol(:SHROOMISH,:WINGULL,)
water_grass_pokemon = starter_species
end
contains_starter = [water_grass_pokemon]
other_pokemon = [water_fire_pokemon,fire_grass_pokemon]
when 2 #WATER
if isPlayerFemale()
fire_grass_pokemon = getFusionSpeciesSymbol(:SLUGMA,:SHROOMISH)
water_fire_pokemon = starter_species
water_grass_pokemon = getFusionSpeciesSymbol(:WAILMER,:NUMEL)
end
if isPlayerMale()
fire_grass_pokemon = getFusionSpeciesSymbol(:LOMBRE,:NUMEL,)
water_fire_pokemon = starter_species
water_grass_pokemon = getFusionSpeciesSymbol(:SLUGMA,:WINGULL)
end
contains_starter = [water_fire_pokemon]
other_pokemon = [water_grass_pokemon,fire_grass_pokemon]
end
team = []
team << Pokemon.new(other_pokemon[0],18)
team << Pokemon.new(other_pokemon[1],18)
team << Pokemon.new(contains_starter[0],20)
rival_trainer.currentTeam = team
$PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY] = rival_trainer
end
def levelUpRivalTeam(experience=0)
rival_trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY]
updated_trainer =makeRebattledTrainerTeamGainExp(rival_trainer,false,experience)
$PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY] = updated_trainer
end
def evolveRivalTeam()
rival_trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY]
updated_trainer = evolveRebattledTrainerPokemon(rival_trainer)
$PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY] = updated_trainer
end
def addPokemonToRivalTeam(species,level)
rival_trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY]
rival_trainer.currentTeam << Pokemon.new(species,level)
$PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY] = rival_trainer
end
def initializeRivalBattledTrainer
trainer_type = :RIVAL1
trainer_name = isPlayerMale ? "May" : "Brendan"
trainer_appearance = $Trainer.rival_appearance
rivalBattledTrainer = BattledTrainer.new(trainer_type,trainer_name,0)
rivalBattledTrainer.set_custom_appearance(trainer_appearance)
echoln rivalBattledTrainer.currentTeam
team = []
team<<Pokemon.new(get_rival_starter,5)
rivalBattledTrainer.currentTeam =team
return rivalBattledTrainer
end
def hoennRivalBattle(loseDialog="...")
$PokemonGlobal.battledTrainers = {} if !$PokemonGlobal.battledTrainers
if !$PokemonGlobal.battledTrainers.has_key?(BATTLED_TRAINER_RIVAL_KEY)
rival_trainer = initializeRivalBattledTrainer()
$PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY] = rival_trainer
else
rival_trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_RIVAL_KEY]
end
echoln rival_trainer
echoln rival_trainer.currentTeam
return customTrainerBattle(rival_trainer.trainerName,rival_trainer.trainerType, rival_trainer.currentTeam,rival_trainer,loseDialog,nil,rival_trainer.custom_appearance)
end

View File

@@ -0,0 +1,15 @@
def player_near_event?(map_id, event_id, radius)
return false if map_id != $game_map.map_id
event = $game_map.events[event_id]
return false if event.nil?
dx = $game_player.x - event.x
dy = $game_player.y - event.y
distance = Math.sqrt(dx * dx + dy * dy)
return distance <= radius
end
def checkOverworldPokemonFlee(radius=4)
event = $game_map.event[@event_id]
return player_near_event?($game_map.map_id, event.id, radius)
end

View File

@@ -0,0 +1,137 @@
# frozen_string_literal: true
CITIES_MAP_IDS = {
:LITTLEROOT => 9,
:OLDALE => 8,
:PETALBURG =>7,
:RUSTBORO => 47,
:DEWFORD => 0,
:SLATEPORT => 0,
:MAUVILLE => 0,
:VERDANTURF => 0,
:FALLARBOR => 0,
:LAVARIDGE => 0,
:FORTREE => 0,
:LILYCOVE => 0,
:MOSSDEEP => 0,
:SOOTOPOLIS => 0,
:PACIFIDLOG => 0,
:EVERGRANDE => 0,
:BATTLEFRONTIER => 0,
}
# Necessary dor setting the various events within the pokemart map, uses the numbers as wondertrade
def get_city_numerical_id_hoenn(city_sym)
current_city_numerical = {
:LITTLEROOT => 1,
:OLDALE => 2,
:PETALBURG => 3,
:RUSTBORO => 4,
:DEWFORD => 5,
:SLATEPORT => 6,
:MAUVILLE => 7,
:VERDANTURF => 8,
:FALLARBOR => 9,
:LAVARIDGE => 10,
:FORTREE => 11,
:LILYCOVE => 12,
:MOSSDEEP => 13,
:SOOTOPOLIS => 14,
:PACIFIDLOG => 15,
:EVERGRANDE => 16
}
return current_city_numerical[city_sym]
end
if Settings::GAME_ID == :IF_HOENN
POKEMART_MAP_ID = 24
POKEMART_DOOR_POS = [12, 12]
end
# city -> Symbol
# def enter_pokemart(city)
# pbSet(VAR_CURRENT_MART, city)
# pbSet(VAR_CURRENT_CITY_NUMERICAL_ID, get_city_numerical_id(city))
# echoln get_city_numerical_id(city)
# pbFadeOutIn {
# $game_temp.player_new_map_id = POKEMART_MAP_ID
# $game_temp.player_new_x = POKEMART_DOOR_POS[0]
# $game_temp.player_new_y = POKEMART_DOOR_POS[1]
# $scene.transfer_player(true)
# $game_map.autoplay
# $game_map.refresh
# }
# end
HOENN_POKEMART_ENTRANCES = {
:LITTLEROOT => [1, 0, 0],
:OLDALE => [1, 0, 0],
:VERMILLION => [1, 0, 0],
:PETALBURG => [7, 32, 19],
:RUSTBORO => [1, 0, 0],
:DEWFORD => [1, 0, 0],
:SLATEPORT => [1, 0, 0],
:MAUVILLE => [1, 0, 0],
:VERDANTURF => [1, 0, 0],
:FALLARBOR => [1, 0, 0],
:LAVARIDGE => [1, 0, 0],
:FORTREE => [1, 0, 0],
:LILYCOVE => [1, 0, 0],
:MOSSDEEP => [1, 0, 0],
:SOOTOPOLIS => [1, 0, 0],
:PACIFIDLOG => [1, 0, 0],
:EVERGRANDE => [1, 0, 0],
}
def exit_pokemart_hoenn()
current_city = pbGet(VAR_CURRENT_MART)
current_city = :PETALBURG if !current_city.is_a?(Symbol)
entrance_map = HOENN_POKEMART_ENTRANCES[current_city][0]
entrance_x = HOENN_POKEMART_ENTRANCES[current_city][1]
entrance_y = HOENN_POKEMART_ENTRANCES[current_city][2]
pbSet(VAR_CURRENT_CITY_NUMERICAL_ID, 0)
pbSet(VAR_CURRENT_MART, 0)
pbFadeOutIn {
$game_temp.player_new_map_id = entrance_map
$game_temp.player_new_x = entrance_x
$game_temp.player_new_y = entrance_y
$scene.transfer_player(true)
$game_map.autoplay
$game_map.refresh
}
end
def get_mart_exclusive_items_hoenn(city)
items_list = []
return items_list
end
def exit_pokemon_center()
if $PokemonGlobal.pokecenterMapId && $PokemonGlobal.pokecenterMapId>=0
pbCancelVehicles
pbRemoveDependencies
$game_temp.player_new_map_id = $PokemonGlobal.pokecenterMapId
$game_temp.player_new_x = $PokemonGlobal.pokecenterX
$game_temp.player_new_y = $PokemonGlobal.pokecenterY
$scene.transfer_player if $scene.is_a?(Scene_Map)
$game_map.refresh
else#Home
$game_temp.player_new_map_id = 9
$game_temp.player_new_x = 16
$game_temp.player_new_y = 23
$scene.transfer_player if $scene.is_a?(Scene_Map)
$game_map.refresh
end
end
def inCity?(city)
city_map_id = CITIES_MAP_IDS[city]
return $PokemonGlobal.pokecenterMapId ==city_map_id
end

View File

@@ -0,0 +1,160 @@
# frozen_string_literal: true
SURF_SPLASH_ANIMATION_ID = 30
class Game_Temp
attr_accessor :surf_patches
def initializeSurfPatches
@surf_patches = []
end
def clearSurfSplashPatches
return unless $game_temp.surf_patches
$game_temp.surf_patches.clear
end
end
class Spriteset_Map
alias surf_patch_update update
def update
surf_patch_update
return unless $scene.is_a?(Scene_Map)
return unless Settings::GAME_ID == :IF_HOENN
return unless $PokemonGlobal.surfing
return if Graphics.frame_count % 32 != 0
animate_surf_water_splashes
end
end
class SurfPatch
MAX_NUMBER_SURF_SPLASHES = 6
attr_accessor :shape #Array of tiles coordinates (ex: [[10,20],[1,5]])
def initialize(patch_size)
x, y = getRandomPositionOnPerimeter(8, 6, $game_player.x, $game_player.y, 2)
variance = rand(5..8)
@shape =getRandomSplashPatch(patch_size,x,y,variance)
end
def getRandomSplashPatch(tile_count, center_x, center_y, variance = rand(4))
return [] if tile_count <= 0
center_pos = getRandomPositionOnPerimeter(tile_count, tile_count, center_x, center_y, variance)
area = [center_pos]
visited = { center_pos => true }
queue = [center_pos]
directions = [[1, 0], [-1, 0], [0, 1], [0, -1],
[1, 1], [-1, -1], [1, -1], [-1, 1]] # 8 directions
while area.length < tile_count && !queue.empty?
current = queue.sample
queue.delete(current)
cx, cy = current
# Randomize how many directions to try (1 to 4)
directions.shuffle.take(rand(1..4)).each do |dx, dy|
nx, ny = cx + dx, cy + dy
new_pos = [nx, ny]
next if visited[new_pos]
visited[new_pos] = true
area << new_pos
queue << new_pos
break if area.length >= tile_count
end
end
# Filter to keep only water tiles
map_id = $game_map.map_id
area.select! do |pos|
x, y = pos
terrain = $MapFactory.getTerrainTag(map_id, x, y, false)
next false unless terrain&.can_surf # Only water/surfable tiles
$game_map.playerPassable?(x, y, 2) # Direction 2 (down) or any direction for checking passability
end
return area
end
end
def animate_surf_water_splashes
animation_frequency = 16 #in frames
return unless $game_temp.surf_patches
return unless Graphics.frame_count % animation_frequency == 0
$game_temp.surf_patches.each do |patch|
next if patch.nil? || patch.shape.empty?
patch.shape.each do |splash_tile|
x_position= splash_tile[0]
y_position = splash_tile[1]
$scene.spriteset.addUserAnimation(SURF_SPLASH_ANIMATION_ID, x_position, y_position, true, 0)
end
end
end
def try_spawn_surf_water_splashes
water_splash_chance = 0.1 #Chance each step 10%
steps_interval = 5 # Only check once every 5 steps
return if $PokemonGlobal.stepcount % steps_interval != 0
return unless rand < water_splash_chance
spawnSurfSplashPatch
end
Events.onStepTaken += proc { |sender, e|
water_encounter_chance = 25
next unless $scene.is_a?(Scene_Map)
next unless Settings::GAME_ID == :IF_HOENN
next unless $PokemonGlobal.surfing
player_x = $game_player.x
player_y = $game_player.y
if $game_temp.surf_patches
$game_temp.surf_patches.each_with_index do |patch,index|
next unless patch && patch.shape
if patch.shape.include?([player_x, player_y])
next if rand(100) > water_encounter_chance
$game_temp.surf_patches.delete_at(index)
echoln "surf patch encounter!"
wild_pokemon = $PokemonEncounters.choose_wild_pokemon(:Water)
species = wild_pokemon[0]
level = wild_pokemon[1]
pbWildBattle(species, level)
break
end
end
end
try_spawn_surf_water_splashes
}
def spawnSurfSplashPatch
$game_temp.initializeSurfPatches unless $game_temp.surf_patches
patch_size = [3,4,5,6].sample
splash_patch = SurfPatch.new(patch_size)
$game_temp.surf_patches << splash_patch
$game_temp.surf_patches.shift if $game_temp.surf_patches.length >= SurfPatch::MAX_NUMBER_SURF_SPLASHES
echoln $game_temp.surf_patches
end

View File

@@ -0,0 +1,33 @@
# frozen_string_literal: true
#eventType
# :EVOLVE
# :FUSE
# :UNFUSE
# :REVERSE
# :CAUGHT
class BattledTrainerRandomEvent
attr_accessor :eventType
attr_accessor :caught_pokemon #species_sym
attr_accessor :unevolved_pokemon #species_sym
attr_accessor :evolved_pokemon #species_sym
attr_accessor :fusion_head_pokemon #species_sym
attr_accessor :fusion_body_pokemon #species_sym
attr_accessor :fusion_fused_pokemon #species_sym
attr_accessor :unreversed_pokemon #species_sym
attr_accessor :reversed_pokemon #species_sym
attr_accessor :unfused_pokemon #species_sym
def initialize(eventType)
@eventType = eventType
end
end

View File

@@ -0,0 +1,217 @@
class BattledTrainer
DELAY_BETWEEN_NPC_TRADES = 180 #In seconds (3 minutes)
MAX_FRIENDSHIP = 100
attr_accessor :trainerType
attr_accessor :trainerName
attr_accessor :currentTeam #list of Pokemon. The game selects in this list for trade offers. They can increase levels & involve as you rebattle them.
#trainers will randomly find items and add them to this list. When they have the :ITEM status, they will
# give one of them at random.
#Items equipped to the Pokemon traded by the player will end up in that list.
#
# If there is an evolution that the trainer can use on one of their Pokemon in that list, they will
# instead use it to evolve their Pokemon.
#
#DNA Splicers/reversers can be used on their Pokemon if they have at least 2 unfused/1 fused
#
#Healing items that are in that list can be used by the trainer in rematches
#
attr_accessor :foundItems
attr_accessor :nb_rematches
#What the trainer currently wants to do
# :IDLE -> Nothing. Normal postbattle dialogue
# Should prompt the player to register the trainer in their phone.
# Or maybe done automatically at the end of the battle?
# :TRADE -> Trainer wants to trade one of its Pokémon with the player
# :BATTLE -> Trainer wants to rebattle the player
# :ITEM -> Trainer has an item they want to give the player
attr_accessor :current_status
attr_accessor :previous_status
attr_accessor :previous_trade_timestamp
attr_accessor :favorite_type
attr_accessor :favorite_pokemon #Used for generating trade offers. Should be set from trainer.txt (todo)
#If empty, then trade offers ask for a Pokemon of a type depending on the trainer's class
attr_accessor :previous_random_events
attr_accessor :has_pending_action
attr_accessor :custom_appearance
attr_accessor :friendship #increases the more you interact with them, unlocks more interact options
attr_accessor :friendship_level
def initialize(trainerType,trainerName,trainerVersion)
@trainerType = trainerType
@trainerName = trainerName
@currentTeam = loadOriginalTrainerTeam(trainerVersion)
@foundItems = []
@nb_rematches = 0
@currentStatus = :IDLE
@previous_status = :IDLE
@previous_trade_timestamp = Time.now-DELAY_BETWEEN_NPC_TRADES
@previous_random_events =[]
@has_pending_action=false
@favorite_type = pick_favorite_type(trainerType)
@friendship = 0
@friendship_level = 0
end
def friendship_level
@friendship_level =0 if !@friendship_level
return @friendship_level
end
def increase_friendship(amount)
@friendship=0 if !@friendship
@friendship_level=0 if !@friendship_level
gain = amount / ((@friendship + 1) ** 0.4)
@friendship += gain
@friendship = MAX_FRIENDSHIP if @friendship > MAX_FRIENDSHIP
echoln "Friendship with #{@trainerName} increased by #{gain.round(2)} (total: #{@friendship.round(2)})"
thresholds = FRIENDSHIP_LEVELS[@trainerType] || []
echoln thresholds
while @friendship_level < thresholds.length && @friendship >= thresholds[@friendship_level]
@friendship_level += 1
trainerClassName = GameData::TrainerType.get(@trainerType).real_name
pbMessage(_INTL("\\C[3]Friendship increased with #{trainerClassName} #{@trainerName}!"))
case @friendship_level
when 1
pbMessage(_INTL("You can now trade with each other!"))
when 2
pbMessage(_INTL("They will now give you items from time to time!"))
when 3
pbMessage(_INTL("You can now partner up with them!"))
end
echoln "🎉 #{@trainerName}'s friendship level increased to #{@friendship_level}!"
end
end
def set_custom_appearance(trainer_appearance)
@custom_appearance = trainer_appearance
end
def pick_favorite_type(trainer_type)
if TRAINER_CLASS_FAVORITE_TYPES.has_key?(trainer_type)
return TRAINER_CLASS_FAVORITE_TYPES[trainer_type].sample
else
return :NORMAL
end
end
def set_pending_action(value)
@has_pending_action=value
end
def log_evolution_event(unevolved_pokemon_species, evolved_pokemon_species)
echoln "NPC Trainer #{@trainerName} evolved their #{get_species_readable_internal_name(unevolved_pokemon_species)} to #{get_species_readable_internal_name(evolved_pokemon_species)}!"
event = BattledTrainerRandomEvent.new(:EVOLVE)
event.unevolved_pokemon = unevolved_pokemon_species
event.evolved_pokemon = evolved_pokemon_species
@previous_random_events = [] unless @previous_random_events
@previous_random_events << event
end
def log_fusion_event(body_pokemon_species, head_pokemon_species, fused_pokemon_species)
echoln "NPC trainer #{@trainerName} fused #{body_pokemon_species} and #{head_pokemon_species}!"
event = BattledTrainerRandomEvent.new(:FUSE)
event.fusion_body_pokemon =body_pokemon_species
event.fusion_head_pokemon =head_pokemon_species
event.fusion_fused_pokemon =fused_pokemon_species
@previous_random_events = [] unless @previous_random_events
@previous_random_events << event
end
def log_unfusion_event(original_fused_pokemon_species, unfused_body_species, unfused_body_head)
echoln "NPC trainer #{@trainerName} unfused #{get_species_readable_internal_name(original_fused_pokemon_species)}!"
event = BattledTrainerRandomEvent.new(:UNFUSE)
event.unfused_pokemon = original_fused_pokemon_species
event.fusion_body_pokemon = unfused_body_species
event.fusion_head_pokemon = unfused_body_head
@previous_random_events = [] unless @previous_random_events
@previous_random_events << event
end
def log_reverse_event(original_fused_pokemon_species, reversed_fusion_species)
echoln "NPC trainer #{@trainerName} reversed #{get_species_readable_internal_name(original_fused_pokemon_species)}!"
event = BattledTrainerRandomEvent.new(:REVERSE)
event.unreversed_pokemon = original_fused_pokemon_species
event.reversed_pokemon = reversed_fusion_species
@previous_random_events = [] unless @previous_random_events
@previous_random_events << event
end
def log_catch_event(new_pokemon_species)
echoln "NPC Trainer #{@trainerName} caught a #{new_pokemon_species}!"
event = BattledTrainerRandomEvent.new(:CATCH)
event.caught_pokemon = new_pokemon_species
@previous_random_events = [] unless @previous_random_events
@previous_random_events << event
end
def clear_previous_random_events()
@previous_random_events = []
end
def loadOriginalTrainer(trainerVersion=0)
return pbLoadTrainer(@trainerType,@trainerName,trainerVersion)
end
def loadOriginalTrainerTeam(trainerVersion=0)
original_trainer = pbLoadTrainer(@trainerType,@trainerName,trainerVersion)
return if !original_trainer
echoln "Loading Trainer #{@trainerType}"
current_party = []
original_trainer.party.each do |partyMember|
echoln "PartyMember: #{partyMember}"
if partyMember.is_a?(Pokemon)
current_party << partyMember
elsif partyMember.is_a?(Array) #normally always gonna be this
pokemon_species = partyMember[0]
pokemon_level = partyMember[1]
current_party << Pokemon.new(pokemon_species,pokemon_level)
else
echoln "Could not add Pokemon #{partyMember} to rematchable trainer's party."
end
end
return current_party
end
def getTimeSinceLastTrade()
@previous_trade_timestamp ||= Time.now - DELAY_BETWEEN_NPC_TRADES
return Time.now - @previous_trade_timestamp
end
def isNextTradeReady?()
return getTimeSinceLastTrade < DELAY_BETWEEN_NPC_TRADES
end
def list_team_unfused_pokemon
list = []
@currentTeam.each do |pokemon|
list << pokemon if !pokemon.isFusion?
end
return list
end
def list_team_fused_pokemon
list = []
@currentTeam.each do |pokemon|
list << pokemon if pokemon.isFusion?
end
return list
end
end

View File

@@ -0,0 +1,70 @@
# frozen_string_literal: true
class PokemonGlobalMetadata
#Map that keeps track of all the npc trainers the player has battled
# [map_id,event_id] =>BattledTrainer
attr_accessor :battledTrainers
end
TIME_FOR_RANDOM_EVENTS = 60#3600 #1 hour
## Extend pbTrainerBattle to call postTrainerBattleAction at the end of every trainer battle
alias original_pbTrainerBattle pbTrainerBattle
def pbTrainerBattle(trainerID, trainerName,endSpeech=nil,
doubleBattle=false, trainerPartyID=0,
*args)
result = original_pbTrainerBattle(trainerID, trainerName, endSpeech,doubleBattle,trainerPartyID, *args)
postTrainerBattleActions(trainerID, trainerName,trainerPartyID) if Settings::GAME_ID == :IF_HOENN
return result
end
def postTrainerBattleActions(trainerID, trainerName,trainerVersion)
trainer = registerBattledTrainer(@event_id,$game_map.map_id,trainerID,trainerName,trainerVersion)
makeRebattledTrainerTeamGainExp(trainer)
end
#Do NOT call this alone. Rebattlable trainers are always intialized after
# defeating them.
# Having a rematchable trainer that is not registered will cause crashes.
def registerBattledTrainer(event_id, mapId, trainerType, trainerName, trainerVersion=0)
key = [event_id,mapId]
$PokemonGlobal.battledTrainers = {} unless $PokemonGlobal.battledTrainers
trainer = BattledTrainer.new(trainerType, trainerName, trainerVersion)
$PokemonGlobal.battledTrainers[key] = trainer
return trainer
end
def unregisterBattledTrainer(event_id, mapId)
key = [event_id,mapId]
$PokemonGlobal.battledTrainers = {} unless $PokemonGlobal.battledTrainers
if $PokemonGlobal.battledTrainers.has_key?(key)
$PokemonGlobal.battledTrainers[key] =nil
echoln "Unregistered Battled Trainer #{key}"
else
echoln "Could not unregister Battled Trainer #{key}"
end
end
def resetTrainerRebattle(event_id, map_id)
trainer = getRebattledTrainer(event_id,map_id)
trainerType = trainer.trainerType
trainerName = trainer.trainerName
unregisterBattledTrainer(event_id,map_id)
registerBattledTrainer(event_id,map_id,trainerType,trainerName)
end
def updateRebattledTrainer(event_id,map_id,updated_trainer)
key = [event_id,map_id]
$PokemonGlobal.battledTrainers = {} if !$PokemonGlobal.battledTrainers
$PokemonGlobal.battledTrainers[key] = updated_trainer
end
def getRebattledTrainer(event_id,map_id)
key = [event_id,map_id]
$PokemonGlobal.battledTrainers = {} if !$PokemonGlobal.battledTrainers
return $PokemonGlobal.battledTrainers[key]
end

View File

@@ -0,0 +1,123 @@
# frozen_string_literal: true
# After each rematch, all of the trainer's Pokémon gain EXP
#
# Gained Exp is calculated from the Pokemon that is in the first slot in the player's team
# so the trainer's levels will scale with the player's.
#
# e.g. If the player uses a stronger Pokemon in the battle, the NPC will get more experience
# as a result
#
def makeRebattledTrainerTeamGainExp(trainer, playerWon=true, gained_exp=nil)
return if !trainer
updated_team = []
trainer_pokemon = $Trainer.party[0]
return if !trainer_pokemon
for pokemon in trainer.currentTeam
if !gained_exp #Set depending on first pokemon in party if not given a specific amount
gained_exp = trainer_pokemon.level * trainer_pokemon.base_exp
gained_exp /= 2 if playerWon #trainer lost so he's not getting full exp
gained_exp /= trainer.currentTeam.length
end
growth_rate = pokemon.growth_rate
new_exp = growth_rate.add_exp(pokemon.exp, gained_exp)
pokemon.exp = new_exp
updated_team.push(pokemon)
end
trainer.currentTeam = updated_team
return trainer
end
def evolveRebattledTrainerPokemon(trainer)
updated_team = []
for pokemon in trainer.currentTeam
evolution_species = pokemon.check_evolution_on_level_up(false)
if evolution_species
trainer.log_evolution_event(pokemon.species,evolution_species)
trainer.set_pending_action(true)
pokemon.species = evolution_species if evolution_species
end
updated_team.push(pokemon)
end
trainer.currentTeam = updated_team
return trainer
end
def healRebattledTrainerPokemon(trainer)
for pokemon in trainer.currentTeam
pokemon.calc_stats
pokemon.heal
end
return trainer
end
def doNPCTrainerRematch(trainer)
return generateTrainerRematch(trainer)
end
def generateTrainerRematch(trainer)
trainer_data = GameData::Trainer.try_get(trainer.trainerType, trainer.trainerName, 0)
loseDialog = trainer_data&.loseText_rematch ? trainer_data.loseText_rematch : "..."
if customTrainerBattle(trainer.trainerName,trainer.trainerType, trainer.currentTeam,nil,loseDialog)
updated_trainer = makeRebattledTrainerTeamGainExp(trainer,true)
updated_trainer = healRebattledTrainerPokemon(updated_trainer)
else
updated_trainer =makeRebattledTrainerTeamGainExp(trainer,false)
end
updated_trainer.set_pending_action(false)
updated_trainer = evolveRebattledTrainerPokemon(updated_trainer)
trainer.increase_friendship(5)
return updated_trainer
end
def showPrerematchDialog()
event = pbMapInterpreter.get_character(0)
map_id = $game_map.map_id if map_id.nil?
trainer = getRebattledTrainer(event.id,map_id)
return "" if trainer.nil?
trainer_data = GameData::Trainer.try_get(trainer.trainerType, trainer.trainerName, 0)
all_previous_random_events = trainer.previous_random_events
if all_previous_random_events
previous_random_event = getBestMatchingPreviousRandomEvent(trainer_data, trainer.previous_random_events)
if previous_random_event
event_message_map = {
CATCH: trainer_data.preRematchText_caught,
EVOLVE: trainer_data.preRematchText_evolved,
FUSE: trainer_data.preRematchText_fused,
UNFUSE: trainer_data.preRematchText_unfused,
REVERSE: trainer_data.preRematchText_reversed
}
message_text = event_message_map[previous_random_event.eventType] || trainer_data.preRematchText
else
message_text = trainer_data.preRematchText
end
end
if previous_random_event
message_text = message_text.gsub("<CAUGHT_POKEMON>", getSpeciesRealName(previous_random_event.caught_pokemon).to_s)
message_text = message_text.gsub("<UNEVOLVED_POKEMON>", getSpeciesRealName(previous_random_event.unevolved_pokemon).to_s)
message_text = message_text.gsub("<EVOLVED_POKEMON>", getSpeciesRealName(previous_random_event.evolved_pokemon).to_s)
message_text = message_text.gsub("<HEAD_POKEMON>", getSpeciesRealName(previous_random_event.fusion_head_pokemon).to_s)
message_text = message_text.gsub("<BODY_POKEMON>", getSpeciesRealName(previous_random_event.fusion_body_pokemon).to_s)
message_text = message_text.gsub("<FUSED_POKEMON>", getSpeciesRealName(previous_random_event.fusion_fused_pokemon).to_s)
message_text = message_text.gsub("<UNREVERSED_POKEMON>", getSpeciesRealName(previous_random_event.unreversed_pokemon).to_s)
message_text = message_text.gsub("<REVERSED_POKEMON>", getSpeciesRealName(previous_random_event.reversed_pokemon).to_s)
message_text = message_text.gsub("<UNFUSED_POKEMON>", getSpeciesRealName(previous_random_event.unfused_pokemon).to_s)
else
message_text = trainer_data.preRematchText
end
if message_text
split_messages = message_text.split("<br>")
split_messages.each do |msg|
pbCallBub(2,event.id)
pbMessage(msg)
end
end
end

View File

@@ -0,0 +1,203 @@
class StartersSelectionScene
POKEBALL_LEFT_X = -20; POKEBALL_LEFT_Y = 70
POKEBALL_MIDDLE_X = 125; POKEBALL_MIDDLE_Y = 100
POKEBALL_RIGHT_X = 275; POKEBALL_RIGHT_Y = 70
TEXT_POSITION_X = 100
TEXT_POSITION_Y = 10
def initialize(starters = [])
@starters_species = starters
@starter_pokemon = []
@starters_species.each do |species|
@starter_pokemon.push(Pokemon.new(species,5))
end
@spritesLoader = BattleSpriteLoader.new
@shown_starter_species=nil
end
def initializeGraphics()
@background = displayPicture("Graphics/Pictures/Trades/hoenn_starter_bag_bg.png", -20, -20)
@background.z=0
@foreground = displayPicture("Graphics/Pictures/Trades/hoenn_starter_bag_foreground.png", -20, -20)
@foreground.z=999
@pokeball_closed_left = displayPicture("Graphics/Pictures/Trades/trade_pokeball_closed_1.png", POKEBALL_LEFT_X, POKEBALL_LEFT_Y)
@pokeball_closed_left.z=2
@pokeball_closed_middle = displayPicture("Graphics/Pictures/Trades/trade_pokeball_closed_2.png", POKEBALL_MIDDLE_X, POKEBALL_MIDDLE_Y)
@pokeball_closed_middle.z=100
@pokeball_closed_right = displayPicture("Graphics/Pictures/Trades/trade_pokeball_closed_3.png", POKEBALL_RIGHT_X, POKEBALL_RIGHT_Y)
@pokeball_closed_right.z=2
end
def updateOpenPokeballPosition
case @index
when 0
@shown_pokemon_x = POKEBALL_LEFT_X
@shown_pokemon_y = POKEBALL_LEFT_Y
when 1
@shown_pokemon_x = POKEBALL_MIDDLE_X
@shown_pokemon_y = POKEBALL_MIDDLE_Y
when 2
@shown_pokemon_x = POKEBALL_RIGHT_X
@shown_pokemon_y = POKEBALL_RIGHT_Y
end
end
def startScene
initializeGraphics
@index=nil
previous_index = nil
loop do
if @index
if Input.trigger?(Input::RIGHT)
previous_index = @index
@index+=1
@index = 0 if @index == @starters_species.length
end
if Input.trigger?(Input::LEFT)
previous_index = @index
@index-=1
@index = @starters_species.length-1 if @index < 0
end
if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
updateOpenPokeballPosition
updateStarterSelectionGraphics
end
if Input.trigger?(Input::USE)
if pbConfirmMessage(_INTL("Do you choose this Pokémon?"))
chosenPokemon = @starter_pokemon[@index]
@spritesLoader.registerSpriteSubstitution(@pif_sprite)
disposeGraphics
pbSet(VAR_HOENN_CHOSEN_STARTER_INDEX,@index)
return chosenPokemon
end
end
else
@index = 0 if Input.trigger?(Input::LEFT)
@index = 1 if Input.trigger?(Input::DOWN)
@index = 2 if Input.trigger?(Input::RIGHT)
end
if previous_index != @index
updateOpenPokeballPosition
updateStarterSelectionGraphics
previous_index = @index
end
Input.update
Graphics.update
end
end
def disposeGraphics()
@pokeball_closed_left.dispose
@pokeball_closed_middle.dispose
@pokeball_closed_right.dispose
@pokeball_open_back.dispose
@pokeball_open_front.dispose
@background.dispose
@foreground.dispose
@pokemon_name_overlay.dispose
@pokemon_category_overlay.dispose
@pokemonSpriteWindow.dispose
end
def updateClosedBallGraphicsVisibility
case @index
when 0
@pokeball_closed_left.visible=false
@pokeball_closed_middle.visible=true
@pokeball_closed_right.visible=true
when 1
@pokeball_closed_left.visible=true
@pokeball_closed_middle.visible=false
@pokeball_closed_right.visible=true
when 2
@pokeball_closed_left.visible=true
@pokeball_closed_middle.visible=true
@pokeball_closed_right.visible=false
else
@pokeball_closed_left.visible=true
@pokeball_closed_middle.visible=true
@pokeball_closed_right.visible=true
end
end
def updateStarterSelectionGraphics()
pbSEPlay("GUI storage pick up", 80, 100)
updateClosedBallGraphicsVisibility
@pokeball_open_back.dispose if @pokeball_open_back
@pokeball_open_front.dispose if @pokeball_open_front
@shown_starter_species = @starters_species[@index]
updateOpenPokeballPosition
@pokeball_open_back = displayPicture("Graphics/Pictures/Trades/trade_pokeball_open_back",@shown_pokemon_x, @shown_pokemon_y,2)
@pokeball_open_front = displayPicture("Graphics/Pictures/Trades/trade_pokeball_open_front",@shown_pokemon_x, @shown_pokemon_y,50)
updatePokemonSprite
end
def updatePokemonSprite()
@pif_sprite = @spritesLoader.get_pif_sprite_from_species(@shown_starter_species.species)
sprite_bitmap = @spritesLoader.load_pif_sprite_directly(@pif_sprite)
pokemon = @starter_pokemon[@index]
if pokemon.shiny?
sprite_bitmap.bitmap.update_shiny_cache(pokemon.id_number, "")
sprite_bitmap.shiftAllColors(pokemon.id_number, pokemon.bodyShiny?, pokemon.headShiny?)
end
@pokemonSpriteWindow.dispose if @pokemonSpriteWindow
@pokemonSpriteWindow = PictureWindow.new(sprite_bitmap.bitmap)
@pokemonSpriteWindow.opacity = 0
@pokemonSpriteWindow.z = 2
@pokemonSpriteWindow.x = @shown_pokemon_x
@pokemonSpriteWindow.y = @shown_pokemon_y-10
updateText
end
def updateText
@pokemon_name_overlay.dispose if @pokemon_name_overlay
@pokemon_category_overlay.dispose if @pokemon_category_overlay
pokemon_name = "#{@shown_starter_species.real_name}"
pokemon_category = "#{@shown_starter_species.real_category} Pokémon"
title_position_y = TEXT_POSITION_Y
subtitle_position_y = TEXT_POSITION_Y + 30
text_x_offset=-100
label_base_color = Color.new(88,88,80)
label_shadow_color = Color.new(168,184,184)
title_base_color = Color.new(248, 248, 248)
title_shadow_color = Color.new(104, 104, 104)
@pokemon_name_overlay = BitmapSprite.new(Graphics.width, Graphics.height, @viewport).bitmap
@pokemon_category_overlay = BitmapSprite.new(Graphics.width, Graphics.height, @viewport).bitmap
@pokemon_name_overlay.font.size = 50
@pokemon_name_overlay.font.name = MessageConfig.pbGetSmallFontName
@pokemon_category_overlay.font.size = 36
@pokemon_category_overlay.font.name = MessageConfig.pbGetSmallFontName
pbDrawTextPositions(@pokemon_name_overlay, [[pokemon_name, (Graphics.width/2)+text_x_offset, title_position_y, 2, title_base_color, title_shadow_color]])
pbDrawTextPositions(@pokemon_category_overlay,[[pokemon_category, (Graphics.width/2)+text_x_offset, subtitle_position_y, 2, label_base_color, label_shadow_color]])
end
end

View File

@@ -0,0 +1,85 @@
class BattledTrainer
TRAINER_CLASS_FAVORITE_TYPES =
{
AROMALADY: [:GRASS, :FAIRY],
BEAUTY: [:FAIRY, :WATER, :NORMAL, :GRASS],
BIKER: [:POISON, :DARK],
BIRDKEEPER: [:FLYING, :NORMAL],
BUGCATCHER: [:BUG],
BURGLAR: [:FIRE, :DARK],
CHANNELER: [:GHOST, :PSYCHIC],
CUEBALL: [:FIGHTING],
ENGINEER: [:ELECTRIC, :STEEL],
FISHERMAN: [:WATER],
GAMBLER: [:NORMAL, :PSYCHIC],
GENTLEMAN: [:NORMAL, :STEEL],
HIKER: [:ROCK, :GROUND],
JUGGLER: [:PSYCHIC, :GHOST],
LADY: [:FAIRY, :NORMAL],
PAINTER: [:NORMAL, :PSYCHIC],
POKEMANIAC: [:DRAGON, :GROUND],
POKEMONBREEDER: [:NORMAL, :GRASS],
PROFESSOR: [:NORMAL, :PSYCHIC],
ROCKER: [:ELECTRIC, :FIRE],
RUINMANIAC: [:GROUND, :ROCK],
SAILOR: [:WATER, :FIGHTING],
SCIENTIST: [:ELECTRIC, :STEEL, :POISON],
SUPERNERD: [:ELECTRIC, :PSYCHIC, :STEEL],
TAMER: [:NORMAL, :DARK],
BLACKBELT: [:FIGHTING],
CRUSHGIRL: [:FIGHTING],
CAMPER: [:BUG, :NORMAL, :GRASS],
PICNICKER: [:GRASS, :NORMAL],
COOLTRAINER_M: [:DRAGON, :STEEL, :FIRE],
COOLTRAINER_F: [:ICE, :PSYCHIC, :FAIRY],
YOUNGSTER: [:NORMAL, :BUG],
LASS: [:NORMAL, :FAIRY],
POKEMONRANGER_M: [:GRASS, :GROUND],
POKEMONRANGER_F: [:GRASS, :WATER],
PSYCHIC_M: [:PSYCHIC, :GHOST],
PSYCHIC_F: [:PSYCHIC, :FAIRY],
SWIMMER_M: [:WATER],
SWIMMER_F: [:WATER, :ICE],
SWIMMER2_M: [:WATER],
SWIMMER2_F: [:WATER],
TUBER_M: [:WATER],
TUBER_F: [:WATER],
TUBER2_M: [:WATER],
TUBER2_F: [:WATER],
COOLCOUPLE: [:FIRE, :ICE],
CRUSHKIN: [:FIGHTING],
SISANDBRO: [:WATER, :GROUND],
TWINS: [:FAIRY, :NORMAL],
YOUNGCOUPLE: [:NORMAL, :PSYCHIC],
SOCIALITE: [:FAIRY, :NORMAL],
BUGCATCHER_F: [:BUG],
ROUGHNECK: [:DARK, :FIGHTING],
TEACHER: [:PSYCHIC, :NORMAL],
PRESCHOOLER_M: [:NORMAL],
PRESCHOOLER_F: [:FAIRY, :NORMAL],
HAUNTEDGIRL_YOUNG: [:GHOST],
HAUNTEDGIRL: [:GHOST, :DARK],
CLOWN: [:PSYCHIC, :FAIRY],
NURSE: [:NORMAL, :FAIRY],
WORKER: [:STEEL, :GROUND],
COOLTRAINER_M2: [:FIGHTING, :STEEL],
COOLTRAINER_F2: [:PSYCHIC, :ICE],
FARMER: [:GRASS, :GROUND, :NORMAL],
PYROMANIAC: [:FIRE],
KIMONOGIRL: [:FAIRY, :PSYCHIC, :GHOST],
SAGE: [:PSYCHIC, :GHOST],
PLAYER: [:ICE, :FIGHTING],
POLICE: [:DARK, :FIGHTING],
SKIER_F: [:ICE],
DELIVERYMAN: [:NORMAL],
RICHBOY: [],
SCHOOLBOY: [],
SCHOOLGIRL: [],
TEAM_AQUA_GRUNT_M: [],
TEAM_AQUA_GRUNT_F: [],
TEAM_MAGMA_GRUNT_M: [],
TEAM_MAGMA_GRUNT_F: [],
TEAM_MAGMAQUA_GRUNT_M: [],
TEAM_MAGMAQUA_GRUNT_F: [],
}
end

View File

@@ -0,0 +1,85 @@
class BattledTrainer
FRIENDSHIP_LEVELS = {
AROMALADY: [10, 25, 45],
BEAUTY: [15, 30, 60],
BIKER: [20, 40, 80],
BIRDKEEPER: [10, 25, 50],
BUGCATCHER: [8, 20, 35],
BURGLAR: [20, 45, 90],
CHANNELER: [15, 35, 70],
CUEBALL: [18, 38, 80],
ENGINEER: [15, 30, 65],
FISHERMAN: [12, 28, 55],
GAMBLER: [15, 30, 60],
GENTLEMAN: [12, 30, 70],
HIKER: [10, 25, 50],
JUGGLER: [15, 30, 65],
LADY: [15, 30, 60],
PAINTER: [8, 22, 40],
POKEMANIAC: [18, 35, 70],
POKEMONBREEDER: [8, 18, 35],
PROFESSOR: [10, 30, 60],
ROCKER: [15, 35, 70],
RUINMANIAC: [15, 35, 65],
SAILOR: [12, 28, 60],
SCIENTIST: [15, 35, 70],
SUPERNERD: [14, 30, 65],
TAMER: [15, 35, 75],
BLACKBELT: [20, 45, 90],
CRUSHGIRL: [18, 40, 85],
CAMPER: [10, 22, 40],
PICNICKER: [10, 22, 40],
COOLTRAINER_M: [20, 45, 95],
COOLTRAINER_F: [20, 45, 95],
YOUNGSTER: [10, 25, 40],
LASS: [10, 22, 38],
POKEMONRANGER_M:[15, 35, 80],
POKEMONRANGER_F:[15, 35, 80],
PSYCHIC_M: [15, 35, 70],
PSYCHIC_F: [15, 35, 70],
SWIMMER_M: [10, 25, 50],
SWIMMER_F: [10, 25, 50],
SWIMMER2_M: [12, 28, 55],
SWIMMER2_F: [12, 28, 55],
TUBER_M: [6, 15, 30],
TUBER_F: [6, 15, 30],
TUBER2_M: [6, 15, 30],
TUBER2_F: [6, 15, 30],
COOLCOUPLE: [15, 35, 80],
CRUSHKIN: [15, 35, 80],
SISANDBRO: [10, 25, 50],
TWINS: [10, 25, 50],
YOUNGCOUPLE: [15, 30, 65],
SOCIALITE: [12, 30, 70],
BUGCATCHER_F: [8, 20, 35],
ROUGHNECK: [18, 38, 85],
TEACHER: [12, 28, 60],
PRESCHOOLER_M: [5, 12, 25],
PRESCHOOLER_F: [5, 12, 25],
HAUNTEDGIRL_YOUNG: [10, 25, 55],
HAUNTEDGIRL: [12, 30, 65],
CLOWN: [10, 25, 50],
NURSE: [8, 20, 35],
WORKER: [12, 30, 65],
COOLTRAINER_M2: [22, 50, 100],
COOLTRAINER_F2: [22, 50, 100],
FARMER: [10, 25, 50],
PYROMANIAC: [20, 45, 90],
KIMONOGIRL: [12, 30, 60],
SAGE: [15, 30, 65],
PLAYER: [12, 30, 60],
POLICE: [20, 45, 90],
SKIER_F: [12, 28, 55],
DELIVERYMAN: [8, 20, 40],
RICHBOY: [15, 35, 70],
SCHOOLBOY: [10, 22, 40],
SCHOOLGIRL: [10, 22, 40],
TEAM_AQUA_GRUNT_M: [25, 60, 100],
TEAM_AQUA_GRUNT_F: [25, 60, 100],
TEAM_MAGMA_GRUNT_M: [25, 60, 100],
TEAM_MAGMA_GRUNT_F: [25, 60, 100],
TEAM_MAGMAQUA_GRUNT_M: [25, 60, 100],
TEAM_MAGMAQUA_GRUNT_F: [25, 60, 100],
}
end

View File

@@ -0,0 +1,118 @@
#####
# Util methods
#####
####
# Methods to be called from events
####
#actionType :
# :BATTLE
# :TRADE
# :PARTNER
def doPostBattleAction(actionType)
event = pbMapInterpreter.get_character(0)
map_id = $game_map.map_id if map_id.nil?
trainer = getRebattledTrainer(event.id,map_id)
trainer.clear_previous_random_events()
return if !trainer
case actionType
when :BATTLE
trainer = doNPCTrainerRematch(trainer)
when :TRADE
trainer = doNPCTrainerTrade(trainer)
when :PARTNER
partnerWithTrainer(event.id,map_id,trainer)
end
updateRebattledTrainer(event.id,map_id,trainer)
end
def setTrainerFriendship(trainer)
params = ChooseNumberParams.new
params.setRange(0,100)
params.setDefaultValue($game_map.map_id)
number = pbMessageChooseNumber("Frienship (0-100)?",params)
trainer.friendship = number
trainer.increase_friendship(0)
return trainer
end
#party: array of pokemon team
# [[:SPECIES,level], ... ]
#
#def customTrainerBattle(trainerName, trainerType, party_array, default_level=50, endSpeech="", sprite_override=nil,custom_appearance=nil)
def postBattleActionsMenu()
rematchCommand = "Rematch"
tradeCommand = "Trade Offer"
partnerCommand = "Partner up"
cancelCommand = "See ya!"
updateTeamDebugCommand = "(Debug) Simulate random event"
resetTrainerDebugCommand = "(Debug) Reset trainer"
setFriendshipDebugCommand = "(Debug) Set Friendship"
printTrainerTeamDebugCommand = "(Debug) Print team"
event = pbMapInterpreter.get_character(0)
map_id = $game_map.map_id if map_id.nil?
trainer = getRebattledTrainer(event.id,map_id)
options = []
options << rematchCommand
options << tradeCommand if trainer.friendship_level >= 1
options << partnerCommand if trainer.friendship_level >= 3
options << updateTeamDebugCommand if $DEBUG
options << resetTrainerDebugCommand if $DEBUG
options << setFriendshipDebugCommand if $DEBUG
options << printTrainerTeamDebugCommand if $DEBUG
options << cancelCommand
trainer = applyTrainerRandomEvents(trainer)
showPrerematchDialog
choice = optionsMenu(options,options.find_index(cancelCommand),options.find_index(cancelCommand))
case options[choice]
when rematchCommand
doPostBattleAction(:BATTLE)
when tradeCommand
doPostBattleAction(:TRADE)
when partnerCommand
doPostBattleAction(:PARTNER)
when updateTeamDebugCommand
echoln("")
echoln "---------------"
makeRebattledTrainerTeamGainExp(trainer,true)
evolveRebattledTrainerPokemon(trainer)
applyTrainerRandomEvents(trainer)
when resetTrainerDebugCommand
resetTrainerRebattle(event.id,map_id)
when setFriendshipDebugCommand
trainer = getRebattledTrainer(event.id,map_id)
trainer = setTrainerFriendship(trainer)
updateRebattledTrainer(event.id,map_id,trainer)
when printTrainerTeamDebugCommand
trainer = getRebattledTrainer(event.id,map_id)
printNPCTrainerCurrentTeam(trainer)
when cancelCommand
else
return
end
end

View File

@@ -0,0 +1,13 @@
COMMON_EVENT_TRAINER_REMATCH_PARTNER = 200
def partnerWithTrainer(eventId, mapID, trainer)
Kernel.pbAddDependency2(eventId,trainer.trainerName,COMMON_EVENT_TRAINER_REMATCH_PARTNER)
pbCancelVehicles
originalTrainer = pbLoadTrainer(trainer.trainerType, trainer.trainerName, 0)
Events.onTrainerPartyLoad.trigger(nil, originalTrainer)
for i in trainer.currentTeam
i.owner = Pokemon::Owner.new_from_trainer(originalTrainer)
i.calc_stats
end
$PokemonGlobal.partner = [trainer.trainerType, trainer.trainerName, 0, trainer.currentTeam]
end

View File

@@ -0,0 +1,195 @@
# frozen_string_literal: true
def printNPCTrainerCurrentTeam(trainer)
team_string = "["
trainer.currentTeam.each do |pokemon|
name= get_pokemon_readable_internal_name(pokemon)
level = pokemon.level
formatted_info = "#{name} (lv.#{level}), "
team_string += formatted_info
end
team_string += "]"
echoln "Trainer's current team is: #{team_string}"
end
def applyTrainerRandomEvents(trainer)
if trainer.has_pending_action
echoln "Trainer has pending action"
end
return trainer if trainer.has_pending_action
trainer.clear_previous_random_events
#time_passed = trainer.getTimeSinceLastAction
#return trainer if time_passed < TIME_FOR_RANDOM_EVENTS
# Weighted chances out of 10
weighted_events = [
[:CATCH, 3],
[:FUSE, 6],
[:REVERSE, 1],
[:UNFUSE, 20]
]
# Create a flat array of events based on weight
event_pool = weighted_events.flat_map { |event, weight| [event] * weight }
selected_event = event_pool.sample
if selected_event
echoln "Trying to do random event: #{selected_event}"
end
return trainer if selected_event.nil?
case selected_event
when :CATCH
trainer = catch_new_team_pokemon(trainer)
when :FUSE
trainer = fuse_random_team_pokemon(trainer)
when :UNFUSE
trainer = unfuse_random_team_pokemon(trainer)
when :REVERSE
trainer = reverse_random_team_pokemon(trainer)
end
trainer.set_pending_action(true)
printNPCTrainerCurrentTeam(trainer)
return trainer
end
def chooseEncounterType(trainerClass)
water_trainer_classes = [:SWIMMER_F, :SWIMMER_M, :FISHERMAN]
if water_trainer_classes.include?(trainerClass )
chance_of_land_encounter = 1
chance_of_surf_encounter= 5
chance_of_cave_encounter = 1
chance_of_fishing_encounter = 5
else
chance_of_land_encounter = 5
chance_of_surf_encounter= 1
chance_of_cave_encounter = 5
chance_of_fishing_encounter = 1
end
if pbCheckHiddenMoveBadge(Settings::BADGE_FOR_SURF, false)
chance_of_surf_encounter =0
chance_of_fishing_encounter = 0
end
possible_encounter_types = []
if $PokemonEncounters.has_land_encounters?
possible_encounter_types += [:Land] * chance_of_land_encounter
end
if $PokemonEncounters.has_cave_encounters?
possible_encounter_types += [:Cave] * chance_of_cave_encounter
end
if $PokemonEncounters.has_water_encounters?
possible_encounter_types += [:GoodRod] * chance_of_fishing_encounter
possible_encounter_types += [:Water] * chance_of_surf_encounter
end
echoln "possible_encounter_types: #{possible_encounter_types}"
return getTimeBasedEncounter(possible_encounter_types.sample)
end
def getTimeBasedEncounter(encounter_type)
time = pbGetTimeNow
return $PokemonEncounters.find_valid_encounter_type_for_time(encounter_type, time)
end
def catch_new_team_pokemon(trainer)
return trainer if trainer.currentTeam.length >= 6
encounter_type = chooseEncounterType(trainer.trainerType)
return trainer if !encounter_type
echoln "Catching a pokemon via encounter_type #{encounter_type}"
wild_pokemon = $PokemonEncounters.choose_wild_pokemon(encounter_type)
echoln wild_pokemon
if wild_pokemon
trainer.currentTeam << Pokemon.new(wild_pokemon[0],wild_pokemon[1])
trainer.log_catch_event(wild_pokemon[0])
end
return trainer
end
def reverse_random_team_pokemon(trainer)
eligible_pokemon = trainer.list_team_fused_pokemon
return trainer if eligible_pokemon.length < 1
return trainer if trainer.currentTeam.length > 5
pokemon_to_reverse = eligible_pokemon.sample
old_species = pokemon_to_reverse.species
trainer.currentTeam.delete(pokemon_to_reverse)
body_pokemon = get_body_species_from_symbol(pokemon_to_reverse.species)
head_pokemon = get_head_species_from_symbol(pokemon_to_reverse.species)
pokemon_to_reverse.species = getFusedPokemonIdFromSymbols(head_pokemon,body_pokemon)
trainer.currentTeam.push(pokemon_to_reverse)
trainer.log_reverse_event(old_species,pokemon_to_reverse.species)
return trainer
end
def unfuse_random_team_pokemon(trainer)
eligible_pokemon = trainer.list_team_fused_pokemon
return trainer if eligible_pokemon.length < 1
return trainer if trainer.currentTeam.length > 5
pokemon_to_unfuse = eligible_pokemon.sample
echoln pokemon_to_unfuse.owner.name
echoln trainer.trainerName
return trainer if pokemon_to_unfuse.owner.name != trainer.trainerName
body_pokemon = get_body_id_from_symbol(pokemon_to_unfuse.species)
head_pokemon = get_head_id_from_symbol(pokemon_to_unfuse.species)
level = calculateUnfuseLevelOldMethod(pokemon_to_unfuse,false)
trainer.currentTeam.delete(pokemon_to_unfuse)
trainer.currentTeam.push(Pokemon.new(body_pokemon,level))
trainer.currentTeam.push(Pokemon.new(head_pokemon,level))
trainer.log_unfusion_event(pokemon_to_unfuse.species, body_pokemon, head_pokemon)
return trainer
end
def fuse_random_team_pokemon(trainer)
eligible_pokemon = trainer.list_team_unfused_pokemon
return trainer if eligible_pokemon.length < 2
pokemon_to_fuse = eligible_pokemon.sample(2)
body_pokemon = pokemon_to_fuse[0]
head_pokemon = pokemon_to_fuse[1]
fusion_species = getFusedPokemonIdFromSymbols(body_pokemon.species,head_pokemon.species)
level = (body_pokemon.level + head_pokemon.level)/2
fused_pokemon = Pokemon.new(fusion_species,level)
trainer.currentTeam.delete(body_pokemon)
trainer.currentTeam.delete(head_pokemon)
trainer.currentTeam.push(fused_pokemon)
trainer.log_fusion_event(body_pokemon.species,head_pokemon.species,fusion_species)
return trainer
end
def getBestMatchingPreviousRandomEvent(trainer_data, previous_events)
return nil if trainer_data.nil? || previous_events.nil?
priority = [:CATCH, :EVOLVE, :FUSE, :UNFUSE, :REVERSE]
event_message_map = {
CATCH: trainer_data.preRematchText_caught,
EVOLVE: trainer_data.preRematchText_evolved,
FUSE: trainer_data.preRematchText_fused,
UNFUSE: trainer_data.preRematchText_unfused,
REVERSE: trainer_data.preRematchText_reversed
}
sorted_events = previous_events.sort_by do |event|
priority.index(event.eventType) || Float::INFINITY
end
sorted_events.find { |event| event_message_map[event.eventType] }
end

View File

@@ -0,0 +1,273 @@
TRAINER_CLASS_FAVORITE_TYPES =
{
AROMALADY: [:GRASS, :FAIRY],
BEAUTY: [:FAIRY, :WATER, :NORMAL, :GRASS],
BIKER: [:POISON, :DARK],
BIRDKEEPER: [:FLYING, :NORMAL],
BUGCATCHER: [:BUG],
BURGLAR: [:FIRE, :DARK],
CHANNELER: [:GHOST, :PSYCHIC],
CUEBALL: [:FIGHTING, :STEEL],
ENGINEER: [:ELECTRIC, :STEEL],
FISHERMAN: [:WATER],
GAMBLER: [:NORMAL, :PSYCHIC],
GENTLEMAN: [:NORMAL, :STEEL],
HIKER: [:ROCK, :GROUND],
JUGGLER: [:PSYCHIC, :GHOST, :NORMAL, :POISON],
LADY: [:FAIRY, :NORMAL],
PAINTER: [:NORMAL, :PSYCHIC, :GRASS],
POKEMANIAC: [:DRAGON, :GROUND],
POKEMONBREEDER: [:NORMAL, :GRASS],
PROFESSOR: [:NORMAL, :PSYCHIC],
ROCKER: [:ELECTRIC, :FIRE],
RUINMANIAC: [:GROUND, :ROCK, :PSYCHIC],
SAILOR: [:WATER, :FIGHTING],
SCIENTIST: [:ELECTRIC, :STEEL, :POISON],
SUPERNERD: [:ELECTRIC, :PSYCHIC, :STEEL],
TAMER: [:NORMAL, :DARK],
BLACKBELT: [:FIGHTING],
CRUSHGIRL: [:FIGHTING],
CAMPER: [:BUG, :NORMAL, :GRASS],
PICNICKER: [:GRASS, :NORMAL],
COOLTRAINER_M: [:DRAGON, :STEEL, :FIRE],
COOLTRAINER_F: [:ICE, :PSYCHIC, :FAIRY],
YOUNGSTER: [:NORMAL, :BUG, :GRASS, :FLYING],
LASS: [:NORMAL, :FAIRY],
POKEMONRANGER_M: [:GRASS, :GROUND],
POKEMONRANGER_F: [:GRASS, :WATER],
PSYCHIC_M: [:PSYCHIC, :GHOST],
PSYCHIC_F: [:PSYCHIC, :FAIRY],
SWIMMER_M: [:WATER, :ICE],
SWIMMER_F: [:WATER, :ICE],
SWIMMER2_M: [:WATER],
SWIMMER2_F: [:WATER],
TUBER_M: [:WATER],
TUBER_F: [:WATER],
TUBER2_M: [:WATER],
TUBER2_F: [:WATER],
COOLCOUPLE: [:FIRE, :ICE],
CRUSHKIN: [:FIGHTING],
SISANDBRO: [:WATER, :GROUND],
TWINS: [:FAIRY, :NORMAL],
YOUNGCOUPLE: [:NORMAL, :PSYCHIC],
SOCIALITE: [:FAIRY, :NORMAL],
BUGCATCHER_F: [:BUG],
ROUGHNECK: [:DARK, :FIGHTING],
TEACHER: [:PSYCHIC, :NORMAL],
PRESCHOOLER_M: [:NORMAL, :BUG],
PRESCHOOLER_F: [:FAIRY, :NORMAL],
HAUNTEDGIRL_YOUNG: [:GHOST],
HAUNTEDGIRL: [:GHOST, :DARK],
CLOWN: [:PSYCHIC, :FAIRY],
NURSE: [:NORMAL, :FAIRY],
WORKER: [:STEEL, :GROUND],
COOLTRAINER_M2: [:FIGHTING, :STEEL],
COOLTRAINER_F2: [:PSYCHIC, :ICE],
FARMER: [:GRASS, :GROUND, :NORMAL],
PYROMANIAC: [:FIRE],
KIMONOGIRL: [:FAIRY, :PSYCHIC, :GHOST],
SAGE: [:PSYCHIC, :GHOST],
PLAYER: [:ICE, :FIGHTING],
POLICE: [:DARK, :FIGHTING],
SKIER_F: [:ICE],
DELIVERYMAN: [:NORMAL],
}
#Higher values: pickier
TRAINER_CLASS_PICKINESS = {
AROMALADY: 1.8,
BEAUTY: 2.2,
BIKER: 1.2,
BIRDKEEPER: 1.4,
BUGCATCHER: 1.1,
BURGLAR: 1.3,
CHANNELER: 1.7,
CUEBALL: 1.3,
ENGINEER: 2.0,
FISHERMAN: 1.4,
GAMBLER: 1.5,
GENTLEMAN: 2.3,
HIKER: 1.5,
JUGGLER: 1.8,
LADY: 2.4,
PAINTER: 2.0,
POKEMANIAC: 1.6,
POKEMONBREEDER: 1.9,
PROFESSOR: 2.5,
ROCKER: 1.4,
RUINMANIAC: 1.5,
SAILOR: 1.3,
SCIENTIST: 2.0,
SUPERNERD: 1.9,
TAMER: 1.5,
BLACKBELT: 1.7,
CRUSHGIRL: 1.6,
CAMPER: 1.2,
PICNICKER: 1.2,
COOLTRAINER_M: 2.4,
COOLTRAINER_F: 2.4,
YOUNGSTER: 0.9,
LASS: 1.0,
POKEMONRANGER_M: 2.0,
POKEMONRANGER_F: 2.0,
PSYCHIC_M: 2.0,
PSYCHIC_F: 2.0,
SWIMMER_M: 1.0,
SWIMMER_F: 1.0,
SWIMMER2_M: 1.0,
SWIMMER2_F: 1.0,
TUBER_M: 0.8,
TUBER_F: 0.8,
TUBER2_M: 0.8,
TUBER2_F: 0.8,
COOLCOUPLE: 2.1,
CRUSHKIN: 1.7,
SISANDBRO: 1.3,
TWINS: 1.0,
YOUNGCOUPLE: 1.6,
SOCIALITE: 2.3,
BUGCATCHER_F: 1.1,
ROUGHNECK: 1.4,
TEACHER: 2.0,
PRESCHOOLER_M: 0.6,
PRESCHOOLER_F: 0.6,
HAUNTEDGIRL_YOUNG: 1.3,
HAUNTEDGIRL: 1.7,
CLOWN: 1.5,
NURSE: 2.0,
WORKER: 1.6,
COOLTRAINER_M2: 2.4,
COOLTRAINER_F2: 2.4,
FARMER: 1.5,
PYROMANIAC: 1.6,
KIMONOGIRL: 2.2,
SAGE: 2.1,
PLAYER: 1.0,
POLICE: 1.8,
SKIER_F: 1.6,
DELIVERYMAN: 1.3
}
def evaluate_pokemon_worth(pkmn, compare_level: nil)
species_data = pkmn.species_data
return 0 unless species_data
level = pkmn.level
level_diff = compare_level ? (level - compare_level) : 0
level_score = level * 2 + [level_diff, 0].max * 1.5 # bonus if player's level is higher
base_stats_score = species_data.base_stats.values.sum / 10.0
rarity_score = (255 - species_data.catch_rate) / 5.0
iv_score = (pkmn.iv&.values&.sum || 0) / 4.0
shiny_score = pkmn.shiny? ? 50 : 0
fusion_bonus = pkmn.isFusion? ? 40 : 0
score = level_score +
base_stats_score +
rarity_score +
iv_score +
shiny_score +
fusion_bonus
echoln("#{pkmn.name} - Score : #{score}")
return score
end
def offerPokemonForTrade(player_pokemon, npc_party, trainer_class)
player_score = evaluate_pokemon_worth(player_pokemon)
pickiness = TRAINER_CLASS_PICKINESS[trainer_class] || 1.0
# Evaluate all NPC Pokémon scores
npc_scores = npc_party.map do |npc_pkmn|
[npc_pkmn, evaluate_pokemon_worth(npc_pkmn, compare_level: player_pokemon.level)]
end
best_npc_pokemon, best_score = npc_scores.max_by { |_, score| score }
return best_npc_pokemon if player_score > best_score
max_difference = [player_score, 100].min * pickiness
candidates = npc_scores.select do |npc_pkmn, npc_score|
(npc_score - player_score).abs <= max_difference
end
return nil if candidates.empty?
candidates.min_by do |_, npc_score|
(npc_score - player_score).abs
end.first
end
def doNPCTrainerTrade(trainer)
echoln trainer.getTimeSinceLastTrade
if trainer.isNextTradeReady?
pbMessage(_INTL("The trainer is not ready to trade yet. Wait a little bit before you make your offer."))
return trainer
end
return generateTrainerTradeOffer(trainer)
end
#prefered type depends on the trainer class
#
def generateTrainerTradeOffer(trainer)
bg_image_id=20
wanted_type = trainer.favorite_type
wanted_type = :NORMAL if !wanted_type
wanted_type_name = GameData::Type.get(wanted_type).real_name
trainerClassName = GameData::TrainerType.get(trainer.trainerType).real_name
pbMessage(_INTL("#{trainerClassName} #{trainer.trainerName} is looking for \\C[1]#{wanted_type_name}-type Pokémon\\C[0]. Which Pokémon do you want to trade?."),)
pbChoosePokemon(1,2,
proc {|pokemon|
pokemon.hasType?(wanted_type)
})
chosen_index = pbGet(1)
echoln pbGet(1)
if chosen_index && chosen_index >= 0
chosen_pokemon = $Trainer.party[chosen_index]
offered_pokemon = offerPokemonForTrade(chosen_pokemon,trainer.currentTeam,trainer.trainerType)
if !offered_pokemon
pbMessage(_INTL("#{trainerClassName} #{trainer.trainerName} does not want to trade..."))
return trainer
end
pif_sprite = BattleSpriteLoader.new.get_pif_sprite_from_species(offered_pokemon.species)
pif_sprite.dump_info()
message = _INTL("#{trainerClassName} #{trainer.trainerName} is offering #{offered_pokemon.name} (Level #{offered_pokemon.level}) for your #{chosen_pokemon.name}.")
showPokemonInPokeballWithMessage(pif_sprite, message)
if pbConfirmMessage(_INTL("Trade away #{chosen_pokemon.name} for #{trainerClassName} #{trainer.trainerName}'s #{offered_pokemon.name}?"))
pbStartTrade(chosen_index, offered_pokemon,offered_pokemon.name,trainer.trainerName,0)
updated_party = trainer.currentTeam
updated_party.delete(offered_pokemon)
updated_party << chosen_pokemon.clone
trainer.previous_trade_timestamp= Time.now
trainer.increase_friendship(20)
return trainer
end
end
return trainer
#todo
#
# NPC says "I'm looking for X or Y tyflpe Pokemon (prefered Pokemon can be determined when initializing from a pool of types that depends on the trainer class)
# Also possible to pass a list of specific Pokemon in trainers.txt that the trainer will ask for instead if it's defined
#
# you select one of your Pokemon and he gives you one for it
# prioritize recently caught pokemon
# prioritive weaker Pokemon
#
#Assign a score to each Pokemon in trainer's team. calculate the same score for trainer's pokemon - select which
# one is closer
#
# NPC says "I can offer A in exchange for your B.
# -Yes -> Trade, update trainer team to put the player's pokemon in there
# Cannot trade again with the same trainer for 5 minutes
# "You just traded with this trainer. Wait a bit before you make another offer
# -No
trainer.set_pending_action(false) if trainer
return trainer
end

View File

@@ -0,0 +1,162 @@
# #===============================================================================
# # * Weather System Configuration
# #===============================================================================
#
# module WeatherConfig
# # Set to false to use the Weather System.
# NO_WEATHER = false # Default: false
#
# # Set to true to show the weather on the Town Map.
# SHOW_WEATHER_ON_MAP = true # Default: true
#
# # Set to true to use the computer's time. Will not work without Unreal Time System.
# USE_REAL_TIME = true # Default: true
#
# # Set to true to have the weather change at midnight.
# CHANGE_MIDNIGHT = true # Default: true
#
# # Define the min and max amount of time (in hours) before the weather changes.
# # Set the same number to not randomize the amount of time before the weather changes.
# CHANGE_TIME_MIN = 1 # Default: 1
# CHANGE_TIME_MAX = 4 # Default: 4
#
# # Set to true to if you want to force the weather to change when interacting with certain events.
# # Use pbForceUpdateWeather in an event to update all zone weathers.
# # Use pbForceUpdateZoneWeather(zone) in an event to update the weather of a zone.
# FORCE_UPDATE = true # Default: false
#
# # Set to true to have the outdoor maps change with seasons.
# # The map's appearance will update when leaving an indoor map.
# SEASON_CHANGE = false # Default: false
#
# # Set to true if your game starts outdoors and want to show the season splash when going somewhere indoors.
# # Set to false if your game starts indoors and want to show the season splash when going somewhere outdoors.
# OUTDOOR = false # Default: false
#
# # Array with the ID of outside tilesets that will change with seasons.
# OUTDOOR_TILESETS = []
#
# # The difference between the ID of the tileset defined for an outdoor map and it's season version.
# # The difference has to be the same for any tileset defined in OUTDOOR_TILESETS.
# # Use the same season tileset as the default outdoor map tileset and define the diference for that season as 0.
# SUMMER_TILESET = 22
# AUTUMN_TILESET = 24
# WINTER_TILESET = 26
# SPRING_TILESET = 0
#
# #===============================================================================
# # * Weather Substitute
# #===============================================================================
# # A hash with the ID of the maps that will have or not have certain weathers.
# # The ID of the weather has to be of the main one you want to change with WEATHER_SUBSTITUTE.
# # Use "exclude" to define a list of maps that will not use that weather when it's the main one.
# # Any maps of a zone not added on the "exclude" list will use the main weather.
# # Use "include" to define a list of maps that will use that weather when it's the main one.
# # Any maps of a zone not added on the "include" list will use the secondary weather.
# MAPS_SUBSTITUTE = {
# :Snow => ["exclude", 1, 4],
# :Blizzard => ["exclude", 1, 4],
# :Sandstorm => ["include", 5]
# }
#
# # The ID of the weathers that will substitute the main when appropiate (conditions defined in MAPS_SUBSTITUTE).
# # There has to be a hash (defined between {}) for each defined zone with weather to substitute.
# # Any weather not defined in the hash for a zone will use the main weather instead.
# WEATHER_SUBSTITUTE = [
# {:None => :None, :Rain => :Rain, :Storm => :Storm, :Snow => :Rain, :Blizzard => :Storm, :Sandstorm => :None, :HeavyRain => :HeavyRain, :Sun => :Sun, :Fog => :Fog},
# {:Snow => :Rain, :Blizzard => :Storm, :Sandstorm => :None},
# {:Snow => :Rain, :Blizzard => :HeavyRain}
# ]
#
# #===============================================================================
# # * Weather Names
# #===============================================================================
# # A hash that contains the ID of weather and the name to display for each one.
# # Using .downcase will make them lowercase.
# def self.weather_names
# return {
# :None => _INTL("None"),
# :Rain => _INTL("Rain"),
# :Storm => _INTL("Storm"),
# :Snow => _INTL("Snow"),
# :Blizzard => _INTL("Blizzard"),
# :Sandstorm => _INTL("Sandstorm"),
# :HeavyRain => _INTL("Heavy rain"),
# :Sun => _INTL("Sun"),
# :Fog => _INTL("Fog")
# }
# end
# #===============================================================================
# # * Zones Configuration
# #===============================================================================
# # Arrays of id of the maps of each zone. Each array within the main array is a zone.
# # The maps within each zone will have the same weather at the same time.
# # Each zone may have a different weather than the others.
# ZONE_MAPS = [
# [9, 5, 8, 11],#Littleroot-Oldale
# [10, 2, 12], #Petalburg
# ]
# #===============================================================================
# # * Map Display
# #===============================================================================
# # Array of hashes to get each map's position in the Town Map. Each hash corresponds to a zone in ZONE_MAPS.
# # In "Map Name" you have to put the name the Town Map displays for that point.
# # In Map ID you have to put the ID of the map the name corresponds to, like in ZONE_MAPS.
# MAPS_POSITIONS = [
# #{"Map Name" => Map ID},
# {"Littleroot Town" => 9, "Route 101" => 5, "Oldale Town" => 8, "Route 103" => 11},
# {"Route 102" => 10, "Petalburg Town" => 2, "Route 103" => 12},
# ]
#
# # A hash for the plugin to display the proper weather image on the map.
# # They have to be on Graphics/Pictures/Weather (in 20+) or Graphics/UI/Weather (in 21+).
# WEATHER_IMAGE = {
# :Rain => "mapRain",
# :Storm => "mapStorm",
# :Snow => "mapSnow",
# :Blizzard => "mapBlizzard",
# :Sandstorm => "mapSand",
# :HeavyRain => "mapRain",
# :Sun => "mapSun",
# :Fog => "mapFog"
# }
# #===============================================================================
# # * Season Probability Configuration
# #===============================================================================
# # Arrays of probability of weather for each zone in the different seasons.
# # Each array within the main array corresponds to a zone in ZONE_MAPS.
# # Put 0 to weather you don't want if you define a probability after it.
# # If your game doesn't use seasons, edit the probabilities of one season and copy it to the others.
#
# # Probability of weather in summer.
# # Order: None, Rain, Storm, Snow, Blizzard, Sandstorm, HeavyRain, Sun/Sunny, Fog
# ZONE_WEATHER_SUMMER = [
# [50, 20, 3, 0, 0, 0, 5, 30],
# [40, 50],
# [60]
# ]
#
# # Probability of weather in autumn.
# # Order: None, Rain, Storm, Snow, Blizzard, Sandstorm, HeavyRain, Sun/Sunny, Fog
# ZONE_WEATHER_AUTUMN = [
# [50, 20, 3, 0, 0, 0, 5, 30],
# [40, 50],
# [60]
# ]
#
# # Probability of weather in winter.
# # Order: None, Rain, Storm, Snow, Blizzard, Sandstorm, HeavyRain, Sun/Sunny, Fog
# ZONE_WEATHER_WINTER = [
# [50, 20, 3, 0, 0, 0, 5, 30],
# [40, 50],
# [60]
# ]
#
# # Probability of weather in spring.
# # Order: None, Rain, Storm, Snow, Blizzard, Sandstorm, HeavyRain, Sun/Sunny, Fog
# ZONE_WEATHER_SPRING = [
# [50, 20, 3, 0, 0, 0, 5, 30],
# [40, 50],
# [60]
# ]
# end