kirip 3 Posted April 20, 2015 (edited) Shared HP Battle System Hello everyone, this is my first script that I'm working on, still learning the ropes but I think I have somewhat of a grasp with Ruby. Feel free to laugh at my terrible code and give me pointers on how to improve upon it.This system is functionally identical to the default battle system, but the difference is, everyone's HP is pooled together and when any member heals or takes damage, only the pool is affected.Another thing to note is that this system adds to the default menu screen to show the party's current total HP.This is still a work-in-progress so please don't mind the messiness. I actually would love input on how to improve upon updating the GUI to reflect this system.Functionally, there shouldn't be any bugs, but please let me know if you run into any. As far as I'm concerned, this is functionally complete. All these are posted above Main. UPDATE (4/29) Just cleaned up some code for potential compatibility with other scripts in the future. Also this is now compatible with Neo Gauge Ultimate Ace! Shared_HP #Values for Shared HP in Game_Party class class Game_Party < Game_Unit alias :partyhealth :initialize attr_reader :partyhp attr_reader :cappedhp def initalize partyhealth @partyhp = 0 @cappedhp = 0 end def pthealth(hp) @partyhp = hp end def cappedhealth(maxhp) @cappedhp = maxhp end def init_sharedHP partyhp = 0 $game_party.battle_members.each do |actor| partyhp = partyhp + actor.mhp end $game_party.pthealth(partyhp) $game_party.cappedhealth(partyhp) end #-------------------------------------------------------------------------- # * Add an Actor #-------------------------------------------------------------------------- def add_actor(actor_id) # affectedhp is the variable that stores the added actor's HP. #-------------------------------------------------------------------------- affectedhp = 0 is_dupe = @actors.include?(actor_id) @actors.push(actor_id) unless @actors.include?(actor_id) if (in_battle && !is_dupe) $game_party.battle_members.each do |actor| affectedhp = actor.hp if actor_id == actor.id end $game_party.cappedhealth($game_party.cappedhp + affectedhp) $game_party.pthealth($game_party.cappedhp) if $game_party.cappedhp < $game_party.partyhp end $game_player.refresh $game_map.need_refresh = true end #-------------------------------------------------------------------------- # * Remove Actor #-------------------------------------------------------------------------- def remove_actor(actor_id) affectedhp = 0 if (in_battle) # This checks for the removed actor to get their HP value. #-------------------------------------------------------------------------- $game_party.battle_members.each do |actor| affectedhp = actor.hp if actor_id == actor.id end $game_party.cappedhealth($game_party.cappedhp - affectedhp) $game_party.pthealth($game_party.cappedhp) if $game_party.cappedhp < $game_party.partyhp end @actors.delete(actor_id) $game_player.refresh $game_map.need_refresh = true end end module BattleManager #-------------------------------------------------------------------------- # * Determine Win/Loss Results #-------------------------------------------------------------------------- def self.judge_win_loss if @phase return process_abort if $game_party.members.empty? return process_defeat if $game_party.partyhp < 1 return process_victory if $game_troop.all_dead? return process_abort if aborting? end return false end class << self alias :sharedhpsetup :setup end #-------------------------------------------------------------------------- # * Setup shared HP #-------------------------------------------------------------------------- def self.setup(troop_id, can_escape = true, can_lose = false) sharedhpsetup(troop_id, can_escape, can_lose) $game_party.init_sharedHP end end class Game_Battler < Game_BattlerBase #------------------------------------------------------------------------------- # Modified so that total HP is affected instead of actor HP when taking damage. #------------------------------------------------------------------------------- def execute_damage(user) on_damage(@result.hp_damage) if @result.hp_damage > 0 self.mp -= @result.mp_damage # When actor takes damage, subtract it from pool of HP instead. #------------------------------------------------------------------------------- if (actor?) $game_party.pthealth($game_party.partyhp - @result.hp_damage) $game_party.partyhp($game_party.cappedhp) if $game_party.partyhp > $game_party.cappedhp $game_party.pthealth(0) if $game_party.partyhp < 1 else # Normal operation #------------------------------------------------------------------------------- self.hp -= @result.hp_damage end user.hp += @result.hp_drain user.mp += @result.mp_drain end #------------------------------------------------------------------------------- # Modifies so that total HP is affected instead of actor HP when healing damage. #------------------------------------------------------------------------------- def item_effect_recover_hp(user, item, effect) value = (mhp * effect.value1 + effect.value2) * rec value *= user.pha if item.is_a?(RPG::Item) value = value.to_i @result.hp_damage -= value @result.success = true # When actor is healed, add it to pool of HP instead. #------------------------------------------------------------------------------- if (actor?) $game_party.pthealth($game_party.partyhp + value) $game_party.pthealth($game_party.cappedhp) if $game_party.partyhp > $game_party.cappedhp # Normal operation #------------------------------------------------------------------------------- else self.hp += value end end end class Game_BattlerBase #------------------------------------------------------------------------------- # Modifies change HP to only affect sharedHP # TODO: Make it so entire party HP is only applied once #------------------------------------------------------------------------------- def change_hp(value, enable_death) if !enable_death && @hp + value <= 0 self.hp = 1 if enemy? $game_party.pthealth(1) if actor? else self.hp += value if enemy? $game_party.pthealth($game_party.partyhp + value) if actor? end end end Shared_HP_ValueDisplay #Shared_HP_ValueDisplagy class Window_Base < Window #-------------------------------------------------------------------------- # * Draw HP #-------------------------------------------------------------------------- def draw_actor_hp(actor, x, y, width = 124) change_color(system_color) draw_text(x, y, 30, line_height, Vocab::hp_a) change_color(normal_color) draw_text(x + 30, y, 42, line_height, actor.mhp, 2) end end module SharedHP GAUGE_NAME = "HP" MENU_HP_NAME = "TEAM HP" HPMAXSLASH = "/" end class Window_Party_HP < Window_Selectable alias :party_hp_display :draw_text_ex #-------------------------------------------------------------------------- # * Object Initialization #-------------------------------------------------------------------------- #--- # Sets location of the shared HP Window #--- def initialize super(0, 0, window_width, window_height) self.opacity = 0 self.y = Graphics.height - fitting_height(6) + 14 refresh end #-------------------------------------------------------------------------- # * Get Window Width #-------------------------------------------------------------------------- def window_width return Graphics.width end #-------------------------------------------------------------------------- # * Window Height #-------------------------------------------------------------------------- def window_height return fitting_height(1) end #-------------------------------------------------------------------------- # * Rate #-------------------------------------------------------------------------- def rate return $game_party.partyhp.to_f / $game_party.cappedhp end #-------------------------------------------------------------------------- # * Gauge colors #-------------------------------------------------------------------------- def gaugecolor return text_color(10) if rate <= 0.2 return text_color(6) if rate <= 0.4 return text_color(1) if rate <= 0.6 return text_color(4) if rate < 1 return text_color(3) if rate >= 1 end def draw_text_exHP(x, y, text) reset_font_settings change_color(gaugecolor) pos = {:x => x, :y => y, :new_x => x, :height => calc_line_height(text)} process_character(text.slice!(0, 1), text, pos) until text.empty? end #-------------------------------------------------------------------------- # * Refresh #-------------------------------------------------------------------------- def refresh contents.clear #-------------------------------------------------------------------------- # * Gets max HP and HP values to display. #-------------------------------------------------------------------------- hpcheck = $game_party.partyhp.to_s maxhpcheck = SharedHP::HPMAXSLASH + $game_party.cappedhp.to_s #-------------------------------------------------------------------------- # * Handles position of the displayed HP values. #-------------------------------------------------------------------------- repositionhp = self.text_size(hpcheck) repositionmaxhp = self.text_size(maxhpcheck) #-------------------------------------------------------------------------- # * Takes the width value of the window, then subtracts it by the width # of the max HP text to display it at the edge of the gauge properly. #-------------------------------------------------------------------------- maxHpPos = item_width - repositionmaxhp.width currentHpPos = maxHpPos - repositionhp.width draw_gauge(0, 0, window_width, rate, gaugecolor, gaugecolor) #-------------------------------------------------------------------------- # * Draws the HP values. #-------------------------------------------------------------------------- draw_text_ex(0, 0, SharedHP::GAUGE_NAME) draw_text_exHP(maxHpPos, 0, maxhpcheck) draw_text_exHP(currentHpPos, 0, hpcheck) end #-------------------------------------------------------------------------- # * Open Window #-------------------------------------------------------------------------- def open refresh super end end #-------------------------------------------------------------------------- # * Calls the window for the shared HP bar. #-------------------------------------------------------------------------- class Scene_Battle < Scene_Base #-------------------------------------------------------------------------- # * Aliased Methods #-------------------------------------------------------------------------- alias :party_hp_display :start alias :party_hp_update :refresh_status #-------------------------------------------------------------------------- # * Start Processing #-------------------------------------------------------------------------- def start party_hp_display display_party_hp end #-------------------------------------------------------------------------- # * Show party HP #-------------------------------------------------------------------------- def display_party_hp @hp_gauge = Window_Party_HP.new end def refresh_status party_hp_update @hp_gauge.refresh end end Shared_HP_MenuWindow #Shared_HP_ValueDisplay class Window_Base < Window #-------------------------------------------------------------------------- # * Draw HP #-------------------------------------------------------------------------- def draw_actor_hp(actor, x, y, width = 124) change_color(system_color) draw_text(x, y, 30, line_height, Vocab::hp_a) change_color(normal_color) draw_text(x + 30, y, 42, line_height, actor.mhp, 2) end end module SharedHP GAUGE_NAME = "HP" MENU_HP_NAME = "TEAM HP" HPMAXSLASH = "/" end class Window_Party_HP < Window_Base alias :party_hp_display :draw_text_ex #-------------------------------------------------------------------------- # * Object Initialization #-------------------------------------------------------------------------- #--- # Sets location of the shared HP Window #--- def initialize super(0, 0, window_width, window_height) self.opacity = 0 self.y = Graphics.height - fitting_height(6) + 14 refresh end #-------------------------------------------------------------------------- # * Get Window Width #-------------------------------------------------------------------------- def window_width return Graphics.width #return 400 end #-------------------------------------------------------------------------- # * Window Height #-------------------------------------------------------------------------- def window_height return fitting_height(1) end def item_width return window_width - 26 end #-------------------------------------------------------------------------- # * Rate #-------------------------------------------------------------------------- def rate return $game_party.partyhp.to_f / $game_party.cappedhp end #-------------------------------------------------------------------------- # * Gauge colors #-------------------------------------------------------------------------- def gaugecolor return text_color(10) if rate <= 0.2 return text_color(6) if rate <= 0.4 return text_color(1) if rate <= 0.6 return text_color(4) if rate < 1 return text_color(3) if rate >= 1 end def draw_text_exHP(x, y, text) reset_font_settings #---------------------------------------------------------------------- #If you want to change the color of the text, uncomment the line below. #---------------------------------------------------------------------- #change_color(gaugecolor) pos = {:x => x, :y => y, :new_x => x, :height => calc_line_height(text)} process_character(text.slice!(0, 1), text, pos) until text.empty? end def draw_hp_value #-------------------------------------------------------------------------- # * Gets max HP and HP values to display. #-------------------------------------------------------------------------- hpcheck = $game_party.partyhp.to_s maxhpcheck = SharedHP::HPMAXSLASH + $game_party.cappedhp.to_s #-------------------------------------------------------------------------- # * Handles position of the displayed HP values. #-------------------------------------------------------------------------- repositionhp = self.text_size(hpcheck) repositionmaxhp = self.text_size(maxhpcheck) #-------------------------------------------------------------------------- # * Takes the width value of the window, then subtracts it by the width # of the max HP text to display it at the edge of the gauge properly. #-------------------------------------------------------------------------- maxHpPos = item_width - repositionmaxhp.width currentHpPos = maxHpPos - repositionhp.width #-------------------------------------------------------------------------- # * Draws the HP values. #-------------------------------------------------------------------------- draw_text_ex(0, 0, SharedHP::GAUGE_NAME) draw_text_exHP(maxHpPos, 0, maxhpcheck) draw_text_exHP(currentHpPos, 0, hpcheck) end #-------------------------------------------------------------------------- # * Refresh #-------------------------------------------------------------------------- def refresh contents.clear draw_gauge(0, 0, window_width, rate, gaugecolor, gaugecolor) draw_hp_value end #-------------------------------------------------------------------------- # * Open Window #-------------------------------------------------------------------------- def open refresh super end end #-------------------------------------------------------------------------- # * Calls the window for the shared HP bar. #-------------------------------------------------------------------------- class Scene_Battle < Scene_Base #-------------------------------------------------------------------------- # * Aliased Methods #-------------------------------------------------------------------------- alias :party_hp_display :start alias :party_hp_update :refresh_status #-------------------------------------------------------------------------- # * Start Processing #-------------------------------------------------------------------------- def start party_hp_display display_party_hp end #-------------------------------------------------------------------------- # * Show party HP #-------------------------------------------------------------------------- def display_party_hp @hp_gauge = Window_Party_HP.new end def refresh_status party_hp_update @hp_gauge.refresh end end Also, for those that wish to use this with Yanfly's Ace Battle Engine, I've taken the liberty to make sure this is compatible with it. I find the GUI of the system PERFECT for this. Yanfly's Ace Battle Engine compatibility, instructions are in script #------------------------------------------------------------------------------------------------- # Shared_HP scripts go above YEA Ace Battle Engine, this script goes below YEA Ace Battle Engine. #------------------------------------------------------------------------------------------------- class Scene_Battle < Scene_Base #-------------------------------------------------------------------------- # alias method: execute_action #-------------------------------------------------------------------------- alias abe_shared_hp execute_action def execute_action abe_shared_hp refresh_status end #-------------------------------------------------------------------------- # overwrite method: refresh_status #-------------------------------------------------------------------------- def refresh_status party_hp_update @hp_gauge.refresh end end class Window_BattleStatus < Window_Selectable #-------------------------------------------------------------------------- # overwrite method: draw_actor_hp, HP doesn't show since you won't need that info #-------------------------------------------------------------------------- def draw_actor_hp(actor, dx, dy, width = 124) end NEO Gauge compatibility patch, instructions in script #------------------------------------------------------------------------------ # This script goes below the Shared_HP scripts, while NEO Gauge goes above. #------------------------------------------------------------------------------ class Window_Base < Window USE_SHARED_HP_GAUGE = true # Uses NEO GAUGE for to displayed SHARED_HP instead def draw_shared_hp(x, y, width) gwidth = width * $game_party.partyhp / $game_party.cappedhp cg = neo_gauge_back_color c1, c2, c3 = cg[0], cg[1], cg[2] draw_neo_gauge(x + HPMP_GAUGE_X_PLUS, y + line_height - 8 + HPMP_GAUGE_Y_PLUS, width, HPMP_GAUGE_HEIGHT, c1, c2, c3) (1..3).each {|i| eval("c#{i} = HP_GCOLOR_#{i}")} draw_neo_gauge(x + HPMP_GAUGE_X_PLUS, y + line_height - 8 + HPMP_GAUGE_Y_PLUS, gwidth, HPMP_GAUGE_HEIGHT, c1, c2, c3, false, false, width, 30) draw_hp_value end end $imported ||= {} $imported[:neo_gauge_ultimate] class Window_Party_HP < Window_Base if USE_SHARED_HP_GAUGE def refresh contents.clear draw_shared_hp(0, 0, window_width) end end end This is my first time posting a script, if I messed up in some way, please let me know. Edited April 29, 2015 by kirip 3 halrawk, TBWCS and Allusion reacted to this Share this post Link to post Share on other sites
+ Galdelonian 13 Posted April 21, 2015 So, could you do this for enemies? This would be nice for a multiple part enemy that you want to affect with states and attacks. For instance, the head of a hydra can be "cut" by a slashing attack and in two turns it would grow 2 more heads. This would up allow the add-in of one more hydra head, more damage, but less health, while also sustaining the idea that you're fighting one creature. --I am terribad at explaining things. Either way, good job. Share this post Link to post Share on other sites
Trihan 40 Posted April 21, 2015 Nice script kirip, especially for a first! Were you looking for pointers on cleaning up/improving the code at all, or are you happy enough with it as-is? There's nothing wrong with it per-se, but I've noticed a few places it could be optimised. Share this post Link to post Share on other sites
kirip 3 Posted April 21, 2015 @Galdelonian: I could give that a try at some point, the idea did come across my head at one point. @Trihan: I would love optimization/clean-up/improving tips! Share this post Link to post Share on other sites
+ TBWCS 951 Posted April 21, 2015 It is indeed a good concept, but calling it a battle system is a green apple for now. Is the font of the numbers denoting the value of the gauge really green? It could be confusing, so watch out for that part. As for your window, you should hook it up more directly to window_base rather than going to Window_Selectable, especially if there's no big reason for you to inherit it there. Overall though it is a good battle system and I am impressed that this is your first script. Good luck with your scripting Share this post Link to post Share on other sites
kirip 3 Posted April 21, 2015 It is indeed a good concept, but calling it a battle system is a green apple for now. Is the font of the numbers denoting the value of the gauge really green? It could be confusing, so watch out for that part. As for your window, you should hook it up more directly to window_base rather than going to Window_Selectable, especially if there's no big reason for you to inherit it there. Overall though it is a good battle system and I am impressed that this is your first script. Good luck with your scripting Thank you very much! I'll see what I do about the window for the gauge sometime. You do bring up a good point about the confusing colors, though. I was trying to go for the color matching the gauge to make it look nice. I was inspired by the gauge in the mobile game, Puzzle and Dragons, if anyone was curious. I just realized that their gauge is a static color while the font is based on the percentage of health you have left. I ended up linking both the gauge and font color to the same thing. Share this post Link to post Share on other sites
Trihan 40 Posted April 21, 2015 If I get a moment I'll have a run through it and see what optimisations I can suggest, then. Share this post Link to post Share on other sites
+ TBWCS 951 Posted April 21, 2015 Here are my thoughts of the things you should allow the user of your script should have, not that it must... Gauge Adjustment - adjust the size of the gauge x and y dimensions for the text of the gauge - a part where they can easily adjust where the whole text are shown (it could go with the gauge as well). It's not much but you can also research a bit and make an add on where you can change the width and height of the gauge or use a custom gauge. You know, these are little details, they sure are not something you should do, but just in case you want it to look better than it is now Share this post Link to post Share on other sites
JmT 0 Posted April 26, 2015 Is it compatible with the Neo Gauge? Share this post Link to post Share on other sites
kirip 3 Posted April 29, 2015 (edited) Is it compatible with the Neo Gauge? I just made it compatible now, lemme know if it works out well. @Galedelonian: I haven't forgotten about your request yet! Edited April 29, 2015 by kirip Share this post Link to post Share on other sites