package game import ( "fmt" "log" "strconv" "strings" "golang.org/x/exp/slices" ) type Permanent interface { Card() *Card Tile() *Tile SetTile(*Tile) Damage() int ContainingPerm() Permanent Pile() []Permanent Controller() *Player Owner() *Player Effects() []string HasEffect(string) bool XEffect(string) (int, error) Marks(PermanentMark) int adjustMarks(PermanentMark, int) String() string Fight(p Permanent) AddDamage(damage int) IsDestroyed() bool Attackable() bool AvailActions() []Action IsAvailableTile(*Tile) bool setContainingPerm(p Permanent) addPermanentToPile(p Permanent) clearPile() RemovePermanentFromPile(p Permanent) onPile(containing Permanent) onUnpile(containing Permanent) onDrop(containing Permanent) } type PermanentBase struct { card *Card tile *Tile damage int pile []Permanent containingPerm Permanent controller *Player owner *Player tmpEffects []string marks map[PermanentMark]int } func NewPermanentBase(card *Card, tile *Tile, owner *Player) PermanentBase { if tile != nil && tile.Permanent != nil { log.Fatalf("Tile at %v already occupied by %v", tile.Position, tile.Permanent) } return PermanentBase{ card: card, tile: tile, controller: owner, owner: owner, } } func (p *PermanentBase) Card() *Card { return p.card } func (p *PermanentBase) Tile() *Tile { return p.tile } func (p *PermanentBase) SetTile(t *Tile) { p.tile = t } func (p *PermanentBase) Damage() int { return p.damage } func (p *PermanentBase) ContainingPerm() Permanent { return p.containingPerm } func (p *PermanentBase) Pile() []Permanent { return p.pile } func (p *PermanentBase) Controller() *Player { return p.controller } func (p *PermanentBase) Owner() *Player { return p.owner } func (p *PermanentBase) String() string { if p.Tile() != nil { return fmt.Sprintf("%s's %s@%v", p.Controller().Name, p.Card().Name, p.Tile().Position) } return fmt.Sprintf("%s's %s", p.Controller().Name, p.Card().Name) } func DealDamage(src, dest Permanent, damage int) { actualDamage := damage shield, err := dest.XEffect("shield") if err != nil { // TODO: implement piercing actualDamage -= shield if actualDamage < 0 { actualDamage = 0 } } dest.AddDamage(actualDamage) } func (p *PermanentBase) AddDamage(damage int) { p.damage += damage } func (p *PermanentBase) Effects() []string { return append(p.card.getEffects(), p.tmpEffects...) } func (p *PermanentBase) HasEffect(effect string) bool { return slices.Contains(p.tmpEffects, effect) || p.card.hasEffect(effect) } func (p *PermanentBase) XEffect(effect string) (int, error) { x, err := p.card.getXEffect(effect) for _, e := range p.tmpEffects { if !strings.Contains(e, effect) { continue } tokens := strings.SplitN(e, " ", 2) tmpX, err := strconv.Atoi(tokens[1]) if err != nil { return 0, err } x += tmpX } return x, err } func (p *PermanentBase) Marks(mark PermanentMark) int { if amount, found := p.marks[mark]; found { return amount } return 0 } func (p *PermanentBase) adjustMarks(mark PermanentMark, amount int) { if amount, found := p.marks[mark]; found { p.marks[mark] += amount } else { p.marks[mark] = amount } } func (p *PermanentBase) IsAvailableTile(tile *Tile) bool { return tile.IsAvailableForCard(p.card) } func (p *PermanentBase) setContainingPerm(containing Permanent) { p.containingPerm = containing } func (p *PermanentBase) addPermanentToPile(a Permanent) { p.pile = append(p.pile, a) } func (p *PermanentBase) clearPile() { p.pile = nil } func (p *PermanentBase) RemovePermanentFromPile(rm Permanent) { for i, piled := range p.pile { if piled != rm { continue } p.pile[i] = p.pile[len(p.pile)-1] p.pile = p.pile[:len(p.pile)-1] return } } func (b *PermanentBase) onPile(containing Permanent) { b.Card().Impl.onPile(containing) } func (b *PermanentBase) onUnpile(containing Permanent) { b.Card().Impl.onUnpile(containing) } func (b *PermanentBase) onDrop(dropped Permanent) { b.onUnpile(dropped.ContainingPerm()) b.Card().Impl.onDrop(dropped) } func AddPermanentToPile(containing, p Permanent) { p.setContainingPerm(containing) containing.addPermanentToPile(p) p.onPile(containing) } func RemovePermanentFromPile(containing, rm Permanent) { containing.RemovePermanentFromPile(rm) rm.onUnpile(containing) rm.setContainingPerm(nil) } func DropPile(containing Permanent) { for _, p := range containing.Pile() { p.onDrop(p) p.setContainingPerm(nil) } containing.clearPile() } func enterTile(p Permanent, t *Tile) { p.SetTile(t) t.entering(p) p.Card().Impl.onEntering(t) } func leaveTile(p Permanent) { if p.Tile() != nil { p.Card().Impl.onLeaving(p.Tile()) p.Tile().leaving(p) p.SetTile(nil) } }