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,91 @@
class OnlineWondertrade
def pbWonderTrade()
givenPokemon = selectPokemonToGive
return if givenPokemon == nil
queryBody = buildWondertradeQueryJson(givenPokemon)
begin
response = HTTPLite.post_body(Settings::WONDERTRADE_BASE_URL + "/wondertrade", queryBody, "application/json")
if response[:status] == 200
body = HTTPLite::JSON.parse(response[:body])
doTrade(body)
else
pbMessage(_INTL("Could not find a trading partner..."))
end
rescue MKXPError
pbMessage(_INTL("There was an error while sending your Pokémon..."))
end
end
def doTrade(receivedData)
receivedPokemonSpecies = receivedData["pokemon_species"].to_sym
receivedPokemonLevel = receivedData["level"].to_i
receivedPokemonName = receivedData["nickname"]
receivedPokemonOT = receivedData["original_trainer_name"]
receivedPokemonTrainerId = receivedData["trainer_id"]
receivedPokemonTrainerName = receivedData["trainer_name"]
receivedPokemonTrainerGender = receivedData["trainer_gender"].to_i
is_head_shiny = receivedData["head_shiny"]
is_body_shiny = receivedData["body_shiny"]
is_debug_shiny = receivedData["debug_shiny"]
newpoke = pbStartTrade(pbGet(1), receivedPokemonSpecies, receivedPokemonName, receivedPokemonTrainerName, receivedPokemonTrainerGender, true) # Starts the trade
newpoke.owner=Pokemon::Owner.new(receivedPokemonTrainerId.to_i,receivedPokemonOT,2,2)
newpoke.level=receivedPokemonLevel
if is_head_shiny || is_body_shiny
newpoke.shiny=true
newpoke.head_shiny=is_head_shiny
newpoke.body_shiny = is_body_shiny
if is_debug_shiny
newpoke.debug_shiny=false
newpoke.natural_shiny=true
else
newpoke.debug_shiny=true
newpoke.natural_shiny=false
end
end
newpoke.calc_stats
end
def selectPokemonToGive
pbChoosePokemon(1, 2, # Choose eligable pokemon
proc {
|poke| !poke.egg? &&
!(poke.isShadow?) &&
poke.isFusion? &&
customSpriteExistsSpecies(poke.species) #&&
# !poke.debug_shiny
})
poke = $Trainer.party[pbGet(1)]
if pbConfirmMessage(_INTL("Trade {1} away?", poke.name))
return poke
end
return nil
end
# @param [Pokemon] givenPokemon
def buildWondertradeQueryJson(givenPokemon)
isDebugShiny = givenPokemon.debug_shiny || !givenPokemon.natural_shiny
postData = {
"trainer_name" => $Trainer.name,
"trainer_gender" => $Trainer.gender,
"trainer_id" => $Trainer.id.to_s,
"nb_badges" => $Trainer.badge_count,
"given_pokemon" => givenPokemon.species.to_s,
"level" => givenPokemon.level,
"nickname" => givenPokemon.name,
"original_trainer_name" => givenPokemon.owner.name,
"original_trainer_id" => givenPokemon.owner.id.to_s,
"body_shiny" => givenPokemon.body_shiny == nil ? false : givenPokemon.body_shiny,
"head_shiny" => givenPokemon.head_shiny == nil ? false : givenPokemon.head_shiny,
"debug_shiny" => isDebugShiny,
"original_trainer_id" => givenPokemon.owner.id.to_s,
}
return HTTPLite::JSON.stringify(postData)
end
end

View File

