mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2026-07-21 23:27:00 +00:00
430 lines
15 KiB
Ruby
430 lines
15 KiB
Ruby
class BattledTrainer
|
|
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.
|
|
|
|
# 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 :inventory
|
|
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
|
|
|
|
attr_accessor :overworld_sprite
|
|
|
|
attr_reader :favorite
|
|
|
|
def initialize(trainerType, trainerName, trainerVersion, trainerKey)
|
|
@trainerKey = trainerKey
|
|
@trainerType = trainerType
|
|
@trainerName = trainerName
|
|
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
|
|
@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
|
|
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
|
|
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] || []
|
|
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))
|
|
case @friendship_level
|
|
when 1
|
|
pbMessage(_INTL("You can now trade with each other!"))
|
|
when 2
|
|
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!"))
|
|
end
|
|
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_chance
|
|
|
|
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)
|
|
@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=nil)
|
|
original_trainer = pbLoadTrainer(@trainerType, @trainerName, trainerVersion) unless original_trainer
|
|
|
|
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, original_trainer.name)
|
|
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 can_trade?()
|
|
trade_unlocked = @friendship_level >= FRIENDSHIP_LEVEL_FOR_TRADE
|
|
if trade_unlocked
|
|
return isNextTradeReady?
|
|
end
|
|
return false
|
|
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 |