Ruby:# ============================================================================
# Multiple Event Interpreter Fixes (partly copied from MVs default scripts)
# ----------------------------------------------------------------------------
# Put this script towards the top of the list, but still below "▼ Materials"
#
# ---
# This script aims to fix some issues related to VXAces default event command
# interpreter (these fixes will generally not work for Script commands):
#
# Saving and Loading:
# By default, the Interpreter can not save a lot of its inner state when
# the game is saved and loaded. Instead, the interpreter will skip the rest
# of any current command when the game is loaded.
# This mostly affects "Parallel Process" events, since opportunities to save
# are usually limited when a non-parallel event is running.
# The script should fix these issues after a savestate is loaded:
# - The remainder of "Wait for Completion" effects are no longer skipped
# - The remainder of any "Common Event" command is longer be skipped
# - Commands waiting for clearance should no longer be skipped
# (e.g. "Scroll Map" may get delayed if the map is already scrolling)
# - Commands should no longer be skipped if the game is saved and loaded
# multiple times before the Interpreter could resume execution.
# (Should usually not happen without other scripts, included for safety)
#
# Other Event Command Fixes:
# - "Break Loop" would normally jump behind the next loop that was less
# indented. Fixed so it will always jump to the end of the right loop.
# - "Jump to Label" will now clear branch data to prevent the interpreter
# from executing additional branches.
# - "Change HP" will no longer trigger an unconditional "Game Over" if the
# entire party is dead (and allow loseable battles to properly finish).
# - "Change Enemy HP" will not stop at the first dead enemy when
# "Entire Troop" is selected.
# - Calls for multiple Battles/Menu screens at the same time are spaced out
# to prevent issues (e.g. calling the battle screen from multiple events
# at once would not assign the outcome of each battle correctly)
#
# Other Changes:
# - Events may no longer wait for the message window to close before starting
# (This might be useful to keep for cutscenes to prevent them from starting
# over status update messages)
# => Can be disabled below (if this messes up event timings)
# - The extra 1-Frame-Wait at the end of each event can be removed.
# (This might be useful to keep for cutscenes in some cases, since they
# might otherwise refresh event page conditions a frame early)
# => Can be disabled below (if this messes up event timings)
# - The Player is now prevented from moving when an "Event Touch" or
# "Autostart" is scheduled to run (Before, the player could move out of
# the way on the frame an "Event Touch" event collided with them. This
# also prevents the player from moving when an "Autostart" event is active
# that can be completed in one frame)
#
# ----------------------------------------------------------------------------
class Game_Interpreter
# Keep Waiting for Message at the beginning of each event:
# 2 : Keep Always (No Change)
# 1 : Keep For Cutscene Events (Action Key, Player/Event Touch, Autostart)
# 0 : Remove
START_OF_EVENT_MESSAGE_WAIT = 1
# Keep the extra one frame Wait at the end of each Event:
# 2 : Keep Always (No Change)
# 1 : Keep For Cutscene Events (Action Key, Player/Event Touch, Autostart)
# 0 : Remove
END_OF_EVENT_WAIT = 1
end
# ----------------------------------------------------------------------------
# END OF CONFIGURATION
# ============================================================================
class Scene_Base
alias_method(:main_afterRegisterActiveScene, :main)
def main
SceneManager.active_scene = self
main_afterRegisterActiveScene
end
end
module SceneManager
class << self
attr_writer :active_scene
end
def self.scene_changing?
!@active_scene || @active_scene.scene_changing?
end
end
class Game_Player
#--------------------------------------------------------------------------
# * Processing of Movement via Input from Directional Buttons
# Block Player Movement when an event is scheduled to trigger.
#--------------------------------------------------------------------------
alias_method(:move_by_input_withoutEventStartingCheck, :move_by_input)
def move_by_input
move_by_input_withoutEventStartingCheck if !$game_map.any_event_starting?
end
end
# Remember if no event was starting (might save 1 lookup per frame)
# (Optional, remove if problematic)
class Game_Map
attr_writer :no_event_starting
alias_method(:any_event_starting_withoutNoStartVar, :any_event_starting?)
def any_event_starting?
result = (!@no_event_starting && any_event_starting_withoutNoStartVar)
@no_event_starting = true if !result
return result
end
end
class Game_Event
alias_method(:start_withoutMapNoStartReset, :start)
def start
$game_map.no_event_starting = false
start_withoutMapNoStartReset
$game_map.no_event_starting = false
end
end
# ---
class Game_Interpreter
# --------------------------------------------------------------------------
# Initialize New Variables
# --------------------------------------------------------------------------
alias_method(:clear_withoutWaitVariables, :clear)
def clear
@wait_count = 0 # Remaining Wait Frames
@wait_mode = nil # Wait For Completion Mode
@wait_param = nil # Wait For Completion Parameter
@index_executed = false # The Current Index is considered executed
clear_withoutWaitVariables
end
WaitModeStruct = Struct.new(:wait_count, :wait_mode, :wait_param, :idle_index)
alias_method(:marshal_dump_withoutWaitVariables, :marshal_dump)
def marshal_dump
extra = WaitModeStruct.new(
@wait_count,
@wait_mode,
@wait_param,
(@index_executed ? nil : @index) # Save Current Index if not executed.
)
return marshal_dump_withoutWaitVariables + [extra]
end
alias_method(:marshal_load_withoutWaitVariables, :marshal_load)
def marshal_load(object)
extra = object.last
if extra.is_a?(WaitModeStruct)
marshal_load_withoutWaitVariables(object[0, object.length - 1])
@wait_count = extra.wait_count
@wait_mode = extra.wait_mode
@wait_param = extra.wait_param
@index = extra.idle_index if extra.idle_index
else # Compatibility with older save states:
marshal_load_withoutWaitVariables(object)
@wait_count = 0
@wait_mode = nil
@wait_param = nil
end
@index_started = false
end
#--------------------------------------------------------------------------
# * Execute
# Redefined: Update Start- and End Waits, track index started.
#--------------------------------------------------------------------------
def run
wait_for_message if wait_option_enabled?(START_OF_EVENT_MESSAGE_WAIT)
wait(@wait_count)
process_wait_mode
@index_executed = true
while @list[@index] do
execute_command
@index += 1
end
Fiber.yield if wait_option_enabled?(END_OF_EVENT_WAIT)
@fiber = nil
end
def wait_option_enabled?(option)
return option == 2 || (option == 1 && self == $game_map.interpreter)
end
#--------------------------------------------------------------------------
# * Event Command Execution
# Redefined: Small Performance Update (Not necessary)
#--------------------------------------------------------------------------
COMMAND_NAME_TABLE = Hash.new { |h, k| h[k] = :"command_#{k}" }
def execute_command
command = @list[@index]
@params = command.parameters
@indent = command.indent
method_name = COMMAND_NAME_TABLE[command.code]
send(method_name) if respond_to?(method_name)
end
#--------------------------------------------------------------------------
# * Wait
# Redefined: Track Wait Counter for Marshal dump/load
#--------------------------------------------------------------------------
def wait(duration)
@wait_count = duration
while @wait_count > 0
@wait_count -= 1
Fiber.yield
end
end
# --------------------------------------------------------------------------
# Event Command Fixes:
# --------------------------------------------------------------------------
# Wait for clearance while executing the given block.
# If the interpreter is dumped and loaded while waiting for clearance,
# the current index is not skipped.
def wait_for_clearance
@index_executed = false
yield
@index_executed = true
end
# Wait for completion at the end of an event command.
def wait_for_completion(wait_mode, wait_parameter = nil)
@wait_mode = wait_mode
@wait_param = wait_parameter
process_wait_mode
end
def process_wait_mode
case @wait_mode
when :common_event then @wait_param.run
when :move_route then Fiber.yield while @wait_param.move_route_forcing
when :gather_followers then Fiber.yield until $game_player.followers.gather?
when :transfer_player then Fiber.yield while $game_player.transfer?
when :show_animation then Fiber.yield while @wait_param.animation_id > 0
when :show_balloon then Fiber.yield while @wait_param.balloon_id > 0
end
@wait_mode = nil
@wait_param = nil
end
# Message Commands:
# (No wait for completion since $game_message is usually not saved anyways)
alias_method(:command_101_withoutClearance, :command_101)
def command_101
wait_for_clearance { wait_for_message }
command_101_withoutClearance
end
alias_method(:command_102_withoutClearance, :command_102)
def command_102
wait_for_clearance { wait_for_message }
command_102_withoutClearance
end
alias_method(:command_103_withoutClearance, :command_103)
def command_103
wait_for_clearance { wait_for_message }
command_103_withoutClearance
end
alias_method(:command_104_withoutClearance, :command_104)
def command_104
wait_for_clearance { wait_for_message }
command_104_withoutClearance
end
alias_method(:command_105_withoutClearance, :command_105)
def command_105
wait_for_clearance { Fiber.yield while $game_message.visible }
command_105_withoutClearance
end
#--------------------------------------------------------------------------
# * Break Loop
# Redefined: Fix a bug that allowed jumping behind an inner loop
#--------------------------------------------------------------------------
def command_113
loop_ends_needed = 1
index_threshold = @list.length - 1
while @index < index_threshold
@index += 1
if @list[@index].code == 413
loop_ends_needed -= 1
break if loop_ends_needed == 0
elsif @list[@index].code == 112
loop_ends_needed += 1
end
end
end
#--------------------------------------------------------------------------
# * Common Event
# Redefined: Keep track of the child interpreter
#--------------------------------------------------------------------------
def command_117
common_event = $data_common_events[@params[0]]
if common_event
child = Game_Interpreter.new(@depth + 1)
child.setup(common_event.list, same_map? ? @event_id : 0)
wait_for_completion(:common_event, child)
end
end
#--------------------------------------------------------------------------
# * Jump to Label
# Alias: Reset Branch Data to prevent jumping into unrelated branches
#--------------------------------------------------------------------------
alias_method(:command_119_withoutBranchFix, :command_119)
def command_119
command_119_withoutBranchFix
@branch.clear
end
#--------------------------------------------------------------------------
# * Transfer Player
# Redefined: Track Clearance and Completion Waiting
#--------------------------------------------------------------------------
def command_201
return if $game_party.in_battle
wait_for_clearance {
Fiber.yield while $game_player.transfer? || $game_message.visible
}
setup_player_transfer
wait_for_completion(:transfer_player)
end
def setup_player_transfer
if @params[0] == 0 # Direct designation
map_id = @params[1]
x = @params[2]
y = @params[3]
else # Designation with variables
map_id = $game_variables[@params[1]]
x = $game_variables[@params[2]]
y = $game_variables[@params[3]]
end
$game_player.reserve_transfer(map_id, x, y, @params[4])
$game_temp.fade_type = @params[5]
end
# Scroll Map:
alias_method(:command_204_withoutClearance, :command_204)
def command_204
return if $game_party.in_battle
wait_for_clearance { Fiber.yield while $game_map.scrolling? }
command_204_withoutClearance
end
#--------------------------------------------------------------------------
# * Set Move Route
# Redefined: Track Move Route Waiting
#--------------------------------------------------------------------------
def command_205
$game_map.refresh if $game_map.need_refresh
character = get_character(@params[0])
if character
character.force_move_route(@params[1])
wait_for_completion(:move_route, character) if @params[1].wait
end
end
#--------------------------------------------------------------------------
# * Show Animation
# Redefined: Track Animation Showing
#--------------------------------------------------------------------------
def command_212
character = get_character(@params[0])
if character
character.animation_id = @params[1]
wait_for_completion(:show_animation, character) if @params[2]
end
end
#--------------------------------------------------------------------------
# * Show Balloon Icon
# Redefined: Track Balloon Showing
#--------------------------------------------------------------------------
def command_213
character = get_character(@params[0])
if character
character.balloon_id = @params[1]
wait_for_completion(:show_balloon, character) if @params[2]
end
end
#--------------------------------------------------------------------------
# * Gather Followers
# Redefined: Track Gathering Waiting
#--------------------------------------------------------------------------
def command_217
return if $game_party.in_battle
$game_player.followers.gather
wait_for_completion(:gather_followers)
end
# Fadeout / Fadein Screen:
alias_method(:command_221_withoutClearance, :command_221)
def command_221
wait_for_clearance { Fiber.yield while $game_message.visible }
command_221_withoutClearance
end
alias_method(:command_222_withoutClearance, :command_222)
def command_222
wait_for_clearance { Fiber.yield while $game_message.visible }
command_222_withoutClearance
end
# Battle/Shop/Name Input Processing:
alias_method(:command_301_withoutClearance, :command_301)
def command_301
return if $game_party.in_battle
wait_for_clearance { Fiber.yield while SceneManager.scene_changing? }
command_301_withoutClearance
end
alias_method(:command_302_withoutClearance, :command_302)
def command_302
return if $game_party.in_battle
wait_for_clearance { Fiber.yield while SceneManager.scene_changing? }
command_302_withoutClearance
end
alias_method(:command_303_withoutClearance, :command_303)
def command_303
return if $game_party.in_battle
wait_for_clearance { Fiber.yield while SceneManager.scene_changing? }
command_303_withoutClearance
end
#--------------------------------------------------------------------------
# * Change HP
# Redefined: Removed the unconditional Game Over.
#--------------------------------------------------------------------------
def command_311
value = operate_value(@params[2], @params[3], @params[4])
iterate_actor_var(@params[0], @params[1]) do |actor|
next if actor.dead?
actor.change_hp(value, @params[5])
actor.perform_collapse_effect if actor.dead?
end
end
#--------------------------------------------------------------------------
# * Change Enemy HP
# Redefined: Dont return if enemy is dead
#--------------------------------------------------------------------------
def command_331
value = operate_value(@params[1], @params[2], @params[3])
iterate_enemy_index(@params[0]) do |enemy|
next if enemy.dead?
enemy.change_hp(value, @params[4])
enemy.perform_collapse_effect if enemy.dead?
end
end
# Other Scene Calls:
alias_method(:command_351_withoutClearance, :command_351)
def command_351
return if $game_party.in_battle
wait_for_clearance { Fiber.yield while SceneManager.scene_changing? }
command_351_withoutClearance
end
alias_method(:command_352_withoutClearance, :command_352)
def command_352
return if $game_party.in_battle
wait_for_clearance { Fiber.yield while SceneManager.scene_changing? }
command_352_withoutClearance
end
alias_method(:command_353_withoutClearance, :command_353)
def command_353
wait_for_clearance { Fiber.yield while SceneManager.scene_changing? }
command_353_withoutClearance
end
alias_method(:command_354_withoutClearance, :command_354)
def command_354
wait_for_clearance { Fiber.yield while SceneManager.scene_changing? }
command_354_withoutClearance
end
end