package game import ( "log" ) type UnitMark int const ( paralysis UnitMark = iota + 1 poison panic_ rage ward faith ) func (mark UnitMark) String() string { switch mark { case paralysis: return "paralysis" case poison: return "poison" case panic_: return "panic" case rage: return "rage" case ward: return "ward" case faith: return "faith" default: log.Fatalf("Unhandled unit mark %d", mark) return "" } } var UnitMarks = struct { Paralysis UnitMark Poison UnitMark Panic UnitMark Rage UnitMark Ward UnitMark Faith UnitMark }{ Paralysis: paralysis, Poison: poison, Panic: panic_, Rage: rage, Ward: ward, Faith: faith, } const ( DEFAULT_AVAIL_MOVE_ACTIONS = 1 DEFAULT_AVAIL_ATTACK_ACTIONS = 1 ) type Unit struct { PermanentBase Health int Movement Movement Attack Attack Upkeep int FullActions []*FullAction FreeActions []*FreeAction States map[UnitMark]int // Effects []Effects AvailMoveActions int additionalMoveActions int AvailAttackActions int additionalAttackActions int } func NewUnit(card *Card, tile *Tile, owner *Player) *Unit { var movement Movement movementDesc, found := card.Values["movement"] if !found { movement = INVALID_MOVEMENT() } else { movement = parseMovement(movementDesc) } upkeep, ok := card.Values["upkeep"].(int) if !ok { upkeep = 0 } var attack Attack if attackDesc, found := card.Values["attack"]; found { attack = parseAttack(attackDesc) } u := &Unit{ PermanentBase: NewPermanentBase(card, tile, owner), Health: card.Values["health"].(int), Movement: movement, Attack: attack, Upkeep: upkeep, States: make(map[UnitMark]int), AvailMoveActions: DEFAULT_AVAIL_MOVE_ACTIONS, AvailAttackActions: DEFAULT_AVAIL_ATTACK_ACTIONS, } // TODO: parse effect for additional actions u.FullActions = card.Impl.fullActions(u) u.FreeActions = card.Impl.freeActions(u) tile.Permanent = u return u } func NewUnitFromPath(cardPath string, tile *Tile, owner *Player) *Unit { return NewUnit(NewCard(cardPath), tile, owner) } func (u *Unit) resetBaseActions() { u.AvailMoveActions = DEFAULT_AVAIL_MOVE_ACTIONS + u.additionalMoveActions u.AvailAttackActions = DEFAULT_AVAIL_ATTACK_ACTIONS + u.additionalAttackActions } func (u *Unit) move(s *State, tile *Tile) { u.tile.Permanent = nil tile.Permanent = u u.tile = tile } func (u *Unit) MoveRangeTiles(m *Map) []*Tile { tiles := []*Tile{} // TODO: build graph only containing possible tile in movement range graph := m.generateMapGraphFor(u) for _, pos := range PositionsInRange(u.tile.Position, u.Movement.Range, false) { tile := m.TileAt(pos) if tile == nil || !u.IsAvailableTile(tile) { continue } path, err := findPathTo(graph, u, pos) if err != nil { continue } if path.Distance/2 > int64(u.Movement.Range) { continue } tiles = append(tiles, tile) } return tiles } func (u *Unit) AttackableTiles(m *Map) []*Tile { return TilesInRange(m, u, u.Attack.MaxRange()) } func (u *Unit) AttackablePermanents(m *Map) []Permanent { permanents := []Permanent{} for _, t := range u.AttackableTiles(m) { if t.Permanent != nil && t.Permanent.Attackable() { permanents = append(permanents, t.Permanent) } } return permanents } func (u *Unit) AttackableEnemyPermanents(m *Map) []Permanent { controller := u.Controller() permanents := []Permanent{} for _, t := range u.AttackableTiles(m) { if t.Permanent != nil && t.Permanent.Attackable() && t.Permanent.Controller() != controller { permanents = append(permanents, t.Permanent) } } return permanents } func (u *Unit) Fight(p Permanent) { r := DistanceBetweenPermanents(p, u) DealDamage(u, p, u.Attack.DamageInRange(r)) } func (u *Unit) IsDestroyed() bool { return u.damage >= u.Health } func (u *Unit) Attackable() bool { return true } func (u *Unit) HasFullAction() bool { return len(u.FullActions) > 0 } func (u *Unit) FullAction() { } func (u *Unit) AvailActions() (actions []Action) { if u.AvailMoveActions > 0 && u.Movement != INVALID_MOVEMENT() { actions = append(actions, NewMoveAction(u)) } m := u.Controller().gameState.Map if u.AvailAttackActions > 0 && !INVALID_ATTACK(u.Attack) && len(u.AttackableEnemyPermanents(m)) > 0 { actions = append(actions, NewAttackAction(u)) } if u.AvailAttackActions > 0 && u.AvailMoveActions > 0 { for _, p := range u.FullActions { actions = append(actions, p) } } for _, p := range u.FreeActions { actions = append(actions, p) } return actions } func (u *Unit) OnPile(containing Permanent) { if _, ok := containing.(*Unit); ok { // TODO: add attack } } func (u *Unit) OnDrop(containing Permanent) { if _, ok := containing.(*Unit); ok { // TODO: remove attack } }