package game import ( "log" "muhq.space/muhqs-game/go/utils" ) const ( MAX_DRAW int = 3 ) type Player struct { Id int Name string Turn int Resource int DrawPerTurn int Hand *Hand DiscardPile *DiscardPile Deck *Deck Store *Store gameState *LocalState Ctrl PlayerControl knownStores map[Position]bool } // NewPlayerWithStore creates a new player. func NewPlayerWithDeckAndStore(id int, name string, deck *Deck, store *Store, gameState *LocalState) *Player { p := Player{ Id: id, Name: name, Turn: 0, Resource: 0, DrawPerTurn: MAX_DRAW, Hand: NewHand(), DiscardPile: NewDiscardPile(), Deck: deck, Store: store, gameState: gameState, Ctrl: nil, knownStores: make(map[Position]bool), } return &p } // NewPlayer creates a new player moving its deck to the store. // It initializes the players starting deck from the map's deck list if available. func NewPlayer(id int, name string, deck *Deck, gameState *LocalState) *Player { store := NewStore() deck.MoveInto(store) p := NewPlayerWithDeckAndStore(id, name, nil, store, gameState) // Prepopulate the Deck if gameState != nil { if m := gameState.Map(); m != nil { p.Deck = NewDeckFromDeckList(m.StartDeckList) } } if p.Deck == nil { p.Deck = NewDeck() } return p } // NewDraftPlayer returns a player struct only initialized with a name and an empty deck. func NewDraftPlayer(name string) *Player { p := Player{ Name: name, Deck: NewDeck(), } return &p } func (p *Player) ResourceGain() int { if p.Name == "The kraken" { return p.gameState.Map().ResourceGain * (len(p.gameState.Players()) - 1) } else { return p.gameState.Map().ResourceGain } } func (p *Player) UpkeepGain() int { return p.ResourceGain() - p.UpkeepCost() } func (p *Player) UpkeepCost() int { cost := 0 for _, u := range p.gameState.units { if u.Controller() != p { continue } cost += u.UpkeepCost() } return cost } func (p *Player) gainResource(gain int) { p.Resource += gain } func (p *Player) reduceResource(amount int) { p.Resource -= amount if p.Resource < 0 { p.Resource = 0 } } func (p *Player) upkeep() []*Player { p.gainResource(p.ResourceGain()) // TODO: handle upkeep triggers // Skip upkeep prompt if player does not controll any units controllsUnits := false for _, u := range p.gameState.units { if u.Controller() == p { controllsUnits = true break } } if !controllsUnits { return nil } a, err := prompt(p.Ctrl, newUpkeepPrompt(p)) if err != nil { // FIXME: handle error } if _, ok := a.(*PassPriority); ok { a = newUpkeepAction(p) } // FIXME: upkeep action is special paying is part of its effect p.gameState.declareAction(a) return p.gameState.stack.resolve() } func (p *Player) actionPhase() []*Player { for { a, err := promptAction(p.Ctrl) if err != nil { // FIXME } if _, ok := a.(*PassPriority); ok { return nil } if a == nil { log.Fatal("Received nil action from ", p.Name) } p.gameState.declareAction(a) w := p.gameState.stack.resolve() if len(w) > 0 { return w } } } func (p *Player) buyPhase() []*Player { s := p.gameState if p.Store.Size() == 0 && s.Map().HasStores() && len(p.knownStores) == 0 { return nil } a, err := promptBuy(p.Ctrl) if err != nil { // FIXME } if _, ok := a.(*PassPriority); ok { return nil } if a.Targets().HasSelections() { s.declareAction(a) return s.stack.resolve() } return nil } func (p *Player) discardHand() { for _, c := range p.Hand.Cards() { p.DiscardCard(c) } } func (p *Player) DiscardCard(c *Card) { p.Hand.MoveCard(c, p.DiscardPile) } func (p *Player) discardStep() { if p.Hand.Size() == 0 { return } cards := p.PromptHandCardSelection(0, p.Hand.Size()) for _, c := range cards { p.DiscardCard(c) } } func (p *Player) PromptHandCardSelection(min, max int) []*Card { p.Ctrl.SendNotification(newHandCardSelectionPrompt(p, min, max)) _a, err := p.Ctrl.RecvAction() if err != nil { // FIXME } switch a := _a.(type) { case *TargetSelection: err := a.CheckTargets(p.gameState) if err != nil { log.Panicf("Invalid hand card selection: %v", err) } return utils.InterfaceSliceToTypedSlice[*Card](a.Target().sel) case *PassPriority: return nil default: log.Panicf("Unexpected response type %T for hand card selection", a) } return nil } func (p *Player) Draw() { toDraw := p.DrawPerTurn - p.Hand.Size() if toDraw < 1 { return } p.DrawN(toDraw) } func (p *Player) DrawN(n int) { drawnCards := p.Deck.Draw(n) p.Hand.AddCards(drawnCards) } func (p *Player) IsEnemy(other *Player) bool { // TODO: support coop / teams return p != other } func (p *Player) AvailableStores() (stores []*Store) { m := p.gameState.Map() for pos, store := range m.Stores { t := m.TileAt(pos) if t.Permanent != nil && t.Permanent.Controller() == p { stores = append(stores, store) } } return } func (p *Player) KnowsStore(pos Position) bool { return p.knownStores[pos] } func (p *Player) addKnownStore(pos Position) { p.knownStores[pos] = true } func (p *Player) clearKnownStore() { p.knownStores = make(map[Position]bool) }