2023-03-27 02:09:28 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# A scene which updates and renders a list of options that get passed through.
|
|
|
|
#
|
|
|
|
# +menu_options+ data structure:
|
|
|
|
# [
|
|
|
|
# {
|
|
|
|
# text: 'some string',
|
|
|
|
# on_select: -> (args) { 'do some stuff in this lambda' }
|
|
|
|
# }
|
|
|
|
# ]
|
|
|
|
class MenuScene < SceneInstance
|
2023-04-11 06:26:39 +00:00
|
|
|
def initialize(args, opts = {}, title = title(), menu_options = [])
|
2023-03-27 02:09:28 +00:00
|
|
|
super args, opts
|
|
|
|
|
|
|
|
@menu_state ||= {
|
|
|
|
current_option_i: 0,
|
|
|
|
hold_delay: 0
|
|
|
|
}
|
2023-05-08 07:03:13 +00:00
|
|
|
@spacer ||= 80
|
2023-03-27 02:09:28 +00:00
|
|
|
@menu_options ||= menu_options
|
2023-04-11 06:26:39 +00:00
|
|
|
@menu_y = opts.menu_y._?(440)
|
|
|
|
@title ||= title
|
|
|
|
@rand_strings = (0..@menu_options.length).map do |i|
|
|
|
|
(0...@title.length).map { ('A'..'Z').to_a[rand(26)] }.join
|
|
|
|
end
|
|
|
|
@first_render = nil
|
2023-03-27 02:09:28 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def render_options(args)
|
|
|
|
labels = []
|
|
|
|
@menu_options.each.with_index do |option, i|
|
2023-04-11 06:26:39 +00:00
|
|
|
active = @menu_state.current_option_i == i && (!mobile? || (mobile? && args.inputs.controller_one.connected))
|
|
|
|
|
2023-03-27 02:09:28 +00:00
|
|
|
text = case option.kind
|
|
|
|
when :toggle
|
|
|
|
"#{text(option[:key])}: #{text_for_setting_val(args, option[:key])}"
|
|
|
|
else
|
|
|
|
text(option[:key])
|
|
|
|
end
|
|
|
|
|
2023-04-11 06:26:39 +00:00
|
|
|
if (args.state.tick_count - @first_render) < 60 * (1.5 + i) * 0.2
|
|
|
|
if args.state.tick_count % 4 == 0
|
|
|
|
@rand_strings[i] = (0...(rand(text.length >= 3 ? text.length : 3) + 3)).map { ('A'..'Z').to_a[rand(26)] }.join
|
|
|
|
end
|
|
|
|
text = @rand_strings[i]
|
|
|
|
active = false
|
|
|
|
end
|
|
|
|
|
2023-03-27 02:09:28 +00:00
|
|
|
l = label(
|
2023-04-11 06:26:39 +00:00
|
|
|
text.upcase,
|
|
|
|
x: (args.grid.w / 2) - 70,
|
2023-03-27 02:09:28 +00:00
|
|
|
y: @menu_y + (@menu_options.length - (i * @spacer)),
|
|
|
|
align: ALIGN_CENTER,
|
2023-04-11 06:26:39 +00:00
|
|
|
size: 16,
|
|
|
|
font: FONT_DOTMATRIX_BOLD
|
2023-03-27 02:09:28 +00:00
|
|
|
)
|
|
|
|
l.key = option[:key]
|
2023-04-11 06:26:39 +00:00
|
|
|
l.width, l.height = args.gtk.calcstringbox(l.text, l.size_enum, l.font)
|
|
|
|
|
|
|
|
labels << l.merge(active ? WHITE : YELLOW)
|
|
|
|
|
|
|
|
if active
|
|
|
|
labels << label(
|
|
|
|
'.',
|
|
|
|
x: l.x - (l.width / 2) - 26 - (Math.sin(args.state.tick_count / 8) * 4),
|
|
|
|
y: l.y + 15,
|
|
|
|
align: ALIGN_CENTER,
|
|
|
|
size: 18,
|
|
|
|
font: FONT_DOTMATRIX
|
|
|
|
).merge(WHITE)
|
2023-03-27 02:09:28 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
button_border = { w: 340, h: 80, x: l.x - 170, y: l.y - 55 }.merge(WHITE)
|
2023-05-08 07:03:13 +00:00
|
|
|
# (args.outputs.borders << button_border) if mobile?
|
2023-03-27 02:09:28 +00:00
|
|
|
if args.inputs.mouse.up && args.inputs.mouse.inside_rect?(button_border)
|
2023-03-27 06:03:10 +00:00
|
|
|
o = @menu_options.find { |o| o[:key] == l[:key] }
|
2023-03-27 02:09:28 +00:00
|
|
|
Sound.play(args, :menu)
|
|
|
|
o[:on_select].call(args) if o
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
args.outputs.labels << labels
|
|
|
|
end
|
|
|
|
|
|
|
|
# called every tick of the game loop
|
|
|
|
def tick(args)
|
|
|
|
super
|
2023-04-11 06:26:39 +00:00
|
|
|
@first_render = args.state.tick_count if @first_render.nil?
|
|
|
|
|
|
|
|
Sprite.for(:menu).render(args)
|
|
|
|
|
|
|
|
args.outputs.labels << label(
|
|
|
|
@title.to_s.upcase,
|
|
|
|
x: (args.grid.w / 2) - 70,
|
|
|
|
y: args.grid.top - 175,
|
|
|
|
align: ALIGN_CENTER,
|
|
|
|
size: SIZE_LG,
|
|
|
|
font: FONT_RUBIK_BLACK
|
|
|
|
).merge(TRUE_BLACK)
|
2023-03-27 02:09:28 +00:00
|
|
|
|
|
|
|
render_options(args)
|
|
|
|
|
|
|
|
move = nil
|
|
|
|
if Input.down?(args)
|
|
|
|
move = :down
|
|
|
|
elsif Input.up?(args)
|
|
|
|
move = :up
|
|
|
|
else
|
|
|
|
@menu_state.hold_delay = 0
|
|
|
|
end
|
|
|
|
|
|
|
|
if move
|
|
|
|
@menu_state.hold_delay -= 1
|
|
|
|
|
|
|
|
if @menu_state.hold_delay <= 0
|
|
|
|
Sound.play(args, :menu)
|
|
|
|
index = @menu_state.current_option_i
|
|
|
|
if move == :up
|
|
|
|
index -= 1
|
|
|
|
else
|
|
|
|
index += 1
|
|
|
|
end
|
|
|
|
|
|
|
|
if index.negative?
|
|
|
|
index = @menu_options.length - 1
|
|
|
|
elsif index > @menu_options.length - 1
|
|
|
|
index = 0
|
|
|
|
end
|
|
|
|
@menu_state.current_option_i = index
|
|
|
|
@menu_state.hold_delay = 10
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if Input.pressed?(args, :primary)
|
|
|
|
@menu_options[@menu_state.current_option_i][:on_select].call(args)
|
|
|
|
Sound.play(args, :select)
|
|
|
|
end
|
2023-03-27 06:03:10 +00:00
|
|
|
|
|
|
|
if Input.pressed?(args, :secondary) && !Scene.stack(args).empty?
|
|
|
|
Sound.play(args, :select)
|
|
|
|
Scene.pop(args)
|
|
|
|
end
|
2023-03-27 02:09:28 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# custom logic to reset this scene
|
|
|
|
def reset(args)
|
|
|
|
super
|
|
|
|
|
|
|
|
@menu_state.current_option_i = 0
|
|
|
|
@menu_state.hold_delay = 0
|
2023-04-11 06:26:39 +00:00
|
|
|
@first_render = nil
|
2023-03-27 02:09:28 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def text_for_setting_val(args, key)
|
|
|
|
val = args.state.setting[key]
|
|
|
|
case val
|
|
|
|
when true
|
|
|
|
text(:on)
|
|
|
|
when false
|
|
|
|
text(:off)
|
|
|
|
else
|
|
|
|
val
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|