Implement health bar system: add HealthBar class for managing health, integrate health updates in HUDLayer, and modify enemy attack handling to reflect damage taken.
This commit is contained in:
+20
-11
@@ -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
|
||||
|
||||
+7
-4
@@ -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
|
||||
|
||||
+3
-1
@@ -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
|
||||
@@ -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
|
||||
+4
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
+10
-2
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user