mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-12 15:44:57 +00:00
update 6.7
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user