Refactor enemy system: enhance EnemyAI with attack cooldown, update Force class to handle damage, and improve EnemyHandler for enemy removal; add OccamsRazor weapon with attack mechanics.
This commit is contained in:
Vendored
+1
@@ -3,4 +3,5 @@
|
|||||||
"editor.insertSpaces": true,
|
"editor.insertSpaces": true,
|
||||||
"editor.fontFamily": "'Operator Mono Lig', monospace",
|
"editor.fontFamily": "'Operator Mono Lig', monospace",
|
||||||
"editor.fontSize": 14,
|
"editor.fontSize": 14,
|
||||||
|
"editor.fontLigatures": true,
|
||||||
}
|
}
|
||||||
+17
-18
@@ -2,6 +2,7 @@ class EnemyAI
|
|||||||
AGGRO_RADIUS = 2000
|
AGGRO_RADIUS = 2000
|
||||||
BASE_SPEED = 130.0
|
BASE_SPEED = 130.0
|
||||||
CORNER_OFFSET = 35 # how far from tile center to place corner waypoint
|
CORNER_OFFSET = 35 # how far from tile center to place corner waypoint
|
||||||
|
ATTACK_COOLDOWN = 0.2 # seconds
|
||||||
|
|
||||||
def initialize(enemy, type)
|
def initialize(enemy, type)
|
||||||
@enemy = enemy
|
@enemy = enemy
|
||||||
@@ -11,22 +12,14 @@ class EnemyAI
|
|||||||
@last_player_tile = nil
|
@last_player_tile = nil
|
||||||
@avoiding_obstacle = false
|
@avoiding_obstacle = false
|
||||||
@speed = BASE_SPEED
|
@speed = BASE_SPEED
|
||||||
@lorentz_effect_active = false
|
@time_since_attack = ATTACK_COOLDOWN
|
||||||
|
|
||||||
$bus.on(:lorentz_field) do
|
|
||||||
@lorentz_effect_active = true
|
|
||||||
end
|
|
||||||
|
|
||||||
$bus.on(:lorentz_field_end) do
|
|
||||||
@lorentz_effect_active = false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(player_x, player_y, dt)
|
def update(player_x, player_y, dt)
|
||||||
distance = Math.sqrt((player_x - @enemy.x)**2 + (player_y - @enemy.y)**2)
|
distance = Math.sqrt((player_x - @enemy.x)**2 + (player_y - @enemy.y)**2)
|
||||||
return unless distance < AGGRO_RADIUS
|
return unless distance < AGGRO_RADIUS
|
||||||
|
|
||||||
if @lorentz_effect_active && distance < 230
|
if @enemy.lorentz && distance < 230
|
||||||
@speed = BASE_SPEED * 0.3
|
@speed = BASE_SPEED * 0.3
|
||||||
else
|
else
|
||||||
@speed = BASE_SPEED
|
@speed = BASE_SPEED
|
||||||
@@ -42,7 +35,14 @@ class EnemyAI
|
|||||||
enemy_tile[0] > start_room_tile[0] - 1 && enemy_tile[0] < start_room_tile[0] + 3 &&
|
enemy_tile[0] > start_room_tile[0] - 1 && enemy_tile[0] < start_room_tile[0] + 3 &&
|
||||||
enemy_tile[1] > start_room_tile[1] - 1 && enemy_tile[1] < start_room_tile[1] + 3
|
enemy_tile[1] > start_room_tile[1] - 1 && enemy_tile[1] < start_room_tile[1] + 3
|
||||||
|
|
||||||
return if $bus.get_all(:collides?, @enemy.rect)&.include?(:character)
|
if $bus.get_all(:collides?, @enemy.rect)&.include?(:character)
|
||||||
|
if @time_since_attack >= ATTACK_COOLDOWN
|
||||||
|
$bus.emit(:enemy_attack, 10) # deal 10 damage
|
||||||
|
@time_since_attack = 0
|
||||||
|
end
|
||||||
|
@time_since_attack += dt
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
prop_in_tile = $bus.get(:prop_at, enemy_tile[0], enemy_tile[1])
|
prop_in_tile = $bus.get(:prop_at, enemy_tile[0], enemy_tile[1])
|
||||||
|
|
||||||
@@ -150,15 +150,14 @@ class EnemyAI
|
|||||||
end
|
end
|
||||||
|
|
||||||
def move_towards(target_x, target_y, dt)
|
def move_towards(target_x, target_y, dt)
|
||||||
angle = Gosu.angle(@enemy.x, @enemy.y, target_x, target_y)
|
dx_raw = target_x - @enemy.x
|
||||||
dx = Gosu.offset_x(angle, @speed * dt)
|
dy_raw = target_y - @enemy.y
|
||||||
dy = Gosu.offset_y(angle, @speed * dt)
|
angle = Math.atan2(dy_raw, dx_raw)
|
||||||
|
|
||||||
|
dx = Math.cos(angle) * @speed * dt
|
||||||
|
dy = Math.sin(angle) * @speed * dt
|
||||||
|
|
||||||
@enemy.x += dx
|
@enemy.x += dx
|
||||||
@enemy.y += dy
|
@enemy.y += dy
|
||||||
|
|
||||||
if $bus.get_all(:collides?, @enemy.rect)&.include?(:character)
|
|
||||||
# Attack player
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
+25
-9
@@ -1,14 +1,14 @@
|
|||||||
require_relative 'ai'
|
require_relative 'ai'
|
||||||
|
|
||||||
class Enemy
|
class Enemy
|
||||||
attr_accessor :x, :y, :w, :h
|
attr_accessor :x, :y, :w, :h, :lorentz
|
||||||
|
|
||||||
def self.load(path, w, h, s)
|
def self.load(path, w, h, s)
|
||||||
# Once artwork is done need to load each animation here.
|
# Once artwork is done need to load each animation here.
|
||||||
# But as artwork takes time, for now just load a single image.
|
# But as artwork takes time, for now just load a single image.
|
||||||
@sprite = Gosu::Image.new(path, retro: true)
|
@sprite = Gosu::Image.new(path, retro: true)
|
||||||
@i_w = w
|
@i_w = w * s
|
||||||
@i_h = h
|
@i_h = h * s
|
||||||
@s = s
|
@s = s
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -20,11 +20,17 @@ class Enemy
|
|||||||
[@i_w, @i_h, @s]
|
[@i_w, @i_h, @s]
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(x, y)
|
def initialize(x, y, health = 100)
|
||||||
@x = x
|
@x = x
|
||||||
@y = y
|
@y = y
|
||||||
@w = 25
|
@w = 25
|
||||||
@h = 30
|
@h = 30
|
||||||
|
@health = health
|
||||||
|
@lorentz = false
|
||||||
|
|
||||||
|
$bus.on(:lorentz_field?) do |lorentz|
|
||||||
|
@lorentz = lorentz
|
||||||
|
end
|
||||||
|
|
||||||
@ai = EnemyAI.new(self, :chase)
|
@ai = EnemyAI.new(self, :chase)
|
||||||
|
|
||||||
@@ -33,6 +39,15 @@ class Enemy
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
def rect
|
def rect
|
||||||
[@x - @w / 2, @y - @h / 2, @w, @h]
|
[@x - @w / 2, @y - @h / 2, @w, @h]
|
||||||
end
|
end
|
||||||
@@ -53,16 +68,17 @@ class Enemy
|
|||||||
screen_x = @x - cam_x
|
screen_x = @x - cam_x
|
||||||
screen_y = @y - cam_y
|
screen_y = @y - cam_y
|
||||||
|
|
||||||
|
|
||||||
self.class.sprite.draw(
|
self.class.sprite.draw(
|
||||||
screen_x - self.class.frame_size[0],
|
screen_x - self.class.frame_size[0] / 2,
|
||||||
screen_y - self.class.frame_size[1],
|
screen_y - self.class.frame_size[1] / 2,
|
||||||
screen_y - self.class.frame_size[1],
|
screen_y - self.class.frame_size[1] / 2 + 10,
|
||||||
self.class.frame_size[2],
|
self.class.frame_size[2],
|
||||||
self.class.frame_size[2]
|
self.class.frame_size[2]
|
||||||
)
|
)
|
||||||
|
|
||||||
Gosu.draw_rect(screen_x - @w / 2, screen_y - @h / 2, @w, @h, Gosu::Color.new(0x40FF0000), Float::INFINITY) if $bus.get(:settings, :debug)
|
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
|
||||||
|
|
||||||
@ai.draw
|
@ai.draw
|
||||||
end
|
end
|
||||||
|
|||||||
+8
-3
@@ -4,8 +4,13 @@ class Force < Enemy
|
|||||||
load 'assets/images/force.png', 21, 32, 1.7
|
load 'assets/images/force.png', 21, 32, 1.7
|
||||||
|
|
||||||
def initialize(x, y)
|
def initialize(x, y)
|
||||||
super
|
super(x, y, 100)
|
||||||
@w = 20
|
|
||||||
@h = 20
|
$bus.on(:occams_razor_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)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -3,6 +3,11 @@ require_relative 'force'
|
|||||||
class EnemyHandler
|
class EnemyHandler
|
||||||
def initialize
|
def initialize
|
||||||
@enemies = []
|
@enemies = []
|
||||||
|
|
||||||
|
$bus.on(:enemy_died) do |enemy|
|
||||||
|
$bus.remove_owner(enemy)
|
||||||
|
@enemies.delete(enemy)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(dt)
|
def update(dt)
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ class HUDLayer
|
|||||||
@hud_bg = Gosu::Image.new("assets/images/hud.png", retro: true)
|
@hud_bg = Gosu::Image.new("assets/images/hud.png", retro: true)
|
||||||
@hud_fg = Gosu::Image.new("assets/images/hud_fg.png", retro: true)
|
@hud_fg = Gosu::Image.new("assets/images/hud_fg.png", retro: true)
|
||||||
@inventory = Inventory.new
|
@inventory = Inventory.new
|
||||||
|
|
||||||
|
$bus.on(:enemy_attack) do |damage|
|
||||||
|
pp "Player takes #{damage} damage!"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def draw
|
def draw
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
require_relative 'lorentz_field'
|
require_relative 'lorentz_field'
|
||||||
#require_relative 'occams_razor'
|
require_relative 'occams_razor'
|
||||||
#require_relative 'quantum_bow'
|
#require_relative 'quantum_bow'
|
||||||
#require_relative 'blade_of_recursion'
|
#require_relative 'blade_of_recursion'
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ class WeaponHandler
|
|||||||
def initialize
|
def initialize
|
||||||
@weapons = {
|
@weapons = {
|
||||||
lorentz_field: LorentzField.new,
|
lorentz_field: LorentzField.new,
|
||||||
#occams_razor: OccamsRazor.new,
|
occams_razor: OccamsRazor.new,
|
||||||
#quantum_bow: QuantumBow.new,
|
#quantum_bow: QuantumBow.new,
|
||||||
#blade_of_recursion: BladeOfRecursion.new
|
#blade_of_recursion: BladeOfRecursion.new
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class LorentzField
|
|||||||
$bus.emit(:consume, :lorentz_field, 1)
|
$bus.emit(:consume, :lorentz_field, 1)
|
||||||
@active = true
|
@active = true
|
||||||
@time = 0
|
@time = 0
|
||||||
$bus.emit(:lorentz_field)
|
$bus.emit(:lorentz_field?, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(dt)
|
def update(dt)
|
||||||
@@ -42,7 +42,7 @@ class LorentzField
|
|||||||
if @time >= BASE_TIME
|
if @time >= BASE_TIME
|
||||||
@time = 0
|
@time = 0
|
||||||
@active = false
|
@active = false
|
||||||
$bus.emit(:lorentz_field_end)
|
$bus.emit(:lorentz_field?, false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,35 @@
|
|||||||
class OccamsRazor
|
class OccamsRazor
|
||||||
|
BASE_TIME = 0.2 # seconds per attack
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
#
|
@time = 0
|
||||||
|
@active = false
|
||||||
|
@damage = 20
|
||||||
|
@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
|
end
|
||||||
|
|
||||||
def attack(dt)
|
def attack
|
||||||
#
|
return if @active
|
||||||
|
|
||||||
|
@active = true
|
||||||
|
@time = BASE_TIME
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(dt)
|
||||||
|
return unless @active
|
||||||
|
|
||||||
|
@time -= dt
|
||||||
|
if @time <= 0
|
||||||
|
@active = false
|
||||||
|
@time = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def draw
|
||||||
|
# Nothing for now, but player attack animation would go here
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Reference in New Issue
Block a user