go_games_collection/wayfarer_quest_state.go

150 lines
6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
// WayfarerQuestState — прогресс игрока в активном квесте (какой
// квест, на каком шаге). Сами тексты и ветвления хранятся отдельно,
// в WayfarerQuestDefs — так, чтобы состояние партии было маленьким и
// не дублировало неизменный контент квеста.
type WayfarerQuestState struct {
Key string
Step int
}
// WayfarerQuestChoice — один вариант выбора на шаге квеста.
type WayfarerQuestChoice struct {
LabelKey string // ключ перевода текста варианта выбора
// NextStep — индекс следующего шага квеста, если выбор не
// завершает квест. Игнорируется, если Terminal == true.
NextStep int
// Terminal — завершает ли этот выбор квест немедленно.
Terminal bool
Success bool // при Terminal: считается ли это успехом или провалом квеста
ResultKey string // ключ перевода текста-результата этого выбора
CreditsDelta float64 // изменение кредитов (может быть отрицательным)
HullDelta int // изменение прочности корпуса (может быть отрицательным)
CargoIdx int // индекс товара в WayfarerCommodities, которого касается CargoDelta (-1, если не используется)
CargoDelta int // изменение количества этого товара в трюме
KillsDelta int // изменение числа побед (для пилотского рейтинга)
}
// WayfarerQuestStep — один узел ветвления квеста: текст ситуации и
// доступные варианты выбора.
type WayfarerQuestStep struct {
TextKey string
Choices []WayfarerQuestChoice
}
// WayfarerQuestDef — полное описание одного квеста: заголовок и все
// его шаги (шаг 0 — стартовый).
type WayfarerQuestDef struct {
Key string
TitleKey string
BriefKey string // короткое описание для списка доступных квестов
Steps []WayfarerQuestStep
}
// FindWayfarerQuest ищет определение квеста по ключу среди
// WayfarerQuestDefs.
func FindWayfarerQuest(key string) (WayfarerQuestDef, bool) {
for _, q := range WayfarerQuestDefs {
if q.Key == key {
return q, true
}
}
return WayfarerQuestDef{}, false
}
// StartQuest запускает квест key с самого начала (шаг 0), если он
// ещё не завершён и не провален ранее и сейчас нет другого
// активного квеста.
func (s *WayfarerShip) StartQuest(key string) error {
if s.ActiveQuest != nil {
return errWayfarerQuestAlreadyActive
}
if s.CompletedQuests[key] || s.FailedQuests[key] {
return errWayfarerQuestAlreadyDone
}
if _, ok := FindWayfarerQuest(key); !ok {
return errWayfarerQuestNotFound
}
s.ActiveQuest = &WayfarerQuestState{Key: key, Step: 0}
return nil
}
// CurrentQuestStep возвращает текущий шаг активного квеста (или
// (_, false), если квеста нет).
func (s *WayfarerShip) CurrentQuestStep() (WayfarerQuestStep, bool) {
if s.ActiveQuest == nil {
return WayfarerQuestStep{}, false
}
def, ok := FindWayfarerQuest(s.ActiveQuest.Key)
if !ok || s.ActiveQuest.Step >= len(def.Steps) {
return WayfarerQuestStep{}, false
}
return def.Steps[s.ActiveQuest.Step], true
}
// ChooseQuestOption применяет выбранный вариант choiceIdx текущего
// шага активного квеста: списывает/начисляет ресурсы и либо
// переходит на следующий шаг, либо завершает квест (успехом или
// провалом).
func (s *WayfarerShip) ChooseQuestOption(choiceIdx int) (WayfarerQuestChoice, error) {
step, ok := s.CurrentQuestStep()
if !ok {
return WayfarerQuestChoice{}, errWayfarerNoActiveQuest
}
if choiceIdx < 0 || choiceIdx >= len(step.Choices) {
return WayfarerQuestChoice{}, errWayfarerInvalidChoice
}
choice := step.Choices[choiceIdx]
s.Credits += choice.CreditsDelta
if s.Credits < 0 {
s.Credits = 0
}
s.Hull += choice.HullDelta
if s.Hull < 0 {
s.Hull = 0
}
if s.Hull > s.MaxHull {
s.Hull = s.MaxHull
}
if choice.CargoIdx >= 0 && choice.CargoIdx < len(s.Cargo) {
s.Cargo[choice.CargoIdx] += choice.CargoDelta
if s.Cargo[choice.CargoIdx] < 0 {
s.Cargo[choice.CargoIdx] = 0
}
}
s.Kills += choice.KillsDelta
key := s.ActiveQuest.Key
if choice.Terminal {
if choice.Success {
s.CompletedQuests[key] = true
} else {
s.FailedQuests[key] = true
}
s.ActiveQuest = nil
} else {
s.ActiveQuest.Step = choice.NextStep
}
return choice, nil
}
// AbandonQuest прерывает текущий активный квест без последствий,
// как если бы он никогда не начинался (не считается ни успехом, ни
// провалом — его можно будет начать заново позже).
func (s *WayfarerShip) AbandonQuest() {
s.ActiveQuest = nil
}
var (
errWayfarerQuestAlreadyActive = wayfarerErr("сейчас уже выполняется другой квест")
errWayfarerQuestAlreadyDone = wayfarerErr("этот квест уже завершён или провален ранее")
errWayfarerQuestNotFound = wayfarerErr("квест с таким ключом не найден")
errWayfarerNoActiveQuest = wayfarerErr("сейчас нет активного квеста")
errWayfarerInvalidChoice = wayfarerErr("недопустимый номер варианта")
)