Last active
October 21, 2025 12:59
-
-
Save amirrajan/aa40adfa985fa2cb3aea1ad4b7a198db to your computer and use it in GitHub Desktop.
DragonRuby Game Toolkit - Parry 33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Game | |
| attr_gtk | |
| def initialize | |
| @clock = 0 | |
| @clock_debounce = 0 | |
| @player = { x: 640 - 64, | |
| y: 72 - 64, | |
| w: 128, | |
| h: 128, | |
| highest_streak: 0, | |
| current_streak: 0, | |
| path: :solid, | |
| parry_at: 0, | |
| parry_frames: 6, | |
| hp: 3, | |
| r: 0, | |
| g: 80, | |
| b: 80 } | |
| @enemy = { x: 640 - 64, | |
| start_x: 640 - 64, | |
| action_x: 0, | |
| y: 72 - 64 + 360, | |
| start_y: 72 - 64 + 360, | |
| action_y: 0, | |
| w: 128, | |
| h: 128, | |
| path: :solid, | |
| action_queue: [], | |
| r: 128, | |
| g: 30, | |
| b: 30 } | |
| @label_fx_queue = [] | |
| @flash_fx_queue = [] | |
| @after_image_fx_queue = [] | |
| @enemy_moves = [:slash, :double_slash] | |
| end | |
| def tick | |
| outputs.background_color = [30, 30, 30] | |
| outputs.primitives << @player | |
| outputs.primitives << { **@enemy, x: @enemy.x + @enemy.action_x, y: @enemy.y + @enemy.action_y, a: 128 } | |
| if @enemy.action_queue.empty? | |
| m = @enemy_moves.sample | |
| @enemy.action_queue = EnemyMoves.send m, @player, @enemy | |
| end | |
| @player.previous_streak ||= 0 | |
| if @player.previous_streak != @player.current_streak | |
| if @player.highest_streak > 12 | |
| @enemy_moves << :round_slash | |
| elsif @player.highest_streak > 9 | |
| @enemy_moves << :triple_stab | |
| elsif @player.highest_streak > 6 | |
| @enemy_moves << :quick_slash | |
| elsif @player.highest_streak > 3 | |
| @enemy_moves << :double_quick_slash | |
| end | |
| @enemy_moves.uniq! | |
| @player.previous_streak = @player.current_streak | |
| end | |
| parry_requested = inputs.keyboard.key_down.j || inputs.keyboard.key_down.space || inputs.controller_one.key_down.a || inputs.mouse.click | |
| if (parry_requested) && @player.parry_at.elapsed_time(@clock) >= 30 | |
| @player.parry_at = @clock | |
| if @player.damage_count_down && @player.damage_count_down > 0 && @player.parry_at.elapsed_time(@clock) <= @player.parry_frames | |
| @label_fx_queue << { x: @player.x + @player.w / 2, | |
| y: @player.y + @player.h / 2, | |
| anchor_x: 0.5, | |
| anchor_y: 0.5, | |
| size_px: 32, | |
| r: 255, g: 255, b: 255, | |
| text: "Parried!", | |
| r: 0, g: 255, b: 255, | |
| at: @clock } | |
| @flash_fx_queue << { at: @clock, r: 0, g: 80, b: 80 } | |
| @player.current_streak += 1 | |
| if @player.current_streak > @player.highest_streak | |
| @player.highest_streak = @player.current_streak | |
| end | |
| @player.damage_count_down = nil | |
| end | |
| end | |
| if @player.damage_count_down && @player.damage_count_down > 0 | |
| @player.damage_count_down -= 1 | |
| if @player.damage_count_down == 0 | |
| @label_fx_queue << { x: @player.x + @player.w / 2, | |
| y: @player.y + @player.h / 2, | |
| anchor_x: 0.5, | |
| anchor_y: 0.5, | |
| r: 255, g: 255, b: 255, | |
| size_px: 32, | |
| text: "Damaged!", | |
| r: 255, g: 0, b: 0, | |
| at: Kernel.tick_count } | |
| @player.current_streak = 0 | |
| @flash_fx_queue << { at: @clock } | |
| end | |
| end | |
| tick_enemy | |
| outputs.primitives << { x: @player.x + @player.w / 2, | |
| y: @player.y + @player.h / 2, | |
| anchor_x: 0.5, | |
| anchor_y: 0.5, | |
| r: 255, g: 255, b: 255, | |
| size_px: 32, | |
| text: "#{@player.current_streak}" } | |
| outputs.primitives << { x: 8, | |
| y: 720 - 16, | |
| text: "Highest Streak: #{@player.highest_streak}", | |
| r: 255, | |
| g: 255, | |
| b: 255, | |
| size_px: 32, | |
| anchor_x: 0, | |
| anchor_y: 0.5 } | |
| outputs.primitives << { x: 8, | |
| y: 720 - 48, | |
| text: "Current Streak: #{@player.current_streak}", | |
| r: 255, | |
| g: 255, | |
| b: 255, | |
| size_px: 32, | |
| anchor_x: 0, | |
| anchor_y: 0.5 } | |
| instructions = if inputs.last_active == :controller | |
| "Press 'A' to Parry" | |
| elsif inputs.last_active == :keyboard | |
| "Press 'J' or 'Space' to Parry" | |
| else | |
| "Click or Tap to Parry" | |
| end | |
| outputs.primitives << { x: 640, | |
| y: 720 - 32, | |
| text: instructions, | |
| r: 255, | |
| g: 255, | |
| b: 255, | |
| size_px: 32, | |
| anchor_x: 0.5, | |
| anchor_y: 0.5 } | |
| tick_flash_fx_queue | |
| tick_after_image_fx_queue | |
| tick_label_fx_queue | |
| @clock += 1 | |
| end | |
| def tick_after_image_fx_queue | |
| @after_image_fx_queue.each do |d| | |
| d.a ||= 128 | |
| d.d ||= 1 | |
| d.a -= d.d | |
| d.d *= 1.1 | |
| d.x += d.d | |
| d.w -= d.d * 2 | |
| d.y += d.d | |
| d.h -= d.d * 2 | |
| end | |
| @after_image_fx_queue.reject! { |d| d.a <= 0 || d.w <= 0 || d.h <= 0 } | |
| outputs.primitives << @after_image_fx_queue | |
| end | |
| def tick_flash_fx_queue | |
| @flash_fx_queue.each do |d| | |
| d.a ||= 128 | |
| d.x ||= @player.x + @player.w / 2 | |
| d.y ||= @player.y + @player.h / 2 | |
| d.w ||= @player.w | |
| d.h ||= @player.h | |
| d.dw ||= 2 | |
| d.dh ||= 2 | |
| d.anchor_x ||= 0.5 | |
| d.anchor_y ||= 0.5 | |
| d.path ||= :solid | |
| d.r ||= 128 | |
| d.g ||= 30 | |
| d.b ||= 30 | |
| d.w += d.dw | |
| d.h += d.dh | |
| d.dw *= 1.5 | |
| d.dh *= 1.5 | |
| end | |
| @flash_fx_queue.reject! { |d| d.w >= 1280 } | |
| outputs.primitives << @flash_fx_queue | |
| end | |
| def tick_label_fx_queue | |
| @label_fx_queue.each do |l| | |
| l.a ||= 255 | |
| l.a -= 5 | |
| l.dy ||= 10 | |
| l.y += l.dy | |
| l.dy *= 0.9 | |
| end | |
| @label_fx_queue.reject { |l| l.a <= 0 } | |
| outputs.primitives << @label_fx_queue.map do |l| | |
| [ | |
| { x: l.x, y: l.y, w: 128 + 16, h: 32 + 8, path: :solid, r: 0, g: 0, b: 0, anchor_x: 0.5, anchor_y: 0.5, a: l.a }, | |
| { **l }, | |
| ] | |
| end | |
| end | |
| def tick_enemy | |
| current_action = @enemy.action_queue[0] | |
| return if !current_action | |
| current_action.start_at ||= @clock | |
| diff_x = current_action.target_x - @enemy.x | |
| diff_y = current_action.target_y - @enemy.y | |
| perc = Easing.send current_action.m, | |
| start_at: current_action.start_at, | |
| duration: current_action.duration, | |
| tick_count: @clock, | |
| power: current_action.power | |
| perc_x = if current_action.perc_x | |
| current_action.perc_x.call perc | |
| else | |
| perc | |
| end | |
| perc_y = if current_action.perc_y | |
| current_action.perc_y.call perc | |
| else | |
| perc | |
| end | |
| @enemy.action_x = diff_x * perc_x | |
| @enemy.action_y = diff_y * perc_y | |
| if current_action.damaging && ((current_action.start_at + current_action.duration) == @clock + 1) | |
| if @player.parry_at.elapsed_time(@clock) <= (@player.parry_frames.idiv(2) - 1) | |
| @player.current_streak += 1 | |
| if @player.current_streak > @player.highest_streak | |
| @player.highest_streak = @player.current_streak | |
| end | |
| @label_fx_queue << { x: @player.x + @player.w / 2, | |
| y: @player.y + @player.h / 2, | |
| anchor_x: 0.5, | |
| anchor_y: 0.5, | |
| r: 255, g: 255, b: 255, | |
| size_px: 32, | |
| text: "Parried!", | |
| r: 0, g: 255, b: 255, | |
| at: @clock } | |
| @flash_fx_queue << { at: @clock, r: 0, g: 80, b: 80 } | |
| else | |
| enemy_rect = { x: @enemy.x + @enemy.action_x, | |
| y: @enemy.y + @enemy.action_y, | |
| w: @enemy.w, | |
| h: @enemy.h } | |
| player_rect = @player | |
| if Geometry.intersect_rect?(enemy_rect, player_rect) && ((@player.damage_count_down || 0) <= 0) | |
| @player.damage_count_down = @player.parry_frames | |
| else | |
| end | |
| end | |
| end | |
| if current_action.start_at + current_action.duration == @clock | |
| @enemy.action_queue.pop_front | |
| @enemy.x = current_action.target_x | |
| @enemy.y = current_action.target_y | |
| @enemy.action_x = 0 | |
| @enemy.action_y = 0 | |
| end | |
| @after_image_fx_queue << { **@enemy.slice(:w, :h, :path, :r, :g, :b), | |
| x: @enemy.x + @enemy.action_x, | |
| y: @enemy.y + @enemy.action_y, a: 64 } | |
| end | |
| end | |
| def boot args | |
| args.state = {} | |
| end | |
| def tick args | |
| $game ||= Game.new | |
| $game.args = args | |
| $game.tick | |
| end | |
| def reset args | |
| $game = nil | |
| end | |
| class EnemyMoves | |
| class << self | |
| def double_slash player, enemy | |
| [ | |
| { | |
| duration: 60, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 1280 - 8 - 128, | |
| target_y: enemy.start_y | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| damaging: true | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 8, | |
| target_y: enemy.start_y, | |
| perc_y: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| damaging: true | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 1280 - 8 - 128, | |
| target_y: enemy.start_y, | |
| perc_y: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 60, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: 640 - 64, | |
| target_y: enemy.start_y | |
| }, | |
| ] | |
| end | |
| def double_quick_slash player, enemy | |
| [ | |
| { | |
| duration: 60, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 8, | |
| target_y: enemy.start_y | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| damaging: true | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 1280 - 8 - 128, | |
| target_y: enemy.start_y, | |
| perc_y: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| damaging: true | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 8, | |
| target_y: enemy.start_y, | |
| perc_y: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 60, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: 640 - 64, | |
| target_y: enemy.start_y | |
| }, | |
| ] | |
| end | |
| def quick_slash player, enemy | |
| [ | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 8, | |
| target_y: enemy.start_y, | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| damaging: true | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 1280 - 8 - 128, | |
| target_y: enemy.start_y, | |
| perc_y: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: 640 - 64, | |
| target_y: enemy.start_y, | |
| }, | |
| ] | |
| end | |
| def slash player, enemy | |
| [ | |
| { | |
| duration: 60, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 1280 - 8 - 128, | |
| target_y: enemy.start_y, | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| damaging: true | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 8, | |
| target_y: enemy.start_y, | |
| perc_y: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 60, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: 640 - 64, | |
| target_y: enemy.start_y, | |
| }, | |
| ] | |
| end | |
| def round_slash player, enemy | |
| [ | |
| { | |
| duration: 30, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y + enemy.h, | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: 8, | |
| target_y: enemy.start_y, | |
| perc_y: ->(perc) { perc**2 } | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 } | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| damaging: true | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 1280 - 8 - 128, | |
| target_y: enemy.y, | |
| perc_y: ->(perc) { perc**2 } | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y + enemy.h, | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: 8, | |
| target_y: enemy.start_y, | |
| perc_y: ->(perc) { perc**2 } | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 } | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| damaging: true | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 1280 - 8 - 128, | |
| target_y: enemy.y, | |
| perc_y: ->(perc) { perc**2 } | |
| }, | |
| { | |
| duration: 8, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y + enemy.h, | |
| }, | |
| { | |
| duration: 8, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: 8, | |
| target_y: enemy.start_y, | |
| perc_y: ->(perc) { perc**2 } | |
| }, | |
| { | |
| duration: 8, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 } | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| damaging: true | |
| }, | |
| { | |
| duration: 8, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: 1280 - 8 - 128, | |
| target_y: enemy.y, | |
| perc_y: ->(perc) { perc**2 } | |
| }, | |
| { | |
| duration: 60, | |
| m: :smooth_stop, | |
| power: 4, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y, | |
| }, | |
| ] | |
| end | |
| def triple_stab player, enemy | |
| [ | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y + enemy.h, | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y + enemy.h, | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_start, | |
| power: 4, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| damaging: true | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y + enemy.h, | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_start, | |
| power: 4, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| damaging: true | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y + enemy.h, | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y + enemy.h, | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_start, | |
| power: 4, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| }, | |
| { | |
| duration: 1, | |
| m: :smooth_start, | |
| power: 2, | |
| target_x: player.x, | |
| target_y: player.y, | |
| perc_x: ->(perc) { perc**2 }, | |
| damaging: true | |
| }, | |
| { | |
| duration: 15, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y, | |
| }, | |
| { | |
| duration: 30, | |
| m: :smooth_stop, | |
| power: 2, | |
| target_x: enemy.start_x, | |
| target_y: enemy.start_y, | |
| }, | |
| ] | |
| end | |
| end | |
| end | |
| GTK.reset |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment