From 0e1f5ff246a0833bc63e994f7dce4cd4f1779716 Mon Sep 17 00:00:00 2001 From: Kistaro Windrider Date: Mon, 3 Apr 2023 00:17:01 -0700 Subject: [PATCH] Reversible policies --- koboldsim/aliases.go | 1 + koboldsim/cardtypes.go | 147 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 koboldsim/cardtypes.go diff --git a/koboldsim/aliases.go b/koboldsim/aliases.go index b71a085..1787343 100644 --- a/koboldsim/aliases.go +++ b/koboldsim/aliases.go @@ -8,3 +8,4 @@ import "git.chromaticdragon.app/kistaro/CardSimEngine/cardsim" type Player = cardsim.Player[*KoboldMine] type Card = cardsim.Card[*KoboldMine] type InfoPanel = cardsim.InfoPanel[*KoboldMine] +type CardOption = cardsim.CardOption[*KoboldMine] diff --git a/koboldsim/cardtypes.go b/koboldsim/cardtypes.go new file mode 100644 index 0000000..0ea40ec --- /dev/null +++ b/koboldsim/cardtypes.go @@ -0,0 +1,147 @@ +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) +}