funfriend/src/buddy_context.cr

243 lines
6.0 KiB
Crystal

class Funfriend::BuddyContext < Funfriend::WindowContext
CHATTER_TIMER = 3.0
getter buddy : Buddy
getter renderer : BuddyRenderer
property chatter_timer : Float64 = 1.0
property chatter_index : Int32 = 0
property chatter_array : Array(String)?
property held : Bool = false
property held_at : Vec2 = Vec2.zero
property started_holding_at : Vec2 = Vec2.zero
STAY_STILL_AFTER_HELD = 1.0
property held_timer : Float64 = 0.0
property waiting_for_stable_pos : Bool = false
property static_pos : Vec2
property easing_from : Vec2 = Vec2.zero
property easing_to : Vec2 = Vec2.zero
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)
super(
title: "??_#{buddy.name}_??",
width: window_size.x_i, height: window_size.y_i,
transparent: true
)
# just for initialization, let OpenGL know this is the current context
window.make_context_current
@renderer = BuddyRenderer.new(buddy)
window.on_mouse_button do |event|
if event.mouse_button.one?
if event.action.press?
buddy.talk_sound
@easing_dur = 0.0
@held = true
@held_at = Vec2.new({
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
@held_timer = STAY_STILL_AFTER_HELD
window.cursor Window::Cursor::Shape::Hand
elsif event.action.release?
@held = false
window.cursor Window::Cursor::Shape::Arrow
end
end
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
random_pos = {
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
}
window.position = random_pos
@static_pos = Vec2.new(random_pos)
@chatter_array = buddy.dialog(DialogType::Chatter).sample
end
def window_size
funfriend_size = ConfigMan.config["window"]["funfriend_size"].as(Int32)
Vec2.new((funfriend_size * 1.3).floor)
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)
end
def goto(pos : Vec2, dur : Float64, set_as_static : Bool = true)
@easing_t = 0.0
@easing_dur = dur
@easing_from = Vec2.new(window.position)
@easing_to = pos
if set_as_static
@static_pos = @easing_to
end
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
def update_wander(dt : Float64)
if moving
@easing_t = @easing_t + dt
a = Ease.inOutSine(@easing_t / @easing_dur)
window.position = (@easing_from * (1.0 - a) + @easing_to * a).xy_i
@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
end
end
end
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
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
end
end
end
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, buddy.font, Vec2.new(
window.position[:x] + window_size.x / 2,
window.position[:y] - 20
), parent: self))
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
end
def speaking
@chatter_array && @chatter_index < @chatter_array.not_nil!.size
end
def update(dt : Float64)
@chatter_timer = @chatter_timer - dt
if @chatter_timer <= 0.0
@chatter_timer = @chatter_timer + CHATTER_TIMER
if @chatter_array.try &.[(@chatter_index)]?
say(@chatter_array.not_nil![@chatter_index])
end
@chatter_index = @chatter_index + 1
end
update_pos(dt)
render(dt)
window.swap_buffers
end
def clean_up
renderer.clean_up
end
end