lawless/server/modules/skill.go
徐勤民 521603a899
一些检测仍在等待运行
Docs Build / build-and-deploy (push) Waiting to run
refactor(client): 删除游戏核心管理器和场景脚本
- 移除 ConfigManager 配置管理器类
- 移除 GameManager 全局单例管理器类
- 移除 NetworkManager 网络连接管理器类
- 移除 CharacterData 和 ItemData 数据模型类
- 移除 BagScene、BattleScene、LobbyScene 等场景脚本
- 移除 EncounterBubble 和 EventFeedPanel UI组件脚本
- 更新代理邀请文档中的服务器连接方式
- 更新同步状态表格中的代理任务分配信息
- 添加 MiMo 任务完成总结和审查修复记录
2026-07-03 21:34:51 +08:00

172 行
5.7 KiB
Go

package modules
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"github.com/heroiclabs/nakama-common/runtime"
)
// RegisterSkill 注册技能相关 RPC。
func RegisterSkill(initializer runtime.Initializer) error {
if err := initializer.RegisterRpc("SkillService/LearnSkill", learnSkill); err != nil {
return err
}
if err := initializer.RegisterRpc("SkillService/CreateJadeSlip", createJadeSlip); err != nil {
return err
}
return nil
}
type learnSkillReq struct {
CharacterID string `json:"character_id"`
SourceType string `json:"source_type"`
SkillID string `json:"skill_id"`
CustomName string `json:"custom_name"`
}
type jadeSlipReq struct {
CharacterID string `json:"character_id"`
SkillInstanceID string `json:"skill_instance_id"`
ConsumeProficiency bool `json:"consume_proficiency"`
CopyCount int32 `json:"copy_count"`
}
type skillData struct {
SkillInstanceID string `json:"skill_instance_id"`
SkillID string `json:"skill_id"`
Name string `json:"name"`
SourceTag string `json:"source_tag"`
Category string `json:"category"`
Domain string `json:"domain"`
Element string `json:"element"`
DamageType string `json:"damage_type"`
CanCopyToJadeSlip bool `json:"can_copy_to_jade_slip"`
Proficiency int32 `json:"proficiency"`
InstanceData interface{} `json:"instance_data"`
}
type jadeSlipItemData struct {
InventoryID string `json:"inventory_id"`
ItemID string `json:"item_id"`
Name string `json:"name"`
Quantity int32 `json:"quantity"`
InstanceData interface{} `json:"instance_data"`
ProficiencyConsumed int32 `json:"proficiency_consumed"`
}
func learnSkill(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
var req learnSkillReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(5009, "invalid payload", traceID)
}
// 校验参数
if req.SkillID == "" {
return errResp(5010, "skill_id required", traceID)
}
// 查询技能信息
var skillName, category, domain, element, alignment, damageType, scalingAttr string
var baseTemplateJSON string
err := db.QueryRowContext(ctx, `
SELECT name, category, domain, element, alignment, damage_type, scaling_attr, base_template
FROM skills WHERE id = $1
`, req.SkillID).Scan(&skillName, &category, &domain, &element, &alignment, &damageType, &scalingAttr, &baseTemplateJSON)
if err != nil {
return errResp(5011, "skill not found", traceID)
}
// 创建技能实例
var skillInstanceID string
err = db.QueryRowContext(ctx, `
INSERT INTO character_skills (
character_id, skill_id, custom_name, instance_data,
source_tag, proficiency, is_unique, lineage
) VALUES ($1, $2, $3, $4, $5, 0, false, '{}')
RETURNING id::text
`, req.CharacterID, req.SkillID, firstNonEmpty(req.CustomName, skillName),
baseTemplateJSON, req.SourceType).Scan(&skillInstanceID)
if err != nil {
logger.Error("learn skill failed: %v", err)
return errResp(9002, "internal error", traceID)
}
logger.Info("SkillService/LearnSkill success: skill_instance_id=%s skill_id=%s", skillInstanceID, req.SkillID)
return okResp(skillData{
SkillInstanceID: skillInstanceID,
SkillID: req.SkillID,
Name: firstNonEmpty(req.CustomName, skillName),
Category: category,
Domain: domain,
Element: element,
DamageType: damageType,
SourceTag: req.SourceType,
}, traceID)
}
func createJadeSlip(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
var req jadeSlipReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(5014, "invalid payload", traceID)
}
// 查询技能信息
var skillID, skillName, flow string
var proficiency int32
err := db.QueryRowContext(ctx, `
SELECT cs.skill_id, s.name, s.flow, cs.proficiency
FROM character_skills cs
JOIN skills s ON cs.skill_id = s.id
WHERE cs.id = $1 AND cs.character_id = $2
`, req.SkillInstanceID, req.CharacterID).Scan(&skillID, &skillName, &flow, &proficiency)
if err != nil {
return errResp(5015, "skill not found", traceID)
}
// 检查是否可拓印
if flow == "unique" || flow == "not Tradable" {
return errResp(5016, "skill cannot be copied", traceID)
}
// 检查熟练度
if proficiency < 240 {
return errResp(5017, "insufficient proficiency", traceID)
}
// 创建玉简物品
var inventoryID string
err = db.QueryRowContext(ctx, `
INSERT INTO inventories (character_id, item_id, slot_type, quantity, instance_data, can_trade)
VALUES ($1, 'jade_slip', 'bag', 1, $2, true)
RETURNING id::text
`, req.CharacterID, fmt.Sprintf(`{"skill_id":"%s","skill_name":"%s","source_tag":"JADE_SLIP"}`, skillID, skillName)).Scan(&inventoryID)
if err != nil {
logger.Error("create jade slip failed: %v", err)
return errResp(9002, "internal error", traceID)
}
// 消耗熟练度
_, err = db.ExecContext(ctx, `
UPDATE character_skills
SET proficiency = proficiency - 240, updated_at = NOW()
WHERE id = $1
`, req.SkillInstanceID)
if err != nil {
logger.Error("consume proficiency failed: %v", err)
}
logger.Info("SkillService/CreateJadeSlip success: inventory_id=%s skill=%s", inventoryID, skillName)
return okResp(jadeSlipItemData{
InventoryID: inventoryID,
ItemID: "jade_slip",
Name: skillName + "玉简",
Quantity: 1,
ProficiencyConsumed: 240,
}, traceID)
}