mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-11 23:24:59 +00:00
update 6.7
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
class PokemonEncounters
|
||||
WEATHER_ENCOUNTER_BASE_CHANCE = 8 #/100 (for weather intensity of 0)
|
||||
alias pokemonEssentials_PokemonEncounter_choose_wild_pokemon choose_wild_pokemon
|
||||
ANIMATION_WEATHER_ENCOUNTER = 3
|
||||
def choose_wild_pokemon(enc_type, *args)
|
||||
return pokemonEssentials_PokemonEncounter_choose_wild_pokemon(enc_type, *args) if !$game_weather
|
||||
current_weather_type = $game_weather.get_map_weather_type($game_map.map_id)
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
Events.onMapChange+= proc { |_old_map_id|
|
||||
next if !$game_weather || !$game_weather.current_weather || !$game_weather.last_update_time
|
||||
next if !$game_map
|
||||
echoln pbGetTimeNow.to_i
|
||||
update_overworld_weather($game_map.map_id)
|
||||
next if $game_weather.last_update_time.to_i + GameWeather::TIME_BETWEEN_WEATHER_UPDATES > pbGetTimeNow.to_i
|
||||
|
||||
echoln "- Updating the weather -"
|
||||
new_map_id = $game_map.map_id
|
||||
mapMetadata = GameData::MapMetadata.try_get(new_map_id)
|
||||
next if mapMetadata.nil?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class BetterRegionMap
|
||||
DEBUG_WEATHER = true
|
||||
DEBUG_WEATHER = $DEBUG
|
||||
def update_weather_icon(location)
|
||||
return
|
||||
return nil if !location
|
||||
|
||||
@@ -21,7 +21,8 @@ class GameWeather
|
||||
attr_accessor :current_weather
|
||||
attr_accessor :last_update_time
|
||||
|
||||
TIME_BETWEEN_WEATHER_UPDATES = 12000 # 180 seconds, only actually changes once the player changes map
|
||||
#TIME_BETWEEN_WEATHER_UPDATES in in-game seconds (1 irl second = 60 in-game seconds)
|
||||
TIME_BETWEEN_WEATHER_UPDATES = 3600 #1 in-game hour (1 irl minute) .
|
||||
|
||||
CHANCE_OF_NEW_WEATHER = 2 # /100 spontaneous new weather popping up somewhere
|
||||
CHANCE_OF_RAIN = 40 #/100
|
||||
@@ -122,6 +123,7 @@ class GameWeather
|
||||
def try_propagate_weather_to_neighbors(map_id,propagating_map_weather_type,propagating_map_weather_intensity)
|
||||
propagating_map_neighbors = @neighbors_maps[map_id]
|
||||
|
||||
return unless propagating_map_neighbors
|
||||
return if propagating_map_weather_type == :None
|
||||
return unless can_weather_spread(propagating_map_weather_type)
|
||||
propagating_map_weather_type, propagating_map_weather_intensity = normalize_legendary_weather(propagating_map_weather_type, propagating_map_weather_intensity)
|
||||
@@ -146,10 +148,10 @@ class GameWeather
|
||||
|
||||
def try_move_weather_to_neighbors(map_id,map_weather_type,weather_intensity)
|
||||
map_neighbors = @neighbors_maps[map_id]
|
||||
return unless map_neighbors
|
||||
return if map_weather_type == :None || weather_intensity <= 1
|
||||
return unless can_weather_spread(map_weather_type)
|
||||
map_weather_type, weather_intensity = normalize_legendary_weather(map_weather_type, weather_intensity)
|
||||
|
||||
map_neighbors.each do |neighbor_id|
|
||||
neighbor_weather_type = get_map_weather_type(neighbor_id)
|
||||
neighbor_weather_intensity = get_map_weather_intensity(neighbor_id)
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
# available channels
|
||||
# :RANDOM
|
||||
# :NEWS
|
||||
# :WEATHER
|
||||
#
|
||||
def showTVText(channel = :NEWS)
|
||||
|
||||
TV_CHANNELS = [:NEWS, :WEATHER]
|
||||
def showTVText(channel = :RANDOM)
|
||||
channel = TV_CHANNELS.sample if channel == :RANDOM
|
||||
case channel
|
||||
when :NEWS
|
||||
pbMessage(getTVNewsCaption())
|
||||
when :WEATHER
|
||||
pbMessage(_INTL("It's the weather channel! Let's see how things are looking out today."))
|
||||
pbWeatherMapMap()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,3 +31,41 @@ def hoennSelectStarter
|
||||
pbAddPokemonSilent(selected_starter)
|
||||
return selected_starter
|
||||
end
|
||||
|
||||
|
||||
def secretBaseQuest_pickedNearbySpot()
|
||||
return false if !$Trainer.secretBase
|
||||
expected_map = 65
|
||||
expected_positions = [
|
||||
[30,43],[31,43],[32,42],[33,42],[34,42],[35,42],[36,40],[37,40],#trees
|
||||
[41,40] #cliff
|
||||
]
|
||||
|
||||
picked_base_map = $Trainer.secretBase.outside_map_id
|
||||
picked_position = $Trainer.secretBase.outside_entrance_position
|
||||
|
||||
echoln picked_base_map
|
||||
echoln picked_position
|
||||
echoln picked_base_map == expected_map && expected_positions.include?(picked_position)
|
||||
return picked_base_map == expected_map && expected_positions.include?(picked_position)
|
||||
end
|
||||
|
||||
|
||||
#To scroll a picture on screen in a seamless, continuous loop (used in the truck scene in the intro)
|
||||
# Provide 2 pictures (so that the loop isn't choppy)
|
||||
# Speed in pixels per frame
|
||||
def scroll_picture_loop(pic_a_nb, pic_b_nb, width, speed)
|
||||
pic_a = $game_screen.pictures[pic_a_nb]
|
||||
pic_b = $game_screen.pictures[pic_b_nb]
|
||||
|
||||
# move both
|
||||
pic_a.x -= speed
|
||||
pic_b.x -= speed
|
||||
|
||||
# wrap-around: always place offscreen one after the other
|
||||
if pic_a.x <= -width
|
||||
pic_a.x = pic_b.x + width
|
||||
elsif pic_b.x <= -width
|
||||
pic_b.x = pic_a.x + width
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,20 +8,19 @@ class Player < Trainer
|
||||
alias pokemonEssentials_player_initialize initialize
|
||||
def initialize(*args)
|
||||
pokemonEssentials_player_initialize(*args)
|
||||
@rival_appearance = init_rival_appearance
|
||||
end
|
||||
|
||||
|
||||
|
||||
def init_rival_appearance
|
||||
if isPlayerMale
|
||||
return TrainerAppearance.new(5,
|
||||
@rival_appearance= TrainerAppearance.new(5,
|
||||
HAT_MAY,
|
||||
CLOTHES_MAY,
|
||||
getFullHairId(HAIR_MAY,3) ,
|
||||
0, 0, 0)
|
||||
else
|
||||
return TrainerAppearance.new(5,
|
||||
@rival_appearance= TrainerAppearance.new(5,
|
||||
HAT_BRENDAN,
|
||||
CLOTHES_BRENDAN,
|
||||
getFullHairId(HAIR_BRENDAN,3),
|
||||
@@ -69,7 +68,7 @@ class Sprite_Character
|
||||
end
|
||||
end
|
||||
|
||||
def get_rival_starter
|
||||
def get_hoenn_rival_starter
|
||||
case get_rival_starter_type()
|
||||
when :GRASS
|
||||
return obtainStarter(0)
|
||||
@@ -225,11 +224,11 @@ def initializeRivalBattledTrainer
|
||||
trainer_type = :RIVAL1
|
||||
trainer_name = isPlayerMale ? "May" : "Brendan"
|
||||
trainer_appearance = $Trainer.rival_appearance
|
||||
rivalBattledTrainer = BattledTrainer.new(trainer_type,trainer_name,0)
|
||||
rivalBattledTrainer = BattledTrainer.new(trainer_type,trainer_name,0,BATTLED_TRAINER_RIVAL_KEY)
|
||||
rivalBattledTrainer.set_custom_appearance(trainer_appearance)
|
||||
echoln rivalBattledTrainer.currentTeam
|
||||
team = []
|
||||
team<<Pokemon.new(get_rival_starter,5)
|
||||
team<<Pokemon.new(get_hoenn_rival_starter,5)
|
||||
rivalBattledTrainer.currentTeam =team
|
||||
return rivalBattledTrainer
|
||||
end
|
||||
|
||||
92
Data/Scripts/053_PIF_Hoenn/Hoenn_Wally.rb
Normal file
92
Data/Scripts/053_PIF_Hoenn/Hoenn_Wally.rb
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
BATTLED_TRAINER_WALLY_KEY = "wally"
|
||||
|
||||
SWITCH_WALLY_CATCHING_POKEMON = 2022
|
||||
SWITCH_WALLY_GAVE_POKEMON = 2023
|
||||
SWITCH_WALLY_GAVE_POKEMON_DIALOGUE = 2024
|
||||
|
||||
COMMON_EVENT_WALLY_FOLLOWING_DIALOGUE = 199
|
||||
|
||||
def wally_initialize()
|
||||
trainer_type = :RIVAL2
|
||||
trainer_name = "Wally"
|
||||
battledTrainer = BattledTrainer.new(trainer_type,trainer_name,0,BATTLED_TRAINER_WALLY_KEY)
|
||||
battledTrainer.currentTeam =[]#team
|
||||
$PokemonGlobal.battledTrainers={} if !$PokemonGlobal.battledTrainers
|
||||
$PokemonGlobal.battledTrainers[BATTLED_TRAINER_WALLY_KEY] = battledTrainer
|
||||
return battledTrainer
|
||||
end
|
||||
|
||||
def wally_add_pokemon(pokemon_species,level)
|
||||
trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_WALLY_KEY]
|
||||
pokemon = Pokemon.new(pokemon_species,level)
|
||||
trainer.currentTeam.push(pokemon)
|
||||
updateRebattledTrainerWithKey(BATTLED_TRAINER_WALLY_KEY,trainer)
|
||||
end
|
||||
|
||||
def wally_remove_pokemon(pokemon_species)
|
||||
|
||||
trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_WALLY_KEY]
|
||||
echoln trainer.currentTeam
|
||||
trainer.currentTeam.each { |pokemon|
|
||||
if pokemon.species == pokemon_species
|
||||
trainer.currentTeam.delete(pokemon)
|
||||
updateRebattledTrainerWithKey(BATTLED_TRAINER_WALLY_KEY, trainer)
|
||||
return
|
||||
end
|
||||
}
|
||||
end
|
||||
def wally_fuse_pokemon(with_fusion_screen=true)
|
||||
|
||||
head_pokemon_index=0
|
||||
body_pokemon_index=1
|
||||
|
||||
trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_WALLY_KEY]
|
||||
|
||||
body_pokemon = trainer.currentTeam[body_pokemon_index]
|
||||
head_pokemon = trainer.currentTeam[head_pokemon_index]
|
||||
return if head_pokemon.isFusion? || body_pokemon.isFusion?
|
||||
npcTrainerFusionScreenPokemon(head_pokemon.clone,body_pokemon.clone) if with_fusion_screen
|
||||
|
||||
fusion_species = getFusedPokemonIdFromSymbols(body_pokemon.species,head_pokemon.species)
|
||||
level = (body_pokemon.level + head_pokemon.level)/2
|
||||
fused_pokemon = Pokemon.new(fusion_species,level)
|
||||
|
||||
trainer.currentTeam.delete(body_pokemon)
|
||||
trainer.currentTeam.delete(head_pokemon)
|
||||
trainer.currentTeam.push(fused_pokemon)
|
||||
|
||||
updateRebattledTrainerWithKey(BATTLED_TRAINER_WALLY_KEY,trainer)
|
||||
end
|
||||
|
||||
|
||||
def npcTrainerFusionScreenPokemon(headPokemon,bodyPokemon)
|
||||
fusionScene = PokemonFusionScene.new
|
||||
newSpecies = getFusedPokemonIdFromSymbols(bodyPokemon.species,headPokemon.species)
|
||||
|
||||
newDexNumber = getDexNumberForSpecies(newSpecies)
|
||||
if fusionScene.pbStartScreen(bodyPokemon, headPokemon, newDexNumber, :DNASPLICERS)
|
||||
fusionScene.pbFusionScreen(false, false, false,false)
|
||||
fusionScene.pbEndScreen
|
||||
end
|
||||
end
|
||||
|
||||
def npcTrainerFusionScreen(headSpecies,bodySpecies)
|
||||
fusionScene = PokemonFusionScene.new
|
||||
newid = getFusedPokemonIdFromSymbols(bodySpecies,headSpecies)
|
||||
fusionScene.pbStartScreen(Pokemon.new(bodySpecies,100), Pokemon.new(headSpecies,100), newid, :DNASPLICERS)
|
||||
end
|
||||
|
||||
def getWallyTrainer()
|
||||
return $PokemonGlobal.battledTrainers[BATTLED_TRAINER_WALLY_KEY]
|
||||
end
|
||||
def wally_follow(eventId)
|
||||
trainer = $PokemonGlobal.battledTrainers[BATTLED_TRAINER_WALLY_KEY]
|
||||
partnerWithTrainer(eventId, $game_map.map_id, trainer,BATTLED_TRAINER_WALLY_KEY, COMMON_EVENT_WALLY_FOLLOWING_DIALOGUE)
|
||||
end
|
||||
|
||||
def wally_unfollow()
|
||||
unpartnerWithTrainer()
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SecretBase
|
||||
attr_reader :outside_map_id #Id of the map where the secret base is
|
||||
attr_reader :inside_map_id #Id of the secret base's map itself
|
||||
|
||||
attr_reader :location_name #Name of the Route where the secret base is in plain text
|
||||
|
||||
|
||||
attr_reader :outside_entrance_position #Fly coordinates
|
||||
attr_reader :inside_entrance_position #Where the player gets warped
|
||||
|
||||
attr_reader :biome_type #:CAVE, :TREE,
|
||||
attr_reader :base_layout_type
|
||||
attr_accessor :base_name
|
||||
attr_accessor :base_message
|
||||
|
||||
attr_accessor :layout
|
||||
attr_accessor :is_visitor
|
||||
|
||||
|
||||
def initialize(biome:,outside_map_id:,outside_entrance_position:, inside_map_id:, base_layout_type:, is_visitor:, layout: nil, visitor_message:nil)
|
||||
@biome_type = biome
|
||||
@outside_map_id = outside_map_id
|
||||
@inside_map_id = inside_map_id
|
||||
|
||||
@outside_entrance_position = outside_entrance_position
|
||||
@base_layout_type = base_layout_type.to_sym
|
||||
|
||||
@inside_entrance_position = SecretBasesData::SECRET_BASE_ENTRANCES[@base_layout_type][:position]
|
||||
|
||||
@base_name=initializeBaseName
|
||||
@base_message=visitor_message
|
||||
initialize_base_message unless @base_message #Message that people see when visiting the secret base
|
||||
@is_visitor=is_visitor
|
||||
@layout = layout
|
||||
initializeLayout unless @layout
|
||||
end
|
||||
|
||||
def initializeBaseName
|
||||
return _INTL("{1}'s secret base",$Trainer.name)
|
||||
end
|
||||
|
||||
def initialize_base_message
|
||||
return _INTL("Welcome to my secret base!")
|
||||
end
|
||||
def initializeLayout
|
||||
@layout = SecretBaseLayout.new(@base_layout_type,!@is_visitor)
|
||||
entrance_x = @inside_entrance_position[0]
|
||||
entrance_y = @inside_entrance_position[1]
|
||||
|
||||
@layout.add_item(:PC,[entrance_x,entrance_y-3])
|
||||
end
|
||||
|
||||
def load_furniture
|
||||
@layout.items.each do |item_instance|
|
||||
next unless item_instance.is_a?(SecretBaseItemInstance)
|
||||
next unless SecretBasesData::SECRET_BASE_ITEMS[item_instance.itemId]
|
||||
|
||||
item_instance.direction = DIRECTION_DOWN
|
||||
|
||||
|
||||
template = item_instance.itemTemplate
|
||||
echoln template
|
||||
|
||||
|
||||
item_instance.create_events
|
||||
|
||||
# event = $PokemonTemp.createTempEvent(TEMPLATE_EVENT_SECRET_BASE_FURNITURE, $game_map.map_id, item_instance.position, DIRECTION_DOWN)
|
||||
# event.character_name = "player/SecretBases/#{template.graphics}"
|
||||
# event.through = template.pass_through
|
||||
# event.under_player = template.under_player
|
||||
# event.direction = item_instance.direction
|
||||
|
||||
|
||||
if item_instance.itemTemplate.id == :MANNEQUIN && @is_visitor
|
||||
setEventAppearance(item_instance.main_event.id, @trainer_appearance) if @trainer_appearance
|
||||
end
|
||||
# item_instance.setMainEventId(event.id)
|
||||
item_instance.refresh_events
|
||||
#event.refresh
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,172 @@
|
||||
# frozen_string_literal: true
|
||||
class SecretBaseItemInstance
|
||||
attr_reader :itemId
|
||||
attr_reader :instanceId
|
||||
attr_accessor :position
|
||||
attr_accessor :direction
|
||||
attr_accessor :itemTemplate
|
||||
attr_accessor :main_event_id
|
||||
attr_reader :disposed
|
||||
attr_reader :main_event
|
||||
|
||||
RANDOM_ID_LENGTH = 6
|
||||
|
||||
def initialize(itemId, position = [0, 0], direction = DIRECTION_DOWN)
|
||||
@itemId = itemId
|
||||
@instanceId = generate_new_instance_id()
|
||||
@direction = direction
|
||||
@position = position
|
||||
@main_event = nil # Objects that are wider or taller than 1 tile work by duplicating the object, but making only the middle instance visible. This contains the id of the middle instance
|
||||
end
|
||||
|
||||
def create_events()
|
||||
template = get_item_template
|
||||
@volume_events = []
|
||||
main_event = $PokemonTemp.createTempEvent(TEMPLATE_EVENT_SECRET_BASE_FURNITURE, $game_map.map_id, @position, @direction)
|
||||
main_event.character_name = "player/SecretBases/#{template.graphics}"
|
||||
main_event.through = template.pass_through
|
||||
main_event.under_player = template.under_player
|
||||
|
||||
main_event.trigger = template.trigger
|
||||
@main_event = main_event
|
||||
occupied_positions = calculate_occupied_volume_positions(@position)
|
||||
occupied_positions.each do |position|
|
||||
event = $PokemonTemp.createTempEvent(TEMPLATE_EVENT_SECRET_BASE_FURNITURE, $game_map.map_id, position)
|
||||
event.character_name = "player/SecretBases/empty" # Game will consider it passable if we don't put anything here
|
||||
event.through = template.pass_through
|
||||
event.trigger = template.trigger
|
||||
|
||||
@volume_events << event
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Use this - can't save it into the object because of proc
|
||||
def get_item_template
|
||||
return SecretBasesData::SECRET_BASE_ITEMS[@itemId]
|
||||
end
|
||||
|
||||
# fake accessor
|
||||
def itemTemplate
|
||||
return get_item_template
|
||||
end
|
||||
|
||||
def get_occupied_positions()
|
||||
occupied_positions = []
|
||||
occupied_positions << [@main_event.x, @main_event.y]
|
||||
@volume_events.each do |event|
|
||||
occupied_positions << [event.x, event.y]
|
||||
end
|
||||
return occupied_positions
|
||||
end
|
||||
|
||||
def interactable?(position = [0,0])
|
||||
return get_interactable_positions.include?(position)
|
||||
end
|
||||
|
||||
def get_interactable_positions()
|
||||
all_positions = get_occupied_positions
|
||||
uninteractable_positions = get_item_template.uninteractable_positions
|
||||
main_x, main_y = @main_event.x, @main_event.y
|
||||
|
||||
uninteractable_absolute_positions = uninteractable_positions.map do |dx, dy|
|
||||
[main_x + dx, main_y + dy]
|
||||
end
|
||||
interactable_positions = all_positions.reject do |pos|
|
||||
uninteractable_absolute_positions.include?(pos)
|
||||
end
|
||||
return interactable_positions
|
||||
end
|
||||
|
||||
def calculate_occupied_volume_positions(main_event_position)
|
||||
template = get_item_template
|
||||
|
||||
item_x, item_y = main_event_position
|
||||
item_height = template.height || 1
|
||||
item_width = template.width || 1
|
||||
direction = @direction
|
||||
|
||||
# Flip width/height if rotated sideways
|
||||
if direction == DIRECTION_LEFT || direction == DIRECTION_RIGHT
|
||||
item_width, item_height = item_height, item_width
|
||||
end
|
||||
|
||||
half_width = (item_width - 1) / 2
|
||||
half_height = (item_height - 1) / 2
|
||||
|
||||
occupied_positions = []
|
||||
|
||||
x_range = (item_x - half_width..item_x + half_width)
|
||||
y_range = (item_y - half_height..item_y + half_height)
|
||||
x_range.each do |x|
|
||||
y_range.each do |y|
|
||||
is_main_event = (x == item_x && y == item_y)
|
||||
next if is_main_event
|
||||
|
||||
occupied_positions << [x, y]
|
||||
end
|
||||
end
|
||||
return occupied_positions
|
||||
|
||||
end
|
||||
|
||||
def refresh_events
|
||||
@main_event.refresh
|
||||
@volume_events.each do |event|
|
||||
event.refresh
|
||||
end
|
||||
end
|
||||
|
||||
def name
|
||||
return itemTemplate&.real_name
|
||||
end
|
||||
|
||||
def getGraphics()
|
||||
return itemTemplate.graphics
|
||||
end
|
||||
|
||||
def height
|
||||
return itemTemplate.height
|
||||
end
|
||||
|
||||
def width
|
||||
return itemTemplate.width
|
||||
end
|
||||
|
||||
def generate_new_instance_id()
|
||||
randomId = rand(36 ** RANDOM_ID_LENGTH).to_s(36)
|
||||
return "#{@itemId}_#{randomId}"
|
||||
end
|
||||
|
||||
def getMainEvent()
|
||||
return @main_event
|
||||
end
|
||||
|
||||
def disposed?
|
||||
return @disposed
|
||||
end
|
||||
|
||||
def dispose
|
||||
@itemId = nil
|
||||
@position = nil
|
||||
@direction = nil
|
||||
@main_event_id = nil
|
||||
@itemTemplate = nil
|
||||
@disposed = true
|
||||
|
||||
@main_event.erase
|
||||
@main_event = nil
|
||||
@volume_events.each do |event|
|
||||
event.erase
|
||||
end
|
||||
@volume_events = nil
|
||||
end
|
||||
|
||||
def connect_to_other_instance(instance_id)
|
||||
@volume_events << instance_id
|
||||
end
|
||||
|
||||
def set_main_instance(instance_id)
|
||||
@main_event = instance_id
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# wrapper for secret base items
|
||||
|
||||
class SecretBaseItem
|
||||
attr_reader :id # Symbol. Used for manipulating in other classes (trainer.unlockedBaseItems is an array of these, etc.)
|
||||
attr_reader :graphics # File path to the item's graphics
|
||||
attr_reader :real_name # Name displayed in-game
|
||||
attr_reader :price
|
||||
|
||||
# Event attributes
|
||||
attr_reader :pass_through # for carpets, etc.
|
||||
attr_reader :under_player # for carpets, etc.
|
||||
|
||||
attr_reader :height
|
||||
attr_reader :width
|
||||
#todo: instead of this, have a 2d array that represents the layout visually and shows which tiles are interactable and which aren't
|
||||
# ex:
|
||||
# [
|
||||
# [[x],[x],[x]],
|
||||
# [[i],[i],[i]
|
||||
# ]
|
||||
# -> 2 rows, only interactable from the bottom
|
||||
|
||||
# Secret base object attributes
|
||||
attr_reader :deletable
|
||||
attr_reader :behavior # Lambda function that's defined when initializing the items. Some secret bases can have special effects when you interact with them (ex: a berry pot to grow berries, a bed, etc.)
|
||||
# -> This is the function that will be called when the player interacts with the item in the base.
|
||||
# Should just display flavor text for most basic items.
|
||||
attr_reader :uninteractable_positions #Positions at which the behavior won't trigger (relative to the center) ex: [[-1,0],[1,0]] in a 3x1 object will trigger in the center, but not on the sides
|
||||
attr_reader :trigger #Can define a different event trigger (Action Button by default)
|
||||
|
||||
def initialize(id:, graphics:, real_name:, price:, deletable: true, pass_through: false, under_player: false, behavior: nil, height:1, width:1, uninteractable_positions:[], trigger:TRIGGER_ACTION_BUTTON)
|
||||
@id = id
|
||||
@graphics = graphics
|
||||
@real_name = real_name
|
||||
@price = price
|
||||
@deletable = deletable
|
||||
@pass_through = pass_through
|
||||
@under_player = under_player
|
||||
|
||||
#Parts of the item that the player shouldn't be able to pass through
|
||||
@height = height
|
||||
@width = width
|
||||
|
||||
# Default behavior just shows text if none provided
|
||||
@behavior = behavior
|
||||
@uninteractable_positions = uninteractable_positions
|
||||
@trigger = trigger
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,81 @@
|
||||
class SecretBaseLayout
|
||||
attr_accessor :items #SecretBaseItemInstance
|
||||
attr_accessor :tileset #todo Reuse the same layouts map for all bases and change the tileset depending on the type
|
||||
attr_accessor :biome_type #:TREES, :CLIFF, :CLIFF_BEACH, :BUSH, etc. -> Determines which tiles are used in the base
|
||||
attr_accessor :is_player_base
|
||||
def initialize(layout_biome,is_player_base=false)
|
||||
@biome_type = layout_biome
|
||||
@items = []
|
||||
@is_player_base = is_player_base
|
||||
end
|
||||
|
||||
def add_item(itemId, position = [0, 0])
|
||||
new_item = SecretBaseItemInstance.new(itemId, position)
|
||||
@items << new_item
|
||||
return new_item.instanceId
|
||||
end
|
||||
|
||||
def get_item_at_position(position = [0,0])
|
||||
@items.each do |item|
|
||||
return item if item.get_occupied_positions.include?(position)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def get_item_by_id(instanceId)
|
||||
@items.each do |item|
|
||||
return item if item.instanceId == instanceId
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
def remove_item_by_instance(instanceId)
|
||||
@items.each do |item|
|
||||
if item.instanceId == instanceId
|
||||
@items.delete(item)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def remove_item_at_position(position = [0, 0])
|
||||
@items.each do |item|
|
||||
if item.position == position
|
||||
@items.delete(item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# returns a list of ids of the items that are currently in the base's layout
|
||||
def list_items_instances()
|
||||
list = []
|
||||
@items.each do |item|
|
||||
list << item.instanceId
|
||||
end
|
||||
end
|
||||
|
||||
def get_all_occupied_positions()
|
||||
occupied_positions = []
|
||||
@items.each do |item|
|
||||
occupied_positions << get_occupied_positions_for_item(item)
|
||||
end
|
||||
return occupied_positions
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def check_position_available_for_item(itemInstance,position)
|
||||
#placed_item_positions = get_all_occupied_positions
|
||||
item_occupied_positions = itemInstance.calculate_occupied_volume_positions(position)
|
||||
item_occupied_positions.each do |position|
|
||||
x, y = position
|
||||
#return false if placed_item_positions.include?(position)
|
||||
return false if !$game_map.passableStrict?(x, y, DIRECTION_ALL)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
class SecretBaseTrainer
|
||||
attr_accessor :name
|
||||
attr_accessor :nb_badges
|
||||
attr_accessor :game_mode
|
||||
attr_accessor :appearance #TrainerAppearance
|
||||
attr_accessor :team #Array of Pokemon
|
||||
|
||||
def initialize(name, nb_badges, game_mode, appearance, team)
|
||||
@name = name
|
||||
@nb_badges = nb_badges
|
||||
@game_mode = game_mode
|
||||
@appearance = appearance
|
||||
@team = team
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
class VisitorSecretBase < SecretBase
|
||||
attr_reader :trainer_name, :trainer_badges, :trainer_game_mode
|
||||
attr_reader :trainer_appearance, :trainer_team
|
||||
|
||||
def initialize(biome:, outside_map_id:, outside_entrance_position:, inside_map_id:, layout:, base_layout_type:, base_message:, trainer_data:)
|
||||
super(biome: biome,
|
||||
outside_map_id: outside_map_id,
|
||||
outside_entrance_position: outside_entrance_position,
|
||||
inside_map_id: inside_map_id,
|
||||
layout: layout,
|
||||
base_layout_type: base_layout_type,
|
||||
visitor_message: base_message,
|
||||
is_visitor: true,)
|
||||
|
||||
@trainer_name = trainer_data.name
|
||||
@trainer_badges = trainer_data.nb_badges || 0
|
||||
@trainer_game_mode = trainer_data.game_mode || 0
|
||||
@trainer_appearance = trainer_data.appearance
|
||||
@trainer_team = trainer_data.team
|
||||
end
|
||||
|
||||
def dump_info
|
||||
echoln "=== Visitor Secret Base ==="
|
||||
echoln "Biome: #{@biome_type}"
|
||||
echoln "Outside Map ID: #{@outside_map_id}"
|
||||
echoln "Inside Map ID: #{@inside_map_id}"
|
||||
echoln "Outside Entrance Position: #{@outside_entrance_position.inspect}"
|
||||
echoln "Inside Entrance Position: #{@inside_entrance_position.inspect}"
|
||||
echoln "Layout Type: #{@base_layout_type}"
|
||||
echoln "Base Name: #{@base_name}"
|
||||
echoln "Base Message: #{@base_message}"
|
||||
echoln "Visitor?: #{@is_visitor}"
|
||||
|
||||
echoln "--- Trainer Info ---"
|
||||
echoln "Name: #{@trainer_name}"
|
||||
echoln "Badges: #{@trainer_badges}"
|
||||
echoln "Game Mode: #{@trainer_game_mode}"
|
||||
echoln "Appearance: #{@trainer_appearance.inspect}"
|
||||
|
||||
echoln "--- Trainer Team ---"
|
||||
if @trainer_team && !@trainer_team.empty?
|
||||
@trainer_team.each_with_index do |pokemon, i|
|
||||
echoln " #{i + 1}. #{pokemon.name} (#{pokemon.species}, Lv#{pokemon.level})"
|
||||
end
|
||||
else
|
||||
echoln " (No Pokémon)"
|
||||
end
|
||||
|
||||
echoln "============================="
|
||||
end
|
||||
|
||||
end
|
||||
332
Data/Scripts/053_PIF_Hoenn/SecretBases/SecretBasesController.rb
Normal file
332
Data/Scripts/053_PIF_Hoenn/SecretBases/SecretBasesController.rb
Normal file
@@ -0,0 +1,332 @@
|
||||
SWITCH_SECRET_BASE_PLACED_FIRST_DECORATION = 2047
|
||||
|
||||
class Trainer
|
||||
attr_accessor :secretBase
|
||||
attr_accessor :owned_decorations
|
||||
end
|
||||
|
||||
class PokemonTemp
|
||||
attr_accessor :enteredSecretBaseController
|
||||
end
|
||||
|
||||
class SecretBaseController
|
||||
attr_accessor :secretBase
|
||||
|
||||
def initialize(secretBase)
|
||||
@secretBase = secretBase
|
||||
end
|
||||
|
||||
def callBehaviorPosition(item_position)
|
||||
item = @secretBase.layout.get_item_at_position(item_position)
|
||||
if item && item.itemTemplate.behavior && item.interactable?(item_position)
|
||||
item.itemTemplate.behavior.call(item)
|
||||
end
|
||||
end
|
||||
def furnitureInteract(item_position = [], menuStartIndex=0)
|
||||
cmd_labels = {
|
||||
use: _INTL("Use"),
|
||||
move: _INTL("Move"),
|
||||
rotate: _INTL("Rotate"),
|
||||
delete: _INTL("Put away"),
|
||||
cancel: _INTL("Cancel"),
|
||||
decorate: _INTL("Decorate!"),
|
||||
storage: _INTL("Pokémon Storage"),
|
||||
item_storage: _INTL("Item Storage")
|
||||
}
|
||||
|
||||
item = @secretBase.layout.get_item_at_position(item_position)
|
||||
return unless item
|
||||
options = []
|
||||
|
||||
if item.itemId == :PC
|
||||
pbMessage(_INTL("\\se[PC open]{1} booted up the PC.", $Trainer.name))
|
||||
options << :decorate unless @secretBase.is_visitor
|
||||
options << :storage
|
||||
options << :item_storage
|
||||
else
|
||||
options << :use if item.itemTemplate.behavior && item.interactable?(item_position)
|
||||
end
|
||||
|
||||
options << :move unless @secretBase.is_visitor
|
||||
options << :rotate unless @secretBase.is_visitor || item.itemId == :PC
|
||||
options << :delete if item.itemTemplate.deletable && !@secretBase.is_visitor
|
||||
options << :cancel
|
||||
|
||||
actionable = options - [:cancel]
|
||||
if actionable.length == 1
|
||||
return executeFurnitureCommand(item, actionable.first,-1)
|
||||
end
|
||||
|
||||
choice = optionsMenu(options.map { |cmd| cmd_labels[cmd] },-1,menuStartIndex)
|
||||
executeFurnitureCommand(item, options[choice],choice, item_position)
|
||||
end
|
||||
|
||||
def executeFurnitureCommand(item, command, commandIndex, position = nil)
|
||||
case command
|
||||
when :use
|
||||
item.itemTemplate.behavior.call(item)
|
||||
when :move
|
||||
moveSecretBaseItem(item.instanceId, item.position)
|
||||
when :rotate
|
||||
rotateSecretBaseItem(item.getMainEvent)
|
||||
furnitureInteract(position,commandIndex)
|
||||
when :delete
|
||||
if pbConfirmMessage(_INTL("Put away the #{item.name}?"))
|
||||
pbSEPlay("GUI storage put down", 80, 100)
|
||||
resetFurniture(item.instanceId)
|
||||
else
|
||||
furnitureInteract(position,commandIndex)
|
||||
end
|
||||
when :decorate
|
||||
decorateSecretBase
|
||||
when :storage
|
||||
pbFadeOutIn {
|
||||
scene = PokemonStorageScene.new
|
||||
screen = PokemonStorageScreen.new(scene, $PokemonStorage)
|
||||
screen.pbStartScreen(0)
|
||||
}
|
||||
when :item_storage
|
||||
pbPCItemStorage
|
||||
when :cancel
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def reloadItems()
|
||||
$PokemonTemp.pbClearTempEvents
|
||||
SecretBaseLoader.new.loadSecretBaseFurniture(@secretBase)
|
||||
end
|
||||
|
||||
|
||||
def isMovingFurniture?
|
||||
return $game_temp.moving_furniture
|
||||
end
|
||||
|
||||
def decorateSecretBase
|
||||
cmd_addItem = _INTL("Add a decoration")
|
||||
cmd_moveItem = _INTL("Move a decoration")
|
||||
cmd_cancel = _INTL("Back")
|
||||
|
||||
commands = []
|
||||
commands << cmd_addItem
|
||||
commands << cmd_moveItem
|
||||
commands << cmd_cancel
|
||||
|
||||
choice = optionsMenu(commands)
|
||||
case commands[choice]
|
||||
when cmd_addItem
|
||||
item_id = selectAnySecretBaseItem
|
||||
addSecretBaseItem(item_id)
|
||||
when cmd_moveItem
|
||||
item_instance = selectPlacedSecretBaseItemInstance
|
||||
moveSecretBaseItem(item_instance.instanceId, item_instance.position)
|
||||
when cmd_cancel
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def addSecretBaseItem(item_id)
|
||||
return if @secretBase.is_a?(VisitorSecretBase)
|
||||
echoln "ADDING ITEM #{item_id}"
|
||||
if item_id
|
||||
new_item_instance = $Trainer.secretBase.layout.add_item(item_id, [$game_player.x, $game_player.y])
|
||||
SecretBaseLoader.new.loadSecretBaseFurniture(@secretBase)
|
||||
$game_temp.original_direction = $game_player.direction
|
||||
$game_player.direction = DIRECTION_DOWN
|
||||
moveSecretBaseItem(new_item_instance, nil)
|
||||
end
|
||||
end
|
||||
|
||||
def rotateSecretBaseItem(event)
|
||||
pbSEPlay("GUI party switch", 80, 100)
|
||||
direction_fix = event.direction_fix
|
||||
event.direction_fix = false
|
||||
event.turn_left_90
|
||||
event.direction_fix = direction_fix
|
||||
end
|
||||
|
||||
def moveSecretBaseItem(itemInstanceId, oldPosition = nil)
|
||||
return if @secretBase.is_a?(VisitorSecretBase)
|
||||
itemInstance = @secretBase.layout.get_item_by_id(itemInstanceId)
|
||||
|
||||
event = itemInstance.getMainEvent
|
||||
|
||||
$game_player.setPlayerGraphicsOverride("SecretBases/#{itemInstance.getGraphics}")
|
||||
$game_player.direction_fix = true
|
||||
$game_player.under_player = event.under_player
|
||||
$game_player.through = event.through # todo: Make it impossible to go past the walls
|
||||
$game_temp.moving_furniture = itemInstanceId
|
||||
$game_temp.moving_furniture_oldPlayerPosition = [$game_player.x, $game_player.y]
|
||||
$game_temp.moving_furniture_oldItemPosition = oldPosition
|
||||
|
||||
event.opacity = 50 if event
|
||||
event.through = true if event
|
||||
|
||||
$game_player.x, $game_player.y = itemInstance.position
|
||||
$game_system.menu_disabled = true
|
||||
$game_map.refresh
|
||||
end
|
||||
|
||||
def cancelMovingFurniture()
|
||||
$game_system.menu_disabled = false
|
||||
$game_player.removeGraphicsOverride()
|
||||
$game_temp.moving_furniture = nil
|
||||
end
|
||||
|
||||
|
||||
def placeFurnitureMenu(menu_position = 0)
|
||||
if !$Trainer.secretBase || !$game_temp.moving_furniture
|
||||
cancelMovingFurniture()
|
||||
end
|
||||
|
||||
cmd_place = _INTL("Place here")
|
||||
cmd_rotate = _INTL("Rotate")
|
||||
cmd_reset = _INTL("Reset")
|
||||
cmd_cancel = _INTL("Cancel")
|
||||
|
||||
options = []
|
||||
options << cmd_place
|
||||
options << cmd_rotate
|
||||
options << cmd_reset
|
||||
options << cmd_cancel
|
||||
|
||||
choice = optionsMenu(options, -1, menu_position)
|
||||
case options[choice]
|
||||
when cmd_place
|
||||
placeFurnitureAtCurrentPosition($game_temp.moving_furniture, $game_player.direction)
|
||||
when cmd_rotate
|
||||
rotateFurniture
|
||||
placeFurnitureMenu(choice)
|
||||
when cmd_reset
|
||||
resetFurniture($game_temp.moving_furniture)
|
||||
when cmd_cancel
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def placeFurnitureAtCurrentPosition(furnitureInstanceId, direction)
|
||||
$game_switches[SWITCH_SECRET_BASE_PLACED_FIRST_DECORATION] = true
|
||||
itemInstance = @secretBase.layout.get_item_by_id(furnitureInstanceId)
|
||||
currentPosition = [$game_player.x, $game_player.y]
|
||||
itemInstance.position = currentPosition
|
||||
itemInstance.direction = direction
|
||||
|
||||
if @secretBase.layout.check_position_available_for_item(itemInstance,currentPosition)
|
||||
main_event = itemInstance.getMainEvent
|
||||
main_event.direction = $game_player.direction
|
||||
|
||||
$PokemonTemp.pbClearTempEvents
|
||||
SecretBaseLoader.new.loadSecretBaseFurniture(@secretBase)
|
||||
|
||||
# Roload after items update
|
||||
itemInstance = $Trainer.secretBase.layout.get_item_by_id(furnitureInstanceId)
|
||||
event = itemInstance.getMainEvent
|
||||
event.direction = $game_player.direction
|
||||
resetPlayerPosition
|
||||
else
|
||||
pbMessage(_INTL("There's no room here!"))
|
||||
end
|
||||
end
|
||||
|
||||
def resetFurniture(furnitureInstanceId)
|
||||
adding_new_item = $game_temp.moving_furniture_oldItemPosition == nil
|
||||
itemInstance = $Trainer.secretBase.layout.get_item_by_id(furnitureInstanceId)
|
||||
$Trainer.secretBase.layout.remove_item_by_instance(itemInstance.instanceId) if adding_new_item
|
||||
reloadItems
|
||||
resetPlayerPosition
|
||||
itemInstance.dispose if adding_new_item
|
||||
end
|
||||
def resetPlayerPosition
|
||||
return unless $game_temp.moving_furniture
|
||||
$game_player.removeGraphicsOverride
|
||||
pbFadeOutIn {
|
||||
$game_player.direction_fix = false
|
||||
if $game_temp.original_direction
|
||||
$game_player.direction = $game_temp.original_direction
|
||||
end
|
||||
$game_player.through = false
|
||||
$game_player.under_player = false
|
||||
$game_temp.player_new_map_id = $game_map.map_id
|
||||
$game_temp.player_new_x = $game_temp.moving_furniture_oldPlayerPosition[0]
|
||||
$game_temp.player_new_y = $game_temp.moving_furniture_oldPlayerPosition[1]
|
||||
$scene.transfer_player(true)
|
||||
$game_map.autoplay
|
||||
$game_map.refresh
|
||||
}
|
||||
$game_temp.moving_furniture_oldPlayerPosition = nil
|
||||
$game_temp.moving_furniture_oldItemPosition = nil
|
||||
$game_temp.moving_furniture = nil
|
||||
$game_system.menu_disabled = false
|
||||
end
|
||||
|
||||
def rotateFurniture()
|
||||
$game_player.direction_fix = false
|
||||
$game_player.turn_right_90
|
||||
$game_player.direction_fix = true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
def getEnteredSecretBase
|
||||
controller = $PokemonTemp.enteredSecretBaseController
|
||||
return controller.secretBase if controller
|
||||
end
|
||||
|
||||
def getSecretBaseController
|
||||
return $PokemonTemp.enteredSecretBaseController
|
||||
end
|
||||
|
||||
def secretBaseItem(event_id)
|
||||
return if $game_temp.moving_furniture
|
||||
begin
|
||||
event = $game_map.events[event_id]
|
||||
pos=[event.x,event.y]
|
||||
controller=getSecretBaseController
|
||||
controller.callBehaviorPosition(pos)
|
||||
end
|
||||
end
|
||||
|
||||
def secretBaseItemMenu
|
||||
return unless Input.trigger?(Input::C)
|
||||
event = $game_player.pbFacingEvent
|
||||
return unless event
|
||||
event_position = [event.x, event.y]
|
||||
controller = getSecretBaseController
|
||||
controller.furnitureInteract(event_position)
|
||||
end
|
||||
|
||||
|
||||
def selectPlacedSecretBaseItemInstance()
|
||||
options = []
|
||||
$Trainer.secretBase.layout.items.each do |item_instance|
|
||||
item_id = item_instance.itemId
|
||||
item_name = SecretBasesData::SECRET_BASE_ITEMS[item_id].real_name
|
||||
options << item_name
|
||||
end
|
||||
options << _INTL("Cancel")
|
||||
chosen = optionsMenu(options)
|
||||
$Trainer.secretBase.layout.items.each do |item_instance|
|
||||
item_id = item_instance.itemId
|
||||
item_name = SecretBasesData::SECRET_BASE_ITEMS[item_id].real_name
|
||||
return item_instance if item_name == options[chosen]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def selectAnySecretBaseItem()
|
||||
options = []
|
||||
$Trainer.owned_decorations = [] if $Trainer.owned_decorations.nil?
|
||||
$Trainer.owned_decorations.each do |item_id|
|
||||
item_name = SecretBasesData::SECRET_BASE_ITEMS[item_id].real_name
|
||||
options << item_name
|
||||
end
|
||||
options << _INTL("Cancel")
|
||||
chosen = optionsMenu(options)
|
||||
$Trainer.owned_decorations.each do |item_id|
|
||||
item_name = SecretBasesData::SECRET_BASE_ITEMS[item_id].real_name
|
||||
return item_id if item_name == options[chosen]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
252
Data/Scripts/053_PIF_Hoenn/SecretBases/Utils/SecretBaseUtils.rb
Normal file
252
Data/Scripts/053_PIF_Hoenn/SecretBases/Utils/SecretBaseUtils.rb
Normal file
@@ -0,0 +1,252 @@
|
||||
def getSecretBaseBiome(terrainTag)
|
||||
return :TREE if terrainTag.secretBase_tree
|
||||
return :CAVE if terrainTag.secretBase_cave
|
||||
return :BUSH if terrainTag.secretBase_bush
|
||||
# todo: other types
|
||||
return nil
|
||||
end
|
||||
|
||||
def pickSecretBaseLayout(baseType)
|
||||
mapId = MAP_SECRET_BASES
|
||||
# Distance is how far away the same coordinates will share the same seed
|
||||
case baseType
|
||||
when :TREE
|
||||
distance = 2
|
||||
else
|
||||
distance = 4
|
||||
end
|
||||
# Snap to 2x2 blocks
|
||||
block_x = $game_player.x / distance
|
||||
block_y = $game_player.y / distance
|
||||
|
||||
# Universal deterministic seed
|
||||
seed_str = "#{baseType}-#{mapId}-#{block_x}-#{block_y}"
|
||||
seed = Zlib.crc32(seed_str)
|
||||
|
||||
rng = Random.new(seed)
|
||||
layoutType = weighted_sample(SecretBasesData::SECRET_BASE_ENTRANCES, rng)
|
||||
return layoutType
|
||||
end
|
||||
|
||||
def weighted_sample(entries, rng)
|
||||
total = entries.values.sum { |v| v[:rareness] }
|
||||
pick = rng.rand * total
|
||||
entries.each do |key, v|
|
||||
return key if (pick -= v[:rareness]) <= 0
|
||||
end
|
||||
# Fallback: return the last key
|
||||
return entries.keys.last
|
||||
end
|
||||
|
||||
|
||||
def pbSecretBase(biome_type, base_layout_type)
|
||||
base_map_id = MAP_SECRET_BASES
|
||||
player_map_id = $game_map.map_id
|
||||
player_position = [$game_player.x, $game_player.y]
|
||||
|
||||
if secretBaseExistsAtPosition(player_map_id, player_position)
|
||||
enterSecretBase
|
||||
else
|
||||
# Todo: Determine the secret base's map ids and coordinates from a seed using the current map and the base type instead of passing it manually.
|
||||
createSecretBaseHere(biome_type, base_map_id, base_layout_type)
|
||||
end
|
||||
end
|
||||
|
||||
def secretBaseExistsAtPosition(map_id, position)
|
||||
return false unless $Trainer.secretBase
|
||||
current_outdoor_id = $Trainer.secretBase.outside_map_id
|
||||
current_outdoor_coordinates = $Trainer.secretBase.outside_entrance_position
|
||||
return current_outdoor_id == map_id && current_outdoor_coordinates == position
|
||||
end
|
||||
|
||||
def createSecretBaseHere(biomeType, secretBaseMap = 0, baseLayoutType = :TYPE_1)
|
||||
if pbConfirmMessage(_INTL("Do you want to create a new secret base here?"))
|
||||
if $Trainer.secretBase
|
||||
unless pbConfirmMessage(_INTL("This will overwrite your current secret base. Do you still wish to continue?"))
|
||||
return
|
||||
end
|
||||
end
|
||||
current_map_id = $game_map.map_id
|
||||
current_position = [$game_player.x, $game_player.y]
|
||||
$Trainer.secretBase = initialize_player_secret_base(biomeType, current_map_id, current_position, secretBaseMap, baseLayoutType)
|
||||
setupAllSecretBaseEntrances
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_player_secret_base(biome_type, outside_map_id, outside_position, base_map_id, layout_shape)
|
||||
return SecretBase.new(
|
||||
biome: biome_type,
|
||||
outside_map_id: outside_map_id,
|
||||
outside_entrance_position: outside_position,
|
||||
inside_map_id: base_map_id,
|
||||
base_layout_type: layout_shape,
|
||||
is_visitor: false
|
||||
)
|
||||
end
|
||||
|
||||
#For when called from Scene_Map
|
||||
def placeFurnitureMenu
|
||||
controller = getSecretBaseController
|
||||
controller.placeFurnitureMenu
|
||||
end
|
||||
|
||||
|
||||
def rotate_held_furniture_right
|
||||
return unless $game_temp.moving_furniture
|
||||
pbSEPlay("GUI party switch", 80, 100)
|
||||
directionFix = $game_player.direction_fix
|
||||
$game_player.direction_fix = false
|
||||
$game_player.turn_right_90
|
||||
$game_player.direction_fix=directionFix
|
||||
end
|
||||
def rotate__held_furniture_left
|
||||
return unless $game_temp.moving_furniture
|
||||
pbSEPlay("GUI party switch", 80, 100)
|
||||
directionFix = $game_player.direction_fix
|
||||
$game_player.direction_fix = false
|
||||
$game_player.turn_left_90
|
||||
$game_player.direction_fix=directionFix
|
||||
end
|
||||
|
||||
|
||||
def exitSecretBase()
|
||||
controller = getSecretBaseController
|
||||
return if controller&.isMovingFurniture?
|
||||
pbStartOver if !$Trainer.secretBase || !$Trainer.secretBase.outside_map_id || !$Trainer.secretBase.outside_entrance_position
|
||||
# Should never happen, but just in case
|
||||
enteredSecretBase = getEnteredSecretBase
|
||||
if enteredSecretBase && enteredSecretBase.is_a?(SecretBase)
|
||||
outdoor_id = enteredSecretBase.outside_map_id
|
||||
outdoor_coordinates = enteredSecretBase.outside_entrance_position
|
||||
else
|
||||
#Fallback on player's base
|
||||
outdoor_id = $Trainer.secretBase.outside_map_id
|
||||
outdoor_coordinates = $Trainer.secretBase.outside_entrance_position
|
||||
end
|
||||
|
||||
|
||||
$PokemonTemp.pbClearTempEvents
|
||||
pbFadeOutIn {
|
||||
$game_temp.player_new_map_id = outdoor_id
|
||||
$game_temp.player_new_x = outdoor_coordinates[0]
|
||||
$game_temp.player_new_y = outdoor_coordinates[1]
|
||||
$scene.transfer_player(true)
|
||||
$game_map.autoplay
|
||||
$game_map.refresh
|
||||
}
|
||||
$PokemonTemp.pbClearTempEvents
|
||||
$PokemonTemp.enteredSecretBaseController=nil
|
||||
setupAllSecretBaseEntrances
|
||||
end
|
||||
|
||||
def enterSecretBase()
|
||||
event = $game_map.events[@event_id]
|
||||
return if event.nil?
|
||||
if event.variable && event.variable.is_a?(SecretBase)
|
||||
secretBase = event.variable
|
||||
else
|
||||
secretBase= $Trainer.secretBase
|
||||
end
|
||||
controller = SecretBaseController.new(secretBase)
|
||||
$PokemonTemp.enteredSecretBaseController = controller
|
||||
|
||||
return unless secretBase.is_a?(SecretBase)
|
||||
$PokemonTemp.pbClearTempEvents
|
||||
pbFadeOutIn {
|
||||
$game_temp.player_new_map_id = MAP_SECRET_BASES
|
||||
$game_temp.player_new_x = secretBase.inside_entrance_position[0]
|
||||
$game_temp.player_new_y = secretBase.inside_entrance_position[1]
|
||||
$scene.transfer_player(true)
|
||||
$game_map.autoplay
|
||||
SecretBaseLoader.new.loadSecretBaseFurniture(secretBase)
|
||||
$game_map.refresh
|
||||
}
|
||||
|
||||
end
|
||||
def obtain_all_decorations
|
||||
$Trainer.owned_decorations = [] unless $Trainer.owned_decorations
|
||||
SecretBasesData::SECRET_BASE_ITEMS.keys.each do |item_id|
|
||||
obtain_decoration_silent(item_id)
|
||||
end
|
||||
end
|
||||
def obtain_decoration(item_id)
|
||||
$Trainer.owned_decorations = [] unless $Trainer.owned_decorations
|
||||
if SecretBasesData::SECRET_BASE_ITEMS[item_id]
|
||||
obtainDecorationMessage(item_id)
|
||||
$Trainer.owned_decorations << item_id
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def obtain_decoration_silent(item_id)
|
||||
$Trainer.owned_decorations = [] unless $Trainer.owned_decorations
|
||||
if SecretBasesData::SECRET_BASE_ITEMS[item_id]
|
||||
$Trainer.owned_decorations << item_id
|
||||
end
|
||||
end
|
||||
|
||||
def give_starting_decorations
|
||||
furniture = [
|
||||
:PLANT,:RED_CHAIR
|
||||
]
|
||||
obtain_decoration_silent(:PC)
|
||||
furniture.each do |item|
|
||||
obtain_decoration(item)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def obtainDecorationMessage(item_id)
|
||||
decoration = SecretBasesData::SECRET_BASE_ITEMS[item_id]
|
||||
pictureViewport = showDecorationPicture(item_id)
|
||||
musical_effect = "Key item get"
|
||||
pbMessage(_INTL("\\me[{1}]You obtained a \\c[1]{2}\\c[0]!", musical_effect, decoration.real_name))
|
||||
pictureViewport.dispose if pictureViewport
|
||||
end
|
||||
|
||||
def showDecorationPicture(item_id)
|
||||
begin
|
||||
decoration = SecretBasesData::SECRET_BASE_ITEMS[item_id]
|
||||
path = "Graphics/Characters/player/secretBases/#{decoration.graphics}"
|
||||
|
||||
viewport = Viewport.new(Graphics.width / 4, 0, Graphics.width / 2, Graphics.height)
|
||||
bg_sprite = Sprite.new(viewport)
|
||||
decoration_sprite = Sprite.new(viewport)
|
||||
|
||||
echoln path
|
||||
echoln pbResolveBitmap(path)
|
||||
|
||||
if pbResolveBitmap(path)
|
||||
sheet = Bitmap.new(path)
|
||||
|
||||
# Character sheets are 4x4
|
||||
frame_width = sheet.width / 4
|
||||
frame_height = sheet.height / 4
|
||||
|
||||
# First frame = top-left corner (row 0, col 0)
|
||||
rect = Rect.new(0, 0, frame_width, frame_height)
|
||||
|
||||
# Copy that frame into its own bitmap
|
||||
cropped = Bitmap.new(frame_width, frame_height)
|
||||
cropped.blt(0, 0, sheet, rect)
|
||||
|
||||
decoration_sprite.bitmap = cropped
|
||||
end
|
||||
|
||||
bg_bitmap = AnimatedBitmap.new("Graphics/Pictures/Outfits/obtain_bg")
|
||||
bg_sprite.bitmap = bg_bitmap.bitmap
|
||||
|
||||
decoration_sprite.x = 92
|
||||
decoration_sprite.y = 50
|
||||
decoration_sprite.zoom_x = 2
|
||||
decoration_sprite.zoom_y = 2
|
||||
|
||||
bg_sprite.x = 0
|
||||
viewport.z = 99999
|
||||
|
||||
return viewport
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
TEMPLATE_EVENT_SECRET_BASE_ENTRANCE_TREE = 4
|
||||
TEMPLATE_EVENT_SECRET_BASE_ENTRANCE_CAVE = 5
|
||||
TEMPLATE_EVENT_SECRET_BASE_ENTRANCE_BUSH = 6
|
||||
|
||||
module SecretBasesData
|
||||
# rareness: The higher, the more common
|
||||
SECRET_BASE_ENTRANCES = {
|
||||
:TYPE_SMALL_1 => { position: [8, 12], rareness: 1 },
|
||||
:TYPE_SMALL_2 => { position: [35, 12], rareness: 1 },
|
||||
:TYPE_SMALL_3 => { position: [55, 12], rareness: 1 },
|
||||
:TYPE_SMALL_4 => { position: [75, 12], rareness: 1 },
|
||||
:TYPE_SMALL_5 => { position: [101, 12], rareness: 1 },
|
||||
:TYPE_SMALL_6 => { position: [124, 12], rareness: 1 },
|
||||
:TYPE_SMALL_7 => { position: [12, 122], rareness: 1 },
|
||||
:TYPE_SMALL_8 => { position: [36, 121], rareness: 1 },
|
||||
:TYPE_SMALL_9 => { position: [65, 121], rareness: 1 },
|
||||
:TYPE_SMALL_10 => { position: [94, 123], rareness: 1 },
|
||||
:TYPE_SMALL_11 => { position: [121, 124], rareness: 1 },
|
||||
|
||||
:TYPE_WIDE_1 => { position: [11, 34], rareness: 0.3 },
|
||||
:TYPE_WIDE_2 => { position: [43, 34], rareness: 0.2 },
|
||||
:TYPE_WIDE_3 => { position: [72, 34], rareness: 0.2 },
|
||||
:TYPE_WIDE_4 => { position: [106, 34], rareness: 0.2 },
|
||||
|
||||
:TYPE_TALL_1 => { position: [7, 71], rareness: 0.3 },
|
||||
:TYPE_TALL_2 => { position: [31, 71], rareness: 0.2 },
|
||||
:TYPE_TALL_3 => { position: [53, 71], rareness: 0.2 },
|
||||
:TYPE_TALL_4 => { position: [85, 71], rareness: 0.2 },
|
||||
:TYPE_TALL_5 => { position: [109, 71], rareness: 0.2 },
|
||||
|
||||
:TYPE_SPECIAL_1 => { position: [11, 98], rareness: 0.05 },
|
||||
:TYPE_SPECIAL_2 => { position: [40, 97], rareness: 0.05 },
|
||||
:TYPE_SPECIAL_3 => { position: [68, 98], rareness: 0.05 },
|
||||
:TYPE_SPECIAL_4 => { position: [92, 99], rareness: 0.05 },
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
225
Data/Scripts/053_PIF_Hoenn/SecretBases/data/SecretBaseItems.rb
Normal file
225
Data/Scripts/053_PIF_Hoenn/SecretBases/data/SecretBaseItems.rb
Normal file
@@ -0,0 +1,225 @@
|
||||
# frozen_string_literal: true
|
||||
module SecretBasesData
|
||||
|
||||
SECRET_BASE_ITEMS = {}
|
||||
|
||||
def SecretBasesData::register_base_item(id, **kwargs)
|
||||
SECRET_BASE_ITEMS[id] = SecretBaseItem.new(id: id, **kwargs)
|
||||
end
|
||||
|
||||
register_base_item(
|
||||
:PC,
|
||||
graphics: "Furniture/pc.png",
|
||||
real_name: "PC",
|
||||
deletable: false,
|
||||
price: 0,
|
||||
behavior: ->(event = nil) {
|
||||
#Behavior for PC is handled in SecretBasesController
|
||||
#useSecretBasePC
|
||||
}
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:MANNEQUIN,
|
||||
graphics: "Furniture/mannequin.png",
|
||||
real_name: _INTL("Mannequin"),
|
||||
price: 500,
|
||||
behavior: ->(event = nil) {
|
||||
useSecretBaseMannequin
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:PLANT,
|
||||
graphics: "Furniture/plant.png",
|
||||
real_name: _INTL("Decorative Plant"),
|
||||
price: 500
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:RED_CHAIR,
|
||||
graphics: "Furniture/red_chair.png",
|
||||
real_name: _INTL("Red Chair"),
|
||||
price: 350,
|
||||
trigger: TRIGGER_PLAYER_TOUCH,
|
||||
behavior: ->(itemInstance = nil) {
|
||||
sit_on_chair(itemInstance)
|
||||
}
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:FANCY_CARPET,
|
||||
graphics: "Carpets/fancy_carpet.png",
|
||||
real_name: _INTL("Fancy Carpet"),
|
||||
price: 5000,
|
||||
pass_through: true,
|
||||
under_player: true
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:FANCY_CARPET_CONNECT,
|
||||
graphics: "Carpets/fancy_carpet_connect.png",
|
||||
real_name: _INTL("Fancy Carpet (Connection)"),
|
||||
price: 100,
|
||||
pass_through: true,
|
||||
under_player: true
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:BOULDER,
|
||||
graphics: "Furniture/boulder.png",
|
||||
real_name: _INTL("Boulder"),
|
||||
price: 600,
|
||||
under_player: false,
|
||||
behavior: ->(itemInstance = nil) {
|
||||
pbStrength
|
||||
if $PokemonMap.strengthUsed
|
||||
pushEvent(itemInstance)
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
#Skitty set
|
||||
|
||||
register_base_item(
|
||||
:SKITTY_CHAIR_3x3,
|
||||
graphics: "skittySet/deco_3x3chair_skitty.png",
|
||||
real_name: _INTL("Skitty Armchair"),
|
||||
price: 1000,
|
||||
height: 1,
|
||||
width: 3,
|
||||
trigger: TRIGGER_PLAYER_TOUCH,
|
||||
behavior: ->(itemInstance = nil) {
|
||||
sit_on_chair(itemInstance)
|
||||
},
|
||||
uninteractable_positions: [[-1,0],[1,0]]
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:SKITTY_CHAIR_3x3,
|
||||
graphics: "skittySet/deco_3x3chair_skitty.png",
|
||||
real_name: _INTL("Skitty Armchair"),
|
||||
price: 1000,
|
||||
height: 1,
|
||||
width: 3,
|
||||
trigger: TRIGGER_PLAYER_TOUCH,
|
||||
behavior: ->(itemInstance = nil) {
|
||||
sit_on_chair(itemInstance)
|
||||
},
|
||||
uninteractable_positions: [[-1,0],[1,0]]
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:SKITTY_COUCH_3x4,
|
||||
graphics: "skittySet/deco_3x4chair_skitty.png",
|
||||
real_name: _INTL("Skitty Couch"),
|
||||
price: 2000,
|
||||
height: 1,
|
||||
width: 4,
|
||||
trigger: TRIGGER_PLAYER_TOUCH,
|
||||
behavior: ->(itemInstance = nil) {
|
||||
sit_on_chair(itemInstance)
|
||||
},
|
||||
uninteractable_positions: [[-2,0],[2,0]]
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:SKITTY_COUCH_3x5,
|
||||
graphics: "skittySet/deco_3x5couch_skitty.png",
|
||||
real_name: _INTL("Wide Skitty Couch"),
|
||||
price: 2000,
|
||||
height: 1,
|
||||
width: 5,
|
||||
trigger: TRIGGER_PLAYER_TOUCH,
|
||||
behavior: ->(itemInstance = nil) {
|
||||
sit_on_chair(itemInstance)
|
||||
},
|
||||
uninteractable_positions: [[-2,0],[2,0]]
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:SKITTY_RUG_3x3,
|
||||
graphics: "skittySet/deco_3x3rug_skitty.png",
|
||||
real_name: _INTL("Large Skitty Rug"),
|
||||
price: 3000,
|
||||
pass_through: true,
|
||||
under_player: true
|
||||
)
|
||||
|
||||
#Rock set
|
||||
register_base_item(
|
||||
:ROCK_CHAIR_1x1,
|
||||
graphics: "rockSet/deco_1x1chair_rock.png",
|
||||
real_name: _INTL("Rocky Stool"),
|
||||
price: 350,
|
||||
trigger: TRIGGER_PLAYER_TOUCH,
|
||||
behavior: ->(itemInstance = nil) {
|
||||
sit_on_chair(itemInstance)
|
||||
}
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:ROCK,
|
||||
graphics: "rockSet/deco_1x1deco_rock.png",
|
||||
real_name: _INTL("Rock"),
|
||||
price: 50
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:ROCK_STATUE,
|
||||
graphics: "rockSet/deco_1x1statue_rock.png",
|
||||
real_name: _INTL("Rocky Statue"),
|
||||
price: 50
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:ROCK_WALL,
|
||||
graphics: "rockSet/deco_1x2wall_rock.png",
|
||||
real_name: _INTL("Rocky Wall"),
|
||||
price: 50
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:ROCK_TABLE_2x3,
|
||||
graphics: "rockSet/deco_2x3table_rock.png",
|
||||
real_name: _INTL("Large Rocky Table"),
|
||||
width:3,
|
||||
height:2,
|
||||
price: 5000
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:ROCK_CHAIR_3x3,
|
||||
graphics: "rockSet/deco_3x3chair_rock.png",
|
||||
real_name: _INTL("Rocky Armchair"),
|
||||
price: 1000,
|
||||
height: 1,
|
||||
width: 3,
|
||||
trigger: TRIGGER_PLAYER_TOUCH,
|
||||
behavior: ->(itemInstance = nil) {
|
||||
sit_on_chair(itemInstance)
|
||||
},
|
||||
uninteractable_positions: [[-1,0],[1,0]]
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:ROCK_RUG_1x1,
|
||||
graphics: "rockSet/deco_1x1rug_rock.png",
|
||||
real_name: _INTL("Small Rocky Rug"),
|
||||
price: 500,
|
||||
pass_through: true,
|
||||
under_player: true
|
||||
)
|
||||
|
||||
register_base_item(
|
||||
:ROCK_RUG_3x3,
|
||||
graphics: "rockSet/deco_3x3rug_rock.png",
|
||||
real_name: _INTL("Large Rocky Rug"),
|
||||
price: 2000,
|
||||
pass_through: true,
|
||||
under_player: true
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
class Game_Map
|
||||
SECRET_BASE_TILESET_OVERRIDES = {
|
||||
:BUSH => 100,
|
||||
:CAVE => 101,
|
||||
:TREE => 102,
|
||||
}
|
||||
|
||||
alias __tileset_swap_updateTileset updateTileset
|
||||
|
||||
def updateTileset
|
||||
if @map_id == MAP_SECRET_BASES && $Trainer.secretBase
|
||||
override = SECRET_BASE_TILESET_OVERRIDES[$Trainer.secretBase.biome_type]
|
||||
@map.tileset_id = override if override
|
||||
end
|
||||
__tileset_swap_updateTileset
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
# For more complex item behaviors - to keep things organized
|
||||
|
||||
def useSecretBaseMannequin
|
||||
# Todo: This is the item that players can use to "place themselves" in the base.
|
||||
# When a base has a mannequin, the base will be shared online and
|
||||
# the mannequin will appear as the player in other people's games.
|
||||
|
||||
|
||||
secretBase = getEnteredSecretBase
|
||||
if secretBase && secretBase.is_visitor
|
||||
interact_other_player(secretBase)
|
||||
else
|
||||
secret_base_mannequin_menu(secretBase)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
def secret_base_mannequin_menu(secretBase)
|
||||
friend_bases = SecretBaseLoader.new.list_friend_bases
|
||||
|
||||
cmd_share = _INTL("Share your secret base")
|
||||
cmd_import = _INTL("Import a friend's secret base")
|
||||
cmd_removeFriend = _INTL("Remove a friend's secret base")
|
||||
cmd_setTeam = _INTL("Set your base's Team")
|
||||
cmd_trainerID = _INTL("[DEBUG] Copy your Trainer ID")
|
||||
cmd_export = _INTL("[DEBUG] Export base to clipboard")
|
||||
cmd_cancel = _INTL("Cancel")
|
||||
commands = [cmd_share, cmd_setTeam, cmd_import]
|
||||
commands << cmd_removeFriend if !friend_bases.empty?
|
||||
commands << cmd_trainerID if $DEBUG
|
||||
commands << cmd_export if $DEBUG
|
||||
commands << cmd_cancel
|
||||
pbMessage(_INTL("What would you like to do?"))
|
||||
choice = optionsMenu(commands)
|
||||
case commands[choice]
|
||||
when cmd_share
|
||||
pbMessage(_INTL("Once you share you base, it may randomly appear in other player's games."))
|
||||
pbMessage(_INTL("The other players will be able to see your character's name, your base's layout, your custom message, and battle your team."))
|
||||
continue = pbConfirmMessage(_INTL("You can only share your secret base once per day. Would you like to continue and publish your current secret base? (Your game will save automatically afterwards)"))
|
||||
if continue
|
||||
begin
|
||||
exporter = SecretBaseExporter.new
|
||||
json = exporter.export_secret_base(secretBase)
|
||||
|
||||
publisher = SecretBasePublisher.new
|
||||
publisher.register unless $Trainer.secretBase_uuid
|
||||
publisher.upload_base(json)
|
||||
pbSEPlay('GUI save choice')
|
||||
pbMessage(_INTL("Your secret base was shared successfully!"))
|
||||
rescue Exception => e
|
||||
echoln e
|
||||
pbMessage(_INTL("There was a problem uploading your Secret Base. The operation was cancelled."))
|
||||
end
|
||||
|
||||
end
|
||||
when cmd_import
|
||||
friend_code = input_friend_code
|
||||
if friend_code
|
||||
fetcher = SecretBaseFetcher.new
|
||||
begin
|
||||
fetcher.import_friend_base(friend_code)
|
||||
loader = SecretBaseLoader.new
|
||||
loader.load_visitor_bases
|
||||
pbMessage(_INTL("Your friend's base was imported!"))
|
||||
rescue
|
||||
pbMessage(_INTL("There was a problem, your friend's secret base was not imported."))
|
||||
end
|
||||
end
|
||||
when cmd_setTeam
|
||||
when cmd_trainerID
|
||||
Input.clipboard = $Trainer.id.to_s
|
||||
pbMessage(_INTL("Your Trainer ID was copied to the clipboard!"))
|
||||
when cmd_export
|
||||
exporter = SecretBaseExporter.new
|
||||
json = exporter.export_secret_base($Trainer.secretBase)
|
||||
Input.clipboard = json
|
||||
end
|
||||
end
|
||||
|
||||
def check_copied_own_trainerId(clipboard_text)
|
||||
if clipboard_text == $Trainer.id.to_s
|
||||
pbMessage(_INTL("The trainer ID you copied is your own! You need to have your friend copy theirs and send it to you."))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def input_friend_code()
|
||||
example = showPicture("Graphics/Pictures/Trainer Card/trainerID_example",0,0,0)
|
||||
|
||||
cmd_refresh = _INTL("Refresh")
|
||||
cmd_confirm = _INTL("Confirm")
|
||||
cmd_manual = _INTL("Enter manually")
|
||||
cmd_cancel = _INTL("Cancel")
|
||||
loop do
|
||||
commands = []
|
||||
clipboard_text = Input.clipboard || ""
|
||||
clipboard_text = clipboard_text.slice(0, 10)
|
||||
|
||||
if numeric_string?(clipboard_text) && clipboard_text.length == 10 && !check_copied_own_trainerId(clipboard_text)
|
||||
message = _INTL("Is this your friend's Trainer ID? \\C[1]#{clipboard_text}\\C[0]")
|
||||
commands << cmd_refresh
|
||||
commands << cmd_confirm
|
||||
commands << cmd_manual
|
||||
commands << cmd_cancel
|
||||
else
|
||||
message = _INTL("Copy your friend's ID and choose 'Refresh'. The game will detect it from your clipboard.")
|
||||
commands << cmd_refresh
|
||||
commands << cmd_manual
|
||||
commands << cmd_cancel
|
||||
end
|
||||
|
||||
choice = pbMessage(message, commands,commands.length)
|
||||
case commands[choice]
|
||||
when cmd_refresh
|
||||
next
|
||||
when cmd_confirm
|
||||
example.dispose
|
||||
return clipboard_text
|
||||
when cmd_manual
|
||||
friend_trainer_id = pbEnterText("Friend's Trainer ID", 10, 10, clipboard_text)
|
||||
unless numeric_string?(friend_trainer_id)
|
||||
pbMessage(_INTL("The Trainer ID you entered is not valid. Trainer IDs are composed of 10 numbers."))
|
||||
end
|
||||
Input.clipboard = friend_trainer_id
|
||||
else
|
||||
example.dispose
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def interact_other_player(secretBase)
|
||||
event = pbMapInterpreter.get_character(0)
|
||||
event.direction_fix = false
|
||||
event.turn_toward_player
|
||||
message = secretBase.base_message
|
||||
pbCallBub(3)
|
||||
pbMessage(_INTL("Hey, I'm \\C[1]{1}\\C[0], welcome to my secret base!",secretBase.trainer_name))
|
||||
if message
|
||||
pbCallBub(3)
|
||||
pbMessage(message)
|
||||
end
|
||||
end
|
||||
|
||||
def pushEvent(itemInstance)
|
||||
event = itemInstance.getMainEvent
|
||||
old_x = event.x
|
||||
old_y = event.y
|
||||
return if !event.can_move_in_direction?($game_player.direction, false)
|
||||
case $game_player.direction
|
||||
when 2 then event.move_down
|
||||
when 4 then event.move_left
|
||||
when 6 then event.move_right
|
||||
when 8 then event.move_up
|
||||
end
|
||||
|
||||
if old_x != event.x || old_y != event.y
|
||||
$game_player.lock
|
||||
loop do
|
||||
Graphics.update
|
||||
Input.update
|
||||
pbUpdateSceneMap
|
||||
break if !event.moving?
|
||||
end
|
||||
itemInstance.position = [event.x, event.y]
|
||||
$PokemonTemp.pbClearTempEvents
|
||||
$PokemonTemp.enteredSecretBaseController.reloadItems
|
||||
|
||||
$game_player.unlock
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def sit_on_chair(itemInstance)
|
||||
event=itemInstance.getMainEvent
|
||||
pbSEPlay("jump", 80, 100)
|
||||
$game_player.always_on_top=true
|
||||
$game_player.through =true
|
||||
$game_player.jump_forward
|
||||
case event.direction
|
||||
when DIRECTION_LEFT; $game_player.direction = DIRECTION_RIGHT
|
||||
when DIRECTION_RIGHT; $game_player.direction = DIRECTION_LEFT
|
||||
when DIRECTION_UP; $game_player.direction = DIRECTION_UP
|
||||
when DIRECTION_DOWN; $game_player.direction = DIRECTION_DOWN
|
||||
end
|
||||
$game_player.through =false
|
||||
loop do
|
||||
Graphics.update
|
||||
Input.update
|
||||
pbUpdateSceneMap
|
||||
if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN) || Input.trigger?(Input::LEFT) || Input.trigger?(Input::RIGHT)
|
||||
$game_player.jump_forward
|
||||
$game_player.always_on_top=false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
# PC behavior set directly in SecretBaseController
|
||||
@@ -0,0 +1,73 @@
|
||||
# Loads other player secret bases from a local file and places them into the world
|
||||
|
||||
# todo: only load max 5
|
||||
class SecretBaseLoader
|
||||
def initialize
|
||||
@importer = SecretBaseImporter.new
|
||||
end
|
||||
|
||||
def load_visitor_bases
|
||||
visitor_bases = @importer.load_bases(SecretBaseImporter::VISITOR_BASES_FILE)
|
||||
friend_bases = @importer.load_bases(SecretBaseImporter::FRIEND_BASES_FILE)
|
||||
all_bases = visitor_bases + friend_bases
|
||||
$game_temp.visitor_secret_bases = all_bases
|
||||
end
|
||||
|
||||
def loadSecretBaseFurniture(secretBase)
|
||||
return unless $scene.is_a?(Scene_Map)
|
||||
secretBase.load_furniture
|
||||
end
|
||||
|
||||
def list_friend_bases
|
||||
return @importer.load_bases(SecretBaseImporter::FRIEND_BASES_FILE)
|
||||
end
|
||||
|
||||
def list_visitor_bases
|
||||
return @importer.load_bases(SecretBaseImporter::FRIEND_BASES_FILE)
|
||||
end
|
||||
end
|
||||
|
||||
class Game_Temp
|
||||
attr_accessor :visitor_secret_bases
|
||||
end
|
||||
|
||||
|
||||
def setupAllSecretBaseEntrances
|
||||
$PokemonTemp.pbClearTempEvents
|
||||
|
||||
if $Trainer && $Trainer.secretBase && $game_map.map_id == $Trainer.secretBase.outside_map_id
|
||||
setupSecretBaseEntranceEvent($Trainer.secretBase)
|
||||
end
|
||||
|
||||
if $game_temp.visitor_secret_bases && !$game_temp.visitor_secret_bases.empty?
|
||||
$game_temp.visitor_secret_bases.each do |base|
|
||||
if $game_map.map_id == base.outside_map_id
|
||||
setupSecretBaseEntranceEvent(base)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# Called on map load
|
||||
def setupSecretBaseEntranceEvent(secretBase)
|
||||
warpPosition = secretBase.outside_entrance_position
|
||||
entrancePosition = [warpPosition[0], warpPosition[1] - 1]
|
||||
case secretBase.biome_type
|
||||
when :TREE
|
||||
template_event_id = TEMPLATE_EVENT_SECRET_BASE_ENTRANCE_TREE
|
||||
when :CAVE
|
||||
template_event_id = TEMPLATE_EVENT_SECRET_BASE_ENTRANCE_CAVE
|
||||
when :BUSH
|
||||
template_event_id = TEMPLATE_EVENT_SECRET_BASE_ENTRANCE_BUSH
|
||||
else
|
||||
template_event_id = TEMPLATE_EVENT_SECRET_BASE_ENTRANCE_CAVE
|
||||
end
|
||||
event = $PokemonTemp.createTempEvent(template_event_id, $game_map.map_id, entrancePosition)
|
||||
event.setVariable(secretBase)
|
||||
event.refresh
|
||||
|
||||
end
|
||||
|
||||
Events.onMapSceneChange += proc { |_sender, e|
|
||||
next unless $PokemonTemp.tempEvents.empty?
|
||||
setupAllSecretBaseEntrances
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
#todo: limit of 10 at once
|
||||
|
||||
#todo: append new friends at the end of the list instead of overwriting everything
|
||||
|
||||
#todo: if the friend's id is already in there, update (overwrite) it
|
||||
#
|
||||
class SecretBaseFetcher
|
||||
SECRETBASE_DOWNLOAD_URL = "https://secretbase-download.pkmninfinitefusion.workers.dev"
|
||||
|
||||
def import_friend_base(friend_player_id)
|
||||
base_json = fetch_base(friend_player_id)
|
||||
if base_json
|
||||
save_friend_base(base_json)
|
||||
else
|
||||
pbMessage(_INTL("The game couldn't find your friend's base. Make sure that they published it and that you wrote their trainer ID correctly."))
|
||||
raise "Secret Base does not exist"
|
||||
end
|
||||
end
|
||||
|
||||
# Fetch a secret base by playerID
|
||||
def fetch_base(player_id)
|
||||
url = "#{SECRETBASE_DOWNLOAD_URL}/get-base?playerID=#{player_id}"
|
||||
|
||||
begin
|
||||
response = HTTPLite.get(url)
|
||||
if response[:status] == 200
|
||||
echoln "[SecretBase] Downloaded base for #{player_id}"
|
||||
base_json = JSON.parse(response[:body])
|
||||
return base_json
|
||||
else
|
||||
echoln "[SecretBase] Failed with status #{response[:status]} for #{player_id}"
|
||||
return nil
|
||||
end
|
||||
rescue MKXPError => e
|
||||
echoln "[SecretBase] MKXPError: #{e.message}"
|
||||
return nil
|
||||
rescue Exception => e
|
||||
echoln "[SecretBase] Error: #{e.message}"
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def save_friend_base(new_base)
|
||||
exporter = SecretBaseExporter.new
|
||||
exporter.write_base_json_to_file(new_base,SecretBaseImporter::FRIEND_BASES_FILE,true)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
class SecretBasePublisher
|
||||
def initialize()
|
||||
@player_id = $Trainer.id
|
||||
end
|
||||
|
||||
def register
|
||||
if $Trainer.secretBase_uuid
|
||||
echoln "Already registered!"
|
||||
else
|
||||
begin
|
||||
payload = { playerID: @player_id }
|
||||
url = "#{Settings::SECRETBASE_UPLOAD_URL}/register"
|
||||
response = pbPostToString(url,payload)
|
||||
echoln response
|
||||
json = JSON.parse(response) rescue {}
|
||||
secret_uuid = json[:secretUUID]
|
||||
echoln json
|
||||
$Trainer.secretBase_uuid = secret_uuid
|
||||
echoln $Trainer.secretBase_uuid
|
||||
Game.save
|
||||
rescue Exception => e
|
||||
echoln e
|
||||
end
|
||||
end
|
||||
|
||||
return $Trainer.secretBase_uuid
|
||||
|
||||
end
|
||||
|
||||
#Trainer needs to be registered before this is called
|
||||
def upload_base(base_json)
|
||||
secret_uuid = $Trainer.secretBase_uuid
|
||||
echoln secret_uuid
|
||||
unless $Trainer.secretBase_uuid
|
||||
echoln "Trainer not registered!"
|
||||
pbMessage(_INTL("The base could not be uploaded"))
|
||||
end
|
||||
|
||||
payload = {
|
||||
playerID: @player_id,
|
||||
secretUUID: secret_uuid,
|
||||
baseJSON: base_json
|
||||
}
|
||||
url = "#{Settings::SECRETBASE_UPLOAD_URL}/upload-base"
|
||||
response = pbPostToString(url,payload)
|
||||
echoln response
|
||||
|
||||
json = JSON.parse(response) rescue {}
|
||||
json["success"] == true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
end
|
||||
@@ -0,0 +1,133 @@
|
||||
class SecretBaseExporter
|
||||
# Export a secret base as JSON
|
||||
def export_secret_base(secretBase)
|
||||
base_data = {
|
||||
base: {
|
||||
biome: secretBase.biome_type || "",
|
||||
entrance_map: secretBase.outside_map_id || 0,
|
||||
outside_entrance_position: secretBase.outside_entrance_position || [0, 0],
|
||||
layout_type: secretBase.base_layout_type || "",
|
||||
base_message: secretBase.base_message || "",
|
||||
layout: {
|
||||
items: list_base_items(secretBase)
|
||||
}
|
||||
},
|
||||
trainer: {
|
||||
name: $Trainer.name || "",
|
||||
id: $Trainer.id,
|
||||
nb_badges: $Trainer.badge_count || 0,
|
||||
game_mode: $Trainer.game_mode || 0,
|
||||
appearance: sanitize_string(export_current_outfit_to_json),
|
||||
team: export_team_as_array
|
||||
}
|
||||
}
|
||||
|
||||
JSON.generate(sanitize_string(base_data))
|
||||
end
|
||||
|
||||
# Export all items in the base layout
|
||||
def list_base_items(secretBase)
|
||||
return [] unless secretBase&.layout&.items
|
||||
secretBase.layout.items.map do |item|
|
||||
{
|
||||
id: item.itemId || "",
|
||||
position: item.position || [0, 0],
|
||||
direction: item.direction || DIRECTION_DOWN,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def write_base_json_to_file(new_base_json, file_path, append = true)
|
||||
ensure_folder_exists(File.dirname(file_path))
|
||||
|
||||
# Parse new_base JSON string into a Ruby object
|
||||
begin
|
||||
new_base = JSON.parse(new_base_json)
|
||||
rescue Exception => e
|
||||
echoln "[SecretBase] Failed to parse new base JSON: #{e.message}"
|
||||
return
|
||||
end
|
||||
|
||||
bases = []
|
||||
|
||||
if File.exist?(file_path)
|
||||
begin
|
||||
file_content = File.read(file_path).strip
|
||||
if !file_content.empty?
|
||||
# parse existing content into array
|
||||
bases = JSON.parse(file_content)
|
||||
bases = [] unless bases.is_a?(Array)
|
||||
end
|
||||
rescue Exception => e
|
||||
echoln "[SecretBase] Error reading existing file: #{e.message}"
|
||||
bases = []
|
||||
end
|
||||
end
|
||||
|
||||
# Append or replace
|
||||
if append
|
||||
bases << new_base
|
||||
else
|
||||
bases = [new_base]
|
||||
end
|
||||
|
||||
# Write back
|
||||
File.open(file_path, "w") do |file|
|
||||
file.write(JSON.generate(bases))
|
||||
end
|
||||
|
||||
echoln "[SecretBase] Saved base to #{file_path}"
|
||||
end
|
||||
|
||||
|
||||
|
||||
# Export the trainer's Pokémon party
|
||||
def export_team_as_array
|
||||
$Trainer.party.compact.map { |p| export_fused_pokemon_hash(p) }
|
||||
end
|
||||
|
||||
# Export a single Pokémon as a hash
|
||||
def export_fused_pokemon_hash(pokemon)
|
||||
{
|
||||
species: pokemon.species.to_s,
|
||||
name: pokemon.name || "",
|
||||
item: pokemon.item ? pokemon.item.id.to_s : "",
|
||||
ability: pokemon.ability ? pokemon.ability.id.to_s : "",
|
||||
level: pokemon.level || 1,
|
||||
evs: {
|
||||
hp: pokemon.ev[:HP] || 0,
|
||||
atk: pokemon.ev[:ATTACK] || 0,
|
||||
def: pokemon.ev[:DEFENSE] || 0,
|
||||
spa: pokemon.ev[:SPECIAL_ATTACK] || 0,
|
||||
spd: pokemon.ev[:SPECIAL_DEFENSE] || 0,
|
||||
spe: pokemon.ev[:SPEED] || 0
|
||||
},
|
||||
ivs: {
|
||||
hp: pokemon.iv[:HP] || 0,
|
||||
atk: pokemon.iv[:ATTACK] || 0,
|
||||
def: pokemon.iv[:DEFENSE] || 0,
|
||||
spa: pokemon.iv[:SPECIAL_ATTACK] || 0,
|
||||
spd: pokemon.iv[:SPECIAL_DEFENSE] || 0,
|
||||
spe: pokemon.iv[:SPEED] || 0
|
||||
},
|
||||
nature: GameData::Nature.get(pokemon.nature).id.to_s,
|
||||
moves: pokemon.moves.compact.map { |m| GameData::Move.get(m.id).id.to_s }
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Recursively replace nils with empty strings (or zero if numeric leaf)
|
||||
def sanitize_string(obj)
|
||||
case obj
|
||||
when Hash
|
||||
obj.transform_values { |v| sanitize_string(v) }
|
||||
when Array
|
||||
obj.map { |v| sanitize_string(v) }
|
||||
when NilClass
|
||||
""
|
||||
else
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,147 @@
|
||||
class SecretBaseImporter
|
||||
FRIEND_BASES_FILE = "Data/bases/friend_bases.json"
|
||||
VISITOR_BASES_FILE = "Data/bases/visitor_bases.json"
|
||||
|
||||
def read_secret_base_json(path)
|
||||
return [] unless File.exist?(path)
|
||||
|
||||
file_contents = File.read(path)
|
||||
begin
|
||||
# Parse with symbolized keys directly
|
||||
raw = JSON.parse(file_contents)
|
||||
return deep_clean(raw) # cleaned object, keys stay symbols
|
||||
rescue Exception => e
|
||||
echoln caller
|
||||
echoln("SecretBaseImporter: Failed to parse JSON: #{e}")
|
||||
return []
|
||||
end
|
||||
end
|
||||
|
||||
# Load all bases from the JSON
|
||||
def load_bases(path)
|
||||
all_bases_data = read_secret_base_json(path)
|
||||
return [] unless all_bases_data.is_a?(Array)
|
||||
|
||||
# Only keep entries with a trainer
|
||||
visitor_bases = []
|
||||
all_bases_data.map do |entry|
|
||||
begin
|
||||
base_data = entry[:base]
|
||||
trainer_data = entry[:trainer]
|
||||
|
||||
biome = base_data[:biome].to_sym
|
||||
base = VisitorSecretBase.new(
|
||||
biome: biome,
|
||||
outside_map_id: base_data[:entrance_map],
|
||||
outside_entrance_position: base_data[:outside_entrance_position],
|
||||
inside_map_id: base_data[:inside_map_id],
|
||||
layout: import_layout_from_json(base_data[:layout],biome),
|
||||
base_layout_type: base_data[:layout_type],
|
||||
trainer_data: import_trainer_from_json(trainer_data),
|
||||
base_message: base_data[:base_message],
|
||||
)
|
||||
echoln base.layout
|
||||
visitor_bases << base
|
||||
#base.dump_info
|
||||
rescue Exception => e
|
||||
echoln "COULD NOT LOAD BASE: #{e}"
|
||||
end
|
||||
end
|
||||
return visitor_bases
|
||||
end
|
||||
|
||||
|
||||
def import_layout_from_json(layout_json, biome)
|
||||
layout = SecretBaseLayout.new(
|
||||
biome,
|
||||
false
|
||||
)
|
||||
|
||||
items = []
|
||||
(layout_json[:items] || []).each do |item_data|
|
||||
id = item_data[:id].to_sym
|
||||
position = item_data[:position]
|
||||
direction = item_data[:direction]
|
||||
item_instance = SecretBaseItemInstance.new(id,position,direction)
|
||||
|
||||
echoln item_instance.direction
|
||||
items << item_instance
|
||||
end
|
||||
|
||||
echoln items
|
||||
layout.items = items
|
||||
return layout
|
||||
end
|
||||
|
||||
|
||||
def import_trainer_from_json(trainer_json)
|
||||
app = trainer_json[:appearance]
|
||||
trainer_appearance = TrainerAppearance.new(
|
||||
app[:skin_color], app[:hat], app[:clothes], app[:hair],
|
||||
app[:hair_color], app[:clothes_color], app[:hat_color],
|
||||
app[:hat2], app[:hat2_color]
|
||||
)
|
||||
|
||||
team = trainer_json[:team].map do |poke_json|
|
||||
pokemon = Pokemon.new(poke_json[:species], poke_json[:level])
|
||||
pokemon.name = poke_json[:name]
|
||||
pokemon.item = poke_json[:item]
|
||||
pokemon.ability = poke_json[:ability]
|
||||
pokemon.nature = poke_json[:nature]
|
||||
pokemon.moves = poke_json[:moves]
|
||||
|
||||
if poke_json[:ivs]
|
||||
poke_json[:ivs].each do |stat, value|
|
||||
case stat.to_s.downcase
|
||||
when "hp" then pokemon.iv[:HP] = value
|
||||
when "atk" then pokemon.iv[:ATTACK] = value
|
||||
when "def" then pokemon.iv[:DEFENSE] = value
|
||||
when "spe" then pokemon.iv[:SPEED] = value
|
||||
when "spa" then pokemon.iv[:SPECIAL_ATTACK] = value
|
||||
when "spd" then pokemon.iv[:SPECIAL_DEFENSE] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if poke_json[:evs]
|
||||
poke_json[:evs].each do |stat, value|
|
||||
case stat.to_s.downcase
|
||||
when "hp" then pokemon.ev[:HP] = value
|
||||
when "atk" then pokemon.ev[:ATTACK] = value
|
||||
when "def" then pokemon.ev[:DEFENSE] = value
|
||||
when "spe" then pokemon.ev[:SPEED] = value
|
||||
when "spa" then pokemon.ev[:SPECIAL_ATTACK] = value
|
||||
when "spd" then pokemon.ev[:SPECIAL_DEFENSE] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pokemon.calc_stats
|
||||
pokemon
|
||||
end
|
||||
|
||||
SecretBaseTrainer.new(
|
||||
trainer_json[:name],
|
||||
trainer_json[:nb_badges],
|
||||
trainer_json[:game_mode],
|
||||
trainer_appearance,
|
||||
team
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Recursively converts "" → nil, but keeps keys as symbols
|
||||
def deep_clean(obj)
|
||||
case obj
|
||||
when Hash
|
||||
obj.transform_values { |v| deep_clean(v) }
|
||||
when Array
|
||||
obj.map { |v| deep_clean(v) }
|
||||
when ""
|
||||
nil
|
||||
else
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,6 +4,7 @@ class BattledTrainer
|
||||
|
||||
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.
|
||||
|
||||
@@ -19,8 +20,6 @@ class BattledTrainer
|
||||
#Healing items that are in that list can be used by the trainer in rematches
|
||||
#
|
||||
attr_accessor :foundItems
|
||||
|
||||
|
||||
attr_accessor :nb_rematches
|
||||
|
||||
#What the trainer currently wants to do
|
||||
@@ -47,7 +46,8 @@ class BattledTrainer
|
||||
|
||||
attr_accessor :friendship #increases the more you interact with them, unlocks more interact options
|
||||
attr_accessor :friendship_level
|
||||
def initialize(trainerType,trainerName,trainerVersion)
|
||||
def initialize(trainerType,trainerName,trainerVersion,trainerKey)
|
||||
@trainerKey = trainerKey
|
||||
@trainerType = trainerType
|
||||
@trainerName = trainerName
|
||||
@currentTeam = loadOriginalTrainerTeam(trainerVersion)
|
||||
@@ -83,7 +83,7 @@ class BattledTrainer
|
||||
@friendship_level += 1
|
||||
|
||||
trainerClassName = GameData::TrainerType.get(@trainerType).real_name
|
||||
pbMessage(_INTL("\\C[3]Friendship increased with #{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!"))
|
||||
|
||||
@@ -30,7 +30,7 @@ end
|
||||
def registerBattledTrainer(event_id, mapId, trainerType, trainerName, trainerVersion=0)
|
||||
key = [event_id,mapId]
|
||||
$PokemonGlobal.battledTrainers = {} unless $PokemonGlobal.battledTrainers
|
||||
trainer = BattledTrainer.new(trainerType, trainerName, trainerVersion)
|
||||
trainer = BattledTrainer.new(trainerType, trainerName, trainerVersion,key)
|
||||
$PokemonGlobal.battledTrainers[key] = trainer
|
||||
return trainer
|
||||
end
|
||||
@@ -58,13 +58,24 @@ end
|
||||
|
||||
def updateRebattledTrainer(event_id,map_id,updated_trainer)
|
||||
key = [event_id,map_id]
|
||||
updateRebattledTrainerWithKey(key,updated_trainer)
|
||||
end
|
||||
|
||||
def updateRebattledTrainerWithKey(key,updated_trainer)
|
||||
$PokemonGlobal.battledTrainers = {} if !$PokemonGlobal.battledTrainers
|
||||
$PokemonGlobal.battledTrainers[key] = updated_trainer
|
||||
end
|
||||
|
||||
def getRebattledTrainer(event_id,map_id)
|
||||
key = [event_id,map_id]
|
||||
def getRebattledTrainerKey(event_id, map_id)
|
||||
return [event_id,map_id]
|
||||
end
|
||||
|
||||
def getRebattledTrainerFromKey(key)
|
||||
$PokemonGlobal.battledTrainers = {} if !$PokemonGlobal.battledTrainers
|
||||
return $PokemonGlobal.battledTrainers[key]
|
||||
end
|
||||
def getRebattledTrainer(event_id,map_id)
|
||||
key = getRebattledTrainerKey(event_id, map_id)
|
||||
return getRebattledTrainerFromKey(key)
|
||||
end
|
||||
|
||||
|
||||
@@ -59,16 +59,18 @@ 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)
|
||||
end
|
||||
updated_trainer.set_pending_action(false)
|
||||
updated_trainer = evolveRebattledTrainerPokemon(updated_trainer)
|
||||
trainer.increase_friendship(5)
|
||||
return updated_trainer
|
||||
return updated_trainer, player_won
|
||||
end
|
||||
|
||||
def showPrerematchDialog()
|
||||
@@ -116,6 +118,7 @@ def showPrerematchDialog()
|
||||
split_messages = message_text.split("<br>")
|
||||
split_messages.each do |msg|
|
||||
pbCallBub(2,event.id)
|
||||
pbCallBub(3) if isPartneredWithTrainer(trainer)
|
||||
pbMessage(msg)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ def doPostBattleAction(actionType)
|
||||
return if !trainer
|
||||
case actionType
|
||||
when :BATTLE
|
||||
trainer = doNPCTrainerRematch(trainer)
|
||||
trainer,player_won = doNPCTrainerRematch(trainer)
|
||||
when :TRADE
|
||||
trainer = doNPCTrainerTrade(trainer)
|
||||
when :PARTNER
|
||||
@@ -46,7 +46,7 @@ def setTrainerFriendship(trainer)
|
||||
params = ChooseNumberParams.new
|
||||
params.setRange(0,100)
|
||||
params.setDefaultValue($game_map.map_id)
|
||||
number = pbMessageChooseNumber("Frienship (0-100)?",params)
|
||||
number = pbMessageChooseNumber(_INTL("Frienship (0-100)?"),params)
|
||||
trainer.friendship = number
|
||||
trainer.increase_friendship(0)
|
||||
return trainer
|
||||
@@ -57,15 +57,15 @@ end
|
||||
#
|
||||
#def customTrainerBattle(trainerName, trainerType, party_array, default_level=50, endSpeech="", sprite_override=nil,custom_appearance=nil)
|
||||
def postBattleActionsMenu()
|
||||
rematchCommand = "Rematch"
|
||||
tradeCommand = "Trade Offer"
|
||||
partnerCommand = "Partner up"
|
||||
cancelCommand = "See ya!"
|
||||
rematchCommand = _INTL("Rematch")
|
||||
tradeCommand = _INTL("Trade Offer")
|
||||
partnerCommand = _INTL("Partner up")
|
||||
cancelCommand = _INTL("See ya!")
|
||||
|
||||
updateTeamDebugCommand = "(Debug) Simulate random event"
|
||||
resetTrainerDebugCommand = "(Debug) Reset trainer"
|
||||
setFriendshipDebugCommand = "(Debug) Set Friendship"
|
||||
printTrainerTeamDebugCommand = "(Debug) Print team"
|
||||
updateTeamDebugCommand = _INTL("(Debug) Simulate random event")
|
||||
resetTrainerDebugCommand = _INTL("(Debug) Reset trainer")
|
||||
setFriendshipDebugCommand = _INTL("(Debug) Set Friendship")
|
||||
printTrainerTeamDebugCommand = _INTL("(Debug) Print team")
|
||||
|
||||
|
||||
event = pbMapInterpreter.get_character(0)
|
||||
@@ -116,3 +116,22 @@ def postBattleActionsMenu()
|
||||
end
|
||||
end
|
||||
|
||||
#leave event_type empty for random
|
||||
def forceRandomRematchEventOnTrainer(event_type=nil)
|
||||
event = pbMapInterpreter.get_character(0)
|
||||
map_id = $game_map.map_id if map_id.nil?
|
||||
trainer = getRebattledTrainer(event.id,map_id)
|
||||
while !trainer.has_pending_action
|
||||
trainer = applyTrainerRandomEvents(trainer,event_type)
|
||||
end
|
||||
updateRebattledTrainer(event.id,map_id,trainer)
|
||||
end
|
||||
|
||||
def forceTrainerFriendshipOnTrainer(friendship=0)
|
||||
event = pbMapInterpreter.get_character(0)
|
||||
map_id = $game_map.map_id if map_id.nil?
|
||||
trainer = getRebattledTrainer(event.id,map_id)
|
||||
trainer.friendship = friendship
|
||||
trainer.increase_friendship(0)
|
||||
updateRebattledTrainer(event.id,map_id,trainer)
|
||||
end
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
|
||||
COMMON_EVENT_TRAINER_REMATCH_PARTNER = 200
|
||||
def partnerWithTrainer(eventId, mapID, trainer)
|
||||
Kernel.pbAddDependency2(eventId,trainer.trainerName,COMMON_EVENT_TRAINER_REMATCH_PARTNER)
|
||||
SWITCH_PARTNERED_WITH_NPC_TRAINER = 2049
|
||||
|
||||
class Trainer
|
||||
attr_accessor :npcPartner
|
||||
end
|
||||
def partnerWithTrainer(eventId, mapID, trainer,trainer_key=nil ,common_event=nil)
|
||||
common_event = COMMON_EVENT_TRAINER_REMATCH_PARTNER if !common_event
|
||||
Kernel.pbAddDependency2(eventId,trainer.trainerName,common_event)
|
||||
pbCancelVehicles
|
||||
originalTrainer = pbLoadTrainer(trainer.trainerType, trainer.trainerName, 0)
|
||||
Events.onTrainerPartyLoad.trigger(nil, originalTrainer)
|
||||
@@ -9,5 +15,51 @@ def partnerWithTrainer(eventId, mapID, trainer)
|
||||
i.owner = Pokemon::Owner.new_from_trainer(originalTrainer)
|
||||
i.calc_stats
|
||||
end
|
||||
trainer_key = getRebattledTrainerKey(eventId,mapID) if !trainer_key
|
||||
$PokemonGlobal.partner = [trainer.trainerType, trainer.trainerName, 0, trainer.currentTeam]
|
||||
$Trainer.npcPartner = trainer_key
|
||||
end
|
||||
|
||||
def unpartnerWithTrainer()
|
||||
pbRemoveDependencies
|
||||
$game_switches[SWITCH_PARTNERED_WITH_NPC_TRAINER]=false
|
||||
$Trainer.npcPartner=nil
|
||||
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
|
||||
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)
|
||||
case command
|
||||
when 0 # Keep
|
||||
return
|
||||
else
|
||||
# Give
|
||||
pbMessage(_INTL("You gave the {1} to {2}!",caughtPokemon.name,partnerTrainer.trainerName))
|
||||
if partnerTrainer.currentTeam.length == 6
|
||||
partnerTrainer.currentTeam[-1] = caughtPokemon
|
||||
else
|
||||
partnerTrainer.currentTeam << caughtPokemon
|
||||
end
|
||||
partnerTrainer.increase_friendship(10)
|
||||
updateRebattledTrainerWithKey($Trainer.npcPartner,partnerTrainer)
|
||||
if $Trainer.npcPartner == BATTLED_TRAINER_WALLY_KEY
|
||||
$game_switches[SWITCH_WALLY_GAVE_POKEMON_DIALOGUE]=true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def isPartneredWithTrainer(trainer)
|
||||
return $Trainer.npcPartner == trainer.trainerKey
|
||||
end
|
||||
def isPartneredWithAnyTrainer()
|
||||
return $Trainer.npcPartner != nil
|
||||
end
|
||||
@@ -12,7 +12,7 @@ def printNPCTrainerCurrentTeam(trainer)
|
||||
echoln "Trainer's current team is: #{team_string}"
|
||||
end
|
||||
|
||||
def applyTrainerRandomEvents(trainer)
|
||||
def applyTrainerRandomEvents(trainer,event_type=nil)
|
||||
if trainer.has_pending_action
|
||||
echoln "Trainer has pending action"
|
||||
end
|
||||
@@ -28,20 +28,21 @@ def applyTrainerRandomEvents(trainer)
|
||||
[:CATCH, 3],
|
||||
[:FUSE, 6],
|
||||
[:REVERSE, 1],
|
||||
[:UNFUSE, 20]
|
||||
[:UNFUSE, 2]
|
||||
]
|
||||
|
||||
# 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
|
||||
if selected_event
|
||||
echoln "Trying to do random event: #{selected_event}"
|
||||
end
|
||||
|
||||
|
||||
return trainer if selected_event.nil?
|
||||
original_team = trainer.currentTeam.clone
|
||||
|
||||
case selected_event
|
||||
when :CATCH
|
||||
@@ -53,7 +54,12 @@ def applyTrainerRandomEvents(trainer)
|
||||
when :REVERSE
|
||||
trainer = reverse_random_team_pokemon(trainer)
|
||||
end
|
||||
trainer.set_pending_action(true)
|
||||
new_team = trainer.currentTeam
|
||||
|
||||
echoln original_team
|
||||
echoln new_team
|
||||
team_changed = original_team != new_team
|
||||
trainer.set_pending_action(team_changed)
|
||||
printNPCTrainerCurrentTeam(trainer)
|
||||
return trainer
|
||||
end
|
||||
|
||||
@@ -217,7 +217,7 @@ def generateTrainerTradeOffer(trainer)
|
||||
|
||||
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?."),)
|
||||
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)
|
||||
@@ -229,17 +229,17 @@ def generateTrainerTradeOffer(trainer)
|
||||
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..."))
|
||||
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("#{trainerClassName} #{trainer.trainerName} is offering #{offered_pokemon.name} (Level #{offered_pokemon.level}) for your #{chosen_pokemon.name}.")
|
||||
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 #{chosen_pokemon.name} for #{trainerClassName} #{trainer.trainerName}'s #{offered_pokemon.name}?"))
|
||||
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)
|
||||
updated_party = trainer.currentTeam
|
||||
updated_party.delete(offered_pokemon)
|
||||
|
||||
73
Data/Scripts/053_PIF_Hoenn/TransferBox.rb
Normal file
73
Data/Scripts/053_PIF_Hoenn/TransferBox.rb
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
class PokemonStorage
|
||||
end
|
||||
|
||||
|
||||
class StorageTransferBox < PokemonBox
|
||||
TRANSFER_BOX_NAME = _INTL("Transfer Box")
|
||||
def initialize()
|
||||
super(TRANSFER_BOX_NAME,PokemonBox::BOX_SIZE)
|
||||
@pokemon = []
|
||||
@background = 16
|
||||
for i in 0...PokemonBox::BOX_SIZE
|
||||
@pokemon[i] = nil
|
||||
end
|
||||
loadTransferBoxPokemon
|
||||
end
|
||||
|
||||
|
||||
def loadTransferBoxPokemon
|
||||
path = transferBoxSavePath
|
||||
if File.exist?(path)
|
||||
File.open(path, "rb") do |f|
|
||||
@pokemon = Marshal.load(f)
|
||||
end
|
||||
end
|
||||
rescue => e
|
||||
echoln "Failed to load transfer box: #{e}"
|
||||
@pokemon = Array.new(PokemonBox::BOX_SIZE, nil)
|
||||
end
|
||||
|
||||
def []=(i,value)
|
||||
@pokemon[i] = value
|
||||
saveTransferBox()
|
||||
Game.save()
|
||||
end
|
||||
|
||||
def saveTransferBox
|
||||
path = transferBoxSavePath
|
||||
dir = File.dirname(path)
|
||||
Dir.mkdir(dir) unless Dir.exist?(dir)
|
||||
File.open(path, "wb") do |f|
|
||||
Marshal.dump(@pokemon, f)
|
||||
end
|
||||
echoln "Transfer box saved to #{path}"
|
||||
$game_temp.must_save_now=true
|
||||
rescue => e
|
||||
echoln "Failed to save transfer box: #{e}"
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def transferBoxSavePath
|
||||
save_dir = System.data_directory # e.g., %appdata%/infinitefusion
|
||||
parent_dir = File.expand_path("..", save_dir)
|
||||
File.join(parent_dir, "infinitefusion_common", "transfer_pokemon_storage")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#Never add more than 1, it would just be a copy
|
||||
def addPokemonStorageTransferBox()
|
||||
$PokemonStorage.boxes << StorageTransferBox.new
|
||||
end
|
||||
|
||||
def verifyTransferBoxAutosave()
|
||||
if !$game_temp.transfer_box_autosave
|
||||
confirmed = pbConfirmMessage(_INTL("Moving Pokémon in and out of the transfer box will save the game automatically. Is this okay?"))
|
||||
$game_temp.transfer_box_autosave=true if confirmed
|
||||
return confirmed
|
||||
end
|
||||
return true
|
||||
end
|
||||
Reference in New Issue
Block a user