Implement first issue.

This seems like the least complicated way to get the desired reusable behaviors.
This commit is contained in:
2023-04-03 01:32:02 -07:00
parent 0e1f5ff246
commit fb5aaeccfc
2 changed files with 191 additions and 17 deletions

View File

@ -16,14 +16,18 @@ type Policy interface {
// 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.
// LastEnacted informs this Policy which choice was last enacted and
// which index it is under; this allows it to prepare to describe
// itself differently depending on the last selected policy. If no
// option has ever been chosen for this card, the index is -1.
//
// 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
LastEnacted(int, Policy)
// Is returns whether this policy is this other policy. This is a strict
// identity equality check, don't do anything clever here.
Is(Policy) bool
}
// A SwitchingCard is an issue card that remembers which option was selected
@ -35,7 +39,7 @@ type SwitchingCard struct {
IsUrgent bool
After func(*SwitchingCard, *Player, CardOption) error
Policies []Policy
lastPolicy CardOption
lastPolicy Policy
}
// Title implements Card.
@ -59,24 +63,42 @@ func (s *SwitchingCard) EventText(*Player) (cardsim.Message, error) {
}
// Options implements Card.
func (s *SwitchingCard) Options(p *Player) ([]CardOption, error) {
func (s *SwitchingCard) Options(*Player) ([]CardOption, error) {
lastIdx := -1
for i, p := range s.Policies {
if p.Is(s.lastPolicy) {
lastIdx = i
break
}
}
ret := make([]CardOption, len(s.Policies))
for i, p := range s.Policies {
ret[i] = p.LastEnacted(s.lastPolicy)
p.LastEnacted(lastIdx, s.lastPolicy)
ret[i] = p
}
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)
newPolicy := o.(Policy)
var errs cardsim.ErrorCollector
if !newPolicy.Is(s.lastPolicy) {
err := s.lastPolicy.Unenact(p)
if cardsim.IsSeriousError(err) {
return err
}
errs.Add(err)
}
return nil
s.lastPolicy = o.(Policy)
if s.After != nil {
errs.Add(s.After(s, p, o))
}
return errs.Emit()
}
// BasicPolicy is a straightfoward implementation of Policy.
// BasicPolicy is a straightfoward implementation of Policy. If the currently
// enacted option is re-enacted, it refunds the player's action point.
type BasicPolicy struct {
UnenactedDesc cardsim.Message
EnactedDesc cardsim.Message
@ -94,18 +116,17 @@ func YesWeCan(*Player) bool {
}
// lastEnacted notifies b about the last-enacted policy in its group. It updates
// b.currentlyEnacted accordingly and returns itself as a generic CardOption.
// b.currentlyEnacted accordingly and returns itself.
//
// It also installs placeholders if CanDo or CanUndo is unspecified.
func (b *BasicPolicy) LastEnacted(c CardOption) CardOption {
func (b *BasicPolicy) LastEnacted(_ int, p Policy) {
b.currentlyEnacted = false
if o, ok := c.(*BasicPolicy); ok && o == b {
if o, ok := p.(*BasicPolicy); ok && o == b {
b.currentlyEnacted = true
}
if b.CanDo == nil {
b.CanDo = YesWeCan
}
return b
}
// OptionText implements CardOption.
@ -119,6 +140,10 @@ func (b *BasicPolicy) OptionText(*Player) (cardsim.Message, error) {
// Enact implements CardOption.
func (b *BasicPolicy) Enact(p *Player) (cardsim.Message, error) {
if b.currentlyEnacted {
p.ActionsRemaining++
if b.NothingChanged == nil {
b.NothingChanged = cardsim.MsgStr("You continue your current approach.")
}
return b.NothingChanged, nil
}
if !b.CanDo(p) {
@ -145,3 +170,54 @@ func (b *BasicPolicy) Enabled(p *Player) bool {
}
return b.CanDo(p)
}
func (b *BasicPolicy) Is(p Policy) bool {
if o, ok := p.(*BasicPolicy); ok {
return o == b
}
return false
}
// A DescResuilt is descriptive text for an option and the text result of
// enacting that option when it was described this way.
type DescResult struct {
Desc cardsim.Message
Result cardsim.Message
}
// A VerbosePolicy is an extension to a BasicPolicy. It emits the BasicPolicy's
// `UnenactedDesc` and the message returned from Do only when no policy has ever
// been enacted for this card; otherwise, it looks up the description and result
// from a slice using the index of the last policy selected.
type VerbosePolicy struct {
*BasicPolicy
lastIdx int
Content []DescResult
}
func (v *VerbosePolicy) LastEnacted(i int, p Policy) {
v.lastIdx = i
v.BasicPolicy.LastEnacted(i, p)
}
func (v *VerbosePolicy) OptionText(*Player) (cardsim.Message, error) {
if v.lastIdx < 0 {
return v.BasicPolicy.UnenactedDesc, nil
}
return v.Content[v.lastIdx].Desc, nil
}
func (v *VerbosePolicy) Enact(p *Player) (cardsim.Message, error) {
msg, err := v.BasicPolicy.Enact(p)
if v.lastIdx >= 0 {
msg = v.Content[v.lastIdx].Result
}
return msg, err
}
func (v *VerbosePolicy) Is(p Policy) bool {
if o, ok := p.(*VerbosePolicy); ok {
return o == p
}
return false
}