Compare commits
40 Commits
ee8b0a2182
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11bf46c7e4 | ||
|
|
c4b5748e5c | ||
|
|
1f1fbd09d4 | ||
|
|
b1ca3df3ae | ||
|
|
57676e998f | ||
|
|
5500684e29 | ||
|
|
7fd89800fa | ||
|
|
b46a1f442b | ||
|
|
eb76c22c41 | ||
|
|
a6386daad8 | ||
|
|
b59beed45f | ||
|
|
77909a5940 | ||
|
|
c4d3ab725c | ||
|
|
808da76bd0 | ||
|
|
dcbd9950d3 | ||
|
|
4b42a64da0 | ||
|
|
d517c822ef | ||
|
|
04038cd16b | ||
|
|
ec608d69cd | ||
|
|
fd5341da1a | ||
|
|
5967414da4 | ||
|
|
da118dc826 | ||
|
|
823eef00ac | ||
|
|
7844c5b76b | ||
|
|
4abd179a23 | ||
|
|
a3e88c7357 | ||
|
|
4e1a9a815f | ||
|
|
de3ae0bca2 | ||
|
|
b1ff4d3a2a | ||
|
|
24b52e14c3 | ||
|
|
2b92baf530 | ||
|
|
819d5f667b | ||
|
|
de6c700bb3 | ||
|
|
3232efd05a | ||
|
|
0c79fee8af | ||
|
|
3d77e146e9 | ||
|
|
a43a25c610 | ||
|
|
3cfde577eb | ||
|
|
85f9c02ced | ||
|
|
9f7fd83626 |
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
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -21,17 +21,25 @@ type PVPMatchJoinPayload struct {
|
|||||||
Nick string `json:"nick"`
|
Nick string `json:"nick"`
|
||||||
FightMode uint32 `json:"fightMode"`
|
FightMode uint32 `json:"fightMode"`
|
||||||
Status uint32 `json:"status"`
|
Status uint32 `json:"status"`
|
||||||
|
IsVip uint32 `json:"isVip"`
|
||||||
|
IsDebug uint8 `json:"isDebug"`
|
||||||
CatchTimes []uint32 `json:"catchTimes"`
|
CatchTimes []uint32 `json:"catchTimes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type pvpMatchQueueKey struct {
|
||||||
|
FightMode uint32
|
||||||
|
IsVip uint32
|
||||||
|
IsDebug uint8
|
||||||
|
}
|
||||||
|
|
||||||
type pvpMatchCoordinator struct {
|
type pvpMatchCoordinator struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
queues map[uint32][]pvpwire.QueuePlayerSnapshot
|
queues map[pvpMatchQueueKey][]pvpwire.QueuePlayerSnapshot
|
||||||
lastSeen map[uint32]time.Time
|
lastSeen map[uint32]time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultPVPMatchCoordinator = &pvpMatchCoordinator{
|
var defaultPVPMatchCoordinator = &pvpMatchCoordinator{
|
||||||
queues: make(map[uint32][]pvpwire.QueuePlayerSnapshot),
|
queues: make(map[pvpMatchQueueKey][]pvpwire.QueuePlayerSnapshot),
|
||||||
lastSeen: make(map[uint32]time.Time),
|
lastSeen: make(map[uint32]time.Time),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +59,8 @@ func (m *pvpMatchCoordinator) JoinOrUpdate(payload PVPMatchJoinPayload) error {
|
|||||||
Nick: payload.Nick,
|
Nick: payload.Nick,
|
||||||
FightMode: payload.FightMode,
|
FightMode: payload.FightMode,
|
||||||
Status: payload.Status,
|
Status: payload.Status,
|
||||||
|
IsVip: payload.IsVip,
|
||||||
|
IsDebug: payload.IsDebug,
|
||||||
JoinedAtUnix: now.Unix(),
|
JoinedAtUnix: now.Unix(),
|
||||||
CatchTimes: append([]uint32(nil), payload.CatchTimes...),
|
CatchTimes: append([]uint32(nil), payload.CatchTimes...),
|
||||||
}
|
}
|
||||||
@@ -62,11 +72,12 @@ func (m *pvpMatchCoordinator) JoinOrUpdate(payload PVPMatchJoinPayload) error {
|
|||||||
m.removeUserLocked(payload.UserID)
|
m.removeUserLocked(payload.UserID)
|
||||||
m.lastSeen[payload.UserID] = now
|
m.lastSeen[payload.UserID] = now
|
||||||
|
|
||||||
queue := m.queues[payload.FightMode]
|
queueKey := newPVPMatchQueueKey(player)
|
||||||
|
queue := m.queues[queueKey]
|
||||||
if len(queue) > 0 {
|
if len(queue) > 0 {
|
||||||
host := queue[0]
|
host := queue[0]
|
||||||
queue = queue[1:]
|
queue = queue[1:]
|
||||||
m.queues[payload.FightMode] = queue
|
m.queues[queueKey] = queue
|
||||||
delete(m.lastSeen, host.UserID)
|
delete(m.lastSeen, host.UserID)
|
||||||
delete(m.lastSeen, payload.UserID)
|
delete(m.lastSeen, payload.UserID)
|
||||||
|
|
||||||
@@ -79,7 +90,7 @@ func (m *pvpMatchCoordinator) JoinOrUpdate(payload PVPMatchJoinPayload) error {
|
|||||||
}
|
}
|
||||||
match = &result
|
match = &result
|
||||||
} else {
|
} else {
|
||||||
m.queues[payload.FightMode] = append(queue, player)
|
m.queues[queueKey] = append(queue, player)
|
||||||
}
|
}
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
|
|
||||||
@@ -109,7 +120,7 @@ func (m *pvpMatchCoordinator) Cancel(userID uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *pvpMatchCoordinator) pruneExpiredLocked(now time.Time) {
|
func (m *pvpMatchCoordinator) pruneExpiredLocked(now time.Time) {
|
||||||
for mode, queue := range m.queues {
|
for key, queue := range m.queues {
|
||||||
next := make([]pvpwire.QueuePlayerSnapshot, 0, len(queue))
|
next := make([]pvpwire.QueuePlayerSnapshot, 0, len(queue))
|
||||||
for _, queued := range queue {
|
for _, queued := range queue {
|
||||||
last := m.lastSeen[queued.UserID]
|
last := m.lastSeen[queued.UserID]
|
||||||
@@ -119,12 +130,12 @@ func (m *pvpMatchCoordinator) pruneExpiredLocked(now time.Time) {
|
|||||||
}
|
}
|
||||||
next = append(next, queued)
|
next = append(next, queued)
|
||||||
}
|
}
|
||||||
m.queues[mode] = next
|
m.queues[key] = next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *pvpMatchCoordinator) removeUserLocked(userID uint32) {
|
func (m *pvpMatchCoordinator) removeUserLocked(userID uint32) {
|
||||||
for mode, queue := range m.queues {
|
for key, queue := range m.queues {
|
||||||
next := make([]pvpwire.QueuePlayerSnapshot, 0, len(queue))
|
next := make([]pvpwire.QueuePlayerSnapshot, 0, len(queue))
|
||||||
for _, queued := range queue {
|
for _, queued := range queue {
|
||||||
if queued.UserID == userID {
|
if queued.UserID == userID {
|
||||||
@@ -132,7 +143,15 @@ func (m *pvpMatchCoordinator) removeUserLocked(userID uint32) {
|
|||||||
}
|
}
|
||||||
next = append(next, queued)
|
next = append(next, queued)
|
||||||
}
|
}
|
||||||
m.queues[mode] = next
|
m.queues[key] = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPVPMatchQueueKey(player pvpwire.QueuePlayerSnapshot) pvpMatchQueueKey {
|
||||||
|
return pvpMatchQueueKey{
|
||||||
|
FightMode: player.FightMode,
|
||||||
|
IsVip: player.IsVip,
|
||||||
|
IsDebug: player.IsDebug,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ var ErrorCodes = enum.New[struct {
|
|||||||
ErrPokemonLevelTooLow ErrorCode `enum:"13007"`
|
ErrPokemonLevelTooLow ErrorCode `enum:"13007"`
|
||||||
// 不能展示背包里的精灵!
|
// 不能展示背包里的精灵!
|
||||||
ErrCannotShowBagPokemon ErrorCode `enum:"13017"`
|
ErrCannotShowBagPokemon ErrorCode `enum:"13017"`
|
||||||
|
// 基地展示精灵数量已达上限!
|
||||||
|
ErrRoomShowPetLimit ErrorCode `enum:"13019"`
|
||||||
|
// 该精灵不在仓库中,无法设为基地展示!
|
||||||
|
ErrPetNotInWarehouse ErrorCode `enum:"13021"`
|
||||||
// 你今天已经被吃掉过一回了,明天再来吧!
|
// 你今天已经被吃掉过一回了,明天再来吧!
|
||||||
ErrAlreadyEatenToday ErrorCode `enum:"17018"`
|
ErrAlreadyEatenToday ErrorCode `enum:"17018"`
|
||||||
// 该道具已经在使用中,无法重复使用。
|
// 该道具已经在使用中,无法重复使用。
|
||||||
|
|||||||
18
help/server_show冠名索引修复.sql
Normal file
18
help/server_show冠名索引修复.sql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
-- server_show 冠名索引修复
|
||||||
|
-- 目的:
|
||||||
|
-- 1. 允许同一服务器存在多个不同玩家的冠名记录
|
||||||
|
-- 2. 保证同一玩家对同一服务器只有一条记录(续费更新该记录)
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- 历史上 server_id 被建成了单列唯一约束/唯一索引,会拦截同服多冠名
|
||||||
|
ALTER TABLE server_show DROP CONSTRAINT IF EXISTS idx_server_show_server_id;
|
||||||
|
DROP INDEX IF EXISTS idx_server_show_server_id;
|
||||||
|
|
||||||
|
-- 保留普通查询索引
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_server_show_server_id ON server_show (server_id);
|
||||||
|
|
||||||
|
-- 保证“一个玩家对一个服务器最多一条”
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_server_show_server_owner ON server_show (server_id, owner);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
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;
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"blazing/common/rpc"
|
"blazing/common/rpc"
|
||||||
"blazing/common/socket/errorcode"
|
"blazing/common/socket/errorcode"
|
||||||
|
"blazing/cool"
|
||||||
"blazing/logic/service/common"
|
"blazing/logic/service/common"
|
||||||
"blazing/logic/service/fight"
|
"blazing/logic/service/fight"
|
||||||
"blazing/logic/service/fight/pvp"
|
"blazing/logic/service/fight/pvp"
|
||||||
@@ -37,6 +38,8 @@ func (h Controller) JoINtop(data *PetTOPLEVELnboundInfo, c *player.Player) (resu
|
|||||||
Nick: c.Info.Nick,
|
Nick: c.Info.Nick,
|
||||||
FightMode: fightMode,
|
FightMode: fightMode,
|
||||||
Status: status,
|
Status: status,
|
||||||
|
IsVip: cool.Config.ServerInfo.IsVip,
|
||||||
|
IsDebug: cool.Config.ServerInfo.IsDebug,
|
||||||
CatchTimes: pvp.AvailableCatchTimes(c.GetPetInfo(0)),
|
CatchTimes: pvp.AvailableCatchTimes(c.GetPetInfo(0)),
|
||||||
}
|
}
|
||||||
if callErr := Maincontroller.RPCClient.MatchJoinOrUpdate(joinPayload); callErr != nil {
|
if callErr := Maincontroller.RPCClient.MatchJoinOrUpdate(joinPayload); callErr != nil {
|
||||||
|
|||||||
@@ -87,6 +87,14 @@ type C2S_RoomPetInfo struct {
|
|||||||
CatchTime uint32 `json:"catchTime"`
|
CatchTime uint32 `json:"catchTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// C2S_RoomPetShowToggle 基地展示精灵添加/移除请求
|
||||||
|
type C2S_RoomPetShowToggle struct {
|
||||||
|
Head common.TomeeHeader `cmd:"2326" struc:"skip"`
|
||||||
|
CatchTime uint32 `json:"catchTime"`
|
||||||
|
PetID uint32 `json:"petID"`
|
||||||
|
Flag uint32 `json:"flag"` // 1=添加展示, 0=移除展示
|
||||||
|
}
|
||||||
|
|
||||||
// C2S_BUY_FITMENT 定义请求或响应数据结构。
|
// C2S_BUY_FITMENT 定义请求或响应数据结构。
|
||||||
type C2S_BUY_FITMENT struct {
|
type C2S_BUY_FITMENT struct {
|
||||||
Head common.TomeeHeader `cmd:"10004" struc:"skip"`
|
Head common.TomeeHeader `cmd:"10004" struc:"skip"`
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,9 +85,10 @@ func (h Controller) PlayerShowPet(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
slot, ok := player.FindPetBagSlot(data.CatchTime)
|
// 仅允许背包精灵跟随:仓库中的精灵不允许跟随
|
||||||
if !ok {
|
slot, found := player.FindPetBagSlot(data.CatchTime)
|
||||||
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
|
if !found {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrCannotShowBagPokemon
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPet := slot.PetInfoPtr()
|
currentPet := slot.PetInfoPtr()
|
||||||
|
|||||||
@@ -48,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
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"blazing/common/socket/errorcode"
|
"blazing/common/socket/errorcode"
|
||||||
"blazing/modules/player/model"
|
"blazing/modules/player/model"
|
||||||
|
"blazing/modules/player/service"
|
||||||
|
|
||||||
"blazing/logic/service/pet"
|
"blazing/logic/service/pet"
|
||||||
"blazing/logic/service/player"
|
"blazing/logic/service/player"
|
||||||
@@ -32,17 +33,45 @@ func (h Controller) GetFitmentUsing(data *FitmentUseringInboundInfo, c *player.P
|
|||||||
func (h Controller) GetRoomPetShowInfo(data *PetRoomListInboundInfo, c *player.Player) (result *room.PetRoomListOutboundInfo, err errorcode.ErrorCode) {
|
func (h Controller) GetRoomPetShowInfo(data *PetRoomListInboundInfo, c *player.Player) (result *room.PetRoomListOutboundInfo, err errorcode.ErrorCode) {
|
||||||
result = &room.PetRoomListOutboundInfo{}
|
result = &room.PetRoomListOutboundInfo{}
|
||||||
result.Pets = make([]pet.PetShortInfo, 0)
|
result.Pets = make([]pet.PetShortInfo, 0)
|
||||||
roomInfo := c.Service.Room.Get(data.TargetUserID)
|
showPets := service.NewPetService(data.TargetUserID).GetShowPets()
|
||||||
for _, catchTime := range roomInfo.ShowPokemon {
|
for i := range showPets {
|
||||||
petInfo := c.Service.Pet.PetInfoOneOther(data.TargetUserID, catchTime)
|
var petShortInfo pet.PetShortInfo
|
||||||
if petInfo.Data.ID == 0 {
|
copier.Copy(&petShortInfo, &showPets[i].Data)
|
||||||
|
result.Pets = append(result.Pets, petShortInfo)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRoomPetShowInfo 设置基地展示精灵并返回最新展示列表(cmd:2323)
|
||||||
|
func (h Controller) SetRoomPetShowInfo(data *C2S_PET_ROOM_SHOW, c *player.Player) (result *room.S2C_PET_ROOM_SHOW, err errorcode.ErrorCode) {
|
||||||
|
result = &room.S2C_PET_ROOM_SHOW{}
|
||||||
|
result.PetShowList = make([]pet.PetShortInfo, 0)
|
||||||
|
|
||||||
|
catchTimes := make([]uint32, 0, len(data.PetShowList))
|
||||||
|
seen := make(map[uint32]struct{}, len(data.PetShowList))
|
||||||
|
for _, item := range data.PetShowList {
|
||||||
|
ct := uint32(item.CatchTime)
|
||||||
|
if ct == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var petShortInfo pet.PetShortInfo
|
if _, ok := seen[ct]; ok {
|
||||||
copier.Copy(&petShortInfo, &petInfo.Data)
|
continue
|
||||||
if petInfo.ID != 0 {
|
|
||||||
result.Pets = append(result.Pets, petShortInfo)
|
|
||||||
}
|
}
|
||||||
|
seen[ct] = struct{}{}
|
||||||
|
catchTimes = append(catchTimes, ct)
|
||||||
|
}
|
||||||
|
|
||||||
|
petSvc := service.NewPetService(c.Info.UserID)
|
||||||
|
if !petSvc.SetShowCatchTimes(catchTimes) {
|
||||||
|
err = errorcode.ErrorCodes.ErrSystemError
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showPets := petSvc.GetShowPets()
|
||||||
|
for i := range showPets {
|
||||||
|
var petShortInfo pet.PetShortInfo
|
||||||
|
copier.Copy(&petShortInfo, &showPets[i].Data)
|
||||||
|
result.PetShowList = append(result.PetShowList, petShortInfo)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/common/socket/errorcode"
|
"blazing/common/socket/errorcode"
|
||||||
"blazing/logic/service/pet"
|
|
||||||
"blazing/logic/service/player"
|
"blazing/logic/service/player"
|
||||||
"blazing/logic/service/room"
|
"blazing/logic/service/room"
|
||||||
|
|
||||||
"github.com/jinzhu/copier"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetFitment 设置基地家具摆放
|
// SetFitment 设置基地家具摆放
|
||||||
@@ -18,29 +15,3 @@ func (h Controller) SetFitment(data *SET_FITMENT, c *player.Player) (result *roo
|
|||||||
c.Service.Room.Set(data.Fitments)
|
c.Service.Room.Set(data.Fitments)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPet 设置基地展示的精灵
|
|
||||||
// data: 包含精灵展示列表的输入信息
|
|
||||||
// c: 当前玩家对象
|
|
||||||
// 返回: 精灵展示列表和错误码
|
|
||||||
func (h Controller) SetPet(data *C2S_PET_ROOM_SHOW, c *player.Player) (result *room.S2C_PET_ROOM_SHOW, err errorcode.ErrorCode) {
|
|
||||||
var showPetCatchTimes []uint32
|
|
||||||
for _, petShowInfo := range data.PetShowList {
|
|
||||||
if petShowInfo.CatchTime != 0 {
|
|
||||||
showPetCatchTimes = append(showPetCatchTimes, petShowInfo.CatchTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.Service.Room.Show(showPetCatchTimes)
|
|
||||||
result = &room.S2C_PET_ROOM_SHOW{}
|
|
||||||
result.PetShowList = make([]pet.PetShortInfo, len(showPetCatchTimes))
|
|
||||||
for _, catchTime := range showPetCatchTimes {
|
|
||||||
petInfo := c.Service.Pet.PetInfoOneByCatchTime(catchTime)
|
|
||||||
if petInfo == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var petShortInfo pet.PetShortInfo
|
|
||||||
copier.Copy(&petShortInfo, &petInfo.Data)
|
|
||||||
result.PetShowList = append(result.PetShowList, petShortInfo)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -22,6 +28,9 @@ func (h Controller) CDK(data *C2S_GET_GIFT_COMPLETE, player *logicplayer.Player)
|
|||||||
if r == nil {
|
if r == nil {
|
||||||
return nil, errorcode.ErrorCodes.ErrMolecularCodeNotExists
|
return nil, errorcode.ErrorCodes.ErrMolecularCodeNotExists
|
||||||
}
|
}
|
||||||
|
if r.Type != configservice.CDKTypeReward {
|
||||||
|
return nil, errorcode.ErrorCodes.ErrMolecularCodeNotExists
|
||||||
|
}
|
||||||
if r.BindUserId != 0 && r.BindUserId != data.Head.UserID {
|
if r.BindUserId != 0 && r.BindUserId != data.Head.UserID {
|
||||||
return nil, errorcode.ErrorCodes.ErrMolecularCodeFrozen
|
return nil, errorcode.ErrorCodes.ErrMolecularCodeFrozen
|
||||||
}
|
}
|
||||||
@@ -35,7 +44,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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ func (e *Effect2278) Skill_Use() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
damage := marked.GetHP().Mul(alpacadecimal.NewFromInt(percent)).Div(hundred)
|
damage := marked.GetMaxHP().Mul(alpacadecimal.NewFromInt(percent)).Div(hundred)
|
||||||
if damage.Cmp(alpacadecimal.Zero) > 0 {
|
if damage.Cmp(alpacadecimal.Zero) > 0 {
|
||||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{
|
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{
|
||||||
Type: info.DamageType.Percent,
|
Type: info.DamageType.Percent,
|
||||||
|
|||||||
@@ -14,18 +14,25 @@ type Effect85 struct {
|
|||||||
// 执行时逻辑
|
// 执行时逻辑
|
||||||
// ----------------------
|
// ----------------------
|
||||||
func (e *Effect85) OnSkill() bool {
|
func (e *Effect85) OnSkill() bool {
|
||||||
|
carrier := e.CarrierInput()
|
||||||
for i, v := range e.Ctx().Opp.Prop[:] {
|
opp := e.OpponentInput()
|
||||||
if v > 0 {
|
if carrier == nil || opp == nil {
|
||||||
e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), v)
|
return true
|
||||||
e.Ctx().Opp.SetProp(e.Ctx().Our, int8(i), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
e.transferPositiveProps(carrier, opp)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// transferPositiveProps 使用显式入参执行业务逻辑,避免嵌套结算时再从 Ctx 取到漂移后的对象。
|
||||||
|
func (e *Effect85) transferPositiveProps(carrier, opp *input.Input) {
|
||||||
|
for i, v := range opp.Prop[:] {
|
||||||
|
if v > 0 {
|
||||||
|
carrier.SetProp(carrier, int8(i), v)
|
||||||
|
opp.SetProp(carrier, int8(i), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------
|
// ----------------------
|
||||||
// 注册所有效果
|
// 注册所有效果
|
||||||
// ----------------------
|
// ----------------------
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"blazing/logic/service/fight/info"
|
"blazing/logic/service/fight/info"
|
||||||
"blazing/logic/service/fight/input"
|
"blazing/logic/service/fight/input"
|
||||||
"blazing/logic/service/fight/node"
|
"blazing/logic/service/fight/node"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Effect 724: {0}回合内受到攻击{1}%恢复1/{2}体力
|
// Effect 724: {0}回合内受到攻击{1}%恢复1/{2}体力
|
||||||
@@ -85,6 +86,7 @@ func (e *Effect727) Action_end() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[Effect727] Action_end RESET: Our.Prop %v -> LastTurnEndProp %v\n", e.Ctx().Our.Prop, e.Ctx().Our.LastTurnEndProp)
|
||||||
e.Ctx().Our.AttackValue.Prop = e.Ctx().Our.LastTurnEndProp
|
e.Ctx().Our.AttackValue.Prop = e.Ctx().Our.LastTurnEndProp
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,58 +36,21 @@ 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 睡眠在“被攻击且未 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 {
|
|
||||||
if e.Duration() <= 0 {
|
|
||||||
e.hasTriedAct = false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
e.hasTriedAct = true
|
|
||||||
return e.StatusCannotAct.ActionStart(attacker, defender)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StatusSleep) Skill_Use_ex() bool {
|
func (e *StatusSleep) Skill_Use_ex() bool {
|
||||||
if !e.hasTriedAct {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if e.Duration() <= 0 && e.Ctx().SkillEntity != nil && e.Ctx().Category() != info.Category.STATUS {
|
|
||||||
e.Alive(false)
|
|
||||||
}
|
|
||||||
e.hasTriedAct = false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *StatusSleep) TurnEnd() {
|
if e.Ctx().SkillEntity != nil {
|
||||||
e.hasTriedAct = false
|
if e.Ctx().SkillEntity.AttackTime != 0 && e.Ctx().Category() != info.Category.STATUS {
|
||||||
e.StatusCannotAct.TurnEnd()
|
e.Alive(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 持续伤害状态基类(中毒、冻伤、烧伤等)
|
// 持续伤害状态基类(中毒、冻伤、烧伤等)
|
||||||
@@ -96,13 +59,13 @@ type ContinuousDamage struct {
|
|||||||
isheal bool //是否回血
|
isheal bool //是否回血
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回合开始触发持续伤害,保证吃药/空过回合时也会正常结算。
|
// 行动开始触发持续伤害:当中招方轮到自己行动时结算。
|
||||||
func (e *ContinuousDamage) TurnStart(attacker, defender *action.SelectSkillAction) {
|
func (e *ContinuousDamage) ActionStart(attacker, defender *action.SelectSkillAction) bool {
|
||||||
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
|
return true
|
||||||
}
|
}
|
||||||
damage := e.calculateDamage()
|
damage := e.calculateDamage()
|
||||||
|
|
||||||
@@ -111,7 +74,7 @@ func (e *ContinuousDamage) TurnStart(attacker, defender *action.SelectSkillActio
|
|||||||
Damage: damage,
|
Damage: damage,
|
||||||
})
|
})
|
||||||
if len(e.SideEffectArgs) == 0 {
|
if len(e.SideEffectArgs) == 0 {
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
// 额外效果
|
// 额外效果
|
||||||
carrier.Damage(source, &info.DamageZone{
|
carrier.Damage(source, &info.DamageZone{
|
||||||
@@ -119,11 +82,12 @@ func (e *ContinuousDamage) TurnStart(attacker, defender *action.SelectSkillActio
|
|||||||
Damage: damage,
|
Damage: damage,
|
||||||
})
|
})
|
||||||
if opp == nil || opp.CurPet[0].GetHP().IntPart() == 0 {
|
if opp == nil || opp.CurPet[0].GetHP().IntPart() == 0 {
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给对方回血(不受回血限制影响)
|
// 给对方回血(不受回血限制影响)
|
||||||
opp.Heal(carrier, nil, damage)
|
opp.Heal(carrier, nil, damage)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算伤害:最大生命值的1/8
|
// 计算伤害:最大生命值的1/8
|
||||||
@@ -299,6 +263,7 @@ func init() {
|
|||||||
info.PetStatus.Paralysis, // 麻痹
|
info.PetStatus.Paralysis, // 麻痹
|
||||||
info.PetStatus.Fear, // 害怕
|
info.PetStatus.Fear, // 害怕
|
||||||
info.PetStatus.Petrified, // 石化
|
info.PetStatus.Petrified, // 石化
|
||||||
|
info.PetStatus.Tired, // 疲惫
|
||||||
}
|
}
|
||||||
for _, status := range nonActingStatuses {
|
for _, status := range nonActingStatuses {
|
||||||
effect := &StatusCannotAct{}
|
effect := &StatusCannotAct{}
|
||||||
@@ -306,10 +271,6 @@ 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{})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ 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"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/alpacahq/alpacadecimal"
|
"github.com/alpacahq/alpacadecimal"
|
||||||
@@ -66,12 +69,15 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
|
|||||||
//还原属性
|
//还原属性
|
||||||
attacker.Prop, defender.Prop = originalProps[0], originalProps[1]
|
attacker.Prop, defender.Prop = originalProps[0], originalProps[1]
|
||||||
attackerPet.Info, defenderPet.Info = originalPetInfo[0], originalPetInfo[1]
|
attackerPet.Info, defenderPet.Info = originalPetInfo[0], originalPetInfo[1]
|
||||||
|
fmt.Printf("[processSkillAttack] After restore: attacker.Prop=%v, defender.Prop=%v\n", attacker.Prop, defender.Prop)
|
||||||
|
|
||||||
if attacker.IsCritical == 1 { //命中了才有暴击
|
if attacker.IsCritical == 1 { //命中了才有暴击
|
||||||
//暴击破防
|
//暴击破防
|
||||||
if skill.Category() == info.Category.PHYSICAL && defender.Prop[1] > 0 {
|
if skill.Category() == info.Category.PHYSICAL && defender.Prop[1] > 0 {
|
||||||
|
fmt.Printf("[processSkillAttack] Crit break DEF: defender.Prop[1] %d -> 0\n", defender.Prop[1])
|
||||||
defender.Prop[1] = 0
|
defender.Prop[1] = 0
|
||||||
} else if skill.Category() == info.Category.SPECIAL && defender.Prop[3] > 0 {
|
} else if skill.Category() == info.Category.SPECIAL && defender.Prop[3] > 0 {
|
||||||
|
fmt.Printf("[processSkillAttack] Crit break SPDEF: defender.Prop[3] %d -> 0\n", defender.Prop[3])
|
||||||
defender.Prop[3] = 0
|
defender.Prop[3] = 0
|
||||||
}
|
}
|
||||||
//暴击翻倍
|
//暴击翻倍
|
||||||
@@ -99,6 +105,7 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
|
|||||||
effect.OnSkill() //调用伤害计算
|
effect.OnSkill() //调用伤害计算
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
fmt.Printf("[processSkillAttack] After OnSkill: attacker.Prop=%v, defender.Prop=%v, attacker.SkillID=%d\n", attacker.Prop, defender.Prop, attacker.SkillID)
|
||||||
|
|
||||||
defender.Damage(attacker, &info.DamageZone{
|
defender.Damage(attacker, &info.DamageZone{
|
||||||
Damage: SumDamage,
|
Damage: SumDamage,
|
||||||
@@ -133,7 +140,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 上下文。
|
||||||
@@ -232,6 +252,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) {
|
||||||
@@ -333,6 +362,10 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
if currentAction == nil {
|
if currentAction == nil {
|
||||||
continue
|
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)
|
||||||
//取消后手历史效果
|
//取消后手历史效果
|
||||||
@@ -410,6 +443,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
fmt.Printf("[enterturn] After Skill_Use: attacker.Prop=%v, defender.Prop=%v\n", attacker.Prop, defender.Prop)
|
||||||
//技能使用后
|
//技能使用后
|
||||||
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果
|
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果
|
||||||
f.setEffectSkillContext(effect, currentSkill, defender)
|
f.setEffectSkillContext(effect, currentSkill, defender)
|
||||||
@@ -422,6 +456,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
effect.Action_end()
|
effect.Action_end()
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
fmt.Printf("[enterturn] After Action_end: attacker.Prop=%v, defender.Prop=%v\n", attacker.Prop, defender.Prop)
|
||||||
|
|
||||||
if defenderPet != nil && attackerPet != nil && defenderPet.Info.Hp <= 0 && attackerPet.Info.Hp <= 0 { //先手方死亡,触发反同归于尽
|
if defenderPet != nil && attackerPet != nil && defenderPet.Info.Hp <= 0 && attackerPet.Info.Hp <= 0 { //先手方死亡,触发反同归于尽
|
||||||
attackerPet.Info.Hp = 1
|
attackerPet.Info.Hp = 1
|
||||||
@@ -460,6 +495,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
}
|
}
|
||||||
|
|
||||||
attackValueResult := f.buildNoteUseSkillOutboundInfo()
|
attackValueResult := f.buildNoteUseSkillOutboundInfo()
|
||||||
|
fmt.Printf("[enterturn] Broadcast: First.Prop=%v, Second.Prop=%v\n", attackValueResult.FirstAttackInfo.Prop, attackValueResult.SecondAttackInfo.Prop)
|
||||||
//因为切完才能广播,所以必须和回合结束分开结算
|
//因为切完才能广播,所以必须和回合结束分开结算
|
||||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||||
for _, switchAction := range f.Switch {
|
for _, switchAction := range f.Switch {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"blazing/logic/service/fight/input"
|
"blazing/logic/service/fight/input"
|
||||||
"blazing/modules/player/model"
|
"blazing/modules/player/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// <!--
|
// <!--
|
||||||
// GBTL:
|
// GBTL:
|
||||||
// 1. AtkNum:本技能同时攻击数量, 默认:1(不能为0)
|
// 1. AtkNum:本技能同时攻击数量, 默认:1(不能为0)
|
||||||
@@ -496,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
|
||||||
@@ -584,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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,6 +107,10 @@ func (our *Input) HealPP(value int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentPet.Info.HealPP(value)
|
currentPet.Info.HealPP(value)
|
||||||
|
if our.AttackValue != nil {
|
||||||
|
our.AttackValue.SkillList = append(our.AttackValue.SkillList[:0], currentPet.Info.SkillList...)
|
||||||
|
our.AttackValue.SkillListLen = uint32(len(our.AttackValue.SkillList))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
func (our *Input) DelPP(value int) {
|
func (our *Input) DelPP(value int) {
|
||||||
@@ -123,6 +127,10 @@ func (our *Input) DelPP(value int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if our.AttackValue != nil {
|
||||||
|
our.AttackValue.SkillList = append(our.AttackValue.SkillList[:0], currentPet.Info.SkillList...)
|
||||||
|
our.AttackValue.SkillListLen = uint32(len(our.AttackValue.SkillList))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +306,7 @@ func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpaca
|
|||||||
|
|
||||||
}
|
}
|
||||||
if skill.XML.PwrBindDv == 2 {
|
if skill.XML.PwrBindDv == 2 {
|
||||||
skill.XML.Power = int(ourPet.Info.Hp/3 + ourPet.Info.Dv)
|
skill.XML.Power = int(ourPet.Info.MaxHp/3 + ourPet.Info.Dv)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,7 +284,9 @@ func (our *Input) GenInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
our.RemainHp = int32(currentPet.Info.Hp)
|
our.RemainHp = int32(currentPet.Info.Hp)
|
||||||
our.SkillList = currentPet.Info.SkillList
|
our.MaxHp = currentPet.Info.MaxHp
|
||||||
|
our.SkillList = append(our.SkillList[:0], currentPet.Info.SkillList...)
|
||||||
|
our.SkillListLen = uint32(len(our.SkillList))
|
||||||
|
|
||||||
// f.Second.SkillList = f.Second.CurPet.Info.SkillList
|
// f.Second.SkillList = f.Second.CurPet.Info.SkillList
|
||||||
// f.Second.RemainHp = int32(f.Second.CurPet.Info.Hp)
|
// f.Second.RemainHp = int32(f.Second.CurPet.Info.Hp)
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package input
|
package input
|
||||||
|
|
||||||
|
// OnSetPropReset 是一个调试钩子,在 SetProp(level==0) 重置 Prop 时被调用。
|
||||||
|
// 外部包可设置此变量来追踪 Prop 被重置的时机和调用栈。
|
||||||
|
var OnSetPropReset func(target *Input, index int8)
|
||||||
|
|
||||||
func (our *Input) HasPropADD() bool {
|
func (our *Input) HasPropADD() bool {
|
||||||
for _, v := range our.Prop[:] {
|
for _, v := range our.Prop[:] {
|
||||||
if v > 0 {
|
if v > 0 {
|
||||||
@@ -55,6 +59,9 @@ func (target *Input) SetProp(source *Input, index, level int8) bool {
|
|||||||
|
|
||||||
if target.AttackValue.Prop[index] != 0 {
|
if target.AttackValue.Prop[index] != 0 {
|
||||||
target.AttackValue.Prop[index] = 0
|
target.AttackValue.Prop[index] = 0
|
||||||
|
if OnSetPropReset != nil {
|
||||||
|
OnSetPropReset(target, index)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -59,9 +59,15 @@ type session struct {
|
|||||||
banPickDeadline time.Time
|
banPickDeadline time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type queueKey struct {
|
||||||
|
FightMode uint32
|
||||||
|
IsVip uint32
|
||||||
|
IsDebug uint8
|
||||||
|
}
|
||||||
|
|
||||||
type manager struct {
|
type manager struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
queues map[uint32][]pvpwire.QueuePlayerSnapshot
|
queues map[queueKey][]pvpwire.QueuePlayerSnapshot
|
||||||
lastSeen map[uint32]time.Time
|
lastSeen map[uint32]time.Time
|
||||||
localQueues map[uint32]*localQueueTicket
|
localQueues map[uint32]*localQueueTicket
|
||||||
sessions map[string]*session
|
sessions map[string]*session
|
||||||
@@ -69,7 +75,7 @@ type manager struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var defaultManager = &manager{
|
var defaultManager = &manager{
|
||||||
queues: make(map[uint32][]pvpwire.QueuePlayerSnapshot),
|
queues: make(map[queueKey][]pvpwire.QueuePlayerSnapshot),
|
||||||
lastSeen: make(map[uint32]time.Time),
|
lastSeen: make(map[uint32]time.Time),
|
||||||
localQueues: make(map[uint32]*localQueueTicket),
|
localQueues: make(map[uint32]*localQueueTicket),
|
||||||
sessions: make(map[string]*session),
|
sessions: make(map[string]*session),
|
||||||
@@ -222,6 +228,8 @@ func (m *manager) queueHeartbeatLoop(p *player.Player, ticket *localQueueTicket)
|
|||||||
Nick: p.Info.Nick,
|
Nick: p.Info.Nick,
|
||||||
FightMode: ticket.fightMode,
|
FightMode: ticket.fightMode,
|
||||||
Status: ticket.status,
|
Status: ticket.status,
|
||||||
|
IsVip: cool.Config.ServerInfo.IsVip,
|
||||||
|
IsDebug: cool.Config.ServerInfo.IsDebug,
|
||||||
JoinedAtUnix: time.Now().Unix(),
|
JoinedAtUnix: time.Now().Unix(),
|
||||||
CatchTimes: filterAvailableCatchTimes(p.GetPetInfo(0)),
|
CatchTimes: filterAvailableCatchTimes(p.GetPetInfo(0)),
|
||||||
},
|
},
|
||||||
@@ -254,12 +262,13 @@ func (m *manager) handleQueueJoin(payload pvpwire.QueueJoinPayload) {
|
|||||||
m.pruneExpiredQueueLocked(now)
|
m.pruneExpiredQueueLocked(now)
|
||||||
playerInfo := payload.Player
|
playerInfo := payload.Player
|
||||||
m.lastSeen[playerInfo.UserID] = now
|
m.lastSeen[playerInfo.UserID] = now
|
||||||
queue := m.queues[playerInfo.FightMode]
|
queueBucket := newQueueKey(playerInfo)
|
||||||
|
queue := m.queues[queueBucket]
|
||||||
|
|
||||||
for idx, queued := range queue {
|
for idx, queued := range queue {
|
||||||
if queued.UserID == playerInfo.UserID {
|
if queued.UserID == playerInfo.UserID {
|
||||||
queue[idx] = playerInfo
|
queue[idx] = playerInfo
|
||||||
m.queues[playerInfo.FightMode] = queue
|
m.queues[queueBucket] = queue
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,7 +276,7 @@ func (m *manager) handleQueueJoin(payload pvpwire.QueueJoinPayload) {
|
|||||||
if len(queue) > 0 {
|
if len(queue) > 0 {
|
||||||
host := queue[0]
|
host := queue[0]
|
||||||
queue = queue[1:]
|
queue = queue[1:]
|
||||||
m.queues[playerInfo.FightMode] = queue
|
m.queues[queueBucket] = queue
|
||||||
delete(m.lastSeen, host.UserID)
|
delete(m.lastSeen, host.UserID)
|
||||||
delete(m.lastSeen, playerInfo.UserID)
|
delete(m.lastSeen, playerInfo.UserID)
|
||||||
|
|
||||||
@@ -286,7 +295,7 @@ func (m *manager) handleQueueJoin(payload pvpwire.QueueJoinPayload) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m.queues[playerInfo.FightMode] = append(queue, playerInfo)
|
m.queues[queueBucket] = append(queue, playerInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) handleQueueCancel(payload pvpwire.QueueCancelPayload) {
|
func (m *manager) handleQueueCancel(payload pvpwire.QueueCancelPayload) {
|
||||||
@@ -294,7 +303,7 @@ func (m *manager) handleQueueCancel(payload pvpwire.QueueCancelPayload) {
|
|||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
delete(m.lastSeen, payload.UserID)
|
delete(m.lastSeen, payload.UserID)
|
||||||
for mode, queue := range m.queues {
|
for key, queue := range m.queues {
|
||||||
next := make([]pvpwire.QueuePlayerSnapshot, 0, len(queue))
|
next := make([]pvpwire.QueuePlayerSnapshot, 0, len(queue))
|
||||||
for _, queued := range queue {
|
for _, queued := range queue {
|
||||||
if queued.UserID == payload.UserID {
|
if queued.UserID == payload.UserID {
|
||||||
@@ -302,7 +311,7 @@ func (m *manager) handleQueueCancel(payload pvpwire.QueueCancelPayload) {
|
|||||||
}
|
}
|
||||||
next = append(next, queued)
|
next = append(next, queued)
|
||||||
}
|
}
|
||||||
m.queues[mode] = next
|
m.queues[key] = next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,7 +556,7 @@ func (m *manager) closeSession(sessionID, reason string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) pruneExpiredQueueLocked(now time.Time) {
|
func (m *manager) pruneExpiredQueueLocked(now time.Time) {
|
||||||
for mode, queue := range m.queues {
|
for key, queue := range m.queues {
|
||||||
next := make([]pvpwire.QueuePlayerSnapshot, 0, len(queue))
|
next := make([]pvpwire.QueuePlayerSnapshot, 0, len(queue))
|
||||||
for _, queued := range queue {
|
for _, queued := range queue {
|
||||||
last := m.lastSeen[queued.UserID]
|
last := m.lastSeen[queued.UserID]
|
||||||
@@ -557,7 +566,15 @@ func (m *manager) pruneExpiredQueueLocked(now time.Time) {
|
|||||||
}
|
}
|
||||||
next = append(next, queued)
|
next = append(next, queued)
|
||||||
}
|
}
|
||||||
m.queues[mode] = next
|
m.queues[key] = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newQueueKey(player pvpwire.QueuePlayerSnapshot) queueKey {
|
||||||
|
return queueKey{
|
||||||
|
FightMode: player.FightMode,
|
||||||
|
IsVip: player.IsVip,
|
||||||
|
IsDebug: player.IsDebug,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ type QueuePlayerSnapshot struct {
|
|||||||
Nick string `json:"nick"`
|
Nick string `json:"nick"`
|
||||||
FightMode uint32 `json:"fightMode"`
|
FightMode uint32 `json:"fightMode"`
|
||||||
Status uint32 `json:"status"`
|
Status uint32 `json:"status"`
|
||||||
|
IsVip uint32 `json:"isVip"`
|
||||||
|
IsDebug uint8 `json:"isDebug"`
|
||||||
JoinedAtUnix int64 `json:"joinedAtUnix"`
|
JoinedAtUnix int64 `json:"joinedAtUnix"`
|
||||||
CatchTimes []uint32 `json:"catchTimes"`
|
CatchTimes []uint32 `json:"catchTimes"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -144,3 +145,37 @@ func TestBuildNoteUseSkillOutboundInfoUsesActionOrder(t *testing.T) {
|
|||||||
t.Fatalf("expected second attack info to keep the idle side placeholder, got %+v", result.SecondAttackInfo)
|
t.Fatalf("expected second attack info to keep the idle side placeholder, got %+v", result.SecondAttackInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildNoteUseSkillOutboundInfoUsesAttackValueSkillPP(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 != 1 {
|
||||||
|
t.Fatalf("expected broadcast PP to come from attack value cache, got %+v", result.FirstAttackInfo.SkillList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,3 +39,9 @@ type C2S_RoomPetInfo struct {
|
|||||||
// CatchTime 精灵的捕获时间(Unix时间戳/前端自定义时间格式)
|
// CatchTime 精灵的捕获时间(Unix时间戳/前端自定义时间格式)
|
||||||
CatchTime uint32 `json:"catchTime"`
|
CatchTime uint32 `json:"catchTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// S2C_RoomPetShowToggle 基地展示精灵添加/移除响应
|
||||||
|
type S2C_RoomPetShowToggle struct {
|
||||||
|
PetShowListLen uint32 `json:"PetShowListLen" struc:"sizeof=PetShowList"`
|
||||||
|
PetShowList []pet.PetShortInfo `json:"PetShowList"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ var limiter *ratelimit.Rule = ratelimit.NewRule()
|
|||||||
|
|
||||||
// 简单规则案例
|
// 简单规则案例
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
//步骤二:增加一条或者多条规则组成复合规则,此复合规则必须至少包含一条规则
|
//步骤二:增加一条或者多条规则组成复合规则,此复合规则必须至少包含一条规则
|
||||||
limiter.AddRule(time.Second*1, 20)
|
limiter.AddRule(time.Second*1, 20)
|
||||||
//步骤三:调用函数判断某用户是否允许访问 allow:= r.AllowVisit(user)
|
//步骤三:调用函数判断某用户是否允许访问 allow:= r.AllowVisit(user)
|
||||||
|
|||||||
@@ -52,6 +52,18 @@ func (s *BaseSysUserService) GetPerson(userId uint32) (res *model.BaseSysUser) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *BaseSysUserService) GetByUsername(username string) (res *model.BaseSysUser) {
|
||||||
|
if strings.TrimSpace(username) == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m := cool.DBM(s.Model)
|
||||||
|
m.Where("username", strings.ToLower(strings.TrimSpace(username))).FieldsEx("password").Scan(&res)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (s *BaseSysUserService) SetdepartmentId(userId, departmentId uint32) (res *model.BaseSysUser) {
|
func (s *BaseSysUserService) SetdepartmentId(userId, departmentId uint32) (res *model.BaseSysUser) {
|
||||||
m := cool.DBM(s.Model)
|
m := cool.DBM(s.Model)
|
||||||
m.Where("id", userId).Data("departmentId", departmentId).Update()
|
m.Where("id", userId).Data("departmentId", departmentId).Update()
|
||||||
@@ -425,15 +437,15 @@ func (s *BaseSysUserService) ServiceUpdate(ctx context.Context, req *cool.Update
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 如果请求的password不为空并且密码加密后的值有变动,说明要修改密码
|
// // 如果请求的password不为空并且密码加密后的值有变动,说明要修改密码
|
||||||
var rPassword = r.Get("password", "").String()
|
// var rPassword = r.Get("password", "").String()
|
||||||
if rPassword != "" && rPassword != userInfo["password"].String() {
|
// if rPassword != "" && rPassword != userInfo["password"].String() {
|
||||||
rMap["password"], _ = gmd5.Encrypt(rPassword)
|
// rMap["password"], _ = gmd5.Encrypt(rPassword)
|
||||||
rMap["passwordV"] = userInfo["passwordV"].Int() + 1
|
// rMap["passwordV"] = userInfo["passwordV"].Int() + 1
|
||||||
cool.CacheManager.Set(ctx, fmt.Sprintf("admin:passwordVersion:%d", userId), rMap["passwordV"], 0)
|
// cool.CacheManager.Set(ctx, fmt.Sprintf("admin:passwordVersion:%d", userId), rMap["passwordV"], 0)
|
||||||
} else {
|
// } else {
|
||||||
delete(rMap, "password")
|
// delete(rMap, "password")
|
||||||
}
|
// }
|
||||||
delete(rMap, "goldbean")
|
delete(rMap, "goldbean")
|
||||||
|
|
||||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
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{})
|
||||||
|
}
|
||||||
@@ -258,7 +258,7 @@ func (s *CdkService) UseServerNamingCDK(ctx context.Context, code string, ownerI
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Model(model.NewServerShow()).Where("server_id", serverID).Data(g.Map{
|
_, err = tx.Model(model.NewServerShow()).Where("server_id", serverID).Where("owner", ownerID).Data(g.Map{
|
||||||
"name": updated.Name,
|
"name": updated.Name,
|
||||||
"owner": updated.Owner,
|
"owner": updated.Owner,
|
||||||
"expire_time": updated.ExpireTime,
|
"expire_time": updated.ExpireTime,
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ func (s *ServerService) GetPort(DepartmentID uint) []ServerShowInfo {
|
|||||||
items = append(items, item)
|
items = append(items, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +194,9 @@ func (s *ServerService) GetDonationAvailableServerIDs() []uint32 {
|
|||||||
if server.OnlineID == 0 {
|
if server.OnlineID == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if server.IsDebug != 0 {
|
||||||
|
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] })
|
||||||
@@ -268,7 +272,27 @@ func (s *ServerService) GetOwnerActiveDonationServers(ownerID uint32) []Donation
|
|||||||
|
|
||||||
// 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 {
|
||||||
return server.OnlineID != 0
|
if server.OnlineID == 0 || ownerID == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var shows []model.ServerShow
|
||||||
|
dbm_nocache_noenable(model.NewServerShow()).Where("server_id", server.OnlineID).Scan(&shows)
|
||||||
|
if len(shows) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range shows {
|
||||||
|
show := &shows[i]
|
||||||
|
if show.Owner != ownerID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.isActiveServerShow(show, now) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) getRawServers() []model.ServerList {
|
func (s *ServerService) getRawServers() []model.ServerList {
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
@@ -72,6 +72,19 @@ func (s *TowerService) Boss(towerLevel ...uint32) []model.BaseTowerConfig {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TowerService) EnabledLevels() []uint32 {
|
||||||
|
var config []model.BaseTowerConfig
|
||||||
|
dbm_enable(s.Model).OrderAsc("tower_level").Cache(gdb.CacheOption{
|
||||||
|
Force: false,
|
||||||
|
}).Scan(&config)
|
||||||
|
|
||||||
|
levels := make([]uint32, 0, len(config))
|
||||||
|
for _, item := range config {
|
||||||
|
levels = append(levels, item.TowerLevel)
|
||||||
|
}
|
||||||
|
return levels
|
||||||
|
}
|
||||||
|
|
||||||
func NewTower1Service() *TowerService {
|
func NewTower1Service() *TowerService {
|
||||||
return NewTowerService(TowerType1)
|
return NewTowerService(TowerType1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,10 @@ package admin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
base "blazing/modules/base/service"
|
|
||||||
"blazing/modules/player/service"
|
"blazing/modules/player/service"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
"github.com/gogf/gf/v2/os/gtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type GoldListController struct {
|
type GoldListController struct {
|
||||||
@@ -38,23 +34,10 @@ type DuihuanGoldAddReq struct {
|
|||||||
func (c *GoldListController) DuihuanGold(ctx context.Context, req *DuihuanGoldAddReq) (res *cool.BaseRes, err error) {
|
func (c *GoldListController) DuihuanGold(ctx context.Context, req *DuihuanGoldAddReq) (res *cool.BaseRes, err error) {
|
||||||
t := cool.GetAdmin(ctx)
|
t := cool.GetAdmin(ctx)
|
||||||
|
|
||||||
r, err := service.NewGoldListService(0).Done(req.ID)
|
_, err = service.NewGoldListService(0).Trade(ctx, req.ID, uint32(t.UserId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
costfree := r.Rate * float64(r.ExchangeNum)
|
|
||||||
|
|
||||||
if base.NewBaseSysUserService().GetFreeGold(t.UserId) < int64(costfree*100) {
|
|
||||||
err = fmt.Errorf("余额不足")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !r.UpdateTime.Add(1 * time.Hour).Before(gtime.Now()) {
|
|
||||||
err = fmt.Errorf("未到购买时间")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
base.NewBaseSysUserService().DuihuanFreeGold(uint32(r.PlayerID), int64(r.ExchangeNum*100), int64(costfree*95))
|
|
||||||
base.NewBaseSysUserService().DuihuanGold(uint32(t.UserId), int64(r.ExchangeNum*95), int64(costfree*100))
|
|
||||||
res = cool.Ok(nil)
|
res = cool.Ok(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"blazing/common/data"
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
|
baseservice "blazing/modules/base/service"
|
||||||
"blazing/modules/player/service"
|
"blazing/modules/player/service"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ItemBagController struct {
|
type ItemBagController struct {
|
||||||
@@ -13,10 +19,54 @@ func init() {
|
|||||||
var task_info_controller = &ItemBagController{
|
var task_info_controller = &ItemBagController{
|
||||||
&cool.Controller{
|
&cool.Controller{
|
||||||
Prefix: "/admin/game/item",
|
Prefix: "/admin/game/item",
|
||||||
Api: []string{"Delete", "Update", "Info", "List", "Page"},
|
Api: []string{"Add", "Delete", "Update", "Info", "List", "Page", "Grant"},
|
||||||
Service: service.NewItemService(0), //因为page已经过滤,所以这里需要改成0
|
Service: service.NewItemService(0), //因为page已经过滤,所以这里需要改成0
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// 注册路由
|
// 注册路由
|
||||||
cool.RegisterController(task_info_controller)
|
cool.RegisterController(task_info_controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ItemGrantReq struct {
|
||||||
|
g.Meta `path:"/grant" method:"POST"`
|
||||||
|
Authorization string `json:"Authorization" in:"header"`
|
||||||
|
PlayerID uint32 `json:"player_id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
ItemID int64 `json:"item_id" v:"required#请选择物品"`
|
||||||
|
ItemCnt int64 `json:"item_cnt" v:"required|min:1#请输入数量|数量必须大于0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ItemBagController) Grant(ctx context.Context, req *ItemGrantReq) (res *cool.BaseRes, err error) {
|
||||||
|
playerID := req.PlayerID
|
||||||
|
if playerID == 0 && req.Username != "" {
|
||||||
|
user := baseservice.NewBaseSysUserService().GetByUsername(req.Username)
|
||||||
|
if user == nil {
|
||||||
|
return cool.Fail("用户不存在"), nil
|
||||||
|
}
|
||||||
|
playerID = uint32(user.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if playerID == 0 {
|
||||||
|
return cool.Fail("请先选择玩家"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
items, err := service.NewItemService(playerID).AddItems([]data.ItemInfo{
|
||||||
|
{
|
||||||
|
ItemId: req.ItemID,
|
||||||
|
ItemCnt: req.ItemCnt,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return cool.Fail(err.Error()), nil
|
||||||
|
}
|
||||||
|
if len(items) == 0 {
|
||||||
|
return cool.Fail("发放失败,可能已达到物品上限"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cool.Ok(g.Map{
|
||||||
|
"player_id": playerID,
|
||||||
|
"item_id": req.ItemID,
|
||||||
|
"item_cnt": req.ItemCnt,
|
||||||
|
"items": items,
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -133,6 +133,34 @@ func (c *PetBagController) ModPrise(ctx context.Context, req *PriseReq) (res *co
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PetShowReq struct {
|
||||||
|
g.Meta `path:"/show" method:"POST"`
|
||||||
|
CatchTime uint32 `json:"catch_time"`
|
||||||
|
IsShow int `json:"is_show"` // 1=展示 0=取消展示
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PetBagController) Show(ctx context.Context, req *PetShowReq) (res *cool.BaseRes, err error) {
|
||||||
|
admin := cool.GetAdmin(ctx)
|
||||||
|
res = &cool.BaseRes{}
|
||||||
|
if req.CatchTime == 0 {
|
||||||
|
err = fmt.Errorf("invalid catch_time")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.IsShow != 0 && req.IsShow != 1 {
|
||||||
|
req.IsShow = 0
|
||||||
|
}
|
||||||
|
petSvc := service.NewPetService(uint32(admin.UserId))
|
||||||
|
pet := petSvc.PetInfoOneByCatchTime(req.CatchTime)
|
||||||
|
if pet == nil {
|
||||||
|
err = fmt.Errorf("pet not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !petSvc.UpdateIsShow(req.CatchTime, req.IsShow) {
|
||||||
|
err = fmt.Errorf("update failed")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type BuyPetReq struct {
|
type BuyPetReq struct {
|
||||||
g.Meta `path:"/buy" method:"POST"`
|
g.Meta `path:"/buy" method:"POST"`
|
||||||
Cid uint32 `json:"id"`
|
Cid uint32 `json:"id"`
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -118,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 == "" {
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -28,6 +27,7 @@ type Pet struct {
|
|||||||
PlayerID uint32 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
PlayerID uint32 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||||
Free int `gorm:"not null;default:0;comment:'是否放生'" json:"free"` //"0为放入仓库,1为放生,2为上架
|
Free int `gorm:"not null;default:0;comment:'是否放生'" json:"free"` //"0为放入仓库,1为放生,2为上架
|
||||||
CatchTime uint32 `gorm:"not null;comment:'捕捉时间'" json:"catch_time"` //唯一键
|
CatchTime uint32 `gorm:"not null;comment:'捕捉时间'" json:"catch_time"` //唯一键
|
||||||
|
IsShow int `gorm:"not null;default:0;comment:'是否基地展示'" json:"is_show"` //1=基地展示 0=未展示
|
||||||
|
|
||||||
SalePrice uint32 `gorm:"not null;default:0;comment:'出售价格'" json:"sale_price"`
|
SalePrice uint32 `gorm:"not null;default:0;comment:'出售价格'" json:"sale_price"`
|
||||||
SaleCount uint32 `gorm:"not null;default:0;comment:'出售次数'" json:"sale_count"`
|
SaleCount uint32 `gorm:"not null;default:0;comment:'出售次数'" json:"sale_count"`
|
||||||
@@ -128,7 +128,7 @@ type PetInfo struct {
|
|||||||
|
|
||||||
SkillListLen uint32 `struc:"sizeof=SkillList" json:"-"`
|
SkillListLen uint32 `struc:"sizeof=SkillList" json:"-"`
|
||||||
|
|
||||||
// 技能信息:固定4条,空则赋值0(固定长度List → [4]SkillInfo,零值即符合“赋值0”)
|
// 技能信息:固定4条,空则赋值0(固定长度List → [4]SkillInfo,零值即符合"赋值0")
|
||||||
SkillList []SkillInfo
|
SkillList []SkillInfo
|
||||||
|
|
||||||
CatchTime uint32 //显式忽略,不参与序列化
|
CatchTime uint32 //显式忽略,不参与序列化
|
||||||
@@ -154,11 +154,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,9 +180,13 @@ 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]
|
if i < len(pet.SkillList) {
|
||||||
|
pet.SkillList[i].ID = bm.SKill[i]
|
||||||
|
} else {
|
||||||
|
pet.SkillList = append(pet.SkillList, SkillInfo{ID: bm.SKill[i]})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ type BaseHouse struct {
|
|||||||
// 基础关联字段
|
// 基础关联字段
|
||||||
PlayerID uint64 `gorm:"not null;index:idx_player_house;comment:'所属玩家ID'" json:"player_id"`
|
PlayerID uint64 `gorm:"not null;index:idx_player_house;comment:'所属玩家ID'" json:"player_id"`
|
||||||
|
|
||||||
// 核心业务字段
|
// // 核心业务字段
|
||||||
// ShowPokemon 基地展示精灵ID列表(支持展示多个精灵)
|
// // ShowPokemon 基地展示精灵ID列表(支持展示多个精灵)
|
||||||
ShowPokemon []uint32 `gorm:"type:jsonb;default:'[]';comment:'基地展示精灵ID列表'" json:"show_pokemon"`
|
// ShowPokemon []uint32 `gorm:"type:jsonb;default:'[]';comment:'基地展示精灵ID列表'" json:"show_pokemon"`
|
||||||
|
|
||||||
UsedItems string `gorm:"type:jsonb;default:'{}';comment:'用户物品列表(物品ID:数量)'" json:"used_items"`
|
UsedItems string `gorm:"type:jsonb;default:'{}';comment:'用户物品列表(物品ID:数量)'" json:"used_items"`
|
||||||
|
|
||||||
@@ -57,11 +57,6 @@ type FitmentShowInfo struct {
|
|||||||
Status uint32 `json:"status"`
|
Status uint32 `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateShowPokemon 更新基地展示精灵列表
|
|
||||||
func (bh *BaseHouse) UpdateShowPokemon(pokemonIDs []uint32) {
|
|
||||||
bh.ShowPokemon = pokemonIDs
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------- 初始化创建表 ---------------
|
// --------------- 初始化创建表 ---------------
|
||||||
func init() {
|
func init() {
|
||||||
// 初始化时创建基地房型表(与现有Talk表初始化逻辑一致)
|
// 初始化时创建基地房型表(与现有Talk表初始化逻辑一致)
|
||||||
|
|||||||
@@ -26,12 +26,18 @@ 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不存在")
|
||||||
}
|
}
|
||||||
|
if cfg.Type != configservice.CDKTypeReward {
|
||||||
|
return nil, fmt.Errorf("当前CDK不是游戏内奖励类型")
|
||||||
|
}
|
||||||
if cfg.BindUserId != 0 && cfg.BindUserId != s.userid {
|
if cfg.BindUserId != 0 && cfg.BindUserId != s.userid {
|
||||||
return nil, fmt.Errorf("CDK已绑定其他用户")
|
return nil, fmt.Errorf("CDK已绑定其他用户")
|
||||||
}
|
}
|
||||||
@@ -45,42 +51,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 {
|
||||||
|
|||||||
@@ -2,26 +2,81 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
|
basemodel "blazing/modules/base/model"
|
||||||
"blazing/modules/base/service"
|
"blazing/modules/base/service"
|
||||||
"blazing/modules/player/model"
|
"blazing/modules/player/model"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
"github.com/gogf/gf/v2/util/gconv"
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
"github.com/gogf/gf/v2/util/grand"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type GoldListService struct {
|
type GoldListService struct {
|
||||||
BaseService
|
BaseService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *GoldListService) validateOrderInput(exchangeNum uint32, rate float64) error {
|
||||||
|
if exchangeNum == 0 {
|
||||||
|
return gerror.New("出售数量必须大于0")
|
||||||
|
}
|
||||||
|
if rate <= 0 {
|
||||||
|
return gerror.New("汇率必须大于0")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GoldListService) validateOrderRecord(order *model.GoldBeanOrder) error {
|
||||||
|
if order == nil || order.ID == 0 {
|
||||||
|
return gerror.New("重复订单")
|
||||||
|
}
|
||||||
|
return s.validateOrderInput(order.ExchangeNum, order.Rate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GoldListService) getPrevDayAvgRate() float64 {
|
||||||
|
var (
|
||||||
|
now = gtime.Now()
|
||||||
|
yesterday = now.AddDate(0, 0, -1)
|
||||||
|
dayStart = gtime.NewFromStr(yesterday.Format("Y-m-d") + " 00:00:00")
|
||||||
|
nextDay = dayStart.AddDate(0, 0, 1)
|
||||||
|
result struct {
|
||||||
|
AvgRate float64 `json:"avg_rate"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
err := s.dbm_fix(s.Model).
|
||||||
|
Fields("COALESCE(AVG(rate), 0) AS avg_rate").
|
||||||
|
Where("status", 1).
|
||||||
|
WhereGTE("createTime", dayStart).
|
||||||
|
WhereLT("createTime", nextDay).
|
||||||
|
Scan(&result)
|
||||||
|
if err != nil || result.AvgRate <= 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.AvgRate
|
||||||
|
}
|
||||||
|
|
||||||
func (s *GoldListService) ModifyBefore(ctx context.Context, method string, param map[string]interface{}) (err error) {
|
func (s *GoldListService) ModifyBefore(ctx context.Context, method string, param map[string]interface{}) (err error) {
|
||||||
admin := cool.GetAdmin(ctx)
|
admin := cool.GetAdmin(ctx)
|
||||||
userId := admin.UserId
|
userId := admin.UserId
|
||||||
|
|
||||||
if method == "Add" {
|
if method == "Add" {
|
||||||
if service.NewBaseSysUserService().GetGold(userId) < gconv.Int64(param["exchange_num"])*100 {
|
var (
|
||||||
|
exchangeNum = gconv.Uint32(param["exchange_num"])
|
||||||
|
rate = gconv.Float64(param["rate"])
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = s.validateOrderInput(exchangeNum, rate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if service.NewBaseSysUserService().GetGold(userId) < int64(exchangeNum)*100 {
|
||||||
return fmt.Errorf("金额不足")
|
return fmt.Errorf("金额不足")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,14 +84,15 @@ func (s *GoldListService) ModifyBefore(ctx context.Context, method string, param
|
|||||||
if t > 0 {
|
if t > 0 {
|
||||||
return fmt.Errorf("不允许多挂单")
|
return fmt.Errorf("不允许多挂单")
|
||||||
}
|
}
|
||||||
if gconv.Float64(param["rate"]) > 2 {
|
|
||||||
r := g.List{}
|
|
||||||
for i := 0; i < grand.N(1, 3); i++ {
|
|
||||||
r = append(r, g.Map{"rate": param["rate"], "exchange_num": param["exchange_num"], "player_id": 10001})
|
|
||||||
}
|
|
||||||
|
|
||||||
s.dbm_fix(s.Model).Data(r).Insert()
|
var (
|
||||||
|
prevAvg = s.getPrevDayAvgRate()
|
||||||
|
minRate = math.Round(prevAvg*0.9*10000) / 10000
|
||||||
|
maxRate = math.Round(prevAvg*1.1*10000) / 10000
|
||||||
|
)
|
||||||
|
|
||||||
|
if rate < minRate || rate > maxRate {
|
||||||
|
return fmt.Errorf("不允许超过10%%的单子,当前需在昨日均价 %.4f 的上下10%%范围内(%.4f - %.4f)", prevAvg, minRate, maxRate)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var items []model.GoldBeanOrder
|
var items []model.GoldBeanOrder
|
||||||
@@ -60,6 +116,9 @@ func (s *GoldListService) ModifyBefore(ctx context.Context, method string, param
|
|||||||
func (s *GoldListService) Done(listid uint32) (*model.GoldBeanOrder, error) {
|
func (s *GoldListService) Done(listid uint32) (*model.GoldBeanOrder, error) {
|
||||||
var rr model.GoldBeanOrder
|
var rr model.GoldBeanOrder
|
||||||
s.dbm_fix(s.Model).Where("id", listid).Scan(&rr)
|
s.dbm_fix(s.Model).Where("id", listid).Scan(&rr)
|
||||||
|
if err := s.validateOrderRecord(&rr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
r, err := s.dbm_fix(s.Model).Data("status", 1).Where("status", 0).Where("id", listid).Update()
|
r, err := s.dbm_fix(s.Model).Data("status", 1).Where("status", 0).Where("id", listid).Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -70,9 +129,108 @@ func (s *GoldListService) Done(listid uint32) (*model.GoldBeanOrder, error) {
|
|||||||
}
|
}
|
||||||
return &rr, nil
|
return &rr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *GoldListService) Trade(ctx context.Context, listid uint32, buyerID uint32) (*model.GoldBeanOrder, error) {
|
||||||
|
var result *model.GoldBeanOrder
|
||||||
|
|
||||||
|
err := g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||||
|
var (
|
||||||
|
order model.GoldBeanOrder
|
||||||
|
buyer basemodel.BaseSysUser
|
||||||
|
seller basemodel.BaseSysUser
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := tx.Model(s.Model).Where("id", listid).Where("status", 0).Scan(&order); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.validateOrderRecord(&order); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !order.UpdateTime.Add(1 * time.Hour).Before(gtime.Now()) {
|
||||||
|
return fmt.Errorf("未到购买时间")
|
||||||
|
}
|
||||||
|
|
||||||
|
costFree := int64(math.Round(order.Rate * float64(order.ExchangeNum) * 100))
|
||||||
|
sellerGold := int64(order.ExchangeNum) * 100
|
||||||
|
sellerIncome := int64(math.Round(order.Rate * float64(order.ExchangeNum) * 95))
|
||||||
|
buyerGold := int64(order.ExchangeNum * 95)
|
||||||
|
|
||||||
|
if err := tx.Model(basemodel.BaseSysUser{}).Where("id", buyerID).Fields("free_gold").Scan(&buyer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if buyer.FreeGold < costFree {
|
||||||
|
return fmt.Errorf("余额不足")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Model(basemodel.BaseSysUser{}).Where("id", order.PlayerID).Fields("goldbean").Scan(&seller); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if seller.GoldBean < sellerGold {
|
||||||
|
return fmt.Errorf("卖家余额不足")
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := tx.Model(basemodel.BaseSysUser{}).
|
||||||
|
Where("id", buyerID).
|
||||||
|
WhereGTE("free_gold", costFree).
|
||||||
|
Increment("free_gold", -costFree)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rows, _ := res.RowsAffected()
|
||||||
|
if rows == 0 {
|
||||||
|
return fmt.Errorf("余额不足")
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = tx.Model(basemodel.BaseSysUser{}).
|
||||||
|
Where("id", order.PlayerID).
|
||||||
|
WhereGTE("goldbean", sellerGold).
|
||||||
|
Increment("goldbean", -sellerGold)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rows, _ = res.RowsAffected()
|
||||||
|
if rows == 0 {
|
||||||
|
return fmt.Errorf("卖家余额不足")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = tx.Model(basemodel.BaseSysUser{}).Where("id", order.PlayerID).Increment("free_gold", sellerIncome); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = tx.Model(basemodel.BaseSysUser{}).Where("id", buyerID).Increment("goldbean", buyerGold); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = tx.Model(s.Model).
|
||||||
|
Data("status", 1).
|
||||||
|
Where("id", listid).
|
||||||
|
Where("status", 0).
|
||||||
|
Update()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rows, _ = res.RowsAffected()
|
||||||
|
if rows == 0 {
|
||||||
|
return fmt.Errorf("重复订单")
|
||||||
|
}
|
||||||
|
|
||||||
|
result = &order
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
func (s *GoldListService) Get() []model.GoldBeanOrder {
|
func (s *GoldListService) Get() []model.GoldBeanOrder {
|
||||||
var rr []model.GoldBeanOrder
|
var rr []model.GoldBeanOrder
|
||||||
s.dbm_fix(s.Model).Where("status", 0).Order("rate", "asc").Scan(&rr)
|
s.dbm_fix(s.Model).
|
||||||
|
Where("status", 0).
|
||||||
|
WhereGT("exchange_num", 0).
|
||||||
|
WhereGT("rate", 0).
|
||||||
|
Order("rate", "asc").
|
||||||
|
Scan(&rr)
|
||||||
|
|
||||||
return rr
|
return rr
|
||||||
}
|
}
|
||||||
@@ -90,6 +248,9 @@ func NewGoldListService(id uint32) *GoldListService {
|
|||||||
},
|
},
|
||||||
ListQueryOp: &cool.QueryOp{
|
ListQueryOp: &cool.QueryOp{
|
||||||
|
|
||||||
|
Extend: func(ctx g.Ctx, m *gdb.Model) *gdb.Model {
|
||||||
|
return m.WhereGT("exchange_num", 0).WhereGT("rate", 0)
|
||||||
|
},
|
||||||
AddOrderby: g.MapStrStr{"updateTime": "asc"},
|
AddOrderby: g.MapStrStr{"updateTime": "asc"},
|
||||||
},
|
},
|
||||||
InsertParam: func(ctx context.Context) g.MapStrAny {
|
InsertParam: func(ctx context.Context) g.MapStrAny {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"blazing/common/data"
|
"blazing/common/data"
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
|
basemodel "blazing/modules/base/model"
|
||||||
"blazing/modules/player/model"
|
"blazing/modules/player/model"
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -345,17 +346,32 @@ func NewItemService(id uint32) *ItemService {
|
|||||||
Service: &cool.Service{Model: model.NewPlayerBag(), UniqueKey: map[string]string{
|
Service: &cool.Service{Model: model.NewPlayerBag(), UniqueKey: map[string]string{
|
||||||
"player_id": "角色名称不能重复",
|
"player_id": "角色名称不能重复",
|
||||||
}, PageQueryOp: &cool.QueryOp{
|
}, PageQueryOp: &cool.QueryOp{
|
||||||
KeyWordField: []string{"player_id"},
|
Select: `player_item.*, base_sys_user.username`,
|
||||||
FieldEQ: []string{"player_id"},
|
Join: []*cool.JoinOp{
|
||||||
|
{
|
||||||
|
Model: basemodel.NewBaseSysUser(),
|
||||||
|
Alias: "base_sys_user",
|
||||||
|
Type: cool.LeftJoin,
|
||||||
|
Condition: `player_item.player_id = base_sys_user.id`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
KeyWordField: []string{"player_item.player_id", "base_sys_user.username"},
|
||||||
|
FieldEQ: []string{"player_id", "item_id"},
|
||||||
Where: func(ctx context.Context) [][]interface{} {
|
Where: func(ctx context.Context) [][]interface{} {
|
||||||
var (
|
var (
|
||||||
//admin = cool.GetAdmin(ctx)
|
r = g.RequestFromCtx(ctx)
|
||||||
//userId = admin.UserId
|
|
||||||
)
|
)
|
||||||
return [][]interface{}{
|
|
||||||
|
where := [][]interface{}{
|
||||||
// {"player_id", userId, true},
|
// {"player_id", userId, true},
|
||||||
// {"free", 0, true},
|
// {"free", 0, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if username := strings.TrimSpace(r.Get("username").String()); username != "" {
|
||||||
|
where = append(where, []interface{}{`LOWER(base_sys_user.username) LIKE ?`, "%" + strings.ToLower(username) + "%"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return where
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -76,8 +76,61 @@ func (s *PetService) PetCount(flag int) int {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PetService) UpdateFree(catchTime, free uint32) bool {
|
// GetShowPets 获取基地展示的精灵列表(is_show=1)
|
||||||
res, err := s.dbm(s.Model).Where("catch_time", catchTime).Data("free", free).Update()
|
func (s *PetService) GetShowPets() []model.Pet {
|
||||||
|
var tt []model.Pet
|
||||||
|
if err := s.dbm_fix(s.Model).Where("is_show", 1).Scan(&tt); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for i := range tt {
|
||||||
|
tt[i].Data.CatchTime = tt[i].CatchTime
|
||||||
|
}
|
||||||
|
return tt
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateIsShow 更新精灵基地展示状态
|
||||||
|
func (s *PetService) UpdateIsShow(catchTime uint32, isShow int) bool {
|
||||||
|
res, err := s.dbm(s.Model).
|
||||||
|
Where("catch_time", catchTime).
|
||||||
|
Data("is_show", isShow).
|
||||||
|
Update()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
r, _ := res.RowsAffected()
|
||||||
|
return r > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetShowCatchTimes 批量设置基地展示精灵:
|
||||||
|
// 先清空当前玩家的展示状态,再将传入 catchTime 列表置为展示。
|
||||||
|
func (s *PetService) SetShowCatchTimes(catchTimes []uint32) bool {
|
||||||
|
if _, err := s.dbm(s.Model).
|
||||||
|
Where("is_show", 1).
|
||||||
|
Data("is_show", 0).
|
||||||
|
Update(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(catchTimes) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, err := s.dbm(s.Model).
|
||||||
|
WhereIn("catch_time", catchTimes).
|
||||||
|
Data("is_show", 1).
|
||||||
|
Update(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PetService) UpdateFree(catchTime, fromFree, toFree uint32) bool {
|
||||||
|
res, err := s.dbm(s.Model).
|
||||||
|
Where("catch_time", catchTime).
|
||||||
|
Where("free", fromFree).
|
||||||
|
Data(g.Map{
|
||||||
|
"free": toFree,
|
||||||
|
"is_show": 0, // 放回背包/放生时取消基地展示
|
||||||
|
}).
|
||||||
|
Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -105,6 +158,7 @@ func (s *PetService) UpdatePrice(catchTime, price, free uint32) error {
|
|||||||
res, _ := s.dbm(s.Model).Where("catch_time", catchTime).Data(g.Map{
|
res, _ := s.dbm(s.Model).Where("catch_time", catchTime).Data(g.Map{
|
||||||
"sale_price": price,
|
"sale_price": price,
|
||||||
"free": free,
|
"free": free,
|
||||||
|
"is_show": 0, // 上架/下架时取消基地展示
|
||||||
}).Update()
|
}).Update()
|
||||||
affected, _ := res.RowsAffected()
|
affected, _ := res.RowsAffected()
|
||||||
if affected > 0 && free != 2 && feeRate != 0 {
|
if affected > 0 && free != 2 && feeRate != 0 {
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ func (s *UserService) PetFusionTx(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := syncPetSnapshotBeforeDeleteTx(tx, userID, currentInfo, masterCatchTime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := deletePetTx(tx, userID, masterCatchTime); err != nil {
|
if err := deletePetTx(tx, userID, masterCatchTime); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -82,6 +85,9 @@ func (s *UserService) PetFusionTx(
|
|||||||
}
|
}
|
||||||
result.CostItemUsed = used
|
result.CostItemUsed = used
|
||||||
if !used {
|
if !used {
|
||||||
|
if err := syncPetSnapshotBeforeDeleteTx(tx, userID, currentInfo, auxCatchTime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := deletePetTx(tx, userID, auxCatchTime); err != nil {
|
if err := deletePetTx(tx, userID, auxCatchTime); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -223,6 +229,28 @@ func updatePetDataTx(tx gdb.TX, userID uint32, catchTime uint32, pet model.PetIn
|
|||||||
return nil
|
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 {
|
func deletePetTx(tx gdb.TX, userID uint32, catchTime uint32) error {
|
||||||
res, err := tx.Model(model.NewPet()).
|
res, err := tx.Model(model.NewPet()).
|
||||||
Where("player_id", userID).
|
Where("player_id", userID).
|
||||||
|
|||||||
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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -41,19 +41,6 @@ func (s *RoomService) Set(id []model.FitmentShowInfo) {
|
|||||||
ttt.PlayerID = uint64(s.userid)
|
ttt.PlayerID = uint64(s.userid)
|
||||||
m.Save(ttt)
|
m.Save(ttt)
|
||||||
|
|
||||||
}
|
|
||||||
func (s *RoomService) Show(cactime []uint32) {
|
|
||||||
|
|
||||||
//todo待测试
|
|
||||||
var ttt model.BaseHouseEx
|
|
||||||
m := s.dbm(s.Model)
|
|
||||||
|
|
||||||
m.Scan(&ttt)
|
|
||||||
ttt.ShowPokemon = cactime
|
|
||||||
|
|
||||||
ttt.PlayerID = uint64(s.userid)
|
|
||||||
m.Save(ttt)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// /添加进来的物品一定是保证存在的
|
// /添加进来的物品一定是保证存在的
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
|
configservice "blazing/modules/config/service"
|
||||||
"blazing/modules/player/model"
|
"blazing/modules/player/model"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -27,6 +28,19 @@ func (s *TaskService) ShopRequirementError() error {
|
|||||||
return errors.New("请先完成试炼任务50")
|
return errors.New("请先完成试炼任务50")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var darkTask *model.Task
|
||||||
|
s.dbm(s.Model).Where("task_id", 110).Scan(&darkTask)
|
||||||
|
darkTaskBits := bitset32.New(0)
|
||||||
|
if darkTask != nil {
|
||||||
|
darkTaskBits = bitset32.From(darkTask.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, level := range configservice.NewTower110Service().EnabledLevels() {
|
||||||
|
if !darkTaskBits.Test(uint(level)) {
|
||||||
|
return errors.New("请先完成暗黑门全部已开放关卡")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user