Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Last active October 21, 2025 12:59
Show Gist options
  • Select an option

  • Save amirrajan/aa40adfa985fa2cb3aea1ad4b7a198db to your computer and use it in GitHub Desktop.

Select an option

Save amirrajan/aa40adfa985fa2cb3aea1ad4b7a198db to your computer and use it in GitHub Desktop.
DragonRuby Game Toolkit - Parry 33
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