#=============================================================================== # #=============================================================================== class Phone attr_accessor :contacts attr_accessor :rematch_variant, :rematches_enabled attr_accessor :time_to_next_call, :last_refresh_time def initialize @contacts = [] @rematch_variant = 0 # Original battle is 0, first rematch is 1, etc. @rematches_enabled = Settings::PHONE_REMATCHES_POSSIBLE_FROM_BEGINNING @time_to_next_call = 0.0 @last_refresh_time = 0 end # Returns a visible contact only. def get(trainer, *args) @contacts.each do |contact| next if !contact.visible? next if contact.trainer? != trainer if trainer next if contact.trainer_type != args[0] || contact.name != args[1] || contact.start_version != (args[2] || 0) else next if contact.name != args[0] end return contact end return nil end def get_version(trainer_type, name, start_version = 0) return 0 if !GameData::TrainerType.exists?(trainer_type) trainer_type = GameData::TrainerType.get(trainer_type).id contact = get(true, trainer_type, name, start_version) return (contact) ? contact.version : 0 end # Trainer type, name[, start_version] # Name def can_add?(*args) return false if !$player.has_pokegear if args.length == 1 # Non-trainer (name only) return false if get(false, args[0]) else # Trainer (has at least trainer type and name) return false if !GameData::TrainerType.exists?(args[0]) trainer_type = GameData::TrainerType.get(args[0]).id return false if get(true, trainer_type, args[1], args[2] || 0) end return true end # Event, trainer type, name, versions_count = 1, start_version = 0, common event ID = 0 # Map ID, event ID, trainer type, name, versions_count = 1, start_version = 0, common event ID = 0 # Map ID, name, common event ID def add(*args) if args[0].is_a?(Game_Event) # Trainer return false if !GameData::TrainerType.exists?(args[1]) trainer_type = GameData::TrainerType.get(args[1]).id name = args[2] contact = get(true, trainer_type, name, args[3] || 0) if contact contact.visible = true @contacts.delete(contact) else contact = Contact.new(true, args[0].map_id, args[0].id, trainer_type, name, args[3], args[4], args[5]) end elsif args[1].is_a?(Numeric) # Trainer return false if !GameData::TrainerType.exists?(args[2]) trainer_type = GameData::TrainerType.get(args[2]).id name = args[3] contact = get(true, trainer_type, name, args[4] || 0) if contact contact.visible = true @contacts.delete(contact) else contact = Contact.new(true, args[0], args[1], trainer_type, name, args[4], args[5], args[6]) end else # Non-trainer name = args[1] contact = get(false, name) if contact contact.visible = true @contacts.delete(contact) else contact = Contact.new(false, *args) end end @contacts.push(contact) sort_contacts return true end # Rearranges the list of phone contacts to put all visible contacts first, # followed by all invisible contacts. def sort_contacts new_contacts = [] 2.times do |i| @contacts.each do |con| next if (i == 0 && !con.visible?) || (i == 1 && con.visible?) new_contacts.push(con) end end @contacts = new_contacts end #----------------------------------------------------------------------------- # Checks once every second. def refresh_ready_trainers return if !@rematches_enabled time = pbGetTimeNow.to_i return if @last_refresh_time == time @last_refresh_time = time @contacts.each do |contact| next if !contact.trainer? || !contact.visible? next if contact.rematch_flag > 0 # Already ready for rematch if contact.time_to_ready <= 0 contact.time_to_ready = rand(20...40) * 60 # 20-40 minutes end contact.time_to_ready -= 1 next if contact.time_to_ready > 0 contact.rematch_flag = 1 # Ready for rematch contact.set_trainer_event_ready_for_rematch end end def reset_after_win(trainer_type, name, start_version = 0) return if !GameData::TrainerType.exists?(trainer_type) trainer_type = GameData::TrainerType.get(trainer_type).id contact = get(true, trainer_type, name, start_version) return if !contact contact.increment_version contact.rematch_flag = 0 contact.time_to_ready = 0 end #----------------------------------------------------------------------------- def self.rematch_variant return $PokemonGlobal.phone.rematch_variant end def self.rematch_variant=(value) $PokemonGlobal.phone.rematch_variant = value end def self.rematches_enabled return $PokemonGlobal.phone.rematches_enabled end def self.rematches_enabled=(value) $PokemonGlobal.phone.rematches_enabled = value end def self.get_trainer(*args) return $PokemonGlobal.phone.get(true, *args) end def self.can_add?(*args) return $PokemonGlobal.phone.can_add?(*args) end def self.add(*args) ret = $PokemonGlobal.phone.add(*args) if ret if args[0].is_a?(Game_Event) contact = $PokemonGlobal.phone.get(true, args[1], args[2], (args[4] || 0)) elsif args[1].is_a?(Numeric) contact = $PokemonGlobal.phone.get(true, args[2], args[3], (args[5] || 0)) else contact = $PokemonGlobal.phone.get(false, args[1]) end pbMessage("\\me[Register phone]" + _INTL("Registered {1} in the Pokégear!", contact.display_name) + "\\wtnp[60]") end return ret end def self.add_silent(*args) return $PokemonGlobal.phone.add(*args) end def self.variant(trainer_type, name, start_version = 0) contact = $PokemonGlobal.phone.get(trainer_type, name, start_version) return (contact) ? contact.variant : 0 end def self.increment_version(trainer_type, name, start_version = 0) contact = $PokemonGlobal.phone.get(trainer_type, name, start_version) contact.increment_version if contact end def self.battle(trainer_type, name, start_version = 0) contact = $PokemonGlobal.phone.get(true, trainer_type, name, start_version) return false if !contact return TrainerBattle.start(trainer_type, name, contact.next_version) end def self.reset_after_win(trainer_type, name, start_version = 0) $PokemonGlobal.phone.reset_after_win(trainer_type, name, start_version) end end #=============================================================================== # #=============================================================================== class Phone class Contact attr_accessor :map_id, :event_id attr_accessor :name attr_accessor :trainer_type attr_accessor :start_version, :versions_count, :version # :version is the last trainer version that was beaten attr_accessor :time_to_ready, :rematch_flag attr_accessor :common_event_id attr_reader :visible # Map ID, event ID, trainer type, name, versions count = 1, start version = 0 # Map ID, name, common event ID def initialize(trainer, *args) @trainer = trainer @map_id = args[0] if @trainer # Trainer @event_id = args[1] @trainer_type = args[2] @name = args[3] @versions_count = [args[4] || 1, 1].max # Includes the original version @start_version = args[5] || 0 @version = @start_version @time_to_ready = 0 @rematch_flag = 0 # 0=counting down, 1=ready for rematch, 2=ready and told player @common_event_id = args[6] || 0 else # Non-trainer @name = args[1] @common_event_id = args[2] || 0 end @visible = true end def trainer? return @trainer end def visible? return @visible end def visible=(value) return if @visible == value @visible = value if !value && trainer? @time_to_ready = 0 @rematch_flag = 0 $game_self_switches[[@map_id, @event_id, "A"]] = true $game_map.need_refresh = true end end def can_hide? return trainer? end def common_event_call? return @common_event_id > 0 end def can_rematch? return trainer? && @rematch_flag >= 1 end def display_name if trainer? return sprintf("%s %s", GameData::TrainerType.get(@trainer_type).name, pbGetMessageFromHash(MessageTypes::TRAINER_NAMES, @name)) end return _INTL(@name) end # Original battle is 0, first rematch is 1, etc. def variant return 0 if !trainer? return @version - @start_version end # Returns the version of this trainer to be battled next. def next_version var = variant + 1 var = [var, $PokemonGlobal.phone.rematch_variant, @versions_count - 1].min return @start_version + var end def increment_version return if !trainer? @version = next_version end def set_trainer_event_ready_for_rematch return if !@trainer $game_self_switches[[@map_id, @event_id, "A"]] = false $game_self_switches[[@map_id, @event_id, "B"]] = true $game_map.need_refresh = true end end end #=============================================================================== # #=============================================================================== class Phone module Call module_function def can_make? return false if $game_map.metadata&.has_flag?("NoPhoneSignal") return true end # For the player initiating the call. def can_call_contact?(contact) return false if !contact if !can_make? pbMessage(_INTL("There is no phone signal here...")) return false end return true if !contact.trainer? if contact.map_id == $game_map.map_id pbMessage(_INTL("The Trainer is close by.\nTalk to the Trainer in person!")) return false end caller_map_metadata = GameData::MapMetadata.try_get(contact.map_id) this_map_metadata = $game_map.metadata if !caller_map_metadata || !caller_map_metadata.town_map_position || !this_map_metadata || !this_map_metadata.town_map_position || caller_map_metadata.town_map_position[0] != this_map_metadata.town_map_position[0] pbMessage(_INTL("The Trainer is out of range.")) return false end return true end # Get a random trainer contact from the region the player is currently in, # but is not in the same map as the player. def get_random_trainer_for_incoming_call player_location = $game_map.metadata&.town_map_position return nil if !player_location player_region = player_location[0] valid_contacts = [] $PokemonGlobal.phone.contacts.each do |contact| next if !contact.trainer? || !contact.visible? next if contact.map_id == $game_map.map_id || pbGetMapNameFromId(contact.map_id) == $game_map.name caller_map_metadata = GameData::MapMetadata.try_get(contact.map_id) next if !caller_map_metadata || !caller_map_metadata.town_map_position next if caller_map_metadata.town_map_position[0] != player_region valid_contacts.push(contact) end return valid_contacts.sample end #----------------------------------------------------------------------------- def make_incoming return if !can_make? contact = get_random_trainer_for_incoming_call return if !contact if contact.common_event_call? if !pbCommonEvent(contact.common_event_id) pbMessage(_INTL("{1}'s messages not defined.\nCouldn't call common event {2}.", contact.display_name, contact.common_event_id)) end else call = generate_trainer_dialogue(contact) play(call, contact) end end # Phone::Contact # Trainer type, name[, start_version] # Name (for non-trainers) def make_outgoing(*args) if args[0].is_a?(Phone::Contact) contact = args[0] elsif args.length > 1 contact = Phone.get(true, args[0], args[1], args[2] || 0) # Trainer else contact = Phone.get(false, args[0]) # Non-trainer end raise _INTL("Couldn't find phone contact given: {1}.", args.inspect) if !contact return if !can_call_contact?(contact) if contact.common_event_call? if !pbCommonEvent(contact.common_event_id) pbMessage(_INTL("{1}'s messages not defined.\nCouldn't call common event {2}.", contact.display_name, contact.common_event_id)) end else call = generate_trainer_dialogue(contact) play(call, contact) end end def start_message(contact = nil) pbMessage("......\\wt[5] ......\1") end def play(dialogue, contact) start_message(contact) contact_pokemon_species = get_random_contact_pokemon_species(contact) random_encounter_species = get_random_encounter_species(contact) contact_map_name = get_map_name(contact) gender_colour_text = "" if Settings::COLOR_PHONE_CALL_MESSAGES_BY_CONTACT_GENDER && contact.trainer? data = GameData::TrainerType.try_get(contact.trainer_type) if data case data.gender when 0 then gender_colour_text = "\\b" when 1 then gender_colour_text = "\\r" end end end messages = dialogue.split("\\m") messages.each_with_index do |message, i| message.gsub!(/\\TN/, _INTL(contact.name)) message.gsub!(/\\TP/, contact_pokemon_species) message.gsub!(/\\TE/, random_encounter_species) message.gsub!(/\\TM/, contact_map_name) message += "\1" if i < messages.length - 1 pbMessage(gender_colour_text + message) end end_message(contact) end def end_message(contact = nil) pbMessage(_INTL("Click!") + "\\wt[10]\n......\\wt[5] ......\1") end #----------------------------------------------------------------------------- def generate_trainer_dialogue(contact) validate contact => Phone::Contact # Get the set of messages to be used by the contact messages = GameData::PhoneMessage.try_get(contact.trainer_type, contact.name, contact.version) messages = GameData::PhoneMessage.try_get(contact.trainer_type, contact.name, contact.start_version) if !messages messages = GameData::PhoneMessage::DATA["default"] if !messages # Create lambda for choosing a random message and translating it get_random_message = lambda do |msgs| return "" if !msgs msg = msgs.sample return "" if !msg return pbGetMessageFromHash(MessageTypes::PHONE_MESSAGES, msg) end # Choose random greeting depending on time of day ret = get_random_message.call(messages.intro) time = pbGetTimeNow if PBDayNight.isMorning?(time) mod_call = get_random_message.call(messages.intro_morning) ret = mod_call if !nil_or_empty?(mod_call) elsif PBDayNight.isAfternoon?(time) mod_call = get_random_message.call(messages.intro_afternoon) ret = mod_call if !nil_or_empty?(mod_call) elsif PBDayNight.isEvening?(time) mod_call = get_random_message.call(messages.intro_evening) ret = mod_call if !nil_or_empty?(mod_call) end ret += "\\m" # Choose main message set if Phone.rematches_enabled && contact.rematch_flag > 0 # Trainer is ready for a rematch, so tell/remind the player case contact.rematch_flag when 1 # Tell the player ret += get_random_message.call(messages.battle_request) contact.rematch_flag = 2 # Ready for rematch and told player when 2 # Remind the player if messages.battle_remind ret += get_random_message.call(messages.battle_remind) else ret += get_random_message.call(messages.battle_request) end end else # Standard messages if messages.body1 && messages.body2 && (!messages.body || rand(100) < 75) # Choose random pair of body messages ret += get_random_message.call(messages.body1) ret += "\\m" ret += get_random_message.call(messages.body2) else # Choose random full body message ret += get_random_message.call(messages.body) end # Choose end message mod_call = get_random_message.call(messages.end) ret += "\\m" + mod_call if !nil_or_empty?(mod_call) end return ret end def get_random_contact_pokemon_species(contact) return "" if !contact.trainer? version = [contact.version, contact.start_version].max trainer_data = GameData::Trainer.try_get(contact.trainer_type, contact.name, version) return "" if !trainer_data pkmn = trainer_data.pokemon.sample[:species] return GameData::Species.get(pkmn).name end def get_random_encounter_species(contact) return "" if !contact.trainer? encounter_data = GameData::Encounter.get(contact.map_id, $PokemonGlobal.encounter_version) return "" if !encounter_data get_species_from_table = lambda do |encounter_table| return nil if !encounter_table || encounter_table.length == 0 len = [encounter_table.length, 4].min # From first 4 slots only return encounter_table[rand(len)][1] end enc_tables = encounter_data.types species = get_species_from_table.call(enc_tables[:Land]) if !species species = get_species_from_table.call(enc_tables[:Cave]) species = get_species_from_table.call(enc_tables[:Water]) if !species end return "" if !species return GameData::Species.get(species).name end def get_map_name(contact) return pbGetMapNameFromId(contact.map_id) end end end #=============================================================================== # #=============================================================================== EventHandlers.add(:on_frame_update, :phone_call_counter, proc { next if !$player&.has_pokegear # Don't count down various phone times if other things are happening next if $game_temp.in_menu || $game_temp.in_battle || $game_temp.message_window_showing next if $game_player.move_route_forcing || pbMapInterpreterRunning? # Count down time to next can-battle for each trainer contact $PokemonGlobal.phone.refresh_ready_trainers # Count down time to next phone call if $PokemonGlobal.phone.time_to_next_call <= 0 $PokemonGlobal.phone.time_to_next_call = rand(20...40) * 60.0 # 20-40 minutes end $PokemonGlobal.phone.time_to_next_call -= Graphics.delta next if $PokemonGlobal.phone.time_to_next_call > 0 # Time for a random phone call; generate one Phone::Call.make_incoming } )