Last active
July 22, 2025 23:31
-
-
Save amirrajan/4abbd7941c4d54865d7b96d9398f1c40 to your computer and use it in GitHub Desktop.
DragonRuby Game Toolkit - I Have A Jet Pack (https://amirrajan.itch.io/i-have-a-jetpack)
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 :args | |
def tick | |
defaults | |
current_scene = state.scene | |
calc | |
render | |
state.clock += 1 | |
if current_scene != state.scene | |
raise "Scene changed during tick. Set state.next_scene." | |
end | |
if state.next_scene | |
state.scene = state.next_scene | |
state.scene_at = state.clock | |
state.next_scene = nil | |
end | |
end | |
def new_player | |
{ | |
x: 360, | |
y: 0, | |
w: 16 * 2, | |
h: 16 * 2, | |
path: :solid, | |
dy: 0, | |
dx: 0, | |
angle: 90, | |
dangle: 0, | |
right_rocket_power: 0, | |
left_rocket_power: 0, | |
dead: false, | |
max_altitude: 0, | |
rotation_count: 0, | |
rotation_accumulator: 0, | |
} | |
end | |
def input_player | |
return if state.scene != :game | |
if player.dead && player.dead_at.elapsed_time(state.clock) > 30 && inputs_continue? | |
state.player = new_player | |
elsif !player.dead | |
if inputs_right_rocket? | |
player.right_rocket_at ||= state.clock | |
else | |
player.right_rocket_at = nil | |
player.right_rocket_power = 0 | |
end | |
if inputs_left_rocket? | |
player.left_rocket_at ||= state.clock | |
else | |
player.left_rocket_at = nil | |
player.left_rocket_power = 0 | |
end | |
end | |
end | |
def inputs_left_rocket? | |
inputs.keyboard.f || inputs.controller_one.l1 || touch_controls_left_pressed? | |
end | |
def inputs_right_rocket? | |
inputs.keyboard.j || inputs.controller_one.r1 || touch_controls_right_pressed? | |
end | |
def calc_player | |
input_player | |
calc_player_alive | |
calc_player_dead | |
end | |
def calc_player_alive | |
return if player.dead | |
if player.right_rocket_at | |
player.right_rocket_power += 0.05 | |
player.right_rocket_power = player.right_rocket_power.clamp(0, 1) | |
player.dx += 0.1 * player.angle.vector_x | |
player.dangle += (0.5 * player.right_rocket_power) | |
right_exhaust_x = player.x + 4 | |
right_exhaust_y = (player.y + player.h / 2) - 12 | |
state.particle_queue << { x: right_exhaust_x, | |
y: right_exhaust_y, | |
anchor_x: 0.5, | |
anchor_y: 0.5, | |
w: 6, | |
h: 6, | |
r: 180, | |
g: 0, | |
b: 0, | |
a: 255, | |
path: :solid } | |
end | |
if player.left_rocket_at | |
player.left_rocket_power += 0.05 | |
player.left_rocket_power = player.left_rocket_power.clamp(0, 1) | |
player.dx += 0.1 * player.angle.vector_x | |
player.dangle -= (0.5 * player.left_rocket_power) | |
right_exhaust_x = player.x - 4 | |
right_exhaust_y = (player.y + player.h / 2) - 12 | |
state.particle_queue << { x: right_exhaust_x, | |
y: right_exhaust_y, | |
anchor_x: 0.5, | |
anchor_y: 0.5, | |
w: 6, | |
h: 6, | |
r: 180, | |
g: 0, | |
b: 0, | |
a: 255, | |
path: :solid } | |
end | |
if player.right_rocket_at || player.left_rocket_at | |
player.dy += 0.16 * player.angle.vector_y | |
end | |
player.dangle = player.dangle.clamp(-4, 4) | |
player.dy = player.dy.clamp(-3, 3) | |
player.y += player.dy | |
player.dy -= 0.10 | |
if player.y > 2 | |
player.on_ground = false | |
player.on_ground_at = nil | |
end | |
if player.y <= 0 && player.dy < 0 | |
if player.dy < -2.1 | |
play_hit_sound | |
player.dead = true | |
player.dead_at ||= state.clock | |
player.death_reason ||= :speed | |
player.death_speed ||= player.dy | |
elsif !Geometry.angle_within_range?(player.angle, 90, 15) | |
play_hit_sound | |
player.dead = true | |
player.dead_at ||= state.clock | |
player.death_reason ||= :angle | |
player.death_angle ||= player.angle | |
end | |
if player.dy.abs > 0.34 | |
player.dy *= -0.5 | |
elsif player.dy < 0 | |
player.dy = 0 | |
player.on_ground = true | |
player.on_ground_at ||= state.clock | |
end | |
player.y = 0 | |
end | |
player.x += player.dx | |
player.dx *= 0.9 | |
player.dangle *= 0.9 | |
if player.on_ground | |
player.angle = player.angle.lerp(90, 0.9).round | |
else | |
player.angle += player.dangle | |
player.rotation_accumulator += player.dangle | |
player.angle = player.angle % 360 | |
end | |
calc_hazards | |
calc_stats | |
determine_mission_success | |
if player.dead | |
state.next_scene = :dead | |
end | |
end | |
def calc_player_dead | |
return if !player.dead | |
player.dy = player.dy.clamp(-3, 3) | |
player.y += player.dy | |
player.dy -= 0.10 | |
if player.y > 2 | |
player.on_ground = false | |
player.on_ground_at = nil | |
end | |
if player.y <= 0 && player.dy < 0 | |
if player.dy.abs > 0.34 | |
play_hit_sound | |
player.dy *= -0.5 | |
player.dangle = 0 | |
if player.angle > 0 && player.angle < 90 | |
player.angle = player.angle.lerp(0, 0.9) | |
elsif player.angle > 90 && player.angle < 180 | |
player.angle = player.angle.lerp(180, 0.9) | |
elsif player.angle > 180 && player.angle < 270 | |
player.angle = player.angle.lerp(180, 0.9) | |
elsif player.angle > 270 && player.angle < 360 | |
player.angle = player.angle.lerp(0, 0.9) | |
end | |
elsif player.dy < 0 | |
player.dy = 0 | |
player.on_ground = true | |
player.on_ground_at ||= state.clock | |
end | |
player.right_rocket_power = 0 | |
player.left_rocket_power = 0 | |
player.dx *= 0.9 | |
player.y = 0 | |
end | |
player.x += player.dx | |
player.dx *= 0.9 | |
if player.on_ground | |
player.dangle *= 0.9 | |
else | |
player.angle += player.dangle | |
player.rotation_accumulator += player.dangle | |
player.angle = player.angle % 360 | |
end | |
player.left_rocket_at = nil | |
player.right_rocket_at = nil | |
if player.on_ground | |
if player.angle > 90 | |
player.angle = player.angle.lerp(180, 0.1) | |
else | |
player.angle = player.angle.lerp(0, 0.1) | |
end | |
end | |
player.dx *= 0.99 | |
player.x += player.dx | |
calc_hazards | |
end | |
def player_hurt_box | |
Geometry.rect_props(player).merge(w: 4, h: 4, anchor_x: 0.5, anchor_y: -1.5) | |
end | |
def calc_hazards | |
hazard = Geometry.find_intersect_rect(player_hurt_box, state.hazards) | |
if hazard | |
lowrez.primitives << hazard.merge(path: :solid, r: 255, g: 0, b: 0) | |
player.killed_by ||= hazard | |
player.dead = true | |
player.dead_at ||= state.clock | |
angle = Geometry.angle player, hazard | |
player.dx = angle.vector_x * -4 | |
player.dy = angle.vector_y * -2 | |
if angle.vector_x != 0 | |
player.dangle = 10 * angle.vector_x.sign | |
end | |
play_hit_sound | |
end | |
end | |
def calc_stats | |
if player.max_altitude < player.y | |
player.max_altitude = player.y | |
end | |
if player.rotation_accumulator.abs > 360 | |
player.rotation_count += 1 | |
player.rotation_accumulator = player.rotation_accumulator % 360 | |
end | |
end | |
def determine_mission_success | |
return if state.scene != :game | |
return if player.dead | |
results = current_mission.successful_if.call(args) | |
if current_mission.successful_if.call(args) | |
state.next_scene = :success | |
end | |
end | |
def calc | |
calc_edit_map | |
calc_game_scene | |
calc_mission_scene | |
calc_success_scene | |
calc_dead_scene | |
end | |
def calc_success_scene | |
return if state.scene != :success | |
if state.scene_at.elapsed_time(state.clock) > 30 && inputs_continue? | |
state.completed_missions << current_mission.id | |
state.current_mission_id = current_mission.next_mission | |
state.next_scene = :mission | |
end | |
end | |
def calc_dead_scene | |
return if state.scene != :dead | |
if state.scene_at.elapsed_time(state.clock) > 30 && inputs_continue? | |
state.player = new_player | |
state.next_scene = :game | |
end | |
end | |
def calc_game_scene | |
calc_player | |
calc_camera | |
calc_particles | |
end | |
def calc_mission_scene | |
return if state.scene != :mission | |
if state.scene_at.elapsed_time(state.clock) > 30 && inputs_continue? | |
state.next_scene = :game | |
end | |
end | |
def inputs_continue? | |
inputs.keyboard.key_down.enter || | |
inputs.controller_one.key_down.start || | |
inputs.controller_one.key_down.a || | |
inputs.mouse.click | |
end | |
def calc_particles | |
state.particle_queue.each do |particle| | |
# particle.x += particle.dx | |
# particle.y += particle.dy | |
# particle.dx *= 0.9 | |
# particle.dy *= 0.9 | |
particle.a -= 10 | |
particle.w -= 0.15 | |
particle.h -= 0.15 | |
end | |
state.particle_queue.reject! do |particle| | |
particle.a < 0 | |
end | |
end | |
def calc_camera | |
state.camera.target_x = player.x + player.dx * 8 | |
state.camera.target_y = player.y + player.dy * 8 | |
state.camera.x += (state.camera.target_x - state.camera.x) * 0.3 | |
state.camera.y += (state.camera.target_y - state.camera.y) * 0.3 | |
end | |
def player_prefab | |
if player.dead | |
player.merge(x: state.player.x.to_i, | |
y: state.player.y.to_i - 4 - 8, | |
angle: (player.angle - 90).ifloor(10), | |
anchor_x: 0.5, | |
path: "sprites/player.png") | |
else | |
path = if player.right_rocket_at && player.left_rocket_at | |
"sprites/player-both-rocket.png" | |
elsif player.right_rocket_at | |
"sprites/player-right-rocket.png" | |
elsif player.left_rocket_at | |
"sprites/player-left-rocket.png" | |
else | |
"sprites/player.png" | |
end | |
path = "sprites/player.png" if state.clock.zmod? 4 | |
player.merge(x: state.player.x.to_i, | |
y: state.player.y.to_i - 8, | |
angle: (player.angle - 90).ifloor(10), | |
anchor_x: 0.5, | |
path: path) | |
end | |
end | |
def mouse_pos | |
{ | |
x: (args.inputs.mouse.x - (Grid.w - rt_size * rt_scale) / 2).idiv(rt_scale), | |
y: args.inputs.mouse.y.idiv(rt_scale) | |
} | |
end | |
def map_size | |
720 | |
end | |
def rt_size | |
64 | |
end | |
def mouse_click? | |
inputs.mouse.click | |
end | |
def defaults | |
outputs.background_color = [0, 0, 0] | |
hud.background_color = [0, 0, 0, 0] | |
hud.w = rt_size | |
hud.h = rt_size | |
lowrez.w = map_size | |
lowrez.h = map_size | |
lowrez.primitives << { | |
x: 0, | |
y: 0, | |
w: map_size, | |
h: map_size, | |
r: 255, | |
g: 255, | |
b: 255, | |
path: :solid | |
} | |
state.clock ||= 0 | |
state.completed_missions ||= [] | |
state.player ||= new_player | |
state.camera ||= { | |
x: 0, | |
y: 0, | |
target_x: 0, | |
target_y: 0, | |
} | |
state.scene ||= :mission | |
state.scene_at ||= state.clock | |
state.current_mission_id ||= missions.first.id | |
state.hazards ||= [] | |
state.particle_queue ||= [] | |
state.touch_controls ||= { | |
left: Layout.rect(row: 19, col: 0, w: 3, h: 3), | |
right: Layout.rect(row: 19, col: 9, w: 3, h: 3), | |
} | |
args.audio[:bg] ||= { | |
input: "sounds/birds-chirping.mp3" | |
} | |
args.audio[:rocket] ||= { | |
input: "sounds/rocket.mp3", | |
gain: 0.0, | |
looping: true | |
} | |
if player.right_rocket_at || player.left_rocket_at | |
args.audio[:rocket].gain += 0.01 | |
else | |
args.audio[:rocket].gain -= 0.01 | |
end | |
args.audio[:rocket].gain = args.audio[:rocket].gain.clamp(0, 0.05) | |
end | |
def play_hit_sound | |
args.audio[:hit] ||= { | |
input: "sounds/hit.ogg", | |
gain: 0.1 | |
} | |
end | |
def current_mission | |
missions.find { |m| m.id == state.current_mission_id } | |
end | |
def missions | |
@missions ||= [ | |
{ | |
id: :above_trees, | |
name: "mission 1:", | |
description_1: "fly above trees", | |
description_2: "land safely", | |
successful_if: lambda do |args| | |
args.state.player.max_altitude >= 60 && | |
args.state.player.on_ground && | |
args.state.player.on_ground_at.elapsed_time(args.state.clock) > 30 | |
end, | |
next_mission: :barrel_roll | |
}, | |
{ | |
id: :barrel_roll, | |
name: "mission 2:", | |
description_1: "do a barrel roll", | |
description_2: "land safely", | |
successful_if: lambda do |args| | |
args.state.player.rotation_count >= 1 && | |
args.state.player.on_ground && | |
args.state.player.on_ground_at.elapsed_time(args.state.clock) > 30 | |
end, | |
next_mission: :reach_the_stars | |
}, | |
{ | |
id: :reach_the_stars, | |
name: "mission 3:", | |
description_1: "reach the stars", | |
description_2: "land safely", | |
successful_if: lambda do |args| | |
args.state.player.max_altitude >= 650 && | |
args.state.player.on_ground && | |
args.state.player.on_ground_at.elapsed_time(args.state.clock) > 30 | |
end, | |
next_mission: :saws | |
}, | |
{ | |
id: :saws, | |
name: "mission 4:", | |
description_1: "reach the stars", | |
description_2: "avoid the saws", | |
successful_if: lambda do |args| | |
args.state.player.max_altitude >= 650 && | |
args.state.player.on_ground && | |
args.state.player.on_ground_at.elapsed_time(args.state.clock) > 30 | |
end, | |
hazards_path: "data/saws.txt", | |
next_mission: :free_roam | |
}, | |
{ | |
id: :free_roam, | |
name: "mission 5:", | |
description_1: "free roam", | |
description_2: "enjoy", | |
successful_if: lambda do |args| | |
false | |
end, | |
next_mission: :free_roam | |
}, | |
] | |
@missions.each do |m| | |
m.hazards_path ||= "data/map.txt" | |
end | |
end | |
def load_hazards | |
contents = GTK.read_file(current_mission.hazards_path) | |
if !contents | |
GTK.write_file(path, "") | |
end | |
contents = GTK.read_file(current_mission.hazards_path) | |
contents.split("\n").map do |line| | |
x, y, w, h = line.split(",").map(&:to_i) | |
{ x: x, | |
y: y, | |
w: w, | |
h: h, | |
path: "sprites/saw.png", | |
r: 255, | |
g: 255, | |
b: 255 } | |
end | |
end | |
def save_hazards | |
contents = state.hazards.map do |hazard| | |
"#{hazard[:x]},#{hazard[:y]},#{hazard[:w]},#{hazard[:h]}" | |
end.join("\n") | |
GTK.write_file(current_mission.hazards_path, contents) | |
end | |
def calc_edit_map | |
if inputs.keyboard.key_down.tab | |
if state.scene == :game | |
state.next_scene = :edit | |
else | |
state.next_scene = :game | |
end | |
end | |
return if state.scene != :edit | |
if inputs.mouse.click || inputs.mouse.held | |
x = inputs.mouse.x.ifloor(4) - 4 | |
y = (inputs.mouse.y - ((1280 - map_size) / 2)).ifloor(4) - 4 | |
hazard_to_add = { x: x, | |
y: y, | |
w: 16, | |
h: 16, | |
path: "sprites/saw.png", | |
r: 255, | |
g: 255, | |
b: 255 } | |
hazard = Geometry.find_intersect_rect(hazard_to_add, state.hazards) | |
if hazard && inputs.keyboard.x | |
state.hazards.delete(hazard) | |
save_hazards | |
elsif !hazard && !inputs.keyboard.x | |
state.hazards << hazard_to_add | |
save_hazards | |
end | |
end | |
end | |
def render | |
outputs.watch player.angle | |
lowrez.primitives << { x: 0, y: 0, w: 720, h: 720, path: "sprites/bg.png" } | |
lowrez.primitives << state.hazards.map do |w| | |
w.merge(angle: state.clock * 10) | |
end | |
lowrez.primitives << state.particle_queue | |
lowrez.primitives << player_prefab | |
render_game_scene | |
render_edit_scene | |
render_mission_scene | |
render_success_scene | |
render_dead_scene | |
render_controls | |
lowrez.primitives << player.killed_by | |
end | |
def render_dead_scene | |
return if state.scene != :dead | |
render_viewport | |
hud.primitives << { x: 0, y: 0, w: 64, h: 64, path: :solid, r: 0, g: 0, b: 0, a: 128 } | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: current_mission.name, | |
anchor_x: 0.5, | |
anchor_y: -5.0, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: current_mission.description_1, | |
anchor_x: 0.5, | |
anchor_y: -3.5, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: current_mission.description_2, | |
anchor_x: 0.5, | |
anchor_y: -2.0, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << md_label(x: 33, | |
y: 33, | |
text: "wasted", | |
anchor_x: 0.5, | |
anchor_y: 0.50, | |
r: 255, | |
g: 0, | |
b: 0) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: "#{input_name_for_enter_key}", | |
anchor_x: 0.5, | |
anchor_y: 3.0, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: "to try again", | |
anchor_x: 0.5, | |
anchor_y: 4.5, | |
r: 255, | |
g: 255, | |
b: 255) | |
end | |
def touch_controls_left_pressed? | |
inputs.finger_left && inputs.finger_left.intersect_rect?(state.touch_controls.left) | |
end | |
def touch_controls_right_pressed? | |
inputs.finger_right && inputs.finger_right.intersect_rect?(state.touch_controls.right) | |
end | |
def touch_controls_left_prefab | |
if touch_controls_left_pressed? | |
state.touch_controls.left.merge(path: "sprites/button-pressed.png") | |
else | |
state.touch_controls.left.merge(path: "sprites/button-released.png") | |
end | |
end | |
def touch_controls_right_prefab | |
if touch_controls_right_pressed? | |
state.touch_controls.right.merge(path: "sprites/button-pressed.png") | |
else | |
state.touch_controls.right.merge(path: "sprites/button-released.png") | |
end | |
end | |
def controller_l1_prefab | |
state.touch_controls.left.merge(path: "sprites/controller-l1.png") | |
end | |
def controller_r1_prefab | |
state.touch_controls.right.merge(path: "sprites/controller-r1.png") | |
end | |
def keyboard_f_prefab | |
state.touch_controls.left.merge(path: "sprites/keyboard-f.png") | |
end | |
def keyboard_j_prefab | |
state.touch_controls.right.merge(path: "sprites/keyboard-j.png") | |
end | |
def render_controls | |
outputs.primitives << { x: 360, | |
y: 640, | |
text: "#{inputs_scheme_name} controls", | |
r: 255, | |
g: 255, | |
b: 255, | |
anchor_x: 0.5, | |
anchor_y: 7.0, | |
size_px: 50 } | |
outputs.primitives << state.touch_controls.left.center.merge(text: "left rocket", | |
anchor_x: 0.5, | |
anchor_y: 4.0, | |
size_px: 35, | |
r: 255, | |
g: 255, | |
b: 255) | |
outputs.primitives << state.touch_controls.right.center.merge(text: "right rocket", | |
anchor_x: 0.5, | |
anchor_y: 4.0, | |
size_px: 35, | |
r: 255, | |
g: 255, | |
b: 255) | |
if inputs_last_active_resolved == :mouse | |
outputs.primitives << touch_controls_left_prefab | |
outputs.primitives << touch_controls_right_prefab | |
elsif inputs_last_active_resolved == :controller | |
outputs.primitives << controller_l1_prefab | |
outputs.primitives << controller_r1_prefab | |
else | |
outputs.primitives << keyboard_f_prefab | |
outputs.primitives << keyboard_j_prefab | |
end | |
end | |
def render_success_scene | |
return if state.scene != :success | |
render_viewport | |
hud.primitives << { x: 0, y: 0, w: 64, h: 64, path: :solid, r: 0, g: 0, b: 0, a: 128 } | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: current_mission.name, | |
anchor_x: 0.5, | |
anchor_y: -5.0, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: current_mission.description_1, | |
anchor_x: 0.5, | |
anchor_y: -3.5, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: current_mission.description_2, | |
anchor_x: 0.5, | |
anchor_y: -2.0, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << md_label(x: 33, | |
y: 33, | |
text: "success", | |
anchor_x: 0.5, | |
anchor_y: 0.50, | |
r: 0, | |
g: 255, | |
b: 0) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: "#{input_name_for_enter_key}", | |
anchor_x: 0.5, | |
anchor_y: 3.0, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 32, | |
y: 33, | |
text: "for next mission", | |
anchor_x: 0.5, | |
anchor_y: 4.5, | |
r: 255, | |
g: 255, | |
b: 255) | |
end | |
def input_name_for_enter_key | |
case inputs_last_active_resolved | |
when :controller | |
"press start" | |
when :keyboard | |
"press enter" | |
else | |
"tap screen" | |
end | |
end | |
def inputs_last_active_resolved | |
r = inputs.last_active | |
if r == :mouse && !GTK.platform?(:touch) | |
r = :keyboard | |
end | |
r | |
end | |
def inputs_scheme_name | |
case inputs_last_active_resolved | |
when :controller | |
"gamepad" | |
when :mouse | |
"touch" | |
when :keyboard | |
"keyboard" | |
else | |
"keyboard" | |
end | |
end | |
def render_edit_scene | |
return if state.scene != :edit | |
render_full_map | |
end | |
def render_game_scene | |
return if state.scene != :game | |
render_viewport | |
end | |
def render_mission_scene | |
return if state.scene != :mission | |
if state.scene_at == state.clock | |
state.player = new_player | |
state.hazards = load_hazards | |
end | |
render_viewport | |
hud.primitives << { x: 0, y: 0, w: 64, h: 64, path: :solid, r: 0, g: 0, b: 0, a: 128 } | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: current_mission.name, | |
anchor_x: 0.5, | |
anchor_y: -5.0, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: current_mission.description_1, | |
anchor_x: 0.5, | |
anchor_y: -3.5, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: current_mission.description_2, | |
anchor_x: 0.5, | |
anchor_y: -2.0, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: "#{input_name_for_enter_key}", | |
anchor_x: 0.5, | |
anchor_y: 3.0, | |
r: 255, | |
g: 255, | |
b: 255) | |
hud.primitives << sm_label(x: 33, | |
y: 33, | |
text: "To Begin", | |
anchor_x: 0.5, | |
anchor_y: 4.5, | |
r: 255, | |
g: 255, | |
b: 255) | |
end | |
def rt_scale | |
11.25 | |
end | |
def render_viewport | |
if player.y > 70 | |
state.target_scale = 2.0 | |
else | |
state.target_scale = 1.0 | |
end | |
state.scale ||= 1.0 | |
state.scale = Easing.smooth_start(initial: state.scale, | |
final: state.target_scale, | |
perc: 0.05, | |
power: 1) | |
source_x = state.camera.x - 32 * state.scale | |
source_y = state.camera.y - 32 * state.scale | |
source_x = source_x.clamp(0, 720 - 64 * state.scale) | |
source_y = source_y.clamp(0, 720 - 64 * state.scale) | |
source_x = source_x.to_i | |
source_y = source_y.to_i | |
outputs[:viewport].w = 64 | |
outputs[:viewport].h = 64 | |
outputs[:viewport].sprites << { x: 0, | |
y: 0, | |
w: rt_size, | |
h: rt_size, | |
source_x: source_x, | |
source_y: source_y, | |
source_w: rt_size * state.scale, | |
source_h: rt_size * state.scale, | |
path: :lowrez, | |
anchor_x: 0.0, | |
anchor_y: 0.0 } | |
outputs.sprites << { x: 360, | |
y: 1280 - rt_size * rt_scale, | |
w: rt_size * rt_scale, | |
h: rt_size * rt_scale, | |
anchor_x: 0.5, | |
anchor_y: 0.0, | |
path: :viewport } | |
outputs.sprites << { x: 360, | |
y: 1280 - rt_size * rt_scale, | |
w: rt_size * rt_scale, | |
h: rt_size * rt_scale, | |
anchor_x: 0.5, | |
anchor_y: 0.0, | |
path: :hud } | |
end | |
def render_full_map | |
outputs.sprites << { x: 360, | |
y: 640, | |
w: 720, | |
h: 720, | |
path: :lowrez, | |
anchor_x: 0.5, | |
anchor_y: 0.5 } | |
end | |
def sm_label opts | |
{ anchor_x: 0, anchor_y: 0, size_px: 5, font: "fonts/pico8.ttf", **opts } | |
end | |
def md_label opts | |
{ anchor_x: 0, anchor_y: 0, size_px: 10, font: "fonts/pico8.ttf", **opts } | |
end | |
def lg_label opts | |
{ anchor_x: 0, anchor_y: 0, size_px: 15, font: "fonts/pico8.ttf", **opts } | |
end | |
def outputs | |
args.outputs | |
end | |
def state | |
args.state ||= {} | |
end | |
def lowrez | |
outputs[:lowrez] | |
end | |
def hud | |
outputs[:hud] | |
end | |
def inputs | |
args.inputs | |
end | |
def player | |
state.player | |
end | |
end | |
def boot args | |
args.state = nil | |
end | |
def tick args | |
$game ||= Game.new | |
$game.args = args | |
$game.tick | |
end | |
def reset args | |
$game = nil | |
end | |
# GTK.reset |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment