// Package modules - 成就图鉴系统模块 // 对齐GDD-30 成就里程碑与图鉴系统设计 package modules import ( "context" "database/sql" "encoding/json" "time" "github.com/heroiclabs/nakama-common/runtime" ) // RegisterAchievement 注册成就相关 RPC。 func RegisterAchievement(initializer runtime.Initializer) error { rpcs := map[string]func(runtime.Initializer) error{ "AchievementService/GetAchievements": getAchievements, "AchievementService/ClaimAchievement": claimAchievement, "AchievementService/GetCollection": getCollection, "AchievementService/GetMilestones": getMilestones, } for path, fn := range rpcs { if err := initializer.RegisterRpc(path, fn); err != nil { return err } } return nil } type getAchievementsReq struct { CharacterID string `json:"character_id"` Category string `json:"category"` // combat/exploration/crafting/social/special } type claimAchievementReq struct { AchievementID string `json:"achievement_id"` } type getCollectionReq struct { CharacterID string `json:"character_id"` CollectionType string `json:"collection_type"` // skill/artifact/manual/monster } type getMilestonesReq struct { CharacterID string `json:"character_id"` } type achievementData struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` Category string `json:"category"` Progress int32 `json:"progress"` Target int32 `json:"target"` IsCompleted bool `json:"is_completed"` IsClaimed bool `json:"is_claimed"` Reward interface{} `json:"reward"` UnlockedAt *time.Time `json:"unlocked_at"` } type collectionData struct { Type string `json:"type"` TotalItems int32 `json:"total_items"` Collected int32 `json:"collected"` CompletionRate float64 `json:"completion_rate"` Items interface{} `json:"items"` } type milestoneData struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` Target int32 `json:"target"` Current int32 `json:"current"` IsCompleted bool `json:"is_completed"` Reward interface{} `json:"reward"` } // 成就模板配置 var achievementTemplates = []achievementData{ {ID: "first_kill", Name: "初出茅庐", Description: "击杀第一个怪物", Category: "combat", Target: 1}, {ID: "kill_100", Name: "百战老兵", Description: "击杀100个怪物", Category: "combat", Target: 100}, {ID: "kill_1000", Name: "千人斩", Description: "击杀1000个怪物", Category: "combat", Target: 1000}, {ID: "realm_reach_3", Name: "金丹大道", Description: "达到金丹期", Category: "exploration", Target: 3}, {ID: "realm_reach_5", Name: "化神飞升", Description: "达到化神期", Category: "exploration", Target: 5}, {ID: "craft_10", Name: "炼丹新手", Description: "成功炼制10颗丹药", Category: "crafting", Target: 10}, {ID: "craft_100", Name: "炼丹大师", Description: "成功炼制100颗丹药", Category: "crafting", Target: 100}, {ID: "friend_10", Name: "广交好友", Description: "添加10个好友", Category: "social", Target: 10}, {ID: "join_guild", Name: "宗门弟子", Description: "加入一个宗门", Category: "social", Target: 1}, {ID: "collect_50", Name: "收藏家", Description: "收集50个不同技能", Category: "special", Target: 50}, } // 里程碑配置 var milestoneTemplates = []milestoneData{ {ID: "ms_realm_3", Name: "筑基有成", Description: "达到筑基期", Target: 2}, {ID: "ms_realm_5", Name: "元婴大能", Description: "达到元婴期", Target: 4}, {ID: "ms_realm_7", Name: "大乘圆满", Description: "达到大乘期", Target: 7}, {ID: "ms_kill_10000", Name: "万军取首", Description: "击杀10000个怪物", Target: 10000}, {ID: "ms_craft_500", Name: "炼丹宗师", Description: "成功炼制500颗丹药", Target: 500}, } func getAchievements(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) { traceID := newTraceID() uid := userIDFromCtx(ctx) if uid == "" { return errResp(1001, "missing token", traceID) } var req getAchievementsReq if err := json.Unmarshal([]byte(payload), &req); err != nil { return errResp(2001, "invalid payload", traceID) } // 筛选成就 var achievements []achievementData for _, a := range achievementTemplates { if req.Category == "" || a.Category == req.Category { achievements = append(achievements, a) } } return okResp(map[string]interface{}{ "achievements": achievements, "count": len(achievements), }, traceID) } func claimAchievement(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) { traceID := newTraceID() uid := userIDFromCtx(ctx) if uid == "" { return errResp(1001, "missing token", traceID) } var req claimAchievementReq if err := json.Unmarshal([]byte(payload), &req); err != nil { return errResp(2001, "invalid payload", traceID) } // 领取成就奖励(简化版) logger.Info("Claim achievement: achievement=%s", req.AchievementID) return okResp(map[string]interface{}{ "success": true, "achievement_id": req.AchievementID, "message": "成就奖励已领取", }, traceID) } func getCollection(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) { traceID := newTraceID() uid := userIDFromCtx(ctx) if uid == "" { return errResp(1001, "missing token", traceID) } var req getCollectionReq if err := json.Unmarshal([]byte(payload), &req); err != nil { return errResp(2001, "invalid payload", traceID) } // 模拟图鉴数据 collection := collectionData{ Type: req.CollectionType, TotalItems: 100, Collected: 0, CompletionRate: 0, Items: []interface{}{}, } return okResp(collection, traceID) } func getMilestones(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) { traceID := newTraceID() uid := userIDFromCtx(ctx) if uid == "" { return errResp(1001, "missing token", traceID) } var req getMilestonesReq if err := json.Unmarshal([]byte(payload), &req); err != nil { return errResp(2001, "invalid payload", traceID) } return okResp(map[string]interface{}{ "milestones": milestoneTemplates, "count": len(milestoneTemplates), }, traceID) }