diff --git a/assets/images/arrow.aseprite b/assets/images/arrow.aseprite new file mode 100644 index 0000000..8b998c3 Binary files /dev/null and b/assets/images/arrow.aseprite differ diff --git a/assets/images/arrow.png b/assets/images/arrow.png new file mode 100644 index 0000000..db698be Binary files /dev/null and b/assets/images/arrow.png differ diff --git a/assets/images/blade_of_recursion.aseprite b/assets/images/blade_of_recursion.aseprite new file mode 100644 index 0000000..1b529ab Binary files /dev/null and b/assets/images/blade_of_recursion.aseprite differ diff --git a/assets/images/blade_of_recursion.png b/assets/images/blade_of_recursion.png new file mode 100644 index 0000000..27930be Binary files /dev/null and b/assets/images/blade_of_recursion.png differ diff --git a/assets/images/quantum_bow.aseprite b/assets/images/quantum_bow.aseprite new file mode 100644 index 0000000..a03261d Binary files /dev/null and b/assets/images/quantum_bow.aseprite differ diff --git a/assets/images/quantum_bow.png b/assets/images/quantum_bow.png new file mode 100644 index 0000000..ed0d5f2 Binary files /dev/null and b/assets/images/quantum_bow.png differ diff --git a/game/scene.rb b/game/scene.rb index 86ea26a..0e3f74e 100644 --- a/game/scene.rb +++ b/game/scene.rb @@ -95,7 +95,7 @@ class Game < Scene return if @hud.button_down(id, pos) return if @props.button_down(id, pos) - #return if @weapons.button_down(id, pos) + return if @weapons.button_down(id, pos) #@character.button_down(id) end diff --git a/game/weapons/blade_of_recursion.rb b/game/weapons/blade_of_recursion.rb index 0705d6e..1e096af 100644 --- a/game/weapons/blade_of_recursion.rb +++ b/game/weapons/blade_of_recursion.rb @@ -1,16 +1,23 @@ class BladeOfRecursion def initialize @damage = 1 - @range = 200 - @speed = 500 + @range = 150 + @speed = 400 @x = 0 @y = 0 + @start_x = 0 + @start_y = 0 @direction = nil + + # the knife is pointed top-right + @sprite = Gosu::Image.new('assets/images/blade_of_recursion.png', retro: true) end def attack(direction) + return if @direction @direction = direction @x, @y = $bus.get(:player_position) + @start_x, @start_y = @x, @y end def update(dt) @@ -19,12 +26,63 @@ class BladeOfRecursion @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 + if Math.hypot(@x - @start_x, @y - @start_y) > @range @direction = nil + @damage = 1 + return + end + + collides = $bus.get_all(:collides?, [@x - 5, @y - 5, 10, 10]) + if collides.include?(:enemy) + $bus.emit(:attack, @x, @y, 30, @damage) + @direction = nil + @damage *= 2 + if @damage > 512 + damage = 1 + $bus.emit(:consume, :blade_of_recursion, 1) + end + elsif collides.include?(:wall) + @direction = nil + @damage = 1 end end + + def draw + cam_x, cam_y = $bus.get(:camera_pos) || [0, 0] + + # Draw the blade if active + if @direction + # Convert radians to degrees + angle_deg = @direction * 180 / Math::PI + # Adjust for knife pointing top-right + angle_deg += 45 + + @sprite.draw_rot( + @x - cam_x, # x in world coords + @y - cam_y, # y in world coords + 1, # z-order + angle_deg, # rotation + 0.5, 0.5, # pivot center + 2, 2, + Gosu::Color::WHITE + ) + end + + # Draw memory bar as HUD in top-right of screen + # this should be in the HUD layer but due to time constraints im putting it here, sorry + bar_width = 100 + bar_height = 10 + padding = 10 + + bar_x = SCREEN_SIZE[0] - bar_width - padding + bar_y = padding + + # full red background + Gosu.draw_rect(bar_x, bar_y, bar_width, bar_height, Gosu::Color::RED, Float::INFINITY) + + # green foreground representing current damage + damage_ratio = Math.log2(@damage) / 9.0 + damage_width = bar_width * [damage_ratio, 1.0].min + Gosu.draw_rect(bar_x, bar_y, damage_width, bar_height, Gosu::Color::GREEN, Float::INFINITY) + end end \ No newline at end of file diff --git a/game/weapons/handler.rb b/game/weapons/handler.rb index 979ce97..f09437e 100644 --- a/game/weapons/handler.rb +++ b/game/weapons/handler.rb @@ -1,39 +1,45 @@ require_relative 'lorentz_field' require_relative 'occams_razor' -#require_relative 'quantum_bow' -#require_relative 'blade_of_recursion' +require_relative 'quantum_bow' +require_relative 'blade_of_recursion' class WeaponHandler def initialize @weapons = { lorentz_field: LorentzField.new, occams_razor: OccamsRazor.new, - #quantum_bow: QuantumBow.new, - #blade_of_recursion: BladeOfRecursion.new + quantum_bow: QuantumBow.new, + blade_of_recursion: BladeOfRecursion.new } end def update(dt) @weapons.each_value { |weapon| weapon.update(dt) } - - selected_item = $bus.get(:selected_item) - return unless @weapons.key?(selected_item) - - weapon = @weapons[selected_item] - 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_pos) - direction = Math.atan2(mouse_y - player_y, mouse_x - player_x) - weapon.attack(direction) - end - end end def draw @weapons.each_value(&:draw) end + + def button_down(id, pos) + selected_item = $bus.get(:selected_item) + return unless @weapons.key?(selected_item) + + weapon = @weapons[selected_item] + if $bus.get(:count, selected_item) > 0 + if id == Gosu::KB_SPACE + player_x, player_y = $bus.get(:player_position) + cam_x, cam_y = $bus.get(:camera_pos) || [0, 0] + mouse_x, mouse_y = $bus.get(:mouse_pos) || [0, 0] + direction = Math.atan2((mouse_y + cam_y - player_y), (mouse_x + cam_x - player_x)) + weapon.attack(direction) + elsif id == Gosu::MS_LEFT + player_x, player_y = $bus.get(:player_position) + cam_x, cam_y = $bus.get(:camera_pos) || [0, 0] + mouse_x, mouse_y = pos + direction = Math.atan2((mouse_y + cam_y - player_y), (mouse_x + cam_x - player_x)) + weapon.attack(direction) + end + end + end end \ No newline at end of file diff --git a/game/weapons/quantum_bow.rb b/game/weapons/quantum_bow.rb index e69de29..c33d38d 100644 --- a/game/weapons/quantum_bow.rb +++ b/game/weapons/quantum_bow.rb @@ -0,0 +1,79 @@ +class QuantumBow + ARROW_SPEED = 700 + LOW_DAMAGE = 10 + HIGH_DAMAGE = 50 + FACING_THRESHOLD = Math::PI / 2.0 # 90° in radians + + def initialize + @arrows = [] + #@sprite = Gosu::Image.new('assets/images/quantum_bow.png', retro: true) + @arrow_sprite = Gosu::Image.new('assets/images/arrow.png', retro: true) + end + + def clamp_angle(intended, player) + # it takes the direction from the attack function and only allows attacks facing forwards from the player, + # and if it is not in 90 deg from players direction then it will return the extreme angle possible else the intended angle + angle_diff = ((intended - player + Math::PI) % (2 * Math::PI)) - Math::PI + if angle_diff > FACING_THRESHOLD + return player + FACING_THRESHOLD + elsif angle_diff < -FACING_THRESHOLD + return player - FACING_THRESHOLD + else + return intended + end + end + + def attack(direction) + player_x, player_y = $bus.get(:player_position) + return unless $bus.get(:consume, :wood, 3) && $bus.get(:consume, :metal, 1) + + @arrows << { x: player_x, y: player_y, direction: clamp_angle(direction, $bus.get(:player_direction)) } + end + + def update(dt) + player_x, player_y = $bus.get(:player_position) + player_dir = $bus.get(:player_direction) + + @arrows.each do |arrow| + arrow[:x] += Math.cos(arrow[:direction]) * ARROW_SPEED * dt + arrow[:y] += Math.sin(arrow[:direction]) * ARROW_SPEED * dt + + rect = [arrow[:x] - 5, arrow[:y] - 2, 10, 4] + collides = $bus.get_all(:collides?, rect) + + if collides.include?(:enemy) + # compute if player is facing the arrow + vec_x = arrow[:x] - player_x + vec_y = arrow[:y] - player_y + angle_to_arrow = Math.atan2(vec_y, vec_x) + angle_diff = ((angle_to_arrow - player_dir + Math::PI) % (2 * Math::PI)) - Math::PI + angle_diff = angle_diff.abs + + if angle_diff > FACING_THRESHOLD + $bus.emit(:blast, arrow[:x], arrow[:y], 30, HIGH_DAMAGE, :safe) + else + $bus.emit(:attack, arrow[:x], arrow[:y], 30, LOW_DAMAGE) + end + + arrow[:hit] = true + elsif collides.include?(:wall) + arrow[:hit] = true + end + end + + @arrows.reject! { |a| a[:hit] } + end + + def draw + cam_x, cam_y = $bus.get(:camera_pos) || [0, 0] + + @arrows.each do |arrow| + angle_deg = arrow[:direction] * 180 / Math::PI + angle_deg -= 45 + 180 + @arrow_sprite.draw_rot(arrow[:x] - cam_x, arrow[:y] - cam_y, 1, angle_deg, 0.5, 0.5, 2, 2, Gosu::Color::WHITE) + end + + #player_x, player_y = $bus.get(:player_position) + #@sprite.draw(player_x - cam_x - 16, player_y - cam_y - 16, 1) + end +end \ No newline at end of file