Compare commits
3 Commits
ef5325bbf2
...
bb4175d279
Author | SHA1 | Date |
---|---|---|
Jill | bb4175d279 | |
Jill | d198bcc5e6 | |
Jill | be3e13794e |
|
@ -1,8 +1,8 @@
|
||||||
class Funfriend::ChatterContext < Funfriend::WindowContext
|
class Funfriend::ChatterContext < Funfriend::WindowContext
|
||||||
getter renderer : TextRenderer
|
getter renderer : TextRenderer
|
||||||
getter parent : WindowContext?
|
getter parent : WindowContext?
|
||||||
property parent_relative_pos : NamedTuple(x: Int32, y: Int32)
|
property parent_relative_pos : Vec2 = Vec2.zero
|
||||||
getter window_size : NamedTuple(width: Int32, height: Int32)
|
getter window_size : Vec2
|
||||||
property timer : Float64
|
property timer : Float64
|
||||||
|
|
||||||
WINDOW_SIZE = {width: 256, height: 32}
|
WINDOW_SIZE = {width: 256, height: 32}
|
||||||
|
@ -10,19 +10,16 @@ class Funfriend::ChatterContext < Funfriend::WindowContext
|
||||||
|
|
||||||
PADDING = 10
|
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")
|
sheet = FontMan.parse_bm(File.read "assets/fonts/SpaceMono.fnt")
|
||||||
|
|
||||||
position_data = FontMan.position_text(text, sheet)
|
position_data = FontMan.position_text(text, sheet)
|
||||||
|
|
||||||
@window_size = {
|
@window_size = Vec2.new(position_data[:width], position_data[:height]) + PADDING * 2
|
||||||
width: position_data[:width] + PADDING * 2,
|
|
||||||
height: position_data[:height] + PADDING * 2
|
|
||||||
}
|
|
||||||
|
|
||||||
super(
|
super(
|
||||||
title: "??__FUNFRIEND__?? > CHATTER",
|
title: "??__FUNFRIEND__?? > CHATTER",
|
||||||
width: window_size[:width], height: window_size[:height],
|
width: window_size.x_i, height: window_size.y_i,
|
||||||
transparent: false
|
transparent: false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,31 +27,20 @@ class Funfriend::ChatterContext < Funfriend::WindowContext
|
||||||
window.make_context_current
|
window.make_context_current
|
||||||
|
|
||||||
@timer = duration
|
@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 = {
|
window.position = (position - window_size / 2).xy_i
|
||||||
x: position[:x] - window_size[:width]//2,
|
|
||||||
y: position[:y] - window_size[:height]//2,
|
|
||||||
}
|
|
||||||
|
|
||||||
@parent = parent
|
@parent = parent
|
||||||
if parent
|
if parent
|
||||||
@parent_relative_pos = {
|
@parent_relative_pos = position - (Vec2.new(parent.window.position) + Vec2.new(parent.window.size) / 2)
|
||||||
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}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_position
|
def update_position
|
||||||
if parent
|
if parent
|
||||||
p = parent.not_nil!
|
p = parent.not_nil!
|
||||||
window.position = {
|
window.position = (Vec2.new(p.window.position) + Vec2.new(p.window.size) / 2 + parent_relative_pos - window_size / 2).xy_i
|
||||||
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,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -82,10 +68,7 @@ class Funfriend::ChatterContext < Funfriend::WindowContext
|
||||||
end
|
end
|
||||||
|
|
||||||
def bump
|
def bump
|
||||||
@parent_relative_pos = {
|
@parent_relative_pos.y -= window_size.y + 10
|
||||||
x: parent_relative_pos[:x],
|
|
||||||
y: parent_relative_pos[:y] - window_size[:height] - 10
|
|
||||||
}
|
|
||||||
update_position
|
update_position
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,16 @@ module Funfriend::ConfigMan
|
||||||
|
|
||||||
CONFIG_NAME = "cfg.ini"
|
CONFIG_NAME = "cfg.ini"
|
||||||
|
|
||||||
alias ConfigValue = String | Int32 | Bool
|
alias ConfigValue = String | Int32 | Bool | Float64
|
||||||
alias ConfigSection = Hash(String, ConfigValue)
|
alias ConfigSection = Hash(String, ConfigValue)
|
||||||
alias Config = Hash(String, ConfigSection)
|
alias Config = Hash(String, ConfigSection)
|
||||||
|
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"window" => {
|
"window" => {
|
||||||
"funfriend_size" => 64.as(ConfigValue)
|
"funfriend_size" => 64.as(ConfigValue)
|
||||||
|
},
|
||||||
|
"sound" => {
|
||||||
|
"volume" => 0.2.as(ConfigValue)
|
||||||
}
|
}
|
||||||
}.as(Config)
|
}.as(Config)
|
||||||
|
|
||||||
|
@ -59,8 +62,9 @@ module Funfriend::ConfigMan
|
||||||
when String
|
when String
|
||||||
value
|
value
|
||||||
when Int32
|
when Int32
|
||||||
puts "huh"
|
|
||||||
value.to_i32?
|
value.to_i32?
|
||||||
|
when Float64
|
||||||
|
value.to_f64?
|
||||||
when Bool
|
when Bool
|
||||||
value === "1" || value.downcase === "true"
|
value === "1" || value.downcase === "true"
|
||||||
end
|
end
|
||||||
|
@ -70,9 +74,13 @@ module Funfriend::ConfigMan
|
||||||
# write to config
|
# write to config
|
||||||
@@config[sect_i][key] = casted_val.not_nil!
|
@@config[sect_i][key] = casted_val.not_nil!
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
# keep as a string just incase it's ever needed
|
||||||
|
@@config[sect_i][key] = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
File.write(config_file, INI.build(@@config))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@config_initialized = true
|
@@config_initialized = true
|
||||||
|
|
|
@ -5,6 +5,7 @@ require "crystimage"
|
||||||
|
|
||||||
require "./log.cr"
|
require "./log.cr"
|
||||||
require "./ease.cr"
|
require "./ease.cr"
|
||||||
|
require "./vec2.cr"
|
||||||
require "./gl.cr"
|
require "./gl.cr"
|
||||||
require "./configman.cr"
|
require "./configman.cr"
|
||||||
require "./textureman.cr"
|
require "./textureman.cr"
|
||||||
|
|
|
@ -8,20 +8,32 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
||||||
["INTERLOPER!", "WELCOME", "BUT ALSO PLEASE DO NOT BOTHER ME", "VERY BUSY"]
|
["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
|
getter renderer : FunfriendRenderer
|
||||||
property chatter_timer : Float64 = 1.0
|
property chatter_timer : Float64 = 1.0
|
||||||
property chatter_index : Int32 = 0
|
property chatter_index : Int32 = 0
|
||||||
property chatter_array : Array(String)? = CHATTER_ARRAY.sample
|
property chatter_array : Array(String)? = CHATTER_ARRAY.sample
|
||||||
|
|
||||||
property held : Bool = false
|
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
|
STAY_STILL_AFTER_HELD = 1.0
|
||||||
property held_timer : Float64 = 0.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_from : Vec2 = Vec2.zero
|
||||||
property easing_to : NamedTuple(x: Int32, y: Int32) = {x: 0, y: 0}
|
property easing_to : Vec2 = Vec2.zero
|
||||||
property easing_dur : Float64 = 0.0
|
property easing_dur : Float64 = 0.0
|
||||||
property easing_t : Float64 = 0.0
|
property easing_t : Float64 = 0.0
|
||||||
|
|
||||||
|
@ -31,7 +43,7 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
||||||
def initialize
|
def initialize
|
||||||
super(
|
super(
|
||||||
title: "??_FUNFRIEND_??",
|
title: "??_FUNFRIEND_??",
|
||||||
width: window_size[:width], height: window_size[:height],
|
width: window_size.x_i, height: window_size.y_i,
|
||||||
transparent: true
|
transparent: true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,10 +60,14 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
||||||
SoundMan.play_sound("assets/sfx/talk#{(1..8).sample}.ogg")
|
SoundMan.play_sound("assets/sfx/talk#{(1..8).sample}.ogg")
|
||||||
@easing_dur = 0.0
|
@easing_dur = 0.0
|
||||||
@held = true
|
@held = true
|
||||||
@held_at = {
|
@held_at = Vec2.new({
|
||||||
x: window.cursor.position[:x].to_i,
|
x: window.cursor.position[:x].to_i,
|
||||||
y: window.cursor.position[:y].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
|
@held_timer = STAY_STILL_AFTER_HELD
|
||||||
window.cursor Window::Cursor::Shape::Hand
|
window.cursor Window::Cursor::Shape::Hand
|
||||||
elsif event.action.release?
|
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
|
y: monitor.position[:y] + (monitor.video_mode.size[:height] * rand((0.0..1.0))).to_i
|
||||||
}
|
}
|
||||||
window.position = random_pos
|
window.position = random_pos
|
||||||
@static_pos = random_pos
|
@static_pos = Vec2.new(random_pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
def window_size
|
def window_size
|
||||||
funfriend_size = ConfigMan.config["window"]["funfriend_size"].as(Int32)
|
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
|
end
|
||||||
|
|
||||||
def render(dt : Float64)
|
def render(dt : Float64)
|
||||||
|
@ -87,51 +103,101 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
||||||
window.make_context_current
|
window.make_context_current
|
||||||
|
|
||||||
# draw funfriend
|
# draw funfriend
|
||||||
renderer.render(dt, window_size[:width], window_size[:height])
|
renderer.render(dt, window_size.x_i, window_size.y_i)
|
||||||
end
|
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_t = 0.0
|
||||||
@easing_dur = dur
|
@easing_dur = dur
|
||||||
@easing_from = window.position
|
@easing_from = Vec2.new(window.position)
|
||||||
@easing_to = pos
|
@easing_to = pos
|
||||||
|
|
||||||
|
if set_as_static
|
||||||
|
@static_pos = @easing_to
|
||||||
|
end
|
||||||
|
|
||||||
LOG.info { "going from #{easing_from} to #{easing_to}" }
|
LOG.info { "going from #{easing_from} to #{easing_to}" }
|
||||||
end
|
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)
|
def update_wander(dt : Float64)
|
||||||
if @easing_dur != 0.0 && @easing_t <= @easing_dur
|
if moving
|
||||||
@easing_t = @easing_t + dt
|
@easing_t = @easing_t + dt
|
||||||
a = Ease.inOutSine(@easing_t / @easing_dur)
|
a = Ease.inOutSine(@easing_t / @easing_dur)
|
||||||
|
|
||||||
window.position = {
|
window.position = (@easing_from * (1.0 - a) + @easing_to * a).xy_i
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
@wander_timer = WANDER_TIMER
|
@wander_timer = WANDER_TIMER
|
||||||
else
|
else
|
||||||
@wander_timer = @wander_timer - dt
|
case behavior
|
||||||
if @wander_timer <= 0
|
when .wander?
|
||||||
goto({
|
@wander_timer = @wander_timer - dt
|
||||||
x: @static_pos[:x] + (-40 .. 40).sample,
|
if @wander_timer <= 0
|
||||||
y: @static_pos[:y] + (-40 .. 40).sample,
|
goto(@static_pos + Vec2.rand((0 .. 40)), 4.0, set_as_static: false)
|
||||||
}, 4.0)
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_pos(dt : Float64)
|
def update_pos(dt : Float64)
|
||||||
if held
|
if held
|
||||||
window.position = {
|
@static_pos = Vec2.new(window.position) - held_at + Vec2.new(window.cursor.position)
|
||||||
x: window.position[:x] - held_at[:x] + window.cursor.position[:x].to_i,
|
window.position = @static_pos.xy_i
|
||||||
y: window.position[:y] - held_at[:y] + window.cursor.position[:y].to_i,
|
|
||||||
}
|
|
||||||
@static_pos = window.position
|
|
||||||
else
|
else
|
||||||
@held_timer = @held_timer - dt
|
@held_timer = @held_timer - dt
|
||||||
if @held_timer <= 0
|
if @held_timer <= 0
|
||||||
update_wander(dt)
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -143,14 +209,18 @@ class Funfriend::FunfriendContext < Funfriend::WindowContext
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Funfriend.add_context(ChatterContext.new(text, {
|
Funfriend.add_context(ChatterContext.new(text, Vec2.new(
|
||||||
x: window.position[:x] + window_size[:width] // 2,
|
window.position[:x] + window_size.x / 2,
|
||||||
y: window.position[:y] - 20
|
window.position[:y] - 20
|
||||||
}, parent: self))
|
), parent: self))
|
||||||
|
|
||||||
SoundMan.play_sound("assets/sfx/talk#{(1..8).sample}.ogg")
|
SoundMan.play_sound("assets/sfx/talk#{(1..8).sample}.ogg")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def speaking
|
||||||
|
@chatter_array && @chatter_index < @chatter_array.not_nil!.size
|
||||||
|
end
|
||||||
|
|
||||||
def update(dt : Float64)
|
def update(dt : Float64)
|
||||||
@chatter_timer = @chatter_timer - dt
|
@chatter_timer = @chatter_timer - dt
|
||||||
if @chatter_timer <= 0.0
|
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.init(SDL::Mix::Init::OGG); at_exit { SDL::Mix.quit }
|
||||||
SDL::Mix.open
|
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
|
||||||
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