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("недопустимый номер варианта") )