mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-11 07:04:59 +00:00
update 6.7
This commit is contained in:
91
Data/Scripts/998_Experimental/OnlineWondertrade.rb
Normal file
91
Data/Scripts/998_Experimental/OnlineWondertrade.rb
Normal 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
|
||||
175
Data/Scripts/998_Experimental/RemoteBattleClient.rb
Normal file
175
Data/Scripts/998_Experimental/RemoteBattleClient.rb
Normal 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
|
||||
@@ -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 event’s 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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user