243 lines
6.0 KiB
Crystal
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, 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
|