Better error management.
* "Uncooperative cards" is now a warning. * Cards and actions get "Then" invoked before the card processor considers erroring out. * Terminal UI: Errors and warnings from actions are displayed during the response; they're not only added to the temporary messages now.
This commit is contained in:
		| @@ -35,6 +35,19 @@ func Msgf(f string, args ...any) Message { | ||||
| 	return stringMessage(fmt.Sprintf(f, args...)) | ||||
| } | ||||
|  | ||||
| // ErrorMessage returns a Message representing an Error. | ||||
| // This is preferred over Msgf for errors, since future versions of the library | ||||
| // may perform special message formatting for errors. | ||||
| func ErrorMessage(e error) Message { | ||||
| 	if e == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if IsSeriousError(e) { | ||||
| 		return MultiMessage{MsgStr("SERIOUS ERROR:"), Msgf("%v", e)} | ||||
| 	} | ||||
| 	return MultiMessage{MsgStr("Warning:"), Msgf("%v", e)} | ||||
| } | ||||
|  | ||||
| // A SpecialMessage is a specific, uniquely identifiable message. | ||||
| type SpecialMessage struct { | ||||
| 	msg Message | ||||
|   | ||||
| @@ -8,14 +8,14 @@ import ( | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrUncooperativeCards = errors.New("a milion cards refused to join the hand") | ||||
| 	ErrInvalidCard        = errors.New("invalid card specified") | ||||
| 	ErrInvalidChoice      = errors.New("invalid choice specified") | ||||
| 	ErrNotUrgent          = errors.New("action not urgent when urgent card is available") | ||||
| 	ErrNoActions          = errors.New("no actions remaining") | ||||
| 	ErrNotDebugging       = errors.New("this is a debug-only feature and you're not in debug mode") | ||||
| 	ErrInvalidCard   = errors.New("invalid card specified") | ||||
| 	ErrInvalidChoice = errors.New("invalid choice specified") | ||||
| 	ErrNotUrgent     = errors.New("action not urgent when urgent card is available") | ||||
| 	ErrNoActions     = errors.New("no actions remaining") | ||||
| 	ErrNotDebugging  = errors.New("this is a debug-only feature and you're not in debug mode") | ||||
|  | ||||
| 	WarningStalemate = errors.New("no actions can be taken") | ||||
| 	WarningStalemate         = &Warning{errors.New("no actions can be taken")} | ||||
| 	WarningUncoperativeCards = &Warning{errors.New("a milion cards refused to join the hand")} | ||||
| ) | ||||
|  | ||||
| // Player stores all gameplay state for one player at a specific point in time. | ||||
| @@ -272,9 +272,9 @@ func (p *Player[C]) StartNextTurn() error { | ||||
| } | ||||
|  | ||||
| // Draw draws a card into the hand, informing the card that it has been drawn. | ||||
| // If more than a million cards refuse to enter the hand, this crashes with | ||||
| // ErrUncooperativeCards. If the deck does not have enough cards, this | ||||
| // returns WarningTooFewCards. | ||||
| // If more than a million cards refuse to enter the hand, this gives up and | ||||
| // returns WarningUncooperativeCards. If the deck does not have enough cards, | ||||
| // this returns WarningTooFewCards. | ||||
| func (p *Player[C]) Draw() error { | ||||
| 	for attempts := 0; attempts < 1000000; attempts++ { | ||||
| 		if p.Deck.Len() == 0 { | ||||
| @@ -286,13 +286,13 @@ func (p *Player[C]) Draw() error { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	return ErrUncooperativeCards | ||||
| 	return WarningUncoperativeCards | ||||
| } | ||||
|  | ||||
| // FillHand draws up to the hand limit, informing cards that they have been | ||||
| // drawn. If more than a million cards refuse to enter the hand, this crashes | ||||
| // with ErrUncooperativeCards. If the deck does not have enough cards, this | ||||
| // returns WarningTooFewCards. | ||||
| // drawn. If more than a million cards refuse to enter the hand, this gives up | ||||
| // and returns WarningUncooperativeCards. If the deck does not have enough | ||||
| // cards, this returns WarningTooFewCards. | ||||
| func (p *Player[C]) FillHand() error { | ||||
| 	var lastErr error | ||||
| 	for p.Deck.Len() > 0 && len(p.Hand) < p.HandLimit { | ||||
| @@ -386,17 +386,14 @@ func (p *Player[C]) EnactCardUnchecked(cardIdx, choiceIdx int) (Message, error) | ||||
|  | ||||
| 	ret, err := options[choiceIdx].Enact(p) | ||||
| 	errs.Add(err) | ||||
| 	if IsSeriousError(err) { | ||||
| 		p.State = GameCrashed | ||||
| 		return ret, errs.Emit() | ||||
| 	} | ||||
|  | ||||
| 	err = card.Then(p, options[choiceIdx]) | ||||
| 	errs.Add(err) | ||||
|  | ||||
| 	err = errs.Emit() | ||||
| 	if IsSeriousError(err) { | ||||
| 		p.State = GameCrashed | ||||
| 	} | ||||
| 	return ret, errs.Emit() | ||||
| 	return ret, err | ||||
| } | ||||
|  | ||||
| // EnactCard executes a card choice, removes it from the hand, and decrements | ||||
| @@ -470,17 +467,13 @@ func (p *Player[C]) enactActionUnchecked(actionSource []Card[C], actionIdx, choi | ||||
|  | ||||
| 	ret, err := chosen.Enact(p) | ||||
| 	errs.Add(err) | ||||
| 	if IsSeriousError(err) { | ||||
| 		p.State = GameCrashed | ||||
| 		return ret, errs.Emit() | ||||
| 	} | ||||
|  | ||||
| 	err = card.Then(p, chosen) | ||||
| 	errs.Add(err) | ||||
| 	if IsSeriousError(err) { | ||||
| 	retErr := errs.Emit() | ||||
| 	if IsSeriousError(retErr) { | ||||
| 		p.State = GameCrashed | ||||
| 	} | ||||
| 	return ret, errs.Emit() | ||||
| 	return ret, retErr | ||||
| } | ||||
|  | ||||
| // EnactPermanentAction executes a permanently-available card and decrements | ||||
| @@ -512,15 +505,11 @@ func (p *Player[C]) ReportError(e error) { | ||||
| 	if e == nil || p.DebugLevel < -1 { | ||||
| 		return | ||||
| 	} | ||||
| 	if p.DebugLevel < 0 && !IsSeriousError(e) { | ||||
| 		return | ||||
| 	} | ||||
| 	p.ChapterBreak() | ||||
| 	severity := "[Warning]" | ||||
| 	minLvl := NotDebugging | ||||
| 	if IsSeriousError(e) { | ||||
| 		severity = "[ERROR]" | ||||
| 		minLvl = HideWarnings | ||||
| 	} | ||||
| 	p.TemporaryMessages = append(p.TemporaryMessages, Msgf("%s: %v", severity, e)) | ||||
| 	p.Debug(minLvl, ErrorMessage(e)) | ||||
| } | ||||
|  | ||||
| // CanAct returns whether the player has actions theoretically available. | ||||
| @@ -534,6 +523,7 @@ func (p *Player[C]) Debug(minLevel int, msg Message) { | ||||
| 	if p.DebugLevel < minLevel || msg == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	p.ChapterBreak() | ||||
| 	p.TemporaryMessages = append(p.TemporaryMessages, msg) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -46,6 +46,10 @@ func RunSimpleTerminalUI[C StatsCollection](p *Player[C]) error { | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				display(ErrorMessage(err)) | ||||
| 				display(MsgStr("")) | ||||
| 			} | ||||
| 			display(msg) | ||||
| 			wait() | ||||
| 		} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user