From 32de437f865c8b3fb92ff9520647cc7e1bb0c9d0 Mon Sep 17 00:00:00 2001 From: Daanish Date: Mon, 30 Mar 2026 20:43:06 +0000 Subject: [PATCH] Implement health bar system: add HealthBar class for managing health, integrate health updates in HUDLayer, and modify enemy attack handling to reflect damage taken. --- game/character.rb | 31 +++++++++++++++++++----------- game/enemy/base.rb | 11 +++++++---- game/enemy/force.rb | 4 +++- game/hud/health.rb | 27 ++++++++++++++++++++++++++ game/hud/layer.rb | 5 ++++- game/weapons/blade_of_recursion.rb | 30 +++++++++++++++++++++++++++++ game/weapons/handler.rb | 12 ++++++++++-- game/weapons/lorentz_field.rb | 10 +++++++--- game/weapons/occams_razor.rb | 4 ++-- 9 files changed, 110 insertions(+), 24 deletions(-) create mode 100644 game/hud/health.rb diff --git a/game/character.rb b/game/character.rb index 0a242e0..9409472 100644 --- a/game/character.rb +++ b/game/character.rb @@ -35,7 +35,7 @@ class Character end @current_animation = :idle_front - @facing = :front + @facing = 0.0 $bus.on(:player_position) do next [@world_x, @world_y] @@ -44,6 +44,10 @@ class Character $bus.on(:collides?) do |rect| next collides?(rect) ? :character : nil end + + $bus.on(:player_direction) do + @facing + end end def rect @@ -56,6 +60,18 @@ class Character false end + def angle_to_direction(angle) + if angle >= -Math::PI / 4 && angle < Math::PI / 4 + :right + elsif angle >= Math::PI / 4 && angle < 3 * Math::PI / 4 + :front # down + elsif angle >= -3 * Math::PI / 4 && angle < -Math::PI / 4 + :back # up + else + :left + end + end + def update(dt) dx = 0.0 dy = 0.0 @@ -68,18 +84,11 @@ class Character moving = dx != 0 || dy != 0 if moving - if dy < 0 - @facing = :back - elsif dy > 0 - @facing = :front - elsif dx < 0 - @facing = :left - elsif dx > 0 - @facing = :right - end + @facing = Math.atan2(dy, dx) end - @current_animation = moving ? :"walk_#{@facing}" : :"idle_#{@facing}" + direction = angle_to_direction(@facing) + @current_animation = moving ? :"walk_#{direction}" : :"idle_#{direction}" # Normalize vector if moving diagonally if dx != 0 && dy != 0 diff --git a/game/enemy/base.rb b/game/enemy/base.rb index ede5997..4ced1ff 100644 --- a/game/enemy/base.rb +++ b/game/enemy/base.rb @@ -26,9 +26,9 @@ class Enemy @w = 25 @h = 30 @health = health - @lorentz = false + @lorentz = $bus.get(:lorentz_field?) || false - $bus.on(:lorentz_field?) do |lorentz| + $bus.on(:lorentz_field!) do |lorentz| @lorentz = lorentz end @@ -41,9 +41,7 @@ class Enemy def take_damage(amount) @health -= amount - pp "Enemy takes #{amount} damage, health now #{@health}" if @health <= 0 - pp "Enemy dies" $bus.emit(:enemy_died, self) end end @@ -76,6 +74,11 @@ class Enemy self.class.frame_size[2] ) + # Health bar + + health_width = (@w * @health / 100.0) + Gosu.draw_rect(screen_x - @w / 2, screen_y - @h / 2 - 10, health_width, 5, Gosu::Color.new(0xFF00FF00), Float::INFINITY) + if $bus.get(:settings, :debug) Gosu.draw_rect(screen_x - @w / 2, screen_y - @h / 2, @w, @h, Gosu::Color.new(0x40FF0000), Float::INFINITY) end diff --git a/game/enemy/force.rb b/game/enemy/force.rb index 060fe8e..c0c91ca 100644 --- a/game/enemy/force.rb +++ b/game/enemy/force.rb @@ -6,11 +6,13 @@ class Force < Enemy def initialize(x, y) super(x, y, 100) - $bus.on(:occams_razor_attack) do |attack_x, attack_y, range, damage| + $bus.on(:attack) do |attack_x, attack_y, range, damage| # Whoa, did'nt know ruby had hypot functions built in! if Math.hypot(@x - attack_x, @y - attack_y) <= range take_damage(damage) + next true end + false end end end \ No newline at end of file diff --git a/game/hud/health.rb b/game/hud/health.rb new file mode 100644 index 0000000..7114369 --- /dev/null +++ b/game/hud/health.rb @@ -0,0 +1,27 @@ +class HealthBar + attr_reader :health, :max_health + + def initialize(max_health) + @max_health = max_health + @health = max_health + end + + def take_damage(amount) + @health -= amount + @health = 0 if @health < 0 + end + + def heal(amount) + @health += amount + @health = @max_health if @health > @max_health + end + + def draw(x, y) + # Draw the background of the health bar (red) + Gosu.draw_rect(x, y, 100, 10, Gosu::Color::RED) + + # Draw the foreground of the health bar (green) based on current health + health_width = (health.to_f / max_health) * 100 + Gosu.draw_rect(x, y, health_width, 10, Gosu::Color::GREEN) + end +end \ No newline at end of file diff --git a/game/hud/layer.rb b/game/hud/layer.rb index 762103a..85b6228 100644 --- a/game/hud/layer.rb +++ b/game/hud/layer.rb @@ -1,13 +1,15 @@ require_relative "inventory" +require_relative "health" class HUDLayer def initialize @hud_bg = Gosu::Image.new("assets/images/hud.png", retro: true) @hud_fg = Gosu::Image.new("assets/images/hud_fg.png", retro: true) @inventory = Inventory.new + @health = HealthBar.new(100) $bus.on(:enemy_attack) do |damage| - pp "Player takes #{damage} damage!" + @health.take_damage(damage) end end @@ -15,6 +17,7 @@ class HUDLayer @hud_bg.draw(0, 0, Float::INFINITY) @inventory.draw + @health.draw(10, 10) @hud_fg.draw(0, 0, Float::INFINITY) end diff --git a/game/weapons/blade_of_recursion.rb b/game/weapons/blade_of_recursion.rb index e69de29..0705d6e 100644 --- a/game/weapons/blade_of_recursion.rb +++ b/game/weapons/blade_of_recursion.rb @@ -0,0 +1,30 @@ +class BladeOfRecursion + def initialize + @damage = 1 + @range = 200 + @speed = 500 + @x = 0 + @y = 0 + @direction = nil + end + + def attack(direction) + @direction = direction + @x, @y = $bus.get(:player_position) + end + + def update(dt) + return unless @direction + + @x += Math.cos(@direction) * @speed * dt + @y += Math.sin(@direction) * @speed * dt + + $bus.get(:attack, @x, @y, @range, @damage) + + # If the blade has traveled its max range, reset it + player_x, player_y = $bus.get(:player_position) + if Math.hypot(@x - player_x, @y - player_y) > @range + @direction = nil + end + end +end \ No newline at end of file diff --git a/game/weapons/handler.rb b/game/weapons/handler.rb index 4c5ec39..75ba6e2 100644 --- a/game/weapons/handler.rb +++ b/game/weapons/handler.rb @@ -20,8 +20,16 @@ class WeaponHandler return unless @weapons.key?(selected_item) weapon = @weapons[selected_item] - if $bus.get(:count, selected_item) > 0 && Gosu.button_down?(Gosu::KB_SPACE) - weapon.attack + if $bus.get(:count, selected_item) > 0 + if Gosu.button_down?(Gosu::KB_SPACE) + direction = $bus.get(:player_direction) + weapon.attack(direction) + elsif Gosu.button_down?(Gosu::MS_LEFT) + player_x, player_y = $bus.get(:player_position) + mouse_x, mouse_y = $bus.get(:mouse_position) + direction = Math.atan2(mouse_y - player_y, mouse_x - player_x) + weapon.attack(direction) + end end end diff --git a/game/weapons/lorentz_field.rb b/game/weapons/lorentz_field.rb index 621efba..21e687d 100644 --- a/game/weapons/lorentz_field.rb +++ b/game/weapons/lorentz_field.rb @@ -6,6 +6,10 @@ class LorentzField @active = false @font = Gosu::Font.new(24, name: "assets/fonts/tn.ttf") + $bus.on(:lorentz_field?) do + next @active + end + $bus.on(:lorentz_field) do if @active t = @time @@ -27,12 +31,12 @@ class LorentzField end end - def attack + def attack(_direction) return if @active $bus.emit(:consume, :lorentz_field, 1) @active = true @time = 0 - $bus.emit(:lorentz_field?, true) + $bus.emit(:lorentz_field!, true) end def update(dt) @@ -42,7 +46,7 @@ class LorentzField if @time >= BASE_TIME @time = 0 @active = false - $bus.emit(:lorentz_field?, false) + $bus.emit(:lorentz_field!, false) end end diff --git a/game/weapons/occams_razor.rb b/game/weapons/occams_razor.rb index 8fb2939..3d91cdd 100644 --- a/game/weapons/occams_razor.rb +++ b/game/weapons/occams_razor.rb @@ -8,7 +8,7 @@ class OccamsRazor @range = 45 # how many pixels from player center to enemy center the attack hits, this is a melee weapon so it should be small end - def attack + def attack(_direction) return if @active @active = true @@ -16,7 +16,7 @@ class OccamsRazor # Check for enemies in range and deal damage player_x, player_y = $bus.get(:player_position) - $bus.emit(:occams_razor_attack, player_x, player_y, @range, @damage) + $bus.emit(:attack, player_x, player_y, @range, @damage) end def update(dt)