Earthbound-Ish Battle System
Version: 1.0
Author: cozziekuns
Date: February 17, 2013
Version History
- <Version 1.0> 2013.02.17 - Original Release
Description
This script attempts to mimic the battle system of the Earthbound/Mother series; most notably Earthbound and Earthbound 2 (Mother 2 and 3).
Features
- Plug and play!
- Easy to customise with various addons.
- Odometer rolling health meter.
- Essentially lag-less (may lag for older computers).
ScreenshotsBase Script
Base with Sprite Display
Base with Sprite Display + Odometer
Instructions
See the script for instructions. The script is virtually plug and play, minus the need for the odometer graphics if you decide to use the odometer add-on. My template for an odometer graphic is attached.
Script
#==============================================================================
# ** Earthbound-Ish Battle Core
#------------------------------------------------------------------------------
# Version: 1.0
# Author: cozziekuns
# Date: February 17, 2013
#==============================================================================
# Description:
#------------------------------------------------------------------------------
# This script attempts to mimic the battle system of the Earthbound/Mother
# series; most notably Earthbound and Earthbound 2 (Mother 2 and 3).
#==============================================================================
# Instructions:
#------------------------------------------------------------------------------
# Paste this script into its own slot in the Script Editor, above Main but
# below Materials. Edit the modules to your liking.
#==============================================================================
#==============================================================================
# ** Cozziekuns
#==============================================================================
module Cozziekuns
module Earthboundish
ActorIcons ={
# Command Name => IconID
"Attack" => 116,
"Magic" => 152,
"Special" => 128,
"Guard" => 161,
"Items" => 260,
"Escape" => 474,
}
end
end
include Cozziekuns
#==============================================================================
# ** Sprite Battler
#==============================================================================
class Sprite_Battler
alias coz_ebish_spbtlr_update_effect update_effect
def update_effect(*args)
update_select_whiten if @effect_type == :select_white
if @battler.sprite_effect_type == :select_white_to_normal
revert_to_normal
@battler.sprite_effect_type = nil
end
coz_ebish_spbtlr_update_effect(*args)
end
def update_select_whiten
self.color.set(255, 255, 255, 0)
self.color.alpha = 128
end
end
#==============================================================================
# ** Window_ActorCommand
#==============================================================================
class Window_ActorCommand
def visible_line_number
return 1
end
def col_max
return 6
end
def contents_height
item_height
end
def top_col
ox / (item_width + spacing)
end
def top_col=(col)
col = 0 if col < 0
col = col_max - 1 if col > col_max - 1
self.ox = col * (item_width + spacing)
end
def bottom_col
top_col + col_max - 1
end
def bottom_col=(col)
self.top_col = col - (col_max - 1)
end
def ensure_cursor_visible
self.top_col = index if index < top_col
self.bottom_col = index if index > bottom_col
end
def item_rect(index)
rect = super
rect.x = index * (item_width + spacing)
rect.y = 0
rect
end
def window_width
Graphics.width - 224
end
def open
@help_window.open
super
end
def close
@help_window.close
super
end
def update
super
@help_window.update
end
def draw_item(index)
rect = item_rect_for_text(index)
x = rect.x + rect.width / 2 - 12
y = item_rect_for_text(index).y
draw_icon(Earthboundish::ActorIcons[command_name(index)], x, y, command_enabled?(index))
end
alias coz_ebish_waccmd_make_command_list make_command_list
def make_command_list(*args)
coz_ebish_waccmd_make_command_list(*args)
add_escape_command
end
def add_escape_command
add_command(Vocab::escape, :escape, BattleManager.can_escape?)
end
def update_help
@help_window.set_text(command_name(index))
end
end
#==============================================================================
# ** Window_BattleStatus
#==============================================================================
class Window_BattleStatus
[:basic_area_rect, :gauge_area_rect].each { |method|
define_method(method) { |index|
rect = item_rect(index)
rect
}
}
def col_max
$game_party.members.size
end
def window_width
Graphics.width / ( 4 / $game_party.members.size.to_f )
end
def window_height
fitting_height($data_system.opt_display_tp ? 5 : 4)
end
def item_width
(window_width - standard_padding * 2) / $game_party.members.size
end
def item_height
(window_height - standard_padding * 2)
end
def draw_basic_area(rect, actor)
draw_actor_name(actor, rect.x, rect.y, rect.width)
draw_actor_icons(actor, rect.x + 4, rect.y + line_height, rect.width)
end
def draw_gauge_area_with_tp(rect, actor)
draw_gauge_area_without_tp(rect, actor)
draw_actor_tp(actor, rect.x + 4, rect.y + line_height * 4, rect.width - 8)
end
def draw_gauge_area_without_tp(rect, actor)
draw_actor_hp(actor, rect.x + 4, rect.y + line_height * 2, rect.width - 8)
draw_actor_mp(actor, rect.x + 4, rect.y + line_height * 3, rect.width - 8)
end
def draw_actor_name(actor, x, y, width = 112)
change_color(hp_color(actor))
draw_text(x, y, width, line_height, actor.name, 1)
end
alias coz_ebish_wbtlsts_item_rect item_rect
def item_rect(index, *args)
rect = coz_ebish_wbtlsts_item_rect(index, *args)
rect.x = index % col_max * item_width
rect
end
def update
super
update_position
end
def update_position
self.x = (Graphics.width - window_width) / 2 + 128
end
end
#==============================================================================
# ** Window_BattleActor
#==============================================================================
class Window_BattleActor
def update_position
self.x = (Graphics.width - window_width) / 2
end
end
#==============================================================================
# ** Window_BattleEnemy
#==============================================================================
class Window_BattleEnemy
def cursor_left(wrap)
select((index - 1 + item_max) % item_max)
end
def cursor_right(wrap)
select((index + 1) % item_max)
end
def cursor_up(wrap)
cursor_left(true)
end
def cursor_down(wrap)
cursor_right(true)
end
def show
select(0)
self
end
def update_help
@help_window.set_text(enemy.name)
end
end
#==============================================================================
# ** Window_BattleSkill + Window_BattleItem
#==============================================================================
[:Window_BattleSkill, :Window_BattleItem].each { |klass|
Object.const_get(klass).send(:define_method, :show) {
select_last
super()
}
Object.const_get(klass).send(:define_method, :hide) { super() }
}
#==============================================================================
# ** Window_ActorHelp
#==============================================================================
class Window_ActorHelp < Window_Help
def initialize
super(1)
self.openness = 0
update_position
end
def update_position
self.x = Graphics.width - 224
self.width = 224
create_contents
end
def refresh
contents.clear
draw_text(4, 0, 224 - standard_padding * 2, line_height, @text, 1)
end
end
#==============================================================================
# ** Scene_Battle
#==============================================================================
class Scene_Battle
def start_party_command_selection
unless scene_changing?
@status_window.open
@status_window.refresh
if BattleManager.input_start
next_command
start_actor_command_selection
else
@party_command_window.deactivate
turn_start
end
end
end
def create_actor_command_window
@actor_command_window = Window_ActorCommand.new
@actor_command_window.set_handler(:attack, method(:command_attack))
@actor_command_window.set_handler(:skill, method(:command_skill))
@actor_command_window.set_handler(:guard, method(:command_guard))
@actor_command_window.set_handler(:item, method(:command_item))
@actor_command_window.set_handler(:escape, method(:command_escape))
@actor_command_window.set_handler(:cancel, method(:prior_command))
@actor_command_window.help_window = Window_ActorHelp.new
end
def create_help_window
@help_window = Window_Help.new(1)
@help_window.visible = false
end
alias coz_ebish_scbtl_create_enemy_window create_enemy_window
def create_enemy_window(*args)
coz_ebish_scbtl_create_enemy_window(*args)
@enemy_window.help_window = @actor_command_window.help_window
end
alias coz_ebish_scbtl_update_basic update_basic
def update_basic(*args)
old_enemy = @enemy_window.active ? @enemy_window.enemy : nil
coz_ebish_scbtl_update_basic(*args)
update_enemy_whiten(old_enemy)
end
def update_enemy_whiten(old_enemy)
if !@enemy_window.active or old_enemy != @enemy_window.enemy
old_enemy.sprite_effect_type = :select_white_to_normal if old_enemy && !old_enemy.dead?
end
@enemy_window.enemy.sprite_effect_type = :select_white if @enemy_window.active
end
def update_info_viewport
move_info_viewport(128)
end
end
Odometer Add-on:
#==============================================================================
# ** Earthbound-Ish Odometer Roll
#------------------------------------------------------------------------------
# Version: 1.0
# Author: cozziekuns
# Date: February 17, 2013
#==============================================================================
# Description:
#------------------------------------------------------------------------------
# This script attempts to emulate the battle system of the Earthbound/Mother
# series; most notably Earthbound and Earthbound 2 (Mother 2 and 3). This
# certain addon addresses the infamous HP/MP scrolling system that made battles
# that much more intense.
#==============================================================================
# Instructions:
#------------------------------------------------------------------------------
# Paste this script into its own slot in the Script Editor, above Main but
# below Materials. Edit the modules to your liking.
#==============================================================================
# Graphics:
#------------------------------------------------------------------------------
# You must have two Odometer pictures in your Graphics/System folder. One of
# them must be named "Odometer_HP", and the other one must be named
# "Odometer_MP". Obviously, they represent the odometer for HP and MP values,
# respectively
#==============================================================================
#==============================================================================
# ** Cozziekuns
#==============================================================================
module Cozziekuns
module Earthboundish
Odometer_Roll_Speed = 4 # Smaller speeds are faster than larger speeds.
end
end
include Cozziekuns
#==============================================================================
# ** Game_Actor
#==============================================================================
class Game_Actor
attr_accessor :odometer_hp
attr_accessor :odometer_mp
alias coz_ebisohd_gmactr_setup setup
def setup(actor_id, *args)
coz_ebisohd_gmactr_setup(actor_id, *args)
@odometer_hp = 0
@odometer_mp = 0
end
alias coz_ebishod_gmactr_execute_damage execute_damage
def execute_damage(user, *args)
if $game_party.in_battle
on_damage(@result.hp_damage) if @result.hp_damage > 0
@odometer_hp += @result.hp_damage
@odometer_mp += @result.mp_damage
user.hp += @result.hp_drain
user.mp += @result.mp_drain
else
coz_ebishod_gmactr_execute_damage(user, *args)
end
end
[:hp, :mp].each { |stat|
alias_method("coz_ebishod_gmactr_item_effect_recover_#{stat}".to_sym, "item_effect_recover_#{stat}".to_sym)
define_method("item_effect_recover_#{stat}".to_sym) { |user, item, effect|
if $game_party.in_battle
value = (send("m#{stat}".to_sym) * effect.value1 + effect.value2) * rec
value *= user.pha if item.is_a?(RPG::Item)
value = value.to_i
@result.send("#{stat}_damage=".to_sym, @result.send("#{stat}_damage".to_sym) - value)
@result.success = true
send("odometer_#{stat}=".to_sym, send("odometer_#{stat}".to_sym) - value)
else
send("coz_ebishod_gmactr_item_effect_recover_#{stat}".to_sym, user, item, effect)
end
}
}
end
#==============================================================================
# ** Game_Enemy
#==============================================================================
class Game_Enemy
def execute_damage(user)
on_damage(@result.hp_damage) if @result.hp_damage > 0
self.hp -= @result.hp_damage
self.mp -= @result.mp_damage
user.odometer_hp -= @result.hp_drain
user.odometer_mp -= @result.mp_drain
end
end
#==============================================================================
# ** Window_BattleStatus
#==============================================================================
class Window_BattleStatus
def refresh_hpmp(actor, index)
rect = item_rect(index)
if gauge_area_rect(index) == item_rect(index)
rect.y += line_height * 2
rect.height -= line_height * 2
contents.clear_rect(rect)
else
contents.clear_rect(gauge_area_rect(index))
end
draw_gauge_area(gauge_area_rect(index), actor)
end
[:hp, :mp].each { |stat|
define_method("draw_actor_#{stat}".to_sym) { |actor, x, y, width|
change_color(system_color)
draw_text(x, y, 30, line_height, Vocab.send("#{stat}_a"))
od_x = x + contents.text_size(Vocab.send("#{stat}_a")).width + 4
actor_hp = actor.send("#{stat}".to_sym)
actor_od_hp = actor.send("odometer_#{stat}".to_sym)
draw_odometer(od_x, y, stat, actor_hp, actor_od_hp)
}
}
def draw_odometer(x, y, type, value, od_value)
bitmap = Cache.system("Odometer_#{type.upcase}")
places = [1000, 100, 10, 1]
od_ary = value.to_s.split("").collect { |str| str.to_i }
(4 - od_ary.size).times { od_ary.unshift(0) }
od_ary.each_index { |i|
src_y = (9 - od_ary[i]) * 20
if (od_ary.join.to_i) % places[i] == 0 and od_value != 0
src_y += 20 / Earthboundish::Odometer_Roll_Speed * (Graphics.frame_count % Earthboundish::Odometer_Roll_Speed)
end
contents.blt(x + i * 24, y + 2, bitmap, Rect.new(0, src_y, 24, 20))
}
end
end
#==============================================================================
# ** Scene_Battle
#==============================================================================
class Scene_Battle
alias coz_ebishod_scbtl_update_basic update_basic
def update_basic(*args)
coz_ebishod_scbtl_update_basic(*args)
update_odometer
end
def update_odometer
$game_party.members.each { |actor|
if actor.odometer_hp != 0 or actor.odometer_mp != 0
if actor.odometer_hp != 0 and Graphics.frame_count % Earthboundish::Odometer_Roll_Speed == 0
damage = actor.odometer_hp > 0 ? 1 : - 1
actor.hp -= damage
actor.odometer_hp -= damage
end
if actor.odometer_mp != 0 and Graphics.frame_count % Earthboundish::Odometer_Roll_Speed == 0
damage = actor.odometer_mp > 0 ? 1 : - 1
actor.mp -= damage
actor.odometer_mp -= damage
end
@status_window.refresh_hpmp(actor, actor.index)
end
}
end
end
Mother 3 Sprite Display Add-on:
#==============================================================================
# ** Earthbound-Ish Sprite Display
#------------------------------------------------------------------------------
# Version: 1.0
# Author: cozziekuns
# Date: February 17, 2013
#==============================================================================
# Description:
#------------------------------------------------------------------------------
# This script attempts to emulate the battle system of the Earthbound/Mother
# series; most notably Earthbound and Earthbound 2 (Mother 2 and 3). This
# certain addon addresses the character sprite movements found in Mother 3,
# where active actors bobbed up and down the screen.
#==============================================================================
# Instructions:
#------------------------------------------------------------------------------
# Paste this script into its own slot in the Script Editor, above Main but
# below Materials.
#==============================================================================
#==============================================================================
# ** BattleManager
#==============================================================================
class << BattleManager
alias coz_ebishspd_btlmngr_init_members init_members
def init_members(*args)
coz_ebishspd_btlmngr_init_members(*args)
@dummy_battler = nil
end
def dummy_battler
@dummy_battler
end
def next_subject
loop do
battler = @action_battlers.shift
unless battler
@dummy_battler = nil
return nil
end
next unless battler.index && battler.alive?
@dummy_battler = battler
return battler
end
end
end
#==============================================================================
# ** Game_Actor
#==============================================================================
class Game_Actor
def use_sprite?
true
end
end
#==============================================================================
# ** Sprite_BattlerCharacter
#==============================================================================
class Sprite_BattlerCharacter < Sprite_Battler
def initialize(viewport, battler = nil)
super(viewport, battler)
@y_offset = 0
end
def make_animation_sprites
@ani_sprites = []
@ani_duplicated = @@ani_checker.include?(@animation)
if !@ani_duplicated && @animation.position == 3
@@ani_checker.push(@animation)
end
end
def update
super
update_position
update_zoom
update_y_offset
end
def update_bitmap
new_bitmap = set_character_bitmap
if bitmap != new_bitmap
self.bitmap = new_bitmap
init_visibility
end
update_src_rect
end
def set_character_bitmap
bitmap = Cache.character(@battler.character_name)
sign = @battler.character_name[/^[\!\$]./]
if sign && sign.include?('$')
@cw = bitmap.width / 3
@ch = bitmap.height / 4
else
@cw = bitmap.width / 12
@ch = bitmap.height / 8
end
self.ox = @cw / 2
self.oy = @ch
bitmap
end
def update_origin
if bitmap
self.ox = @cw / 2
self.oy = 0
end
end
def update_src_rect
index = @battler.character_index
sx = (index % 4 * 3 + 1) * @cw
sy = (index / 4 * 4) * @ch
self.src_rect.set(sx, sy, @cw, @y_offset)
end
def update_position
width = Graphics.width / ( 4 / $game_party.members.size.to_f )
sx = (Graphics.width - width) / 2 + 128
self.x = @battler.index * 128 + sx / 2 + 12 + 32 * (4 - $game_party.members.size)
self.y = 296 - @y_offset * 2
end
def update_zoom
self.zoom_x = 2.0
self.zoom_y = 2.0
end
def update_y_offset
if BattleManager.actor == @battler or BattleManager.dummy_battler == @battler
@y_offset = [@y_offset + 4, 32].min
else
@y_offset = [@y_offset - 4, 0].max
end
end
end
#==============================================================================
# ** Spriteset_Battle
#==============================================================================
class Spriteset_Battle
def create_actors
@actor_sprites = $game_party.members.collect { |actor|
Sprite_BattlerCharacter.new(@viewport3, actor)
}
end
end
Credit
Thanks
- Earthbound, for being such a great game.
Support
Post below and I'll try to be prompt in my response.
Known Compatibility Issues
Please don't try and combine this with other battle systems.
Author's Notes
This was a long time coming.