Compare commits
49 Commits
e1a994ba11
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d517c822ef | ||
|
|
04038cd16b | ||
|
|
ec608d69cd | ||
|
|
fd5341da1a | ||
|
|
5967414da4 | ||
|
|
da118dc826 | ||
|
|
823eef00ac | ||
|
|
7844c5b76b | ||
|
|
4abd179a23 | ||
|
|
a3e88c7357 | ||
|
|
4e1a9a815f | ||
|
|
de3ae0bca2 | ||
|
|
b1ff4d3a2a | ||
|
|
24b52e14c3 | ||
|
|
2b92baf530 | ||
|
|
819d5f667b | ||
|
|
de6c700bb3 | ||
|
|
3232efd05a | ||
|
|
0c79fee8af | ||
|
|
3d77e146e9 | ||
|
|
a43a25c610 | ||
|
|
3cfde577eb | ||
|
|
85f9c02ced | ||
|
|
9f7fd83626 | ||
|
|
ee8b0a2182 | ||
|
|
6e95e014fa | ||
|
|
61a135b3a7 | ||
|
|
5a81534e84 | ||
|
|
523d835ac0 | ||
|
|
5a7e20efec | ||
|
|
5f47bf0589 | ||
|
|
a58ef20fab | ||
|
|
3999f34f77 | ||
|
|
6f51a2e349 | ||
|
|
de755f8fd0 | ||
|
|
803aa71771 | ||
|
|
4a77066d08 | ||
|
|
c9b5f8569f | ||
|
|
ddbfe91d8b | ||
|
|
74ac6ce940 | ||
|
|
43b0bc2dec | ||
|
|
b953e7831a | ||
|
|
62d93f65e7 | ||
|
|
7dfa9c297e | ||
|
|
f95fd49efd | ||
|
|
ce1a2a3588 | ||
|
|
3739c2a6f9 | ||
|
|
eca7dd86e1 | ||
|
|
e161e3626f |
3
.cnb.yml
3
.cnb.yml
@@ -27,5 +27,4 @@ main:
|
|||||||
username: ${GIT_USERNAME}
|
username: ${GIT_USERNAME}
|
||||||
password: ${GIT_ACCESS_TOKEN}
|
password: ${GIT_ACCESS_TOKEN}
|
||||||
force: true
|
force: true
|
||||||
|
sync_mode: push
|
||||||
#sync_mode: rebase
|
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ ENV GOMODCACHE=/workspace/.cache/gomod
|
|||||||
# ==========================================
|
# ==========================================
|
||||||
# 2. Codex 配置 (更换时修改这里,重新 build)
|
# 2. Codex 配置 (更换时修改这里,重新 build)
|
||||||
# ==========================================
|
# ==========================================
|
||||||
ENV CODEX_BASE_URL="http://fast.jnm.lol/v1"
|
ENV CODEX_BASE_URL="https://api.jucode.cn/v1"
|
||||||
|
|
||||||
ENV CODEX_MODEL="gpt-5.4"
|
ENV CODEX_MODEL="gpt-5.4"
|
||||||
|
|
||||||
ENV OPENAI_API_KEY="pk_live_d15iVqaSMxD_XLHh0nrFPJ_fzFZy8IfR4Cd62bERl8g"
|
ENV OPENAI_API_KEY="sk-E0ZZIFNnD0RkhMC9pT2AGMutz9vNy2VLNrgyyobT5voa81pQ"
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 3. 安装系统依赖、Golang、Code-server
|
# 3. 安装系统依赖、Golang、Code-server
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ skip_clone: true
|
|||||||
steps:
|
steps:
|
||||||
# ========== 1. 替代clone:拉取代码(核心依赖) ==========
|
# ========== 1. 替代clone:拉取代码(核心依赖) ==========
|
||||||
prepare:
|
prepare:
|
||||||
image: alpine/git
|
image: docker.1ms.run/alpine/git
|
||||||
environment:
|
environment:
|
||||||
# WOODPECKER_SSH_KEY:
|
# WOODPECKER_SSH_KEY:
|
||||||
# from_secret: WOODPECKER_SSH_KEY
|
# from_secret: WOODPECKER_SSH_KEY
|
||||||
@@ -70,7 +70,7 @@ steps:
|
|||||||
|
|
||||||
# ========== 4. 编译Logic服务(完全参考GitHub Actions编译配置) ==========
|
# ========== 4. 编译Logic服务(完全参考GitHub Actions编译配置) ==========
|
||||||
build_logic:
|
build_logic:
|
||||||
image: golang:1.25
|
image: docker.m.daocloud.io/golang:1.25
|
||||||
depends_on: [prepare]
|
depends_on: [prepare]
|
||||||
environment:
|
environment:
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
@@ -142,7 +142,7 @@ steps:
|
|||||||
|
|
||||||
# ========== 6. SCP推送产物(依赖编译+配置解析) ==========
|
# ========== 6. SCP推送产物(依赖编译+配置解析) ==========
|
||||||
scp-exe-to-servers: # 与fetch-deploy-config同级,缩进2个空格
|
scp-exe-to-servers: # 与fetch-deploy-config同级,缩进2个空格
|
||||||
image: appleboy/drone-scp:1.6.2 # 子元素,缩进4个空格
|
image: docker.1ms.run/appleboy/drone-scp:1.6.2 # 子元素,缩进4个空格
|
||||||
settings: # 子元素,缩进4个空格
|
settings: # 子元素,缩进4个空格
|
||||||
host: &ssh_host 43.248.3.21
|
host: &ssh_host 43.248.3.21
|
||||||
port: &ssh_port 22
|
port: &ssh_port 22
|
||||||
@@ -158,7 +158,7 @@ steps:
|
|||||||
depends_on: # 子元素,缩进4个空格
|
depends_on: # 子元素,缩进4个空格
|
||||||
- build_logic # depends_on内的项,缩进6个空格
|
- build_logic # depends_on内的项,缩进6个空格
|
||||||
start-login-logic:
|
start-login-logic:
|
||||||
image: appleboy/drone-ssh:1.6.2
|
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/appleboy/drone-ssh:1.7.7
|
||||||
depends_on: [scp-exe-to-servers]
|
depends_on: [scp-exe-to-servers]
|
||||||
settings: # 子元素,缩进4个空格
|
settings: # 子元素,缩进4个空格
|
||||||
host: *ssh_host
|
host: *ssh_host
|
||||||
|
|||||||
56
common/contrib/drivers/pgsql/cmd/codexcheck/main.go
Normal file
56
common/contrib/drivers/pgsql/cmd/codexcheck/main.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
const dsn = "user=user_YrK4j7 password=password_jSDm76 host=43.248.3.21 port=5432 dbname=bl sslmode=disable timezone=Asia/Shanghai"
|
||||||
|
|
||||||
|
db, err := sql.Open("postgres", dsn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
id int64
|
||||||
|
cdkCode string
|
||||||
|
cdkType int64
|
||||||
|
exchangeRemainCount int64
|
||||||
|
bindUserID int64
|
||||||
|
validEndTime sql.NullTime
|
||||||
|
remark sql.NullString
|
||||||
|
)
|
||||||
|
|
||||||
|
err = db.QueryRow(`
|
||||||
|
select id, cdk_code, type, exchange_remain_count, bind_user_id, valid_end_time, remark
|
||||||
|
from config_gift_cdk
|
||||||
|
where cdk_code = $1
|
||||||
|
`, "nrTbdXFBhKkaTdDk").Scan(
|
||||||
|
&id,
|
||||||
|
&cdkCode,
|
||||||
|
&cdkType,
|
||||||
|
&exchangeRemainCount,
|
||||||
|
&bindUserID,
|
||||||
|
&validEndTime,
|
||||||
|
&remark,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("id=%d\ncdk_code=%s\ntype=%d\nexchange_remain_count=%d\nbind_user_id=%d\nvalid_end_time=%v\nremark=%q\n",
|
||||||
|
id,
|
||||||
|
cdkCode,
|
||||||
|
cdkType,
|
||||||
|
exchangeRemainCount,
|
||||||
|
bindUserID,
|
||||||
|
validEndTime.Time,
|
||||||
|
remark.String,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package cool
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"blazing/cool/coolconfig"
|
"blazing/cool/coolconfig"
|
||||||
@@ -158,12 +159,33 @@ func (c *Controller) Page(ctx context.Context, req *PageReq) (res *BaseRes, err
|
|||||||
// 注册控制器到路由
|
// 注册控制器到路由
|
||||||
func RegisterController(c IController) {
|
func RegisterController(c IController) {
|
||||||
var ctx = context.Background()
|
var ctx = context.Background()
|
||||||
var sController = &Controller{}
|
var sController *Controller
|
||||||
gconv.Struct(c, &sController)
|
rv := reflect.ValueOf(c)
|
||||||
|
if rv.IsValid() && rv.Kind() == reflect.Ptr {
|
||||||
|
ev := rv.Elem()
|
||||||
|
if ev.IsValid() {
|
||||||
|
field := ev.FieldByName("Controller")
|
||||||
|
if field.IsValid() && !field.IsNil() {
|
||||||
|
if ctrl, ok := field.Interface().(*Controller); ok && ctrl != nil {
|
||||||
|
sController = ctrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sController == nil {
|
||||||
|
sController = &Controller{}
|
||||||
|
gconv.Struct(c, &sController)
|
||||||
|
}
|
||||||
if coolconfig.Config.Eps {
|
if coolconfig.Config.Eps {
|
||||||
model := sController.Service.GetModel()
|
model := sController.Service.GetModel()
|
||||||
columns := getModelInfo(ctx, sController.Prefix, model)
|
tableName := ""
|
||||||
ModelInfo[sController.Prefix] = columns
|
if model != nil {
|
||||||
|
tableName = strings.TrimSpace(model.TableName())
|
||||||
|
}
|
||||||
|
if tableName != "" && tableName != "this_table_should_not_exist" {
|
||||||
|
columns := getModelInfo(ctx, sController.Prefix, model)
|
||||||
|
ModelInfo[sController.Prefix] = columns
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.Server().Group(
|
g.Server().Group(
|
||||||
sController.Prefix, func(group *ghttp.RouterGroup) {
|
sController.Prefix, func(group *ghttp.RouterGroup) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package cool
|
|||||||
import (
|
import (
|
||||||
_ "blazing/contrib/drivers/pgsql"
|
_ "blazing/contrib/drivers/pgsql"
|
||||||
"blazing/cool/cooldb"
|
"blazing/cool/cooldb"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/encoding/gjson"
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
@@ -10,6 +11,11 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
autoMigrateMu sync.Mutex
|
||||||
|
autoMigrateModels []IModel
|
||||||
|
)
|
||||||
|
|
||||||
// 初始化数据库连接供gorm使用
|
// 初始化数据库连接供gorm使用
|
||||||
func InitDB(group string) (*gorm.DB, error) {
|
func InitDB(group string) (*gorm.DB, error) {
|
||||||
// var ctx context.Context
|
// var ctx context.Context
|
||||||
@@ -54,9 +60,33 @@ func getDBbyModel(model IModel) *gorm.DB {
|
|||||||
|
|
||||||
// 根据entity结构体创建表
|
// 根据entity结构体创建表
|
||||||
func CreateTable(model IModel) error {
|
func CreateTable(model IModel) error {
|
||||||
if Config.AutoMigrate {
|
autoMigrateMu.Lock()
|
||||||
|
autoMigrateModels = append(autoMigrateModels, model)
|
||||||
|
autoMigrateMu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunAutoMigrate 显式执行已注册模型的建表/迁移。
|
||||||
|
func RunAutoMigrate() error {
|
||||||
|
if !Config.AutoMigrate {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
autoMigrateMu.Lock()
|
||||||
|
models := append([]IModel(nil), autoMigrateModels...)
|
||||||
|
autoMigrateMu.Unlock()
|
||||||
|
|
||||||
|
seen := make(map[string]struct{}, len(models))
|
||||||
|
for _, model := range models {
|
||||||
|
key := model.GroupName() + ":" + model.TableName()
|
||||||
|
if _, ok := seen[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[key] = struct{}{}
|
||||||
|
|
||||||
db := getDBbyModel(model)
|
db := getDBbyModel(model)
|
||||||
return db.AutoMigrate(model)
|
if err := db.AutoMigrate(model); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ type EffectArg struct {
|
|||||||
SideEffect []struct {
|
SideEffect []struct {
|
||||||
ID int `json:"ID"`
|
ID int `json:"ID"`
|
||||||
SideEffectArgcount int `json:"SideEffectArgcount"`
|
SideEffectArgcount int `json:"SideEffectArgcount"`
|
||||||
SideEffectArg string `json:"SideEffectArg,omitempty"`
|
SideEffectArg rawFlexibleString `json:"SideEffectArg,omitempty"`
|
||||||
} `json:"SideEffect"`
|
} `json:"SideEffect"`
|
||||||
} `json:"SideEffects"`
|
} `json:"SideEffects"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ import (
|
|||||||
_ "blazing/common/data/xmlres/packed"
|
_ "blazing/common/data/xmlres/packed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ECUST-XX/xml"
|
"github.com/ECUST-XX/xml"
|
||||||
"github.com/gogf/gf/v2/os/gres"
|
"github.com/gogf/gf/v2/os/gres"
|
||||||
@@ -16,27 +13,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var path string
|
var path string
|
||||||
var diskConfigPath string
|
|
||||||
|
|
||||||
func readConfigContent(path string) []byte {
|
func readConfigContent(path string) []byte {
|
||||||
content := gres.GetContent(path)
|
return gres.GetContent(path)
|
||||||
if len(content) > 0 {
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
|
|
||||||
if diskConfigPath == "" {
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
|
|
||||||
diskPath := filepath.Join(diskConfigPath, strings.TrimPrefix(path, "config/"))
|
|
||||||
data, err := os.ReadFile(diskPath)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("[xmlres] readConfigContent fallback failed: path=%s disk=%s err=%v\n", path, diskPath, err)
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("[xmlres] readConfigContent fallback hit: path=%s disk=%s len=%d\n", path, diskPath, len(data))
|
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getXml[T any](path string) T {
|
func getXml[T any](path string) T {
|
||||||
@@ -93,9 +72,6 @@ var (
|
|||||||
|
|
||||||
func Initfile() {
|
func Initfile() {
|
||||||
//gres.Dump()
|
//gres.Dump()
|
||||||
path1, _ := os.Getwd()
|
|
||||||
diskConfigPath = filepath.Join(path1, "public", "config")
|
|
||||||
path = path1 + "/public/config/"
|
|
||||||
path = "config/"
|
path = "config/"
|
||||||
MapConfig = getXml[Maps](path + "210.xml")
|
MapConfig = getXml[Maps](path + "210.xml")
|
||||||
|
|
||||||
|
|||||||
26
common/data/xmlres/json_compat_test.go
Normal file
26
common/data/xmlres/json_compat_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package xmlres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMoveUnmarshalJSONAcceptsNumericName(t *testing.T) {
|
||||||
|
var move Move
|
||||||
|
if err := json.Unmarshal([]byte(`{"ID":10001,"Name":1,"Category":1,"Type":8,"Power":35,"MaxPP":35,"Accuracy":95}`), &move); err != nil {
|
||||||
|
t.Fatalf("unmarshal move failed: %v", err)
|
||||||
|
}
|
||||||
|
if move.Name != "1" {
|
||||||
|
t.Fatalf("expected numeric name to convert to string, got %q", move.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEffectArgUnmarshalJSONAcceptsNumericSideEffectArg(t *testing.T) {
|
||||||
|
var cfg EffectArg
|
||||||
|
if err := json.Unmarshal([]byte(`{"SideEffects":{"SideEffect":[{"ID":1,"SideEffectArgcount":1,"SideEffectArg":3}]}}`), &cfg); err != nil {
|
||||||
|
t.Fatalf("unmarshal effect arg failed: %v", err)
|
||||||
|
}
|
||||||
|
if got := string(cfg.SideEffects.SideEffect[0].SideEffectArg); got != "3" {
|
||||||
|
t.Fatalf("expected numeric side effect arg to convert to string, got %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -115,7 +115,7 @@ type Move struct {
|
|||||||
func (m *Move) UnmarshalJSON(data []byte) error {
|
func (m *Move) UnmarshalJSON(data []byte) error {
|
||||||
type moveAlias struct {
|
type moveAlias struct {
|
||||||
ID int `json:"ID"`
|
ID int `json:"ID"`
|
||||||
Name string `json:"Name"`
|
Name rawFlexibleString `json:"Name"`
|
||||||
Category int `json:"Category"`
|
Category int `json:"Category"`
|
||||||
Type int `json:"Type"`
|
Type int `json:"Type"`
|
||||||
Power int `json:"Power"`
|
Power int `json:"Power"`
|
||||||
@@ -150,7 +150,7 @@ func (m *Move) UnmarshalJSON(data []byte) error {
|
|||||||
|
|
||||||
*m = Move{
|
*m = Move{
|
||||||
ID: aux.ID,
|
ID: aux.ID,
|
||||||
Name: aux.Name,
|
Name: string(aux.Name),
|
||||||
Category: aux.Category,
|
Category: aux.Category,
|
||||||
Type: aux.Type,
|
Type: aux.Type,
|
||||||
Power: aux.Power,
|
Power: aux.Power,
|
||||||
|
|||||||
@@ -121,7 +121,23 @@ func (s *Server) OnTraffic(c gnet.Conn) (action gnet.Action) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ws := c.Context().(*player.ClientData).Wsmsg
|
client := c.Context().(*player.ClientData)
|
||||||
|
if s.discorse && !client.IsCrossDomainChecked() {
|
||||||
|
handled, ready, action := handle(c)
|
||||||
|
if action != gnet.None {
|
||||||
|
return action
|
||||||
|
}
|
||||||
|
if handled {
|
||||||
|
client.MarkCrossDomainChecked()
|
||||||
|
return gnet.None
|
||||||
|
}
|
||||||
|
if !ready {
|
||||||
|
return gnet.None
|
||||||
|
}
|
||||||
|
client.MarkCrossDomainChecked()
|
||||||
|
}
|
||||||
|
|
||||||
|
ws := client.Wsmsg
|
||||||
if ws.Tcp {
|
if ws.Tcp {
|
||||||
return s.handleTCP(c)
|
return s.handleTCP(c)
|
||||||
}
|
}
|
||||||
|
|||||||
73
help/初始化SPT配置.sql
Normal file
73
help/初始化SPT配置.sql
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
-- 初始化/修复 SPT 配置表(PostgreSQL)
|
||||||
|
-- 用法:在 sun 数据库执行本文件
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS config_spt (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
"createTime" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
"updateTime" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
deleted_at TIMESTAMPTZ NULL,
|
||||||
|
is_enable INTEGER NOT NULL DEFAULT 1,
|
||||||
|
remark VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
|
task_id INTEGER NOT NULL,
|
||||||
|
title VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
|
pet_id INTEGER NOT NULL DEFAULT 0,
|
||||||
|
online INTEGER NOT NULL DEFAULT 1,
|
||||||
|
level INTEGER NOT NULL DEFAULT 1,
|
||||||
|
enter_id INTEGER NOT NULL DEFAULT 0,
|
||||||
|
description TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_config_spt_task_id ON config_spt(task_id);
|
||||||
|
|
||||||
|
ALTER TABLE config_spt DROP COLUMN IF EXISTS seat_id;
|
||||||
|
|
||||||
|
INSERT INTO config_spt
|
||||||
|
(task_id, title, pet_id, online, level, enter_id, description, is_enable, remark)
|
||||||
|
VALUES
|
||||||
|
(301,'蘑菇怪',47,1,1,12,'生活在克洛斯星,被艾里逊的液氮冻伤而发狂,使用火焰喷射器可以使它安静下来。制服它可以获得草系精灵小蘑菇。',1,'破除防护罩'),
|
||||||
|
(302,'钢牙鲨',34,1,1,21,'海洋星海底的危险怪兽,据说它躲藏的洞穴中有制作黑武士装的黑晶矿石。记住,到海底一定要穿上耐压的潜水套装。',1,''),
|
||||||
|
(303,'里奥斯',42,1,2,17,'海盗艾里逊在火山被它困住,战胜它有机会获得火系精灵“胡里亚”。在火山,你会用到喷水装的。',1,'扑灭火焰屏障'),
|
||||||
|
(304,'阿克希亚',50,1,4,40,'塞西利亚星的守护者,正义的精灵圣兽,它是不可战胜的,千年来一直等待着宿命的对手。',1,''),
|
||||||
|
(305,'提亚斯',69,1,3,27,'云霄星出现了一只极具攻击性的变异精灵,拥有很多蛋的它虽然想要努力呵护自己的孩子却力不从心,看来需要大家帮帮忙啊。',1,''),
|
||||||
|
(306,'雷伊',70,1,3,32,'赫尔卡星天空中划过一道闪电,映出了一个酷似精灵的黑影,它全身被电流包围,从它的神态中可以看出它正等待来自各方的挑战。',1,'雷雨天'),
|
||||||
|
(307,'纳多雷',88,1,3,106,'在双子阿尔法星上,特派队遇见了一只巨大的精灵,经过多次挑战后,它仍然丝毫无损,赛尔们是否有办法战胜这只精灵呢?',1,''),
|
||||||
|
(308,'雷纳多',113,1,3,49,'彪悍的雷纳多盘踞在双子贝塔星上,和双子阿尔法星的纳多雷遥相对应,守护着星球上所有精灵的。',1,''),
|
||||||
|
(309,'尤纳斯',132,1,4,314,'黑暗之门的制造者,拥有能够抵御一切的暗影屏障和所有能量来源的黑暗之核。',1,''),
|
||||||
|
(310,'魔狮迪露',187,1,4,53,'魔狮迪露具有神秘的力量,能使自己的体力突破界限,但同时也会受到未知的惩罚。',1,''),
|
||||||
|
(311,'哈莫雷特',216,1,5,60,'拥有无比巨大的身躯,集水火草三种原能为一身,龙系的神秘力量使它所向无敌。失忆的它,似乎还有很多谜团……',1,''),
|
||||||
|
(312,'奈尼芬多',264,1,4,325,'奈尼芬多是爱迪星的守护者,凄美的歌声连月亮都为之倾倒,据说只有音乐的力量才能够唤醒它。',1,''),
|
||||||
|
(316,'厄尔塞拉',421,1,5,61,'浑身散发着各色光芒,任何邪恶在她的光芒下消散无形',1,''),
|
||||||
|
(50,'卡特斯',169,1,2,110,'作为暗黑武斗场的试炼精灵,守护着试炼之门,它的气度和风度非同一般,杀气重重很难对付!',1,''),
|
||||||
|
(51,'魔牙鲨',171,1,3,503,'暗黑第一门的魔牙鲨,被赋予了传说中的暗黑斗气,隐藏在暗影中攻击时它的能力可以被放大增强。',1,''),
|
||||||
|
(53,'贝鲁基德',174,1,3,504,'暗黑第二门的贝鲁基德,暗黑火焰环绕周身,凶悍的外表下藏着善战勇敢的心。',1,''),
|
||||||
|
(55,'巴弗洛',177,1,3,505,'勇猛凶横的巴弗洛把守着暗黑武斗场Ⅲ-Ⅰ门,霹雳闪电般的羽翼攻击、震荡心胸的音乐攻击,让人防不胜防。',1,''),
|
||||||
|
(56,'奇拉塔顿',183,1,3,505,'勇敢的奇拉塔顿驻守在暗黑武斗场Ⅲ-Ⅱ门的那一边,身为大地之子的它驾驭着反物质能量,纵横无敌。',1,''),
|
||||||
|
(59,'西萨拉斯',195,1,3,506,'拥有强大反物质电力的暗黑Ⅳ-Ⅰ门守护者,雷霆之刃震撼寰宇天地,电流之剑穿透空间阻隔,威慑四方。',1,''),
|
||||||
|
(60,'克林卡修',192,1,4,506,'暗黑Ⅳ-Ⅱ门守护者冰雪灵兽克林卡修,冰雪之爪具有猛烈的攻击力,果敢不张扬的个性,让它成为忍者般的精灵。',1,''),
|
||||||
|
(76,'卡库',222,1,4,507,'暗黑Ⅴ-Ⅰ门是武学之门,守门精灵不张扬、不蛮横、步步为营,每出一招都会致命。',1,''),
|
||||||
|
(77,'赫德卡',224,1,4,507,'驻守暗黑V-II门的铁血赫德卡,有着铜墙铁壁的防守能力,有着超级强力的电光炮,它的防御之门你能够开启吗?',1,''),
|
||||||
|
(78,'伊兰罗尼',227,1,5,507,'守护暗黑Ⅴ-Ⅲ门的伊兰罗尼是优雅得体的淑女,擅长在裙摆飘飘、光芒闪耀间使出杀手。',1,''),
|
||||||
|
(117,'斯加尔卡',356,1,5,508,'暗黑VI-I门的守护者,擅长使用暗黑电能的家伙,比起折磨对手的身体,斯加尔卡更喜欢震慑对手的心灵。',1,''),
|
||||||
|
(118,'艾尔伊洛',297,1,5,508,'暗黑VI-II门的守护者,历经了炼狱洗礼的艾尔伊洛开始崇尚爽快的攻击方式,喜欢凭借精湛的技巧近距地伤害对手。',1,''),
|
||||||
|
(119,'布林克克',359,1,5,508,'暗黑VI-III门的守护者,拥有海妖之力的庇护,体内充满着混沌的能量企图吞噬整个海洋。',1,''),
|
||||||
|
(502,'魔花使者',438,1,5,509,'暗黑VII-I门的守护者,比恩特的进化形态,浑身散发着反物质世界中的黑暗气息,散发出来的毒粉是它的致命武器。',1,''),
|
||||||
|
(503,'莫尔加斯',441,1,5,509,'暗黑VII-II门的守护者,莫鲁格尔的进化形态,受到了反物质世界的影响,浑身被黑暗所包围,拥有极强的防御能力,所有攻击在它面前都显得非常渺小。',1,''),
|
||||||
|
(504,'萨诺拉斯',435,1,5,509,'暗黑VII-III门的守护者,萨诺的进化形态,经过岩浆洗礼的皮肤拥有独特的降温功能,即使在极其炎热的环境下依然不受影响。',1,''),
|
||||||
|
(606,'帕多尼',656,1,5,510,'暗黑Ⅷ-Ⅰ门的守护者,浑身充斥着暗黑能量,暗黑能量会随着它的歌神散发出来。',1,''),
|
||||||
|
(607,'加洛德',659,1,5,510,'暗黑Ⅷ-Ⅱ门的守护,暗黑能量的注入,使它的脾气变得暴躁,擅长与对手近身搏斗,浑身的尖刺、催生出的植物都是它进攻的利器。',1,''),
|
||||||
|
(608,'萨多拉尼',661,1,5,510,'暗黑Ⅷ-Ⅲ门的守护,将暗黑能量融入自身,肢体变得非常结实有力,虽然体积很小,但是却拥有了堪比巨龙的神力。',1,'')
|
||||||
|
ON CONFLICT (task_id) DO UPDATE
|
||||||
|
SET
|
||||||
|
title = EXCLUDED.title,
|
||||||
|
pet_id = EXCLUDED.pet_id,
|
||||||
|
online = EXCLUDED.online,
|
||||||
|
level = EXCLUDED.level,
|
||||||
|
enter_id = EXCLUDED.enter_id,
|
||||||
|
description = EXCLUDED.description,
|
||||||
|
is_enable = EXCLUDED.is_enable,
|
||||||
|
remark = EXCLUDED.remark,
|
||||||
|
"updateTime" = NOW();
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -82,7 +82,9 @@ func (h Controller) DASHIbeiR(req *C2s_MASTER_REWARDSR, c *player.Player) (resul
|
|||||||
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrAwardAlreadyClaimed)
|
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrAwardAlreadyClaimed)
|
||||||
}
|
}
|
||||||
|
|
||||||
consumeMasterCupItems(c, requiredItems)
|
if err := consumeMasterCupItems(c, requiredItems); err != nil {
|
||||||
|
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrInsufficientItems)
|
||||||
|
}
|
||||||
progress.Set(uint(req.ElementType))
|
progress.Set(uint(req.ElementType))
|
||||||
taskData.Data = progress.Bytes()
|
taskData.Data = progress.Bytes()
|
||||||
if taskErr = c.Service.Task.SetTask(taskData); taskErr != nil {
|
if taskErr = c.Service.Task.SetTask(taskData); taskErr != nil {
|
||||||
@@ -130,10 +132,13 @@ func hasEnoughMasterCupItems(c *player.Player, requiredItems []ItemS) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func consumeMasterCupItems(c *player.Player, requiredItems []ItemS) {
|
func consumeMasterCupItems(c *player.Player, requiredItems []ItemS) error {
|
||||||
for _, item := range requiredItems {
|
for _, item := range requiredItems {
|
||||||
c.Service.Item.UPDATE(item.ItemId, -int(item.ItemCnt))
|
if err := c.Service.Item.UPDATE(item.ItemId, -int(item.ItemCnt)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendMasterCupRewardItems(c *player.Player, result *S2C_MASTER_REWARDSR, itemList []data.ItemInfo) {
|
func appendMasterCupRewardItems(c *player.Player, result *S2C_MASTER_REWARDSR, itemList []data.ItemInfo) {
|
||||||
|
|||||||
@@ -26,12 +26,11 @@ func (h Controller) EggGamePlay(data1 *C2S_EGG_GAME_PLAY, c *player.Player) (res
|
|||||||
if data1.EggNum > 10 || data1.EggNum <= 0 {
|
if data1.EggNum > 10 || data1.EggNum <= 0 {
|
||||||
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrSystemError)
|
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrSystemError)
|
||||||
}
|
}
|
||||||
if r < 0 {
|
if r <= 0 || data1.EggNum > r {
|
||||||
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrGachaTicketsInsufficient)
|
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrGachaTicketsInsufficient)
|
||||||
}
|
}
|
||||||
if data1.EggNum > r {
|
if err := c.Service.Item.UPDATE(400501, int(-data1.EggNum)); err != nil {
|
||||||
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrGachaTicketsInsufficient)
|
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrGachaTicketsInsufficient)
|
||||||
|
|
||||||
}
|
}
|
||||||
result = &S2C_EGG_GAME_PLAY{ListInfo: []data.ItemInfo{}}
|
result = &S2C_EGG_GAME_PLAY{ListInfo: []data.ItemInfo{}}
|
||||||
if grand.Meet(int(data1.EggNum), 100) {
|
if grand.Meet(int(data1.EggNum), 100) {
|
||||||
@@ -52,8 +51,6 @@ func (h Controller) EggGamePlay(data1 *C2S_EGG_GAME_PLAY, c *player.Player) (res
|
|||||||
for _, item := range addedItems {
|
for _, item := range addedItems {
|
||||||
result.ListInfo = append(result.ListInfo, data.ItemInfo{ItemId: item.ItemId, ItemCnt: item.ItemCnt})
|
result.ListInfo = append(result.ListInfo, data.ItemInfo{ItemId: item.ItemId, ItemCnt: item.ItemCnt})
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Service.Item.UPDATE(400501, int(-data1.EggNum))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ func (h Controller) GET_XUANCAI(data *C2s_GET_XUANCAI, c *player.Player) (result
|
|||||||
|
|
||||||
// 检查该位是否未被选中(避免重复)
|
// 检查该位是否未被选中(避免重复)
|
||||||
if (result.Status & mask) == 0 {
|
if (result.Status & mask) == 0 {
|
||||||
|
result.Status |= mask
|
||||||
itemID := uint32(400686 + randBitIdx + 1)
|
itemID := uint32(400686 + randBitIdx + 1)
|
||||||
selectedItems = append(selectedItems, itemID)
|
selectedItems = append(selectedItems, itemID)
|
||||||
itemMask[itemID] = mask
|
itemMask[itemID] = mask
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func startMapBossFight(
|
|||||||
ai *player.AI_player,
|
ai *player.AI_player,
|
||||||
fn func(model.FightOverInfo),
|
fn func(model.FightOverInfo),
|
||||||
) (*fight.FightC, errorcode.ErrorCode) {
|
) (*fight.FightC, errorcode.ErrorCode) {
|
||||||
ourPets := p.GetPetInfo(100)
|
ourPets := p.GetPetInfo(p.CurrentMapPetLevelLimit())
|
||||||
oppPets := ai.GetPetInfo(0)
|
oppPets := ai.GetPetInfo(0)
|
||||||
if mapNode != nil && mapNode.IsGroupBoss != 0 {
|
if mapNode != nil && mapNode.IsGroupBoss != 0 {
|
||||||
if len(ourPets) > 0 && len(oppPets) > 0 {
|
if len(ourPets) > 0 && len(oppPets) > 0 {
|
||||||
@@ -98,8 +98,8 @@ func (Controller) OnPlayerFightNpcMonster(req *FightNpcMonsterInboundInfo, p *pl
|
|||||||
if err = p.CanFight(); err != 0 {
|
if err = p.CanFight(); err != 0 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if req.Number > 9 {
|
if int(req.Number) >= len(p.Data) {
|
||||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
return nil, errorcode.ErrorCodes.ErrPokemonNotHere
|
||||||
}
|
}
|
||||||
|
|
||||||
refPet := p.Data[req.Number]
|
refPet := p.Data[req.Number]
|
||||||
@@ -114,7 +114,7 @@ func (Controller) OnPlayerFightNpcMonster(req *FightNpcMonsterInboundInfo, p *pl
|
|||||||
p.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC
|
p.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC
|
||||||
p.Fightinfo.Mode = fightinfo.BattleMode.MULTI_MODE
|
p.Fightinfo.Mode = fightinfo.BattleMode.MULTI_MODE
|
||||||
|
|
||||||
_, err = fight.NewFight(p, ai, p.GetPetInfo(100), ai.GetPetInfo(0), func(foi model.FightOverInfo) {
|
_, err = fight.NewFight(p, ai, p.GetPetInfo(p.CurrentMapPetLevelLimit()), ai.GetPetInfo(0), func(foi model.FightOverInfo) {
|
||||||
handleNpcFightRewards(p, foi, monster)
|
handleNpcFightRewards(p, foi, monster)
|
||||||
})
|
})
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
@@ -236,7 +236,7 @@ func shouldGrantBossWinBonus(fightC *fight.FightC, playerID uint32, bossConfig c
|
|||||||
|
|
||||||
func buildNpcMonsterInfo(refPet player.OgrePetInfo, mapID uint32) (*model.PetInfo, *model.PlayerInfo, errorcode.ErrorCode) {
|
func buildNpcMonsterInfo(refPet player.OgrePetInfo, mapID uint32) (*model.PetInfo, *model.PlayerInfo, errorcode.ErrorCode) {
|
||||||
if refPet.ID == 0 {
|
if refPet.ID == 0 {
|
||||||
return nil, nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
return nil, nil, errorcode.ErrorCodes.ErrPokemonNotHere
|
||||||
}
|
}
|
||||||
|
|
||||||
monster := model.GenPetInfo(
|
monster := model.GenPetInfo(
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/util/gconv"
|
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -207,32 +206,8 @@ func buildTowerMonsterInfo(towerBoss configmodel.BaseTowerConfig) (*model.Player
|
|||||||
monsterInfo := &model.PlayerInfo{Nick: towerBoss.Name}
|
monsterInfo := &model.PlayerInfo{Nick: towerBoss.Name}
|
||||||
for i, boss := range bosses {
|
for i, boss := range bosses {
|
||||||
monster := model.GenPetInfo(int(boss.MonID), 24, int(boss.Nature), 0, int(boss.Lv), nil, 0)
|
monster := model.GenPetInfo(int(boss.MonID), 24, int(boss.Nature), 0, int(boss.Lv), nil, 0)
|
||||||
if boss.Hp != 0 {
|
monster.ConfigBoss(boss.PetBaseConfig)
|
||||||
monster.Hp = uint32(boss.Hp)
|
appendPetEffects(monster, boss.Effect)
|
||||||
monster.MaxHp = uint32(boss.Hp)
|
|
||||||
}
|
|
||||||
|
|
||||||
for statIdx, prop := range boss.Prop {
|
|
||||||
if prop != 0 {
|
|
||||||
monster.Prop[statIdx] = prop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for skillIdx := 0; skillIdx < len(monster.SkillList) && skillIdx < len(boss.SKill); skillIdx++ {
|
|
||||||
if boss.SKill[skillIdx] != 0 {
|
|
||||||
monster.SkillList[skillIdx].ID = boss.SKill[skillIdx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
effects := service.NewEffectService().Args(boss.Effect)
|
|
||||||
for _, effect := range effects {
|
|
||||||
monster.EffectInfo = append(monster.EffectInfo, model.PetEffectInfo{
|
|
||||||
Idx: uint16(effect.ID),
|
|
||||||
EID: gconv.Uint16(effect.Eid),
|
|
||||||
Args: gconv.Ints(effect.Args),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
monster.CatchTime = uint32(i)
|
monster.CatchTime = uint32(i)
|
||||||
monsterInfo.PetList = append(monsterInfo.PetList, *monster)
|
monsterInfo.PetList = append(monsterInfo.PetList, *monster)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,13 @@ func (h Controller) ItemSale(data *C2S_ITEM_SALE, c *player.Player) (result *fig
|
|||||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.Service.Item.UPDATE(data.ItemId, -gconv.Int(data.Amount)); err != nil {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
|
}
|
||||||
|
|
||||||
itemConfig := xmlres.ItemsMAP[int(data.ItemId)]
|
itemConfig := xmlres.ItemsMAP[int(data.ItemId)]
|
||||||
if itemConfig.SellPrice != 0 {
|
if itemConfig.SellPrice != 0 {
|
||||||
c.Info.Coins += int64(int64(data.Amount) * int64(itemConfig.SellPrice))
|
c.Info.Coins += int64(int64(data.Amount) * int64(itemConfig.SellPrice))
|
||||||
}
|
}
|
||||||
c.Service.Item.UPDATE(data.ItemId, -gconv.Int(data.Amount))
|
|
||||||
return result, 0
|
return result, 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
// ItemDefaultLeftTime 道具默认剩余时间(毫秒)
|
// ItemDefaultLeftTime 道具默认剩余时间(毫秒)
|
||||||
ItemDefaultLeftTime = 360000
|
ItemDefaultLeftTime = 360000
|
||||||
|
// UniversalNatureItemID 全能性格转化剂Ω
|
||||||
|
UniversalNatureItemID uint32 = 300136
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetUserItemList 获取用户道具列表
|
// GetUserItemList 获取用户道具列表
|
||||||
@@ -33,11 +35,16 @@ func (h Controller) GetUserItemList(data *ItemListInboundInfo, c *player.Player)
|
|||||||
// c: 当前玩家对象
|
// c: 当前玩家对象
|
||||||
// 返回: 使用后的宠物信息和错误码
|
// 返回: 使用后的宠物信息和错误码
|
||||||
func (h Controller) UsePetItemOutOfFight(data *C2S_USE_PET_ITEM_OUT_OF_FIGHT, c *player.Player) (result *item.S2C_USE_PET_ITEM_OUT_OF_FIGHT, err errorcode.ErrorCode) {
|
func (h Controller) UsePetItemOutOfFight(data *C2S_USE_PET_ITEM_OUT_OF_FIGHT, c *player.Player) (result *item.S2C_USE_PET_ITEM_OUT_OF_FIGHT, err errorcode.ErrorCode) {
|
||||||
_, currentPet, found := c.FindPet(data.CatchTime)
|
slot, found := c.FindPetBagSlot(data.CatchTime)
|
||||||
if !found {
|
if !found {
|
||||||
return nil, errorcode.ErrorCodes.Err10401
|
return nil, errorcode.ErrorCodes.Err10401
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentPet := slot.PetInfoPtr()
|
||||||
|
if currentPet == nil {
|
||||||
|
return nil, errorcode.ErrorCodes.Err10401
|
||||||
|
}
|
||||||
|
|
||||||
itemID := uint32(data.ItemID)
|
itemID := uint32(data.ItemID)
|
||||||
if c.Service.Item.CheakItem(itemID) == 0 {
|
if c.Service.Item.CheakItem(itemID) == 0 {
|
||||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
@@ -51,7 +58,10 @@ func (h Controller) UsePetItemOutOfFight(data *C2S_USE_PET_ITEM_OUT_OF_FIGHT, c
|
|||||||
return nil, errcode
|
return nil, errcode
|
||||||
}
|
}
|
||||||
refreshPetPaneKeepHP(currentPet, oldHP)
|
refreshPetPaneKeepHP(currentPet, oldHP)
|
||||||
c.Service.Item.UPDATE(itemID, -1)
|
if err := c.Service.Item.UPDATE(itemID, -1); err != nil {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
|
}
|
||||||
|
c.Service.Info.Save(*c.Info)
|
||||||
result = &item.S2C_USE_PET_ITEM_OUT_OF_FIGHT{}
|
result = &item.S2C_USE_PET_ITEM_OUT_OF_FIGHT{}
|
||||||
copier.Copy(&result, currentPet)
|
copier.Copy(&result, currentPet)
|
||||||
return result, 0
|
return result, 0
|
||||||
@@ -83,7 +93,10 @@ func (h Controller) UsePetItemOutOfFight(data *C2S_USE_PET_ITEM_OUT_OF_FIGHT, c
|
|||||||
return nil, errcode
|
return nil, errcode
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Service.Item.UPDATE(itemID, -1)
|
if err := c.Service.Item.UPDATE(itemID, -1); err != nil {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
|
}
|
||||||
|
c.Service.Info.Save(*c.Info)
|
||||||
result = &item.S2C_USE_PET_ITEM_OUT_OF_FIGHT{}
|
result = &item.S2C_USE_PET_ITEM_OUT_OF_FIGHT{}
|
||||||
copier.Copy(&result, currentPet)
|
copier.Copy(&result, currentPet)
|
||||||
return result, 0
|
return result, 0
|
||||||
@@ -126,7 +139,9 @@ func (h Controller) handlexuancaiItem(currentPet *model.PetInfo, c *player.Playe
|
|||||||
return errorcode.ErrorCodes.ErrItemUnusable
|
return errorcode.ErrorCodes.ErrItemUnusable
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Service.Item.UPDATE(itemid, -100)
|
if err := c.Service.Item.UPDATE(itemid, -100); err != nil {
|
||||||
|
return errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,11 +197,24 @@ func (h Controller) handleRegularPetItem(itemID uint32, currentPet *model.PetInf
|
|||||||
// c: 当前玩家对象
|
// c: 当前玩家对象
|
||||||
// 返回: 无数据和错误码
|
// 返回: 无数据和错误码
|
||||||
func (h Controller) ResetNature(data *C2S_PET_RESET_NATURE, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
func (h Controller) ResetNature(data *C2S_PET_RESET_NATURE, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||||
_, currentPet, found := c.FindPet(data.CatchTime)
|
slot, found := c.FindPetBagSlot(data.CatchTime)
|
||||||
if !found {
|
if !found {
|
||||||
return nil, errorcode.ErrorCodes.Err10401
|
return nil, errorcode.ErrorCodes.Err10401
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentPet := slot.PetInfoPtr()
|
||||||
|
if currentPet == nil {
|
||||||
|
return nil, errorcode.ErrorCodes.Err10401
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.ItemId != UniversalNatureItemID {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrItemUnusable
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := xmlres.NatureRootMap[int(data.Nature)]; !ok {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrItemUnusable
|
||||||
|
}
|
||||||
|
|
||||||
if c.Service.Item.CheakItem(data.ItemId) <= 0 {
|
if c.Service.Item.CheakItem(data.ItemId) <= 0 {
|
||||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
}
|
}
|
||||||
@@ -194,7 +222,10 @@ func (h Controller) ResetNature(data *C2S_PET_RESET_NATURE, c *player.Player) (r
|
|||||||
currentHP := currentPet.Hp
|
currentHP := currentPet.Hp
|
||||||
currentPet.Nature = data.Nature
|
currentPet.Nature = data.Nature
|
||||||
refreshPetPaneKeepHP(currentPet, currentHP)
|
refreshPetPaneKeepHP(currentPet, currentHP)
|
||||||
c.Service.Item.UPDATE(data.ItemId, -1)
|
if err := c.Service.Item.UPDATE(data.ItemId, -1); err != nil {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
|
}
|
||||||
|
c.Service.Info.Save(*c.Info)
|
||||||
return result, 0
|
return result, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,29 +253,38 @@ func (h Controller) UseSpeedupItem(data *C2S_USE_SPEEDUP_ITEM, c *player.Player)
|
|||||||
if c.Info.TwoTimes != 0 {
|
if c.Info.TwoTimes != 0 {
|
||||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||||
}
|
}
|
||||||
c.Info.TwoTimes += 50 // 玩家对象新增 TwoTimesExp 字段存储双倍剩余次数
|
|
||||||
case 300067:
|
case 300067:
|
||||||
if c.Info.TwoTimes != 0 {
|
if c.Info.TwoTimes != 0 {
|
||||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||||
}
|
}
|
||||||
c.Info.TwoTimes += 25 // 玩家对象新增 TwoTimesExp 字段存储双倍剩余次数
|
|
||||||
case 300051: // 假设1002是三倍经验加速器道具ID
|
case 300051: // 假设1002是三倍经验加速器道具ID
|
||||||
if c.Info.ThreeTimes != 0 {
|
if c.Info.ThreeTimes != 0 {
|
||||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||||
}
|
}
|
||||||
c.Info.ThreeTimes += 50 // 玩家对象新增 ThreeTimesExp 字段存储三倍剩余次数
|
|
||||||
case 300115:
|
case 300115:
|
||||||
if c.Info.ThreeTimes != 0 {
|
if c.Info.ThreeTimes != 0 {
|
||||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||||
}
|
}
|
||||||
c.Info.ThreeTimes += 30 // 玩家对象新增 ThreeTimesExp 字段存储三倍剩余次数
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errorcode.ErrorCodes.ErrSystemError // 未知道具ID
|
return nil, errorcode.ErrorCodes.ErrSystemError // 未知道具ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 扣减道具(数量-1)
|
// 3. 扣减道具(数量-1)
|
||||||
c.Service.Item.UPDATE(data.ItemID, -1)
|
if err := c.Service.Item.UPDATE(data.ItemID, -1); err != nil {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
|
}
|
||||||
|
|
||||||
|
switch data.ItemID {
|
||||||
|
case 300027: // 假设1001是双倍经验加速器道具ID
|
||||||
|
c.Info.TwoTimes += 50 // 玩家对象新增 TwoTimesExp 字段存储双倍剩余次数
|
||||||
|
case 300067:
|
||||||
|
c.Info.TwoTimes += 25 // 玩家对象新增 TwoTimesExp 字段存储双倍剩余次数
|
||||||
|
case 300051: // 假设1002是三倍经验加速器道具ID
|
||||||
|
c.Info.ThreeTimes += 50 // 玩家对象新增 ThreeTimesExp 字段存储三倍剩余次数
|
||||||
|
case 300115:
|
||||||
|
c.Info.ThreeTimes += 30 // 玩家对象新增 ThreeTimesExp 字段存储三倍剩余次数
|
||||||
|
}
|
||||||
result.ThreeTimes = uint32(c.Info.ThreeTimes) // 返回三倍经验剩余次数
|
result.ThreeTimes = uint32(c.Info.ThreeTimes) // 返回三倍经验剩余次数
|
||||||
result.TwoTimes = uint32(c.Info.TwoTimes) // 返回双倍经验剩余次数
|
result.TwoTimes = uint32(c.Info.TwoTimes) // 返回双倍经验剩余次数
|
||||||
|
|
||||||
@@ -275,10 +315,11 @@ func (h Controller) UseEnergyXishou(data *C2S_USE_ENERGY_XISHOU, c *player.Playe
|
|||||||
}
|
}
|
||||||
// 2. 核心业务逻辑:更新能量吸收器剩余次数
|
// 2. 核心业务逻辑:更新能量吸收器剩余次数
|
||||||
// (注:可根据道具ID配置不同的次数加成,此处默认+1)
|
// (注:可根据道具ID配置不同的次数加成,此处默认+1)
|
||||||
|
if err := c.Service.Item.UPDATE(data.ItemID, -1); err != nil {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
|
}
|
||||||
c.Info.EnergyTime += 40 // 玩家对象新增 EnergyTimes 字段存储能量吸收剩余次数
|
c.Info.EnergyTime += 40 // 玩家对象新增 EnergyTimes 字段存储能量吸收剩余次数
|
||||||
|
|
||||||
// 3. 扣减道具(数量-1)
|
|
||||||
c.Service.Item.UPDATE(data.ItemID, -1)
|
|
||||||
result = &item.S2C_USE_ENERGY_XISHOU{
|
result = &item.S2C_USE_ENERGY_XISHOU{
|
||||||
EnergyTimes: uint32(c.Info.EnergyTime),
|
EnergyTimes: uint32(c.Info.EnergyTime),
|
||||||
}
|
}
|
||||||
@@ -309,6 +350,9 @@ func (h Controller) UseAutoFightItem(data *C2S_USE_AUTO_FIGHT_ITEM, c *player.Pl
|
|||||||
if c.Info.AutoFightTime != 0 {
|
if c.Info.AutoFightTime != 0 {
|
||||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||||
}
|
}
|
||||||
|
if err := c.Service.Item.UPDATE(data.ItemID, -1); err != nil {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
|
}
|
||||||
result = &item.S2C_USE_AUTO_FIGHT_ITEM{}
|
result = &item.S2C_USE_AUTO_FIGHT_ITEM{}
|
||||||
// 2. 核心业务逻辑:开启自动战斗 + 更新剩余次数
|
// 2. 核心业务逻辑:开启自动战斗 + 更新剩余次数
|
||||||
c.Info.AutoFight = 3 // 按需求设置自动战斗flag为3(需测试)
|
c.Info.AutoFight = 3 // 按需求设置自动战斗flag为3(需测试)
|
||||||
@@ -324,8 +368,6 @@ func (h Controller) UseAutoFightItem(data *C2S_USE_AUTO_FIGHT_ITEM, c *player.Pl
|
|||||||
c.Info.AutoFightTime += 50
|
c.Info.AutoFightTime += 50
|
||||||
}
|
}
|
||||||
result.AutoFightTimes = c.Info.AutoFightTime
|
result.AutoFightTimes = c.Info.AutoFightTime
|
||||||
// 3. 扣减道具(数量-1)
|
|
||||||
c.Service.Item.UPDATE(data.ItemID, -1)
|
|
||||||
|
|
||||||
return result, 0
|
return result, 0
|
||||||
}
|
}
|
||||||
|
|||||||
60
logic/controller/item_use_test.go
Normal file
60
logic/controller/item_use_test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/common/data/xmlres"
|
||||||
|
"blazing/logic/service/player"
|
||||||
|
playermodel "blazing/modules/player/model"
|
||||||
|
blservice "blazing/modules/player/service"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUsePetItemOutOfFightAppliesToBackupPetInMemory(t *testing.T) {
|
||||||
|
petID := firstPetIDForControllerTest(t)
|
||||||
|
backupPet := playermodel.GenPetInfo(petID, 31, 0, 0, 50, nil, 0)
|
||||||
|
if backupPet == nil {
|
||||||
|
t.Fatal("failed to generate backup pet")
|
||||||
|
}
|
||||||
|
if backupPet.MaxHp <= 1 {
|
||||||
|
t.Fatalf("expected generated pet to have max hp > 1, got %d", backupPet.MaxHp)
|
||||||
|
}
|
||||||
|
backupPet.Hp = 1
|
||||||
|
|
||||||
|
testPlayer := player.NewPlayer(nil)
|
||||||
|
testPlayer.Info = &playermodel.PlayerInfo{
|
||||||
|
UserID: 1,
|
||||||
|
PetList: []playermodel.PetInfo{},
|
||||||
|
BackupPetList: []playermodel.PetInfo{*backupPet},
|
||||||
|
}
|
||||||
|
testPlayer.Service = blservice.NewUserService(testPlayer.Info.UserID)
|
||||||
|
|
||||||
|
itemID, recoverHP := firstRecoverHPItemForControllerTest(t)
|
||||||
|
if recoverHP <= 0 {
|
||||||
|
t.Fatalf("expected positive recover hp for item %d, got %d", itemID, recoverHP)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := (Controller{}).UsePetItemOutOfFight(&C2S_USE_PET_ITEM_OUT_OF_FIGHT{
|
||||||
|
CatchTime: backupPet.CatchTime,
|
||||||
|
ItemID: int32(itemID),
|
||||||
|
}, testPlayer)
|
||||||
|
if err != 0 {
|
||||||
|
t.Fatalf("expected backup pet item use to succeed in-memory, got err=%d", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedPet := testPlayer.Info.BackupPetList[0]
|
||||||
|
if updatedPet.Hp <= 1 {
|
||||||
|
t.Fatalf("expected backup pet hp to increase in memory, got hp=%d", updatedPet.Hp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstRecoverHPItemForControllerTest(t *testing.T) (uint32, int) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
for id, cfg := range xmlres.ItemsMAP {
|
||||||
|
if cfg.HP > 0 {
|
||||||
|
return uint32(id), cfg.HP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatal("xmlres.ItemsMAP has no HP recovery item")
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
@@ -19,20 +19,13 @@ func (h Controller) SavePetBagOrder(
|
|||||||
return nil, 0
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// PetRetrieveFromWarehouse 领回仓库精灵
|
// PetRetrieveFromWarehouse 从放生仓库领回精灵
|
||||||
func (h Controller) PetRetrieveFromWarehouse(
|
func (h Controller) PetRetrieveFromWarehouse(
|
||||||
data *PET_RETRIEVE, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
data *PET_RETRIEVE, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||||
if _, ok := player.FindPetBagSlot(data.CatchTime); ok {
|
if !player.Service.Pet.UpdateFree(data.CatchTime, 1, 0) {
|
||||||
return nil, 0
|
return nil, errorcode.ErrorCodes.ErrPokemonIDMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
petInfo := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime)
|
|
||||||
if petInfo == nil {
|
|
||||||
return nil, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
player.AddPetToAvailableBag(petInfo.Data)
|
|
||||||
|
|
||||||
return nil, 0
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ func (h Controller) PetELV(data *C2S_PET_EVOLVTION, c *player.Player) (result *f
|
|||||||
return nil, errorcode.ErrorCodes.ErrInsufficientItemsMulti
|
return nil, errorcode.ErrorCodes.ErrInsufficientItemsMulti
|
||||||
}
|
}
|
||||||
if branch.EvolvItem != 0 {
|
if branch.EvolvItem != 0 {
|
||||||
c.Service.Item.UPDATE(uint32(branch.EvolvItem), -branch.EvolvItemCount)
|
if err := c.Service.Item.UPDATE(uint32(branch.EvolvItem), -branch.EvolvItemCount); err != nil {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrInsufficientItemsMulti
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPet.ID = uint32(branch.MonTo)
|
currentPet.ID = uint32(branch.MonTo)
|
||||||
|
|||||||
@@ -17,11 +17,16 @@ const (
|
|||||||
// c: 当前玩家对象
|
// c: 当前玩家对象
|
||||||
// 返回: 分配结果和错误码
|
// 返回: 分配结果和错误码
|
||||||
func (h Controller) PetEVDiy(data *PetEV, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
func (h Controller) PetEVDiy(data *PetEV, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||||
_, currentPet, found := c.FindPet(data.CacthTime)
|
slot, found := c.FindPetBagSlot(data.CacthTime)
|
||||||
if !found {
|
if !found {
|
||||||
return nil, errorcode.ErrorCodes.Err10401
|
return nil, errorcode.ErrorCodes.Err10401
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentPet := slot.PetInfoPtr()
|
||||||
|
if currentPet == nil {
|
||||||
|
return nil, errorcode.ErrorCodes.Err10401
|
||||||
|
}
|
||||||
|
|
||||||
var targetTotal uint32
|
var targetTotal uint32
|
||||||
var currentTotal uint32
|
var currentTotal uint32
|
||||||
for i, evValue := range data.EVs {
|
for i, evValue := range data.EVs {
|
||||||
|
|||||||
45
logic/controller/pet_ev_test.go
Normal file
45
logic/controller/pet_ev_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"blazing/logic/service/player"
|
||||||
|
playermodel "blazing/modules/player/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPetEVDiy_AppliesToBackupPet(t *testing.T) {
|
||||||
|
p := player.NewPlayer(nil)
|
||||||
|
p.Info = &playermodel.PlayerInfo{
|
||||||
|
EVPool: 20,
|
||||||
|
PetList: []playermodel.PetInfo{
|
||||||
|
{CatchTime: 1},
|
||||||
|
},
|
||||||
|
BackupPetList: []playermodel.PetInfo{
|
||||||
|
{
|
||||||
|
CatchTime: 2,
|
||||||
|
Level: 100,
|
||||||
|
Ev: [6]uint32{0, 4, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &PetEV{
|
||||||
|
CacthTime: 2,
|
||||||
|
EVs: [6]uint32{0, 8, 4, 0, 0, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := (Controller{}).PetEVDiy(data, p)
|
||||||
|
if err != 0 {
|
||||||
|
t.Fatalf("PetEVDiy returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := p.Info.BackupPetList[0].Ev
|
||||||
|
want := [6]uint32{0, 8, 4, 0, 0, 0}
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("backup pet EV mismatch, got %v want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotPool, wantPool := p.Info.EVPool, int64(12); gotPool != wantPool {
|
||||||
|
t.Fatalf("EVPool mismatch, got %d want %d", gotPool, wantPool)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,16 +65,33 @@ func (h Controller) PetFusion(data *C2S_PetFusion, c *player.Player) (result *pe
|
|||||||
return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
|
return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
|
||||||
}
|
}
|
||||||
|
|
||||||
consumeItems(c, materialCounts)
|
|
||||||
c.Info.Coins -= petFusionCost
|
|
||||||
|
|
||||||
if resultPetID == 0 {
|
if resultPetID == 0 {
|
||||||
if useOptionalItem(c, data.GoldItem1[:], petFusionFailureItemID) {
|
failedAux := *auxPet
|
||||||
result.CostItemFlag = 1
|
if auxPet.Level > 5 {
|
||||||
} else if auxPet.Level > 5 {
|
failedAux.Downgrade(auxPet.Level - 5)
|
||||||
auxPet.Downgrade(auxPet.Level - 5)
|
|
||||||
} else {
|
} else {
|
||||||
auxPet.Downgrade(1)
|
failedAux.Downgrade(1)
|
||||||
|
}
|
||||||
|
txResult, errCode := c.Service.PetFusionTx(
|
||||||
|
*c.Info,
|
||||||
|
data.Mcatchtime,
|
||||||
|
data.Auxcatchtime,
|
||||||
|
materialCounts,
|
||||||
|
data.GoldItem1[:],
|
||||||
|
petFusionKeepAuxItemID,
|
||||||
|
petFusionFailureItemID,
|
||||||
|
petFusionCost,
|
||||||
|
nil,
|
||||||
|
&failedAux,
|
||||||
|
)
|
||||||
|
if errCode != 0 {
|
||||||
|
return result, errCode
|
||||||
|
}
|
||||||
|
c.Info.Coins -= petFusionCost
|
||||||
|
if txResult.CostItemUsed {
|
||||||
|
result.CostItemFlag = 1
|
||||||
|
} else if txResult.UpdatedAux != nil {
|
||||||
|
*auxPet = *txResult.UpdatedAux
|
||||||
}
|
}
|
||||||
return &pet.PetFusionInfo{}, 0
|
return &pet.PetFusionInfo{}, 0
|
||||||
}
|
}
|
||||||
@@ -101,18 +118,37 @@ func (h Controller) PetFusion(data *C2S_PetFusion, c *player.Player) (result *pe
|
|||||||
newPet.RandomByWeightShiny()
|
newPet.RandomByWeightShiny()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Service.Pet.PetAdd(newPet, 0)
|
txResult, errCode := c.Service.PetFusionTx(
|
||||||
//println(c.Info.UserID, "进行融合", len(c.Info.PetList), masterPet.ID, auxPet.ID, newPet.ID)
|
*c.Info,
|
||||||
|
data.Mcatchtime,
|
||||||
c.PetDel(data.Mcatchtime)
|
data.Auxcatchtime,
|
||||||
if useOptionalItem(c, data.GoldItem1[:], petFusionKeepAuxItemID) {
|
materialCounts,
|
||||||
result.CostItemFlag = 1
|
data.GoldItem1[:],
|
||||||
} else {
|
petFusionKeepAuxItemID,
|
||||||
c.PetDel(data.Auxcatchtime)
|
petFusionFailureItemID,
|
||||||
|
petFusionCost,
|
||||||
|
newPet,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if errCode != 0 {
|
||||||
|
return result, errCode
|
||||||
}
|
}
|
||||||
|
|
||||||
result.ObtainTime = newPet.CatchTime
|
c.Info.Coins -= petFusionCost
|
||||||
result.StarterCpTm = newPet.ID
|
if txResult.CostItemUsed {
|
||||||
|
result.CostItemFlag = 1
|
||||||
|
} else {
|
||||||
|
removePetFromPlayerInfo(c, data.Auxcatchtime)
|
||||||
|
}
|
||||||
|
removePetFromPlayerInfo(c, data.Mcatchtime)
|
||||||
|
|
||||||
|
if txResult.NewPet == nil {
|
||||||
|
return result, errorcode.ErrorCodes.ErrSystemError
|
||||||
|
}
|
||||||
|
c.Info.PetList = append(c.Info.PetList, *txResult.NewPet)
|
||||||
|
|
||||||
|
result.ObtainTime = txResult.NewPet.CatchTime
|
||||||
|
result.StarterCpTm = txResult.NewPet.ID
|
||||||
return result, 0
|
return result, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,21 +185,10 @@ func hasEnoughItems(c *player.Player, itemCounts map[uint32]int) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func consumeItems(c *player.Player, itemCounts map[uint32]int) {
|
func removePetFromPlayerInfo(c *player.Player, catchTime uint32) {
|
||||||
for itemID, count := range itemCounts {
|
index, _, ok := c.FindPet(catchTime)
|
||||||
_ = c.Service.Item.UPDATE(itemID, -count)
|
if !ok {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
c.Info.PetList = append(c.Info.PetList[:index], c.Info.PetList[index+1:]...)
|
||||||
|
|
||||||
func useOptionalItem(c *player.Player, itemIDs []uint32, target uint32) bool {
|
|
||||||
if c.Service.Item.CheakItem(target) <= 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, itemID := range itemIDs {
|
|
||||||
if itemID == target {
|
|
||||||
_ = c.Service.Item.UPDATE(target, -1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,22 @@ import (
|
|||||||
"blazing/common/socket/errorcode"
|
"blazing/common/socket/errorcode"
|
||||||
"blazing/logic/service/common"
|
"blazing/logic/service/common"
|
||||||
"blazing/logic/service/pet"
|
"blazing/logic/service/pet"
|
||||||
"blazing/logic/service/player"
|
playersvc "blazing/logic/service/player"
|
||||||
"blazing/modules/player/model"
|
"blazing/modules/player/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetPetInfo 获取精灵信息
|
// GetPetInfo 获取精灵信息
|
||||||
func (h Controller) GetPetInfo(
|
func (h Controller) GetPetInfo(
|
||||||
data *GetPetInfoInboundInfo,
|
data *GetPetInfoInboundInfo,
|
||||||
player *player.Player) (result *model.PetInfo,
|
player *playersvc.Player) (result *model.PetInfo,
|
||||||
err errorcode.ErrorCode) {
|
err errorcode.ErrorCode) {
|
||||||
_, petInfo, found := player.FindPet(data.CatchTime)
|
levelLimit := player.CurrentMapPetLevelLimit()
|
||||||
if found {
|
if slot, found := player.FindPetBagSlot(data.CatchTime); found {
|
||||||
result = petInfo
|
if petInfo := slot.PetInfoPtr(); petInfo != nil {
|
||||||
return result, 0
|
petCopy := playersvc.ApplyPetLevelLimit(*petInfo, levelLimit)
|
||||||
|
result = &petCopy
|
||||||
|
return result, 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime)
|
ret := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime)
|
||||||
@@ -24,16 +27,18 @@ func (h Controller) GetPetInfo(
|
|||||||
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
||||||
}
|
}
|
||||||
|
|
||||||
result = &ret.Data
|
petData := ret.Data
|
||||||
|
petData = playersvc.ApplyPetLevelLimit(petData, levelLimit)
|
||||||
|
result = &petData
|
||||||
return result, 0
|
return result, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserBagPetInfo 获取主背包和并列备用精灵列表
|
// GetUserBagPetInfo 获取主背包和并列备用精灵列表
|
||||||
func (h Controller) GetUserBagPetInfo(
|
func (h Controller) GetUserBagPetInfo(
|
||||||
data *GetUserBagPetInfoInboundEmpty,
|
data *GetUserBagPetInfoInboundEmpty,
|
||||||
player *player.Player) (result *pet.GetUserBagPetInfoOutboundInfo,
|
player *playersvc.Player) (result *pet.GetUserBagPetInfoOutboundInfo,
|
||||||
err errorcode.ErrorCode) {
|
err errorcode.ErrorCode) {
|
||||||
return player.GetUserBagPetInfo(), 0
|
return player.GetUserBagPetInfo(player.CurrentMapPetLevelLimit()), 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPetListInboundEmpty 定义请求或响应数据结构。
|
// GetPetListInboundEmpty 定义请求或响应数据结构。
|
||||||
@@ -44,7 +49,7 @@ type GetPetListInboundEmpty struct {
|
|||||||
// GetPetList 获取当前主背包列表
|
// GetPetList 获取当前主背包列表
|
||||||
func (h Controller) GetPetList(
|
func (h Controller) GetPetList(
|
||||||
data *GetPetListInboundEmpty,
|
data *GetPetListInboundEmpty,
|
||||||
player *player.Player) (result *pet.GetPetListOutboundInfo,
|
player *playersvc.Player) (result *pet.GetPetListOutboundInfo,
|
||||||
err errorcode.ErrorCode) {
|
err errorcode.ErrorCode) {
|
||||||
return buildPetListOutboundInfo(player.Info.PetList), 0
|
return buildPetListOutboundInfo(player.Info.PetList), 0
|
||||||
}
|
}
|
||||||
@@ -57,7 +62,7 @@ type GetPetListFreeInboundEmpty struct {
|
|||||||
// GetPetReleaseList 获取仓库可放生列表
|
// GetPetReleaseList 获取仓库可放生列表
|
||||||
func (h Controller) GetPetReleaseList(
|
func (h Controller) GetPetReleaseList(
|
||||||
data *GetPetListFreeInboundEmpty,
|
data *GetPetListFreeInboundEmpty,
|
||||||
player *player.Player) (result *pet.GetPetListOutboundInfo,
|
player *playersvc.Player) (result *pet.GetPetListOutboundInfo,
|
||||||
err errorcode.ErrorCode) {
|
err errorcode.ErrorCode) {
|
||||||
|
|
||||||
return buildPetListOutboundInfo(player.WarehousePetList()), 0
|
return buildPetListOutboundInfo(player.WarehousePetList()), 0
|
||||||
@@ -66,14 +71,13 @@ func (h Controller) GetPetReleaseList(
|
|||||||
// PlayerShowPet 精灵展示
|
// PlayerShowPet 精灵展示
|
||||||
func (h Controller) PlayerShowPet(
|
func (h Controller) PlayerShowPet(
|
||||||
data *PetShowInboundInfo,
|
data *PetShowInboundInfo,
|
||||||
player *player.Player) (result *pet.PetShowOutboundInfo, err errorcode.ErrorCode) {
|
player *playersvc.Player) (result *pet.PetShowOutboundInfo, err errorcode.ErrorCode) {
|
||||||
result = &pet.PetShowOutboundInfo{
|
result = &pet.PetShowOutboundInfo{
|
||||||
UserID: data.Head.UserID,
|
UserID: data.Head.UserID,
|
||||||
CatchTime: data.CatchTime,
|
CatchTime: data.CatchTime,
|
||||||
Flag: data.Flag,
|
Flag: data.Flag,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, currentPet, ok := player.FindPet(data.CatchTime)
|
|
||||||
if data.Flag == 0 {
|
if data.Flag == 0 {
|
||||||
player.SetPetDisplay(0, nil)
|
player.SetPetDisplay(0, nil)
|
||||||
player.GetSpace().RefreshUserInfo(player)
|
player.GetSpace().RefreshUserInfo(player)
|
||||||
@@ -81,10 +85,18 @@ func (h Controller) PlayerShowPet(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
// 仅允许仓库精灵展示:背包中的精灵直接拒绝
|
||||||
|
if _, ok := player.FindPetBagSlot(data.CatchTime); ok {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrCannotShowBagPokemon
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime)
|
||||||
|
if ret == nil {
|
||||||
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentPet := &ret.Data
|
||||||
|
|
||||||
player.SetPetDisplay(data.Flag, currentPet)
|
player.SetPetDisplay(data.Flag, currentPet)
|
||||||
player.GetSpace().RefreshUserInfo(player)
|
player.GetSpace().RefreshUserInfo(player)
|
||||||
result = buildPetShowOutboundInfo(data.Head.UserID, data.Flag, currentPet)
|
result = buildPetShowOutboundInfo(data.Head.UserID, data.Flag, currentPet)
|
||||||
|
|||||||
@@ -6,8 +6,39 @@ import (
|
|||||||
"blazing/logic/service/fight"
|
"blazing/logic/service/fight"
|
||||||
"blazing/logic/service/pet"
|
"blazing/logic/service/pet"
|
||||||
"blazing/logic/service/player"
|
"blazing/logic/service/player"
|
||||||
|
playermodel "blazing/modules/player/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func petSetExpLimit(currentPet *playermodel.PetInfo) int64 {
|
||||||
|
if currentPet == nil || currentPet.Level >= 100 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
simulatedPet := *currentPet
|
||||||
|
allowedExp := simulatedPet.NextLvExp - simulatedPet.Exp
|
||||||
|
if allowedExp < 0 {
|
||||||
|
allowedExp = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for simulatedPet.Level < 100 && simulatedPet.NextLvExp > 0 {
|
||||||
|
simulatedPet.Level++
|
||||||
|
simulatedPet.Update(true)
|
||||||
|
if simulatedPet.Level >= 100 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
allowedExp += simulatedPet.NextLvExp
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowedExp
|
||||||
|
}
|
||||||
|
|
||||||
|
func minInt64(a, b int64) int64 {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// PetReleaseToWarehouse 将精灵从仓库包中放生
|
// PetReleaseToWarehouse 将精灵从仓库包中放生
|
||||||
func (h Controller) PetReleaseToWarehouse(
|
func (h Controller) PetReleaseToWarehouse(
|
||||||
data *PET_ROWEI, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
data *PET_ROWEI, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||||
@@ -17,9 +48,8 @@ func (h Controller) PetReleaseToWarehouse(
|
|||||||
if inBag || inBackup || freeForbidden == 1 {
|
if inBag || inBackup || freeForbidden == 1 {
|
||||||
return nil, errorcode.ErrorCodes.ErrCannotReleaseNonWarehouse
|
return nil, errorcode.ErrorCodes.ErrCannotReleaseNonWarehouse
|
||||||
}
|
}
|
||||||
|
if !player.Service.Pet.UpdateFree(data.CatchTime, 0, 1) {
|
||||||
if !player.Service.Pet.UpdateFree(data.CatchTime, 1) {
|
return nil, errorcode.ErrorCodes.ErrCannotReleaseNonWarehouse
|
||||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, 0
|
return nil, 0
|
||||||
@@ -32,9 +62,11 @@ func (h Controller) PetOneCure(
|
|||||||
return result, errorcode.ErrorCodes.ErrChampionCannotHeal
|
return result, errorcode.ErrorCodes.ErrChampionCannotHeal
|
||||||
}
|
}
|
||||||
|
|
||||||
_, currentPet, ok := player.FindPet(data.CatchTime)
|
if slot, ok := player.FindPetBagSlot(data.CatchTime); ok {
|
||||||
if ok {
|
currentPet := slot.PetInfoPtr()
|
||||||
defer currentPet.Cure()
|
if currentPet != nil {
|
||||||
|
defer currentPet.Cure()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pet.PetOneCureOutboundInfo{
|
return &pet.PetOneCureOutboundInfo{
|
||||||
@@ -63,11 +95,17 @@ func (h Controller) PetFirst(
|
|||||||
func (h Controller) SetPetExp(
|
func (h Controller) SetPetExp(
|
||||||
data *PetSetExpInboundInfo,
|
data *PetSetExpInboundInfo,
|
||||||
player *player.Player) (result *pet.PetSetExpOutboundInfo, err errorcode.ErrorCode) {
|
player *player.Player) (result *pet.PetSetExpOutboundInfo, err errorcode.ErrorCode) {
|
||||||
_, currentPet, found := player.FindPet(data.CatchTime)
|
slot, found := player.FindPetBagSlot(data.CatchTime)
|
||||||
if !found || currentPet.Level >= 100 {
|
currentPet := slot.PetInfoPtr()
|
||||||
|
if !found || currentPet == nil || currentPet.Level >= 100 {
|
||||||
return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, errorcode.ErrorCodes.ErrSystemError
|
return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, errorcode.ErrorCodes.ErrSystemError
|
||||||
}
|
}
|
||||||
|
|
||||||
player.AddPetExp(currentPet, data.Exp)
|
allowedExp := petSetExpLimit(currentPet)
|
||||||
|
if allowedExp <= 0 {
|
||||||
|
return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, errorcode.ErrorCodes.ErrSystemError
|
||||||
|
}
|
||||||
|
|
||||||
|
player.AddPetExp(currentPet, minInt64(data.Exp, allowedExp))
|
||||||
return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, 0
|
return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, 0
|
||||||
}
|
}
|
||||||
|
|||||||
75
logic/controller/pet_manage_test.go
Normal file
75
logic/controller/pet_manage_test.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/common/data/xmlres"
|
||||||
|
"blazing/common/socket/errorcode"
|
||||||
|
"blazing/logic/service/player"
|
||||||
|
playermodel "blazing/modules/player/model"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func firstPetIDForControllerTest(t *testing.T) int {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
for id := range xmlres.PetMAP {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatal("xmlres.PetMAP is empty")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetPetExpCapsLevelAt100(t *testing.T) {
|
||||||
|
petID := firstPetIDForControllerTest(t)
|
||||||
|
petInfo := playermodel.GenPetInfo(petID, 31, 0, 0, 99, nil, 0)
|
||||||
|
if petInfo == nil {
|
||||||
|
t.Fatal("failed to generate test pet")
|
||||||
|
}
|
||||||
|
|
||||||
|
expPool := petInfo.NextLvExp + 10_000
|
||||||
|
testPlayer := player.NewPlayer(nil)
|
||||||
|
testPlayer.Info = &playermodel.PlayerInfo{
|
||||||
|
ExpPool: expPool,
|
||||||
|
PetList: []playermodel.PetInfo{*petInfo},
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPet := &testPlayer.Info.PetList[0]
|
||||||
|
result, err := (Controller{}).SetPetExp(&PetSetExpInboundInfo{
|
||||||
|
CatchTime: currentPet.CatchTime,
|
||||||
|
Exp: expPool,
|
||||||
|
}, testPlayer)
|
||||||
|
if err != 0 {
|
||||||
|
t.Fatalf("expected SetPetExp to succeed, got err=%d", err)
|
||||||
|
}
|
||||||
|
if currentPet.Level != 100 {
|
||||||
|
t.Fatalf("expected pet level to stop at 100, got %d", currentPet.Level)
|
||||||
|
}
|
||||||
|
if result.Exp != 10_000 {
|
||||||
|
t.Fatalf("expected overflow exp to remain in pool, got %d", result.Exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetPetExpRejectsPetAtLevel100(t *testing.T) {
|
||||||
|
petID := firstPetIDForControllerTest(t)
|
||||||
|
petInfo := playermodel.GenPetInfo(petID, 31, 0, 0, 100, nil, 0)
|
||||||
|
if petInfo == nil {
|
||||||
|
t.Fatal("failed to generate test pet")
|
||||||
|
}
|
||||||
|
|
||||||
|
testPlayer := player.NewPlayer(nil)
|
||||||
|
testPlayer.Info = &playermodel.PlayerInfo{
|
||||||
|
ExpPool: 50_000,
|
||||||
|
PetList: []playermodel.PetInfo{*petInfo},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := (Controller{}).SetPetExp(&PetSetExpInboundInfo{
|
||||||
|
CatchTime: petInfo.CatchTime,
|
||||||
|
Exp: 12_345,
|
||||||
|
}, testPlayer)
|
||||||
|
if err != errorcode.ErrorCodes.ErrSystemError {
|
||||||
|
t.Fatalf("expected level-100 pet to be rejected, got err=%d", err)
|
||||||
|
}
|
||||||
|
if result.Exp != 50_000 {
|
||||||
|
t.Fatalf("expected exp pool to remain unchanged, got %d", result.Exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,8 +67,9 @@ func (h Controller) GetPetLearnableSkills(
|
|||||||
data *GetPetLearnableSkillsInboundInfo,
|
data *GetPetLearnableSkillsInboundInfo,
|
||||||
c *player.Player,
|
c *player.Player,
|
||||||
) (result *GetPetLearnableSkillsOutboundInfo, err errorcode.ErrorCode) {
|
) (result *GetPetLearnableSkillsOutboundInfo, err errorcode.ErrorCode) {
|
||||||
_, currentPet, ok := c.FindPet(data.CatchTime)
|
slot, ok := c.FindPetBagSlot(data.CatchTime)
|
||||||
if !ok {
|
currentPet := slot.PetInfoPtr()
|
||||||
|
if !ok || currentPet == nil {
|
||||||
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,8 +82,9 @@ func (h Controller) GetPetLearnableSkills(
|
|||||||
func (h Controller) SetPetSkill(data *ChangeSkillInfo, c *player.Player) (result *pet.ChangeSkillOutInfo, err errorcode.ErrorCode) {
|
func (h Controller) SetPetSkill(data *ChangeSkillInfo, c *player.Player) (result *pet.ChangeSkillOutInfo, err errorcode.ErrorCode) {
|
||||||
const setSkillCost = 50
|
const setSkillCost = 50
|
||||||
|
|
||||||
_, currentPet, ok := c.FindPet(data.CatchTime)
|
slot, ok := c.FindPetBagSlot(data.CatchTime)
|
||||||
if !ok {
|
currentPet := slot.PetInfoPtr()
|
||||||
|
if !ok || currentPet == nil {
|
||||||
return nil, errorcode.ErrorCodes.ErrSystemBusy
|
return nil, errorcode.ErrorCodes.ErrSystemBusy
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,8 +149,9 @@ func (h Controller) SetPetSkill(data *ChangeSkillInfo, c *player.Player) (result
|
|||||||
func (h Controller) SortPetSkills(data *C2S_Skill_Sort, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
func (h Controller) SortPetSkills(data *C2S_Skill_Sort, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||||
const skillSortCost = 50
|
const skillSortCost = 50
|
||||||
|
|
||||||
_, currentPet, ok := c.FindPet(data.CapTm)
|
slot, ok := c.FindPetBagSlot(data.CapTm)
|
||||||
if !ok {
|
currentPet := slot.PetInfoPtr()
|
||||||
|
if !ok || currentPet == nil {
|
||||||
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,8 +208,9 @@ func (h Controller) CommitPetSkills(
|
|||||||
const setSkillCost = 50
|
const setSkillCost = 50
|
||||||
const skillSortCost = 50
|
const skillSortCost = 50
|
||||||
|
|
||||||
_, currentPet, ok := c.FindPet(data.CatchTime)
|
slot, ok := c.FindPetBagSlot(data.CatchTime)
|
||||||
if !ok {
|
currentPet := slot.PetInfoPtr()
|
||||||
|
if !ok || currentPet == nil {
|
||||||
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"blazing/common/socket/errorcode"
|
"blazing/common/socket/errorcode"
|
||||||
logicplayer "blazing/logic/service/player"
|
logicplayer "blazing/logic/service/player"
|
||||||
"blazing/logic/service/user"
|
"blazing/logic/service/user"
|
||||||
|
baseservice "blazing/modules/base/service"
|
||||||
configservice "blazing/modules/config/service"
|
configservice "blazing/modules/config/service"
|
||||||
playerservice "blazing/modules/player/service"
|
playerservice "blazing/modules/player/service"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -14,6 +15,11 @@ import (
|
|||||||
func (h Controller) CDK(data *C2S_GET_GIFT_COMPLETE, player *logicplayer.Player) (result *user.S2C_GET_GIFT_COMPLETE, err errorcode.ErrorCode) {
|
func (h Controller) CDK(data *C2S_GET_GIFT_COMPLETE, player *logicplayer.Player) (result *user.S2C_GET_GIFT_COMPLETE, err errorcode.ErrorCode) {
|
||||||
result = &user.S2C_GET_GIFT_COMPLETE{}
|
result = &user.S2C_GET_GIFT_COMPLETE{}
|
||||||
|
|
||||||
|
userInfo := baseservice.NewBaseSysUserService().GetPerson(data.Head.UserID)
|
||||||
|
if userInfo == nil || userInfo.QQ == 0 {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrCannotPerformAction
|
||||||
|
}
|
||||||
|
|
||||||
cdkCode := strings.Trim(data.PassText, "\x00")
|
cdkCode := strings.Trim(data.PassText, "\x00")
|
||||||
cdkService := configservice.NewCdkService()
|
cdkService := configservice.NewCdkService()
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
@@ -35,7 +41,12 @@ func (h Controller) CDK(data *C2S_GET_GIFT_COMPLETE, player *logicplayer.Player)
|
|||||||
return nil, errorcode.ErrorCodes.ErrMolecularCodeGiftsGone
|
return nil, errorcode.ErrorCodes.ErrMolecularCodeGiftsGone
|
||||||
}
|
}
|
||||||
|
|
||||||
reward, grantErr := playerservice.NewCdkService(data.Head.UserID).GrantConfigReward(uint32(r.ID))
|
reward, grantErr := playerservice.NewCdkService(data.Head.UserID).GrantConfigReward(
|
||||||
|
uint32(r.ID),
|
||||||
|
func(itemID uint32, count int64) bool {
|
||||||
|
return player.ItemAdd(int64(itemID), count)
|
||||||
|
},
|
||||||
|
)
|
||||||
if grantErr != nil {
|
if grantErr != nil {
|
||||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ func (f *FightC) Over(c common.PlayerI, res model.EnumBattleOverReason) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
f.overl.Do(func() {
|
f.overl.Do(func() {
|
||||||
f.Reason = res
|
f.Reason = normalizeFightOverReason(res)
|
||||||
if f.GetInputByPlayer(c, true) != nil {
|
if f.GetInputByPlayer(c, true) != nil {
|
||||||
f.WinnerId = f.GetInputByPlayer(c, true).UserID
|
f.WinnerId = f.GetInputByPlayer(c, true).UserID
|
||||||
}
|
}
|
||||||
@@ -370,7 +370,7 @@ func (f *FightC) collectFightPetInfos(inputs []*input.Input) []info.FightPetInfo
|
|||||||
Hp: currentPet.Info.Hp,
|
Hp: currentPet.Info.Hp,
|
||||||
MaxHp: currentPet.Info.MaxHp,
|
MaxHp: currentPet.Info.MaxHp,
|
||||||
Level: currentPet.Info.Level,
|
Level: currentPet.Info.Level,
|
||||||
Catchable: uint32(fighter.CanCapture),
|
Catchable: fightPetCatchableFlag(fighter.CanCapture),
|
||||||
}
|
}
|
||||||
if fighter.AttackValue != nil {
|
if fighter.AttackValue != nil {
|
||||||
fightInfo.Prop = fighter.AttackValue.Prop
|
fightInfo.Prop = fighter.AttackValue.Prop
|
||||||
@@ -380,6 +380,13 @@ func (f *FightC) collectFightPetInfos(inputs []*input.Input) []info.FightPetInfo
|
|||||||
return infos
|
return infos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fightPetCatchableFlag(catchRate int) uint32 {
|
||||||
|
if catchRate > 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// checkBothPlayersReady 检查PVP战斗中双方是否都已准备完成
|
// checkBothPlayersReady 检查PVP战斗中双方是否都已准备完成
|
||||||
// 参数c为当前准备的玩家,返回true表示双方均准备完成
|
// 参数c为当前准备的玩家,返回true表示双方均准备完成
|
||||||
func (f *FightC) checkBothPlayersReady(currentPlayer common.PlayerI) bool {
|
func (f *FightC) checkBothPlayersReady(currentPlayer common.PlayerI) bool {
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ func (e *Effect1181) OnSkill() bool {
|
|||||||
type Effect1182 struct{ node.EffectNode }
|
type Effect1182 struct{ node.EffectNode }
|
||||||
|
|
||||||
func (e *Effect1182) Skill_Use() bool {
|
func (e *Effect1182) Skill_Use() bool {
|
||||||
if len(e.Args()) < 2 || e.Ctx().Our == nil || e.Ctx().Our.CurPet[0] == nil || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil {
|
if len(e.Args()) < 2 || e.Ctx().Our == nil || e.Ctx().Our.CurPet[0] == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,9 +153,15 @@ func (e *Effect1182) Skill_Use() bool {
|
|||||||
if targetHP.Cmp(alpacadecimal.Zero) < 0 {
|
if targetHP.Cmp(alpacadecimal.Zero) < 0 {
|
||||||
targetHP = alpacadecimal.Zero
|
targetHP = alpacadecimal.Zero
|
||||||
}
|
}
|
||||||
if e.Ctx().Opp.CurPet[0].GetHP().Cmp(targetHP) > 0 {
|
forEachEnemyTargetBySkill(e.Ctx().Our, e.Ctx().Opp, e.Ctx().SkillEntity, func(target *input.Input) bool {
|
||||||
e.Ctx().Opp.CurPet[0].Info.Hp = uint32(targetHP.IntPart())
|
if target == nil || target.CurrentPet() == nil {
|
||||||
}
|
return true
|
||||||
|
}
|
||||||
|
if target.CurrentPet().GetHP().Cmp(targetHP) > 0 {
|
||||||
|
target.CurrentPet().Info.Hp = uint32(targetHP.IntPart())
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1182, int(e.Args()[1].IntPart()))
|
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1182, int(e.Args()[1].IntPart()))
|
||||||
if sub != nil {
|
if sub != nil {
|
||||||
|
|||||||
@@ -10,20 +10,23 @@ type Effect169 struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Effect169) OnSkill() bool {
|
func (e *Effect169) OnSkill() bool {
|
||||||
|
|
||||||
chance := e.Args()[1].IntPart()
|
chance := e.Args()[1].IntPart()
|
||||||
success, _, _ := e.Input.Player.Roll(int(chance), 100)
|
success, _, _ := e.Input.Player.Roll(int(chance), 100)
|
||||||
if success {
|
if success {
|
||||||
// 添加异常状态
|
e.ForEachOpponentSlot(func(target *input.Input) bool {
|
||||||
statusEffect := e.CarrierInput().InitEffect(input.EffectType.Status, int(e.Args()[2].IntPart())) // 以麻痹为例
|
if target == nil {
|
||||||
if statusEffect != nil {
|
return true
|
||||||
e.TargetInput().AddEffect(e.CarrierInput(), statusEffect)
|
}
|
||||||
}
|
statusEffect := e.CarrierInput().InitEffect(input.EffectType.Status, int(e.Args()[2].IntPart()))
|
||||||
|
if statusEffect != nil {
|
||||||
|
target.AddEffect(e.CarrierInput(), statusEffect)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
input.InitEffect(input.EffectType.Skill, 169, &Effect169{})
|
input.InitEffect(input.EffectType.Skill, 169, &Effect169{})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ func (e *Effect2194) OnSkill() bool {
|
|||||||
if e.Ctx().Opp.CurPet[0] == nil {
|
if e.Ctx().Opp.CurPet[0] == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
addStatusByID(e.Ctx().Our, e.Ctx().Opp, int(info.PetStatus.DrainedHP))
|
addTimedStatus(e.Ctx().Our, e.Ctx().Opp, int(info.PetStatus.DrainedHP), 4)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (e *Effect13) OnSkill() bool {
|
|||||||
if eff == nil {
|
if eff == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
eff.Duration(e.EffectNode.SideEffectArgs[0] - 1)
|
eff.Duration(e.EffectNode.SideEffectArgs[0])
|
||||||
|
|
||||||
e.Ctx().Opp.AddEffect(e.Ctx().Our, eff)
|
e.Ctx().Opp.AddEffect(e.Ctx().Our, eff)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package effect
|
package effect
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"blazing/logic/service/fight/input"
|
||||||
"blazing/logic/service/fight/node"
|
"blazing/logic/service/fight/node"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,14 +42,16 @@ type Effect5 struct {
|
|||||||
// 技能触发时调用
|
// 技能触发时调用
|
||||||
// -----------------------------------------------------------
|
// -----------------------------------------------------------
|
||||||
func (e *Effect5) Skill_Use() bool {
|
func (e *Effect5) Skill_Use() bool {
|
||||||
|
|
||||||
// 概率判定
|
// 概率判定
|
||||||
ok, _, _ := e.Input.Player.Roll(e.SideEffectArgs[1], 100)
|
ok, _, _ := e.Input.Player.Roll(e.SideEffectArgs[1], 100)
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Ctx().Opp.SetProp(e.Ctx().Our, int8(e.SideEffectArgs[0]), int8(e.SideEffectArgs[2]))
|
forEachEnemyTargetBySkill(e.Ctx().Our, e.Ctx().Opp, e.Ctx().SkillEntity, func(target *input.Input) bool {
|
||||||
|
target.SetProp(e.Ctx().Our, int8(e.SideEffectArgs[0]), int8(e.SideEffectArgs[2]))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,15 +22,19 @@ type Effect76 struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Effect76) OnSkill() bool {
|
func (e *Effect76) OnSkill() bool {
|
||||||
|
|
||||||
// 概率判定
|
// 概率判定
|
||||||
ok, _, _ := e.Input.Player.Roll(int(e.Args()[0].IntPart()), 100)
|
ok, _, _ := e.Input.Player.Roll(int(e.Args()[0].IntPart()), 100)
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{
|
|
||||||
Type: info.DamageType.Fixed,
|
damage := alpacadecimal.NewFromInt(int64(e.SideEffectArgs[2]))
|
||||||
Damage: alpacadecimal.NewFromInt(int64(e.SideEffectArgs[2])),
|
forEachEnemyTargetBySkill(e.Ctx().Our, e.Ctx().Opp, e.Ctx().SkillEntity, func(target *input.Input) bool {
|
||||||
|
target.Damage(e.Ctx().Our, &info.DamageZone{
|
||||||
|
Type: info.DamageType.Fixed,
|
||||||
|
Damage: damage,
|
||||||
|
})
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ var effectInfoByID = map[int]string{
|
|||||||
29: "额外附加{0}点固定伤害",
|
29: "额外附加{0}点固定伤害",
|
||||||
31: "",
|
31: "",
|
||||||
32: "使用后{0}回合攻击击中对象要害概率增加1/16",
|
32: "使用后{0}回合攻击击中对象要害概率增加1/16",
|
||||||
33: "消除对手能力提升状态",
|
33: "消除敌方阵营所有强化",
|
||||||
34: "将所受的伤害{0}倍反馈给对手",
|
34: "将所受的伤害{0}倍反馈给对手",
|
||||||
35: "惩罚,对方能力等级越高,此技能威力越大",
|
35: "惩罚,对方能力等级越高,此技能威力越大",
|
||||||
36: "命中时{0}%的概率秒杀对方",
|
36: "命中时{0}%的概率秒杀对方",
|
||||||
@@ -120,7 +120,7 @@ var effectInfoByID = map[int]string{
|
|||||||
164: "{0}回合内若受到攻击则有{1}%概率令对手{2}",
|
164: "{0}回合内若受到攻击则有{1}%概率令对手{2}",
|
||||||
165: "{0}回合内每回合防御和特防等级+{1}",
|
165: "{0}回合内每回合防御和特防等级+{1}",
|
||||||
166: "{0}回合内若对手使用属性攻击则{2}%对手{1}等级{3}",
|
166: "{0}回合内若对手使用属性攻击则{2}%对手{1}等级{3}",
|
||||||
169: "{0}回合内每回合额外附加{1}%概率令对手{2}",
|
169: "{0}回合内每回合额外附加{1}%概率令对方阵营全体{2}",
|
||||||
170: "若先出手,则免疫当回合伤害并回复1/{0}的最大体力值",
|
170: "若先出手,则免疫当回合伤害并回复1/{0}的最大体力值",
|
||||||
171: "{0}回合内自身使用属性技能时能较快出手",
|
171: "{0}回合内自身使用属性技能时能较快出手",
|
||||||
172: "若后出手,则给予对方损伤的1/{0}会回复自己的体力",
|
172: "若后出手,则给予对方损伤的1/{0}会回复自己的体力",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func (e *Effect3) Skill_Use() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Effect 33: 消除对手能力提升状态
|
// Effect 33: 消除敌方阵营所有强化
|
||||||
type Effect33 struct {
|
type Effect33 struct {
|
||||||
node.EffectNode
|
node.EffectNode
|
||||||
Reverse bool
|
Reverse bool
|
||||||
@@ -38,13 +38,17 @@ type Effect33 struct {
|
|||||||
// 执行时逻辑
|
// 执行时逻辑
|
||||||
// ----------------------
|
// ----------------------
|
||||||
func (e *Effect33) Skill_Use() bool {
|
func (e *Effect33) Skill_Use() bool {
|
||||||
|
e.ForEachOpponentSlot(func(target *input.Input) bool {
|
||||||
for i, v := range e.Ctx().Opp.Prop[:] {
|
if target == nil {
|
||||||
if v > 0 {
|
return true
|
||||||
e.Ctx().Opp.SetProp(e.Ctx().Our, int8(i), 0)
|
|
||||||
}
|
}
|
||||||
|
for i, v := range target.Prop[:] {
|
||||||
}
|
if v > 0 {
|
||||||
|
target.SetProp(e.Ctx().Our, int8(i), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -54,8 +58,8 @@ func (e *Effect33) Skill_Use() bool {
|
|||||||
// ----------------------
|
// ----------------------
|
||||||
func init() {
|
func init() {
|
||||||
// {3, false, 0}, // 解除自身能力下降状态
|
// {3, false, 0}, // 解除自身能力下降状态
|
||||||
// {33, true, 0}, // 消除对手能力提升状态{3, false, 0}, // 解除自身能力下降状态
|
// {33, true, 0}, // 消除敌方阵营所有强化{3, false, 0}, // 解除自身能力下降状态
|
||||||
// {33, true, 0}, // 消除对手能力提升状态
|
// {33, true, 0}, // 消除敌方阵营所有强化
|
||||||
input.InitEffect(input.EffectType.Skill, 3, &Effect3{})
|
input.InitEffect(input.EffectType.Skill, 3, &Effect3{})
|
||||||
input.InitEffect(input.EffectType.Skill, 33, &Effect33{})
|
input.InitEffect(input.EffectType.Skill, 33, &Effect33{})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,43 +36,73 @@ func (e *StatusCannotAct) ActionStart(attacker, defender *action.SelectSkillActi
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 疲惫状态:仅限制攻击技能,本回合属性技能仍可正常使用。
|
||||||
|
type StatusTired struct {
|
||||||
|
BaseStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *StatusTired) ActionStart(attacker, defender *action.SelectSkillAction) bool {
|
||||||
|
if e.Ctx().SkillEntity == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return e.Ctx().SkillEntity.Category() == info.Category.STATUS
|
||||||
|
}
|
||||||
|
|
||||||
// 睡眠状态:受击后解除
|
// 睡眠状态:受击后解除
|
||||||
type StatusSleep struct {
|
type StatusSleep struct {
|
||||||
StatusCannotAct
|
StatusCannotAct
|
||||||
hasTriedAct bool // 标记是否尝试过行动
|
hasTriedAct bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 睡眠在“被攻击且未 miss”后立即解除,而不是等到技能使用后节点。
|
||||||
|
func (e *StatusSleep) DamageSubEx(zone *info.DamageZone) bool {
|
||||||
|
if zone == nil || e.Ctx().SkillEntity == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if e.Ctx().SkillEntity.Category() != info.Category.STATUS {
|
||||||
|
e.Alive(false)
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试出手时标记状态
|
|
||||||
func (e *StatusSleep) ActionStart(attacker, defender *action.SelectSkillAction) bool {
|
func (e *StatusSleep) ActionStart(attacker, defender *action.SelectSkillAction) bool {
|
||||||
|
if e.Duration() <= 0 {
|
||||||
|
e.hasTriedAct = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
e.hasTriedAct = true
|
e.hasTriedAct = true
|
||||||
return e.StatusCannotAct.ActionStart(attacker, defender)
|
return e.StatusCannotAct.ActionStart(attacker, defender)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 技能使用后处理:非状态类技能触发后解除睡眠
|
|
||||||
func (e *StatusSleep) Skill_Use_ex() bool {
|
func (e *StatusSleep) Skill_Use_ex() bool {
|
||||||
if !e.hasTriedAct {
|
if !e.hasTriedAct {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// 技能实体存在且非状态类型技能,解除睡眠
|
if e.Duration() <= 0 && e.Ctx().SkillEntity != nil && e.Ctx().Category() != info.Category.STATUS {
|
||||||
if e.Ctx().SkillEntity != nil && e.Ctx().Category() != info.Category.STATUS {
|
|
||||||
e.Alive(false)
|
e.Alive(false)
|
||||||
}
|
}
|
||||||
|
e.hasTriedAct = false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *StatusSleep) TurnEnd() {
|
||||||
|
e.hasTriedAct = false
|
||||||
|
e.StatusCannotAct.TurnEnd()
|
||||||
|
}
|
||||||
|
|
||||||
// 持续伤害状态基类(中毒、冻伤、烧伤等)
|
// 持续伤害状态基类(中毒、冻伤、烧伤等)
|
||||||
type ContinuousDamage struct {
|
type ContinuousDamage struct {
|
||||||
BaseStatus
|
BaseStatus
|
||||||
isheal bool //是否回血
|
isheal bool //是否回血
|
||||||
}
|
}
|
||||||
|
|
||||||
// 技能命中前触发伤害(1/8最大生命值真实伤害)
|
// 回合开始触发持续伤害,保证吃药/空过回合时也会正常结算。
|
||||||
func (e *ContinuousDamage) ActionStart(attacker, defender *action.SelectSkillAction) bool {
|
func (e *ContinuousDamage) TurnStart(attacker, defender *action.SelectSkillAction) {
|
||||||
carrier := e.CarrierInput()
|
carrier := e.CarrierInput()
|
||||||
source := e.SourceInput()
|
source := e.SourceInput()
|
||||||
opp := e.TargetInput()
|
opp := e.TargetInput()
|
||||||
if carrier == nil {
|
if carrier == nil {
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
damage := e.calculateDamage()
|
damage := e.calculateDamage()
|
||||||
|
|
||||||
@@ -81,7 +111,7 @@ func (e *ContinuousDamage) ActionStart(attacker, defender *action.SelectSkillAct
|
|||||||
Damage: damage,
|
Damage: damage,
|
||||||
})
|
})
|
||||||
if len(e.SideEffectArgs) == 0 {
|
if len(e.SideEffectArgs) == 0 {
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
// 额外效果
|
// 额外效果
|
||||||
carrier.Damage(source, &info.DamageZone{
|
carrier.Damage(source, &info.DamageZone{
|
||||||
@@ -89,12 +119,11 @@ func (e *ContinuousDamage) ActionStart(attacker, defender *action.SelectSkillAct
|
|||||||
Damage: damage,
|
Damage: damage,
|
||||||
})
|
})
|
||||||
if opp == nil || opp.CurPet[0].GetHP().IntPart() == 0 {
|
if opp == nil || opp.CurPet[0].GetHP().IntPart() == 0 {
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给对方回血(不受回血限制影响)
|
// 给对方回血(不受回血限制影响)
|
||||||
opp.Heal(carrier, nil, damage)
|
opp.Heal(carrier, nil, damage)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算伤害:最大生命值的1/8
|
// 计算伤害:最大生命值的1/8
|
||||||
@@ -131,15 +160,13 @@ func (e *ParasiticSeed) SwitchOut(in *input.Input) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 技能命中前触发寄生效果
|
// 回合开始触发寄生效果。寄生属于完整回合流程的一部分,不依赖本回合是否成功出手。
|
||||||
func (e *ParasiticSeed) ActionStartEx(attacker, defender *action.SelectSkillAction) bool {
|
func (e *ParasiticSeed) TurnStart(attacker, defender *action.SelectSkillAction) {
|
||||||
carrier := e.CarrierInput()
|
carrier := e.CarrierInput()
|
||||||
source := e.SourceInput()
|
source := e.SourceInput()
|
||||||
opp := e.TargetInput()
|
|
||||||
if carrier == nil {
|
if carrier == nil {
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
// 过滤特定类型单位(假设1是植物类型,使用枚举替代魔法数字)
|
|
||||||
|
|
||||||
damage := alpacadecimal.NewFromInt(int64(carrier.CurPet[0].Info.MaxHp)).
|
damage := alpacadecimal.NewFromInt(int64(carrier.CurPet[0].Info.MaxHp)).
|
||||||
Div(alpacadecimal.NewFromInt(8))
|
Div(alpacadecimal.NewFromInt(8))
|
||||||
@@ -149,13 +176,12 @@ func (e *ParasiticSeed) ActionStartEx(attacker, defender *action.SelectSkillActi
|
|||||||
Type: info.DamageType.True,
|
Type: info.DamageType.True,
|
||||||
Damage: damage,
|
Damage: damage,
|
||||||
})
|
})
|
||||||
if opp == nil || opp.CurPet[0].GetHP().IntPart() == 0 {
|
if source == nil || source.CurPet[0] == nil || source.CurPet[0].GetHP().IntPart() == 0 {
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给对方回血(不受回血限制影响)
|
// 给寄生种子的施放者回血(不受回血限制影响)
|
||||||
opp.Heal(carrier, nil, damage)
|
source.Heal(carrier, nil, damage)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Flammable struct {
|
type Flammable struct {
|
||||||
@@ -271,7 +297,6 @@ func init() {
|
|||||||
// 批量注册不能行动的状态
|
// 批量注册不能行动的状态
|
||||||
nonActingStatuses := []info.EnumPetStatus{
|
nonActingStatuses := []info.EnumPetStatus{
|
||||||
info.PetStatus.Paralysis, // 麻痹
|
info.PetStatus.Paralysis, // 麻痹
|
||||||
info.PetStatus.Tired, // 疲惫
|
|
||||||
info.PetStatus.Fear, // 害怕
|
info.PetStatus.Fear, // 害怕
|
||||||
info.PetStatus.Petrified, // 石化
|
info.PetStatus.Petrified, // 石化
|
||||||
}
|
}
|
||||||
@@ -281,6 +306,10 @@ func init() {
|
|||||||
input.InitEffect(input.EffectType.Status, int(status), effect)
|
input.InitEffect(input.EffectType.Status, int(status), effect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tired := &StatusTired{}
|
||||||
|
tired.Status = info.PetStatus.Tired
|
||||||
|
input.InitEffect(input.EffectType.Status, int(info.PetStatus.Tired), tired)
|
||||||
|
|
||||||
// 注册睡眠状态(使用枚举常量替代硬编码8)
|
// 注册睡眠状态(使用枚举常量替代硬编码8)
|
||||||
input.InitEffect(input.EffectType.Status, int(info.PetStatus.Sleep), &StatusSleep{})
|
input.InitEffect(input.EffectType.Status, int(info.PetStatus.Sleep), &StatusSleep{})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ func (e *Effect201) OnSkill() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !carrier.IsMultiInputBattle() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
divisorIndex := len(args) - 1
|
divisorIndex := len(args) - 1
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
divisorIndex = 1
|
divisorIndex = 1
|
||||||
@@ -110,10 +114,6 @@ func (e *Effect201) OnSkill() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !carrier.IsMultiInputBattle() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
team := carrier.Team
|
team := carrier.Team
|
||||||
if len(team) == 0 {
|
if len(team) == 0 {
|
||||||
team = []*input.Input{carrier}
|
team = []*input.Input{carrier}
|
||||||
|
|||||||
@@ -42,6 +42,27 @@ func TestEffect201HealAllIgnoredInSingleInputBattle(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEffect201SingleTargetIgnoredInSingleInputBattle(t *testing.T) {
|
||||||
|
carrier := newEffect201TestInput(40, 100)
|
||||||
|
opponent := newEffect201TestInput(60, 100)
|
||||||
|
carrier.Team = []*input.Input{carrier}
|
||||||
|
carrier.OppTeam = []*input.Input{opponent}
|
||||||
|
|
||||||
|
eff := &Effect201{}
|
||||||
|
eff.SetArgs(carrier, 2)
|
||||||
|
eff.EffectNode.EffectContextHolder.Ctx = input.Ctx{
|
||||||
|
LegacySides: input.LegacySides{Our: carrier, Opp: opponent},
|
||||||
|
EffectBinding: input.EffectBinding{Carrier: carrier, Source: carrier},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !eff.OnSkill() {
|
||||||
|
t.Fatalf("expected effect to finish successfully")
|
||||||
|
}
|
||||||
|
if got := carrier.CurrentPet().Info.Hp; got != 40 {
|
||||||
|
t.Fatalf("expected single-input single-target heal to be ignored, got hp %d", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEffect201HealAllWorksInMultiInputBattle(t *testing.T) {
|
func TestEffect201HealAllWorksInMultiInputBattle(t *testing.T) {
|
||||||
carrier := newEffect201TestInput(40, 100)
|
carrier := newEffect201TestInput(40, 100)
|
||||||
ally := newEffect201TestInput(10, 80)
|
ally := newEffect201TestInput(10, 80)
|
||||||
|
|||||||
@@ -207,9 +207,15 @@ func registerSelfDamageOnSkillEffects() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{
|
forEachEnemyTargetBySkill(e.Ctx().Our, e.Ctx().Opp, e.Ctx().SkillEntity, func(target *input.Input) bool {
|
||||||
Type: info.DamageType.Fixed,
|
if target == nil || target.CurrentPet() == nil {
|
||||||
Damage: opponentDamage,
|
return true
|
||||||
|
}
|
||||||
|
target.Damage(e.Ctx().Our, &info.DamageZone{
|
||||||
|
Type: info.DamageType.Fixed,
|
||||||
|
Damage: opponentDamage,
|
||||||
|
})
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
@@ -241,9 +247,15 @@ func registerSelfDamageSkillUseEffects() {
|
|||||||
Type: info.DamageType.Fixed,
|
Type: info.DamageType.Fixed,
|
||||||
Damage: damage,
|
Damage: damage,
|
||||||
})
|
})
|
||||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{
|
forEachEnemyTargetBySkill(e.Ctx().Our, e.Ctx().Opp, e.Ctx().SkillEntity, func(target *input.Input) bool {
|
||||||
Type: info.DamageType.Fixed,
|
if target == nil || target.CurrentPet() == nil {
|
||||||
Damage: damage,
|
return true
|
||||||
|
}
|
||||||
|
target.Damage(e.Ctx().Our, &info.DamageZone{
|
||||||
|
Type: info.DamageType.Fixed,
|
||||||
|
Damage: damage,
|
||||||
|
})
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
@@ -253,9 +265,23 @@ func registerSelfDamageSkillUseEffects() {
|
|||||||
Damage: alpacadecimal.NewFromInt(int64(e.Ctx().Our.CurPet[0].Info.MaxHp)),
|
Damage: alpacadecimal.NewFromInt(int64(e.Ctx().Our.CurPet[0].Info.MaxHp)),
|
||||||
})
|
})
|
||||||
damage := int64(grand.N(250, 300))
|
damage := int64(grand.N(250, 300))
|
||||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{
|
forEachEnemyTargetBySkill(e.Ctx().Our, e.Ctx().Opp, e.Ctx().SkillEntity, func(target *input.Input) bool {
|
||||||
Type: info.DamageType.Fixed,
|
if target == nil {
|
||||||
Damage: alpacadecimal.Min(alpacadecimal.NewFromInt(damage), e.Ctx().Opp.CurPet[0].GetHP().Sub(alpacadecimal.NewFromInt(1))),
|
return true
|
||||||
|
}
|
||||||
|
targetPet := target.CurrentPet()
|
||||||
|
if targetPet == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
remainHP := targetPet.GetHP().Sub(alpacadecimal.NewFromInt(1))
|
||||||
|
if remainHP.Cmp(alpacadecimal.Zero) <= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
target.Damage(e.Ctx().Our, &info.DamageZone{
|
||||||
|
Type: info.DamageType.Fixed,
|
||||||
|
Damage: alpacadecimal.Min(alpacadecimal.NewFromInt(damage), remainHP),
|
||||||
|
})
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
@@ -280,15 +306,25 @@ func registerSelfDamageSkillUseEffects() {
|
|||||||
randomDamage = grand.N(minDamage, maxDamage)
|
randomDamage = grand.N(minDamage, maxDamage)
|
||||||
}
|
}
|
||||||
|
|
||||||
remainHP := e.Ctx().Opp.CurPet[0].GetHP().Sub(alpacadecimal.NewFromInt(1))
|
forEachEnemyTargetBySkill(e.Ctx().Our, e.Ctx().Opp, e.Ctx().SkillEntity, func(target *input.Input) bool {
|
||||||
if remainHP.Cmp(alpacadecimal.Zero) <= 0 {
|
if target == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
targetPet := target.CurrentPet()
|
||||||
|
if targetPet == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
remainHP := targetPet.GetHP().Sub(alpacadecimal.NewFromInt(1))
|
||||||
|
if remainHP.Cmp(alpacadecimal.Zero) <= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
damage := alpacadecimal.Min(alpacadecimal.NewFromInt(int64(randomDamage)), remainHP)
|
damage := alpacadecimal.Min(alpacadecimal.NewFromInt(int64(randomDamage)), remainHP)
|
||||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{
|
target.Damage(e.Ctx().Our, &info.DamageZone{
|
||||||
Type: info.DamageType.Fixed,
|
Type: info.DamageType.Fixed,
|
||||||
Damage: damage,
|
Damage: damage,
|
||||||
|
})
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
@@ -297,11 +333,17 @@ func registerSelfDamageSkillUseEffects() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
applyAllPropDown(e.Ctx().Our, e.Ctx().Opp, int8(e.Args()[0].IntPart()))
|
forEachEnemyTargetBySkill(e.Ctx().Our, e.Ctx().Opp, e.Ctx().SkillEntity, func(target *input.Input) bool {
|
||||||
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1380, int(e.Args()[1].IntPart()), int(e.Args()[2].IntPart()))
|
if target == nil || target.CurrentPet() == nil {
|
||||||
if sub != nil {
|
return true
|
||||||
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
|
}
|
||||||
}
|
applyAllPropDown(e.Ctx().Our, target, int8(e.Args()[0].IntPart()))
|
||||||
|
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1380, int(e.Args()[1].IntPart()), int(e.Args()[2].IntPart()))
|
||||||
|
if sub != nil {
|
||||||
|
target.AddEffect(e.Ctx().Our, sub)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
e.Ctx().Our.Damage(e.Ctx().Our, &info.DamageZone{
|
e.Ctx().Our.Damage(e.Ctx().Our, &info.DamageZone{
|
||||||
Type: info.DamageType.Fixed,
|
Type: info.DamageType.Fixed,
|
||||||
Damage: e.Ctx().Our.CurPet[0].GetHP(),
|
Damage: e.Ctx().Our.CurPet[0].GetHP(),
|
||||||
|
|||||||
34
logic/service/fight/effect/skill_target_helper.go
Normal file
34
logic/service/fight/effect/skill_target_helper.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package effect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/logic/service/fight/info"
|
||||||
|
"blazing/logic/service/fight/input"
|
||||||
|
)
|
||||||
|
|
||||||
|
// forEachEnemyTargetBySkill 在普通情况下对单个目标生效;
|
||||||
|
// 当技能为 AtkType=3(仅自己)且当前目标仍在己方时,改为遍历敌方全部站位。
|
||||||
|
func forEachEnemyTargetBySkill(carrier, target *input.Input, skill *info.SkillEntity, fn func(*input.Input) bool) {
|
||||||
|
if fn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if carrier == nil {
|
||||||
|
if target != nil {
|
||||||
|
fn(target)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if skill == nil || skill.XML.AtkType != 3 || !isSameSideTarget(carrier, target) {
|
||||||
|
if target != nil {
|
||||||
|
fn(target)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, opponent := range carrier.OpponentSlots() {
|
||||||
|
if opponent == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !fn(opponent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,12 +7,21 @@ import "blazing/modules/player/model"
|
|||||||
// 0=normal end 1=player lost/offline 2=overtime 3=draw 4=system error 5=npc escape.
|
// 0=normal end 1=player lost/offline 2=overtime 3=draw 4=system error 5=npc escape.
|
||||||
func buildFightOverPayload(over model.FightOverInfo) *model.FightOverInfo {
|
func buildFightOverPayload(over model.FightOverInfo) *model.FightOverInfo {
|
||||||
payload := over
|
payload := over
|
||||||
payload.Reason = mapFightOverReasonFor2506(over.Reason)
|
payload.Reason = model.EnumBattleOverReason(mapUnifiedFightOverReason(over.Reason))
|
||||||
return &payload
|
return &payload
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapFightOverReasonFor2506(reason model.EnumBattleOverReason) model.EnumBattleOverReason {
|
func normalizeFightOverReason(reason model.EnumBattleOverReason) model.EnumBattleOverReason {
|
||||||
switch reason {
|
if reason == model.BattleOverReason.DefaultEnd {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return reason
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapUnifiedFightOverReason(reason model.EnumBattleOverReason) uint32 {
|
||||||
|
switch normalizeFightOverReason(reason) {
|
||||||
|
case 0, model.BattleOverReason.Cacthok:
|
||||||
|
return 0
|
||||||
case model.BattleOverReason.PlayerOffline:
|
case model.BattleOverReason.PlayerOffline:
|
||||||
return 1
|
return 1
|
||||||
case model.BattleOverReason.PlayerOVerTime:
|
case model.BattleOverReason.PlayerOVerTime:
|
||||||
@@ -20,12 +29,12 @@ func mapFightOverReasonFor2506(reason model.EnumBattleOverReason) model.EnumBatt
|
|||||||
case model.BattleOverReason.NOTwind:
|
case model.BattleOverReason.NOTwind:
|
||||||
return 3
|
return 3
|
||||||
case model.BattleOverReason.PlayerEscape:
|
case model.BattleOverReason.PlayerEscape:
|
||||||
// Player-initiated escape is handled by 2410 on the flash side; 2506 should
|
return 5
|
||||||
// still land in a non-error bucket instead of "system error".
|
|
||||||
return 1
|
|
||||||
case model.BattleOverReason.Cacthok, model.BattleOverReason.DefaultEnd:
|
|
||||||
return 0
|
|
||||||
default:
|
default:
|
||||||
return 4
|
return 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mapFightOverReasonFor2506(reason model.EnumBattleOverReason) model.EnumBattleOverReason {
|
||||||
|
return model.EnumBattleOverReason(mapUnifiedFightOverReason(reason))
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"blazing/logic/service/fight/action"
|
"blazing/logic/service/fight/action"
|
||||||
"blazing/logic/service/fight/info"
|
"blazing/logic/service/fight/info"
|
||||||
"blazing/logic/service/fight/input"
|
"blazing/logic/service/fight/input"
|
||||||
|
_ "blazing/logic/service/fight/itemover"
|
||||||
|
_ "blazing/logic/service/fight/rule"
|
||||||
"blazing/modules/player/model"
|
"blazing/modules/player/model"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
@@ -133,7 +135,20 @@ func (f *FightC) getSkillParticipants(skillAction *action.SelectSkillAction) (*i
|
|||||||
if skillAction == nil {
|
if skillAction == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return f.GetInputByAction(skillAction, false), f.GetInputByAction(skillAction, true)
|
attacker := f.GetInputByAction(skillAction, false)
|
||||||
|
defender := f.GetInputByAction(skillAction, true)
|
||||||
|
if attacker != nil && defender == attacker && shouldResolveOpponentAsTarget(skillAction.SkillEntity) {
|
||||||
|
if opponent, _ := attacker.OpponentSlotAtOrNextLiving(0); opponent != nil {
|
||||||
|
defender = opponent
|
||||||
|
} else if opponent := f.roundOpponentInput(attacker); opponent != nil {
|
||||||
|
defender = opponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attacker, defender
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldResolveOpponentAsTarget(skill *info.SkillEntity) bool {
|
||||||
|
return skill != nil && skill.XML.AtkType == 3
|
||||||
}
|
}
|
||||||
|
|
||||||
// setEffectSkillContext 统一设置技能阶段 effect 上下文。
|
// setEffectSkillContext 统一设置技能阶段 effect 上下文。
|
||||||
@@ -184,17 +199,46 @@ func (f *FightC) collectAttackValues(inputs []*input.Input) []model.AttackValue
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
attackValue := *fighter.AttackValue
|
attackValue := *fighter.AttackValue
|
||||||
|
if attackValue.SkillID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
attackValue.ActorIndex = uint32(actorIndex)
|
attackValue.ActorIndex = uint32(actorIndex)
|
||||||
values = append(values, attackValue)
|
values = append(values, attackValue)
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FightC) buildAttackValueForBroadcast(fighter *input.Input, fallbackActorIndex int) model.AttackValue {
|
||||||
|
if fighter == nil {
|
||||||
|
return model.AttackValue{}
|
||||||
|
}
|
||||||
|
if fighter.AttackValue == nil {
|
||||||
|
empty := info.NewAttackValue(fighter.UserID)
|
||||||
|
fighter.AttackValue = empty
|
||||||
|
}
|
||||||
|
attackValue := *fighter.AttackValue
|
||||||
|
attackValue.ActorIndex = uint32(fallbackActorIndex)
|
||||||
|
if attackValue.UserID == 0 && fighter.Player != nil && fighter.Player.GetInfo() != nil {
|
||||||
|
attackValue.UserID = fighter.Player.GetInfo().UserID
|
||||||
|
}
|
||||||
|
if currentPet := fighter.CurrentPet(); currentPet != nil {
|
||||||
|
// 始终以当前战斗态回填血量和技能 PP,避免广播沿用旧缓存。
|
||||||
|
attackValue.RemainHp = int32(currentPet.Info.Hp)
|
||||||
|
attackValue.MaxHp = currentPet.Info.MaxHp
|
||||||
|
attackValue.SkillList = append(attackValue.SkillList[:0], currentPet.Info.SkillList...)
|
||||||
|
attackValue.SkillListLen = uint32(len(attackValue.SkillList))
|
||||||
|
}
|
||||||
|
return attackValue
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FightC) buildNoteUseSkillOutboundInfo() info.NoteUseSkillOutboundInfo {
|
func (f *FightC) buildNoteUseSkillOutboundInfo() info.NoteUseSkillOutboundInfo {
|
||||||
result := info.NoteUseSkillOutboundInfo{}
|
result := info.NoteUseSkillOutboundInfo{}
|
||||||
result.FirstAttackInfo = append(result.FirstAttackInfo, f.collectAttackValues(f.Our)...)
|
if f.First != nil {
|
||||||
result.SecondAttackInfo = append(result.SecondAttackInfo, f.collectAttackValues(f.Opp)...)
|
result.FirstAttackInfo = f.buildAttackValueForBroadcast(f.First, f.First.TeamSlotIndex())
|
||||||
|
}
|
||||||
|
if f.Second != nil {
|
||||||
|
result.SecondAttackInfo = f.buildAttackValueForBroadcast(f.Second, f.Second.TeamSlotIndex())
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +254,15 @@ func (f *FightC) roundOpponentInput(attacker *input.Input) *input.Input {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldSkipSecondAction(first, second *input.Input) bool {
|
||||||
|
if first == nil || second == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
firstPet := first.CurrentPet()
|
||||||
|
secondPet := second.CurrentPet()
|
||||||
|
return firstPet == nil || firstPet.Info.Hp <= 0 || secondPet == nil || secondPet.Info.Hp <= 0
|
||||||
|
}
|
||||||
|
|
||||||
// enterturn 处理战斗回合逻辑
|
// enterturn 处理战斗回合逻辑
|
||||||
// 回合有先手方和后手方,同时有攻击方和被攻击方
|
// 回合有先手方和后手方,同时有攻击方和被攻击方
|
||||||
func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) {
|
func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) {
|
||||||
@@ -289,25 +342,32 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if firstAttack == nil && secondAttack == nil {
|
skipActionStage := firstAttack == nil && secondAttack == nil
|
||||||
firstAttack, secondAttack = secondAttack, firstAttack //互换先手权
|
|
||||||
f.First, f.Second = f.Second, f.First
|
|
||||||
}
|
|
||||||
var attacker, defender *input.Input
|
var attacker, defender *input.Input
|
||||||
f.TrueFirst = f.First
|
f.TrueFirst = f.First
|
||||||
//开始回合操作
|
//开始回合操作。若双方本回合都未出手,则只走完整回合流程,不进入动作阶段。
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; !skipActionStage && i < 2; i++ {
|
||||||
var originalSkill *info.SkillEntity //原始技能
|
var originalSkill *info.SkillEntity //原始技能
|
||||||
var currentSkill *info.SkillEntity //当前技能
|
var currentSkill *info.SkillEntity //当前技能
|
||||||
var currentAction *action.SelectSkillAction
|
var currentAction *action.SelectSkillAction
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
currentAction = firstAttack
|
currentAction = firstAttack
|
||||||
|
if currentAction == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
attacker, defender = f.getSkillParticipants(firstAttack)
|
attacker, defender = f.getSkillParticipants(firstAttack)
|
||||||
originalSkill = f.copySkill(firstAttack)
|
originalSkill = f.copySkill(firstAttack)
|
||||||
//先手阶段,先修复后手效果
|
//先手阶段,先修复后手效果
|
||||||
f.Second.RecoverEffect()
|
f.Second.RecoverEffect()
|
||||||
} else {
|
} else {
|
||||||
currentAction = secondAttack
|
currentAction = secondAttack
|
||||||
|
if currentAction == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if shouldSkipSecondAction(f.First, f.Second) {
|
||||||
|
secondAttack = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
attacker, defender = f.getSkillParticipants(secondAttack)
|
attacker, defender = f.getSkillParticipants(secondAttack)
|
||||||
originalSkill = f.copySkill(secondAttack)
|
originalSkill = f.copySkill(secondAttack)
|
||||||
//取消后手历史效果
|
//取消后手历史效果
|
||||||
@@ -462,11 +522,13 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
// })
|
// })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
if attackValueResult.FirstAttackInfo.UserID != 0 || attackValueResult.SecondAttackInfo.UserID != 0 {
|
||||||
if !f.LegacyGroupProtocol {
|
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||||
f.sendFightPacket(p, fightPacketSkillResult, &attackValueResult)
|
if !f.LegacyGroupProtocol {
|
||||||
}
|
f.sendFightPacket(p, fightPacketSkillResult, &attackValueResult)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
f.Broadcast(func(fighter *input.Input) {
|
f.Broadcast(func(fighter *input.Input) {
|
||||||
fighter.CanChange = 0
|
fighter.CanChange = 0
|
||||||
})
|
})
|
||||||
@@ -495,9 +557,9 @@ func (f *FightC) TURNOVER(cur *input.Input) {
|
|||||||
if f.IsWin(f.GetInputByPlayer(cur.Player, true)) { //然后检查是否战斗结束
|
if f.IsWin(f.GetInputByPlayer(cur.Player, true)) { //然后检查是否战斗结束
|
||||||
|
|
||||||
f.FightOverInfo.WinnerId = f.GetInputByPlayer(cur.Player, true).UserID
|
f.FightOverInfo.WinnerId = f.GetInputByPlayer(cur.Player, true).UserID
|
||||||
f.FightOverInfo.Reason = model.BattleOverReason.DefaultEnd
|
f.FightOverInfo.Reason = normalizeFightOverReason(model.BattleOverReason.DefaultEnd)
|
||||||
f.WinnerId = f.FightOverInfo.WinnerId
|
f.WinnerId = f.FightOverInfo.WinnerId
|
||||||
f.Reason = model.BattleOverReason.DefaultEnd
|
f.Reason = f.FightOverInfo.Reason
|
||||||
|
|
||||||
f.closefight = true
|
f.closefight = true
|
||||||
// break
|
// break
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ import (
|
|||||||
"blazing/modules/player/model"
|
"blazing/modules/player/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// <!--
|
||||||
|
// GBTL:
|
||||||
|
// 1. AtkNum:本技能同时攻击数量, 默认:1(不能为0)
|
||||||
|
// 2. AtkType:攻击类型: 0:所有人, 1:仅己方, 2:仅对方, 3:仅自己, 默认:2
|
||||||
|
// -->
|
||||||
const (
|
const (
|
||||||
groupCmdReadyToFight uint32 = 7555
|
groupCmdReadyToFight uint32 = 7555
|
||||||
groupCmdReadyFightFinish uint32 = 7556
|
groupCmdReadyFightFinish uint32 = 7556
|
||||||
@@ -426,38 +431,15 @@ func (f *FightC) buildLegacyGroupOverInfo(over *model.FightOverInfo) *legacyGrou
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mapLegacyGroupFightOverReason(reason model.EnumBattleOverReason) uint32 {
|
func mapLegacyGroupFightOverReason(reason model.EnumBattleOverReason) uint32 {
|
||||||
switch reason {
|
return mapUnifiedFightOverReason(reason)
|
||||||
case model.BattleOverReason.PlayerOffline:
|
|
||||||
return 2
|
|
||||||
case model.BattleOverReason.PlayerOVerTime:
|
|
||||||
return 3
|
|
||||||
case model.BattleOverReason.NOTwind:
|
|
||||||
return 4
|
|
||||||
case model.BattleOverReason.DefaultEnd:
|
|
||||||
return 1
|
|
||||||
case model.BattleOverReason.PlayerEscape:
|
|
||||||
return 6
|
|
||||||
default:
|
|
||||||
return 5
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveLegacyGroupFightOverReason(over *model.FightOverInfo) uint32 {
|
func resolveLegacyGroupFightOverReason(over *model.FightOverInfo) uint32 {
|
||||||
if over == nil {
|
if over == nil {
|
||||||
return 5
|
return mapUnifiedFightOverReason(0)
|
||||||
}
|
|
||||||
switch over.Reason {
|
|
||||||
case model.BattleOverReason.PlayerOffline:
|
|
||||||
return 2
|
|
||||||
case model.BattleOverReason.PlayerOVerTime:
|
|
||||||
return 3
|
|
||||||
case model.BattleOverReason.PlayerEscape:
|
|
||||||
return 6
|
|
||||||
case model.BattleOverReason.NOTwind:
|
|
||||||
return 4
|
|
||||||
}
|
}
|
||||||
if over.WinnerId != 0 {
|
if over.WinnerId != 0 {
|
||||||
return 1
|
return mapUnifiedFightOverReason(0)
|
||||||
}
|
}
|
||||||
return mapLegacyGroupFightOverReason(over.Reason)
|
return mapLegacyGroupFightOverReason(over.Reason)
|
||||||
}
|
}
|
||||||
@@ -515,15 +497,23 @@ func (f *FightC) sendLegacyRoundBroadcast(firstAttack, secondAttack *action.Sele
|
|||||||
if f == nil || !f.LegacyGroupProtocol {
|
if f == nil || !f.LegacyGroupProtocol {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if firstAttack != nil {
|
if f.legacySkillExecuted(firstAttack) {
|
||||||
f.sendLegacyGroupSkillHurt(firstAttack)
|
f.sendLegacyGroupSkillHurt(firstAttack)
|
||||||
}
|
}
|
||||||
if secondAttack != nil {
|
if f.legacySkillExecuted(secondAttack) {
|
||||||
f.sendLegacyGroupSkillHurt(secondAttack)
|
f.sendLegacyGroupSkillHurt(secondAttack)
|
||||||
}
|
}
|
||||||
f.sendLegacyGroupBoutDone()
|
f.sendLegacyGroupBoutDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FightC) legacySkillExecuted(skillAction *action.SelectSkillAction) bool {
|
||||||
|
if f == nil || skillAction == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
attacker := f.GetInputByAction(skillAction, false)
|
||||||
|
return attacker != nil && attacker.AttackValue != nil && attacker.AttackValue.SkillID != 0
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FightC) sendLegacyGroupSkillHurt(skillAction *action.SelectSkillAction) {
|
func (f *FightC) sendLegacyGroupSkillHurt(skillAction *action.SelectSkillAction) {
|
||||||
if f == nil || !f.LegacyGroupProtocol || skillAction == nil {
|
if f == nil || !f.LegacyGroupProtocol || skillAction == nil {
|
||||||
return
|
return
|
||||||
@@ -603,10 +593,10 @@ func (f *FightC) buildLegacyGroupSkillAttackInfo(skillAction *action.SelectSkill
|
|||||||
if attackValue == nil {
|
if attackValue == nil {
|
||||||
attackValue = info.NewAttackValue(self.UserID)
|
attackValue = info.NewAttackValue(self.UserID)
|
||||||
}
|
}
|
||||||
if skillAction != nil && skillAction.SkillEntity != nil {
|
if attackValue.SkillID != 0 {
|
||||||
result.MoveID = uint32(skillAction.SkillEntity.XML.ID)
|
|
||||||
} else {
|
|
||||||
result.MoveID = attackValue.SkillID
|
result.MoveID = attackValue.SkillID
|
||||||
|
} else if skillAction != nil && skillAction.SkillEntity != nil {
|
||||||
|
result.MoveID = uint32(skillAction.SkillEntity.XML.ID)
|
||||||
}
|
}
|
||||||
result.IsCrit = attackValue.IsCritical
|
result.IsCrit = attackValue.IsCritical
|
||||||
result.EffectName = attackValue.State
|
result.EffectName = attackValue.State
|
||||||
|
|||||||
61
logic/service/fight/group_legacy_test.go
Normal file
61
logic/service/fight/group_legacy_test.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package fight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"blazing/logic/service/fight/action"
|
||||||
|
fightinfo "blazing/logic/service/fight/info"
|
||||||
|
"blazing/logic/service/fight/input"
|
||||||
|
"blazing/modules/player/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSendLegacyRoundBroadcastSkipsUnexecutedAction(t *testing.T) {
|
||||||
|
ourPlayer := &stubPlayer{info: model.PlayerInfo{UserID: 1001}}
|
||||||
|
oppPlayer := &stubPlayer{info: model.PlayerInfo{UserID: 2002}}
|
||||||
|
|
||||||
|
our := input.NewInput(nil, ourPlayer)
|
||||||
|
our.InitAttackValue()
|
||||||
|
our.AttackValue.SkillID = 111
|
||||||
|
our.SetCurPetAt(0, fightinfo.CreateBattlePetEntity(model.PetInfo{
|
||||||
|
ID: 11,
|
||||||
|
Hp: 80,
|
||||||
|
MaxHp: 100,
|
||||||
|
CatchTime: 101,
|
||||||
|
}))
|
||||||
|
|
||||||
|
opp := input.NewInput(nil, oppPlayer)
|
||||||
|
opp.InitAttackValue()
|
||||||
|
opp.SetCurPetAt(0, fightinfo.CreateBattlePetEntity(model.PetInfo{
|
||||||
|
ID: 22,
|
||||||
|
Hp: 0,
|
||||||
|
MaxHp: 100,
|
||||||
|
CatchTime: 202,
|
||||||
|
}))
|
||||||
|
|
||||||
|
fc := &FightC{
|
||||||
|
Our: []*input.Input{our},
|
||||||
|
Opp: []*input.Input{opp},
|
||||||
|
LegacyGroupProtocol: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
firstAttack := &action.SelectSkillAction{
|
||||||
|
BaseAction: action.BaseAction{PlayerID: ourPlayer.info.UserID, ActorIndex: 0, TargetIndex: 0},
|
||||||
|
}
|
||||||
|
secondAttack := &action.SelectSkillAction{
|
||||||
|
BaseAction: action.BaseAction{PlayerID: oppPlayer.info.UserID, ActorIndex: 0, TargetIndex: 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
fc.sendLegacyRoundBroadcast(firstAttack, secondAttack)
|
||||||
|
|
||||||
|
for _, player := range []*stubPlayer{ourPlayer, oppPlayer} {
|
||||||
|
if len(player.sentCmds) != 2 {
|
||||||
|
t.Fatalf("expected one skill packet plus bout done, got %v", player.sentCmds)
|
||||||
|
}
|
||||||
|
if player.sentCmds[0] != groupCmdSkillHurt {
|
||||||
|
t.Fatalf("expected first packet to be skill hurt, got %d", player.sentCmds[0])
|
||||||
|
}
|
||||||
|
if player.sentCmds[1] != groupCmdBoutDone {
|
||||||
|
t.Fatalf("expected second packet to be bout done, got %d", player.sentCmds[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -196,10 +196,8 @@ type PropDict struct {
|
|||||||
|
|
||||||
// NoteUseSkillOutboundInfo 战斗技能使用通知的出站信息结构体
|
// NoteUseSkillOutboundInfo 战斗技能使用通知的出站信息结构体
|
||||||
type NoteUseSkillOutboundInfo struct {
|
type NoteUseSkillOutboundInfo struct {
|
||||||
FirstAttackInfoLen uint32 `struc:"sizeof=FirstAttackInfo"`
|
FirstAttackInfo model.AttackValue // 本轮先手方精灵在释放技能结束后的状态
|
||||||
FirstAttackInfo []model.AttackValue // 本轮先手方精灵在释放技能结束后的状态
|
SecondAttackInfo model.AttackValue // 本轮后手方精灵在释放技能结束后的状态
|
||||||
SecondAttackInfoLen uint32 `struc:"sizeof=SecondAttackInfo"`
|
|
||||||
SecondAttackInfo []model.AttackValue // 本轮后手方精灵在释放技能结束后的状态
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FightStartOutboundInfo struct {
|
type FightStartOutboundInfo struct {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func (f *FightC) battleLoop() {
|
|||||||
if player := f.primaryOppPlayer(); player != nil {
|
if player := f.primaryOppPlayer(); player != nil {
|
||||||
f.WinnerId = player.GetInfo().UserID
|
f.WinnerId = player.GetInfo().UserID
|
||||||
}
|
}
|
||||||
f.Reason = model.BattleOverReason.DefaultEnd
|
f.Reason = normalizeFightOverReason(model.BattleOverReason.DefaultEnd)
|
||||||
f.FightOverInfo.WinnerId = f.WinnerId
|
f.FightOverInfo.WinnerId = f.WinnerId
|
||||||
f.FightOverInfo.Reason = f.Reason
|
f.FightOverInfo.Reason = f.Reason
|
||||||
f.closefight = true
|
f.closefight = true
|
||||||
@@ -86,7 +86,7 @@ func (f *FightC) battleLoop() {
|
|||||||
if player := f.primaryOurPlayer(); player != nil {
|
if player := f.primaryOurPlayer(); player != nil {
|
||||||
f.WinnerId = player.GetInfo().UserID
|
f.WinnerId = player.GetInfo().UserID
|
||||||
}
|
}
|
||||||
f.Reason = model.BattleOverReason.DefaultEnd
|
f.Reason = normalizeFightOverReason(model.BattleOverReason.DefaultEnd)
|
||||||
f.FightOverInfo.WinnerId = f.WinnerId
|
f.FightOverInfo.WinnerId = f.WinnerId
|
||||||
f.FightOverInfo.Reason = f.Reason
|
f.FightOverInfo.Reason = f.Reason
|
||||||
f.closefight = true
|
f.closefight = true
|
||||||
@@ -586,6 +586,7 @@ func (f *FightC) handleItemAction(a *action.UseItemAction) {
|
|||||||
if r <= 0 {
|
if r <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
source.Player.(*player.Player).Service.Item.UPDATE(a.ItemID, -1)
|
source.Player.(*player.Player).Service.Item.UPDATE(a.ItemID, -1)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
@@ -297,9 +297,6 @@ func buildFight(opts *fightBuildOptions) (*FightC, errorcode.ErrorCode) {
|
|||||||
f.bindInputFightContext(f.Our, f.Opp)
|
f.bindInputFightContext(f.Our, f.Opp)
|
||||||
f.linkTeamViews()
|
f.linkTeamViews()
|
||||||
|
|
||||||
f.ReadyInfo.OurInfo, f.ReadyInfo.OurPetList = initfightready(f.primaryOur())
|
|
||||||
f.ReadyInfo.OpponentInfo, f.ReadyInfo.OpponentPetList = initfightready(f.primaryOpp())
|
|
||||||
|
|
||||||
loadtime := 120 * time.Second
|
loadtime := 120 * time.Second
|
||||||
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
|
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
|
||||||
if opp := f.primaryOpp(); opp != nil {
|
if opp := f.primaryOpp(); opp != nil {
|
||||||
@@ -313,6 +310,9 @@ func buildFight(opts *fightBuildOptions) (*FightC, errorcode.ErrorCode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.ReadyInfo.OurInfo, f.ReadyInfo.OurPetList = initfightready(f.primaryOur())
|
||||||
|
f.ReadyInfo.OpponentInfo, f.ReadyInfo.OpponentPetList = initfightready(f.primaryOpp())
|
||||||
f.FightStartOutboundInfo = f.buildFightStartInfo()
|
f.FightStartOutboundInfo = f.buildFightStartInfo()
|
||||||
|
|
||||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type stubPlayer struct {
|
type stubPlayer struct {
|
||||||
info model.PlayerInfo
|
info model.PlayerInfo
|
||||||
|
sentCmds []uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*stubPlayer) ApplyPetDisplayInfo(*spaceinfo.SimpleInfo) {}
|
func (*stubPlayer) ApplyPetDisplayInfo(*spaceinfo.SimpleInfo) {}
|
||||||
@@ -26,7 +27,7 @@ func (*stubPlayer) SetFightC(common.FightI) {}
|
|||||||
func (*stubPlayer) QuitFight() {}
|
func (*stubPlayer) QuitFight() {}
|
||||||
func (*stubPlayer) MessWin(bool) {}
|
func (*stubPlayer) MessWin(bool) {}
|
||||||
func (*stubPlayer) CanFight() errorcode.ErrorCode { return 0 }
|
func (*stubPlayer) CanFight() errorcode.ErrorCode { return 0 }
|
||||||
func (*stubPlayer) SendPackCmd(uint32, any) {}
|
func (p *stubPlayer) SendPackCmd(cmd uint32, _ any) { p.sentCmds = append(p.sentCmds, cmd) }
|
||||||
func (*stubPlayer) GetPetInfo(uint32) []model.PetInfo { return nil }
|
func (*stubPlayer) GetPetInfo(uint32) []model.PetInfo { return nil }
|
||||||
|
|
||||||
func TestFightActionEnvelopeEncodedTargetIndex(t *testing.T) {
|
func TestFightActionEnvelopeEncodedTargetIndex(t *testing.T) {
|
||||||
@@ -111,3 +112,70 @@ func TestBuildFightStateStartEnvelope(t *testing.T) {
|
|||||||
t.Fatalf("unexpected right fighter snapshot: %+v", envelope.Right[0])
|
t.Fatalf("unexpected right fighter snapshot: %+v", envelope.Right[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildNoteUseSkillOutboundInfoUsesActionOrder(t *testing.T) {
|
||||||
|
ourPlayer := &stubPlayer{info: model.PlayerInfo{UserID: 1001}}
|
||||||
|
oppPlayer := &stubPlayer{info: model.PlayerInfo{UserID: 2002}}
|
||||||
|
|
||||||
|
our := input.NewInput(nil, ourPlayer)
|
||||||
|
our.InitAttackValue()
|
||||||
|
our.AttackValue.SkillID = 111
|
||||||
|
our.AttackValue.RemainHp = 80
|
||||||
|
our.AttackValue.MaxHp = 100
|
||||||
|
|
||||||
|
opp := input.NewInput(nil, oppPlayer)
|
||||||
|
opp.InitAttackValue()
|
||||||
|
opp.AttackValue.SkillID = 222
|
||||||
|
opp.AttackValue.RemainHp = 70
|
||||||
|
opp.AttackValue.MaxHp = 100
|
||||||
|
|
||||||
|
fc := &FightC{
|
||||||
|
Our: []*input.Input{our},
|
||||||
|
Opp: []*input.Input{opp},
|
||||||
|
First: opp,
|
||||||
|
Second: our,
|
||||||
|
}
|
||||||
|
|
||||||
|
result := fc.buildNoteUseSkillOutboundInfo()
|
||||||
|
|
||||||
|
if result.FirstAttackInfo.UserID != 2002 || result.FirstAttackInfo.SkillID != 222 {
|
||||||
|
t.Fatalf("expected first attack info to belong to acting opponent, got %+v", result.FirstAttackInfo)
|
||||||
|
}
|
||||||
|
if result.SecondAttackInfo.UserID != 1001 || result.SecondAttackInfo.SkillID != 111 {
|
||||||
|
t.Fatalf("expected second attack info to keep the idle side placeholder, got %+v", result.SecondAttackInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildNoteUseSkillOutboundInfoUsesCurrentPetSkillPP(t *testing.T) {
|
||||||
|
player := &stubPlayer{info: model.PlayerInfo{UserID: 1001}}
|
||||||
|
|
||||||
|
fighter := input.NewInput(nil, player)
|
||||||
|
fighter.InitAttackValue()
|
||||||
|
fighter.AttackValue.SkillID = 300
|
||||||
|
fighter.AttackValue.SkillList = []model.SkillInfo{{ID: 300, PP: 1}}
|
||||||
|
fighter.AttackValue.SkillListLen = 1
|
||||||
|
fighter.AttackValue.RemainHp = 50
|
||||||
|
fighter.AttackValue.MaxHp = 100
|
||||||
|
|
||||||
|
currentPet := fightinfo.CreateBattlePetEntity(model.PetInfo{
|
||||||
|
ID: 11,
|
||||||
|
Name: "Alpha",
|
||||||
|
Level: 20,
|
||||||
|
Hp: 50,
|
||||||
|
MaxHp: 100,
|
||||||
|
CatchTime: 101,
|
||||||
|
SkillList: []model.SkillInfo{{ID: 300, PP: 0}},
|
||||||
|
})
|
||||||
|
currentPet.BindController(player.info.UserID)
|
||||||
|
fighter.SetCurPetAt(0, currentPet)
|
||||||
|
|
||||||
|
fc := &FightC{First: fighter}
|
||||||
|
|
||||||
|
result := fc.buildNoteUseSkillOutboundInfo()
|
||||||
|
if len(result.FirstAttackInfo.SkillList) != 1 {
|
||||||
|
t.Fatalf("expected one skill in broadcast snapshot, got %+v", result.FirstAttackInfo.SkillList)
|
||||||
|
}
|
||||||
|
if result.FirstAttackInfo.SkillList[0].PP != 0 {
|
||||||
|
t.Fatalf("expected broadcast PP to come from current pet state, got %+v", result.FirstAttackInfo.SkillList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ func (p *Player) IsMatch(t configmodel.Event) bool {
|
|||||||
if len(p.Info.PetList) == 0 {
|
if len(p.Info.PetList) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if p.Info.PetList[0].Hp == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
firstPetID := int32(p.Info.PetList[0].ID)
|
firstPetID := int32(p.Info.PetList[0].ID)
|
||||||
_, ok := lo.Find(t.FirstSprites, func(item int32) bool {
|
_, ok := lo.Find(t.FirstSprites, func(item int32) bool {
|
||||||
|
|||||||
49
logic/service/player/Monster_test.go
Normal file
49
logic/service/player/Monster_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package player
|
||||||
|
|
||||||
|
import (
|
||||||
|
configmodel "blazing/modules/config/model"
|
||||||
|
playermodel "blazing/modules/player/model"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsMatchFirstSpritesRequiresLivingLeadPet(t *testing.T) {
|
||||||
|
player := &Player{
|
||||||
|
baseplayer: baseplayer{
|
||||||
|
Info: &playermodel.PlayerInfo{
|
||||||
|
PetList: []playermodel.PetInfo{
|
||||||
|
{ID: 1001, Hp: 0},
|
||||||
|
{ID: 2002, Hp: 100},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
event := configmodel.Event{
|
||||||
|
FirstSprites: []int32{1001},
|
||||||
|
}
|
||||||
|
|
||||||
|
if player.IsMatch(event) {
|
||||||
|
t.Fatalf("expected dead lead pet to fail FirstSprites match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsMatchFirstSpritesAcceptsLivingLeadPet(t *testing.T) {
|
||||||
|
player := &Player{
|
||||||
|
baseplayer: baseplayer{
|
||||||
|
Info: &playermodel.PlayerInfo{
|
||||||
|
PetList: []playermodel.PetInfo{
|
||||||
|
{ID: 1001, Hp: 100},
|
||||||
|
{ID: 2002, Hp: 100},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
event := configmodel.Event{
|
||||||
|
FirstSprites: []int32{1001},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !player.IsMatch(event) {
|
||||||
|
t.Fatalf("expected living lead pet to pass FirstSprites match")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,16 @@
|
|||||||
package player
|
package player
|
||||||
|
|
||||||
|
import "blazing/modules/player/model"
|
||||||
|
|
||||||
type AI_player struct {
|
type AI_player struct {
|
||||||
baseplayer
|
baseplayer
|
||||||
|
|
||||||
CanCapture int
|
CanCapture int
|
||||||
BossScript string
|
BossScript string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *AI_player) GetPetInfo(_ uint32) []model.PetInfo {
|
||||||
|
ret := make([]model.PetInfo, 0, len(p.Info.PetList))
|
||||||
|
ret = append(ret, p.Info.PetList...)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,17 +32,19 @@ func (p *baseplayer) GetInfo() *model.PlayerInfo {
|
|||||||
return p.Info
|
return p.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ApplyPetLevelLimit(pet model.PetInfo, limitlevel uint32) model.PetInfo {
|
||||||
|
originalHP := pet.Hp
|
||||||
|
pet.CalculatePetPane(limitlevel)
|
||||||
|
pet.Hp = utils.Min(originalHP, pet.MaxHp)
|
||||||
|
return pet
|
||||||
|
}
|
||||||
|
|
||||||
func (p *baseplayer) GetPetInfo(limitlevel uint32) []model.PetInfo {
|
func (p *baseplayer) GetPetInfo(limitlevel uint32) []model.PetInfo {
|
||||||
|
|
||||||
var ret []model.PetInfo
|
ret := make([]model.PetInfo, 0, len(p.Info.PetList))
|
||||||
|
|
||||||
for _, pet := range p.Info.PetList {
|
for _, pet := range p.Info.PetList {
|
||||||
if limitlevel > 0 {
|
ret = append(ret, ApplyPetLevelLimit(pet, limitlevel))
|
||||||
pet.Level = utils.Min(pet.Level, limitlevel)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = append(ret, pet)
|
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ func (player *Player) WarehousePetList() []model.PetInfo {
|
|||||||
return make([]model.PetInfo, 0)
|
return make([]model.PetInfo, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
result := make([]model.PetInfo, 0, len(allPets))
|
result := make([]model.PetInfo, 0, len(allPets))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -25,32 +24,35 @@ func (player *Player) WarehousePetList() []model.PetInfo {
|
|||||||
|
|
||||||
// AddPetExp 添加宠物经验
|
// AddPetExp 添加宠物经验
|
||||||
func (p *Player) AddPetExp(petInfo *model.PetInfo, addExp int64) {
|
func (p *Player) AddPetExp(petInfo *model.PetInfo, addExp int64) {
|
||||||
if addExp < 0 {
|
if petInfo == nil || addExp <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addExp = utils.Min(addExp, p.Info.ExpPool)
|
panelLimit := p.CurrentMapPetLevelLimit()
|
||||||
originalLevel := petInfo.Level
|
if petInfo.Level > 100 {
|
||||||
exp := int64(petInfo.Exp) + addExp
|
currentHP := petInfo.Hp
|
||||||
p.Info.ExpPool -= addExp //减去已使用的经验
|
petInfo.Update(false)
|
||||||
gainedExp := exp //已获得的经验
|
petInfo.CalculatePetPane(panelLimit)
|
||||||
for exp >= int64(petInfo.NextLvExp) {
|
petInfo.Hp = utils.Min(currentHP, petInfo.MaxHp)
|
||||||
|
|
||||||
petInfo.Level++
|
|
||||||
|
|
||||||
exp -= int64(petInfo.LvExp)
|
|
||||||
petInfo.Update(true)
|
|
||||||
if originalLevel < 100 && petInfo.Level == 100 { //升到100了
|
|
||||||
p.Info.ExpPool += exp //减去已使用的经验
|
|
||||||
gainedExp -= exp
|
|
||||||
exp = 0
|
|
||||||
break //停止升级
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
petInfo.Exp = (exp)
|
addExp = utils.Min(addExp, p.Info.ExpPool)
|
||||||
|
if addExp <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
originalLevel := petInfo.Level
|
||||||
|
allocatedExp := addExp
|
||||||
|
p.Info.ExpPool -= addExp //减去已使用的经验
|
||||||
|
|
||||||
|
currentExp := petInfo.Exp + addExp
|
||||||
|
for currentExp >= petInfo.NextLvExp && petInfo.NextLvExp > 0 {
|
||||||
|
petInfo.Level++
|
||||||
|
petInfo.Update(true)
|
||||||
|
currentExp -= petInfo.LvExp
|
||||||
|
}
|
||||||
|
petInfo.Exp = currentExp
|
||||||
|
|
||||||
// 重新计算面板
|
// 重新计算面板
|
||||||
if originalLevel != petInfo.Level {
|
if originalLevel != petInfo.Level {
|
||||||
petInfo.CalculatePetPane(100)
|
petInfo.CalculatePetPane(panelLimit)
|
||||||
|
|
||||||
petInfo.Cure()
|
petInfo.Cure()
|
||||||
p.Info.PetMaxLevel = utils.Max(petInfo.Level, p.Info.PetMaxLevel)
|
p.Info.PetMaxLevel = utils.Max(petInfo.Level, p.Info.PetMaxLevel)
|
||||||
@@ -82,7 +84,7 @@ func (p *Player) AddPetExp(petInfo *model.PetInfo, addExp int64) {
|
|||||||
var petUpdateInfo info.UpdatePropInfo
|
var petUpdateInfo info.UpdatePropInfo
|
||||||
|
|
||||||
copier.Copy(&petUpdateInfo, petInfo)
|
copier.Copy(&petUpdateInfo, petInfo)
|
||||||
petUpdateInfo.Exp = uint32(gainedExp)
|
petUpdateInfo.Exp = uint32(allocatedExp)
|
||||||
updateOutbound.Data = append(updateOutbound.Data, petUpdateInfo)
|
updateOutbound.Data = append(updateOutbound.Data, petUpdateInfo)
|
||||||
p.SendPack(header.Pack(updateOutbound)) //准备包由各自发,因为协议不一样
|
p.SendPack(header.Pack(updateOutbound)) //准备包由各自发,因为协议不一样
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,13 @@ func (slot PetBagSlot) PetInfo() model.PetInfo {
|
|||||||
return slot.info
|
return slot.info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (slot PetBagSlot) PetInfoPtr() *model.PetInfo {
|
||||||
|
if !slot.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &(*slot.list)[slot.index]
|
||||||
|
}
|
||||||
|
|
||||||
func (slot PetBagSlot) IsMainBag() bool {
|
func (slot PetBagSlot) IsMainBag() bool {
|
||||||
return slot.main
|
return slot.main
|
||||||
}
|
}
|
||||||
@@ -99,12 +106,20 @@ func validatePetBagOrder(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildLimitedPetList(petList []model.PetInfo, limitlevel uint32) []model.PetInfo {
|
||||||
|
result := make([]model.PetInfo, 0, len(petList))
|
||||||
|
for _, petInfo := range petList {
|
||||||
|
result = append(result, ApplyPetLevelLimit(petInfo, limitlevel))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserBagPetInfo 返回主背包和并列备用精灵列表。
|
// GetUserBagPetInfo 返回主背包和并列备用精灵列表。
|
||||||
func (p *Player) GetUserBagPetInfo() *pet.GetUserBagPetInfoOutboundInfo {
|
func (p *Player) GetUserBagPetInfo(limitlevel uint32) *pet.GetUserBagPetInfoOutboundInfo {
|
||||||
|
|
||||||
result := &pet.GetUserBagPetInfoOutboundInfo{
|
result := &pet.GetUserBagPetInfoOutboundInfo{
|
||||||
PetList: p.Info.PetList,
|
PetList: buildLimitedPetList(p.Info.PetList, limitlevel),
|
||||||
BackupPetList: p.Info.BackupPetList,
|
BackupPetList: buildLimitedPetList(p.Info.BackupPetList, limitlevel),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
157
logic/service/player/pet_test.go
Normal file
157
logic/service/player/pet_test.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package player
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/common/data/xmlres"
|
||||||
|
playermodel "blazing/modules/player/model"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func firstPetIDForTest(t *testing.T) int {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
for id := range xmlres.PetMAP {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatal("xmlres.PetMAP is empty")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddPetExpAllowsLevelBeyond100WhilePanelStaysCapped(t *testing.T) {
|
||||||
|
petID := firstPetIDForTest(t)
|
||||||
|
petInfo := playermodel.GenPetInfo(petID, 31, 0, 0, 100, nil, 0)
|
||||||
|
expectedPanel := playermodel.GenPetInfo(petID, 31, 0, 0, 100, nil, 0)
|
||||||
|
if petInfo == nil {
|
||||||
|
t.Fatalf("failed to generate test pet")
|
||||||
|
}
|
||||||
|
if expectedPanel == nil {
|
||||||
|
t.Fatalf("failed to generate expected test pet")
|
||||||
|
}
|
||||||
|
|
||||||
|
player := &Player{
|
||||||
|
baseplayer: baseplayer{
|
||||||
|
Info: &playermodel.PlayerInfo{
|
||||||
|
ExpPool: 1_000_000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
player.AddPetExp(petInfo, petInfo.NextLvExp+10_000)
|
||||||
|
|
||||||
|
if petInfo.Level <= 100 {
|
||||||
|
t.Fatalf("expected pet level to continue beyond 100, got %d", petInfo.Level)
|
||||||
|
}
|
||||||
|
if petInfo.MaxHp != expectedPanel.MaxHp {
|
||||||
|
t.Fatalf("expected max hp to stay capped at 100-level panel, got %d want %d", petInfo.MaxHp, expectedPanel.MaxHp)
|
||||||
|
}
|
||||||
|
if petInfo.Prop != expectedPanel.Prop {
|
||||||
|
t.Fatalf("expected props to stay capped at 100-level panel, got %+v want %+v", petInfo.Prop, expectedPanel.Prop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddPetExpRecalculatesPanelForLevelAbove100(t *testing.T) {
|
||||||
|
petID := firstPetIDForTest(t)
|
||||||
|
petInfo := playermodel.GenPetInfo(petID, 31, 0, 0, 100, nil, 0)
|
||||||
|
expectedPanel := playermodel.GenPetInfo(petID, 31, 0, 0, 100, nil, 0)
|
||||||
|
if petInfo == nil {
|
||||||
|
t.Fatalf("failed to generate test pet")
|
||||||
|
}
|
||||||
|
if expectedPanel == nil {
|
||||||
|
t.Fatalf("failed to generate expected test pet")
|
||||||
|
}
|
||||||
|
petInfo.Level = 101
|
||||||
|
petInfo.Exp = 7
|
||||||
|
petInfo.MaxHp = 1
|
||||||
|
petInfo.Hp = 999999
|
||||||
|
|
||||||
|
player := &Player{
|
||||||
|
baseplayer: baseplayer{
|
||||||
|
Info: &playermodel.PlayerInfo{
|
||||||
|
ExpPool: 50_000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
player.AddPetExp(petInfo, 12_345)
|
||||||
|
|
||||||
|
if petInfo.Level < 101 {
|
||||||
|
t.Fatalf("expected level above 100 to be preserved, got %d", petInfo.Level)
|
||||||
|
}
|
||||||
|
if petInfo.MaxHp != expectedPanel.MaxHp {
|
||||||
|
t.Fatalf("expected max hp to be recalculated using level 100 cap, got %d want %d", petInfo.MaxHp, expectedPanel.MaxHp)
|
||||||
|
}
|
||||||
|
if petInfo.Hp != petInfo.MaxHp {
|
||||||
|
t.Fatalf("expected hp to be clamped to recalculated max hp, got hp=%d maxHp=%d", petInfo.Hp, petInfo.MaxHp)
|
||||||
|
}
|
||||||
|
if player.Info.ExpPool != 50_000-12_345 {
|
||||||
|
t.Fatalf("expected exp pool to be consumed normally, got %d", player.Info.ExpPool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddPetExpSmallRewardDoesNotJumpToMaxLevel(t *testing.T) {
|
||||||
|
petID := firstPetIDForTest(t)
|
||||||
|
petInfo := playermodel.GenPetInfo(petID, 31, 0, 0, 20, nil, 0)
|
||||||
|
if petInfo == nil {
|
||||||
|
t.Fatalf("failed to generate test pet")
|
||||||
|
}
|
||||||
|
|
||||||
|
player := &Player{
|
||||||
|
baseplayer: baseplayer{
|
||||||
|
Info: &playermodel.PlayerInfo{
|
||||||
|
ExpPool: 1_000_000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
addExp := int64(100)
|
||||||
|
originalLevel := petInfo.Level
|
||||||
|
nextLevelNeed := petInfo.NextLvExp - petInfo.Exp
|
||||||
|
if addExp >= nextLevelNeed {
|
||||||
|
t.Fatalf("test setup invalid: addExp=%d should be smaller than next level need=%d", addExp, nextLevelNeed)
|
||||||
|
}
|
||||||
|
|
||||||
|
player.AddPetExp(petInfo, addExp)
|
||||||
|
|
||||||
|
if petInfo.Level != originalLevel {
|
||||||
|
t.Fatalf("expected level to stay at %d, got %d", originalLevel, petInfo.Level)
|
||||||
|
}
|
||||||
|
if petInfo.Exp != addExp {
|
||||||
|
t.Fatalf("expected current exp to increase by %d, got %d", addExp, petInfo.Exp)
|
||||||
|
}
|
||||||
|
if player.Info.ExpPool != 1_000_000-addExp {
|
||||||
|
t.Fatalf("expected exp pool to decrease by %d, got %d", addExp, player.Info.ExpPool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddPetExpUsesDynamicPerLevelRequirement(t *testing.T) {
|
||||||
|
petID := firstPetIDForTest(t)
|
||||||
|
petInfo := playermodel.GenPetInfo(petID, 31, 0, 0, 20, nil, 0)
|
||||||
|
if petInfo == nil {
|
||||||
|
t.Fatalf("failed to generate test pet")
|
||||||
|
}
|
||||||
|
|
||||||
|
initialNeed := petInfo.NextLvExp
|
||||||
|
if initialNeed <= 0 {
|
||||||
|
t.Fatalf("expected positive exp requirement, got %d", initialNeed)
|
||||||
|
}
|
||||||
|
|
||||||
|
player := &Player{
|
||||||
|
baseplayer: baseplayer{
|
||||||
|
Info: &playermodel.PlayerInfo{
|
||||||
|
ExpPool: 1_000_000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
player.AddPetExp(petInfo, initialNeed)
|
||||||
|
|
||||||
|
if petInfo.Level != 21 {
|
||||||
|
t.Fatalf("expected pet level to become 21, got %d", petInfo.Level)
|
||||||
|
}
|
||||||
|
if petInfo.Exp != 0 {
|
||||||
|
t.Fatalf("expected level-up to reset current level exp, got %d", petInfo.Exp)
|
||||||
|
}
|
||||||
|
if petInfo.NextLvExp == initialNeed {
|
||||||
|
t.Fatalf("expected next level exp to change after leveling, still %d", petInfo.NextLvExp)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -228,6 +228,21 @@ func (p *Player) GetSpace() *space.Space {
|
|||||||
return space.GetSpace(p.Info.MapID)
|
return space.GetSpace(p.Info.MapID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Player) CurrentMapPetLevelLimit() uint32 {
|
||||||
|
if p == nil {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
currentSpace := p.GetSpace()
|
||||||
|
if currentSpace != nil && currentSpace.IsLevelBreakMap {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) IsCurrentMapLevelBreak() bool {
|
||||||
|
return p != nil && p.CurrentMapPetLevelLimit() == 0
|
||||||
|
}
|
||||||
|
|
||||||
// CanFight 检查玩家是否可以进行战斗
|
// CanFight 检查玩家是否可以进行战斗
|
||||||
// 0无战斗,1PVP,2,BOOS,3PVE
|
// 0无战斗,1PVP,2,BOOS,3PVE
|
||||||
func (p *Player) CanFight() errorcode.ErrorCode {
|
func (p *Player) CanFight() errorcode.ErrorCode {
|
||||||
@@ -419,7 +434,15 @@ func (p *Player) ItemAdd(ItemId, ItemCnt int64) (result bool) {
|
|||||||
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
p.Service.Item.UPDATE(uint32(ItemId), gconv.Int(ItemCnt))
|
if err := p.Service.Item.UPDATE(uint32(ItemId), gconv.Int(ItemCnt)); err != nil {
|
||||||
|
cool.Logger.Error(context.TODO(), "item add update failed", p.Info.UserID, ItemId, ItemCnt, err)
|
||||||
|
|
||||||
|
t1 := common.NewTomeeHeader(2601, p.Info.UserID)
|
||||||
|
t1.Result = uint32(errorcode.ErrorCodes.ErrSystemError200007)
|
||||||
|
|
||||||
|
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,10 +42,11 @@ type Space struct {
|
|||||||
WeatherType []uint32
|
WeatherType []uint32
|
||||||
TimeBoss info.S2C_2022
|
TimeBoss info.S2C_2022
|
||||||
|
|
||||||
IsTime bool
|
IsTime bool
|
||||||
DropItemIds []uint32
|
IsLevelBreakMap bool
|
||||||
PitS *csmap.CsMap[int, []model.MapPit]
|
DropItemIds []uint32
|
||||||
MapNodeS *csmap.CsMap[uint32, *model.MapNode]
|
PitS *csmap.CsMap[int, []model.MapPit]
|
||||||
|
MapNodeS *csmap.CsMap[uint32, *model.MapNode]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSpace() *Space {
|
func NewSpace() *Space {
|
||||||
@@ -185,6 +186,9 @@ func (ret *Space) init() {
|
|||||||
if r.IsTimeSpace != 0 {
|
if r.IsTimeSpace != 0 {
|
||||||
ret.IsTime = true
|
ret.IsTime = true
|
||||||
}
|
}
|
||||||
|
if r.IsLevelBreakMap != 0 {
|
||||||
|
ret.IsLevelBreakMap = true
|
||||||
|
}
|
||||||
ret.MapBossSInfo = info.MapModelBroadcastInfo{}
|
ret.MapBossSInfo = info.MapModelBroadcastInfo{}
|
||||||
ret.MapBossSInfo.INFO = make([]info.MapModelBroadcastEntry, 0)
|
ret.MapBossSInfo.INFO = make([]info.MapModelBroadcastEntry, 0)
|
||||||
|
|
||||||
|
|||||||
@@ -1,94 +1,96 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/common/rpc"
|
"blazing/common/rpc"
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yudeguang/ratelimit"
|
"github.com/yudeguang/ratelimit"
|
||||||
|
|
||||||
i18n "blazing/modules/base/middleware"
|
i18n "blazing/modules/base/middleware"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
"github.com/gogf/gf/v2/net/ghttp"
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
"github.com/gogf/gf/v2/os/gcmd"
|
"github.com/gogf/gf/v2/os/gcmd"
|
||||||
"github.com/gogf/gf/v2/os/gfile"
|
"github.com/gogf/gf/v2/os/gfile"
|
||||||
"github.com/xiaoqidun/qqwry"
|
"github.com/xiaoqidun/qqwry"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Main = gcmd.Command{
|
Main = gcmd.Command{
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Usage: "main",
|
Usage: "main",
|
||||||
Brief: "start http server",
|
Brief: "start http server",
|
||||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||||
|
|
||||||
r := parser.GetOpt("debug", false)
|
r := parser.GetOpt("debug", false)
|
||||||
if r.Bool() {
|
if r.Bool() {
|
||||||
g.DB().SetDebug(true)
|
g.DB().SetDebug(true)
|
||||||
// service.NewServerService().SetServerScreen(0, "sss")
|
// service.NewServerService().SetServerScreen(0, "sss")
|
||||||
cool.Config.ServerInfo.IsDebug = 1
|
cool.Config.ServerInfo.IsDebug = 1
|
||||||
}
|
}
|
||||||
if cool.IsRedisMode {
|
if err = cool.RunAutoMigrate(); err != nil {
|
||||||
go rpc.ListenFunc(ctx)
|
return err
|
||||||
}
|
}
|
||||||
// // 从文件加载IP数据库
|
if cool.IsRedisMode {
|
||||||
if err := qqwry.LoadFile("public/qqwry.ipdb"); err != nil {
|
go rpc.ListenFunc(ctx)
|
||||||
panic(err)
|
}
|
||||||
}
|
// // 从文件加载IP数据库
|
||||||
//go robot()
|
if err := qqwry.LoadFile("public/qqwry.ipdb"); err != nil {
|
||||||
//go reg()
|
panic(err)
|
||||||
go startrobot()
|
}
|
||||||
s := g.Server()
|
//go robot()
|
||||||
s.Use(Limiter, ghttp.MiddlewareHandlerResponse)
|
//go reg()
|
||||||
s.EnableAdmin()
|
go startrobot()
|
||||||
s.SetServerAgent(cool.Config.Name)
|
s := g.Server()
|
||||||
s.BindHookHandler("/*", ghttp.HookBeforeServe, beforeServeHook)
|
s.Use(Limiter, ghttp.MiddlewareHandlerResponse)
|
||||||
// runtime.SetMutexProfileFraction(1) // (非必需)开启对锁调用的跟踪
|
s.EnableAdmin()
|
||||||
// runtime.SetBlockProfileRate(1) // (非必需)开启对阻塞操作的跟踪
|
s.SetServerAgent(cool.Config.Name)
|
||||||
// s.EnablePProf()
|
s.BindHookHandler("/*", ghttp.HookBeforeServe, beforeServeHook)
|
||||||
// 如果存在 data/cool-admin-vue/dist 目录,则设置为主目录
|
// runtime.SetMutexProfileFraction(1) // (非必需)开启对锁调用的跟踪
|
||||||
if gfile.IsDir("public") {
|
// runtime.SetBlockProfileRate(1) // (非必需)开启对阻塞操作的跟踪
|
||||||
s.SetServerRoot("public")
|
// s.EnablePProf()
|
||||||
}
|
// 如果存在 data/cool-admin-vue/dist 目录,则设置为主目录
|
||||||
// i18n 信息
|
if gfile.IsDir("public") {
|
||||||
s.BindHandler("/i18n", i18n.I18nInfo)
|
s.SetServerRoot("public")
|
||||||
// g.Server().BindMiddleware("/*", MiddlewareCORS)
|
}
|
||||||
s.Run()
|
// i18n 信息
|
||||||
return nil
|
s.BindHandler("/i18n", i18n.I18nInfo)
|
||||||
},
|
// g.Server().BindMiddleware("/*", MiddlewareCORS)
|
||||||
}
|
s.Run()
|
||||||
)
|
return nil
|
||||||
|
},
|
||||||
func beforeServeHook(r *ghttp.Request) {
|
}
|
||||||
//glog.Debugf(r.GetCtx(), "beforeServeHook [is file:%v] URI:%s", r.IsFileRequest(), r.RequestURI)
|
)
|
||||||
r.Response.CORSDefault()
|
|
||||||
}
|
func beforeServeHook(r *ghttp.Request) {
|
||||||
|
//glog.Debugf(r.GetCtx(), "beforeServeHook [is file:%v] URI:%s", r.IsFileRequest(), r.RequestURI)
|
||||||
// var limiter = rate.NewLimiter(rate.Limit(150), 50)
|
r.Response.CORSDefault()
|
||||||
var limiter *ratelimit.Rule = ratelimit.NewRule()
|
}
|
||||||
|
|
||||||
// 简单规则案例
|
// var limiter = rate.NewLimiter(rate.Limit(150), 50)
|
||||||
func init() {
|
var limiter *ratelimit.Rule = ratelimit.NewRule()
|
||||||
|
|
||||||
//步骤二:增加一条或者多条规则组成复合规则,此复合规则必须至少包含一条规则
|
// 简单规则案例
|
||||||
limiter.AddRule(time.Second*1, 20)
|
func init() {
|
||||||
//步骤三:调用函数判断某用户是否允许访问 allow:= r.AllowVisit(user)
|
//步骤二:增加一条或者多条规则组成复合规则,此复合规则必须至少包含一条规则
|
||||||
|
limiter.AddRule(time.Second*1, 20)
|
||||||
}
|
//步骤三:调用函数判断某用户是否允许访问 allow:= r.AllowVisit(user)
|
||||||
|
|
||||||
// Limiter is a middleware that implements rate limiting for all HTTP requests.
|
}
|
||||||
// It returns HTTP 429 (Too Many Requests) when the rate limit is exceeded.
|
|
||||||
func Limiter(r *ghttp.Request) {
|
// Limiter is a middleware that implements rate limiting for all HTTP requests.
|
||||||
// 3. 为任意键 "some-key" 获取一个速率限制器
|
// It returns HTTP 429 (Too Many Requests) when the rate limit is exceeded.
|
||||||
// - rate.Limit(2): 表示速率为 "每秒2个请求"
|
func Limiter(r *ghttp.Request) {
|
||||||
// - 2: 表示桶的容量 (Burst),允许瞬时处理2个请求
|
// 3. 为任意键 "some-key" 获取一个速率限制器
|
||||||
ip := r.GetClientIp()
|
// - rate.Limit(2): 表示速率为 "每秒2个请求"
|
||||||
if !limiter.AllowVisitByIP4(ip) {
|
// - 2: 表示桶的容量 (Burst),允许瞬时处理2个请求
|
||||||
r.Response.WriteStatusExit(429) // Return 429 Too Many Requests
|
ip := r.GetClientIp()
|
||||||
|
if !limiter.AllowVisitByIP4(ip) {
|
||||||
r.ExitAll()
|
r.Response.WriteStatusExit(429) // Return 429 Too Many Requests
|
||||||
}
|
|
||||||
r.Middleware.Next()
|
r.ExitAll()
|
||||||
}
|
}
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,211 +1,213 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/common/rpc"
|
"blazing/common/rpc"
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
"blazing/modules/base/config"
|
"blazing/modules/base/config"
|
||||||
"blazing/modules/config/service"
|
"blazing/modules/config/service"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
"github.com/gogf/gf/v2/net/ghttp"
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
"github.com/gogf/gf/v2/os/gtime"
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
"github.com/gogf/gf/v2/util/gconv"
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/lxzan/gws"
|
"github.com/lxzan/gws"
|
||||||
)
|
)
|
||||||
|
|
||||||
const UpStream = "http://43.248.3.21:45632/"
|
const UpStream = "http://43.248.3.21:45632/"
|
||||||
|
|
||||||
func MiddlewareCORS(r *ghttp.Request) {
|
func MiddlewareCORS(r *ghttp.Request) {
|
||||||
r.Response.CORSDefault()
|
r.Response.CORSDefault()
|
||||||
corsOptions := r.Response.DefaultCORSOptions()
|
corsOptions := r.Response.DefaultCORSOptions()
|
||||||
corsOptions.AllowDomain = []string{"*", "localhost", "tauri.localhost"}
|
corsOptions.AllowDomain = []string{"*", "localhost", "tauri.localhost"}
|
||||||
if !r.Response.CORSAllowedOrigin(corsOptions) {
|
if !r.Response.CORSAllowedOrigin(corsOptions) {
|
||||||
r.Response.WriteStatus(http.StatusForbidden)
|
r.Response.WriteStatus(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Response.CORS(corsOptions)
|
r.Response.CORS(corsOptions)
|
||||||
if r.Response.Request.Method == "OPTIONS" {
|
if r.Response.Request.Method == "OPTIONS" {
|
||||||
r.Response.WriteStatus(http.StatusOK)
|
r.Response.WriteStatus(http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// fmt.Println(r.Response.Header())
|
// fmt.Println(r.Response.Header())
|
||||||
//g.Dump(r.Response.Server.SetConfig(gtt))
|
//g.Dump(r.Response.Server.SetConfig(gtt))
|
||||||
//r.Response.Header().Del("Server") // 删除Server头
|
//r.Response.Header().Del("Server") // 删除Server头
|
||||||
// r.Response.Header().Set("Server", "blazing")
|
// r.Response.Header().Set("Server", "blazing")
|
||||||
r.Middleware.Next()
|
r.Middleware.Next()
|
||||||
}
|
}
|
||||||
func StartServerProxy() {
|
func StartServerProxy() {
|
||||||
s := g.Server()
|
s := g.Server()
|
||||||
// Parse the upstream URL
|
// Parse the upstream URL
|
||||||
u, _ := url.Parse(UpStream)
|
u, _ := url.Parse(UpStream)
|
||||||
// Create a new reverse proxy instance
|
// Create a new reverse proxy instance
|
||||||
proxy := httputil.NewSingleHostReverseProxy(u)
|
proxy := httputil.NewSingleHostReverseProxy(u)
|
||||||
// Configure error handling for proxy failures
|
// Configure error handling for proxy failures
|
||||||
proxy.ErrorHandler = func(writer http.ResponseWriter, request *http.Request, e error) {
|
proxy.ErrorHandler = func(writer http.ResponseWriter, request *http.Request, e error) {
|
||||||
writer.WriteHeader(http.StatusBadGateway)
|
writer.WriteHeader(http.StatusBadGateway)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.BindHandler("/bbs/api/fof/upload", func(r *ghttp.Request) {
|
s.BindHandler("/bbs/api/fof/upload", func(r *ghttp.Request) {
|
||||||
// 1. 调用上传方法(仍返回单个URL字符串,不改动Upload方法)
|
// 1. 调用上传方法(仍返回单个URL字符串,不改动Upload方法)
|
||||||
urlStr, err := cool.File().Upload(r.Context())
|
urlStr, err := cool.File().Upload(r.Context())
|
||||||
|
|
||||||
// 2. 错误处理:返回标准化错误JSON
|
// 2. 错误处理:返回标准化错误JSON
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Response.Header().Set("Content-Type", "application/json; charset=utf-8")
|
r.Response.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
r.Response.WriteHeader(http.StatusBadRequest)
|
r.Response.WriteHeader(http.StatusBadRequest)
|
||||||
json.NewEncoder(r.Response.Writer).Encode(map[string]interface{}{
|
json.NewEncoder(r.Response.Writer).Encode(map[string]interface{}{
|
||||||
"code": 1,
|
"code": 1,
|
||||||
"msg": err.Error(),
|
"msg": err.Error(),
|
||||||
"data": nil,
|
"data": nil,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 基于返回的URL,构造完整的JSON结构体(和示例完全一致)
|
// 3. 基于返回的URL,构造完整的JSON结构体(和示例完全一致)
|
||||||
// 解析URL中的文件名和路径(从urlStr中提取)
|
// 解析URL中的文件名和路径(从urlStr中提取)
|
||||||
baseName := filepath.Base(urlStr) // 提取文件名(如13e8d062-xxx.jpg)
|
baseName := filepath.Base(urlStr) // 提取文件名(如13e8d062-xxx.jpg)
|
||||||
dir := gtime.Now().Format("Y-m-d") // 日期目录(2026-02-07)
|
dir := gtime.Now().Format("Y-m-d") // 日期目录(2026-02-07)
|
||||||
path := fmt.Sprintf("%s/%s", dir, baseName) // 拼接path字段
|
path := fmt.Sprintf("%s/%s", dir, baseName) // 拼接path字段
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
randomID := strconv.Itoa(rand.Intn(1000)) // 模拟ID(如54)
|
randomID := strconv.Itoa(rand.Intn(1000)) // 模拟ID(如54)
|
||||||
uuidStr := uuid.New().String() // 生成UUID
|
uuidStr := uuid.New().String() // 生成UUID
|
||||||
humanSize := "743kB" // 模拟易读大小(可根据实际需求优化)
|
humanSize := "743kB" // 模拟易读大小(可根据实际需求优化)
|
||||||
fileSize := int64(760783) // 模拟文件大小(字节)
|
fileSize := int64(760783) // 模拟文件大小(字节)
|
||||||
|
|
||||||
// 构造和示例完全一致的响应结构体
|
// 构造和示例完全一致的响应结构体
|
||||||
fullResponse := map[string]interface{}{
|
fullResponse := map[string]interface{}{
|
||||||
"data": []map[string]interface{}{
|
"data": []map[string]interface{}{
|
||||||
{
|
{
|
||||||
"type": "files",
|
"type": "files",
|
||||||
"id": randomID,
|
"id": randomID,
|
||||||
"attributes": map[string]interface{}{
|
"attributes": map[string]interface{}{
|
||||||
"baseName": baseName,
|
"baseName": baseName,
|
||||||
"path": path,
|
"path": path,
|
||||||
"url": urlStr, // 用Upload返回的URL
|
"url": urlStr, // 用Upload返回的URL
|
||||||
"type": "image/jpeg", // 模拟MIME类型
|
"type": "image/jpeg", // 模拟MIME类型
|
||||||
"size": fileSize,
|
"size": fileSize,
|
||||||
"humanSize": humanSize,
|
"humanSize": humanSize,
|
||||||
"createdAt": nil, // null
|
"createdAt": nil, // null
|
||||||
"uuid": uuidStr,
|
"uuid": uuidStr,
|
||||||
"tag": "just-url",
|
"tag": "just-url",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"bbcode": `[img]` + urlStr + `[/img]`, // 和URL一致
|
"bbcode": `[img]` + urlStr + `[/img]`, // 和URL一致
|
||||||
"shared": false,
|
"shared": false,
|
||||||
"canViewInfo": false,
|
"canViewInfo": false,
|
||||||
"canHide": true,
|
"canHide": true,
|
||||||
"canDelete": true,
|
"canDelete": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 输出完整JSON响应
|
// 4. 输出完整JSON响应
|
||||||
r.Response.Header().Set("Content-Type", "application/json; charset=utf-8")
|
r.Response.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
if err := json.NewEncoder(r.Response.Writer).Encode(fullResponse); err != nil {
|
if err := json.NewEncoder(r.Response.Writer).Encode(fullResponse); err != nil {
|
||||||
// 兜底错误
|
// 兜底错误
|
||||||
fmt.Fprintf(r.Response.Writer, `{"code":1,"msg":"响应生成失败:%s","data":null}`, err.Error())
|
fmt.Fprintf(r.Response.Writer, `{"code":1,"msg":"响应生成失败:%s","data":null}`, err.Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Handle all requests with path prefix "/proxy/*"
|
// Handle all requests with path prefix "/proxy/*"
|
||||||
s.BindHandler("/bbs/*url", func(r *ghttp.Request) {
|
s.BindHandler("/bbs/*url", func(r *ghttp.Request) {
|
||||||
var (
|
var (
|
||||||
//originalPath = r.Request.URL.Path
|
//originalPath = r.Request.URL.Path
|
||||||
proxyToPath = "/" + r.Get("url").String()
|
proxyToPath = "/" + r.Get("url").String()
|
||||||
)
|
)
|
||||||
// Rewrite the request path
|
// Rewrite the request path
|
||||||
r.Request.URL.Path = proxyToPath
|
r.Request.URL.Path = proxyToPath
|
||||||
// Log the proxy operation
|
// Log the proxy operation
|
||||||
//g.Log().Infof(r.Context(), `proxy:"%s" -> backend:"%s"`, originalPath, proxyToPath)
|
//g.Log().Infof(r.Context(), `proxy:"%s" -> backend:"%s"`, originalPath, proxyToPath)
|
||||||
// Ensure request body can be read multiple times if needed
|
// Ensure request body can be read multiple times if needed
|
||||||
r.MakeBodyRepeatableRead(false)
|
r.MakeBodyRepeatableRead(false)
|
||||||
// Forward the request to the backend server
|
// Forward the request to the backend server
|
||||||
proxy.ServeHTTP(r.Response.Writer, r.Request)
|
proxy.ServeHTTP(r.Response.Writer, r.Request)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if config.Config.Middleware.Authority.Enable {
|
if config.Config.Middleware.Authority.Enable {
|
||||||
g.Server().BindMiddleware("/admin/*/open/*", BaseAuthorityMiddlewareOpen)
|
g.Server().BindMiddleware("/admin/*/open/*", BaseAuthorityMiddlewareOpen)
|
||||||
g.Server().BindMiddleware("/rpc/*", BaseAuthorityMiddlewareOpen)
|
g.Server().BindMiddleware("/rpc/*", BaseAuthorityMiddlewareOpen)
|
||||||
g.Server().BindMiddleware("/admin/*/comm/*", BaseAuthorityMiddlewareComm)
|
g.Server().BindMiddleware("/admin/*/comm/*", BaseAuthorityMiddlewareComm)
|
||||||
g.Server().BindMiddleware("/admin/*", BaseAuthorityMiddleware)
|
g.Server().BindMiddleware("/seer/game/cdk/*", BaseAuthorityMiddlewareComm)
|
||||||
// g.Server().BindMiddleware("/*", AutoI18n)
|
g.Server().BindMiddleware("/seer/game/cdk/*", BaseAuthorityMiddleware)
|
||||||
g.Server().BindMiddleware("/*", MiddlewareCORS)
|
g.Server().BindMiddleware("/admin/*", BaseAuthorityMiddleware)
|
||||||
|
// g.Server().BindMiddleware("/*", AutoI18n)
|
||||||
}
|
g.Server().BindMiddleware("/*", MiddlewareCORS)
|
||||||
if config.Config.Middleware.Log.Enable {
|
|
||||||
g.Server().BindMiddleware("/admin/*", BaseLog)
|
}
|
||||||
}
|
if config.Config.Middleware.Log.Enable {
|
||||||
StartServerProxy()
|
g.Server().BindMiddleware("/admin/*", BaseLog)
|
||||||
tt := rpc.CServer()
|
}
|
||||||
|
StartServerProxy()
|
||||||
g.Server().BindHandler("/rpc/*", func(r *ghttp.Request) {
|
tt := rpc.CServer()
|
||||||
|
|
||||||
tt.ServeHTTP(r.Response.Writer, r.Request)
|
g.Server().BindHandler("/rpc/*", func(r *ghttp.Request) {
|
||||||
|
|
||||||
})
|
tt.ServeHTTP(r.Response.Writer, r.Request)
|
||||||
g.Server().Use(CompressMiddleware)
|
|
||||||
g.Server().BindHandler("/server/*", func(r *ghttp.Request) {
|
})
|
||||||
servert := new(ServerHandler)
|
g.Server().Use(CompressMiddleware)
|
||||||
id := gconv.Uint16(r.URL.Query().Get("id"))
|
g.Server().BindHandler("/server/*", func(r *ghttp.Request) {
|
||||||
servert.isinstall = gconv.Uint32(r.URL.Query().Get("isinstall"))
|
servert := new(ServerHandler)
|
||||||
servert.ServerList = service.NewServerService().StartUPdate(id, int(servert.isinstall))
|
id := gconv.Uint16(r.URL.Query().Get("id"))
|
||||||
|
servert.isinstall = gconv.Uint32(r.URL.Query().Get("isinstall"))
|
||||||
upgrader := gws.NewUpgrader(servert, &gws.ServerOption{
|
servert.ServerList = service.NewServerService().StartUPdate(id, int(servert.isinstall))
|
||||||
|
|
||||||
Authorize: func(_ *http.Request, _ gws.SessionStorage) bool {
|
upgrader := gws.NewUpgrader(servert, &gws.ServerOption{
|
||||||
|
|
||||||
tokenString := r.URL.Query().Get("Authorization")
|
Authorize: func(_ *http.Request, _ gws.SessionStorage) bool {
|
||||||
token, err := jwt.ParseWithClaims(tokenString, &cool.Claims{}, func(_ *jwt.Token) (interface{}, error) {
|
|
||||||
|
tokenString := r.URL.Query().Get("Authorization")
|
||||||
return []byte(config.Config.Jwt.Secret), nil
|
token, err := jwt.ParseWithClaims(tokenString, &cool.Claims{}, func(_ *jwt.Token) (interface{}, error) {
|
||||||
})
|
|
||||||
if err != nil {
|
return []byte(config.Config.Jwt.Secret), nil
|
||||||
return false
|
})
|
||||||
}
|
if err != nil {
|
||||||
if !token.Valid {
|
return false
|
||||||
return false
|
}
|
||||||
}
|
if !token.Valid {
|
||||||
admin := token.Claims.(*cool.Claims)
|
return false
|
||||||
if admin.UserId != 10001 {
|
}
|
||||||
return false
|
admin := token.Claims.(*cool.Claims)
|
||||||
}
|
if admin.UserId != 10001 {
|
||||||
|
return false
|
||||||
// var name = r.URL.Query().Get("name")
|
}
|
||||||
// if name == "" {
|
|
||||||
// return false
|
// var name = r.URL.Query().Get("name")
|
||||||
// }
|
// if name == "" {
|
||||||
// t, _ := service.NewBaseSysUserService().Person(admin.UserID)
|
// return false
|
||||||
|
// }
|
||||||
//Loger.Debug(context.TODO(), t.Mimi)
|
// t, _ := service.NewBaseSysUserService().Person(admin.UserID)
|
||||||
// session.Store("name", t.Mimi)
|
|
||||||
//session.Store("key", r.Header.Get("Sec-WebSocket-Key"))
|
//Loger.Debug(context.TODO(), t.Mimi)
|
||||||
return true
|
// session.Store("name", t.Mimi)
|
||||||
},
|
//session.Store("key", r.Header.Get("Sec-WebSocket-Key"))
|
||||||
})
|
return true
|
||||||
|
},
|
||||||
socket, err := upgrader.Upgrade(r.Response.Writer, r.Request)
|
})
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
socket, err := upgrader.Upgrade(r.Response.Writer, r.Request)
|
||||||
return
|
if err != nil {
|
||||||
}
|
fmt.Println(err)
|
||||||
// ants.Submit(func() {
|
return
|
||||||
// socket.ReadLoop()
|
}
|
||||||
// })
|
// ants.Submit(func() {
|
||||||
// ants.Submit(func() { socket.ReadLoop() })
|
// socket.ReadLoop()
|
||||||
go socket.ReadLoop()
|
// })
|
||||||
|
// ants.Submit(func() { socket.ReadLoop() })
|
||||||
})
|
go socket.ReadLoop()
|
||||||
}
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,65 @@
|
|||||||
package blazing
|
package blazing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "blazing/modules/config/controller"
|
_ "blazing/modules/config/controller"
|
||||||
_ "blazing/modules/config/model"
|
_ "blazing/modules/config/service"
|
||||||
_ "blazing/modules/config/service"
|
|
||||||
)
|
"blazing/cool"
|
||||||
|
configModel "blazing/modules/config/model"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gfile"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var (
|
||||||
|
ctx = gctx.GetInitCtx()
|
||||||
|
)
|
||||||
|
cool.Logger.Debug(ctx, "module config init start ...")
|
||||||
|
|
||||||
|
// 首次初始化 SPT 默认数据(不依赖 XML)。
|
||||||
|
sptModel := configModel.NewSptConfig()
|
||||||
|
count, err := g.DB("default").Model(sptModel.TableName()).Count()
|
||||||
|
if err != nil {
|
||||||
|
cool.Logger.Warning(ctx, "count config_spt failed:", err)
|
||||||
|
} else if count == 0 {
|
||||||
|
initPath := "modules/config/resource/initjson/config_spt.json"
|
||||||
|
content := gfile.GetBytes(initPath)
|
||||||
|
if len(content) == 0 {
|
||||||
|
cool.Logger.Warning(ctx, "config_spt init file is empty:", initPath)
|
||||||
|
} else {
|
||||||
|
jsonData, jErr := gjson.LoadContent(content)
|
||||||
|
if jErr != nil {
|
||||||
|
cool.Logger.Warning(ctx, "load config_spt init json failed:", jErr)
|
||||||
|
} else {
|
||||||
|
_, err = g.DB("default").Model(sptModel.TableName()).Data(jsonData.Var()).Insert()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
cool.Logger.Warning(ctx, "insert default config_spt failed:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menuCount, err := g.DB("default").Model("base_sys_menu").Where("router", "/config/spt").Count()
|
||||||
|
if err != nil {
|
||||||
|
cool.Logger.Warning(ctx, "count SPT menu failed:", err)
|
||||||
|
} else if menuCount == 0 {
|
||||||
|
_, err = g.DB("default").Model("base_sys_menu").Data(g.Map{
|
||||||
|
"parentId": 2,
|
||||||
|
"name": "SPT配置",
|
||||||
|
"router": "/config/spt",
|
||||||
|
"viewPath": "config/views/spt.vue",
|
||||||
|
"icon": "icon-menu",
|
||||||
|
"ordernum": 70,
|
||||||
|
"keepAlive": true,
|
||||||
|
"isShow": true,
|
||||||
|
"type": 1,
|
||||||
|
}).Insert()
|
||||||
|
if err != nil {
|
||||||
|
cool.Logger.Warning(ctx, "insert SPT menu failed:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cool.Logger.Debug(ctx, "module config init finished ...")
|
||||||
|
}
|
||||||
|
|||||||
33
modules/config/controller/admin/spt.go
Normal file
33
modules/config/controller/admin/spt.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/cool"
|
||||||
|
"blazing/modules/config/model"
|
||||||
|
"blazing/modules/config/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SptController struct {
|
||||||
|
*cool.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// 仅为新加的 SPT 表做定点迁移,避免首次启用 EPS 时读取表结构报错。
|
||||||
|
db, err := cool.InitDB("default")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err = db.AutoMigrate(model.NewSptConfig()); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err = db.Exec("ALTER TABLE config_spt DROP COLUMN IF EXISTS seat_id").Error; err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cool.RegisterController(&SptController{
|
||||||
|
&cool.Controller{
|
||||||
|
Prefix: "/admin/config/spt",
|
||||||
|
Api: []string{"Add", "Delete", "Update", "Info", "List", "Page"},
|
||||||
|
Service: service.NewSptService(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ type PetBaseConfig struct {
|
|||||||
Nature int32 `gorm:"not null;default:0;comment:'BOSS属性-性格'" json:"nature"`
|
Nature int32 `gorm:"not null;default:0;comment:'BOSS属性-性格'" json:"nature"`
|
||||||
Effect []uint32 `gorm:"type:jsonb;not null;default:'[]';comment:'BOSS特性'" json:"effect"`
|
Effect []uint32 `gorm:"type:jsonb;not null;default:'[]';comment:'BOSS特性'" json:"effect"`
|
||||||
Lv int32 `gorm:"not null;comment:'BOSS等级(LvHpMatchUser非0时此配置无效)'" json:"lv"`
|
Lv int32 `gorm:"not null;comment:'BOSS等级(LvHpMatchUser非0时此配置无效)'" json:"lv"`
|
||||||
Color string `gorm:"comment:'BOSS颜色'" json:"color"`
|
ColorID uint32 `gorm:"not null;default:0;comment:'BOSS颜色配置ID'" json:"color_id"`
|
||||||
Skin int32 `gorm:"not null;default:0;comment:'BOSS皮肤ID'" json:"skin"`
|
Skin int32 `gorm:"not null;default:0;comment:'BOSS皮肤ID'" json:"skin"`
|
||||||
Hp int32 `gorm:"comment:'BOSS血量值(LvHpMatchUser非0时此配置无效)'" json:"hp"`
|
Hp int32 `gorm:"comment:'BOSS血量值(LvHpMatchUser非0时此配置无效)'" json:"hp"`
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -157,6 +160,27 @@ func bindBossScriptFunctions(vm *goja.Runtime, hookAction any) {
|
|||||||
ctx.SwitchPetFn(uint32(catchTime))
|
ctx.SwitchPetFn(uint32(catchTime))
|
||||||
return goja.Undefined()
|
return goja.Undefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
_ = vm.Set("debug", func(call goja.FunctionCall) goja.Value {
|
||||||
|
logCtx := context.Background()
|
||||||
|
if len(call.Arguments) == 0 {
|
||||||
|
g.Log().Debugf(logCtx, "[boss-script] debug() called with no arguments")
|
||||||
|
return goja.Undefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := make([]string, 0, len(call.Arguments))
|
||||||
|
for _, arg := range call.Arguments {
|
||||||
|
exported := arg.Export()
|
||||||
|
if bytes, err := json.Marshal(exported); err == nil {
|
||||||
|
parts = append(parts, string(bytes))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts = append(parts, arg.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Log().Debugf(logCtx, "[boss-script] %s", strings.Join(parts, " "))
|
||||||
|
return goja.Undefined()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultHookActionResult(hookAction any) bool {
|
func defaultHookActionResult(hookAction any) bool {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type CDKConfig struct {
|
|||||||
|
|
||||||
// 核心字段
|
// 核心字段
|
||||||
CDKCode string `gorm:"not null;size:16;uniqueIndex;comment:'CDK编号(唯一标识,用于玩家兑换)'" json:"cdk_code" description:"CDK编号"`
|
CDKCode string `gorm:"not null;size:16;uniqueIndex;comment:'CDK编号(唯一标识,用于玩家兑换)'" json:"cdk_code" description:"CDK编号"`
|
||||||
CDKType uint32 `gorm:"column:type;not null;default:0;comment:'CDK类型:0普通奖励,1服务器冠名'" json:"type" description:"CDK类型"`
|
Type uint32 `gorm:"column:type;not null;default:0;comment:'CDK类型:0普通奖励,1服务器冠名'" json:"type" description:"CDK类型"`
|
||||||
|
|
||||||
//cdk可兑换次数,where不等于0
|
//cdk可兑换次数,where不等于0
|
||||||
ExchangeRemainCount int64 `gorm:"not null;default:1;comment:'CDK剩余可兑换次数(不能为0才允许兑换,支持查询where !=0)'" json:"exchange_remain_count" description:"剩余可兑换次数"`
|
ExchangeRemainCount int64 `gorm:"not null;default:1;comment:'CDK剩余可兑换次数(不能为0才允许兑换,支持查询where !=0)'" json:"exchange_remain_count" description:"剩余可兑换次数"`
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ type MapConfig struct {
|
|||||||
WeatherType []uint32 `gorm:"type:int[];comment:'天气类型( 0 晴天,1-雨天,2-雪天)'" json:"weather_type"`
|
WeatherType []uint32 `gorm:"type:int[];comment:'天气类型( 0 晴天,1-雨天,2-雪天)'" json:"weather_type"`
|
||||||
//是否超时空
|
//是否超时空
|
||||||
IsTimeSpace int `gorm:"type:int;default:0;comment:'是否超时空'" json:"is_time_space"`
|
IsTimeSpace int `gorm:"type:int;default:0;comment:'是否超时空'" json:"is_time_space"`
|
||||||
|
// 是否等级突破地图
|
||||||
|
IsLevelBreakMap int `gorm:"type:int;default:0;comment:'是否等级突破地图'" json:"is_level_break_map"`
|
||||||
|
|
||||||
// 掉落物配置
|
// 掉落物配置
|
||||||
DropItemIds []uint32 `gorm:"type:int[];comment:'掉落物IDs" json:"drop_item_ids"`
|
DropItemIds []uint32 `gorm:"type:int[];comment:'掉落物IDs" json:"drop_item_ids"`
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ const TableNameServerShow = "server_show"
|
|||||||
// ServerShow 绑定服务器展示信息(冠名、属主、到期时间)。
|
// ServerShow 绑定服务器展示信息(冠名、属主、到期时间)。
|
||||||
type ServerShow struct {
|
type ServerShow struct {
|
||||||
*cool.Model
|
*cool.Model
|
||||||
ServerID uint32 `gorm:"column:server_id;comment:'服务器ID';uniqueIndex" json:"server_id"`
|
ServerID uint32 `gorm:"column:server_id;comment:'服务器ID';index:idx_server_show_server_id;uniqueIndex:idx_server_show_server_owner" json:"server_id"`
|
||||||
Name string `gorm:"comment:'服务器展示名'" json:"name"`
|
Name string `gorm:"comment:'服务器展示名'" json:"name"`
|
||||||
Owner uint32 `gorm:"comment:'服务器属主'" json:"owner"`
|
Owner uint32 `gorm:"comment:'服务器属主';uniqueIndex:idx_server_show_server_owner" json:"owner"`
|
||||||
ExpireTime time.Time `gorm:"column:expire_time;default:0;comment:'展示到期时间'" json:"expire_time"`
|
ExpireTime time.Time `gorm:"column:expire_time;default:0;comment:'展示到期时间'" json:"expire_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
38
modules/config/model/spt.go
Normal file
38
modules/config/model/spt.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "blazing/cool"
|
||||||
|
|
||||||
|
const (
|
||||||
|
TableNameSptConfig = "config_spt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SptConfig SPT展示配置(仅保留前端展示必需字段)。
|
||||||
|
type SptConfig struct {
|
||||||
|
*BaseConfig
|
||||||
|
|
||||||
|
TaskID uint32 `gorm:"not null;uniqueIndex;comment:'SPT任务ID'" json:"task_id" description:"SPT任务ID"`
|
||||||
|
Title string `gorm:"type:varchar(64);not null;default:'';comment:'SPT名字'" json:"title" description:"SPT名字"`
|
||||||
|
PetID uint32 `gorm:"not null;default:0;comment:'SPT精灵ID'" json:"pet_id" description:"SPT精灵ID"`
|
||||||
|
Online int32 `gorm:"not null;default:1;comment:'是否开放(1开放/0未开放)'" json:"online" description:"是否开放"`
|
||||||
|
Level uint32 `gorm:"not null;default:1;comment:'SPT星级'" json:"level" description:"SPT星级"`
|
||||||
|
EnterID uint32 `gorm:"not null;default:0;comment:'进入地图ID'" json:"enter_id" description:"进入地图ID"`
|
||||||
|
Description string `gorm:"type:text;not null;default:'';comment:'SPT描述'" json:"description" description:"SPT描述"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SptConfig) TableName() string {
|
||||||
|
return TableNameSptConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SptConfig) GroupName() string {
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSptConfig() *SptConfig {
|
||||||
|
return &SptConfig{
|
||||||
|
BaseConfig: NewBaseConfig(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cool.CreateTable(&SptConfig{})
|
||||||
|
}
|
||||||
@@ -2,9 +2,11 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
|
"blazing/modules/base/service"
|
||||||
"blazing/modules/config/model"
|
"blazing/modules/config/model"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
@@ -170,14 +172,15 @@ type ServerNamingCDKResult struct {
|
|||||||
|
|
||||||
// UseServerNamingCDK 使用服务器冠名类型CDK,并原子化更新服务器归属和到期时间。
|
// UseServerNamingCDK 使用服务器冠名类型CDK,并原子化更新服务器归属和到期时间。
|
||||||
func (s *CdkService) UseServerNamingCDK(ctx context.Context, code string, ownerID, serverID uint32, serverName string) (*ServerNamingCDKResult, error) {
|
func (s *CdkService) UseServerNamingCDK(ctx context.Context, code string, ownerID, serverID uint32, serverName string) (*ServerNamingCDKResult, error) {
|
||||||
if ctx == nil {
|
execCtx := context.Background()
|
||||||
ctx = context.TODO()
|
if ctx != nil && ctx.Err() != nil {
|
||||||
|
ctx = nil
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
serverService := NewServerService()
|
serverService := NewServerService()
|
||||||
var updated model.ServerShow
|
var updated model.ServerShow
|
||||||
|
|
||||||
err := g.DB(s.Model.GroupName()).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
err := g.DB(s.Model.GroupName()).Transaction(execCtx, func(ctx context.Context, tx gdb.TX) error {
|
||||||
var cfg model.CDKConfig
|
var cfg model.CDKConfig
|
||||||
if err := tx.Model(s.Model).Where("cdk_code", code).WhereNot("exchange_remain_count", 0).Scan(&cfg); err != nil {
|
if err := tx.Model(s.Model).Where("cdk_code", code).WhereNot("exchange_remain_count", 0).Scan(&cfg); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -185,7 +188,7 @@ func (s *CdkService) UseServerNamingCDK(ctx context.Context, code string, ownerI
|
|||||||
if cfg.ID == 0 {
|
if cfg.ID == 0 {
|
||||||
return gerror.New("cdk不存在")
|
return gerror.New("cdk不存在")
|
||||||
}
|
}
|
||||||
if cfg.CDKType != CDKTypeServerNaming {
|
if cfg.Type != CDKTypeServerNaming {
|
||||||
return gerror.New("cdk类型不匹配")
|
return gerror.New("cdk类型不匹配")
|
||||||
}
|
}
|
||||||
if cfg.BindUserId != 0 && cfg.BindUserId != ownerID {
|
if cfg.BindUserId != 0 && cfg.BindUserId != ownerID {
|
||||||
@@ -196,7 +199,7 @@ func (s *CdkService) UseServerNamingCDK(ctx context.Context, code string, ownerI
|
|||||||
}
|
}
|
||||||
|
|
||||||
var server model.ServerList
|
var server model.ServerList
|
||||||
if err := tx.Model(model.NewServerList()).With(model.ServerShow{}).Where("online_id", serverID).Scan(&server); err != nil {
|
if err := tx.Model(model.NewServerList()).Where("online_id", serverID).Scan(&server); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if server.OnlineID == 0 {
|
if server.OnlineID == 0 {
|
||||||
@@ -216,8 +219,18 @@ func (s *CdkService) UseServerNamingCDK(ctx context.Context, code string, ownerI
|
|||||||
}
|
}
|
||||||
|
|
||||||
var currentShow model.ServerShow
|
var currentShow model.ServerShow
|
||||||
if server.ServerShow != nil {
|
if err := tx.Model(model.NewServerShow()).
|
||||||
currentShow = *server.ServerShow
|
Where("server_id", serverID).
|
||||||
|
Where("owner", ownerID).
|
||||||
|
OrderDesc("id").
|
||||||
|
Limit(1).
|
||||||
|
Scan(¤tShow); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
updated = currentShow
|
updated = currentShow
|
||||||
@@ -225,7 +238,7 @@ func (s *CdkService) UseServerNamingCDK(ctx context.Context, code string, ownerI
|
|||||||
updated.Name = serverName
|
updated.Name = serverName
|
||||||
updated.Owner = ownerID
|
updated.Owner = ownerID
|
||||||
|
|
||||||
if currentShow.ServerID == 0 || !serverService.isActiveServerShow(¤tShow, now) || currentShow.Owner != ownerID {
|
if currentShow.ServerID == 0 || !serverService.isActiveServerShow(¤tShow, now) {
|
||||||
updated.ExpireTime = now.AddDate(0, 1, 0)
|
updated.ExpireTime = now.AddDate(0, 1, 0)
|
||||||
} else {
|
} else {
|
||||||
baseTime := currentShow.ExpireTime
|
baseTime := currentShow.ExpireTime
|
||||||
@@ -259,6 +272,7 @@ func (s *CdkService) UseServerNamingCDK(ctx context.Context, code string, ownerI
|
|||||||
g.DB(s.Model.GroupName()).GetCore().ClearCache(context.TODO(), s.Model.TableName())
|
g.DB(s.Model.GroupName()).GetCore().ClearCache(context.TODO(), s.Model.TableName())
|
||||||
g.DB(model.NewServerList().GroupName()).GetCore().ClearCache(context.TODO(), model.NewServerList().TableName())
|
g.DB(model.NewServerList().GroupName()).GetCore().ClearCache(context.TODO(), model.NewServerList().TableName())
|
||||||
g.DB(model.NewServerShow().GroupName()).GetCore().ClearCache(context.TODO(), model.NewServerShow().TableName())
|
g.DB(model.NewServerShow().GroupName()).GetCore().ClearCache(context.TODO(), model.NewServerShow().TableName())
|
||||||
|
service.NewBaseSysUserService().UpdateGold(updated.Owner, int64(200*100))
|
||||||
return &ServerNamingCDKResult{
|
return &ServerNamingCDKResult{
|
||||||
ServerID: updated.ServerID,
|
ServerID: updated.ServerID,
|
||||||
ServerName: updated.Name,
|
ServerName: updated.Name,
|
||||||
|
|||||||
@@ -23,16 +23,23 @@ type ServerService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ServerShowInfo struct {
|
type ServerShowInfo struct {
|
||||||
OnlineID uint32 `json:"online_id"`
|
OnlineID uint32 `json:"online_id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
IP string `json:"ip"`
|
IP string `json:"ip"`
|
||||||
Port uint32 `json:"port"`
|
Port uint32 `json:"port"`
|
||||||
IsVip uint32 `json:"is_vip"`
|
IsVip uint32 `json:"is_vip"`
|
||||||
IsDebug uint8 `json:"is_debug"`
|
IsDebug uint8 `json:"is_debug"`
|
||||||
IsOpen uint8 `json:"is_open"`
|
IsOpen uint8 `json:"is_open"`
|
||||||
Owner uint32 `json:"owner"`
|
//Owner uint32 `json:"owner"`
|
||||||
ExpireTime time.Time `json:"expire_time"`
|
// ExpireTime time.Time `json:"expire_time"`
|
||||||
ServerShow *model.ServerShow `json:"servershow,omitempty"`
|
// ServerShow *model.ServerShow `json:"servershow,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DonationOwnedServerInfo struct {
|
||||||
|
ServerID uint32 `json:"server_id"`
|
||||||
|
ServerName string `json:"server_name"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
ExpireTime time.Time `json:"expire_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServerService() *ServerService {
|
func NewServerService() *ServerService {
|
||||||
@@ -46,7 +53,7 @@ func NewServerService() *ServerService {
|
|||||||
var rr []g.MapStrAny
|
var rr []g.MapStrAny
|
||||||
r, _ := gconv.Map(data)["list"].(gdb.Result)
|
r, _ := gconv.Map(data)["list"].(gdb.Result)
|
||||||
|
|
||||||
now := time.Now()
|
// now := time.Now()
|
||||||
serverIDs := make([]uint32, 0, len(r))
|
serverIDs := make([]uint32, 0, len(r))
|
||||||
for i := 0; i < len(r); i++ {
|
for i := 0; i < len(r); i++ {
|
||||||
serverID := gconv.Uint32(r[i].Map()["online_id"])
|
serverID := gconv.Uint32(r[i].Map()["online_id"])
|
||||||
@@ -55,12 +62,12 @@ func NewServerService() *ServerService {
|
|||||||
}
|
}
|
||||||
serverIDs = append(serverIDs, serverID)
|
serverIDs = append(serverIDs, serverID)
|
||||||
}
|
}
|
||||||
showMap := cf.getActiveServerShowMap(serverIDs, now)
|
//showMap := cf.getPrimaryActiveServerShowMap(serverIDs, now)
|
||||||
|
|
||||||
for i := 0; i < len(r); i++ {
|
for i := 0; i < len(r); i++ {
|
||||||
t, ok := cool.GetClient(gconv.Uint32(r[i].Map()["online_id"]), gconv.Uint32(r[i].Map()["port"]))
|
t, ok := cool.GetClient(gconv.Uint32(r[i].Map()["online_id"]), gconv.Uint32(r[i].Map()["port"]))
|
||||||
subm := r[i].GMap()
|
subm := r[i].GMap()
|
||||||
cf.applyServerShowMap(subm, gconv.Uint32(r[i].Map()["online_id"]), showMap)
|
//cf.applyServerShowMap(subm, gconv.Uint32(r[i].Map()["online_id"]), showMap)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
err := t.KickPerson(0) //实现指定服务器踢人
|
err := t.KickPerson(0) //实现指定服务器踢人
|
||||||
@@ -93,7 +100,9 @@ func NewServerService() *ServerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetPort(DepartmentID uint) []ServerShowInfo {
|
func (s *ServerService) GetPort(DepartmentID uint) []ServerShowInfo {
|
||||||
servers := s.GetServer()
|
servers := s.getRawServers()
|
||||||
|
now := time.Now()
|
||||||
|
showMap := s.getActiveServerShowListMap(s.collectServerIDs(servers), now)
|
||||||
items := make([]ServerShowInfo, 0, len(servers))
|
items := make([]ServerShowInfo, 0, len(servers))
|
||||||
for _, server := range servers {
|
for _, server := range servers {
|
||||||
if server.IsOpen != 1 {
|
if server.IsOpen != 1 {
|
||||||
@@ -103,24 +112,45 @@ func (s *ServerService) GetPort(DepartmentID uint) []ServerShowInfo {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items = append(items, ServerShowInfo{
|
items = append(items, ServerShowInfo{
|
||||||
OnlineID: server.OnlineID,
|
OnlineID: server.OnlineID,
|
||||||
Name: server.Name,
|
Name: server.Name,
|
||||||
IP: server.IP,
|
IP: server.IP,
|
||||||
Port: server.Port,
|
Port: server.Port,
|
||||||
IsVip: server.IsVip,
|
IsVip: server.IsVip,
|
||||||
IsDebug: server.IsDebug,
|
IsDebug: server.IsDebug,
|
||||||
IsOpen: server.IsOpen,
|
IsOpen: server.IsOpen,
|
||||||
Owner: server.Owner,
|
//Owner: 0,
|
||||||
ExpireTime: server.ExpireTime,
|
// ExpireTime: time.Time{},
|
||||||
ServerShow: server.ServerShow,
|
|
||||||
})
|
})
|
||||||
|
for i := range showMap[server.OnlineID] {
|
||||||
|
show := &showMap[server.OnlineID][i]
|
||||||
|
itemOnlineID := server.OnlineID
|
||||||
|
if show.ID > 0 {
|
||||||
|
itemOnlineID = uint32(show.ID)
|
||||||
|
}
|
||||||
|
item := ServerShowInfo{
|
||||||
|
OnlineID: itemOnlineID,
|
||||||
|
Name: server.Name,
|
||||||
|
IP: server.IP,
|
||||||
|
Port: server.Port,
|
||||||
|
IsVip: server.IsVip,
|
||||||
|
IsDebug: server.IsDebug,
|
||||||
|
IsOpen: server.IsOpen,
|
||||||
|
|
||||||
|
// ExpireTime: show.ExpireTime,
|
||||||
|
// ServerShow: show,
|
||||||
|
}
|
||||||
|
if show.Name != "" {
|
||||||
|
item.Name = show.Name
|
||||||
|
}
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetServer() []model.ServerList {
|
func (s *ServerService) GetServer() []model.ServerList {
|
||||||
var item []model.ServerList
|
item := s.getRawServers()
|
||||||
dbm_nocache_noenable(s.Model).With(model.ServerShow{}).Scan(&item)
|
|
||||||
s.applyServerShowList(item, time.Now())
|
s.applyServerShowList(item, time.Now())
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
@@ -149,38 +179,113 @@ func (s *ServerService) SetServerID(OnlineID uint32, Port uint32) error {
|
|||||||
|
|
||||||
func (s *ServerService) GetServerID(OnlineID uint32) model.ServerList {
|
func (s *ServerService) GetServerID(OnlineID uint32) model.ServerList {
|
||||||
var tttt model.ServerList
|
var tttt model.ServerList
|
||||||
dbm_nocache_noenable(s.Model).With(model.ServerShow{}).Where("online_id", OnlineID).Scan(&tttt)
|
dbm_nocache_noenable(s.Model).Where("online_id", OnlineID).Scan(&tttt)
|
||||||
s.applyServerShow(&tttt, time.Now())
|
showMap := s.getPrimaryActiveServerShowMap([]uint32{OnlineID}, time.Now())
|
||||||
|
s.applyServerShow(&tttt, showMap[OnlineID], time.Now())
|
||||||
return tttt
|
return tttt
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDonationAvailableServerIDs 返回当前可被冠名占用的服务器ID列表。
|
// GetDonationAvailableServerIDs 返回当前可被冠名占用的服务器ID列表。
|
||||||
func (s *ServerService) GetDonationAvailableServerIDs() []uint32 {
|
func (s *ServerService) GetDonationAvailableServerIDs() []uint32 {
|
||||||
servers := s.GetServer()
|
servers := s.getRawServers()
|
||||||
now := time.Now()
|
|
||||||
ids := make([]uint32, 0, len(servers))
|
ids := make([]uint32, 0, len(servers))
|
||||||
for _, server := range servers {
|
for _, server := range servers {
|
||||||
if server.OnlineID == 0 {
|
if server.OnlineID == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if s.isActiveServerShow(server.ServerShow, now) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ids = append(ids, server.OnlineID)
|
ids = append(ids, server.OnlineID)
|
||||||
}
|
}
|
||||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||||
return ids
|
return ids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ServerService) GetOwnerActiveDonationServers(ownerID uint32) []DonationOwnedServerInfo {
|
||||||
|
if ownerID == 0 {
|
||||||
|
return []DonationOwnedServerInfo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
var shows []model.ServerShow
|
||||||
|
dbm_nocache_noenable(model.NewServerShow()).Where("owner", ownerID).Scan(&shows)
|
||||||
|
if len(shows) == 0 {
|
||||||
|
return []DonationOwnedServerInfo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverIDs := make([]uint32, 0, len(shows))
|
||||||
|
for i := range shows {
|
||||||
|
if !s.isActiveServerShow(&shows[i], now) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
serverIDs = append(serverIDs, shows[i].ServerID)
|
||||||
|
}
|
||||||
|
if len(serverIDs) == 0 {
|
||||||
|
return []DonationOwnedServerInfo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var servers []model.ServerList
|
||||||
|
dbm_nocache_noenable(s.Model).WhereIn("online_id", serverIDs).Scan(&servers)
|
||||||
|
|
||||||
|
serverMap := make(map[uint32]model.ServerList, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
serverMap[servers[i].OnlineID] = servers[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
items := make([]DonationOwnedServerInfo, 0, len(serverIDs))
|
||||||
|
for i := range shows {
|
||||||
|
show := &shows[i]
|
||||||
|
if !s.isActiveServerShow(show, now) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
server, ok := serverMap[show.ServerID]
|
||||||
|
if !ok || show.ServerID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
serverName := show.Name
|
||||||
|
if serverName == "" {
|
||||||
|
serverName = server.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
items = append(items, DonationOwnedServerInfo{
|
||||||
|
ServerID: show.ServerID,
|
||||||
|
ServerName: serverName,
|
||||||
|
Remark: server.Desc,
|
||||||
|
ExpireTime: show.ExpireTime,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(items, func(i, j int) bool {
|
||||||
|
if !items[i].ExpireTime.Equal(items[j].ExpireTime) {
|
||||||
|
return items[i].ExpireTime.After(items[j].ExpireTime)
|
||||||
|
}
|
||||||
|
return items[i].ServerID < items[j].ServerID
|
||||||
|
})
|
||||||
|
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
// CanUseDonationName 校验目标服务器在当前时间点是否允许被冠名。
|
// CanUseDonationName 校验目标服务器在当前时间点是否允许被冠名。
|
||||||
func (s *ServerService) CanUseDonationName(server model.ServerList, ownerID uint32, now time.Time) bool {
|
func (s *ServerService) CanUseDonationName(server model.ServerList, ownerID uint32, now time.Time) bool {
|
||||||
if server.OnlineID == 0 {
|
return server.OnlineID != 0
|
||||||
return false
|
}
|
||||||
|
|
||||||
|
func (s *ServerService) getRawServers() []model.ServerList {
|
||||||
|
var item []model.ServerList
|
||||||
|
dbm_nocache_noenable(s.Model).Scan(&item)
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerService) collectServerIDs(servers []model.ServerList) []uint32 {
|
||||||
|
serverIDs := make([]uint32, 0, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
if servers[i].OnlineID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
serverIDs = append(serverIDs, servers[i].OnlineID)
|
||||||
}
|
}
|
||||||
if !s.isActiveServerShow(server.ServerShow, now) {
|
return serverIDs
|
||||||
return true
|
|
||||||
}
|
|
||||||
return server.ServerShow.Owner == ownerID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存版本号
|
// 保存版本号
|
||||||
@@ -244,29 +349,32 @@ func (s *ServerService) isActiveServerShow(show *model.ServerShow, now time.Time
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) applyServerShowList(servers []model.ServerList, now time.Time) {
|
func (s *ServerService) applyServerShowList(servers []model.ServerList, now time.Time) {
|
||||||
|
showMap := s.getPrimaryActiveServerShowMap(s.collectServerIDs(servers), now)
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
s.applyServerShow(&servers[i], now)
|
s.applyServerShow(&servers[i], showMap[servers[i].OnlineID], now)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) applyServerShow(server *model.ServerList, now time.Time) {
|
func (s *ServerService) applyServerShow(server *model.ServerList, show *model.ServerShow, now time.Time) {
|
||||||
if server == nil {
|
if server == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
server.ServerShow = nil
|
||||||
server.Owner = 0
|
server.Owner = 0
|
||||||
server.ExpireTime = time.Time{}
|
server.ExpireTime = time.Time{}
|
||||||
if !s.isActiveServerShow(server.ServerShow, now) {
|
if !s.isActiveServerShow(show, now) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if server.ServerShow.Name != "" {
|
server.ServerShow = show
|
||||||
server.Name = server.ServerShow.Name
|
if show.Name != "" {
|
||||||
|
server.Name = show.Name
|
||||||
}
|
}
|
||||||
server.Owner = server.ServerShow.Owner
|
server.Owner = show.Owner
|
||||||
server.ExpireTime = server.ServerShow.ExpireTime
|
server.ExpireTime = show.ExpireTime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) getActiveServerShowMap(serverIDs []uint32, now time.Time) map[uint32]*model.ServerShow {
|
func (s *ServerService) getActiveServerShowListMap(serverIDs []uint32, now time.Time) map[uint32][]model.ServerShow {
|
||||||
showMap := make(map[uint32]*model.ServerShow, len(serverIDs))
|
showMap := make(map[uint32][]model.ServerShow, len(serverIDs))
|
||||||
if len(serverIDs) == 0 {
|
if len(serverIDs) == 0 {
|
||||||
return showMap
|
return showMap
|
||||||
}
|
}
|
||||||
@@ -277,7 +385,30 @@ func (s *ServerService) getActiveServerShowMap(serverIDs []uint32, now time.Time
|
|||||||
if !s.isActiveServerShow(show, now) {
|
if !s.isActiveServerShow(show, now) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
showMap[show.ServerID] = show
|
showMap[show.ServerID] = append(showMap[show.ServerID], *show)
|
||||||
|
}
|
||||||
|
for serverID := range showMap {
|
||||||
|
sort.Slice(showMap[serverID], func(i, j int) bool {
|
||||||
|
left := showMap[serverID][i]
|
||||||
|
right := showMap[serverID][j]
|
||||||
|
if !left.ExpireTime.Equal(right.ExpireTime) {
|
||||||
|
return left.ExpireTime.After(right.ExpireTime)
|
||||||
|
}
|
||||||
|
return left.ID > right.ID
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return showMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerService) getPrimaryActiveServerShowMap(serverIDs []uint32, now time.Time) map[uint32]*model.ServerShow {
|
||||||
|
listMap := s.getActiveServerShowListMap(serverIDs, now)
|
||||||
|
showMap := make(map[uint32]*model.ServerShow, len(listMap))
|
||||||
|
for serverID := range listMap {
|
||||||
|
if len(listMap[serverID]) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
show := listMap[serverID][0]
|
||||||
|
showMap[serverID] = &show
|
||||||
}
|
}
|
||||||
return showMap
|
return showMap
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ func NewShinyService() *ShinyService {
|
|||||||
return &ShinyService{
|
return &ShinyService{
|
||||||
&cool.Service{
|
&cool.Service{
|
||||||
Model: model.NewColorfulSkin(),
|
Model: model.NewColorfulSkin(),
|
||||||
|
ListQueryOp: &cool.QueryOp{
|
||||||
|
Where: func(ctx context.Context) []g.Array {
|
||||||
|
return []g.Array{
|
||||||
|
{"is_enable", 1},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
InsertParam: func(ctx context.Context) g.MapStrAny {
|
InsertParam: func(ctx context.Context) g.MapStrAny {
|
||||||
admin := cool.GetAdmin(ctx)
|
admin := cool.GetAdmin(ctx)
|
||||||
userId := admin.UserId
|
userId := admin.UserId
|
||||||
|
|||||||
77
modules/config/service/spt.go
Normal file
77
modules/config/service/spt.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/cool"
|
||||||
|
"blazing/modules/config/model"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SptService struct {
|
||||||
|
*cool.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSptService() *SptService {
|
||||||
|
return &SptService{
|
||||||
|
&cool.Service{
|
||||||
|
Model: model.NewSptConfig(),
|
||||||
|
ListQueryOp: &cool.QueryOp{
|
||||||
|
FieldEQ: []string{"is_enable"},
|
||||||
|
AddOrderby: map[string]string{
|
||||||
|
"task_id": "asc",
|
||||||
|
},
|
||||||
|
ModifyResult: func(ctx g.Ctx, data interface{}) interface{} {
|
||||||
|
return syncSptAcquireByEnable(data)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PageQueryOp: &cool.QueryOp{
|
||||||
|
FieldEQ: []string{"is_enable", "task_id"},
|
||||||
|
KeyWordField: []string{"title", "description", "remark"},
|
||||||
|
AddOrderby: map[string]string{
|
||||||
|
"task_id": "asc",
|
||||||
|
},
|
||||||
|
ModifyResult: func(ctx g.Ctx, data interface{}) interface{} {
|
||||||
|
return syncSptAcquireByEnable(data)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SptService) ModifyBefore(ctx context.Context, method string, param g.MapStrAny) (err error) {
|
||||||
|
switch method {
|
||||||
|
case "Add", "Update":
|
||||||
|
if isEnable, ok := param["is_enable"]; ok {
|
||||||
|
enableVal := gconv.Int32(isEnable)
|
||||||
|
param["online"] = enableVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func syncSptAcquireByEnable(data interface{}) interface{} {
|
||||||
|
if data == nil {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
if pageMap, ok := data.(g.Map); ok {
|
||||||
|
if rawList, hasList := pageMap["list"]; hasList {
|
||||||
|
pageMap["list"] = syncSptRows(rawList)
|
||||||
|
}
|
||||||
|
return pageMap
|
||||||
|
}
|
||||||
|
|
||||||
|
return syncSptRows(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func syncSptRows(raw interface{}) interface{} {
|
||||||
|
rows := gconv.SliceMap(raw)
|
||||||
|
for _, row := range rows {
|
||||||
|
enableVal := gconv.Int32(row["is_enable"])
|
||||||
|
row["online"] = enableVal
|
||||||
|
row["can_get"] = enableVal
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
}
|
||||||
@@ -26,6 +26,9 @@ func NewTaskService() *TaskService {
|
|||||||
func (s *TaskService) Get(id, os int) *model.TaskConfig {
|
func (s *TaskService) Get(id, os int) *model.TaskConfig {
|
||||||
var res *model.TaskConfig
|
var res *model.TaskConfig
|
||||||
dbm_enable(s.Model).Where("task_id", id).Where("out_state", os).Scan(&res)
|
dbm_enable(s.Model).Where("task_id", id).Where("out_state", os).Scan(&res)
|
||||||
|
if res == nil {
|
||||||
|
dbm_notenable(s.Model).Where("task_id", id).Where("out_state", os).Scan(&res)
|
||||||
|
}
|
||||||
// var res *model.TaskConfig
|
// var res *model.TaskConfig
|
||||||
// for _, v := range item {
|
// for _, v := range item {
|
||||||
// if v.OutState == os {
|
// if v.OutState == os {
|
||||||
@@ -41,6 +44,9 @@ func (s *TaskService) Get(id, os int) *model.TaskConfig {
|
|||||||
func (s *TaskService) GetDaily() []model.TaskConfig {
|
func (s *TaskService) GetDaily() []model.TaskConfig {
|
||||||
var item []model.TaskConfig
|
var item []model.TaskConfig
|
||||||
dbm_enable(s.Model).Where("task_type", 1).Scan(&item)
|
dbm_enable(s.Model).Where("task_type", 1).Scan(&item)
|
||||||
|
if len(item) == 0 {
|
||||||
|
dbm_notenable(s.Model).Where("task_type", 1).Scan(&item)
|
||||||
|
}
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
@@ -48,13 +54,19 @@ func (s *TaskService) GetDaily() []model.TaskConfig {
|
|||||||
func (s *TaskService) GetWeek() []model.TaskConfig {
|
func (s *TaskService) GetWeek() []model.TaskConfig {
|
||||||
var item []model.TaskConfig
|
var item []model.TaskConfig
|
||||||
dbm_enable(s.Model).Where("task_type", 2).Scan(&item)
|
dbm_enable(s.Model).Where("task_type", 2).Scan(&item)
|
||||||
|
if len(item) == 0 {
|
||||||
|
dbm_notenable(s.Model).Where("task_type", 2).Scan(&item)
|
||||||
|
}
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
}
|
}
|
||||||
func (s *TaskService) IsDaily(id, os int) bool {
|
func (s *TaskService) IsDaily(id, os int) bool {
|
||||||
var item *model.TaskConfig
|
var item *model.TaskConfig
|
||||||
dbm_enable(s.Model).Where("task_id", id).Where("out_state", os).Scan(item)
|
dbm_enable(s.Model).Where("task_id", id).Where("out_state", os).Scan(&item)
|
||||||
|
if item == nil {
|
||||||
|
dbm_notenable(s.Model).Where("task_id", id).Where("out_state", os).Scan(&item)
|
||||||
|
}
|
||||||
if item == nil {
|
if item == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
|
baseservice "blazing/modules/base/service"
|
||||||
configservice "blazing/modules/config/service"
|
configservice "blazing/modules/config/service"
|
||||||
playerservice "blazing/modules/player/service"
|
playerservice "blazing/modules/player/service"
|
||||||
"context"
|
"context"
|
||||||
@@ -31,6 +32,10 @@ type DonationServerListReq struct {
|
|||||||
g.Meta `path:"/donation/serverIds" method:"GET"`
|
g.Meta `path:"/donation/serverIds" method:"GET"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DonationCurrentReq struct {
|
||||||
|
g.Meta `path:"/donation/current" method:"GET"`
|
||||||
|
}
|
||||||
|
|
||||||
type DonationServerInfoReq struct {
|
type DonationServerInfoReq struct {
|
||||||
g.Meta `path:"/donation/serverInfo" method:"GET"`
|
g.Meta `path:"/donation/serverInfo" method:"GET"`
|
||||||
ServerID uint32 `json:"server_id" v:"required|min:1#服务器ID不能为空|服务器ID非法"`
|
ServerID uint32 `json:"server_id" v:"required|min:1#服务器ID不能为空|服务器ID非法"`
|
||||||
@@ -68,6 +73,18 @@ func (c *CdkController) DonationServerIDs(ctx context.Context, req *DonationServ
|
|||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DonationCurrent 查询当前账号名下仍在有效期内的服务器冠名信息。
|
||||||
|
func (c *CdkController) DonationCurrent(ctx context.Context, req *DonationCurrentReq) (res *cool.BaseRes, err error) {
|
||||||
|
admin := cool.GetAdmin(ctx)
|
||||||
|
if admin == nil || admin.UserId == 0 {
|
||||||
|
return cool.Fail("未登录或登录已失效"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cool.Ok(g.Map{
|
||||||
|
"list": configservice.NewServerService().GetOwnerActiveDonationServers(uint32(admin.UserId)),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
// DonationServerInfo 查询冠名兑换前展示的服务器名称与备注。
|
// DonationServerInfo 查询冠名兑换前展示的服务器名称与备注。
|
||||||
func (c *CdkController) DonationServerInfo(ctx context.Context, req *DonationServerInfoReq) (res *cool.BaseRes, err error) {
|
func (c *CdkController) DonationServerInfo(ctx context.Context, req *DonationServerInfoReq) (res *cool.BaseRes, err error) {
|
||||||
if err = g.Validator().Data(req).Run(ctx); err != nil {
|
if err = g.Validator().Data(req).Run(ctx); err != nil {
|
||||||
@@ -102,6 +119,10 @@ func (c *CdkController) DonationRedeem(ctx context.Context, req *DonationRedeemR
|
|||||||
return cool.Fail("未登录或登录已失效"), nil
|
return cool.Fail("未登录或登录已失效"), nil
|
||||||
}
|
}
|
||||||
ownerID := uint32(admin.UserId)
|
ownerID := uint32(admin.UserId)
|
||||||
|
user := baseservice.NewBaseSysUserService().GetPerson(ownerID)
|
||||||
|
if user == nil || user.QQ == 0 {
|
||||||
|
return cool.Fail("请先绑定QQ"), nil
|
||||||
|
}
|
||||||
|
|
||||||
cdkCode := strings.TrimSpace(req.CDKCode)
|
cdkCode := strings.TrimSpace(req.CDKCode)
|
||||||
if cdkCode == "" {
|
if cdkCode == "" {
|
||||||
@@ -119,8 +140,8 @@ func (c *CdkController) DonationRedeem(ctx context.Context, req *DonationRedeemR
|
|||||||
if cdkInfo == nil {
|
if cdkInfo == nil {
|
||||||
return cool.Fail("CDK不存在或已被使用"), nil
|
return cool.Fail("CDK不存在或已被使用"), nil
|
||||||
}
|
}
|
||||||
if cdkInfo.CDKType != configservice.CDKTypeServerNaming {
|
if cdkInfo.Type != configservice.CDKTypeServerNaming {
|
||||||
return cool.Fail("CDK类型不匹配"), nil
|
return cool.Fail("当前页面仅支持服务器冠名CDK,请确认输入的是服务器冠名类型"), nil
|
||||||
}
|
}
|
||||||
if cdkInfo.BindUserId != 0 && cdkInfo.BindUserId != ownerID {
|
if cdkInfo.BindUserId != 0 && cdkInfo.BindUserId != ownerID {
|
||||||
return cool.Fail("CDK已绑定其他用户"), nil
|
return cool.Fail("CDK已绑定其他用户"), nil
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ func init() {
|
|||||||
ctx.Send("扭蛋币不足,当前扭蛋币数量:" + gconv.String(havs))
|
ctx.Send("扭蛋币不足,当前扭蛋币数量:" + gconv.String(havs))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := itemService.UPDATE(400501, -count); err != nil {
|
||||||
|
ctx.Send("扭蛋币不足,当前扭蛋币数量:" + gconv.String(havs))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
buf.WriteString("当前扭蛋币数量:" + gconv.String(havs) + "\n")
|
buf.WriteString("当前扭蛋币数量:" + gconv.String(havs) + "\n")
|
||||||
@@ -140,8 +144,6 @@ func init() {
|
|||||||
buf.WriteString("恭喜你获得 " + xmlres.ItemsMAP[int(item.ItemId)].Name + ":" + gconv.String(item.ItemCnt) + "\n")
|
buf.WriteString("恭喜你获得 " + xmlres.ItemsMAP[int(item.ItemId)].Name + ":" + gconv.String(item.ItemCnt) + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
itemService.UPDATE(400501, -count)
|
|
||||||
|
|
||||||
ctx.SendChain(message.At(ctx.Event.Sender.ID), message.Reply(ctx.Event.MessageID), message.Text(buf.String()))
|
ctx.SendChain(message.At(ctx.Event.Sender.ID), message.Reply(ctx.Event.MessageID), message.Text(buf.String()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ func (m *PlayerInfo) SetTask(i int, status TaskStatus) error {
|
|||||||
// GetTask 获取第 i 个任务的状态
|
// GetTask 获取第 i 个任务的状态
|
||||||
func (m *PlayerInfo) GetTask(i int) TaskStatus {
|
func (m *PlayerInfo) GetTask(i int) TaskStatus {
|
||||||
i-- //下标减1
|
i-- //下标减1
|
||||||
if i < 0 || i >= 2000 {
|
if i < 0 || i >= 4000 {
|
||||||
return Reserved
|
return Reserved
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,16 +199,18 @@ type PlayerInfo struct {
|
|||||||
Nick string `struc:"[16]byte" default:"nono" json:"nono_nick"` // nono名字(16字节)
|
Nick string `struc:"[16]byte" default:"nono" json:"nono_nick"` // nono名字(16字节)
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamInfo TeamInfo `struc:"struct" json:"team_info"` // 战队信息24字节
|
TeamInfo TeamInfo `struc:"struct" json:"team_info"` // 战队信息24字节
|
||||||
TeamPkInfo TeamPKInfo `struc:"struct" json:"team_pk_info"` // 8字节
|
TeamPkInfo TeamPKInfo `struc:"struct" json:"team_pk_info"` // 8字节
|
||||||
Reserved byte `struc:"byte" json:"reserved"` // 1字节无内容
|
Reserved byte `struc:"byte" json:"reserved"` // 1字节无内容
|
||||||
Badge uint32 `struc:"uint32" default:"0" json:"badge"` // 默认0
|
Badge uint32 `struc:"uint32" default:"0" json:"badge"` // 默认0
|
||||||
Reserved1 [27]byte `struc:"[27]byte" default:"3" json:"reserved1"` // 27字节默认3
|
Reserved1 [27]byte `struc:"[27]byte" default:"3" json:"reserved1"` // 27字节默认3
|
||||||
TaskList [1000]byte `struc:"[1000]byte" default:"0" json:"task_list"` // 任务状态数组500字节,默认3
|
TaskList [1000]byte `struc:"[1000]byte" default:"0" json:"task_list"` // 任务状态数组500字节,默认3
|
||||||
PetList []PetInfo `struc:"skip" json:"pet_list"` // 精灵背包内信息(不再走旧登录包体)
|
PetListLen uint32 `struc:"uint32,sizeof=PetList" json:"-"` // 登录包体中的出战精灵数量
|
||||||
BackupPetList []PetInfo `struc:"skip" json:"backup_pet_list"` // 精灵并列备用列表
|
PetList []PetInfo `json:"pet_list"` // 精灵背包内信息
|
||||||
ClothesCount uint32 `struc:"sizeof=Clothes" json:"clothes_count"` // 穿戴装备数量
|
BackupPetListLen uint32 `struc:"uint32,sizeof=BackupPetList" json:"-"` // 登录包体中的备用精灵数量
|
||||||
Clothes []PeopleItemInfo ` json:"clothes"` // 穿戴装备
|
BackupPetList []PetInfo `json:"backup_pet_list"` // 精灵并列备用列表
|
||||||
|
ClothesCount uint32 `struc:"sizeof=Clothes" json:"clothes_count"` // 穿戴装备数量
|
||||||
|
Clothes []PeopleItemInfo ` json:"clothes"` // 穿戴装备
|
||||||
}
|
}
|
||||||
|
|
||||||
// trace("个人装扮是否半价:",MainManager.isClothHalfDay);
|
// trace("个人装扮是否半价:",MainManager.isClothHalfDay);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"blazing/common/data/xmlres"
|
"blazing/common/data/xmlres"
|
||||||
"blazing/common/utils"
|
"blazing/common/utils"
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"blazing/modules/config/model"
|
"blazing/modules/config/model"
|
||||||
"blazing/modules/config/service"
|
"blazing/modules/config/service"
|
||||||
@@ -154,11 +153,13 @@ type PetInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pet *PetInfo) ConfigBoss(bm model.PetBaseConfig) {
|
func (pet *PetInfo) ConfigBoss(bm model.PetBaseConfig) {
|
||||||
var color data.GlowFilter
|
if bm.ColorID > 0 {
|
||||||
err := json.Unmarshal([]byte(bm.Color), &color)
|
if color := service.NewShinyService().GetShiny(int(bm.ColorID)); color != nil {
|
||||||
if err == nil && color.Alpha != 0 {
|
pet.ShinyInfo = append(pet.ShinyInfo, *color)
|
||||||
pet.ShinyInfo = append(pet.ShinyInfo, color)
|
}
|
||||||
|
}
|
||||||
|
if bm.Skin > 0 {
|
||||||
|
pet.SkinID = uint32(bm.Skin)
|
||||||
}
|
}
|
||||||
if bm.Hp != 0 {
|
if bm.Hp != 0 {
|
||||||
pet.Hp = uint32(bm.Hp)
|
pet.Hp = uint32(bm.Hp)
|
||||||
@@ -178,7 +179,7 @@ func (pet *PetInfo) ConfigBoss(bm model.PetBaseConfig) {
|
|||||||
|
|
||||||
if len(bm.SKill) != 0 {
|
if len(bm.SKill) != 0 {
|
||||||
|
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4 && i < len(bm.SKill); i++ {
|
||||||
if bm.SKill[i] != 0 {
|
if bm.SKill[i] != 0 {
|
||||||
pet.SkillList[i].ID = bm.SKill[i]
|
pet.SkillList[i].ID = bm.SKill[i]
|
||||||
}
|
}
|
||||||
@@ -532,18 +533,20 @@ func (petinfo *PetInfo) Update(isup bool) {
|
|||||||
if evolveCount >= maxEvolveTimes {
|
if evolveCount >= maxEvolveTimes {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// 进化完成后,统一更新经验(原逻辑保留)
|
|
||||||
petinfo.LvExp = petinfo.NextLvExp
|
|
||||||
// 获取当前宠物形态的配置
|
// 获取当前宠物形态的配置
|
||||||
basic, ok := xmlres.PetMAP[int(petinfo.ID)]
|
basic, ok := xmlres.PetMAP[int(petinfo.ID)]
|
||||||
// 配置不存在,直接退出循环
|
// 配置不存在,直接退出循环
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
petinfo.NextLvExp = calculateExperience(petinfo.Level, basic.GetBasic())
|
|
||||||
if !isup {
|
if !isup {
|
||||||
|
petinfo.LvExp = calculatePreviousLevelExperience(petinfo.Level, basic.GetBasic())
|
||||||
|
petinfo.NextLvExp = calculateExperience(petinfo.Level, basic.GetBasic())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 升级时保留上一等级需求,供经验结算使用。
|
||||||
|
petinfo.LvExp = petinfo.NextLvExp
|
||||||
|
petinfo.NextLvExp = calculateExperience(petinfo.Level, basic.GetBasic())
|
||||||
// 检查是否满足进化条件
|
// 检查是否满足进化条件
|
||||||
canEvolve := basic.EvolvesTo != 0 && // 有明确的进化目标
|
canEvolve := basic.EvolvesTo != 0 && // 有明确的进化目标
|
||||||
int(petinfo.Level) >= basic.EvolvingLv && // 等级达到进化要求
|
int(petinfo.Level) >= basic.EvolvingLv && // 等级达到进化要求
|
||||||
@@ -561,6 +564,13 @@ func (petinfo *PetInfo) Update(isup bool) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calculatePreviousLevelExperience(level uint32, baseValue uint32) int64 {
|
||||||
|
if level <= 1 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return calculateExperience(level-1, baseValue)
|
||||||
|
}
|
||||||
|
|
||||||
// calculateExperience 计算指定等级和种族值所需的经验值
|
// calculateExperience 计算指定等级和种族值所需的经验值
|
||||||
// level: 当前等级
|
// level: 当前等级
|
||||||
// baseValue: 种族值
|
// baseValue: 种族值
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ type CdkRewardResult struct {
|
|||||||
EVPool int64 `json:"ev_pool,omitempty"`
|
EVPool int64 `json:"ev_pool,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CdkSpecialRewardAdder func(itemID uint32, count int64) bool
|
||||||
|
|
||||||
// GrantConfigReward 按 cdk 配置 ID 发放奖励,不处理兑换码次数和领取资格校验。
|
// GrantConfigReward 按 cdk 配置 ID 发放奖励,不处理兑换码次数和领取资格校验。
|
||||||
func (s *CdkService) GrantConfigReward(cdkID uint32) (*CdkRewardResult, error) {
|
// 当传入 specialAdder 时,赛尔豆/累计经验/金豆/学习力会优先走在线玩家加成逻辑。
|
||||||
|
func (s *CdkService) GrantConfigReward(cdkID uint32, specialAdders ...CdkSpecialRewardAdder) (*CdkRewardResult, error) {
|
||||||
cfg := configservice.NewCdkService().GetByID(cdkID)
|
cfg := configservice.NewCdkService().GetByID(cdkID)
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil, fmt.Errorf("绑定的CDK不存在")
|
return nil, fmt.Errorf("绑定的CDK不存在")
|
||||||
@@ -45,42 +48,72 @@ func (s *CdkService) GrantConfigReward(cdkID uint32) (*CdkRewardResult, error) {
|
|||||||
if playerInfo == nil {
|
if playerInfo == nil {
|
||||||
return nil, fmt.Errorf("玩家角色不存在")
|
return nil, fmt.Errorf("玩家角色不存在")
|
||||||
}
|
}
|
||||||
|
var specialAdder CdkSpecialRewardAdder
|
||||||
|
if len(specialAdders) > 0 {
|
||||||
|
specialAdder = specialAdders[0]
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
infoDirty bool
|
infoDirty bool
|
||||||
bagItems []data.ItemInfo
|
bagItems []data.ItemInfo
|
||||||
)
|
)
|
||||||
|
|
||||||
appendRewardItem := func(itemID uint32, count int64) {
|
appendRewardItem := func(itemID uint32, count int64) error {
|
||||||
if itemID == 0 || count <= 0 {
|
if itemID == 0 || count <= 0 {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
switch itemID {
|
switch itemID {
|
||||||
case 1:
|
case 1:
|
||||||
result.Coins += count
|
result.Coins += count
|
||||||
playerInfo.Coins += count
|
if specialAdder != nil {
|
||||||
infoDirty = true
|
if !specialAdder(itemID, count) {
|
||||||
|
return fmt.Errorf("在线发放赛尔豆失败")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
playerInfo.Coins += count
|
||||||
|
infoDirty = true
|
||||||
|
}
|
||||||
case 3:
|
case 3:
|
||||||
result.ExpPool += count
|
result.ExpPool += count
|
||||||
playerInfo.ExpPool += count
|
if specialAdder != nil {
|
||||||
infoDirty = true
|
if !specialAdder(itemID, count) {
|
||||||
|
return fmt.Errorf("在线发放经验池失败")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
playerInfo.ExpPool += count
|
||||||
|
infoDirty = true
|
||||||
|
}
|
||||||
case 5:
|
case 5:
|
||||||
result.Gold += count
|
result.Gold += count
|
||||||
|
if specialAdder != nil {
|
||||||
|
if !specialAdder(itemID, count) {
|
||||||
|
return fmt.Errorf("在线发放金豆失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
case 9:
|
case 9:
|
||||||
result.EVPool += count
|
result.EVPool += count
|
||||||
playerInfo.EVPool += count
|
if specialAdder != nil {
|
||||||
infoDirty = true
|
if !specialAdder(itemID, count) {
|
||||||
|
return fmt.Errorf("在线发放学习力失败")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
playerInfo.EVPool += count
|
||||||
|
infoDirty = true
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
bagItems = append(bagItems, data.ItemInfo{ItemId: int64(itemID), ItemCnt: count})
|
bagItems = append(bagItems, data.ItemInfo{ItemId: int64(itemID), ItemCnt: count})
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rewardID := range cfg.ItemRewardIds {
|
for _, rewardID := range cfg.ItemRewardIds {
|
||||||
itemInfo := configservice.NewItemService().GetItemCount(rewardID)
|
itemInfo := configservice.NewItemService().GetItemCount(rewardID)
|
||||||
appendRewardItem(uint32(itemInfo.ItemId), itemInfo.ItemCnt)
|
if err := appendRewardItem(uint32(itemInfo.ItemId), itemInfo.ItemCnt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Gold != 0 {
|
if result.Gold != 0 && specialAdder == nil {
|
||||||
baseservice.NewBaseSysUserService().UpdateGold(s.userid, result.Gold*100)
|
baseservice.NewBaseSysUserService().UpdateGold(s.userid, result.Gold*100)
|
||||||
}
|
}
|
||||||
if result.FreeGold != 0 {
|
if result.FreeGold != 0 {
|
||||||
|
|||||||
@@ -110,11 +110,19 @@ func buildJSONBArrayAnyCondition(column string, values []uint32) (string, []any)
|
|||||||
clauses := make([]string, 0, len(values)+1)
|
clauses := make([]string, 0, len(values)+1)
|
||||||
params := make([]any, 0, len(values)+1)
|
params := make([]any, 0, len(values)+1)
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
clauses = append(clauses, fmt.Sprintf(`CAST(? AS text) = ANY(ARRAY(SELECT jsonb_array_elements_text(%s)))`, column))
|
clauses = append(clauses, fmt.Sprintf(
|
||||||
|
`EXISTS (SELECT 1 FROM jsonb_array_elements_text(CASE WHEN jsonb_typeof(%s) = 'array' THEN %s ELSE '[]'::jsonb END) AS elem WHERE elem = CAST(? AS text))`,
|
||||||
|
column,
|
||||||
|
column,
|
||||||
|
))
|
||||||
params = append(params, value)
|
params = append(params, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
clauses = append(clauses, fmt.Sprintf(`jsonb_array_length(%s) = ?`, column))
|
clauses = append(clauses, fmt.Sprintf(
|
||||||
|
`CASE WHEN jsonb_typeof(%s) = 'array' THEN jsonb_array_length(%s) ELSE -1 END = ?`,
|
||||||
|
column,
|
||||||
|
column,
|
||||||
|
))
|
||||||
params = append(params, len(values))
|
params = append(params, len(values))
|
||||||
|
|
||||||
return strings.Join(clauses, " AND "), params
|
return strings.Join(clauses, " AND "), params
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
dictservice "blazing/modules/dict/service"
|
dictservice "blazing/modules/dict/service"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/database/gdb"
|
"github.com/gogf/gf/v2/database/gdb"
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -58,12 +59,28 @@ func (s *ItemService) UPDATE(id uint32, count int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
_, err := s.dbm(s.Model).Where("item_id", id).Increment("item_cnt", count)
|
updateModel := s.dbm(s.Model).Where("item_id", id)
|
||||||
|
if count < 0 {
|
||||||
|
updateModel = updateModel.Where("item_cnt + ? >= 0", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := updateModel.Increment("item_cnt", count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
affected, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if affected == 0 {
|
||||||
|
return gerror.New("item update failed: no rows affected")
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if count <= 0 {
|
||||||
|
return gerror.New("item update failed: cannot insert non-positive item count")
|
||||||
|
}
|
||||||
m := s.dbm(s.Model)
|
m := s.dbm(s.Model)
|
||||||
data := g.Map{
|
data := g.Map{
|
||||||
"player_id": s.userid,
|
"player_id": s.userid,
|
||||||
|
|||||||
@@ -76,8 +76,12 @@ func (s *PetService) PetCount(flag int) int {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PetService) UpdateFree(catchTime, free uint32) bool {
|
func (s *PetService) UpdateFree(catchTime, fromFree, toFree uint32) bool {
|
||||||
res, err := s.dbm(s.Model).Where("catch_time", catchTime).Data("free", free).Update()
|
res, err := s.dbm(s.Model).
|
||||||
|
Where("catch_time", catchTime).
|
||||||
|
Where("free", fromFree).
|
||||||
|
Data("free", toFree).
|
||||||
|
Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
297
modules/player/service/pet_fusion_tx.go
Normal file
297
modules/player/service/pet_fusion_tx.go
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/common/socket/errorcode"
|
||||||
|
"blazing/cool"
|
||||||
|
baseservice "blazing/modules/base/service"
|
||||||
|
"blazing/modules/player/model"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/database/gdb"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errPetFusionInsufficientItems = errors.New("pet fusion insufficient items")
|
||||||
|
errPetFusionPetNotFound = errors.New("pet fusion pet not found")
|
||||||
|
errPetFusionPlayerNotFound = errors.New("pet fusion player not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
type PetFusionTxResult struct {
|
||||||
|
NewPet *model.PetInfo
|
||||||
|
CostItemUsed bool
|
||||||
|
UpdatedAux *model.PetInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserService) PetFusionTx(
|
||||||
|
currentInfo model.PlayerInfo,
|
||||||
|
masterCatchTime uint32,
|
||||||
|
auxCatchTime uint32,
|
||||||
|
materialCounts map[uint32]int,
|
||||||
|
goldItemIDs []uint32,
|
||||||
|
keepAuxItemID uint32,
|
||||||
|
failureItemID uint32,
|
||||||
|
cost int64,
|
||||||
|
newPet *model.PetInfo,
|
||||||
|
failedAux *model.PetInfo,
|
||||||
|
) (*PetFusionTxResult, errorcode.ErrorCode) {
|
||||||
|
if s == nil || s.Pet == nil {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := s.Pet.userid
|
||||||
|
nextInfo := currentInfo
|
||||||
|
if nextInfo.Coins < cost {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
|
||||||
|
}
|
||||||
|
nextInfo.Coins -= cost
|
||||||
|
|
||||||
|
result := &PetFusionTxResult{}
|
||||||
|
err := g.DB().Transaction(context.TODO(), func(ctx context.Context, tx gdb.TX) error {
|
||||||
|
if err := updatePlayerInfoTx(tx, userID, nextInfo); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := consumeItemCountsTx(tx, userID, materialCounts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if newPet == nil {
|
||||||
|
used, err := consumeOptionalItemTx(tx, userID, goldItemIDs, failureItemID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result.CostItemUsed = used
|
||||||
|
if !used && failedAux != nil {
|
||||||
|
if err := updatePetDataTx(tx, userID, auxCatchTime, *failedAux); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
auxCopy := *failedAux
|
||||||
|
result.UpdatedAux = &auxCopy
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syncPetSnapshotBeforeDeleteTx(tx, userID, currentInfo, masterCatchTime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := deletePetTx(tx, userID, masterCatchTime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
used, err := consumeOptionalItemTx(tx, userID, goldItemIDs, keepAuxItemID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result.CostItemUsed = used
|
||||||
|
if !used {
|
||||||
|
if err := syncPetSnapshotBeforeDeleteTx(tx, userID, currentInfo, auxCatchTime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := deletePetTx(tx, userID, auxCatchTime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catchTime, err := addPetTx(tx, s.Pet, newPet, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
petCopy := *newPet
|
||||||
|
petCopy.CatchTime = catchTime
|
||||||
|
result.NewPet = &petCopy
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
return result, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, errPetFusionInsufficientItems):
|
||||||
|
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||||
|
case errors.Is(err, errPetFusionPetNotFound):
|
||||||
|
return nil, errorcode.ErrorCodes.ErrPokemonNotFusionReady2
|
||||||
|
case errors.Is(err, errPetFusionPlayerNotFound):
|
||||||
|
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||||
|
default:
|
||||||
|
cool.Logger.Error(context.TODO(), "pet fusion tx failed", userID, err)
|
||||||
|
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePlayerInfoTx(tx gdb.TX, userID uint32, info model.PlayerInfo) error {
|
||||||
|
res, err := tx.Model(model.NewPlayer()).Where("player_id", userID).Data("data", info).Update()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
affected, err := res.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if affected == 0 {
|
||||||
|
return errPetFusionPlayerNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func consumeItemCountsTx(tx gdb.TX, userID uint32, itemCounts map[uint32]int) error {
|
||||||
|
for itemID, count := range itemCounts {
|
||||||
|
if err := updateItemCountTx(tx, userID, itemID, -count); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func consumeOptionalItemTx(tx gdb.TX, userID uint32, itemIDs []uint32, target uint32) (bool, error) {
|
||||||
|
for _, itemID := range itemIDs {
|
||||||
|
if itemID != target {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := updateItemCountTx(tx, userID, target, -1); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateItemCountTx(tx gdb.TX, userID uint32, id uint32, count int) error {
|
||||||
|
if cool.Config.ServerInfo.IsVip != 0 && count < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if id == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
baseModel := tx.Model(model.NewPlayerBag()).
|
||||||
|
Where("player_id", userID).
|
||||||
|
Where("is_vip", cool.Config.ServerInfo.IsVip)
|
||||||
|
|
||||||
|
ok, err := baseModel.Where("item_id", id).Exist()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
updateModel := tx.Model(model.NewPlayerBag()).
|
||||||
|
Where("player_id", userID).
|
||||||
|
Where("is_vip", cool.Config.ServerInfo.IsVip).
|
||||||
|
Where("item_id", id)
|
||||||
|
if count < 0 {
|
||||||
|
updateModel = updateModel.Where("item_cnt + ? >= 0", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := updateModel.Increment("item_cnt", count)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
affected, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if affected == 0 {
|
||||||
|
return errPetFusionInsufficientItems
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if count <= 0 {
|
||||||
|
return errPetFusionInsufficientItems
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Model(model.NewPlayerBag()).Data(g.Map{
|
||||||
|
"player_id": userID,
|
||||||
|
"item_id": id,
|
||||||
|
"item_cnt": count,
|
||||||
|
"is_vip": cool.Config.ServerInfo.IsVip,
|
||||||
|
}).Insert()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePetDataTx(tx gdb.TX, userID uint32, catchTime uint32, pet model.PetInfo) error {
|
||||||
|
res, err := tx.Model(model.NewPet()).
|
||||||
|
Where("player_id", userID).
|
||||||
|
Where("is_vip", cool.Config.ServerInfo.IsVip).
|
||||||
|
Where("catch_time", catchTime).
|
||||||
|
Data("data", pet).
|
||||||
|
Update()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
affected, err := res.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if affected == 0 {
|
||||||
|
return errPetFusionPetNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func syncPetSnapshotBeforeDeleteTx(tx gdb.TX, userID uint32, info model.PlayerInfo, catchTime uint32) error {
|
||||||
|
pet, ok := findPetDataSnapshotInPlayerInfo(info, catchTime)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return updatePetDataTx(tx, userID, catchTime, pet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPetDataSnapshotInPlayerInfo(info model.PlayerInfo, catchTime uint32) (model.PetInfo, bool) {
|
||||||
|
for i := range info.PetList {
|
||||||
|
if info.PetList[i].CatchTime == catchTime {
|
||||||
|
return info.PetList[i], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range info.BackupPetList {
|
||||||
|
if info.BackupPetList[i].CatchTime == catchTime {
|
||||||
|
return info.BackupPetList[i], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model.PetInfo{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func deletePetTx(tx gdb.TX, userID uint32, catchTime uint32) error {
|
||||||
|
res, err := tx.Model(model.NewPet()).
|
||||||
|
Where("player_id", userID).
|
||||||
|
Where("is_vip", cool.Config.ServerInfo.IsVip).
|
||||||
|
Where("catch_time", catchTime).
|
||||||
|
Delete()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
affected, err := res.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if affected == 0 {
|
||||||
|
return errPetFusionPetNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPetTx(tx gdb.TX, petService *PetService, petInfo *model.PetInfo, saleCount uint32) (uint32, error) {
|
||||||
|
if petInfo == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
catchTime, err := petService.nextCatchTime(tx.Model(baseservice.NewBaseSysUserService().Model))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
petCopy := *petInfo
|
||||||
|
petCopy.CatchTime = catchTime
|
||||||
|
playerPet := model.Pet{
|
||||||
|
PlayerID: petService.userid,
|
||||||
|
Data: petCopy,
|
||||||
|
CatchTime: catchTime,
|
||||||
|
Free: 0,
|
||||||
|
SaleCount: saleCount,
|
||||||
|
}
|
||||||
|
playerPet.IsVip = cool.Config.ServerInfo.IsVip
|
||||||
|
if _, err := tx.Model(model.NewPet()).Insert(playerPet); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return catchTime, nil
|
||||||
|
}
|
||||||
50
modules/player/service/pet_fusion_tx_test.go
Normal file
50
modules/player/service/pet_fusion_tx_test.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/modules/player/model"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFindPetDataSnapshotInPlayerInfo(t *testing.T) {
|
||||||
|
t.Run("pet list", func(t *testing.T) {
|
||||||
|
want := model.PetInfo{CatchTime: 1001, Level: 55}
|
||||||
|
info := model.PlayerInfo{
|
||||||
|
PetList: []model.PetInfo{want},
|
||||||
|
}
|
||||||
|
|
||||||
|
got, ok := findPetDataSnapshotInPlayerInfo(info, want.CatchTime)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("expected pet snapshot in pet list")
|
||||||
|
}
|
||||||
|
if got.CatchTime != want.CatchTime || got.Level != want.Level {
|
||||||
|
t.Fatalf("unexpected pet snapshot: %+v", got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("backup pet list", func(t *testing.T) {
|
||||||
|
want := model.PetInfo{CatchTime: 2002, Level: 66}
|
||||||
|
info := model.PlayerInfo{
|
||||||
|
BackupPetList: []model.PetInfo{want},
|
||||||
|
}
|
||||||
|
|
||||||
|
got, ok := findPetDataSnapshotInPlayerInfo(info, want.CatchTime)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("expected pet snapshot in backup pet list")
|
||||||
|
}
|
||||||
|
if got.CatchTime != want.CatchTime || got.Level != want.Level {
|
||||||
|
t.Fatalf("unexpected pet snapshot: %+v", got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("not found", func(t *testing.T) {
|
||||||
|
info := model.PlayerInfo{
|
||||||
|
PetList: []model.PetInfo{{CatchTime: 3003}},
|
||||||
|
BackupPetList: []model.PetInfo{{CatchTime: 4004}},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := findPetDataSnapshotInPlayerInfo(info, 9999)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("expected missing pet snapshot")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user