145 lines
2.9 KiB
Go
145 lines
2.9 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"math"
|
||
|
"sort"
|
||
|
)
|
||
|
|
||
|
type Unit struct {
|
||
|
Kind int
|
||
|
Hitpoints int
|
||
|
Power int
|
||
|
Tile *Tile
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
defaultHitpoints = 200
|
||
|
defaultPower = 3
|
||
|
)
|
||
|
|
||
|
type SortableUnits []*Unit
|
||
|
|
||
|
func (s SortableUnits) Len() int {
|
||
|
return len(s)
|
||
|
}
|
||
|
func (s SortableUnits) Less(i, j int) bool {
|
||
|
if s[i].Tile.Y == s[j].Tile.Y {
|
||
|
return s[i].Tile.X < s[j].Tile.X
|
||
|
}
|
||
|
return s[i].Tile.Y < s[j].Tile.Y
|
||
|
}
|
||
|
|
||
|
func (s SortableUnits) Swap(i, j int) {
|
||
|
s[i], s[j] = s[j], s[i]
|
||
|
}
|
||
|
|
||
|
func NewUnit(tile *Tile, kind, elfPower int) *Unit {
|
||
|
unit := &Unit{
|
||
|
Kind: kind,
|
||
|
Hitpoints: defaultHitpoints,
|
||
|
Power: defaultPower,
|
||
|
Tile: tile,
|
||
|
}
|
||
|
tile.Unit = unit
|
||
|
if unit.Kind == KindElf {
|
||
|
unit.Power = elfPower
|
||
|
}
|
||
|
return unit
|
||
|
}
|
||
|
|
||
|
func (u Unit) Targets(c *Cave) bool {
|
||
|
for _, unit := range c.Units {
|
||
|
if unit.Kind != u.Kind && unit.Hitpoints > 0 {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// NextTile returns the next tile to move to and the target tile (or nil if no target found)
|
||
|
func (u *Unit) NextTile(c *Cave) (*Tile, *Tile) {
|
||
|
var targets SortableTiles
|
||
|
closestTargetDistance := math.MaxInt32
|
||
|
distances, path := c.Map.FindWalkableTiles(u.Tile)
|
||
|
enemies := u.Enemies(c)
|
||
|
for _, enemy := range enemies {
|
||
|
for _, target := range enemy.Tile.WalkableNeighbors() {
|
||
|
if distance, ok := distances[target]; ok && distance <= closestTargetDistance {
|
||
|
if distance < closestTargetDistance {
|
||
|
closestTargetDistance = distance
|
||
|
targets = SortableTiles{}
|
||
|
}
|
||
|
targets = append(targets, target)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
sort.Sort(targets)
|
||
|
if len(targets) > 0 {
|
||
|
target := targets[0]
|
||
|
current := target
|
||
|
for {
|
||
|
if path[current] == u.Tile {
|
||
|
return current, target
|
||
|
}
|
||
|
current = path[current]
|
||
|
}
|
||
|
}
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
// Enemies returns enemy units sorted by map position in reading order
|
||
|
func (u *Unit) Enemies(c *Cave) SortableUnits {
|
||
|
var enemies SortableUnits
|
||
|
for _, unit := range c.Units {
|
||
|
if unit.Kind != u.Kind && unit.Hitpoints > 0 {
|
||
|
enemies = append(enemies, unit)
|
||
|
}
|
||
|
}
|
||
|
sort.Sort(enemies)
|
||
|
return enemies
|
||
|
}
|
||
|
|
||
|
func (u *Unit) EnemyNeighbor(c *Cave) *Unit {
|
||
|
var target *Unit
|
||
|
for _, offset := range offsets {
|
||
|
if t := c.Map.Tile(u.Tile.X+offset.X, u.Tile.Y+offset.Y); t != nil && t.Unit != nil && t.Unit.Kind != u.Kind && t.Unit.Hitpoints > 0 {
|
||
|
if target == nil || t.Unit.Hitpoints < target.Hitpoints {
|
||
|
target = t.Unit
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return target
|
||
|
}
|
||
|
|
||
|
func (u *Unit) Move(c *Cave) {
|
||
|
if u.EnemyNeighbor(c) != nil {
|
||
|
return
|
||
|
}
|
||
|
next, _ := u.NextTile(c)
|
||
|
if next != nil {
|
||
|
next.Unit = u
|
||
|
next.Kind = u.Kind
|
||
|
u.Tile.Kind = KindSpace
|
||
|
u.Tile.Unit = nil
|
||
|
u.Tile = next
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (u *Unit) Attack(c *Cave) bool {
|
||
|
enemy := u.EnemyNeighbor(c)
|
||
|
if enemy != nil {
|
||
|
killed := enemy.Damage(c, u.Power)
|
||
|
return killed && enemy.Kind == KindElf
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (u *Unit) Damage(c *Cave, damage int) bool {
|
||
|
u.Hitpoints = u.Hitpoints - damage
|
||
|
if u.Hitpoints <= 0 {
|
||
|
c.RemoveUnit(u)
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|