@@ -0,0 +1,175 @@
# This allows to obtain the next move in battles by a third party instead of relying on the internal game AI.
# The game sends information about the current state of a battle and expects a move in return.
#
# To use, you need the PIF_RemoteBattlerServer script (or custom equivalent) running at the address in Settings::REMOTE_BATTLE_CONTROL_SERVER_URL (localhost by default)
# and to set Settings::REMOTE_BATTLES_CONTROL to true.
#
# PIF_RemoteBattlerServer can be set in either AI mode or HUMAN mode (manual selection)
class RemotePokeBattle_AI < PokeBattle_AI
def initialize(battle)
super
end
def pbChooseMoves(idxBattler)
current_battler = @battle.battlers[idxBattler]
ally_battlers, enemy_battlers = get_battlers_by_side(current_battler)
echoln "ally_battlers: #{ally_battlers}, enemy_battler: #{enemy_battlers}"
state_params = serialize_battle_state_to_params(current_battler, ally_battlers, enemy_battlers)
safe_params = convert_to_json_safe(state_params)
json_data = JSON.generate(safe_params)
response = pbPostToString(Settings::REMOTE_BATTLE_CONTROL_SERVER_URL, { "battle_state" => json_data })
response = clean_json_string(response)
available_moves = current_battler.moves.map { |m| m.id.to_s.upcase }
echoln available_moves
chosen_index = available_moves.index(response) || 0
@battle.pbRegisterMove(idxBattler, chosen_index, false)
PBDebug.log("[Remote AI] #{current_battler.pbThis(true)} (#{current_battler.index}) will use #{current_battler.moves[chosen_index].name}")
end
private
def get_battlers_by_side(current_battler)
# Determine which side the current battler is on
side_index = 0
count = 0
@battle.sideSizes.each_with_index do |size, idx|
if @battle.battlers.index(current_battler) < count + size
side_index = idx
break
end
count += size
end
my_side_battlers = []
opposing_battlers = []
idx_counter = 0
@battle.sideSizes.each_with_index do |size, idx|
size.times do
battler = @battle.battlers[idx_counter]
if idx == side_index
my_side_battlers << battler unless battler == current_battler
else
opposing_battlers << battler
end
idx_counter += 1
end
end
return my_side_battlers, opposing_battlers
end
def serialize_battle_state_to_params(current_battler, ally_battlers, enemy_battlers)
# Remove the current Pokémon from allies
filtered_allies = ally_battlers.reject { |b| b == current_battler }
{
current: serialize_battler(current_battler),
allies: (filtered_allies.first(2).map { |ally| serialize_battler(ally) } + [nil]*2)[0,2],
enemies: (enemy_battlers.first(3).map { |enemy| serialize_battler(enemy) } + [nil]*3)[0,3]
}
end
def serialize_battler(battler)
return nil unless battler
{
species: get_pokemon_readable_internal_name(battler.pokemon),
level: battler.level,
type1: battler.type1,
type2: battler.type2,
ability_id: battler.ability_id,
item_id: battler.item_id,
gender: battler.gender,
iv: battler.iv,
moves: battler.moves.map { |m| m ? m.id : nil },
# Stats
attack: battler.attack,
spatk: battler.spatk,
speed: battler.speed,
stages: battler.stages,
total_hp: battler.totalhp,
current_hp: battler.hp,
# Status
fainted: battler.fainted,
captured: battler.captured,
effects: battler.effects,
# Battle history / actions
turn_count: battler.turnCount,
participants: battler.participants,
last_attacker: battler.lastAttacker&.index,
last_foe_attacker: battler.lastFoeAttacker&.index,
last_hp_lost: battler.lastHPLost,
last_hp_lost_from_foe: battler.lastHPLostFromFoe,
last_move_used: battler.lastMoveUsed,
last_move_used_type: battler.lastMoveUsedType,
last_regular_move_used: battler.lastRegularMoveUsed,
last_regular_move_target: battler.lastRegularMoveTarget,
last_round_moved: battler.lastRoundMoved,
last_move_failed: battler.lastMoveFailed,
last_round_move_failed: battler.lastRoundMoveFailed,
moves_used: battler.movesUsed&.map { |m| m },
current_move: battler.currentMove,
took_damage: battler.tookDamage,
took_physical_hit: battler.tookPhysicalHit,
damage_state: battler.damageState,
initial_hp: battler.initialHP
}
end
def fetch_sprite_from_web(url, destinationPath = nil)
return false unless downloadAllowed?()
begin
response = HTTPLite.get(url)
if response[:status] == 200
if destinationPath
File.open(destinationPath, "wb") { |f| f.write(response[:body]) }
end
return response[:body]
else
echoln "Failed to fetch from #{url}"
return nil
end
rescue MKXPError => e
echoln "MKXPError: #{e.message}"
return nil
rescue Errno::ENOENT => e
echoln "File Error: #{e.message}"
return nil
end
end
end
#------------------------------
# Convert objects to JSON-safe
# ------------------------------
def convert_to_json_safe(obj)
case obj
when Hash
obj.each_with_object({}) { |(k,v), h| h[k.to_s] = convert_to_json_safe(v) }
when Array
obj.compact.map { |v| convert_to_json_safe(v) }
when Symbol
obj.to_s
when TrueClass, FalseClass, NilClass, Numeric
obj
else
obj.to_s
end
end

View File

