Refactor event bus to use non-retrievable events;
update collision handling and add new inventory initialization; add torch lighting in fog shader;
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 622 B |
+7
-3
@@ -37,9 +37,13 @@ class Character
|
|||||||
@current_animation = :idle_front
|
@current_animation = :idle_front
|
||||||
@facing = :front
|
@facing = :front
|
||||||
|
|
||||||
$bus.on_retrievable(:player_position) do
|
$bus.on(:player_position) do
|
||||||
next [@world_x, @world_y]
|
next [@world_x, @world_y]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
$bus.on(:collides?) do |rect|
|
||||||
|
next collides?(rect) ? :character : nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def rect
|
def rect
|
||||||
@@ -95,13 +99,13 @@ class Character
|
|||||||
steps.times do
|
steps.times do
|
||||||
# X axis
|
# X axis
|
||||||
@world_x += step_dx
|
@world_x += step_dx
|
||||||
if ($bus.get(:collides?, rect) & [:wall, :object])&.any?
|
if ($bus.get_all(:collides?, rect) & [:wall, :object])&.any?
|
||||||
@world_x -= step_dx
|
@world_x -= step_dx
|
||||||
end
|
end
|
||||||
|
|
||||||
# Y axis
|
# Y axis
|
||||||
@world_y += step_dy
|
@world_y += step_dy
|
||||||
if ($bus.get(:collides?, rect) & [:wall, :object])&.any?
|
if ($bus.get_all(:collides?, rect) & [:wall, :object])&.any?
|
||||||
@world_y -= step_dy
|
@world_y -= step_dy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+2
-2
@@ -19,7 +19,7 @@ class EnemyAI
|
|||||||
player_tile = [(player_x - 90) / 120, (player_y - 90) / 120].map(&:round)
|
player_tile = [(player_x - 90) / 120, (player_y - 90) / 120].map(&:round)
|
||||||
enemy_tile = [(@enemy.x - 90) / 120, (@enemy.y - 90) / 120].map(&:round)
|
enemy_tile = [(@enemy.x - 90) / 120, (@enemy.y - 90) / 120].map(&:round)
|
||||||
|
|
||||||
return if $bus.get(:collides?, @enemy.rect)&.include?(:character)
|
return if $bus.get_all(:collides?, @enemy.rect)&.include?(:character)
|
||||||
|
|
||||||
object_in_tile = $bus.get(:object_at, enemy_tile[0], enemy_tile[1])
|
object_in_tile = $bus.get(:object_at, enemy_tile[0], enemy_tile[1])
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ class EnemyAI
|
|||||||
@enemy.x += dx
|
@enemy.x += dx
|
||||||
@enemy.y += dy
|
@enemy.y += dy
|
||||||
|
|
||||||
if $bus.get(:collides?, @enemy.rect)&.include?(:character)
|
if $bus.get_all(:collides?, @enemy.rect)&.include?(:character)
|
||||||
# Attack player
|
# Attack player
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ class Enemy
|
|||||||
@h = 30
|
@h = 30
|
||||||
|
|
||||||
@ai = EnemyAI.new(self, :chase)
|
@ai = EnemyAI.new(self, :chase)
|
||||||
|
|
||||||
|
$bus.on(:collides?) do |rect|
|
||||||
|
next collides?(rect) ? :enemy : nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def rect
|
def rect
|
||||||
|
|||||||
+5
-1
@@ -6,9 +6,13 @@ class Maze
|
|||||||
@spritesheet = Gosu::Image.load_tiles("assets/images/walls.png", 20, 40, retro: true)
|
@spritesheet = Gosu::Image.load_tiles("assets/images/walls.png", 20, 40, retro: true)
|
||||||
puts @spritesheet.size
|
puts @spritesheet.size
|
||||||
|
|
||||||
$bus.on_retrievable(:maze_solve) do |x1, y1, x2, y2|
|
$bus.on(:maze_solve) do |x1, y1, x2, y2|
|
||||||
next @maze.solve(x1, y1, x2, y2)
|
next @maze.solve(x1, y1, x2, y2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
$bus.on(:collides?) do |rect|
|
||||||
|
next collides?(rect) ? :wall : nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def collides?(rect)
|
def collides?(rect)
|
||||||
|
|||||||
+33
-9
@@ -12,6 +12,14 @@ class Placeable
|
|||||||
@y = y
|
@y = y
|
||||||
@frames_count = 0
|
@frames_count = 0
|
||||||
@spritesheet = nil
|
@spritesheet = nil
|
||||||
|
|
||||||
|
$bus.on(:object_at) do |x, y|
|
||||||
|
next @x == x && @y == y ? self : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
$bus.on(:collides?) do |rect|
|
||||||
|
next collides?(rect) ? :object : nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def collides?(rect)
|
def collides?(rect)
|
||||||
@@ -54,26 +62,42 @@ class Radar < Placeable
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Torch < Placeable
|
||||||
|
def initialize(x, y)
|
||||||
|
super(x, y)
|
||||||
|
@frames_count = 4
|
||||||
|
@spritesheet = Gosu::Image.load_tiles("assets/images/torch.png", 20, 20, retro: true)
|
||||||
|
|
||||||
|
$bus.on(:torch_position) do
|
||||||
|
next rect
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Chest < Placeable
|
||||||
|
def initialize(x, y)
|
||||||
|
super(x, y)
|
||||||
|
@frames_count = 1
|
||||||
|
@spritesheet = Gosu::Image.load_tiles("assets/images/chest.png", 20, 20, retro: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class ObjectHandler
|
class ObjectHandler
|
||||||
attr_reader :objects
|
attr_reader :objects
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
start_room_coords = $bus.get(:start_room_coords)
|
start_room_coords = $bus.get(:start_room_coords)
|
||||||
@objects = [Radar.new(start_room_coords[0] / 2 - 1, start_room_coords[1] / 2 - 1)]
|
@objects = [
|
||||||
|
Torch.new(start_room_coords[0] / 2 - 1, start_room_coords[1] / 2 - 1),
|
||||||
$bus.on_retrievable(:object_at) do |x, y|
|
Torch.new(start_room_coords[0] / 2 + 3, start_room_coords[1] / 2),
|
||||||
next @objects.find { |obj| obj.x == x && obj.y == y }
|
Torch.new(start_room_coords[0] / 2, start_room_coords[1] / 2 - 1),
|
||||||
end
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def add(object)
|
def add(object)
|
||||||
@objects << object
|
@objects << object
|
||||||
end
|
end
|
||||||
|
|
||||||
def collides?(rect)
|
|
||||||
@objects.any? { |obj| obj.collides?(rect) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@objects.each(&:update)
|
@objects.each(&:update)
|
||||||
end
|
end
|
||||||
|
|||||||
+45
-25
@@ -27,18 +27,9 @@ class Game < Scene
|
|||||||
@camera = pos
|
@camera = pos
|
||||||
end
|
end
|
||||||
|
|
||||||
$bus.on_retrievable(:camera_pos) do
|
$bus.on(:camera_pos) do
|
||||||
next @camera
|
next @camera
|
||||||
end
|
end
|
||||||
|
|
||||||
$bus.on_retrievable(:collides?) do |rect|
|
|
||||||
collisions = []
|
|
||||||
collisions << :wall if @maze.collides?(rect)
|
|
||||||
collisions << :character if @character.collides?(rect)
|
|
||||||
collisions << :enemy if @enemies.collides?(rect)
|
|
||||||
collisions << :object if @objects.collides?(rect)
|
|
||||||
next collisions
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def draw
|
def draw
|
||||||
@@ -74,13 +65,19 @@ class Game < Scene
|
|||||||
def draw_fog!
|
def draw_fog!
|
||||||
cam_x, cam_y = @camera
|
cam_x, cam_y = @camera
|
||||||
cell = 10
|
cell = 10
|
||||||
i_radius = 90.0
|
p_i_radius = 80.0
|
||||||
o_radius = 400.0
|
p_o_radius = 350.0
|
||||||
|
t_i_radius = 10.0
|
||||||
|
o_radius = 160.0
|
||||||
|
|
||||||
# Player screen position
|
# Player screen position
|
||||||
px = SCREEN_SIZE[0] / 2.0
|
px = SCREEN_SIZE[0] / 2.0
|
||||||
py = SCREEN_SIZE[1] / 2.0
|
py = SCREEN_SIZE[1] / 2.0
|
||||||
|
|
||||||
|
# Collect all light sources (player + torches)
|
||||||
|
torches = $bus.get_all(:torch_position)
|
||||||
|
torch_lights = torches.map { |t| [t[0] - cam_x, t[1] - cam_y] }
|
||||||
|
|
||||||
tiles_x = (SCREEN_SIZE[0] / cell) + 2
|
tiles_x = (SCREEN_SIZE[0] / cell) + 2
|
||||||
tiles_y = (SCREEN_SIZE[1] / cell) + 2
|
tiles_y = (SCREEN_SIZE[1] / cell) + 2
|
||||||
|
|
||||||
@@ -89,22 +86,45 @@ class Game < Scene
|
|||||||
sx = i * cell
|
sx = i * cell
|
||||||
sy = j * cell
|
sy = j * cell
|
||||||
|
|
||||||
dist = Math.sqrt((sx - px)**2 + (sy - py)**2)
|
light_strength = 0.0
|
||||||
|
|
||||||
if dist < i_radius
|
d = Math.sqrt((px - sx)**2 + (py - sy)**2)
|
||||||
next # fully clear
|
if d < p_i_radius
|
||||||
elsif dist > o_radius
|
light_strength = 1.0
|
||||||
alpha = 255
|
elsif d < p_o_radius
|
||||||
else
|
t = (d - p_i_radius) / (p_o_radius - p_i_radius) # 0..1
|
||||||
t = (dist - i_radius) / (o_radius - i_radius) # 0..1
|
light_strength = 1.0 - t
|
||||||
world_x = (sx + cam_x) * 0.005
|
|
||||||
world_y = (sy + cam_y) * 0.005
|
|
||||||
# Sample noise at world position so it scrolls with camera
|
|
||||||
# Add time-based offsets for animation
|
|
||||||
noise = (@noise[world_x + Gosu.milliseconds / 5000.0, world_y + Gosu.milliseconds / 6000.0] + 1.0) / 2.0
|
|
||||||
alpha = (t * (1.0 + noise) * 255).to_i.clamp(0, 255)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
torch_lights.each do |lx, ly|
|
||||||
|
d = Math.sqrt((sx - lx)**2 + (sy - ly)**2)
|
||||||
|
|
||||||
|
if d < t_i_radius
|
||||||
|
strength = 1.0
|
||||||
|
elsif d < o_radius
|
||||||
|
t = (d - t_i_radius) / (o_radius - t_i_radius) # 0..1
|
||||||
|
strength = 1.0 - t
|
||||||
|
else
|
||||||
|
strength = 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
light_strength += strength
|
||||||
|
end
|
||||||
|
|
||||||
|
light_strength = light_strength.clamp(0.0, 1.0)
|
||||||
|
|
||||||
|
t = 1.0 - light_strength
|
||||||
|
|
||||||
|
world_x = (sx + cam_x) * 0.005
|
||||||
|
world_y = (sy + cam_y) * 0.005
|
||||||
|
|
||||||
|
noise = (@noise[
|
||||||
|
world_x + Gosu.milliseconds / 5000.0,
|
||||||
|
world_y + Gosu.milliseconds / 6000.0
|
||||||
|
] + 1.0) / 2.0
|
||||||
|
|
||||||
|
alpha = (t * (1.0 + noise) * 255).to_i.clamp(0, 255)
|
||||||
|
|
||||||
Gosu.draw_rect(sx, sy, cell, cell, Gosu::Color.new(alpha, 0, 0, 0), 10000 + sy)
|
Gosu.draw_rect(sx, sy, cell, cell, Gosu::Color.new(alpha, 0, 0, 0), 10000 + sy)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,4 +4,35 @@ class State
|
|||||||
# also their health, stealth, and memory bars
|
# also their health, stealth, and memory bars
|
||||||
# it can handle mouse input for inventory management and item usage
|
# it can handle mouse input for inventory management and item usage
|
||||||
# it can also handle keyboard input for quick item usage
|
# it can also handle keyboard input for quick item usage
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@inventory = {
|
||||||
|
# basic resources
|
||||||
|
:wood => 0,
|
||||||
|
:metal => 0,
|
||||||
|
:science => 0,
|
||||||
|
|
||||||
|
# nuetrals
|
||||||
|
:torch => 0,
|
||||||
|
:radar => 0,
|
||||||
|
|
||||||
|
# placeable weapons
|
||||||
|
:landmine => 0,
|
||||||
|
:shrodingers_mine => 0,
|
||||||
|
:event_horizon => 0,
|
||||||
|
|
||||||
|
# attack weapons
|
||||||
|
:occams_razor => 0,
|
||||||
|
:quantum_bow => 0,
|
||||||
|
:blade_of_recursion => 0,
|
||||||
|
|
||||||
|
# utility weapon
|
||||||
|
:lorentz_field => 0
|
||||||
|
}
|
||||||
|
@inventory_selected = :wood
|
||||||
|
@upgrades = []
|
||||||
|
@health = 100
|
||||||
|
@stealth = 100
|
||||||
|
@memory = 256
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@@ -20,7 +20,7 @@ class Window < Gosu::Window
|
|||||||
|
|
||||||
$bus.on(:quit_game) { close! }
|
$bus.on(:quit_game) { close! }
|
||||||
|
|
||||||
$bus.on_retrievable(:mouse_pos) do
|
$bus.on(:mouse_pos) do
|
||||||
next mouse_relative(mouse_x, mouse_y)
|
next mouse_relative(mouse_x, mouse_y)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+19
-16
@@ -2,12 +2,11 @@ class EventBus
|
|||||||
def initialize
|
def initialize
|
||||||
# store callbacks with owner: {event => [ {callback:, owner:} ] }
|
# store callbacks with owner: {event => [ {callback:, owner:} ] }
|
||||||
@events = Hash.new { |h, k| h[k] = [] }
|
@events = Hash.new { |h, k| h[k] = [] }
|
||||||
@retrievable_events = {}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Emit an event
|
# Emit an event
|
||||||
def emit(event, *args)
|
def emit(event, *args)
|
||||||
@events[event].each do |h|
|
@events[event].dup.each do |h|
|
||||||
h[:callback].call(*args)
|
h[:callback].call(*args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -28,30 +27,34 @@ class EventBus
|
|||||||
on(event, owner: owner, &wrapper)
|
on(event, owner: owner, &wrapper)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Unsubscribe from an event
|
||||||
def off(event, &callback)
|
def off(event, &callback)
|
||||||
@events[event].reject! { |h| h[:callback] == callback }
|
@events[event].reject! { |h| h[:callback] == callback }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Register a retrievable event (only one allowed)
|
# For events that return a value (e.g. queries), get the first non-nil response
|
||||||
def on_retrievable(event, owner = nil, &callback)
|
|
||||||
owner ||= callback.binding.eval("self")
|
|
||||||
@retrievable_events[event] = { callback: callback, owner: owner }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get data from a retrievable event
|
|
||||||
def get(event, *args)
|
def get(event, *args)
|
||||||
if @retrievable_events[event]
|
@events[event].dup.each do |h|
|
||||||
@retrievable_events[event][:callback].call(*args)
|
result = h[:callback].call(*args)
|
||||||
else
|
return result unless result.nil?
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Unsubscribe from an object's events
|
# For events that return multiple values (e.g. queries), get all non-nil responses
|
||||||
def unlisten_owner(owner)
|
def get_all(event, *args)
|
||||||
|
results = []
|
||||||
|
@events[event].dup.each do |h|
|
||||||
|
result = h[:callback].call(*args)
|
||||||
|
results << result unless result.nil?
|
||||||
|
end
|
||||||
|
results
|
||||||
|
end
|
||||||
|
|
||||||
|
# Unsubscribe all events for a given owner (e.g. when an object is destroyed)
|
||||||
|
def remove_owner(owner)
|
||||||
@events.each do |event, handlers|
|
@events.each do |event, handlers|
|
||||||
handlers.reject! { |h| h[:owner] == owner }
|
handlers.reject! { |h| h[:owner] == owner }
|
||||||
end
|
end
|
||||||
@retrievable_events.delete_if { |_, h| h[:owner] == owner }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
+2
-2
@@ -15,14 +15,14 @@ class MazeData
|
|||||||
|
|
||||||
return if standalone
|
return if standalone
|
||||||
|
|
||||||
$bus.on_retrievable(:start_room_coords) do
|
$bus.on(:start_room_coords) do
|
||||||
next @start_room ? [@start_room[0] * 2 + 1, @start_room[1] * 2 + 1] : nil
|
next @start_room ? [@start_room[0] * 2 + 1, @start_room[1] * 2 + 1] : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return unless DEBUG
|
return unless DEBUG
|
||||||
|
|
||||||
print_debug
|
print_debug
|
||||||
$bus.on_retrievable(:boss_room_coords) do
|
$bus.on(:boss_room_coords) do
|
||||||
next @boss_rooms.first ? [@boss_rooms.first[0] * 2 + 1, @boss_rooms.first[1] * 2 + 1] : nil
|
next @boss_rooms.first ? [@boss_rooms.first[0] * 2 + 1, @boss_rooms.first[1] * 2 + 1] : nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+1
-1
@@ -14,6 +14,6 @@ class Scene
|
|||||||
end
|
end
|
||||||
|
|
||||||
def close
|
def close
|
||||||
$bus.unlisten_owner(self)
|
$bus.remove_owner(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Reference in New Issue
Block a user