CardSimEngine/cardsim/card.go
Kistaro Windrider 09fdf19948
CardOption.Enable
Make it possible for a card to display an option but not actually allow it to be selected. It's up to the UI layer to decide how to display options that are not enabled. The option text should probably contiain a note on why the option cannot be selected...
2023-04-01 22:09:05 -07:00

153 lines
5.0 KiB
Go

package cardsim
// A Card represents an option available to the player. Its methods may be
// called many times per turn as the player considers their options.
type Card[C StatsCollection] interface {
// Title is the short name of the card displayed in the hand
// and at the top of the card output. It receives the current
// player as an argument. If it returns an error that is not
// a warning, the game crashes.
Title(p *Player[C]) (Message, error)
// Urgent reports whether the card is considered urgent. If
// the player has any urgent cards in hand, they cannot choose to act
// on a non-urgent card.
Urgent(p *Player[C]) bool
// Drawn is invoked after a card is drawn, before presenting it to the
// player. If Drawn returns `false`, the card is discarded without being
// put into the hand or shown to the player and a replacement is drawn
// instead. To put a card back on the bottom of the deck (or similar)
// use p.Deck.Insert (or a related function) to put it back explicitly
// in the right position. Do not put it right back on top of the deck or
// you'll create an infinite loop.
Drawn(p *Player[C]) bool
// EventText returns the text to display on the card. If it returns an
// error that is not a warning, the game crashes.
EventText(p *Player[C]) (Message, error)
// Options returns the possible actions the player can take for this card.
// There must be at least one option.
Options(p *Player[C]) ([]CardOption[C], error)
// Then is invoked after an option is selected and executed. The selected
// option is provided as an argument. This allows cards to do certain
// cleanup for every action -- for example, returning to the deck.
Then(p *Player[C], option CardOption[C]) error
}
// A CardOption represents a choice a player could make for some card.
type CardOption[C StatsCollection] interface {
// OptionText returns the text displayed for this option. It may be called
// many times within a turn as the player considers their options. If it
// returns an error that is not a warning, the game crashes.
OptionText(p *Player[C]) (Message, error)
// Enact is called exactly once if the player chooses the option. It is
// expected to update values in `p`. It returns the text displayed to the
// player as a result of their action. If it returns an error that is not
// a warning, the game crashes.
//
// After an option is enacted, the card is deleted. If a card should be
// repeatable, Enact must return it to the deck (on every option).
Enact(p *Player[C]) (Message, error)
// Enabled returns whether this option can curently be enacted.
Enabled(p *Player[C]) bool
}
// A BasicCard is a Card with fixed title, text, options, and optional
// post-option callback. It never does anything in particular when drawn.
type BasicCard[C StatsCollection] struct {
CardTitle Message
IsUrgent bool
CardText Message
CardOptions []CardOption[C]
AfterOption func(p *Player[C], option CardOption[C]) error
}
// Title implements Card.
func (b *BasicCard[C]) Title(_ *Player[C]) (Message, error) {
return b.CardTitle, nil
}
// Urgent implements Card.
func (b *BasicCard[C]) Urgent(_ *Player[C]) bool {
return b.IsUrgent
}
// EventText implements Card.
func (b *BasicCard[C]) EventText(_ *Player[C]) (Message, error) {
return b.CardText, nil
}
// Options implements Card.
func (b *BasicCard[C]) Options(_ *Player[C]) ([]CardOption[C], error) {
return b.CardOptions, nil
}
// Then implements Card.
func (b *BasicCard[C]) Then(p *Player[C], option CardOption[C]) error {
if b.AfterOption == nil {
return nil
}
return b.AfterOption(p, option)
}
// Drawn implements Card.
func (b *BasicCard[C]) Drawn(_ *Player[C]) bool {
return true
}
// A BasicOption is a CardOption with fixed text, effects, and output.
// It's always enabled.
type BasicOption[C StatsCollection] struct {
Text Message
Effect func(*Player[C]) error
Output Message
}
// OptionText implements CardOption.
func (b *BasicOption[C]) OptionText(p *Player[C]) (Message, error) {
return b.Text, nil
}
// Enact implements CardOption.
func (b *BasicOption[C]) Enact(p *Player[C]) (Message, error) {
return b.Output, b.Effect(p)
}
// Enabled implements CardOption.
func (b *BasicOption[C]) Enabled(p *Player[C]) bool {
return true
}
// OptionFunc attaches a fixed prompt to an Enact-like function. Unlike
// BasicOption, the enactment function provided to OptionFunc returns
// the text that should be output as a result of the action, so it is
// possible to dynamically generate this text.
func OptionFunc[C StatsCollection](text Message, f func(*Player[C]) (Message, error)) CardOption[C] {
return &optionFunc[C]{text, f}
}
type optionFunc[C StatsCollection] struct {
text Message
f func(*Player[C]) (Message, error)
}
// OptionText implements CardOption.
func (o *optionFunc[C]) OptionText(p *Player[C]) (Message, error) {
return o.text, nil
}
// Enact implements CardOption.
func (o *optionFunc[C]) Enact(p *Player[C]) (Message, error) {
return o.f(p)
}
// Enabled implements CardOption.
func (o *optionFunc[C]) Enabled(p *Player[C]) bool {
return true
}