First run at basic structure

This commit is contained in:
2025-11-19 01:19:03 -07:00
commit bd33e7e123
10 changed files with 699 additions and 0 deletions

117
internal/hero/hero.go Normal file
View File

@@ -0,0 +1,117 @@
package hero
import (
"image/color"
"math"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/vector"
)
// Direction flags from the controls.
type Input struct {
Left bool
Right bool
Up bool
Down bool
}
// Playfield limits for movement.
type Bounds struct {
Width float64
Height float64
}
// Player avatar data.
type Hero struct {
X float64
Y float64
Radius float64
Speed float64
Color color.NRGBA
}
// Spawn settings for the avatar.
type Config struct {
StartX float64
StartY float64
Radius float64
Speed float64
Color color.NRGBA
}
// Builds an avatar from the config with fallbacks.
func New(cfg Config) *Hero {
if cfg.Radius <= 0 {
cfg.Radius = 24
}
if cfg.Speed <= 0 {
cfg.Speed = 180
}
if cfg.Color.A == 0 {
cfg.Color = color.NRGBA{R: 210, G: 220, B: 255, A: 255}
}
return &Hero{
X: cfg.StartX,
Y: cfg.StartY,
Radius: cfg.Radius,
Speed: cfg.Speed,
Color: cfg.Color,
}
}
// Applies movement input and clamps to the playfield.
func (h *Hero) Update(input Input, dt float64, bounds Bounds) {
dx, dy := 0.0, 0.0
if input.Left {
dx -= 1
}
if input.Right {
dx += 1
}
if input.Up {
dy -= 1
}
if input.Down {
dy += 1
}
if dx != 0 || dy != 0 {
length := math.Hypot(dx, dy)
dx /= length
dy /= length
}
h.X += dx * h.Speed * dt
h.Y += dy * h.Speed * dt
maxX := math.Max(h.Radius, bounds.Width-h.Radius)
maxY := math.Max(h.Radius, bounds.Height-h.Radius)
h.X = clamp(h.X, h.Radius, maxX)
h.Y = clamp(h.Y, h.Radius, maxY)
}
// Renders the avatar as a filled circle.
func (h *Hero) Draw(screen *ebiten.Image) {
vector.FillCircle(
screen,
float32(h.X),
float32(h.Y),
float32(h.Radius),
h.Color,
false,
)
}
func clamp(value, min, max float64) float64 {
if value < min {
return min
}
if value > max {
return max
}
return value
}