update 6.7

This commit is contained in:
chardub
2025-09-28 15:53:01 -04:00
parent ef5e023ae0
commit 318ff90d8d
696 changed files with 111759 additions and 198230 deletions

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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