funfriend/src/buddy_context.cr

243 lines
6.0 KiB
Crystal
Raw Normal View History

class Funfriend::BuddyContext < Funfriend::WindowContext
2023-05-31 12:14:38 +02:00
CHATTER_TIMER = 3.0
getter buddy : Buddy
2023-05-30 15:47:50 +02:00
getter renderer : BuddyRenderer
2023-05-31 12:38:50 +02:00
property chatter_timer : Float64 = 1.0
property chatter_index : Int32 = 0
property chatter_array : Array(String)?
2023-05-31 12:38:50 +02:00
property held : Bool = false
property held_at : Vec2 = Vec2.zero
property started_holding_at : Vec2 = Vec2.zero
2023-05-31 12:38:50 +02:00
STAY_STILL_AFTER_HELD = 1.0
property held_timer : Float64 = 0.0
property waiting_for_stable_pos : Bool = false
2023-05-31 12:38:50 +02:00
property static_pos : Vec2
2023-05-31 12:38:50 +02:00
property easing_from : Vec2 = Vec2.zero
property easing_to : Vec2 = Vec2.zero
2023-05-31 12:38:50 +02:00
property easing_dur : Float64 = 0.0
property easing_t : Float64 = 0.0
WANDER_TIMER = 4.0
property wander_timer : Float64 = WANDER_TIMER
def initialize(@buddy : Buddy)
2023-05-30 15:47:50 +02:00
super(
title: "??_#{buddy.name}_??",
width: window_size.x_i, height: window_size.y_i,
2023-05-30 15:47:50 +02:00
transparent: true
)
# just for initialization, let OpenGL know this is the current context
window.make_context_current
@renderer = BuddyRenderer.new(buddy)
2023-05-30 15:47:50 +02:00
window.on_mouse_button do |event|
2023-05-31 12:14:38 +02:00
if event.mouse_button.one?
if event.action.press?
buddy.talk_sound
2023-05-31 12:38:50 +02:00
@easing_dur = 0.0
2023-05-31 12:14:38 +02:00
@held = true
@held_at = Vec2.new({
2023-05-31 12:14:38 +02:00
x: window.cursor.position[:x].to_i,
y: window.cursor.position[:y].to_i,
})
if @held_timer <= 0
@started_holding_at = Vec2.new(window.position)
LOG.info { "starting holding at #{started_holding_at}" }
end
2023-05-31 12:38:50 +02:00
@held_timer = STAY_STILL_AFTER_HELD
2023-05-31 12:14:38 +02:00
window.cursor Window::Cursor::Shape::Hand
elsif event.action.release?
@held = false
window.cursor Window::Cursor::Shape::Arrow
end
end
2023-05-30 15:47:50 +02:00
end
window.on_key do |event|
if event.action.press? && event.key.escape?
event.window.should_close
end
end
# pick a random pos
monitor = Monitor.primary
2023-06-01 12:41:01 +02:00
random_pos = {
2023-05-30 15:47:50 +02:00
x: monitor.position[:x] + (monitor.video_mode.size[:width] * rand((0.0..1.0))).to_i,
y: monitor.position[:y] + (monitor.video_mode.size[:height] * rand((0.0..1.0))).to_i
}
2023-06-01 12:41:01 +02:00
window.position = random_pos
@static_pos = Vec2.new(random_pos)
@chatter_array = buddy.dialog(DialogType::Chatter).sample
2023-06-01 12:41:01 +02:00
end
def window_size
funfriend_size = ConfigMan.config["window"]["funfriend_size"].as(Int32)
Vec2.new((funfriend_size * 1.3).floor)
2023-05-30 15:47:50 +02:00
end
def render(dt : Float64)
# let OpenGL draw to it
window.make_context_current
# draw funfriend
renderer.render(dt, window_size.x_i, window_size.y_i)
2023-05-30 15:47:50 +02:00
end
def goto(pos : Vec2, dur : Float64, set_as_static : Bool = true)
2023-05-31 12:38:50 +02:00
@easing_t = 0.0
@easing_dur = dur
@easing_from = Vec2.new(window.position)
2023-05-31 12:38:50 +02:00
@easing_to = pos
if set_as_static
@static_pos = @easing_to
end
2023-05-31 12:38:50 +02:00
LOG.info { "going from #{easing_from} to #{easing_to}" }
end
enum Behavior
Wander
Follow
Stay
end
FOLLOW_DIST = 120
def behavior : Behavior
speaking ? Behavior::Follow : Behavior::Wander
end
def moving
@easing_dur != 0.0 && @easing_t <= @easing_dur
end
2023-05-31 12:38:50 +02:00
def update_wander(dt : Float64)
if moving
2023-05-31 12:38:50 +02:00
@easing_t = @easing_t + dt
a = Ease.inOutSine(@easing_t / @easing_dur)
window.position = (@easing_from * (1.0 - a) + @easing_to * a).xy_i
2023-05-31 12:38:50 +02:00
@wander_timer = WANDER_TIMER
else
case behavior
when .wander?
@wander_timer = @wander_timer - dt
if @wander_timer <= 0
goto(@static_pos + Vec2.rand((0 .. 40)), 4.0, set_as_static: false)
end
when .follow?
if !moving
x_dist = window.cursor.position[:x]
y_dist = window.cursor.position[:y]
x_target = window.position[:x].to_f
y_target = window.position[:y].to_f
if x_dist.abs > FOLLOW_DIST
x_target = window.position[:x] + x_dist - FOLLOW_DIST * x_dist.sign
end
if y_dist.abs > FOLLOW_DIST
y_target = window.position[:y] + y_dist - FOLLOW_DIST * y_dist.sign
end
goto(Vec2.new(x_target, y_target), 1.0)
end
2023-05-31 12:38:50 +02:00
end
end
end
2023-05-31 12:14:38 +02:00
def update_pos(dt : Float64)
if held
@static_pos = Vec2.new(window.position) - held_at + Vec2.new(window.cursor.position)
window.position = @static_pos.xy_i
2023-05-31 12:38:50 +02:00
else
@held_timer = @held_timer - dt
if @held_timer <= 0
update_wander(dt)
if @waiting_for_stable_pos
@waiting_for_stable_pos = false
stable_pos_dist = @static_pos.dist(@started_holding_at)
LOG.info { "travelled #{stable_pos_dist}" }
if !speaking
if stable_pos_dist > 50
# moved quite a bit from initial point
say buddy.dialog(DialogType::Moved)
else
# just touched
say buddy.dialog(DialogType::Touched)
end
end
end
else
@waiting_for_stable_pos = true
2023-05-31 12:38:50 +02:00
end
2023-05-31 12:14:38 +02:00
end
end
2023-05-31 12:52:39 +02:00
def say(text : String)
Funfriend.contexts.each do |context|
if context.is_a?(ChatterContext) && context.parent == self
context.bump
end
end
Funfriend.add_context(ChatterContext.new(text, Vec2.new(
window.position[:x] + window_size.x / 2,
window.position[:y] - 20
), parent: self))
2023-05-31 12:52:39 +02:00
buddy.talk_sound
end
def say(text : Array(String))
@chatter_array = text
@chatter_timer = 0.0
@chatter_index = 0
end
def say(text : Array(Array(String)))
if text.size > 0
say text.sample
end
2023-05-31 12:52:39 +02:00
end
def speaking
@chatter_array && @chatter_index < @chatter_array.not_nil!.size
end
2023-05-30 15:47:50 +02:00
def update(dt : Float64)
@chatter_timer = @chatter_timer - dt
if @chatter_timer <= 0.0
@chatter_timer = @chatter_timer + CHATTER_TIMER
2023-05-31 12:14:38 +02:00
if @chatter_array.try &.[(@chatter_index)]?
2023-05-31 12:52:39 +02:00
say(@chatter_array.not_nil![@chatter_index])
2023-05-30 15:47:50 +02:00
end
2023-05-31 12:14:38 +02:00
@chatter_index = @chatter_index + 1
2023-05-30 15:47:50 +02:00
end
2023-05-31 12:14:38 +02:00
update_pos(dt)
2023-05-30 15:47:50 +02:00
render(dt)
window.swap_buffers
end
def clean_up
renderer.clean_up
end
end