Update 6.8

This commit is contained in:
chardub
2026-07-10 15:42:06 -04:00
parent 5b85e72cb2
commit 6a6f126a18
7871 changed files with 493194 additions and 224826 deletions
@@ -1,28 +1,28 @@
class BattledTrainer
DELAY_BETWEEN_NPC_TRADES = 180 #In seconds (3 minutes)
DELAY_BETWEEN_NPC_TRADES = 180 # In seconds (3 minutes)
MAX_FRIENDSHIP = 100
attr_accessor :trainerType
attr_accessor :trainerName
attr_accessor :trainerKey
attr_accessor :currentTeam #list of Pokemon. The game selects in this list for trade offers. They can increase levels & involve as you rebattle them.
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
# 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.
# 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
# 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
# Healing items that are in that list can be used by the trainer in rematches
#
attr_accessor :foundItems
attr_accessor :inventory
attr_accessor :nb_rematches
#What the trainer currently wants to do
# 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?
@@ -37,39 +37,105 @@ class BattledTrainer
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 :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 # increases the more you interact with them, unlocks more interact options
attr_accessor :friendship_level
def initialize(trainerType,trainerName,trainerVersion,trainerKey)
attr_accessor :overworld_sprite
attr_reader :favorite
def initialize(trainerType, trainerName, trainerVersion, trainerKey)
@trainerKey = trainerKey
@trainerType = trainerType
@trainerName = trainerName
@currentTeam = loadOriginalTrainerTeam(trainerVersion)
@foundItems = []
original_trainer = pbLoadTrainer(@trainerType, @trainerName, trainerVersion)
@currentTeam = loadOriginalTrainerTeam(trainerVersion, original_trainer)
@inventory = original_trainer.items || []
@nb_rematches = 0
@currentStatus = :IDLE
@previous_status = :IDLE
@previous_trade_timestamp = Time.now-DELAY_BETWEEN_NPC_TRADES
@previous_random_events =[]
@has_pending_action=false
@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
@overworld_sprite = ""
@location = _INTL("Unknown location")
@favorite = false
end
def id
return @trainerKey
end
def location
if @location.nil?
return _INTL("Unknown location")
end
return @location
end
# For double trainer classes like twins, etc. Adds an additional double rematch option.
def setLinkedTrainer(linked_trainer_event)
return if @linked_event
@linked_event = linked_trainer_event
end
def setOverworldSprite(overworld_sprite)
@overworld_sprite = overworld_sprite
end
def setLocation(location_name)
@location = location_name
end
def setFavorite(value)
@favorite = value
end
def getLinkedTrainer()
trainer = getRebattledTrainer(@linked_event, $game_map.map_id)
return trainer if trainer
return nil
end
def friendship_level
@friendship_level =0 if !@friendship_level
@friendship_level = 0 if !@friendship_level
return @friendship_level
end
def list_battle_items
@inventory = [] unless @inventory
battle_items = []
@inventory.each do |item_id|
item = GameData::Item.get(item_id)
can_use_in_battle = item.has_battle_use? && !item.is_poke_ball?
battle_items << item_id if can_use_in_battle
end
return battle_items
end
def list_evolution_items
@inventory = [] unless @inventory
evo_items = []
@inventory.each do |item_id|
item = GameData::Item.get(item_id)
evo_items << item_id if item.is_evolution_stone?
end
return evo_items
end
def increase_friendship(amount)
@friendship=0 if !@friendship
@friendship_level=0 if !@friendship_level
@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
@@ -77,24 +143,162 @@ class BattledTrainer
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 {1} {2}!",trainerClassName,@trainerName))
pbMessage(_INTL("\\C[3]Friendship increased with {1} {2}!", 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!"))
pbMessage(_INTL("They will now give you items after rematches from time to time!"))
$Trainer.nb_npc_friends = 0 unless $Trainer.nb_npc_friends
$Trainer.nb_npc_friends += 1 # odds of shiny pokemon increases slightly the more NPCs at matx friendship you have
when 3
pbMessage(_INTL("You can now partner up with them!"))
# pbMessage(_INTL("You can now partner up with them!"))
end
echoln "🎉 #{@trainerName}'s friendship level increased to #{@friendship_level}!"
echoln "#{@trainerName}'s friendship level increased to #{@friendship_level}!"
end
if @friendship_level >= 3 && !@gave_clothes
tryGiftTrainerClothes(@trainerType)
@gave_clothes = true
end
end
def process_party_pokemon_held_items
store_held_item_chance = 25
store_evolution_item_chance = 70
store_usable_item_chance = 80
chance_to_give_item = 60
#Move pokemon held items to inventory
@currentTeam.each do |pokemon|
next unless pokemon.item
next if pokemon.item.id == :EVERSTONE
held_item = pokemon.item
is_holdable_item = HELD_ITEMS.include?(held_item.id)
echoln "#{held_item} #{is_holdable_item}"
next if is_holdable_item && rand(100) >= store_held_item_chancex
is_evolution_item = held_item.is_evolution_stone?
is_battle_item = held_item.has_battle_use?
chance_to_store = store_held_item_chance
chance_to_store = store_evolution_item_chance if is_evolution_item
chance_to_store = store_usable_item_chance if is_battle_item
if rand(100) <= chance_to_store
@inventory << held_item.id
echoln "#{@trainerType} #{@trainerName} took the #{pokemon.item} from #{pokemon.name}"
pokemon.item = nil
end
end
#give inventory items to pokemon
items_to_delete = []
@inventory.each do |item|
is_holdable_item = HELD_ITEMS.include?(item)
if is_holdable_item && rand(100) <= chance_to_give_item
party_pokemon_without_items = []
# give to random party member
party_index = 0
@currentTeam.each do |pokemon|
party_pokemon_without_items << party_index unless pokemon.item
party_index+=1
end
unless party_pokemon_without_items.empty?
chosen_pokemon_index= party_pokemon_without_items.sample
@currentTeam[chosen_pokemon_index].item = item
echoln "#{@trainerType} #{@trainerName} gave a #{item} to #{@currentTeam[chosen_pokemon_index].name}"
items_to_delete << item
end
end
end
items_to_delete.each do |item|
index = @inventory.index(item)
@inventory.delete_at(index) if index
end
end
def tryGiftTrainerClothes(trainerType)
event = pbMapInterpreter.get_character(0)
case trainerType
when :BUGCATCHER
if !hasClothes?(CLOTHES_BUG_CATCHER_RSE)
pbCallBub(2, event.id)
pbMessage(_INTL("Oh, you really like bugs too, right? Well, I have something for you!"))
obtainClothes(CLOTHES_BUG_CATCHER_RSE)
elsif !hasClothes?(CLOTHES_BUG_CATCHER_ORAS)
pbCallBub(2, event.id)
pbMessage(_INTL("Hey! You know, if you're looking to catch more bugs, you should try putting this on!"))
obtainClothes(CLOTHES_BUG_CATCHER_ORAS)
obtainHat(HAT_BUG_CATCHER_ORAS)
end
when :FISHERMAN
if !hasClothes?(CLOTHES_FISHERMAN_ORAS)
pbCallBub(2, event.id)
pbMessage(_INTL("Ho ho! You've been hanging around while I fish for so long, you're practically an angler yourself. It's time you dressed like one!"))
obtainClothes(CLOTHES_FISHERMAN_ORAS)
obtainHat(HAT_FISHERMAN_ORAS)
end
when :YOUNGSTER
if !hasClothes?(CLOTHES_YOUNGSTER_RSE)
pbCallBub(2, event.id)
pbMessage(_INTL("All these rematches are so fun! Here, you should dress like me!"))
obtainClothes(CLOTHES_YOUNGSTER_RSE)
elsif !hasClothes?(CLOTHES_YOUNGSTER_ORAS)
pbCallBub(2, event.id)
pbMessage(_INTL("I give clothes to all of my friends! Here you go!"))
obtainClothes(CLOTHES_YOUNGSTER_ORAS)
elsif !hasClothes?(CLOTHES_YOUNGSTER_HGSS)
pbCallBub(2, event.id)
pbMessage(_INTL("You moved here from Johto, right? My friend from Johto wears clothes like these!"))
obtainClothes(CLOTHES_YOUNGSTER_HGSS)
end
when :LADY
if !hasClothes?(CLOTHES_LADY)
pbCallBub(2, event.id)
pbMessage(_INTL("Oh my goodness, look at your clothes! This simply won't do. Here, I bought this for you!"))
obtainClothes(CLOTHES_LADY)
end
when :TUBER_M, :TUBER_F # Todo: Change for Swimmer Male when it's in the game!
if !hasClothes?(CLOTHES_SWIMMING_M)
pbCallBub(2, event.id)
pbMessage(_INTL("The beach is so fun! Oh! Wear this before you go swimming in the water!"))
obtainClothes(CLOTHES_SWIMMING_M)
end
when :SAILOR # Todo: Change for Swimmer Male when it's in the game!
if !hasClothes?(CLOTHES_SAILOR)
pbCallBub(2, event.id)
pbMessage(_INTL("Sailor! You're a good battler, but you need to wear something this if you want to conquer the sea!"))
obtainClothes(CLOTHES_SAILOR)
obtainHat(HAT_SAILOR)
end
when :PSYCHIC_M
if !hasClothes?(CLOTHES_PSYSHAMAN_M)
pbCallBub(2, event.id)
pbMessage(_INTL("Don't say anything... I know you want this."))
obtainClothes(CLOTHES_PSYSHAMAN_M)
end
when :PSYCHIC_F
if !hasClothes?(CLOTHES_PSYSHAMAN_F)
pbCallBub(2, event.id)
pbMessage(_INTL("Don't say anything... I know you want this."))
obtainClothes(CLOTHES_PSYSHAMAN_F)
end
when :POKEFAN_M, :POKEFAN_F
possible_masks = [HAT_POOCHYENA_MASK, HAT_LOTAD_MASK, HAT_ZIGZAGOON_MASK, HAT_WURMPLE_MASK]
unobtained_masks = possible_masks.reject { |hatID| hasHat?(hatID) }
unless unobtained_masks.empty?
pbCallBub(2, event.id)
pbMessage(_INTL("\\PN, you're a true Poké Fan like me! Here's a fun mask for you or your Pokémon!"))
obtainHat(unobtained_masks.sample)
end
end
end
def set_custom_appearance(trainer_appearance)
@@ -110,7 +314,7 @@ class BattledTrainer
end
def set_pending_action(value)
@has_pending_action=value
@has_pending_action = value
end
def log_evolution_event(unevolved_pokemon_species, evolved_pokemon_species)
@@ -126,9 +330,9 @@ class BattledTrainer
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
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
@@ -158,19 +362,20 @@ class BattledTrainer
event = BattledTrainerRandomEvent.new(:CATCH)
event.caught_pokemon = new_pokemon_species
@previous_random_events = [] unless @previous_random_events
@previous_random_events << event
@previous_random_events << event
end
def clear_previous_random_events()
@previous_random_events = []
end
def loadOriginalTrainer(trainerVersion=0)
return pbLoadTrainer(@trainerType,@trainerName,trainerVersion)
def loadOriginalTrainer(trainerVersion = 0)
return pbLoadTrainer(@trainerType, @trainerName, trainerVersion)
end
def loadOriginalTrainerTeam(trainerVersion=0)
original_trainer = pbLoadTrainer(@trainerType,@trainerName,trainerVersion)
def loadOriginalTrainerTeam(trainerVersion = 0, original_trainer=nil)
original_trainer = pbLoadTrainer(@trainerType, @trainerName, trainerVersion) unless original_trainer
return if !original_trainer
echoln "Loading Trainer #{@trainerType}"
current_party = []
@@ -178,10 +383,10 @@ class BattledTrainer
echoln "PartyMember: #{partyMember}"
if partyMember.is_a?(Pokemon)
current_party << partyMember
elsif partyMember.is_a?(Array) #normally always gonna be this
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)
current_party << Pokemon.new(pokemon_species, pokemon_level, original_trainer.name)
else
echoln "Could not add Pokemon #{partyMember} to rematchable trainer's party."
end
@@ -196,7 +401,15 @@ class BattledTrainer
end
def isNextTradeReady?()
return getTimeSinceLastTrade < DELAY_BETWEEN_NPC_TRADES
return getTimeSinceLastTrade >= DELAY_BETWEEN_NPC_TRADES
end
def can_trade?()
trade_unlocked = @friendship_level >= FRIENDSHIP_LEVEL_FOR_TRADE
if trade_unlocked
return isNextTradeReady?
end
return false
end
def list_team_unfused_pokemon
@@ -11,15 +11,107 @@ 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
canLose=false, outcomeVar=1,
name_override = nil, trainer_type_overide = nil,
event_id = nil, map_id = nil)
trainer_data = GameData::Trainer.get(trainerID,trainerName,trainerPartyID)
displayPreBattleText(trainer_data)
map_id = $game_map.map_id
result = original_pbTrainerBattle(trainerID, trainerName, endSpeech,doubleBattle,trainerPartyID,
canLose, outcomeVar,name_override,trainer_type_overide,event_id,map_id)
updateRematchableTrainer(trainerID, trainerName, trainerPartyID, event_id, nil, map_id) 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)
def displayPreBattleText(trainer_data)
if trainer_data.battleText && !trainer_data.battleText.empty? && @event_id
messages = trainer_data.battle_text.split("<br>")
messages.each do |msg|
msg = msg.gsub("<PLAYER_NAME>", $Trainer.name)
pbCallBub(2,@event_id)
pbMessage(msg)
end
end
end
# Important: Use this instead of pbDoubleBattle and pbTripleBattle so that the trainers are rematchable!
# trainers_array is an array of 2 or 3 arrays defining a trainer like such
# [:TRAINER_CLASS, "Name", eventId]
# e.g.
# [[:TWIN_1,"Gina",12],[:TWIN_2, "Mia", 13]]
def pbMultiTrainerBattle(trainers_array,canLose=false, outcomeVar=1)
battle_size = trainers_array.size
if $Trainer.party.size < battle_size
battle_size = $Trainer.party.size
end
case battle_size
when 1
trainer_id = trainers_array[0][0]
trainer_name = trainers_array[0][1]
return pbTrainerBattle(trainer_id,trainer_name)
when 2
trainer_1 = trainers_array[0]
trainer_1_id = trainer_1[0]
trainer_1_name = trainer_1[1]
trainer_1_event = trainer_1[2]
trainer_2 = trainers_array[1]
trainer_2_id = trainer_2[0]
trainer_2_name = trainer_2[1]
trainer_2_event = trainer_2[2]
trainer1_data = GameData::Trainer.get(trainer_1_id,trainer_1_name,0)
displayPreBattleText(trainer1_data)
result= pbDoubleTrainerBattle(trainer_1_id,trainer_1_name,0,nil,
trainer_2_id,trainer_2_name,0,nil,
canLose,outcomeVar)
if Settings::GAME_ID == :IF_HOENN
updateRematchableTrainer(trainer_1_id, trainer_1_name, 0, trainer_1_event, trainer_2_event)
updateRematchableTrainer(trainer_2_id, trainer_2_name, 0, trainer_2_event, trainer_1_event)
end
return result
when 3
trainer_1 = trainers_array[0]
trainer_1_id = trainer_1[0]
trainer_1_name = trainer_1[1]
trainer_1_event = trainer_1[2]
trainer_2 = trainers_array[1]
trainer_2_id = trainer_2[0]
trainer_2_name = trainer_2[1]
trainer_2_event = trainer_2[2]
trainer_3 = trainers_array[2]
trainer_3_id = trainer_3[0]
trainer_3_name = trainer_3[1]
trainer_3_event = trainer_3[2]
result= pbTripleTrainerBattle(trainer_1_id,trainer_1_name,0,nil,
trainer_2_id,trainer_2_name,0,nil,
trainer_3_id,trainer_3_name,0,nil,
canLose,outcomeVar)
if Settings::GAME_ID == :IF_HOENN
updateRematchableTrainer(trainer_1_id, trainer_1_name, 0, trainer_1_event)
updateRematchableTrainer(trainer_2_id, trainer_2_name, 0, trainer_2_event)
updateRematchableTrainer(trainer_3_id, trainer_3_name, 0, trainer_3_event)
end
return result
end
end
def updateRematchableTrainer(trainerID, trainerName, trainerVersion, event_id=nil, linked_event=nil, map_id = nil)
event_id = @event_id unless event_id
map_id = $game_map.map_id unless map_id
trainer = registerBattledTrainer(event_id,map_id,trainerID,trainerName,trainerVersion,linked_event)
makeRebattledTrainerTeamGainExp(trainer)
end
@@ -27,14 +119,33 @@ 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)
def registerBattledTrainer(event_id, mapId, trainerType, trainerName, trainerVersion=0, linked_event=nil)
key = [event_id,mapId]
$PokemonGlobal.battledTrainers = {} unless $PokemonGlobal.battledTrainers
return if $PokemonGlobal.battledTrainers.has_key?(key)
trainer = BattledTrainer.new(trainerType, trainerName, trainerVersion,key)
trainer.setLinkedTrainer(linked_event) if linked_event
trainer.setOverworldSprite(getOverworldSprite(event_id,mapId))
trainer.setLocation(getMapName(mapId))
$PokemonGlobal.battledTrainers[key] = trainer
echoln "Registered rematchable trainer #{key}"
return trainer
end
def getOverworldSprite(event_id,mapId)
current_map = $game_map.map_id
if mapId == current_map
event = $game_map.events[event_id]
else
map = $MapFactory.getMap(mapId,false)
event = map&.events[event_id]
end
if event
return event.character_name
end
return ""
end
def unregisterBattledTrainer(event_id, mapId)
key = [event_id,mapId]
$PokemonGlobal.battledTrainers = {} unless $PokemonGlobal.battledTrainers
@@ -56,8 +167,8 @@ def resetTrainerRebattle(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]
def updateRebattledTrainer(updated_trainer)
key = updated_trainer.trainerKey
updateRebattledTrainerWithKey(key,updated_trainer)
end
@@ -8,21 +8,26 @@
# 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)
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
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 /= 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
# NPC trainers capped at level cap
level_cap = getCurrentLevelCap
if pokemon.level > level_cap
pokemon.level = level_cap
end
updated_team.push(pokemon)
end
trainer.currentTeam = updated_team
@@ -33,10 +38,21 @@ 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)
unless evolution_species #Level up evolution has priority over stone evolution
trainer.list_evolution_items.each do |evolution_item|
evolution_species = pokemon.check_evolution_on_use_item(evolution_item)
if evolution_species
index = trainer.inventory.index(evolution_item)
trainer.inventory.delete_at(index) if index
end
end
end
if evolution_species && pokemonAllowedToEvolve(pokemon, evolution_species)
trainer.log_evolution_event(pokemon.species, evolution_species)
trainer.set_pending_action(true)
pokemon.species = evolution_species if evolution_species
pokemon.species = evolution_species
pokemon.pif_sprite = nil
end
updated_team.push(pokemon)
end
@@ -44,43 +60,87 @@ def evolveRebattledTrainerPokemon(trainer)
return trainer
end
def update_items(trainer)
return unless $PokemonTemp.battle_npc_used_items
$PokemonTemp.battle_npc_used_items.each do |item|
if trainer.inventory.include?(item)
index = trainer.inventory.index(item)
trainer.inventory.delete_at(index) if index
end
end
return trainer
$PokemonTemp.battle_npc_used_items=nil
end
def pokemonAllowedToEvolve(pokemon, evolution_species=nil)
return false if pokemon.item == :EVERSTONE
return true
end
def healRebattledTrainerPokemon(trainer)
for pokemon in trainer.currentTeam
pokemon.calc_stats
pokemon.heal
echoln "healing #{pokemon.name}"
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 : "..."
player_won = false
if customTrainerBattle(trainer.trainerName,trainer.trainerType, trainer.currentTeam,nil,loseDialog)
updated_trainer = makeRebattledTrainerTeamGainExp(trainer,true)
updated_trainer = healRebattledTrainerPokemon(updated_trainer)
player_won=true
else
updated_trainer =makeRebattledTrainerTeamGainExp(trainer,false)
def generateTrainerRematch(trainer, allow_double = true)
battle_trainers = []
battle_trainers << trainer
partner = trainer.getLinkedTrainer
if partner && allow_double # perma-double battles (twins, etc.)
battle_trainers << partner
end
player_won = rematchable_trainer_battle(battle_trainers)
checkTrainerRematchChallenges()
# trainer
updated_trainer = makeRebattledTrainerTeamGainExp(trainer, player_won)
updated_trainer = healRebattledTrainerPokemon(updated_trainer)
updated_trainer.set_pending_action(false)
updated_trainer.update_items(updated_trainer)
updated_trainer = evolveRebattledTrainerPokemon(updated_trainer)
trainer.increase_friendship(5)
trainer.nb_rematches +=1
# partner
if partner
updated_partner_trainer = makeRebattledTrainerTeamGainExp(partner, player_won)
updated_partner_trainer = healRebattledTrainerPokemon(updated_partner_trainer)
updated_partner_trainer.set_pending_action(false)
updated_partner_trainer = evolveRebattledTrainerPokemon(updated_partner_trainer)
updateRebattledTrainerWithKey(updated_partner_trainer&.trainerKey, updated_partner_trainer)
end
return updated_trainer, player_won
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?
def showGiftDialog(trainer=nil, event_id = nil)
unless trainer && event_id
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?
else
event = $game_map.events[event_id]
end
trainer_data = GameData::Trainer.try_get(trainer.trainerType, trainer.trainerName, 0)
message_text = trainer_data.preRematch_text_gift
message_text = message_text.gsub("<PLAYER_NAME>", $Trainer.name)
showTrainerMessage(event, trainer, message_text)
end
def showPrerematchDialog(trainer=nil, event_id = nil)
unless trainer && event_id
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?
else
event = $game_map.events[event_id]
end
trainer_data = GameData::Trainer.try_get(trainer.trainerType, trainer.trainerName, 0)
all_previous_random_events = trainer.previous_random_events
if all_previous_random_events
@@ -88,19 +148,20 @@ def showPrerematchDialog()
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
CATCH: trainer_data.preRematch_text_caught,
EVOLVE: trainer_data.preRematch_text_evolved,
FUSE: trainer_data.preRematch_text_fused,
UNFUSE: trainer_data.preRematch_text_unfused,
REVERSE: trainer_data.preRematch_text_reversed,
GIFT: trainer_data.preRematch_text_gift
}
message_text = event_message_map[previous_random_event.eventType] || trainer_data.preRematchText
message_text = event_message_map[previous_random_event.eventType] || trainer_data.preRematch_text_default
else
message_text = trainer_data.preRematchText
message_text = trainer_data.preRematch_text_default
end
end
message_text = message_text.gsub("<PLAYER_NAME>", $Trainer.name)
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)
@@ -112,15 +173,18 @@ def showPrerematchDialog()
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
message_text = trainer_data.preRematch_text_default
end
showTrainerMessage(event, trainer, message_text)
end
def showTrainerMessage(event, trainer, message_text)
if message_text
split_messages = message_text.split("<br>")
split_messages.each do |msg|
pbCallBub(2,event.id)
pbCallBub(2, event.id)
pbCallBub(3) if isPartneredWithTrainer(trainer)
pbMessage(msg)
end
end
end
@@ -1,44 +1,46 @@
class BattledTrainer
TRAINER_CLASS_FAVORITE_TYPES =
{
RIVAL1: [:ANY],
RIVAL2: [:ANY],
AROMALADY: [:GRASS, :FAIRY],
BEAUTY: [:FAIRY, :WATER, :NORMAL, :GRASS],
BIKER: [:POISON, :DARK],
BIRDKEEPER: [:FLYING, :NORMAL],
BIRDKEEPER: [:FLYING],
BUGCATCHER: [:BUG],
BURGLAR: [:FIRE, :DARK],
CHANNELER: [:GHOST, :PSYCHIC],
CUEBALL: [:FIGHTING],
CUEBALL: [:FIGHTING, :STEEL],
ENGINEER: [:ELECTRIC, :STEEL],
FISHERMAN: [:WATER],
GAMBLER: [:NORMAL, :PSYCHIC],
GENTLEMAN: [:NORMAL, :STEEL],
HIKER: [:ROCK, :GROUND],
JUGGLER: [:PSYCHIC, :GHOST],
JUGGLER: [:PSYCHIC, :GHOST, :NORMAL, :POISON],
LADY: [:FAIRY, :NORMAL],
PAINTER: [:NORMAL, :PSYCHIC],
PAINTER: [:NORMAL, :PSYCHIC, :GRASS],
POKEMANIAC: [:DRAGON, :GROUND],
POKEMONBREEDER: [:NORMAL, :GRASS],
POKEMONBREEDER_F: [:NORMAL, :GRASS],
PROFESSOR: [:NORMAL, :PSYCHIC],
ROCKER: [:ELECTRIC, :FIRE],
RUINMANIAC: [:GROUND, :ROCK],
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],
CAMPER: [:BUG, :FIRE, :GRASS],
PICNICKER: [:GRASS, :NORMAL],
COOLTRAINER_M: [:DRAGON, :STEEL, :FIRE],
COOLTRAINER_F: [:ICE, :PSYCHIC, :FAIRY],
YOUNGSTER: [:NORMAL, :BUG],
LASS: [:NORMAL, :FAIRY],
YOUNGSTER: [:NORMAL, :BUG, :GRASS, :FLYING],
LASS: [:NORMAL, :FAIRY, :GRASS, :FLYING],
POKEMONRANGER_M: [:GRASS, :GROUND],
POKEMONRANGER_F: [:GRASS, :WATER],
PSYCHIC_M: [:PSYCHIC, :GHOST],
PSYCHIC_F: [:PSYCHIC, :FAIRY],
SWIMMER_M: [:WATER],
SWIMMER_M: [:WATER, :ICE],
SWIMMER_F: [:WATER, :ICE],
SWIMMER2_M: [:WATER],
SWIMMER2_F: [:WATER],
@@ -55,7 +57,7 @@ class BattledTrainer
BUGCATCHER_F: [:BUG],
ROUGHNECK: [:DARK, :FIGHTING],
TEACHER: [:PSYCHIC, :NORMAL],
PRESCHOOLER_M: [:NORMAL],
PRESCHOOLER_M: [:NORMAL, :BUG],
PRESCHOOLER_F: [:FAIRY, :NORMAL],
HAUNTEDGIRL_YOUNG: [:GHOST],
HAUNTEDGIRL: [:GHOST, :DARK],
@@ -72,14 +74,64 @@ class BattledTrainer
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: [],
SCHOOLBOY: [:BUG, :NORMAL, :GRASS],
SCHOOLGIRL: [:BUG, :NORMAL , :GRASS],
TWIN_1: [:FAIRY, :NORMAL, :FLYING],
TWIN_2: [:FAIRY, :NORMAL, :FLYING],
POKEFAN_M: [:WATER, :FIRE, :GRASS, :ELECTRIC],
POKEFAN_F: [:WATER, :FIRE, :GRASS, :ELECTRIC],
RICHBOY: [:NORMAL, :GRASS],
POKEMONBREEDER_M: [:NORMAL, :GRASS],
STREETTHUG: [:POISON, :DARK, :FIGHTING],
DELINQUENT: [:POISON, :DARK, :FIGHTING],
BUGMANIAC: [:BUG],
EXPERT_M: [:FIGHTING],
EXPERT_F: [:FIGHTING],
COLLECTOR: [:ROCK, :STEEL, :DRAGON, :FAIRY, :GHOST, :PSYCHIC],
SURFER: [:WATER, :FLYING],
DIVER_M: [:WATER],
DIVER_F: [:WATER],
REPORTER: [:NORMAL],
CAMERAMAN: [:ELECTRIC],
NINJABOY: [:BUG, :POISON],
DRAGONTAMER: [:DRAGON],
TRIATHLETE_BIKE_M: [:GROUND,:WATER, :FLYING],
TRIATHLETE_BIKE_F: [:GROUND,:WATER, :FLYING],
TRIATHLETE_SWIM_M: [:GROUND,:WATER, :FLYING],
TRIATHLETE_SWIM_F: [:GROUND,:WATER, :FLYING],
TRIATHLETE_RUN_M: [:GROUND,:WATER, :FLYING],
TRIATHLETE_RUN_F: [:GROUND,:WATER, :FLYING],
YOUNGCOUPLE_M: [:NORMAL, :PSYCHIC],
YOUNGCOUPLE_F: [:NORMAL, :PSYCHIC],
PARASOLLADY: [:WATER, :FLYING, :ELECTRIC],
HEXMANIAC: [:GHOST],
SECRETBASEEXPERT: [:NORMAL, :GROUND, :ROCK, :GRASS],
SCHOOLMATE_SR: [:FAIRY, :NORMAL, :GRASS],
SCHOOLMATE_JR: [:FAIRY, :NORMAL, :GRASS],
LEADER_Roxanne: [:ROCK],
LEADER_Brawly: [:FIGHTING],
LEADER_Wattson: [:ELECTRIC],
LEADER_Flannery: [:FIRE],
LEADER_Norman: [:NORMAL],
LEADER_Winona: [:FLYING],
LEADER_Tate: [:PSYCHIC],
LEADER_Liza: [:PSYCHIC],
LEADER_Wallace: [:WATER],
LEADER_Juan: [:WATER],
ELITEFOUR_Sidney: [:PSYCHIC],
ELITEFOUR_Phoebe: [:GHOST],
ELITEFOUR_Glacia: [:ICE],
ELITEFOUR_Drake: [:DRAGON],
CHAMPION_Steven: [:STEEL],
TEAM_AQUA_BOSS: [:WATER],
TEAM_AQUA_EXEC_M: [:WATER],
TEAM_AQUA_EXEC_F: [:WATER],
TEAM_MAGMA_BOSS: [:GROUND],
TEAM_MAGMA_EXEC_M: [:GROUND],
TEAM_MAGMA_EXEC_F: [:GROUND],
}
end
@@ -1,5 +1,9 @@
#[TRADE, ITEM, PARTNER]
# Max is 100 - put a value above that if a level should not be attainable
class BattledTrainer
FRIENDSHIP_LEVELS = {
#Normal trainers
AROMALADY: [10, 25, 45],
BEAUTY: [15, 30, 60],
BIKER: [20, 40, 80],
@@ -17,7 +21,7 @@ class BattledTrainer
LADY: [15, 30, 60],
PAINTER: [8, 22, 40],
POKEMANIAC: [18, 35, 70],
POKEMONBREEDER: [8, 18, 35],
POKEMONBREEDER_F: [8, 18, 35],
PROFESSOR: [10, 30, 60],
ROCKER: [15, 35, 70],
RUINMANIAC: [15, 35, 65],
@@ -49,6 +53,8 @@ class BattledTrainer
CRUSHKIN: [15, 35, 80],
SISANDBRO: [10, 25, 50],
TWINS: [10, 25, 50],
TWIN_1: [10, 25, 50],
TWIN_2: [10, 25, 50],
YOUNGCOUPLE: [15, 30, 65],
SOCIALITE: [12, 30, 70],
BUGCATCHER_F: [8, 20, 35],
@@ -80,6 +86,55 @@ class BattledTrainer
TEAM_MAGMA_GRUNT_F: [25, 60, 100],
TEAM_MAGMAQUA_GRUNT_M: [25, 60, 100],
TEAM_MAGMAQUA_GRUNT_F: [25, 60, 100],
POKEFAN_M: [10, 22, 38],
POKEFAN_F: [10, 22, 38],
SCHOOLMATE_SR: [12, 25, 50],
SCHOOLMATE_JR: [10, 22, 42],
POKEMONBREEDER_M: [8, 18, 35],
STREETTHUG: [22, 45, 85],
DELINQUENT: [22, 45, 85],
BUGMANIAC: [15, 30, 60],
EXPERT_M: [25, 55, 100],
EXPERT_F: [25, 55, 100],
COLLECTOR: [25, 50, 90],
SURFER: [12, 28, 55],
DIVER_M: [12, 28, 55],
DIVER_F: [12, 28, 55],
REPORTER: [15, 30, 60],
CAMERAMAN: [15, 30, 60],
NINJABOY: [12, 25, 45],
DRAGONTAMER: [22, 45, 95],
HEXMANIAC: [15, 35, 70],
PARASOLLADY: [12, 28, 55],
SECRETBASEEXPERT: [15, 35, 75],
YOUNGCOUPLE_M: [15, 30, 65],
YOUNGCOUPLE_F: [15, 30, 65],
TRIATHLETE_BIKE_M: [15, 35, 70],
TRIATHLETE_BIKE_F: [15, 35, 70],
TRIATHLETE_SWIM_M: [15, 35, 70],
TRIATHLETE_SWIM_F: [15, 35, 70],
TRIATHLETE_RUN_M: [15, 35, 70],
TRIATHLETE_RUN_F: [15, 35, 70],
#story / bosses
LEADER_Roxanne: [20, 40, 200],
LEADER_Brawly: [20, 40, 200],
LEADER_Wattson: [20, 40, 200],
LEADER_Flannery: [20, 40, 200],
LEADER_Norman: [5, 40, 200],
LEADER_Winona: [20, 40, 200],
LEADER_Tate: [20, 40, 200],
LEADER_Liza: [20, 40, 200],
LEADER_Wallace: [20, 40, 200],
LEADER_Juan: [20, 40, 200],
ELITEFOUR_Sidney: [30, 60, 200],
ELITEFOUR_Phoebe: [30, 60, 200],
ELITEFOUR_Glacia: [30, 60, 200],
ELITEFOUR_Drake: [30, 60, 200],
CHAMPION_Steven: [50, 70, 200],
TEAM_AQUA_BOSS: [60, 200, 200],
TEAM_MAGMA_BOSS: [60, 200, 200]
}
end
@@ -0,0 +1,107 @@
# todo: Also do trainer class specific items.
# e.g. Rich boy can give luxury ball
#
TRAINER_REMATCH_GIFTS = {
# 2 tiers of gift.
# common, rare
# Odds depend on friendship level
#
# [[Tier 1], [Tier 2]]
:NORMAL => [[:CHILANBERRY, :POTION, :FULLHEAL],
[:HPUP, :PPUP]],
:FIGHTING => [[:CHOPLEBERRY,],
[:IRON, :CALCIUM]],
:FLYING => [[:COBABERRY, :PRETTYWING,],
[:RAZORFANG]],
:POISON => [[:KEBIABERRY, :ANTIDOTE],
[:POISONBARB,],
[]],
:GROUND => [[:SHUCABERRY,],
[:SOFTSAND,]],
:ROCK => [[:CHARTIBERRY, :HARDSTONE],
[:STARDUST, :PROTECTOR]],
:BUG => [[:TANGABERRY, :HONEY],
[:BIGMUSHROOM]],
:GHOST => [[:KASIBBERRY,],
[:REAPERCLOTH]],
:STEEL => [[:BABIRIBERRY,],
[:METALCOAT, :METALPOWDER, :IRON]],
:FIRE => [[:BURNHEAL, :OCCABERRY],
[:FIRESTONE, :LAVACOOKIE]],
:WATER => [[:PASSHOBERRY, :HEARTSCALE, :LUREBALL],
[:PEARL, :WATERSTONE]],
:GRASS => [[:RINDOBERRY, :TINYMUSHROOM],
[:LEAFSTONE]],
:ELECTRIC => [[:PARLYZHEAL, :WACANBERRY],
[:THUNDERSTONE]],
:PSYCHIC => [[:PAYAPABERRY],
[:MENTALHERB]],
:ICE => [[:YACHEBERRY, :ICEHEAL],
[:ICESTONE, :SNOWBALL]],
:DRAGON => [[:HABANBERRY],
[:DRAGONSCALE]],
:DARK => [[:COLBURBERRY],
[:RAZORCLAW]],
:FAIRY => [[:ROSELIBERRY],
[:WHIPPEDDREAM, :MOONSTONE]],
:ANY => [:DNASPLICERS, :DNAREVERSER, :REPEL, :POTION, :SUPERPOTION, :GREATBALL, :FRESHWATER]
}
TRAINER_REMATCH_SPECIFIC_GIFTS = {
:RICHBOY => [[:LUXURYBALL], [:NUGGET]],
:LADY => [[:LUXURYBALL], [:NUGGET]],
:GENTLEMAN => [[:LUXURYBALL], [:NUGGET]],
:HIKER => [[:TINYMUSHROOM, :REPEL], [:BIGMUSHROOM]],
:CRUSHGIRL => [[:XSPEED], [:CARBOS]],
:BLACKBELT => [[:XATTACK], [:PROTEIN]],
:NURSE => [[:HEALBALL, :SUPERPOTION], [:FULLRESTORE]],
:ROCKER => [[:PARLYZHEAL], [:THUNDERSTONE]],
:SAILOR => [[:LUREBALL], [:PEARL, :WATERSTONE]],
:FISHERMAN => [[:LUREBALL], [:PEARL, :WATERSTONE]],
:AROMALADY => [[:PINKNECTAR, :REDNECTAR, :YELLOWNECTAR, :BLUENECTAR], [:LEAFSTONE]],
:TRIATHLETE_BIKE_F => [[:FRESHWATER], [:FRESHWATER]],
:TRIATHLETE_BIKE_M => [[:FRESHWATER], [:FRESHWATER]],
}
def should_find_item(trainer)
base_rate = 30 # percent
return rand(100) < base_rate
end
def should_give_item(trainer)
return false unless trainer.friendship_level >= 2
return false if trainer.inventory.empty?
base_rate = 30 # percent
item_chances = base_rate + (trainer.friendship / 10).floor
return rand(100) < item_chances
end
def find_random_trainer_item(trainer)
rare_item_chances = 5 + (trainer.friendship / 5).floor
chance_trainer_class_item = 40
giving_rare_item = rand(100) < rare_item_chances
typed_items = TRAINER_REMATCH_GIFTS[trainer.favorite_type]
items_list = typed_items
items_list[0] += trainer.inventory
if TRAINER_REMATCH_SPECIFIC_GIFTS.has_key?(trainer.trainerType) && rand(100) < chance_trainer_class_item
items_list = TRAINER_REMATCH_GIFTS[trainer.trainerType] if TRAINER_REMATCH_GIFTS[trainer.trainerType]
end
if giving_rare_item
return items_list[1].sample
else
items = items_list[0] + TRAINER_REMATCH_GIFTS[:ANY]
return items.sample
end
end
def select_gift_item(trainer)
return nil unless trainer.inventory
return trainer.inventory.sample
end
@@ -9,44 +9,42 @@
####
# Methods to be called from events
####
FRIENDSHIP_LEVEL_FOR_TRADE = 1
#actionType :
# :BATTLE
# :TRADE
# :PARTNER
def doPostBattleAction(actionType)
def doPostBattleAction(actionType,trainer, double_allowed=true)
event = pbMapInterpreter.get_character(0)
map_id = $game_map.map_id if map_id.nil?
trainer = getRebattledTrainer(event.id,map_id)
unless trainer
trainer = getRebattledTrainer(event.id,map_id)
end
trainer.clear_previous_random_events()
return if !trainer
case actionType
when :BATTLE
trainer,player_won = doNPCTrainerRematch(trainer)
trainer,player_won = generateTrainerRematch(trainer,double_allowed)
when :TRADE
trainer = doNPCTrainerTrade(trainer)
when :PARTNER
partnerWithTrainer(event.id,map_id,trainer)
end
updateRebattledTrainer(event.id,map_id,trainer)
updateRebattledTrainer(trainer)
end
def setTrainerFriendship(trainer)
params = ChooseNumberParams.new
params.setRange(0,100)
params.setDefaultValue($game_map.map_id)
number = pbMessageChooseNumber(_INTL("Frienship (0-100)?"),params)
params.setDefaultValue(trainer.friendship)
number = pbMessageChooseNumber(_INTL("Friendship (0-100)?"),params)
trainer.friendship = number
trainer.increase_friendship(0)
return trainer
@@ -56,8 +54,14 @@ end
# [[:SPECIES,level], ... ]
#
#def customTrainerBattle(trainerName, trainerType, party_array, default_level=50, endSpeech="", sprite_override=nil,custom_appearance=nil)
def postBattleActionsMenu()
def postBattleActionsMenu(trainer=nil, event_id=nil)
rematchCommand = _INTL("Rematch")
rematchSingleCommand = _INTL("Rematch (Single)")
rematchDoubleCommand = _INTL("Rematch (Double)")
viewTeamCommand = _INTL("View Team")
tradeCommand = _INTL("Trade Offer")
partnerCommand = _INTL("Partner up")
cancelCommand = _INTL("See ya!")
@@ -66,35 +70,54 @@ def postBattleActionsMenu()
resetTrainerDebugCommand = _INTL("(Debug) Reset trainer")
setFriendshipDebugCommand = _INTL("(Debug) Set Friendship")
printTrainerTeamDebugCommand = _INTL("(Debug) Print team")
printTrainerInventoryCommand = _INTL("(Debug) Print inventory")
event = pbMapInterpreter.get_character(0)
map_id = $game_map.map_id if map_id.nil?
trainer = getRebattledTrainer(event.id,map_id)
unless trainer
event = pbMapInterpreter.get_character(0)
map_id = $game_map.map_id if map_id.nil?
trainer = getRebattledTrainer(event.id,map_id)
end
return unless trainer
options = []
options << rematchCommand
options << tradeCommand if trainer.friendship_level >= 1
options << partnerCommand if trainer.friendship_level >= 3
if trainer.getLinkedTrainer
options << rematchSingleCommand if trainer.friendship_level >=1
options << rematchDoubleCommand
else
options << rematchCommand
end
options << tradeCommand if trainer.friendship_level >= FRIENDSHIP_LEVEL_FOR_TRADE
#options << partnerCommand if trainer.friendship_level >= 3
options << viewTeamCommand
options << updateTeamDebugCommand if $DEBUG
options << resetTrainerDebugCommand if $DEBUG
options << setFriendshipDebugCommand if $DEBUG
options << printTrainerTeamDebugCommand if $DEBUG
options << printTrainerInventoryCommand if $DEBUG
options << cancelCommand
trainer = applyTrainerRandomEvents(trainer)
showPrerematchDialog
trainer = applyTrainerRandomEvents(trainer) if trainer.nb_rematches >=1
showPrerematchDialog(trainer,event_id)
choice = optionsMenu(options,options.find_index(cancelCommand),options.find_index(cancelCommand))
case options[choice]
when rematchCommand
doPostBattleAction(:BATTLE)
doPostBattleAction(:BATTLE,trainer)
trainer.inventory = [] unless trainer.inventory
try_find_item(trainer)
try_give_gift(trainer,event_id)
trainer.process_party_pokemon_held_items
when viewTeamCommand
pbFadeOutIn {
screen = ContactsAppInfoPageScreen.new
screen.view_trainer_team(trainer.id)
}
when rematchSingleCommand
doPostBattleAction(:BATTLE,trainer,false)
when rematchDoubleCommand
doPostBattleAction(:BATTLE,trainer,true)
when tradeCommand
doPostBattleAction(:TRADE)
doPostBattleAction(:TRADE,trainer)
when partnerCommand
doPostBattleAction(:PARTNER)
doPostBattleAction(:PARTNER,trainer)
when updateTeamDebugCommand
echoln("")
echoln "---------------"
@@ -106,17 +129,47 @@ def postBattleActionsMenu()
when setFriendshipDebugCommand
trainer = getRebattledTrainer(event.id,map_id)
trainer = setTrainerFriendship(trainer)
updateRebattledTrainer(event.id,map_id,trainer)
updateRebattledTrainer(trainer)
when printTrainerTeamDebugCommand
trainer = getRebattledTrainer(event.id,map_id)
printNPCTrainerCurrentTeam(trainer)
when printTrainerInventoryCommand
trainer = getRebattledTrainer(event.id,map_id)
echoln trainer.inventory
when cancelCommand
else
$PokemonGlobal.nextBattleBack=nil
return
end
$PokemonGlobal.nextBattleBack=nil
end
def try_find_item(trainer)
if should_find_item(trainer)
item = find_random_trainer_item(trainer)
trainer.inventory = [] unless trainer.inventory
trainer.inventory << item
updateRebattledTrainer(trainer)
end
end
def try_give_gift(trainer,event_id=nil)
if should_give_item(trainer)
item = select_gift_item(trainer)
return unless item
if trainer.inventory.include?(item)
index = trainer.inventory.index(item)
trainer.inventory.delete_at(index) if index
updateRebattledTrainer(trainer)
end
showGiftDialog(trainer,event_id)
pbReceiveItem(item)
return true
end
return false
end
#leave event_type empty for random
# Used in quest, called from event
def forceRandomRematchEventOnTrainer(event_type=nil)
event = pbMapInterpreter.get_character(0)
map_id = $game_map.map_id if map_id.nil?
@@ -124,7 +177,7 @@ def forceRandomRematchEventOnTrainer(event_type=nil)
while !trainer.has_pending_action
trainer = applyTrainerRandomEvents(trainer,event_type)
end
updateRebattledTrainer(event.id,map_id,trainer)
updateRebattledTrainer(trainer)
end
def forceTrainerFriendshipOnTrainer(friendship=0)
@@ -133,5 +186,5 @@ def forceTrainerFriendshipOnTrainer(friendship=0)
trainer = getRebattledTrainer(event.id,map_id)
trainer.friendship = friendship
trainer.increase_friendship(0)
updateRebattledTrainer(event.id,map_id,trainer)
updateRebattledTrainer(trainer)
end
@@ -29,15 +29,21 @@ end
def promptGiveToPartner(caughtPokemon)
return false if !$Trainer.npcPartner
return false if $Trainer.npcPartner == BATTLED_TRAINER_WALLY_KEY && $game_switches[SWITCH_WALLY_GAVE_POKEMON]
if $Trainer.npcPartner == BATTLED_TRAINER_WALLY_KEY && caughtPokemon.isFusion?
pbMessage(_INTL("I... I don't think I can handle a fused Pokémon. Can we try to catch a different one?"))
return
return false if $Trainer.npcPartner == BATTLED_TRAINER_WALLY_KEY && $game_switches[SWITCH_DIED_WITH_WALLY]
if $Trainer.npcPartner == BATTLED_TRAINER_WALLY_KEY
$game_switches[SWITCH_WALLY_SAW_CAUGHT_POKEMON] = true
if caughtPokemon.isFusion?
pbMessage(_INTL("I... I don't think I can handle a fused Pokémon. Can we try to catch a different one?"))
return
end
end
partnerTrainer = getRebattledTrainerFromKey($Trainer.npcPartner)
return false if $Trainer.npcPartner == BATTLED_TRAINER_WALLY_KEY && partnerTrainer.currentTeam.length > 0
return false if !partnerTrainer
command = pbMessage(_INTL("Would you like to give the newly caught {1} to {2}?",caughtPokemon.name,partnerTrainer.trainerName),
[_INTL("Keep"),_INTL("Give to {1}",partnerTrainer.trainerName)], 2)
[_INTL("Keep"),_INTL("Give to {1}",partnerTrainer.trainerName)], 0)
case command
when 0 # Keep
return
@@ -22,25 +22,32 @@ def applyTrainerRandomEvents(trainer,event_type=nil)
#time_passed = trainer.getTimeSinceLastAction
#return trainer if time_passed < TIME_FOR_RANDOM_EVENTS
catch_chance_for_number_in_team = [100,40,30,20,10,5,0]
unfuse_chance_for_number_in_team = [0,10,10,5,5,0,0]
fuse_chance_for_number_in_team = [0,0,40,60,65,75,85]
# Weighted chances out of 10
#Odds of certain events depends on nb. of pokemon in current party
catch_chance = catch_chance_for_number_in_team[trainer.currentTeam.length]
unfuse_chance = unfuse_chance_for_number_in_team[trainer.currentTeam.length]
fuse_chance = fuse_chance_for_number_in_team[trainer.currentTeam.length]
# Chances out of 100
weighted_events = [
[:CATCH, 3],
[:FUSE, 6],
[:REVERSE, 1],
[:UNFUSE, 2]
[:CATCH, catch_chance], #Depends on nb of pokemon in team. 40% if only 1, then DECREASES with the nb.
[:FUSE, fuse_chance], #Depends on nb of pokemon in team. 40% if 2, then INCREASES with the nb.
[:REVERSE, 10],
[:UNFUSE, unfuse_chance] #Depends on nb of pokemon in team. 10% if 1 or 2, 5% if 3 or 4. .
]
# Create a flat array of events based on weight
event_pool = weighted_events.flat_map { |event, weight| [event] * weight }
selected_event = event_pool.sample
selected_event = event_type if event_type
#Random event guaranteed after 1st rematch only (can still fail if they don't have the requirements tho)
if selected_event.nil? && trainer.nb_rematches == 1
selected_event = event_pool.sample until selected_event
end
if selected_event
echoln "Trying to do random event: #{selected_event}"
end
return trainer if selected_event.nil?
original_team = trainer.currentTeam.clone
@@ -55,9 +62,6 @@ def applyTrainerRandomEvents(trainer,event_type=nil)
trainer = reverse_random_team_pokemon(trainer)
end
new_team = trainer.currentTeam
echoln original_team
echoln new_team
team_changed = original_team != new_team
trainer.set_pending_action(team_changed)
printNPCTrainerCurrentTeam(trainer)
@@ -67,24 +71,26 @@ end
def chooseEncounterType(trainerClass)
water_trainer_classes = [:SWIMMER_F, :SWIMMER_M, :FISHERMAN]
water_trainer_classes = [:SWIMMER_F, :SWIMMER_M,:SWIMMER2_M,:SWIMMER2_F, :FISHERMAN, :SURFER, :DIVER_M, :DIVER_F]
hybrid_trainer_classes = [:COOLTRAINER_M, :COOLTRAINER_F, :TRIATHLETE_SWIM_M, :TRIATHLETE_SWIM_F,
:TRIATHLETE_RUN_M, :TRIATHLETE_RUN_F, :SAILOR, :TUBER_M, :TUBER_F]
if water_trainer_classes.include?(trainerClass )
chance_of_land_encounter = 1
chance_of_land_encounter = 0
chance_of_surf_encounter= 5
chance_of_cave_encounter = 1
chance_of_cave_encounter = 0
chance_of_fishing_encounter = 5
elsif hybrid_trainer_classes.include?(trainerClass )
chance_of_land_encounter = 5
chance_of_surf_encounter= 2
chance_of_cave_encounter = 5
chance_of_fishing_encounter = 2
else
chance_of_land_encounter = 5
chance_of_surf_encounter= 1
chance_of_surf_encounter= 0
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
$PokemonEncounters.has_water_encounters?
possible_encounter_types = []
if $PokemonEncounters.has_land_encounters?
possible_encounter_types += [:Land] * chance_of_land_encounter
@@ -96,7 +102,6 @@ def chooseEncounterType(trainerClass)
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
@@ -112,16 +117,41 @@ def catch_new_team_pokemon(trainer)
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
# 3 rolls for favorite type, then normal roll
wild_pokemon = select_npc_new_pokemon(encounter_type, trainer.favorite_type)
if wild_pokemon
trainer.currentTeam << Pokemon.new(wild_pokemon[0],wild_pokemon[1])
trainer_highest_level = get_trainer_highest_level(trainer)
species = wild_pokemon[0]
level = [wild_pokemon[1], trainer_highest_level].min
original_trainer =pbLoadTrainer(trainer.trainerType,trainer.trainerName,0)
trainer.currentTeam << Pokemon.new(species,level,original_trainer)
trainer.log_catch_event(wild_pokemon[0])
end
return trainer
end
# 3 rolls for favorite type, then normal roll
def select_npc_new_pokemon(encounter_type, favorite_type)
(1..3).each { |i|
wild_pokemon = $PokemonEncounters.choose_wild_pokemon(encounter_type)
next unless wild_pokemon
species = wild_pokemon[0]
if GameData::Species.get(species)&.hasType?(favorite_type)
return wild_pokemon
end
}
return $PokemonEncounters.choose_wild_pokemon(encounter_type)
end
def get_trainer_highest_level(trainer)
highest_level = 1
trainer.currentTeam.each do |pokemon|
highest_level = pokemon.level if pokemon.level > highest_level
end
return highest_level
end
def reverse_random_team_pokemon(trainer)
@@ -136,6 +166,7 @@ def reverse_random_team_pokemon(trainer)
head_pokemon = get_head_species_from_symbol(pokemon_to_reverse.species)
pokemon_to_reverse.species = getFusedPokemonIdFromSymbols(head_pokemon,body_pokemon)
pokemon_to_reverse.pif_sprite=nil
trainer.currentTeam.push(pokemon_to_reverse)
trainer.log_reverse_event(old_species,pokemon_to_reverse.species)
return trainer
@@ -148,18 +179,19 @@ def unfuse_random_team_pokemon(trainer)
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)
if pokemon_to_unfuse.item
trainer.inventory << pokemon_to_unfuse.item.id
end
trainer.currentTeam.delete(pokemon_to_unfuse)
trainer.currentTeam.push(Pokemon.new(body_pokemon,level))
trainer.currentTeam.push(Pokemon.new(head_pokemon,level))
original_trainer =pbLoadTrainer(trainer.trainerType,trainer.trainerName,0)
trainer.currentTeam.push(Pokemon.new(body_pokemon,level,original_trainer))
trainer.currentTeam.push(Pokemon.new(head_pokemon,level,original_trainer))
trainer.log_unfusion_event(pokemon_to_unfuse.species, body_pokemon, head_pokemon)
return trainer
end
@@ -173,7 +205,8 @@ def fuse_random_team_pokemon(trainer)
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)
original_trainer =pbLoadTrainer(trainer.trainerType,trainer.trainerName,0)
fused_pokemon = Pokemon.new(fusion_species,level,original_trainer)
trainer.currentTeam.delete(body_pokemon)
trainer.currentTeam.delete(head_pokemon)
@@ -1,189 +1,172 @@
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
# 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,
# TIER 1: Masters (2.4-3.0)
CHAMPION_Steven: 3.0,
ELITEFOUR_Drake: 2.8,
ELITEFOUR_Glacia: 2.8,
ELITEFOUR_Phoebe: 2.8,
ELITEFOUR_Sidney: 2.8,
TEAM_AQUA_BOSS: 2.8,
TEAM_MAGMA_BOSS: 2.8,
LEADER_Wallace: 2.7,
LEADER_Juan: 2.7,
LEADER_Roxanne: 2.6,
LEADER_Brawly: 2.6,
LEADER_Wattson: 2.6,
LEADER_Flannery: 2.6,
LEADER_Norman: 2.6,
LEADER_Winona: 2.6,
LEADER_Tate: 2.6,
LEADER_Liza: 2.6,
PROFESSOR: 2.5,
EXPERT_M: 2.5,
EXPERT_F: 2.5,
# TIER 2: Professionals & wealthy (1.9 - 2.3)
COOLTRAINER_M: 2.3,
COOLTRAINER_F: 2.3,
COOLTRAINER_M2: 2.3,
COOLTRAINER_F2: 2.3,
DRAGONTAMER: 2.3,
LADY: 2.4,
GENTLEMAN: 2.3,
SOCIALITE: 2.3,
RICHBOY: 2.3,
COLLECTOR: 2.3,
BEAUTY: 2.2,
KIMONOGIRL: 2.2,
SAGE: 2.1,
COOLCOUPLE: 2.1,
ENGINEER: 2.0,
PAINTER: 2.0,
SCIENTIST: 2.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,
PSYCHIC_M: 2.0,
PSYCHIC_F: 2.0,
TEACHER: 2.0,
NURSE: 2.0,
POKEMONBREEDER_F: 1.9,
POKEMONBREEDER_M: 1.9,
SUPERNERD: 1.9,
# TIER 3: Hobbyists (1.5 - 1.8)
AROMALADY: 1.8,
JUGGLER: 1.8,
POLICE: 1.8,
PARASOLLADY: 1.8,
SECRETBASEEXPERT: 1.8,
CHANNELER: 1.7,
BLACKBELT: 1.7,
HAUNTEDGIRL: 1.7,
CRUSHKIN: 1.7,
POKEMANIAC: 1.6,
BUGMANIAC: 1.6,
POKEFAN_M: 1.6,
POKEFAN_F: 1.6,
CRUSHGIRL: 1.6,
YOUNGCOUPLE: 1.6,
YOUNGCOUPLE_M: 1.6,
YOUNGCOUPLE_F: 1.6,
WORKER: 1.6,
PYROMANIAC: 1.6,
SKIER_F: 1.6,
GAMBLER: 1.5,
HIKER: 1.5,
RUINMANIAC: 1.5,
TAMER: 1.5,
CLOWN: 1.5,
FARMER: 1.5,
REPORTER: 1.5,
CAMERAMAN: 1.5,
TRIATHLETE_BIKE_M: 1.5,
TRIATHLETE_BIKE_F: 1.5,
TRIATHLETE_SWIM_M: 1.5,
TRIATHLETE_SWIM_F: 1.5,
TRIATHLETE_RUN_M: 1.5,
TRIATHLETE_RUN_F: 1.5,
# TIER 4: Average joes (1.1 - 1.4)
BIRDKEEPER: 1.4,
FISHERMAN: 1.4,
ROCKER: 1.4,
ROUGHNECK: 1.4,
SCHOOLMATE_SR: 1.4,
BURGLAR: 1.3,
CUEBALL: 1.3,
SAILOR: 1.3,
SISANDBRO: 1.3,
DELIVERYMAN: 1.3,
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
SURFER: 1.3,
DIVER_M: 1.3,
DIVER_F: 1.3,
SWIMMER_M: 1.3,
SWIMMER_F: 1.3,
SWIMMER2_M: 1.3,
SWIMMER2_F: 1.3,
BIKER: 1.2,
CAMPER: 1.2,
PICNICKER: 1.2,
STREETTHUG: 1.2,
DELINQUENT: 1.2,
SCHOOLBOY: 1.2,
SCHOOLGIRL: 1.2,
NINJABOY: 1.2,
BUGCATCHER: 1.1,
BUGCATCHER_F: 1.1,
SCHOOLMATE_JR: 1.1,
# TIER 5: Novices & children
LASS: 1.0,
PLAYER: 1.0,
TWINS: 1.0,
TWIN_1: 1.0,
TWIN_2: 1.0,
YOUNGSTER: 0.9,
TUBER_M: 0.8,
TUBER_F: 0.8,
TUBER2_M: 0.8,
TUBER2_F: 0.8,
PRESCHOOLER_M: 0.6,
PRESCHOOLER_F: 0.6
}
def evaluate_pokemon_worth(pkmn, compare_level: nil)
def evaluate_pokemon_worth(pkmn, compare_level: nil, favorite_type: 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
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
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
type_exists = GameData::Type.exists?(favorite_type) || nil
type_bonus = (type_exists && pkmn.hasType?(favorite_type)) ? 30 : 0
score = level_score +
base_stats_score +
rarity_score +
iv_score +
shiny_score +
fusion_bonus
fusion_bonus +
type_bonus
echoln("#{pkmn.name} - Score : #{score}")
return score
end
def offerPokemonForTrade(player_pokemon, npc_party, trainer_class)
player_score = evaluate_pokemon_worth(player_pokemon)
def offerPokemonForTrade(player_pokemon, npc_party, trainer_class, favorite_type)
player_score = evaluate_pokemon_worth(player_pokemon, favorite_type: favorite_type)
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)]
[npc_pkmn, evaluate_pokemon_worth(npc_pkmn, compare_level: player_pokemon.level, favorite_type: favorite_type)]
end
best_npc_pokemon, best_score = npc_scores.max_by { |_, score| score }
return best_npc_pokemon if player_score > best_score
@@ -200,74 +183,62 @@ def offerPokemonForTrade(player_pokemon, npc_party, trainer_class)
end
def doNPCTrainerTrade(trainer)
echoln trainer.getTimeSinceLastTrade
if trainer.isNextTradeReady?
echoln "Time since last trade: #{trainer.getTimeSinceLastTrade}"
unless 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
# 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("{1} {2} is looking for {3}-type Pokémon. Which Pokémon do you want to trade?.", trainerClassName, trainer.trainerName, wanted_type_name))
pbChoosePokemon(1,2,
proc {|pokemon|
pokemon.hasType?(wanted_type)
})
wanted_types = BattledTrainer::TRAINER_CLASS_FAVORITE_TYPES[trainer.trainerType]
wanted_types = [:NORMAL] if !wanted_types || wanted_types.empty?
if wanted_types.include?(:ANY)
pbChoosePokemon(1, 2)
else
wanted_types_string = wanted_types.map { |type|
type_name = GameData::Type.get(type).real_name
"\\C[1]#{type_name}\\C[0]"
}.join(", ")
trainerClassName = GameData::TrainerType.get(trainer.trainerType).real_name
pbMessage(_INTL("{1} {2} is looking for Pokémon of the following type(s):\\n{3}\\nWhich Pokémon do you want to trade?",
trainerClassName, trainer.trainerName, wanted_types_string))
pbChoosePokemon(1, 2,
proc { |pokemon|
pokemon.hasOneOfTheseTypes?(wanted_types)
})
end
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)
offered_pokemon = offerPokemonForTrade(chosen_pokemon, trainer.currentTeam, trainer.trainerType, trainer.favorite_type)
if !offered_pokemon
pbMessage(_INTL("{1} {2} does not want to trade...", trainerClassName, trainer.trainerName))
return trainer
end
pif_sprite = BattleSpriteLoader.new.get_pif_sprite_from_species(offered_pokemon.species)
pif_sprite.dump_info()
message = _INTL("{1} {2} is offering {3} (Level {4}) for your {5}.", trainerClassName, trainer.trainerName, offered_pokemon.name, offered_pokemon.level, chosen_pokemon.name)
showPokemonInPokeballWithMessage(pif_sprite, message)
if pbConfirmMessage(_INTL("Trade away {1} for {2} {3}'s {4}?", chosen_pokemon.name, trainerClassName, trainer.trainerName, offered_pokemon.name))
pbStartTrade(chosen_index, offered_pokemon,offered_pokemon.name,trainer.trainerName,0)
pbStartTrade(chosen_index, offered_pokemon, offered_pokemon.name, trainer.trainerName, 0)
updated_party = trainer.currentTeam
trainer.increase_friendship(10) if GameData::Type.exists?(trainer.favorite_type) && offered_pokemon.hasType?(trainer.favorite_type)
updated_party.delete(offered_pokemon)
updated_party << chosen_pokemon.clone
trainer.previous_trade_timestamp= Time.now
trainer.previous_trade_timestamp = Time.now
trainer.increase_friendship(20)
trainer.process_party_pokemon_held_items
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