feat: add characters
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package economy
|
||||
|
||||
import "context"
|
||||
|
||||
// Avatar یک شخصیتِ آواتار (کازمتیک تصویری) که در کلِ بازی دیده میشود. تصویرِ آن
|
||||
// از /avatars/<id>.png سرو میشود. Owned یعنی قابلِ انتخاب.
|
||||
type Avatar struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
PriceCoins int64 `json:"price_coins"`
|
||||
VIP bool `json:"vip"`
|
||||
Img string `json:"img"` // URL کاملِ تصویرِ شخصیت (URL-safe)
|
||||
Owned bool `json:"owned"`
|
||||
}
|
||||
|
||||
// Avatars آواتارهای فعال را با وضعیتِ مالکیتِ کاربر برمیگرداند.
|
||||
func (s *Service) Avatars(ctx context.Context, userID int64) ([]Avatar, error) {
|
||||
rows, err := s.db.QueryContext(ctx,
|
||||
`SELECT id, title, price_coins, vip, img_ext FROM avatars WHERE enabled = 1 ORDER BY sort`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var avatars []Avatar
|
||||
for rows.Next() {
|
||||
var a Avatar
|
||||
var vip int
|
||||
var ext string
|
||||
if err := rows.Scan(&a.ID, &a.Title, &a.PriceCoins, &vip, &ext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.VIP = vip == 1
|
||||
a.Img = s.imgURL("avatars", a.ID+"."+ext) // URL کاملِ URL-safe
|
||||
avatars = append(avatars, a)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
owned, err := s.ownedAvatars(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
isVIP := s.isVIP(ctx, userID)
|
||||
for i := range avatars {
|
||||
a := &avatars[i]
|
||||
a.Owned = (a.PriceCoins == 0 && !a.VIP) || (a.VIP && isVIP) || owned[a.ID]
|
||||
}
|
||||
return avatars, nil
|
||||
}
|
||||
|
||||
func (s *Service) ownedAvatars(ctx context.Context, userID int64) (map[string]bool, error) {
|
||||
rows, err := s.db.QueryContext(ctx,
|
||||
`SELECT avatar_id FROM user_avatars WHERE user_id = ?`, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
owned := map[string]bool{}
|
||||
for rows.Next() {
|
||||
var id string
|
||||
if err := rows.Scan(&id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
owned[id] = true
|
||||
}
|
||||
return owned, rows.Err()
|
||||
}
|
||||
|
||||
// BuyAvatar یک شخصیتِ آواتار را با سکه باز میکند.
|
||||
func (s *Service) BuyAvatar(ctx context.Context, userID int64, avatarID string) error {
|
||||
var price int64
|
||||
var vip, enabled int
|
||||
err := s.db.QueryRowContext(ctx,
|
||||
`SELECT price_coins, vip, enabled FROM avatars WHERE id = ?`, avatarID).
|
||||
Scan(&price, &vip, &enabled)
|
||||
if err != nil || enabled != 1 {
|
||||
return ErrNotFound
|
||||
}
|
||||
tx, err := s.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
var exists int
|
||||
_ = tx.QueryRowContext(ctx,
|
||||
`SELECT 1 FROM user_avatars WHERE user_id = ? AND avatar_id = ?`, userID, avatarID).Scan(&exists)
|
||||
if exists == 1 {
|
||||
return ErrAlreadyOwned
|
||||
}
|
||||
if price > 0 {
|
||||
if err := adjustTx(ctx, tx, userID, CurrencyCoin, -price, "buy_avatar", avatarID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx,
|
||||
`INSERT INTO user_avatars (user_id, avatar_id) VALUES (?, ?)`, userID, avatarID); err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// SelectAvatar شخصیتِ انتخابیِ کاربر را تنظیم میکند (خالی ⇒ بازگشت به آواتارِ
|
||||
// تصادفی؛ وگرنه باید مالکش باشد یا رایگان/VIP).
|
||||
func (s *Service) SelectAvatar(ctx context.Context, userID int64, avatarID string) error {
|
||||
if avatarID == "" {
|
||||
_, err := s.db.ExecContext(ctx,
|
||||
`UPDATE users SET selected_avatar = '' WHERE id = ?`, userID)
|
||||
return err
|
||||
}
|
||||
avatars, err := s.Avatars(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, a := range avatars {
|
||||
if a.ID == avatarID {
|
||||
if !a.Owned {
|
||||
return ErrNotOwned
|
||||
}
|
||||
_, err := s.db.ExecContext(ctx,
|
||||
`UPDATE users SET selected_avatar = ? WHERE id = ?`, avatarID, userID)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return ErrNotFound
|
||||
}
|
||||
Reference in New Issue
Block a user