krampus19/soko/state.go

145 lines
3.2 KiB
Go
Raw Normal View History

package soko
import "opslag.de/schobers/geom"
type State struct {
Player Entity
Target Entity
BasicTiles Locations
MagmaTiles Locations
Bricks EntityLocations
SunkenBricks EntityLocations
Entities Entities
nextEntityID int
}
func NewState() State {
return State{
BasicTiles: Locations{},
MagmaTiles: Locations{},
Bricks: EntityLocations{},
SunkenBricks: EntityLocations{},
Entities: Entities{},
}
}
// Clone creates and returns a clone of the state.
func (s State) Clone() State {
return State{
Player: s.Player,
Target: s.Target,
BasicTiles: s.BasicTiles.Clone(),
MagmaTiles: s.MagmaTiles.Clone(),
Bricks: s.Bricks.Clone(),
SunkenBricks: s.SunkenBricks.Clone(),
Entities: s.Entities.Clone(),
nextEntityID: s.nextEntityID,
}
}
func (s *State) addEntity(pos geom.Point, typ EntityType, add func(Entity)) {
id := s.nextEntityID
e := Entity{id, pos, typ}
add(e)
s.Entities[id] = e
s.nextEntityID++
}
func (s *State) AddBrick(pos geom.Point) {
s.addEntity(pos, EntityTypeBrick, func(e Entity) {
s.Bricks.Add(pos, e.ID)
})
}
func (s State) All(pred func(State, geom.Point) bool, p ...geom.Point) bool {
for _, p := range p {
if !pred(s, p) {
return false
}
}
return true
}
func (s State) Any(pred func(State, geom.Point) bool, p ...geom.Point) bool {
for _, p := range p {
if pred(s, p) {
return true
}
}
return false
}
func (s State) IsOpenForBrick(p geom.Point) bool {
if s.IsWalkable(p) {
return !s.Bricks.Has(p)
}
return s.MagmaTiles[p]
}
// IsWalkable indicates that a player could walk over this tile regardless of any bricks that might be there.
func (s State) IsWalkable(p geom.Point) bool {
return s.BasicTiles[p] || (s.MagmaTiles[p] && s.SunkenBricks.Has(p))
}
// MovePlayer tries to move the player in the specified direction. Returns the new state and true if the move is valid. Returns the current state and false is the move was invalid.
func (s State) MovePlayer(dir Direction) (State, bool) {
to := s.Player.Pos.Add(dir.ToPoint())
if !s.IsWalkable(to) {
return s, false
}
if !s.Bricks.Has(to) {
return s.Mutate(func(s *State) {
s.movePlayer(to)
}), true
}
brickTo := to.Add(dir.ToPoint())
if !s.IsOpenForBrick(brickTo) {
return s, false
}
brickID := s.Bricks[to]
return s.Mutate(func(s *State) {
s.movePlayer(to)
if s.MagmaTiles[brickTo] && !s.SunkenBricks.Has(brickTo) {
s.Bricks.Remove(to)
s.SunkenBricks.Add(brickTo, brickID)
} else {
s.Bricks.Move(to, brickTo)
}
s.Entities[brickID] = Entity{brickID, brickTo, EntityTypeBrick}
}), true
}
// Mutate clones the state, applies the mutation on the clone and returns the clone.
func (s State) Mutate(fn func(s *State)) State {
clone := s.Clone()
fn(&clone)
return clone
}
func (s *State) movePlayer(to geom.Point) {
s.Player.Pos = to
s.Entities[s.Player.ID] = Entity{s.Player.ID, to, EntityTypePlayer}
}
func (s *State) SetPlayer(pos geom.Point) {
s.addEntity(pos, EntityTypePlayer, func(e Entity) {
s.Player = e
})
}
func (s *State) SetTarget(pos geom.Point) {
s.addEntity(pos, EntityTypeTarget, func(e Entity) {
s.Target = e
})
}
func IsMagma(s State, p geom.Point) bool { return s.MagmaTiles[p] }