123 lines
2.5 KiB
Go
123 lines
2.5 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5/pgconn"
|
|
"gitrepo.ru/neonxp/nquest/pkg/models"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
var (
|
|
ErrGameNotStarted = errors.New("game not started")
|
|
ErrInvalidCode = errors.New("invalid code")
|
|
ErrOldCode = errors.New("old code")
|
|
ErrGameFinished = errors.New("game finished")
|
|
)
|
|
|
|
type Engine struct {
|
|
DB *gorm.DB
|
|
}
|
|
|
|
func NewEngine(db *gorm.DB) *Engine {
|
|
return &Engine{
|
|
DB: db,
|
|
}
|
|
}
|
|
|
|
func (e *Engine) GetState(ctx context.Context, game *models.Game, user *models.User) (*models.GameCursor, error) {
|
|
db := e.DB.WithContext(ctx)
|
|
|
|
// Пытаемся получить GamePassing
|
|
cursor := &models.GameCursor{
|
|
User: user,
|
|
Game: game,
|
|
Task: game.Tasks[0],
|
|
Status: models.TaskStarted,
|
|
Codes: []*models.Code{},
|
|
}
|
|
err := db.
|
|
Where(`user_id = ? and game_id = ? and status = ?`, user.ID, game.ID, models.TaskStarted).
|
|
Preload("Task").
|
|
Preload("Task.Codes").
|
|
Preload("Task.Next").
|
|
Preload("Task.Next.Codes").
|
|
Preload("Codes").
|
|
FirstOrCreate(cursor).
|
|
Error
|
|
if err != nil {
|
|
if err, ok := err.(*pgconn.PgError); ok {
|
|
if err.Code == "23505" {
|
|
return nil, ErrGameNotStarted
|
|
}
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
return cursor, nil
|
|
}
|
|
|
|
func (e *Engine) EnterCode(ctx context.Context, game *models.Game, user *models.User, code string) (*models.GameCursor, error) {
|
|
db := e.DB.WithContext(ctx)
|
|
st, err := e.GetState(ctx, game, user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
code = strings.Trim(code, " \n\t")
|
|
code = strings.ToLower(code)
|
|
var currentCode *models.Code
|
|
for _, c := range st.Task.Codes {
|
|
if c.Code == code {
|
|
currentCode = c
|
|
break
|
|
}
|
|
}
|
|
if currentCode == nil {
|
|
return nil, ErrInvalidCode
|
|
}
|
|
for _, c := range st.Codes {
|
|
if c.ID == currentCode.ID {
|
|
return nil, ErrOldCode
|
|
}
|
|
}
|
|
|
|
st.Codes = append(st.Codes, currentCode)
|
|
|
|
if err := db.Save(st).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(st.Codes) != len(st.Task.Codes) {
|
|
return st, nil
|
|
}
|
|
|
|
// Уровень пройден. Выдаем следующий
|
|
|
|
st.Status = models.TaskFinished
|
|
if err := db.Save(st).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if st.Task.Next == nil {
|
|
|
|
user.Experience += st.Game.Points
|
|
if err := db.Save(user).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return nil, ErrGameFinished
|
|
}
|
|
|
|
newState := &models.GameCursor{
|
|
User: user,
|
|
Game: game,
|
|
Task: st.Task.Next,
|
|
Status: models.TaskStarted,
|
|
Codes: []*models.Code{},
|
|
}
|
|
|
|
return newState, db.Create(newState).Error
|
|
}
|