Compare commits
3 Commits
ef5325bbf2
...
bb4175d279
Author | SHA1 | Date |
---|---|---|
Jill | bb4175d279 | |
Jill | d198bcc5e6 | |
Jill | be3e13794e |
|
@ -1,8 +1,8 @@
|
|||
class Funfriend::ChatterContext < Funfriend::WindowContext
|
||||
getter renderer : TextRenderer
|
||||
getter parent : WindowContext?
|
||||
property parent_relative_pos : NamedTuple(x: Int32, y: Int32)
|
||||
getter window_size : NamedTuple(width: Int32, height: Int32)
|
||||
property parent_relative_pos : Vec2 = Vec2.zero
|
||||
getter window_size : Vec2
|
||||
property timer : Float64
|
||||
|
||||
WINDOW_SIZE = {width: 256, height: 32}
|
||||
|
@ -10,19 +10,16 @@ class Funfriend::ChatterContext < Funfriend::WindowContext
|
|||
|
||||
PADDING = 10
|
||||
|
||||
def initialize(text : String, position : NamedTuple(x: Int32, y: Int32), duration : Float64 = DEFAULT_DURATION, parent : WindowContext? = nil)
|
||||
def initialize(text : String, position : Vec2, duration : Float64 = DEFAULT_DURATION, parent : WindowContext? = nil)
|
||||
sheet = FontMan.parse_bm(File.read "assets/fonts/SpaceMono.fnt")
|
||||
|
||||
position_data = FontMan.position_text(text, sheet)
|
||||
|
||||
@window_size = {
|
||||
width: position_data[:width] + PADDING * 2,
|
||||
height: position_data[:height] + PADDING * 2
|
||||
}
|
||||
@window_size = Vec2.new(position_data[:width], position_data[:height]) + PADDING * 2
|
||||
|
||||
super(
|
||||
title: "??__FUNFRIEND__?? > CHATTER",
|
||||
width: window_size[:width], height: window_size[:height],
|
||||
width: window_size.x_i, height: window_size.y_i,
|
||||
transparent: false
|
||||
)
|
||||
|
||||
|
@ -30,31 +27,20 @@ class Funfriend::ChatterContext < Funfriend::WindowContext
|
|||
window.make_context_current
|
||||
|
||||
@timer = duration
|
||||
@renderer = TextRenderer.new(text, sheet, window_size[:width], window_size[:height])
|
||||
@renderer = TextRenderer.new(text, sheet, window_size.x_i, window_size.y_i)
|
||||
|
||||
window.position = {
|
||||
x: position[:x] - window_size[:width]//2,
|
||||
y: position[:y] - window_size[:height]//2,
|
||||
}
|
||||
window.position = (position - window_size / 2).xy_i
|
||||
|
||||
@parent = parent
|
||||
if parent
|
||||
@parent_relative_pos = {
|
||||
x: position[:x] - (parent.window.position[:x] + parent.window.size[:width] // 2),
|
||||
y: position[:y] - (parent.window.position[:y] + parent.window.size[:height] // 2),
|
||||
}
|
||||
else
|
||||
@parent_relative_pos = {x: 0, y: 0}
|
||||
@parent_relative_pos = position - (Vec2.new(parent.window.position) + Vec2.new(parent.window.size) / 2)
|
||||
end
|
||||
end
|
||||
|
||||
def update_position
|
||||
if parent
|
||||
p = parent.not_nil!
|
||||
window.position = {
|
||||
x: p.window.position[:x] + p.window.size[:width] // 2 + parent_relative_pos[:x] - window_size[:width] // 2,
|
||||
y: p.window.position[:y] + p.window.size[:height] // 2 + parent_relative_pos[:y] - window_size[:height] // 2,
|
||||
}
|
||||
window.position = (Vec2.new(p.window.position) + Vec2.new(p.window.size) / 2 + parent_relative_pos - window_size / 2).xy_i
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -82,10 +68,7 @@ class Funfriend::ChatterContext < Funfriend::WindowContext
|
|||
end
|
||||
|
||||
def bump
|
||||
@parent_relative_pos = {
|
||||
x: parent_relative_pos[:x],
|
||||
y: parent_relative_pos[:y] - window_size[:height] - 10
|
||||
}
|
||||
@parent_relative_pos.y -= window_size.y + 10
|
||||
update_position
|
||||
end
|
||||
|
||||
|
|
|
@ -8,13 +8,16 @@ module Funfriend::ConfigMan
|
|||
|
||||
CONFIG_NAME = "cfg.ini"
|
||||
|
||||
alias ConfigValue = String | Int32 | Bool
|
||||
alias ConfigValue = String | Int32 | Bool | Float64
|
||||
alias ConfigSection = Hash(String, ConfigValue)
|
||||
alias Config = Hash(String, ConfigSection)
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"window" => {
|
||||
"funfriend_size" => 64.as(ConfigValue)
|
||||
},
|
||||
"sound" => {
|
||||
"volume" => 0.2.as(ConfigValue)
|
||||
}
|
||||
}.as(Config)
|
||||
|
||||
|
@ -59,8 +62,9 @@ module Funfriend::ConfigMan
|
|||
when String
|
||||
value
|
||||
when Int32
|
||||
puts "huh"
|
||||
value.to_i32?
|
||||
when Float64
|
||||
value.to_f64?
|
||||
when Bool
|
||||
value === "1" || value.downcase === "true"
|
||||
end
|
||||
|
@ -70,9 +74,13 @@ module Funfriend::ConfigMan
|
|||
# write to config
|
||||
@@config[sect_i][key] = casted_val.not_nil!
|
||||
end
|
||||
else
|
||||
# keep as a string just incase it's ever needed
|
||||
@@config[sect_i][key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
File.write(config_file, INI.build(@@config))
|
||||
end
|
||||
|
||||
@@config_initialized = true
|
||||
|
|
|
@ -5,6 +5,7 @@ require "crystimage"
|
|||
|
||||
require "./log.cr"
|
||||
require "./ease.cr"
|
||||
require "./vec2.cr"
|
||||
require "./gl.cr"
|
||||
require "./configman.cr"
|
||||
require "./textureman.cr"
|
||||
|
|
|
@ -8,20 +8,32 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
|||
["INTERLOPER!", "WELCOME", "BUT ALSO PLEASE DO NOT BOTHER ME", "VERY BUSY"]
|
||||
]
|
||||
|
||||
STAY_ARRAY = [
|
||||
"OK I'LL BE HERE"
|
||||
]
|
||||
|
||||
TOUCH_ARRAY = [
|
||||
"HI INTERLOPER!",
|
||||
"HELLO!",
|
||||
"HI!"
|
||||
]
|
||||
|
||||
getter renderer : FunfriendRenderer
|
||||
property chatter_timer : Float64 = 1.0
|
||||
property chatter_index : Int32 = 0
|
||||
property chatter_array : Array(String)? = CHATTER_ARRAY.sample
|
||||
|
||||
property held : Bool = false
|
||||
property held_at : NamedTuple(x: Int32, y: Int32) = {x: 0, y: 0}
|
||||
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 : NamedTuple(x: Int32, y: Int32)
|
||||
property static_pos : Vec2
|
||||
|
||||
property easing_from : NamedTuple(x: Int32, y: Int32) = {x: 0, y: 0}
|
||||
property easing_to : NamedTuple(x: Int32, y: Int32) = {x: 0, y: 0}
|
||||
property easing_from : Vec2 = Vec2.zero
|
||||
property easing_to : Vec2 = Vec2.zero
|
||||
property easing_dur : Float64 = 0.0
|
||||
property easing_t : Float64 = 0.0
|
||||
|
||||
|
@ -31,7 +43,7 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
|||
def initialize
|
||||
super(
|
||||
title: "??_FUNFRIEND_??",
|
||||
width: window_size[:width], height: window_size[:height],
|
||||
width: window_size.x_i, height: window_size.y_i,
|
||||
transparent: true
|
||||
)
|
||||
|
||||
|
@ -48,10 +60,14 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
|||
SoundMan.play_sound("assets/sfx/talk#{(1..8).sample}.ogg")
|
||||
@easing_dur = 0.0
|
||||
@held = true
|
||||
@held_at = {
|
||||
@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?
|
||||
|
@ -74,12 +90,12 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
|||
y: monitor.position[:y] + (monitor.video_mode.size[:height] * rand((0.0..1.0))).to_i
|
||||
}
|
||||
window.position = random_pos
|
||||
@static_pos = random_pos
|
||||
@static_pos = Vec2.new(random_pos)
|
||||
end
|
||||
|
||||
def window_size
|
||||
funfriend_size = ConfigMan.config["window"]["funfriend_size"].as(Int32)
|
||||
{width: (funfriend_size * 1.3).to_i, height: (funfriend_size * 1.3).to_i}
|
||||
Vec2.new((funfriend_size * 1.3).floor)
|
||||
end
|
||||
|
||||
def render(dt : Float64)
|
||||
|
@ -87,51 +103,101 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
|||
window.make_context_current
|
||||
|
||||
# draw funfriend
|
||||
renderer.render(dt, window_size[:width], window_size[:height])
|
||||
renderer.render(dt, window_size.x_i, window_size.y_i)
|
||||
end
|
||||
|
||||
def goto(pos : NamedTuple(x: Int32, y: Int32), dur : Float64)
|
||||
def goto(pos : Vec2, dur : Float64, set_as_static : Bool = true)
|
||||
@easing_t = 0.0
|
||||
@easing_dur = dur
|
||||
@easing_from = window.position
|
||||
@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 @easing_dur != 0.0 && @easing_t <= @easing_dur
|
||||
if moving
|
||||
@easing_t = @easing_t + dt
|
||||
a = Ease.inOutSine(@easing_t / @easing_dur)
|
||||
|
||||
window.position = {
|
||||
x: (@easing_from[:x] * (1.0 - a) + @easing_to[:x] * a).to_i,
|
||||
y: (@easing_from[:y] * (1.0 - a) + @easing_to[:y] * a).to_i,
|
||||
}
|
||||
window.position = (@easing_from * (1.0 - a) + @easing_to * a).xy_i
|
||||
|
||||
@wander_timer = WANDER_TIMER
|
||||
else
|
||||
@wander_timer = @wander_timer - dt
|
||||
if @wander_timer <= 0
|
||||
goto({
|
||||
x: @static_pos[:x] + (-40 .. 40).sample,
|
||||
y: @static_pos[:y] + (-40 .. 40).sample,
|
||||
}, 4.0)
|
||||
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
|
||||
window.position = {
|
||||
x: window.position[:x] - held_at[:x] + window.cursor.position[:x].to_i,
|
||||
y: window.position[:y] - held_at[:y] + window.cursor.position[:y].to_i,
|
||||
}
|
||||
@static_pos = window.position
|
||||
@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 STAY_ARRAY.sample
|
||||
else
|
||||
# just touched
|
||||
say TOUCH_ARRAY.sample
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
@waiting_for_stable_pos = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -143,14 +209,18 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
|||
end
|
||||
end
|
||||
|
||||
Funfriend.add_context(ChatterContext.new(text, {
|
||||
x: window.position[:x] + window_size[:width] // 2,
|
||||
y: window.position[:y] - 20
|
||||
}, parent: self))
|
||||
Funfriend.add_context(ChatterContext.new(text, Vec2.new(
|
||||
window.position[:x] + window_size.x / 2,
|
||||
window.position[:y] - 20
|
||||
), parent: self))
|
||||
|
||||
SoundMan.play_sound("assets/sfx/talk#{(1..8).sample}.ogg")
|
||||
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
|
||||
|
|
|
@ -20,6 +20,6 @@ module Funfriend::SoundMan
|
|||
SDL::Mix.init(SDL::Mix::Init::OGG); at_exit { SDL::Mix.quit }
|
||||
SDL::Mix.open
|
||||
|
||||
SDL::Mix::Channel.volume = SDL::Mix::MAX_VOLUME // 5
|
||||
SDL::Mix::Channel.volume = SDL::Mix::MAX_VOLUME * ConfigMan.config["sound"]["volume"].as(Float64)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
require "math"
|
||||
|
||||
include Math
|
||||
|
||||
module Funfriend
|
||||
struct Vec2
|
||||
property x : Float64
|
||||
property y : Float64
|
||||
|
||||
def initialize(@x = 0.0, @y = 0.0)
|
||||
end
|
||||
def initialize(xy : Int32)
|
||||
@x = @y = xy
|
||||
end
|
||||
def initialize(xy : Float64)
|
||||
@x = @y = xy
|
||||
end
|
||||
def initialize(xy : NamedTuple(x: Int32, y: Int32))
|
||||
initialize(xy[:x], xy[:y])
|
||||
end
|
||||
def initialize(xy : NamedTuple(x: Float64, y: Float64))
|
||||
initialize(xy[:x], xy[:y])
|
||||
end
|
||||
def initialize(wh : NamedTuple(width: Int32, height: Int32))
|
||||
initialize(wh[:width], wh[:height])
|
||||
end
|
||||
|
||||
# swizzles
|
||||
|
||||
def xy
|
||||
{x: @x, y: @y}
|
||||
end
|
||||
def yx
|
||||
{x: @y, y: @x}
|
||||
end
|
||||
|
||||
# int swizzles
|
||||
|
||||
def xy_i
|
||||
{x: @x.to_i, y: @y.to_i}
|
||||
end
|
||||
def yx_i
|
||||
{x: @y.to_i, y: @x.to_i}
|
||||
end
|
||||
|
||||
def x_i
|
||||
x.to_i
|
||||
end
|
||||
def y_i
|
||||
y.to_i
|
||||
end
|
||||
|
||||
# simpler swizzle
|
||||
|
||||
def values
|
||||
{x, y}
|
||||
end
|
||||
|
||||
def dot(other : Vec2)
|
||||
x * other.x + y * other.y
|
||||
end
|
||||
|
||||
def cross(other : Vec2)
|
||||
Vec2.new(
|
||||
self.x * other.y - self.y * other.x,
|
||||
self.y * other.x - self.x * other.y
|
||||
)
|
||||
end
|
||||
|
||||
def angle
|
||||
atan2(y, x)
|
||||
end
|
||||
|
||||
def len
|
||||
sqrt(x ** 2 + y ** 2)
|
||||
end
|
||||
def square_len
|
||||
x ** 2 + y ** 2
|
||||
end
|
||||
|
||||
def angle(other : Vec2)
|
||||
self ** other / (self.length * other.length)
|
||||
end
|
||||
|
||||
def +(other : Vec2)
|
||||
Vec2.new(self.x + other.x, self.y + other.y)
|
||||
end
|
||||
def +(other : Number)
|
||||
Vec2.new(self.x + other, self.y + other)
|
||||
end
|
||||
def -(other : Vec2)
|
||||
Vec2.new(self.x - other.x, self.y - other.y)
|
||||
end
|
||||
def -(other : Number)
|
||||
Vec2.new(self.x - other, self.y - other)
|
||||
end
|
||||
def -
|
||||
Vec2.new(-self.x, -self.y)
|
||||
end
|
||||
def *(other : Vec2)
|
||||
Vec2.new(self.x * other.x, self.y * other.y)
|
||||
end
|
||||
def *(other : Number)
|
||||
Vec2.new(self.x * other, self.y * other)
|
||||
end
|
||||
def /(other : Vec2)
|
||||
Vec2.new(self.x / other.x, self.y / other.y)
|
||||
end
|
||||
def /(other : Number)
|
||||
Vec2.new(self.x / other, self.y / other)
|
||||
end
|
||||
|
||||
def clone
|
||||
Vec2.new(self.x, self.y)
|
||||
end
|
||||
|
||||
def normalize!
|
||||
m = length
|
||||
unless m == 0
|
||||
inverse = 1.0 / m
|
||||
self.x *= inverse
|
||||
self.y *= inverse
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def normalize
|
||||
clone.normalize!
|
||||
end
|
||||
|
||||
def scale!(scale : Float64)
|
||||
normalize!
|
||||
self.x *= scale
|
||||
self.y *= scale
|
||||
end
|
||||
|
||||
def scale(scale : Float64)
|
||||
clone.scale!(scale)
|
||||
end
|
||||
|
||||
def dist(other : Vec2)
|
||||
(self - other).len
|
||||
end
|
||||
def square_dist(other : Vec2)
|
||||
(self - other).square_len
|
||||
end
|
||||
|
||||
def ==(other : Vec2)
|
||||
self.x == other.x && self.y == other.y
|
||||
end
|
||||
|
||||
def !=(other : Vec2)
|
||||
self.x != other.x || self.y != other.y
|
||||
end
|
||||
|
||||
def to_s
|
||||
"(#{x}, #{y})"
|
||||
end
|
||||
def inspect
|
||||
"(x: #{x.inspect}, y: #{y.inspect})"
|
||||
end
|
||||
|
||||
def self.zero
|
||||
Vec2.new(0.0)
|
||||
end
|
||||
def self.additive_identity
|
||||
Vec2.new(0.0)
|
||||
end
|
||||
def self.multiplicative_identity
|
||||
Vec2.new(1.0)
|
||||
end
|
||||
|
||||
def self.from_polar(angle : Float64, length : Float64)
|
||||
Vec2.new(cos(angle) * length, sin(angle) * length)
|
||||
end
|
||||
|
||||
def self.rand(length : Range = (0 .. 1))
|
||||
from_polar((0..360).sample, length.sample)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue