167 行
5.4 KiB
Go
167 行
5.4 KiB
Go
package modules
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
|
|
"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"`
|
|
SourceID string `json:"source_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"`
|
|
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)
|
|
}
|