@@ -0,0 +1,114 @@
#===============================================================================
# ** Game_Temp extensions
#===============================================================================
class Game_Temp
attr_accessor :talking_npc_id # Current NPC being spoken to
attr_accessor :dialog_context # Stores accumulated dialog per NPC
attr_accessor :active_event_finalizer # Proc to run when event is fully finished
end
#===============================================================================
# ** Utility
#===============================================================================
def getNPCContextKey(event_id)
"map_#{$game_map.map_id}_#{event_id}"
end
def add_npc_context(event_id, value, is_player_response=false)
npc_context_key = getNPCContextKey(event_id)
npc_context = $game_temp.dialog_context[npc_context_key] || []
actor = is_player_response ? "Player" : "NPC"
value = "#{actor}: #{value}"
npc_context << value unless npc_context.include?(value)
$game_temp.dialog_context[npc_context_key] = npc_context
end
def get_npc_context(event_id)
npc_context_key = getNPCContextKey(event_id)
return $game_temp.dialog_context[npc_context_key] if npc_context_key
end
#===============================================================================
# ** Window_AdvancedTextPokemon extensions
# Intercepts text display to save dialog context, and runs finalizer when done
#===============================================================================
class Window_AdvancedTextPokemon
alias _remoteNPCDialog_setText_original setText
def setText(value)
_remoteNPCDialog_setText_original(value)
return unless Settings::REMOTE_NPC_DIALOG
return if value.nil? || value.empty? || !$PokemonTemp.speechbubble_bubble
# Initialize dialog_context if needed
$game_temp.dialog_context ||= {}
event_id = $game_temp.talking_npc_id
return unless event_id
add_npc_context(event_id, value, false)
echoln $game_temp.dialog_context
end
alias _remoteNPCDialog_dispose_original dispose
def dispose
_remoteNPCDialog_dispose_original
end
end
#===============================================================================
# ** Interpreter extensions
# Sets up active_event_finalizer when an event starts
#===============================================================================
class Interpreter
alias _remoteNPCDialog_setup setup
def setup(list, event_id, map_id = nil)
_remoteNPCDialog_setup(list, event_id, map_id)
return unless Settings::REMOTE_NPC_DIALOG
if event_id > 0 && map_id
$game_temp.talking_npc_id = event_id
return unless $game_temp.talking_npc_id
# Prepare finalizer for end-of-event
$game_temp.active_event_finalizer = Proc.new {
extraDialogPrompt(event_id)
$game_temp.talking_npc_id = nil
}
end
end
alias _remoteNPCDialog_command_end command_end
def command_end
_remoteNPCDialog_command_end
# Run finalizer once when the events interpreter finishes
if $game_temp.active_event_finalizer
$game_temp.active_event_finalizer.call
$game_temp.active_event_finalizer = nil
end
end
end
def extraDialogPrompt(event_id)
return unless Settings::REMOTE_NPC_DIALOG
return unless $game_temp.dialog_context
npc_context_key = getNPCContextKey(event_id)
npc_context = $game_temp.dialog_context[npc_context_key]
return unless npc_context
cmd_leave = _INTL("See ya!")
cmd_talk = _INTL("Say something")
commands = [cmd_leave, cmd_talk]
choice = optionsMenu(commands)
case commands[choice]
when cmd_talk
text = pbEnterText(_INTL("What do you want to say?"),0,100)
add_npc_context(event_id, text, true)
response = getRemoteNPCResponse(event_id)
add_npc_context(event_id, response, false)
extraDialogPrompt(event_id)
else
end
end

View File

@@ -0,0 +1,33 @@
#Npc context an array of dialogues in order
# ex: ["NPC: hello, I'm an NPC"], ["Player: Hello!"]
def getRemoteNPCResponse(event_id)
npc_event = $game_map.events[event_id]
npc_context = get_npc_context(event_id) # ["NPC: Hello...", "Player: ..."]
npc_sprite_name = npc_event.character_name
current_location = Kernel.getMapName($game_map.map_id)
# Build state params
state_params = {
context: npc_context,
sprite: npc_sprite_name,
location: current_location
}
# Convert into JSON-safe form (like battle code does)
safe_params = convert_to_json_safe(state_params)
json_data = JSON.generate(safe_params)
# Send to your remote dialogue server
response = pbPostToString(Settings::REMOTE_NPC_DIALOG_SERVER_URL, { "npc_state" => json_data },10)
response = clean_json_string(response)
echoln "npc sprite name: #{npc_sprite_name}"
echoln "current location: #{current_location}"
echoln "[Remote NPC] Sent state: #{json_data}"
echoln "[Remote NPC] Got response: #{response}"
pbCallBub(2,event_id)
pbMessage(response)
return response
end