ichigo/engine/walls.go

119 lines
2.7 KiB
Go
Raw Normal View History

2021-08-26 20:06:38 +10:00
package engine
import (
"image"
2021-09-08 20:08:57 +10:00
"drjosh.dev/gurgle/geom"
2021-08-26 20:06:38 +10:00
"github.com/hajimehoshi/ebiten/v2"
)
var (
_ interface {
Collider
Identifier
Scanner
2021-09-01 13:46:26 +10:00
Prepper
Transformer
2021-08-26 20:06:38 +10:00
} = &Wall{}
_ interface {
Drawer
Disabler
Hider
2021-09-01 09:52:40 +10:00
Scanner
Transformer
2021-08-26 20:06:38 +10:00
} = &WallUnit{}
)
// Wall is a more flexible kind of tilemap. WallUnits can be added at the same
// level as other components and are responsible for their own drawing, so that
2021-09-01 09:55:18 +10:00
// Game can do draw ordering, e.g. hide the player character behind a wall.
2021-08-26 20:06:38 +10:00
// But Wall is still responsible for collisions.
type Wall struct {
ID
2021-08-27 12:00:20 +10:00
Ersatz bool // disables collisions ("fake wall")
2021-08-26 20:06:38 +10:00
Offset image.Point // offset the whole wall
Sheet Sheet
UnitOffset image.Point // drawing offset
UnitSize image.Point // tile size
2021-09-01 13:46:26 +10:00
Units map[image.Point]*WallUnit
2021-08-26 20:06:38 +10:00
}
2021-09-01 13:46:26 +10:00
// CollidesWith implements a tilerange collosion check, similar to Tilemap.
2021-09-08 20:08:57 +10:00
func (w *Wall) CollidesWith(b geom.Box) bool {
2021-08-26 20:06:38 +10:00
if w.Ersatz {
return false
}
// Probe the map at all tilespace coordinates overlapping the rect.
2021-09-02 20:17:45 +10:00
r := b.XY().Sub(w.Offset)
2021-09-08 20:08:57 +10:00
min := geom.CDiv(r.Min, w.UnitSize)
max := geom.CDiv(r.Max.Sub(image.Pt(1, 1)), w.UnitSize) // NB: fencepost
2021-08-26 20:06:38 +10:00
for j := min.Y; j <= max.Y; j++ {
for i := min.X; i <= max.X; i++ {
2021-09-01 13:46:26 +10:00
if w.Units[image.Pt(i, j)] != nil {
2021-08-26 20:06:38 +10:00
return true
}
}
}
return false
}
2021-09-22 15:55:38 +10:00
// Scan visits &w.Sheet and all WallUnits.
2021-09-22 15:48:02 +10:00
func (w *Wall) Scan(visit func(interface{}) error) error {
if err := visit(&w.Sheet); err != nil {
return err
}
for _, unit := range w.Units {
if err := visit(unit); err != nil {
return err
}
}
return nil
}
2021-09-01 13:46:26 +10:00
2021-09-02 15:55:38 +10:00
// Prepare makes sure all WallUnits know about Wall and where they are, for
// drawing.
2021-09-01 13:46:26 +10:00
func (w *Wall) Prepare(*Game) error {
// Ensure all child units know about wall, which houses common attributes
2021-09-02 15:55:38 +10:00
for p, u := range w.Units {
u.pos, u.wall = p, w
2021-09-01 13:46:26 +10:00
}
return nil
}
// Transform returns a GeoM translation by Offset.
2021-09-07 14:00:50 +10:00
func (w *Wall) Transform() (opts ebiten.DrawImageOptions) {
2021-09-08 20:08:57 +10:00
opts.GeoM.Translate(geom.CFloat(w.Offset))
2021-09-07 14:00:50 +10:00
return opts
2021-09-01 13:46:26 +10:00
}
2021-08-26 20:06:38 +10:00
2021-08-31 15:44:25 +10:00
// WallUnit is a unit in a wall. Unlike a tile in a tilemap, WallUnit is
// responsible for drawing itself.
2021-08-26 20:06:38 +10:00
type WallUnit struct {
2021-09-13 16:50:35 +10:00
Disables
Hides
2021-09-02 15:55:38 +10:00
Tile Tile // chooses which cell in wall.Sheet to draw
2021-08-26 20:06:38 +10:00
2021-09-02 15:55:38 +10:00
pos image.Point // tilespace coordinates
2021-08-26 20:06:38 +10:00
wall *Wall
}
2021-09-01 13:46:26 +10:00
// Draw draws this wall unit.
func (u *WallUnit) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
2021-09-02 11:53:04 +10:00
screen.DrawImage(u.wall.Sheet.SubImage(u.Tile.Cell()), opts)
2021-08-26 20:06:38 +10:00
}
2021-09-22 15:55:38 +10:00
// Scan visits u.Tile.
2021-09-22 15:48:02 +10:00
func (u *WallUnit) Scan(visit func(interface{}) error) error {
return visit(u.Tile)
}
2021-09-07 14:00:50 +10:00
func (u *WallUnit) Transform() (opts ebiten.DrawImageOptions) {
2021-09-08 20:08:57 +10:00
opts.GeoM.Translate(geom.CFloat(
geom.CMul(u.pos, u.wall.UnitSize).Add(u.wall.UnitOffset),
2021-09-03 10:08:56 +10:00
))
2021-09-07 14:00:50 +10:00
return opts
}