package engine import ( "math" "math/rand" ) type Positioned struct { pos Position } func WithPosition(pos Position) Positioned { return Positioned{ pos: pos, } } func (wp *Positioned) Position() Position { return wp.pos } func (wp *Positioned) SetPosition(pos Position) { wp.pos = pos } type Position struct { x int y int } func PositionAt(x int, y int) Position { return Position{int(x), int(y)} } func (p Position) X() int { return p.x } func (p Position) Y() int { return p.y } func (p Position) XY() (int, int) { return p.x, p.y } func (p Position) DistanceSquared(pos Position) int { return (pos.x-p.x)*(pos.x-p.x) + (pos.y-p.y)*(pos.y-p.y) } func (p Position) Distance(pos Position) int { return int(math.Floor(math.Sqrt(float64(p.DistanceSquared(pos))))) } func (p Position) Equals(other Position) bool { return p.x == other.x && p.y == other.y } func (p Position) WithOffset(xOffset int, yOffset int) Position { p.x = p.x + xOffset p.y = p.y + yOffset return p } func (p Position) Diff(other Position) Position { p.x = p.x - other.x p.y = p.y - other.y return p } func (p Position) Sign() Position { p.x = IntSign(p.x) p.y = IntSign(p.y) return p } func IntSign(i int) int { if i < 0 { return -1 } if i > 0 { return 1 } return 0 } type Sized struct { size Size } func WithSize(size Size) Sized { return Sized{ size: size, } } // Checks if the provided coordinates fit within the sized struct, [0, N) func (ws *Sized) FitsWithin(x, y int) bool { return 0 <= x && x < ws.size.width && 0 <= y && y < ws.size.height } func (ws *Sized) Size() Size { return ws.size } type Size struct { width int height int } func SizeOf(width int, height int) Size { return Size{int(width), int(height)} } func (s Size) Width() int { return s.width } func (s Size) Height() int { return s.height } func (s Size) WH() (int, int) { return s.width, s.height } func (s Size) Area() int { return s.width * s.height } func (s Size) AsArrayIndex(x, y int) int { return y*s.width + x } func (s Size) Contains(x, y int) bool { return 0 <= x && x < s.width && 0 <= y && y < s.height } func LimitAdd(original, amount, limit int) int { if original+amount > limit { return limit } return original + amount } func LimitIncrement(i int, limit int) int { if (i + 1) > limit { return i } return i + 1 } func LimitSubtract(original, amount, limit int) int { if original-amount < limit { return limit } return original - amount } func LimitDecrement(i int, limit int) int { if (i - 1) < limit { return i } return i - 1 } func RandInt(min, max int) int { if min == max { return min } return min + rand.Intn(max-min) } func MapSlice[S ~[]E, E any, R any](slice S, mappingFunc func(e E) R) []R { newSlice := make([]R, 0, len(slice)) for _, el := range slice { newSlice = append(newSlice, mappingFunc(el)) } return newSlice } func AbsInt(val int) int { switch { case val < 0: return -val case val == 0: return 0 } return val } func Lerp(start, end float64, t float64) float64 { return start*(1.0-t) + end*t } func LerpPositions(p0, p1 Position, t float64) Position { return PositionAt( int(math.Round(Lerp(float64(p0.x), float64(p1.x), t))), int(math.Round(Lerp(float64(p0.y), float64(p1.y), t))), ) }