=begin ============================================================================== ** Battle Summons Author: Hime Date: Jul 10, 2012 ------------------------------------------------------------------------------ ** Change log Jul 10 -added summon_only_battle to game system as a flag Jun 24 -fixed enemy summoning: only refresh newly summoned sprites Jun 12 -assign specific summons to items/skills -summon ID is now the same as the actor ID -added remove summon functionality. May 30 -summon limits and type limits added -summons recover all after battle -summon implemented as a skill -added Command Setup compatibility -added enemy summons -summon equip menu added May 20 -option to remove all actors when summon appears -actors will return when the summon disappears May 19 -Created summon scene/window -added summon command -summons are now treated as actors and level accordingly May 18 -Initial release ------------------------------------------------------------------------------ Summon battlers to fight for you! In the configuration, designate skills that should be treated as "summon skills". Then set up the skill in the database. Enemies can also be set up to use the summon skill. They will need a list of enemy ID's to choose from. Tag enemies with the enemy ID's they should summon Then give them the summon skill in their action list To add a summon to your party, use the script call $game_party.add_summon(summon_id) To remove a summon from your party, $game_party.remove_summon(summon_id) To force summon a party to battle, use the call $game_summons.summon(id) ============================================================================== =end $imported = {} if $imported.nil? $imported["Tsuki_BattleSummon"] = true #============================================================================== # ** Configuration. #============================================================================== module Tsuki module Battle_Summon # Skill IDs that are treated as summon skills Summon_Skill = [135] Summon_Item = [17, 18] # variable used to determine the number of turns summoned Default_Summon_Turns = 5 Summon_Turn_Variable = 1 Summon_Limit = 4 # How many summons you can have in battle Summon_Type_Limit = 2 # How many different summons you can have in battle # Messages to display on successful summon in battle # format [user name], [summon name] Summon_Message = "%s has called %s to battle!" # Should the summon command be shown in battle? Enable_Summon_Command = false # party members leave when a summon appears Summon_Only_Battle = true # Monster Hunter add-on requires that script. It adds all captured monsters # to your summon list rather than the party list. Monster_Hunter_Addon = false #============================================================================== # ** Rest of the Script #============================================================================== def self.summon_effect?(item) return Summon_Skill.include?(item.id) if item.is_a?(RPG::Skill) return Summon_Item.include?(item.id) if item.is_a?(RPG::Item) return false end Enemy_Summon_Regex = //i Summon_Limit_Regex = //i Summon_ID_Regex = //i end end module RPG class Actor < BaseItem def summon_limit return @summon_limit unless @summon_limit.nil? res = Tsuki::Battle_Summon::Summon_Limit_Regex.match(self.note) return @summon_limit = res ? res[1].to_i : 99 end end class Enemy < BaseItem def enemy_summons return @enemy_summon unless @enemy_summon.nil? return @enemy_summon = load_notetags_enemy_summons end def load_notetags_enemy_summons summons = [] self.note.split(/[\r\n]+/).each do |line| case line when Tsuki::Battle_Summon::Enemy_Summon_Regex $1.scan(/\d+/).each {|id| summons << id.to_i} end end return summons end end class UsableItem < BaseItem def summon_id return @summon_id unless @summon_id.nil? res = Tsuki::Battle_Summon::Summon_ID_Regex.match(self.note) return @summon_id = res ? res[1].to_i : 0 end end end module DataManager class << self alias :tsuki_battle_summon_create_objects :create_game_objects alias :tsuki_battle_summon_make_save_contents :make_save_contents alias :tsuki_battle_summon_extract_save_contents :extract_save_contents end def self.create_game_objects tsuki_battle_summon_create_objects $game_summons = Game_Summons.new end def self.make_save_contents contents = tsuki_battle_summon_make_save_contents contents[:summons] = $game_summons contents end def self.extract_save_contents(contents) tsuki_battle_summon_extract_save_contents(contents) $game_summons = contents[:summons] end end module BattleManager class << self alias :tsuki_battle_summon_turn_end :turn_end alias :tsuki_battle_summon_gain_exp :gain_exp alias :tsuki_battle_summon_battle_end :battle_end end def self.turn_end tsuki_battle_summon_turn_end $game_summons.update_summons end def self.gain_exp tsuki_battle_summon_gain_exp $game_summons.battle_members.each do |summon| summon.gain_exp($game_troop.exp_total) end wait_for_message end def self.battle_end(result) tsuki_battle_summon_battle_end(result) $game_summons.on_battle_end end end class Spriteset_Battle def refresh_enemies new_enemies = $game_troop.members.reverse.collect do |enemy| @enemy_sprites << Sprite_Battler.new(@viewport1, enemy) if enemy.summoned? end end end class Game_System attr_accessor :summon_only_battle alias :th_battle_summon_init_system :initialize def initialize th_battle_summon_init_system @summon_only_battle = Tsuki::Battle_Summon::Summon_Only_Battle end end class Game_Action attr_accessor :summon_id #id of summon to call alias :th_battle_summon_clear_action :clear def clear th_battle_summon_clear_action @summon_id = 0 end end class Game_ActionResult attr_accessor :called_summon alias :th_battle_summon_clear_results :clear def clear th_battle_summon_clear_results clear_summon_result end def clear_summon_result @called_summon = nil end def called_summon_text if @called_summon fmt = Tsuki::Battle_Summon::Summon_Message sprintf(fmt, @battler.name, @called_summon.name) else "" end end end class Game_Battler < Game_BattlerBase def is_summon_action?(item) return false unless item Tsuki::Battle_Summon.summon_effect?(item) end alias :th_summon_battler_item_apply :item_apply def item_apply(user, item) th_summon_battler_item_apply(user, item) summon_battler(user, item) if is_summon_action?(item) end def summon_battler(user, item) called_summon = user.friends_unit.summon_battler(user.actions[0].summon_id) if called_summon @result.success = true @result.called_summon = called_summon end end end class Game_Enemy < Game_Battler attr_accessor :just_summoned alias :th_battle_summon_init_enemy :initialize def initialize(index, enemy_id) th_battle_summon_init_enemy(index, enemy_id) @just_summoned = false init_summons end def summoned? if @just_summoned @just_summoned = false return true end return false end def init_summons @summons = enemy.enemy_summons end alias :th_battle_summon_make_enemy_actions :make_actions def make_actions th_battle_summon_make_enemy_actions @actions.each do |action| make_summon_action(action) if is_summon_action?(action.item) end end # pick a random target to draw a random magic from def make_summon_action(action) action.summon_id = @summons.sample end end class Game_Summon < Game_Actor attr_reader :summon_id attr_accessor :summon_turns attr_accessor :summon_index def initialize(actor_id) super(actor_id) @summon_id = actor_id @summon_turns = 0 @summon_index = 0 # 0 if not summoned @dupe = false end def can_dupe? @dupe end def update_summon @summon_turns -= 1 if @summon_turns <= 0 $game_summons.unsummon(@summon_id) end end def die $game_summons.unsummon(@summon_id) super end def index $game_summons.members.index(self) end end class Game_Summons < Game_Unit include Tsuki::Battle_Summon def initialize @data = [nil] @ids = [nil] @dispatched = [] @summon_count = 0 #how many summons are on the field @limit = Summon_Limit @type_limit = Summon_Type_Limit @menu_actor_id = 0 end def [](summon_id) @data[summon_id] ||= Game_Summon.new(summon_id) end def members in_battle ? battle_members : all_summons end def all_summons @data.select {|summon| !summon.nil?} end # summons that are currently in battle def battle_members @dispatched.collect {|id| @data[id]} end def summon_actor(id) return if @ids.include?(id) summon = Game_Summon.new(id) @data[id] = summon @ids << id end def unsummon(id) return unless @dispatched.include?(id) @dispatched.delete_at(@data[id].summon_index - 1) @data[id].summon_index = 0 @summon_count -= 1 end def summon(id) return 0 unless @data[id] @data[id].summon_turns = [Default_Summon_Turns, $game_variables[Summon_Turn_Variable]].max @data[id].make_actions @summon_count += 1 @data[id].summon_index = @summon_count @dispatched << id return id end def can_summon?(summon_id) return false if @data[summon_id].nil? return false if @summon_count >= @limit || @dispatched.uniq.size >= @type_limit return false if @dispatched.include?(summon_id) && !@data[summon_id].can_dupe? return true end def update_summons members.each {|summon| summon.update_summon} end def on_battle_end alive_members.each {|summon| summon.recover_all } @dispatched = [] @summon_count = 0 end def menu_actor @data[@menu_actor_id] || members[0] end def menu_actor=(summon) return unless summon @menu_actor_id = summon.summon_id end def menu_actor_next index = members.index(menu_actor) || -1 index = (index + 1) % members.size self.menu_actor = members[index] end def menu_actor_prev index = members.index(menu_actor) || 1 index = (index + members.size - 1) % members.size self.menu_actor = members[index] end end class Game_Party < Game_Unit include Tsuki::Battle_Summon alias tsuki_battle_summon_initialize_party initialize def initialize tsuki_battle_summon_initialize_party @summons = [] end alias tsuki_battle_summon_battle_members battle_members def battle_members summons = $game_summons.battle_members if $game_system.summon_only_battle summons.empty? ? tsuki_battle_summon_battle_members : summons else members = tsuki_battle_summon_battle_members members.concat(summons) end end def summons @summons.collect {|id| $game_summons[id]} end # try to call a summon, return summoned_id if successful def summon_battler(id) return unless $game_summons.can_summon?(id) summoned_id = $game_summons.summon(id) return $game_summons[summoned_id] end def add_summon(summon_id) $game_summons.summon_actor(summon_id) @summons.push(summon_id) unless @summons.include?(summon_id) end def remove_summon(summon_id) @summons.delete(summon_id) end def clear_summons @summons = [] end def on_battle_end super clear_summons end end class Game_Troop < Game_Unit # create a new enemy on the spot def summon_battler(enemy_id) return unless $data_enemies[enemy_id] new_enemy = Game_Enemy.new(@enemies.size, enemy_id) new_enemy.just_summoned = true new_enemy.screen_x = rand(Graphics.width / 2) + 100 new_enemy.screen_y = rand(Graphics.height / 2) + 100 @enemies.push(new_enemy) SceneManager.scene.refresh_enemies return new_enemy end end class Game_Interpreter def add_actor_summon(actor_id) $game_party.add_summon(actor_id) end def summon_battler(summon_id) $game_party.summon_battler(summon_id) end end #============================================================================== # ** Menu windows and scenes #============================================================================== class Window_SummonList < Window_Selectable #-------------------------------------------------------------------------- # * Object Initialization #-------------------------------------------------------------------------- def initialize(x, y, width, height) super @actor = nil @data = [] refresh end #-------------------------------------------------------------------------- # * Set Category #-------------------------------------------------------------------------- def actor=(actor) return if @actor == actor @actor = actor refresh self.oy = 0 end #-------------------------------------------------------------------------- # * Get Digit Count #-------------------------------------------------------------------------- def col_max return 2 end def item_height 104 end #-------------------------------------------------------------------------- # * Get Number of Items #-------------------------------------------------------------------------- def item_max @data ? @data.size : 1 end #-------------------------------------------------------------------------- # * Get Item #-------------------------------------------------------------------------- def item @data && index >= 0 ? @data[index] : nil end #-------------------------------------------------------------------------- # * Get Activation State of Selection Item #-------------------------------------------------------------------------- def current_item_enabled? enable?(@data[index]) end def enable?(summon) summon && !$game_summons.battle_members.include?(summon) end def include?(summon) return false unless summon return true end def select_last select(0) end def make_item_list @data = $game_party.summons.select {|item| include?(item) } @data.push(nil) if include?(nil) end def draw_item(index) summon = @data[index] if summon rect = item_rect(index) rect.width -= 4 draw_summon_basic(summon, rect.x + 4, rect.y, enable?(summon)) draw_actor_face(summon, rect.x + rect.width - 96, rect.y) end end def draw_summon_name(summon, x, y, enabled, width = 112) change_color(normal_color, enabled) draw_text(x, y, width, line_height, summon.name) end def draw_summon_basic(summon, x, y, enabled=true, width=172) return unless summon text_width = text_size(summon.name).width draw_summon_name(summon, x, y + line_height*0, enabled) draw_actor_level(summon, x, y + line_height*1) draw_actor_hp(summon, x, y + line_height*2) draw_actor_mp(summon, x, y + line_height*3) end def update_help @help_window.set_item(item) end def refresh make_item_list create_contents draw_all_items end end class Window_SummonCommand < Window_HorzCommand def window_width Graphics.width end def make_command_list add_command("Equip", :equip) add_command("Skill", :skill) add_command("Status", :status) end end class Scene_Summons < Scene_MenuBase def start super create_summon_command create_summon_window end def create_summon_command @summon_command = Window_SummonCommand.new(0, 0) @summon_command.set_handler(:cancel, method(:return_scene)) @summon_command.set_handler(:equip, method(:command_personal)) @summon_command.set_handler(:skill, method(:command_personal)) @summon_command.set_handler(:status, method(:command_personal)) end def create_summon_window wy = @summon_command.height width = Graphics.width height = Graphics.height - wy @summon_window = Window_SummonList.new(0, wy, width, height) end def command_personal @summon_window.select_last @summon_window.activate @summon_window.set_handler(:ok, method(:on_personal_ok)) @summon_window.set_handler(:cancel, method(:on_personal_cancel)) end def on_personal_ok $game_summons.menu_actor = @summon_window.item case @summon_command.current_symbol when :equip SceneManager.call(Scene_SummonEquip) when :skill SceneManager.call(Scene_SummonSkill) when :status SceneManager.call(Scene_SummonStatus) end end def on_personal_cancel @summon_window.unselect @summon_command.activate end end class Scene_SummonSkill < Scene_Skill def start super @actor = $game_summons.menu_actor on_actor_change end def next_actor @actor = $game_summons.menu_actor_next on_actor_change end def prev_actor @actor = $game_summons.menu_actor_prev on_actor_change end end class Scene_SummonEquip < Scene_Equip def start super @actor = $game_summons.menu_actor on_actor_change end def next_actor @actor = $game_summons.menu_actor_next on_actor_change end def prev_actor @actor = $game_summons.menu_actor_prev on_actor_change end end class Scene_SummonStatus < Scene_Status def start super @actor = $game_summons.menu_actor on_actor_change end def next_actor @actor = $game_summons.menu_actor_next on_actor_change end def prev_actor @actor = $game_summons.menu_actor_prev on_actor_change end end #============================================================================== # ** Battle windows and scenes #============================================================================== class Window_BattleSummon < Window_SummonList def initialize(help_window, info_viewport) y = 0 super(0, y, Graphics.width, info_viewport.rect.y - y) self.visible = false @help_window = help_window @info_viewport = info_viewport select(0) end def enable?(summon) summon && $game_summons.can_summon?(summon.summon_id) end end class Window_BattleStatus < Window_Selectable def content_height $game_party.battle_members.size * line_height - standard_padding * 2 end alias tsuki_battle_summon_status_refresh refresh def refresh create_contents tsuki_battle_summon_status_refresh end end class Scene_Battle < Scene_Base def refresh_enemies @spriteset.refresh_enemies end alias :battle_summon_create_battle_windows :create_all_windows def create_all_windows battle_summon_create_battle_windows create_summon_window end def create_summon_window @summon_window = Window_BattleSummon.new(@help_window, @info_viewport) @summon_window.set_handler(:ok, method(:on_summon_ok)) @summon_window.set_handler(:cancel, method(:on_summon_cancel)) end alias :th_battle_summon_on_skill_ok :on_skill_ok def on_skill_ok @skill = @skill_window.item if Tsuki::Battle_Summon.summon_effect?(@skill) BattleManager.actor.input.set_skill(@skill.id) BattleManager.actor.last_skill.object = @skill @skill_window.hide if @skill.summon_id > 0 BattleManager.actor.input.summon_id = @skill.summon_id next_command else activate_summon_window end else th_battle_summon_on_skill_ok end end alias :th_battle_summon_on_item_ok :on_item_ok def on_item_ok @item = @item_window.item if Tsuki::Battle_Summon.summon_effect?(@item) BattleManager.actor.input.set_item(@item.id) $game_party.last_item.object = @item @item_window.hide if @item.summon_id > 0 BattleManager.actor.input.summon_id = @skill.summon_id next_command else activate_summon_window end else th_battle_summon_on_item_ok end end def command_summon activate_summon_window end def on_summon_ok BattleManager.actor.input.summon_id = @summon_window.item.summon_id @summon_window.hide next_command end def on_summon_cancel @summon_window.hide @skill_window.show.activate if @skill @item_window.show.activate if @item end def activate_summon_window @summon_window.refresh @summon_window.show.activate end end class Window_BattleLog < Window_Selectable alias :th_battle_summon_display_damage :display_damage def display_damage(target, item) th_battle_summon_display_damage(target, item) display_summon_results(target, item) end def display_summon_results(target, item) return unless target.result.called_summon && Tsuki::Battle_Summon.summon_effect?(item) add_text(target.result.called_summon_text) wait end end #============================================================================== # ** Update menu scene to add option #============================================================================== class Window_MenuCommand < Window_Command #-------------------------------------------------------------------------- # * Add arena command to List #-------------------------------------------------------------------------- alias tsuki_summons_commands add_main_commands def add_main_commands tsuki_summons_commands add_command("Summons", :summons) end end class Scene_Menu < Scene_MenuBase #-------------------------------------------------------------------------- # * Add arena handler #-------------------------------------------------------------------------- alias tsuki_summons_command_window create_command_window def create_command_window tsuki_summons_command_window @command_window.set_handler(:summons, method(:command_summons)) end def command_summons SceneManager.call(Scene_Summons) end end #============================================================================== # ** Compatibility mods #============================================================================== if $imported["FP_InventoryPlus"] class Scene_Battle < Scene_Base # directly use item from inventory def on_item_ok @item = @item_window.item if Tsuki::Battle_Summon.summon_effect?(@item) BattleManager.actor.input.set_item(@item) $game_party.last_item.object = @item @item_window.hide if @item.summon_id > 0 BattleManager.actor.input.summon_id = @item.summon_id next_command else activate_summon_window end else th_battle_summon_on_item_ok end end end end if $imported["Tsuki_CommandSetup"] class Scene_Battle < Scene_Base alias :th_battle_summon_command_use_skill :command_use_skill def command_use_skill @skill = $data_skills[@actor_command_window.current_data[:ext]] if Tsuki::Battle_Summon.summon_effect?(@skill) BattleManager.actor.input.set_skill(@skill.id) BattleManager.actor.last_skill.object = @skill @skill_window.hide if @skill.summon_id > 0 BattleManager.actor.input.summon_id = @skill.summon_id next_command else activate_summon_window end else th_battle_summon_command_use_skill end end def on_summon_cancel @enemy_draw_window.hide if @skill == $data_skills[@actor_command_window.current_data[:ext]] @actor_command_window.activate else @summon_window.hide @skill_window.show.activate end end end end #============================================================================== # ** Add-ons #============================================================================== if Tsuki::Battle_Summon::Monster_Hunter_Addon if $imported["Tsuki_MonsterHunter"] module Monster_Hunter def self.create_game_actor(actor_id) return if $BTEST $game_summons.summon_actor(actor_id) end end else msgbox("Monster Hunter must placed above this script") end end