package koboldsim import ( "errors" "git.chromaticdragon.app/kistaro/CardSimEngine/cardsim" ) var ( ErrOptionNotEnabled = errors.New("option not enabled") ErrPolicyNotEnacted = errors.New("cannot unenact policy that is not enacted") ) type Policy interface { cardsim.CardOption[*KoboldMine] // Unenact reverses the previous enactment of this policy. Unenact(p *Player) error // LastEnacted informs this Policy which choice was last enacted; it will // need to prepare to describe itself differently depending on whether // this is itself or not. The policy must return the CardOption that should // be used further, which is usually self. // // The Card that would present this Policy as an option must use this, // and provide the active policy that this is a candidate to replace, LastEnacted(CardOption) CardOption } // A SwitchingCard is an issue card that remembers which option was selected // previously, and undoes it before doing a different one. It is always valid // to draw. type SwitchingCard struct { Name cardsim.Message Desc cardsim.Message IsUrgent bool After func(*SwitchingCard, *Player, CardOption) error Policies []Policy lastPolicy CardOption } // Title implements Card. func (s *SwitchingCard) Title(*Player) cardsim.Message { return s.Name } // Urgent implements Card. func (s *SwitchingCard) Urgent(*Player) bool { return s.IsUrgent } // Drawn implements Card. func (s *SwitchingCard) Drawn(*Player) bool { return true } // EventText implements Card. func (s *SwitchingCard) EventText(*Player) (cardsim.Message, error) { return s.Desc, nil } // Options implements Card. func (s *SwitchingCard) Options(p *Player) ([]CardOption, error) { ret := make([]CardOption, len(s.Policies)) for i, p := range s.Policies { ret[i] = p.LastEnacted(s.lastPolicy) } return ret, nil } // Then implements Card. func (s *SwitchingCard) Then(p *Player, o CardOption) error { s.lastPolicy = o if s.After != nil { return s.After(s, p, o) } return nil } // BasicPolicy is a straightfoward implementation of Policy. type BasicPolicy struct { UnenactedDesc cardsim.Message EnactedDesc cardsim.Message NothingChanged cardsim.Message Do func(*Player) (cardsim.Message, error) Undo func(*Player) error CanDo func(*Player) bool currentlyEnacted bool } // YesWeCan returns true. It's the default value for BasicPolicy.CanDo / BasicPolicy.CanUndo. func YesWeCan(*Player) bool { return true } // lastEnacted notifies b about the last-enacted policy in its group. It updates // b.currentlyEnacted accordingly and returns itself as a generic CardOption. // // It also installs placeholders if CanDo or CanUndo is unspecified. func (b *BasicPolicy) LastEnacted(c CardOption) CardOption { b.currentlyEnacted = false if o, ok := c.(*BasicPolicy); ok && o == b { b.currentlyEnacted = true } if b.CanDo == nil { b.CanDo = YesWeCan } return b } // OptionText implements CardOption. func (b *BasicPolicy) OptionText(*Player) (cardsim.Message, error) { if b.currentlyEnacted { return b.EnactedDesc, nil } return b.UnenactedDesc, nil } // Enact implements CardOption. func (b *BasicPolicy) Enact(p *Player) (cardsim.Message, error) { if b.currentlyEnacted { return b.NothingChanged, nil } if !b.CanDo(p) { return nil, ErrOptionNotEnabled } return b.Do(p) } // Unenact implements Policy. func (b *BasicPolicy) Unenact(p *Player) error { if !b.currentlyEnacted { return ErrPolicyNotEnacted } return b.Undo(p) } // Enabled implements CardOption. func (b *BasicPolicy) Enabled(p *Player) bool { if b.currentlyEnacted { return true } if b.CanDo == nil { b.CanDo = YesWeCan } return b.CanDo(p) }