Files
infinitefusion-e18/Data/Scripts/053_PIF_Hoenn/TrainerRematches/TrainerRematch_Trade.rb
2025-06-07 08:16:50 -04:00

274 lines
9.0 KiB
Ruby

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