Compare commits
3 Commits
f433a26a6d
...
40ec827342
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40ec827342 | ||
|
|
a16a06e389 | ||
|
|
5b37d9493b |
@@ -44,7 +44,7 @@ func (Controller) PlayerFightBoss(req *ChallengeBossInboundInfo, p *player.Playe
|
||||
ai := player.NewAI_player(monsterInfo)
|
||||
ai.CanCapture = resolveBossCaptureRate(bossConfigs[0].IsCapture, leadMonsterID)
|
||||
ai.BossScript = bossConfigs[0].Script
|
||||
ai.Prop[0] = 2
|
||||
ai.AddBattleProp(0, 2)
|
||||
|
||||
var fightC *fight.FightC
|
||||
fightC, err = fight.NewFight(p, ai, p.GetPetInfo(100), ai.GetPetInfo(0), func(foi model.FightOverInfo) {
|
||||
|
||||
@@ -166,14 +166,7 @@ func refreshPetPaneKeepHP(currentPet *model.PetInfo, hp uint32) {
|
||||
|
||||
// handleRegularPetItem 处理普通宠物道具
|
||||
func (h Controller) handleRegularPetItem(itemID uint32, currentPet *model.PetInfo) errorcode.ErrorCode {
|
||||
handler := item.PetItemRegistry.GetHandler(itemID)
|
||||
if handler == nil {
|
||||
return errorcode.ErrorCodes.ErrItemUnusable
|
||||
}
|
||||
if !handler(itemID, currentPet) {
|
||||
return errorcode.ErrorCodes.ErrItemUnusable
|
||||
}
|
||||
return 0
|
||||
return item.PetItemRegistry.Handle(itemID, currentPet)
|
||||
}
|
||||
|
||||
// ResetNature 重置宠物性格
|
||||
|
||||
75
logic/service/fight/boss/NewSeIdx_247.go
Normal file
75
logic/service/fight/boss/NewSeIdx_247.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package effect
|
||||
|
||||
import (
|
||||
"blazing/logic/service/fight/action"
|
||||
"blazing/logic/service/fight/input"
|
||||
)
|
||||
|
||||
// 247. 固定增加体力/攻击/防御/特攻/特防/速度;(a1-a6: hp/atk/def/spatk/spdef/spd)
|
||||
type NewSel247 struct {
|
||||
NewSel0
|
||||
}
|
||||
|
||||
func (e *NewSel247) TurnStart(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) {
|
||||
if !e.IsOwner() {
|
||||
return
|
||||
}
|
||||
|
||||
pet := e.Ctx().Our.CurPet[0]
|
||||
if pet == nil {
|
||||
return
|
||||
}
|
||||
|
||||
hpBonus := uint32(e.Args()[0].IntPart())
|
||||
if hpBonus > 0 {
|
||||
pet.Info.MaxHp += hpBonus
|
||||
pet.Info.Hp += hpBonus
|
||||
}
|
||||
|
||||
for i, propIdx := range []int{0, 1, 2, 3, 4} {
|
||||
add := uint32(e.Args()[i+1].IntPart())
|
||||
if add == 0 {
|
||||
continue
|
||||
}
|
||||
pet.Info.Prop[propIdx] += add
|
||||
}
|
||||
}
|
||||
|
||||
func (e *NewSel247) TurnEnd() {
|
||||
if !e.IsOwner() {
|
||||
return
|
||||
}
|
||||
|
||||
pet := e.Ctx().Our.CurPet[0]
|
||||
if pet == nil {
|
||||
return
|
||||
}
|
||||
|
||||
hpBonus := uint32(e.Args()[0].IntPart())
|
||||
if hpBonus > 0 {
|
||||
if pet.Info.MaxHp > hpBonus {
|
||||
pet.Info.MaxHp -= hpBonus
|
||||
} else {
|
||||
pet.Info.MaxHp = 1
|
||||
}
|
||||
if pet.Info.Hp > pet.Info.MaxHp {
|
||||
pet.Info.Hp = pet.Info.MaxHp
|
||||
}
|
||||
}
|
||||
|
||||
for i, propIdx := range []int{0, 1, 2, 3, 4} {
|
||||
sub := uint32(e.Args()[i+1].IntPart())
|
||||
if sub == 0 {
|
||||
continue
|
||||
}
|
||||
if pet.Info.Prop[propIdx] > sub {
|
||||
pet.Info.Prop[propIdx] -= sub
|
||||
} else {
|
||||
pet.Info.Prop[propIdx] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
input.InitEffect(input.EffectType.NewSel, 247, &NewSel247{})
|
||||
}
|
||||
@@ -11,10 +11,9 @@ type NewSel26 struct {
|
||||
}
|
||||
|
||||
func (e *NewSel26) TurnStart(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) {
|
||||
e.Ctx().Our.CurPet[0].Info.Prop[int(e.Args()[0].IntPart())] += uint32(e.Args()[1].IntPart())
|
||||
}
|
||||
|
||||
func (e *NewSel26) TurnEnd() {
|
||||
e.Ctx().Our.CurPet[0].Info.Prop[int(e.Args()[0].IntPart())] -= uint32(e.Args()[1].IntPart())
|
||||
}
|
||||
func init() {
|
||||
input.InitEffect(input.EffectType.NewSel, 26, &NewSel26{})
|
||||
|
||||
@@ -27,16 +27,26 @@ const (
|
||||
// 2. targetIndex 始终表示目标在所属阵营内的槽位。
|
||||
// 3. targetRelation 用来区分 targetIndex 属于敌方、自己还是队友。
|
||||
type FightActionEnvelope struct {
|
||||
ActionType FightActionType `json:"actionType"`
|
||||
ActorIndex int `json:"actorIndex"`
|
||||
TargetIndex int `json:"targetIndex"`
|
||||
TargetRelation uint8 `json:"targetRelation,omitempty"`
|
||||
SkillID uint32 `json:"skillId,omitempty"`
|
||||
ItemID uint32 `json:"itemId,omitempty"`
|
||||
CatchTime uint32 `json:"catchTime,omitempty"`
|
||||
Escape bool `json:"escape,omitempty"`
|
||||
Chat string `json:"chat,omitempty"`
|
||||
AtkType uint8 `json:"atkType,omitempty"`
|
||||
// ActionType 当前动作类型,例如 skill、item、change、escape、chat。
|
||||
ActionType FightActionType `json:"actionType"`
|
||||
// ActorIndex 发起动作的我方槽位。
|
||||
ActorIndex int `json:"actorIndex"`
|
||||
// TargetIndex 目标在所属阵营中的槽位下标。
|
||||
TargetIndex int `json:"targetIndex"`
|
||||
// TargetRelation 目标关系:0=对方,1=自己,2=队友。
|
||||
TargetRelation uint8 `json:"targetRelation,omitempty"`
|
||||
// SkillID 技能 ID;仅技能动作使用。
|
||||
SkillID uint32 `json:"skillId,omitempty"`
|
||||
// ItemID 道具 ID;仅道具动作使用。
|
||||
ItemID uint32 `json:"itemId,omitempty"`
|
||||
// CatchTime 精灵实例 ID;切宠或部分道具动作使用。
|
||||
CatchTime uint32 `json:"catchTime,omitempty"`
|
||||
// Escape 是否为逃跑动作;主要用于协议层兼容和调试。
|
||||
Escape bool `json:"escape,omitempty"`
|
||||
// Chat 聊天内容;仅聊天动作使用。
|
||||
Chat string `json:"chat,omitempty"`
|
||||
// AtkType 前端技能目标类型兜底值,沿用技能表 AtkType 定义。
|
||||
AtkType uint8 `json:"atkType,omitempty"`
|
||||
}
|
||||
|
||||
// NewSkillActionEnvelope 构造技能动作 envelope。
|
||||
|
||||
@@ -41,6 +41,35 @@ func (t *BattlePetEntity) Alive() bool {
|
||||
|
||||
}
|
||||
|
||||
func (t *BattlePetEntity) AddBattleAttr(attr int, value uint32) {
|
||||
if t == nil || value == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
switch attr {
|
||||
case 0:
|
||||
t.Info.MaxHp += value
|
||||
t.Info.Hp += value
|
||||
case 1, 2, 3, 4, 5:
|
||||
t.Info.Prop[attr-1] += value
|
||||
}
|
||||
}
|
||||
|
||||
func (t *BattlePetEntity) ApplyInitEffectBonus(effect model.PetEffectInfo) {
|
||||
if t == nil || effect.EID != 26 || len(effect.Args) < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i+1 < len(effect.Args); i += 2 {
|
||||
attr := effect.Args[i]
|
||||
value := effect.Args[i+1]
|
||||
if value <= 0 {
|
||||
continue
|
||||
}
|
||||
t.AddBattleAttr(attr, uint32(value))
|
||||
}
|
||||
}
|
||||
|
||||
// 创建精灵实例
|
||||
func CreateBattlePetEntity(info model.PetInfo) *BattlePetEntity {
|
||||
ret := &BattlePetEntity{}
|
||||
|
||||
@@ -45,7 +45,7 @@ type FighterState struct {
|
||||
Level uint32 `json:"level"`
|
||||
// Anger 怒气值;当前服务端主链路暂未实际填充时默认为 0,先为协议对齐预留。
|
||||
Anger uint32 `json:"anger"`
|
||||
// Status 当前异常/增益状态回合数组;下标语义沿用现有战斗状态定义。
|
||||
// Status 当前异常或增益状态回合数组;下标语义沿用现有战斗状态定义。
|
||||
Status [20]int8 `json:"status"`
|
||||
// Prop 当前能力等级变化数组:攻击、防御、特攻、特防、速度、命中。
|
||||
Prop [6]int8 `json:"prop"`
|
||||
@@ -55,30 +55,42 @@ type FighterState struct {
|
||||
|
||||
// FightStateMeta 是统一状态包的公共元数据。
|
||||
type FightStateMeta struct {
|
||||
Round uint32 `json:"round"`
|
||||
Weather uint32 `json:"weather,omitempty"`
|
||||
WinnerID uint32 `json:"winnerId,omitempty"`
|
||||
Reason model.EnumBattleOverReason `json:"reason,omitempty"`
|
||||
LegacyCmd uint32 `json:"legacyCmd,omitempty"`
|
||||
// Round 当前回合数。
|
||||
Round uint32 `json:"round"`
|
||||
// Weather 当前天气或场地编号;当前主链路未填充时可为 0。
|
||||
Weather uint32 `json:"weather,omitempty"`
|
||||
// WinnerID 当前已确定的胜者 ID;未结束时通常为 0。
|
||||
WinnerID uint32 `json:"winnerId,omitempty"`
|
||||
// Reason 当前已确定的结束原因;未结束时通常为 0。
|
||||
Reason model.EnumBattleOverReason `json:"reason,omitempty"`
|
||||
// LegacyCmd 对应旧协议命令号,便于新旧包对照和过渡期调试。
|
||||
LegacyCmd uint32 `json:"legacyCmd,omitempty"`
|
||||
}
|
||||
|
||||
// FightSkillHurtState 保存技能结算后的左右两侧战报快照。
|
||||
// FightSkillHurtState 保存技能结算阶段的详细战报。
|
||||
type FightSkillHurtState struct {
|
||||
Left []model.AttackValue `json:"left,omitempty"`
|
||||
// Left 我方阵营本次技能结算后的攻击值快照列表。
|
||||
Left []model.AttackValue `json:"left,omitempty"`
|
||||
// Right 敌方阵营本次技能结算后的攻击值快照列表。
|
||||
Right []model.AttackValue `json:"right,omitempty"`
|
||||
}
|
||||
|
||||
// FightLoadState 保存加载进度信息。
|
||||
type FightLoadState struct {
|
||||
UserID uint32 `json:"userId"`
|
||||
// UserID 当前上报加载进度的玩家 ID。
|
||||
UserID uint32 `json:"userId"`
|
||||
// Percent 当前加载百分比。
|
||||
Percent uint32 `json:"percent"`
|
||||
}
|
||||
|
||||
// FightChatState 保存战斗内聊天信息。
|
||||
type FightChatState struct {
|
||||
SenderID uint32 `json:"senderId"`
|
||||
// SenderID 发言玩家 ID。
|
||||
SenderID uint32 `json:"senderId"`
|
||||
// SenderNickname 发言玩家昵称。
|
||||
SenderNickname string `json:"senderNickname"`
|
||||
Message string `json:"message"`
|
||||
// Message 聊天内容。
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// FightStateEnvelope 是统一出站状态结构。
|
||||
|
||||
@@ -167,6 +167,7 @@ func (our *Input) SortPet() {
|
||||
|
||||
t.Duration(-1)
|
||||
|
||||
s.ApplyInitEffectBonus(e1)
|
||||
our.AddEffect(our, t)
|
||||
}
|
||||
|
||||
@@ -338,9 +339,6 @@ func (our *Input) Parseskill(skill *action.SelectSkillAction) {
|
||||
args := xmlres.EffectArgs[v]
|
||||
t := our.InitEffect(EffectType.Skill, v, temparg[:args]...)
|
||||
|
||||
|
||||
|
||||
|
||||
//这里是给双方添加buff
|
||||
if t != nil {
|
||||
// t.SetArgs(our, temparg[:args]...) //设置入参,施加方永远是我方
|
||||
|
||||
@@ -24,6 +24,26 @@ import (
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
func consumeLimitedPetEffects(pet *model.PetInfo) {
|
||||
if pet == nil || len(pet.EffectInfo) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
next := pet.EffectInfo[:0]
|
||||
for _, eff := range pet.EffectInfo {
|
||||
if eff.Status == 2 {
|
||||
if eff.LeftCount > 0 {
|
||||
eff.LeftCount--
|
||||
}
|
||||
if eff.LeftCount == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
next = append(next, eff)
|
||||
}
|
||||
pet.EffectInfo = next
|
||||
}
|
||||
|
||||
func (f *FightC) battleLoop() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil { // 恢复 panic,err 为 panic 错误值
|
||||
@@ -69,26 +89,28 @@ func (f *FightC) battleLoop() {
|
||||
tt.Alive(false) //将所有属性变化失效掉
|
||||
return true
|
||||
})
|
||||
if f.Info.Mode != info.BattleMode.PET_MELEE { //不是乱斗,传回血量
|
||||
for i := 0; i < len(ff.AllPet); i++ {
|
||||
for j := 0; j < len(ff.Player.GetInfo().PetList); j++ {
|
||||
if ff.Player.GetInfo().PetList[j].CatchTime == ff.AllPet[i].Info.CatchTime {
|
||||
|
||||
if ff.UserID == f.WinnerId {
|
||||
currentPet := ff.CurrentPet()
|
||||
if currentPet != nil && currentPet.Info.CatchTime == ff.Player.GetInfo().PetList[j].CatchTime {
|
||||
f.Winpet = &ff.Player.GetInfo().PetList[j]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ff.Player.GetInfo().PetList[j].Hp = utils.Min(ff.Player.GetInfo().PetList[j].MaxHp, ff.AllPet[i].Info.Hp)
|
||||
ff.Player.GetInfo().PetList[j].SkillList = ff.AllPet[i].Info.SkillList
|
||||
}
|
||||
|
||||
for i := 0; i < len(ff.AllPet); i++ {
|
||||
consumeLimitedPetEffects(&ff.AllPet[i].Info)
|
||||
for j := 0; j < len(ff.Player.GetInfo().PetList); j++ {
|
||||
if ff.Player.GetInfo().PetList[j].CatchTime != ff.AllPet[i].Info.CatchTime {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
ff.Player.GetInfo().PetList[j].EffectInfo = ff.AllPet[i].Info.EffectInfo
|
||||
if f.Info.Mode == info.BattleMode.PET_MELEE {
|
||||
continue
|
||||
}
|
||||
|
||||
if ff.UserID == f.WinnerId {
|
||||
currentPet := ff.CurrentPet()
|
||||
if currentPet != nil && currentPet.Info.CatchTime == ff.Player.GetInfo().PetList[j].CatchTime {
|
||||
f.Winpet = &ff.Player.GetInfo().PetList[j]
|
||||
}
|
||||
}
|
||||
|
||||
ff.Player.GetInfo().PetList[j].Hp = utils.Min(ff.Player.GetInfo().PetList[j].MaxHp, ff.AllPet[i].Info.Hp)
|
||||
ff.Player.GetInfo().PetList[j].SkillList = ff.AllPet[i].Info.SkillList
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
@@ -154,7 +154,7 @@ func buildFight(opts *fightBuildOptions) (*FightC, errorcode.ErrorCode) {
|
||||
if ai.CanCapture > 0 {
|
||||
opp.CanCapture = ai.CanCapture
|
||||
}
|
||||
opp.AttackValue.Prop = ai.Prop
|
||||
ai.ApplyBattleProps(opp.AttackValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package item
|
||||
|
||||
import (
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/common/socket/errorcode"
|
||||
"blazing/common/utils"
|
||||
"blazing/modules/player/model"
|
||||
"strings"
|
||||
@@ -111,6 +112,71 @@ func nvfunc(itemid uint32, onpet *model.PetInfo) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func handleNewSeIdxPetItem(itemid uint32, onpet *model.PetInfo) errorcode.ErrorCode {
|
||||
itemCfg, ok := xmlres.ItemsMAP[int(itemid)]
|
||||
if !ok {
|
||||
return errorcode.ErrorCodes.ErrItemUnusable
|
||||
}
|
||||
if itemCfg.NewSeIdx == 0 {
|
||||
if itemCfg.MaxHPUp > 0 {
|
||||
if !onpet.AddMaxHPUpEffect(itemid, itemCfg.MaxHPUp) {
|
||||
return errorcode.ErrorCodes.ErrCannotInjectPillAgain
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return errorcode.ErrorCodes.ErrItemUnusable
|
||||
}
|
||||
|
||||
effectCfg, ok := xmlres.EffectMAP[itemCfg.NewSeIdx]
|
||||
if !ok {
|
||||
return errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
|
||||
effectStatus := byte(gconv.Int(effectCfg.Stat))
|
||||
effectIdx := uint16(itemCfg.NewSeIdx)
|
||||
leftCount := 1
|
||||
if effectCfg.Times != nil && *effectCfg.Times != "" {
|
||||
leftCount = gconv.Int(*effectCfg.Times)
|
||||
if leftCount <= 0 {
|
||||
leftCount = 1
|
||||
}
|
||||
}
|
||||
|
||||
limitedCount := 0
|
||||
for _, eff := range onpet.EffectInfo {
|
||||
if eff.Idx == effectIdx {
|
||||
return errorcode.ErrorCodes.ErrCannotInjectPillAgain
|
||||
}
|
||||
if eff.Status == 2 {
|
||||
limitedCount++
|
||||
}
|
||||
}
|
||||
if effectStatus == 2 && limitedCount >= 2 {
|
||||
return errorcode.ErrorCodes.ErrTooManyEnergyOrbs
|
||||
}
|
||||
|
||||
onpet.EffectInfo = append(onpet.EffectInfo, model.PetEffectInfo{
|
||||
ItemID: itemid,
|
||||
Idx: effectIdx,
|
||||
Status: effectStatus,
|
||||
LeftCount: byte(leftCount),
|
||||
EID: uint16(gconv.Int(effectCfg.Eid)),
|
||||
Args: effectCfg.ArgsS,
|
||||
})
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *PetItemHandlerRegistry) Handle(itemID uint32, onpet *model.PetInfo) errorcode.ErrorCode {
|
||||
handler := r.GetHandler(itemID)
|
||||
if handler != nil {
|
||||
if handler(itemID, onpet) {
|
||||
return 0
|
||||
}
|
||||
return errorcode.ErrorCodes.ErrItemUnusable
|
||||
}
|
||||
return handleNewSeIdxPetItem(itemID, onpet)
|
||||
}
|
||||
|
||||
// -------------------------- 6. 初始化注册器(注册所有处理器) --------------------------
|
||||
func init() {
|
||||
|
||||
|
||||
@@ -44,6 +44,27 @@ func (p *baseplayer) GetPetInfo(limitlevel uint32) []model.PetInfo {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *baseplayer) AddBattleProp(index int, level int8) {
|
||||
if p == nil || index < 0 || index >= len(p.Prop) || level == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
p.Prop[index] += level
|
||||
if p.Prop[index] > 6 {
|
||||
p.Prop[index] = 6
|
||||
}
|
||||
if p.Prop[index] < -6 {
|
||||
p.Prop[index] = -6
|
||||
}
|
||||
}
|
||||
|
||||
func (p *baseplayer) ApplyBattleProps(target *model.AttackValue) {
|
||||
if p == nil || target == nil {
|
||||
return
|
||||
}
|
||||
target.Prop = p.Prop
|
||||
}
|
||||
func (f *baseplayer) InvitePlayer(ff common.PlayerI) {
|
||||
|
||||
}
|
||||
|
||||
20
modules/config/controller/admin/sign.go
Normal file
20
modules/config/controller/admin/sign.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
"blazing/modules/config/service"
|
||||
)
|
||||
|
||||
type SignController struct {
|
||||
*cool.Controller
|
||||
}
|
||||
|
||||
func init() {
|
||||
cool.RegisterController(&SignController{
|
||||
&cool.Controller{
|
||||
Prefix: "/admin/config/sign",
|
||||
Api: []string{"Add", "Delete", "Update", "Info", "List", "Page"},
|
||||
Service: service.NewSignInService(),
|
||||
},
|
||||
})
|
||||
}
|
||||
34
modules/config/model/sign.go
Normal file
34
modules/config/model/sign.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package model
|
||||
|
||||
import "blazing/cool"
|
||||
|
||||
const TableNameSignIn = "config_sign_in"
|
||||
|
||||
const (
|
||||
SignTypeTotal uint32 = 1
|
||||
SignTypeContinuous uint32 = 2
|
||||
)
|
||||
|
||||
// SignIn 签到阶段配置表。
|
||||
type SignIn struct {
|
||||
*BaseConfig
|
||||
SignType uint32 `gorm:"not null;default:1;uniqueIndex:idx_sign_type_stage;comment:'签到类别(1-累计 2-连续)'" json:"sign_type"`
|
||||
StageDays uint32 `gorm:"not null;default:1;uniqueIndex:idx_sign_type_stage;comment:'签到阶段天数(1/3/7/14/30)'" json:"stage_days"`
|
||||
CdkID uint32 `gorm:"not null;uniqueIndex;comment:'绑定的CDK配置ID'" json:"cdk_id"`
|
||||
}
|
||||
|
||||
func (*SignIn) TableName() string {
|
||||
return TableNameSignIn
|
||||
}
|
||||
|
||||
func (*SignIn) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
func NewSignIn() *SignIn {
|
||||
return &SignIn{BaseConfig: NewBaseConfig()}
|
||||
}
|
||||
|
||||
func init() {
|
||||
cool.CreateTable(&SignIn{})
|
||||
}
|
||||
@@ -8,14 +8,13 @@ import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"github.com/google/uuid"
|
||||
) // 1. 扩展字符集:数字+大小写字母+安全符号(避开URL/输入易冲突的符号,如/、?、&)
|
||||
)
|
||||
|
||||
const charsetWithSymbol = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz"
|
||||
|
||||
func Generate16CharSecure() string {
|
||||
result := make([]byte, 16)
|
||||
for i := 0; i < 16; i++ {
|
||||
|
||||
result[i] = charsetWithSymbol[grand.N(0, len(charsetWithSymbol)-1)]
|
||||
}
|
||||
return string(result)
|
||||
@@ -38,22 +37,30 @@ func NewCdkService() *CdkService {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CdkService) Get(id string) *model.CDKConfig {
|
||||
var item *model.CDKConfig
|
||||
dbm_notenable(s.Model).Where("cdk_code", id).WhereNot("exchange_remain_count", 0).Scan(&item)
|
||||
|
||||
return item
|
||||
|
||||
}
|
||||
|
||||
func (s *CdkService) GetByID(id uint32) *model.CDKConfig {
|
||||
if id == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var item *model.CDKConfig
|
||||
dbm_notenable(s.Model).Where("id", id).Scan(&item)
|
||||
return item
|
||||
}
|
||||
|
||||
func (s *CdkService) All() []model.CDKConfig {
|
||||
var item []model.CDKConfig
|
||||
dbm_notenable(s.Model).WhereLT("exchange_remain_count", 0).Scan(&item)
|
||||
|
||||
return item
|
||||
|
||||
}
|
||||
func (s *CdkService) Set(id string) bool {
|
||||
|
||||
func (s *CdkService) Set(id string) bool {
|
||||
res, err := cool.DBM(s.Model).Where("cdk_code", id).WhereNot("exchange_remain_count", 0).Decrement("exchange_remain_count", 1)
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -62,7 +69,5 @@ func (s *CdkService) Set(id string) bool {
|
||||
if rows == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
79
modules/config/service/sign.go
Normal file
79
modules/config/service/sign.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
"blazing/modules/config/model"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var signStageDays = map[uint32]struct{}{
|
||||
1: {},
|
||||
3: {},
|
||||
7: {},
|
||||
14: {},
|
||||
30: {},
|
||||
}
|
||||
|
||||
type SignInService struct {
|
||||
*cool.Service
|
||||
}
|
||||
|
||||
func NewSignInService() *SignInService {
|
||||
return &SignInService{
|
||||
&cool.Service{
|
||||
Model: model.NewSignIn(),
|
||||
PageQueryOp: &cool.QueryOp{
|
||||
FieldEQ: []string{"sign_type", "stage_days", "cdk_id", "is_enable"},
|
||||
KeyWordField: []string{"remark"},
|
||||
},
|
||||
ListQueryOp: &cool.QueryOp{
|
||||
FieldEQ: []string{"sign_type", "stage_days", "cdk_id", "is_enable"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignInService) ModifyBefore(ctx context.Context, method string, param g.MapStrAny) (err error) {
|
||||
if method == "Delete" {
|
||||
return nil
|
||||
}
|
||||
|
||||
signType := gconv.Uint32(param["sign_type"])
|
||||
if signType != model.SignTypeTotal && signType != model.SignTypeContinuous {
|
||||
return fmt.Errorf("签到类别非法,只支持1(累计)或2(连续)")
|
||||
}
|
||||
|
||||
stageDays := gconv.Uint32(param["stage_days"])
|
||||
if _, ok := signStageDays[stageDays]; !ok {
|
||||
return fmt.Errorf("签到阶段仅支持1、3、7、14、30天")
|
||||
}
|
||||
|
||||
cdkID := gconv.Uint32(param["cdk_id"])
|
||||
if cdkID == 0 {
|
||||
return fmt.Errorf("cdk_id不能为空")
|
||||
}
|
||||
if NewCdkService().GetByID(cdkID) == nil {
|
||||
return fmt.Errorf("绑定的CDK不存在")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SignInService) GetEnabled() []model.SignIn {
|
||||
var items []model.SignIn
|
||||
dbm_enable(s.Model).Scan(&items)
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
if items[i].SignType != items[j].SignType {
|
||||
return items[i].SignType < items[j].SignType
|
||||
}
|
||||
if items[i].StageDays != items[j].StageDays {
|
||||
return items[i].StageDays < items[j].StageDays
|
||||
}
|
||||
return items[i].CdkID < items[j].CdkID
|
||||
})
|
||||
return items
|
||||
}
|
||||
36
modules/player/controller/admin/sign.go
Normal file
36
modules/player/controller/admin/sign.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
"blazing/modules/player/service"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type SignRecordController struct {
|
||||
*cool.Controller
|
||||
}
|
||||
|
||||
func init() {
|
||||
cool.RegisterController(&SignRecordController{
|
||||
&cool.Controller{
|
||||
Prefix: "/admin/game/signrecord",
|
||||
Api: []string{"Delete", "Update", "Info", "List", "Page"},
|
||||
Service: service.NewSignService(0),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type ResetAllReq struct {
|
||||
g.Meta `path:"/resetAll" method:"POST"`
|
||||
Authorization string `json:"Authorization" in:"header"`
|
||||
}
|
||||
|
||||
func (c *SignRecordController) ResetAll(ctx context.Context, req *ResetAllReq) (res *cool.BaseRes, err error) {
|
||||
result, err := service.NewSignService(0).ResetAll()
|
||||
if err != nil {
|
||||
return cool.Fail(err.Error()), nil
|
||||
}
|
||||
return cool.Ok(result), nil
|
||||
}
|
||||
104
modules/player/controller/app/sign.go
Normal file
104
modules/player/controller/app/sign.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
configservice "blazing/modules/config/service"
|
||||
playerservice "blazing/modules/player/service"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/deatil/go-cryptobin/cryptobin/crypto"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type SignController struct {
|
||||
*cool.Controller
|
||||
}
|
||||
|
||||
func init() {
|
||||
controller := &SignController{
|
||||
&cool.Controller{
|
||||
Prefix: "/seer/game/sign",
|
||||
Api: []string{},
|
||||
Service: configservice.NewSignInService(),
|
||||
},
|
||||
}
|
||||
cool.RegisterController(controller)
|
||||
}
|
||||
|
||||
type SignStateReq struct {
|
||||
g.Meta `path:"/state" method:"GET"`
|
||||
UserID uint32 `json:"user_id" v:"required|min:1#用户ID不能为空|用户ID非法"`
|
||||
Session string `json:"session" v:"required#session不能为空"`
|
||||
}
|
||||
|
||||
type SignClaimReq struct {
|
||||
g.Meta `path:"/claim" method:"POST"`
|
||||
UserID uint32 `json:"user_id" v:"required|min:1#用户ID不能为空|用户ID非法"`
|
||||
Session string `json:"session" v:"required#session不能为空"`
|
||||
}
|
||||
|
||||
func (c *SignController) State(ctx context.Context, req *SignStateReq) (res *cool.BaseRes, err error) {
|
||||
if err = g.Validator().Data(req).Run(ctx); err != nil {
|
||||
return cool.Fail(err.Error()), nil
|
||||
}
|
||||
if err = validateGameSession(req.UserID, req.Session); err != nil {
|
||||
return cool.Fail(err.Error()), nil
|
||||
}
|
||||
|
||||
state, err := playerservice.NewSignService(req.UserID).GetState()
|
||||
if err != nil {
|
||||
return cool.Fail(err.Error()), nil
|
||||
}
|
||||
return cool.Ok(state), nil
|
||||
}
|
||||
|
||||
func (c *SignController) Claim(ctx context.Context, req *SignClaimReq) (res *cool.BaseRes, err error) {
|
||||
if err = g.Validator().Data(req).Run(ctx); err != nil {
|
||||
return cool.Fail(err.Error()), nil
|
||||
}
|
||||
if err = validateGameSession(req.UserID, req.Session); err != nil {
|
||||
return cool.Fail(err.Error()), nil
|
||||
}
|
||||
|
||||
result, err := playerservice.NewSignService(req.UserID).Claim()
|
||||
if err != nil {
|
||||
return cool.Fail(err.Error()), nil
|
||||
}
|
||||
return cool.Ok(result), nil
|
||||
}
|
||||
|
||||
func validateGameSession(userID uint32, session string) error {
|
||||
if userID == 0 {
|
||||
return fmt.Errorf("user_id不能为空")
|
||||
}
|
||||
session = strings.TrimSpace(session)
|
||||
if session == "" {
|
||||
return fmt.Errorf("session不能为空")
|
||||
}
|
||||
|
||||
cached, err := cool.CacheManager.Get(context.Background(), fmt.Sprintf("session:%d", userID))
|
||||
if err != nil || cached.IsEmpty() {
|
||||
return fmt.Errorf("session已过期,请重新登录")
|
||||
}
|
||||
|
||||
rawSession := session
|
||||
decrypted := crypto.
|
||||
FromBase64String(session).
|
||||
SetKey("gfertf12dfertf12").
|
||||
SetIv("gfertf12dfertf12").
|
||||
Aes().
|
||||
CBC().
|
||||
PKCS7Padding().
|
||||
Decrypt().
|
||||
ToString()
|
||||
if decrypted != "" {
|
||||
rawSession = decrypted
|
||||
}
|
||||
|
||||
if rawSession != cached.String() {
|
||||
return fmt.Errorf("session无效,请重新登录")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -383,16 +383,64 @@ func (pet *PetInfo) RnadEffect() {
|
||||
// 7 :繁殖加成
|
||||
// 8 :体力提升加成
|
||||
|
||||
const (
|
||||
maxHPUpEffectIdx uint16 = 60000
|
||||
maxHPUpEffectStatus byte = 8
|
||||
maxHPUpEffectEID uint16 = 26
|
||||
maxHPUpEffectCap = 20
|
||||
)
|
||||
|
||||
// 繁殖加成,体力提升加成 ,这里是防止和其他重复所以定义不同类别,但是实际上,能量珠那些事调用不同id的effect实现
|
||||
// <!-- Stat: 精灵特效Stat: 0: 无效(默认值), 1: 永久, 2: 有`有效次数'的特效 3: 爆发特效 4: 异能精灵特质,5特训,6魂印-->
|
||||
func (pet *PetInfo) GetEffect(ptype int) (int, *PetEffectInfo, bool) {
|
||||
|
||||
return utils.FindWithIndex(pet.EffectInfo, func(item PetEffectInfo) bool {
|
||||
return item.Status == 1
|
||||
return int(item.Status) == ptype
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (pet *PetInfo) AddMaxHPUpEffect(itemID uint32, value int) bool {
|
||||
if pet == nil || value <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, eff, ok := pet.GetEffect(int(maxHPUpEffectStatus)); ok {
|
||||
current := 0
|
||||
if len(eff.Args) >= 2 && eff.Args[0] == 0 && eff.Args[1] > 0 {
|
||||
current = eff.Args[1]
|
||||
}
|
||||
if current >= maxHPUpEffectCap {
|
||||
return false
|
||||
}
|
||||
|
||||
next := current + value
|
||||
if next > maxHPUpEffectCap {
|
||||
next = maxHPUpEffectCap
|
||||
}
|
||||
|
||||
eff.ItemID = itemID
|
||||
eff.Idx = maxHPUpEffectIdx
|
||||
eff.Status = maxHPUpEffectStatus
|
||||
eff.EID = maxHPUpEffectEID
|
||||
eff.Args = []int{0, next}
|
||||
return next > current
|
||||
}
|
||||
|
||||
if value > maxHPUpEffectCap {
|
||||
value = maxHPUpEffectCap
|
||||
}
|
||||
|
||||
pet.EffectInfo = append(pet.EffectInfo, PetEffectInfo{
|
||||
ItemID: itemID,
|
||||
Idx: maxHPUpEffectIdx,
|
||||
Status: maxHPUpEffectStatus,
|
||||
EID: maxHPUpEffectEID,
|
||||
Args: []int{0, value},
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
func (pet *PetInfo) Downgrade(level uint32) {
|
||||
|
||||
for pet.Level > uint32(level) {
|
||||
|
||||
@@ -16,9 +16,12 @@ type SignInRecord struct {
|
||||
PlayerID uint32 `gorm:"not null;index:idx_player_id;comment:'玩家ID'" json:"player_id"`
|
||||
SignInID uint32 `gorm:"not null;index:idx_sign_in_id;comment:'关联的签到活动ID(对应player_sign_in表的SignInID)'" json:"sign_in_id"`
|
||||
|
||||
IsCompleted bool `gorm:"not null;default:false;comment:'签到是否完成(0-未完成 1-已完成)'" json:"is_completed"`
|
||||
//通过bitset来实现签到的进度记录
|
||||
SignInProgress []uint32 `gorm:"type:jsonb;not null;comment:'签到进度(状压实现,存储每日签到状态)'" json:"sign_in_progress"`
|
||||
IsCompleted bool `gorm:"not null;default:false;comment:'签到是否完成(0-未完成 1-已完成)'" json:"is_completed"`
|
||||
ContinuousDays uint32 `gorm:"not null;default:0;comment:'连续签到天数'" json:"continuous_days"`
|
||||
TotalDays uint32 `gorm:"not null;default:0;comment:'累计签到天数'" json:"total_days"`
|
||||
LastSignDate string `gorm:"type:varchar(10);not null;default:'';comment:'最近一次签到日期(YYYY-MM-DD)'" json:"last_sign_date"`
|
||||
// 通过 bitset 记录每日签到状态,位索引从 0 开始,对应签到第 1 天。
|
||||
SignInProgress []uint32 `gorm:"type:jsonb;not null;default:'[]';comment:'签到进度(状压实现,存储每日签到状态)'" json:"sign_in_progress"`
|
||||
}
|
||||
|
||||
// TableName 指定表名(遵循现有规范)
|
||||
|
||||
@@ -1,40 +1,14 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
)
|
||||
import configmodel "blazing/modules/config/model"
|
||||
|
||||
// 表名常量(遵循现有命名规范:小写+下划线)
|
||||
const TableNameSignIn = "config_sign_in"
|
||||
// Deprecated: 签到配置已迁移到 modules/config/model/sign.go。
|
||||
const TableNameSignIn = configmodel.TableNameSignIn
|
||||
|
||||
// SignIn 签到记录表
|
||||
// 核心字段:签到完成状态、状压签到进度、签到奖励脚本
|
||||
type SignIn struct {
|
||||
*cool.Model // 嵌入基础Model(包含主键、创建/更新时间等通用字段)
|
||||
SignInID uint32 `gorm:"not null;index:idx_sign_in_id;comment:'签到活动ID'" json:"sign_in_id"`
|
||||
Status uint32 `gorm:"not null;default:0;comment:'签到状态(0-未完成 1-已完成)'" json:"status"`
|
||||
//传入用户名,签到天数,给予奖励,这个搭配里程碑表实现
|
||||
RewardScript string `gorm:"type:varchar(512);default:'';comment:'签到奖励脚本(执行奖励发放的脚本内容)'" json:"reward_script"`
|
||||
}
|
||||
|
||||
// TableName 指定表名(遵循现有规范)
|
||||
func (*SignIn) TableName() string {
|
||||
return TableNameSignIn
|
||||
}
|
||||
|
||||
// GroupName 指定表分组(默认分组,与现有Item表/精灵特效表一致)
|
||||
func (*SignIn) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewSignIn 创建签到记录表实例(初始化基础Model)
|
||||
func NewSignIn() *SignIn {
|
||||
return &SignIn{
|
||||
Model: cool.NewModel(),
|
||||
}
|
||||
}
|
||||
|
||||
// init 程序启动时自动创建表(与现有PlayerPetSpecialEffect表的初始化逻辑一致)
|
||||
func init() {
|
||||
cool.CreateTable(&SignIn{})
|
||||
// Deprecated: 签到配置已迁移到 modules/config/model/sign.go。
|
||||
type SignIn = configmodel.SignIn
|
||||
|
||||
// Deprecated: 签到配置已迁移到 modules/config/model/sign.go。
|
||||
func NewSignIn() *configmodel.SignIn {
|
||||
return configmodel.NewSignIn()
|
||||
}
|
||||
|
||||
406
modules/player/service/sign.go
Normal file
406
modules/player/service/sign.go
Normal file
@@ -0,0 +1,406 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"blazing/common/data"
|
||||
"blazing/cool"
|
||||
baseservice "blazing/modules/base/service"
|
||||
configmodel "blazing/modules/config/model"
|
||||
configservice "blazing/modules/config/service"
|
||||
"blazing/modules/player/model"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
const signRecordID uint32 = 1
|
||||
|
||||
// SignStageState 表示一个签到阶段的当前状态。
|
||||
type SignStageState struct {
|
||||
SignType uint32 `json:"sign_type"`
|
||||
StageDays uint32 `json:"stage_days"`
|
||||
CdkID uint32 `json:"cdk_id"`
|
||||
Reached bool `json:"reached"`
|
||||
Claimed bool `json:"claimed"`
|
||||
}
|
||||
|
||||
// SignState 表示玩家当前签到进度和阶段状态。
|
||||
type SignState struct {
|
||||
TotalDays uint32 `json:"total_days"`
|
||||
ContinuousDays uint32 `json:"continuous_days"`
|
||||
LastSignDate string `json:"last_sign_date"`
|
||||
TodaySigned bool `json:"today_signed"`
|
||||
Stages []SignStageState `json:"stages"`
|
||||
}
|
||||
|
||||
// SignRewardResult 表示一次签到后自动发放的阶段奖励。
|
||||
type SignRewardResult struct {
|
||||
SignType uint32 `json:"sign_type"`
|
||||
StageDays uint32 `json:"stage_days"`
|
||||
CdkID uint32 `json:"cdk_id"`
|
||||
Items []data.ItemInfo `json:"items,omitempty"`
|
||||
PetIDs []uint32 `json:"pet_ids,omitempty"`
|
||||
TitleIDs []uint32 `json:"title_ids,omitempty"`
|
||||
Coins int64 `json:"coins,omitempty"`
|
||||
Gold int64 `json:"gold,omitempty"`
|
||||
FreeGold int64 `json:"free_gold,omitempty"`
|
||||
ExpPool int64 `json:"exp_pool,omitempty"`
|
||||
EVPool int64 `json:"ev_pool,omitempty"`
|
||||
}
|
||||
|
||||
// SignClaimResult 表示签到后的完整结果。
|
||||
type SignClaimResult struct {
|
||||
State *SignState `json:"state"`
|
||||
Rewards []SignRewardResult `json:"rewards,omitempty"`
|
||||
}
|
||||
|
||||
// SignResetResult 表示管理端执行的签到重置结果。
|
||||
type SignResetResult struct {
|
||||
SignRecordRows int64 `json:"sign_record_rows"`
|
||||
CdkLogRows int64 `json:"cdk_log_rows"`
|
||||
ResetCdkIDs []uint32 `json:"reset_cdk_ids"`
|
||||
}
|
||||
|
||||
// SignService 管理玩家签到进度。
|
||||
type SignService struct {
|
||||
BaseService
|
||||
}
|
||||
|
||||
func NewSignService(id uint32) *SignService {
|
||||
return &SignService{
|
||||
BaseService: BaseService{
|
||||
userid: id,
|
||||
Service: &cool.Service{
|
||||
Model: model.NewSignInRecord(),
|
||||
ListQueryOp: &cool.QueryOp{
|
||||
FieldEQ: []string{"player_id", "is_completed"},
|
||||
},
|
||||
PageQueryOp: &cool.QueryOp{
|
||||
FieldEQ: []string{"player_id", "is_completed"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignService) GetState() (*SignState, error) {
|
||||
record, err := s.getRecord()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.buildState(record), nil
|
||||
}
|
||||
|
||||
func (s *SignService) Claim() (*SignClaimResult, error) {
|
||||
record, isNew, err := s.getOrInitRecord()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
today := currentDateString()
|
||||
if record.LastSignDate == today {
|
||||
return nil, fmt.Errorf("今天已经签到过了")
|
||||
}
|
||||
|
||||
prevDate := record.LastSignDate
|
||||
record.LastSignDate = today
|
||||
record.TotalDays++
|
||||
if isYesterday(prevDate, today) {
|
||||
record.ContinuousDays++
|
||||
} else {
|
||||
record.ContinuousDays = 1
|
||||
}
|
||||
|
||||
rewards, err := s.grantReachedStageRewards(record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.saveRecord(record, isNew); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SignClaimResult{
|
||||
State: s.buildState(record),
|
||||
Rewards: rewards,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SignService) ResetAll() (*SignResetResult, error) {
|
||||
result := &SignResetResult{}
|
||||
|
||||
signRes, err := cool.DBM(model.NewSignInRecord()).Delete()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if signRes != nil {
|
||||
result.SignRecordRows, _ = signRes.RowsAffected()
|
||||
}
|
||||
|
||||
configs := configservice.NewSignInService().GetEnabled()
|
||||
cdkIDs := make([]uint32, 0, len(configs))
|
||||
seen := make(map[uint32]struct{}, len(configs))
|
||||
for _, cfg := range configs {
|
||||
if cfg.CdkID == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[cfg.CdkID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[cfg.CdkID] = struct{}{}
|
||||
cdkIDs = append(cdkIDs, cfg.CdkID)
|
||||
}
|
||||
sort.Slice(cdkIDs, func(i, j int) bool { return cdkIDs[i] < cdkIDs[j] })
|
||||
result.ResetCdkIDs = cdkIDs
|
||||
|
||||
if len(cdkIDs) > 0 {
|
||||
cdkRes, err := cool.DBM(model.NewCdkLog()).WhereIn("code_id", cdkIDs).Delete()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cdkRes != nil {
|
||||
result.CdkLogRows, _ = cdkRes.RowsAffected()
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *SignService) grantReachedStageRewards(record *model.SignInRecord) ([]SignRewardResult, error) {
|
||||
configs := configservice.NewSignInService().GetEnabled()
|
||||
if len(configs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cdkLogService := NewCdkService(s.userid)
|
||||
infoService := NewInfoService(s.userid)
|
||||
playerInfo := infoService.GetLogin()
|
||||
if playerInfo == nil {
|
||||
return nil, fmt.Errorf("玩家角色不存在")
|
||||
}
|
||||
|
||||
infoDirty := false
|
||||
results := make([]SignRewardResult, 0)
|
||||
for _, cfg := range configs {
|
||||
if !stageReached(cfg.SignType, cfg.StageDays, record) {
|
||||
continue
|
||||
}
|
||||
if !cdkLogService.CanGet(cfg.CdkID) {
|
||||
continue
|
||||
}
|
||||
|
||||
reward, changed, err := s.applyCdkReward(cfg.CdkID, playerInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reward.SignType = cfg.SignType
|
||||
reward.StageDays = cfg.StageDays
|
||||
reward.CdkID = cfg.CdkID
|
||||
results = append(results, *reward)
|
||||
if changed {
|
||||
infoDirty = true
|
||||
}
|
||||
cdkLogService.Log(cfg.CdkID)
|
||||
}
|
||||
|
||||
if infoDirty {
|
||||
infoService.Save(*playerInfo)
|
||||
}
|
||||
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
if results[i].SignType != results[j].SignType {
|
||||
return results[i].SignType < results[j].SignType
|
||||
}
|
||||
if results[i].StageDays != results[j].StageDays {
|
||||
return results[i].StageDays < results[j].StageDays
|
||||
}
|
||||
return results[i].CdkID < results[j].CdkID
|
||||
})
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (s *SignService) applyCdkReward(cdkID uint32, playerInfo *model.PlayerInfo) (*SignRewardResult, bool, error) {
|
||||
cfg := configservice.NewCdkService().GetByID(cdkID)
|
||||
if cfg == nil {
|
||||
return nil, false, fmt.Errorf("绑定的CDK不存在")
|
||||
}
|
||||
if cfg.BindUserId != 0 && cfg.BindUserId != s.userid {
|
||||
return nil, false, fmt.Errorf("CDK已绑定其他用户")
|
||||
}
|
||||
if !cfg.ValidEndTime.IsZero() && cfg.ValidEndTime.Before(time.Now()) {
|
||||
return nil, false, fmt.Errorf("绑定的CDK已过期")
|
||||
}
|
||||
|
||||
result := &SignRewardResult{}
|
||||
var (
|
||||
infoDirty bool
|
||||
bagItems []data.ItemInfo
|
||||
)
|
||||
|
||||
appendRewardItem := func(itemID uint32, count int64) {
|
||||
if itemID == 0 || count <= 0 {
|
||||
return
|
||||
}
|
||||
switch itemID {
|
||||
case 1:
|
||||
result.Coins += count
|
||||
playerInfo.Coins += count
|
||||
infoDirty = true
|
||||
case 3:
|
||||
result.ExpPool += count
|
||||
playerInfo.ExpPool += count
|
||||
infoDirty = true
|
||||
case 5:
|
||||
result.Gold += count
|
||||
case 9:
|
||||
result.EVPool += count
|
||||
playerInfo.EVPool += count
|
||||
infoDirty = true
|
||||
default:
|
||||
bagItems = append(bagItems, data.ItemInfo{ItemId: int64(itemID), ItemCnt: count})
|
||||
}
|
||||
}
|
||||
|
||||
for _, rewardID := range cfg.ItemRewardIds {
|
||||
itemInfo := configservice.NewItemService().GetItemCount(rewardID)
|
||||
appendRewardItem(uint32(itemInfo.ItemId), itemInfo.ItemCnt)
|
||||
}
|
||||
|
||||
if result.Gold != 0 {
|
||||
baseservice.NewBaseSysUserService().UpdateGold(s.userid, result.Gold*100)
|
||||
}
|
||||
if result.FreeGold != 0 {
|
||||
baseservice.NewBaseSysUserService().UpdateFreeGold(s.userid, result.FreeGold*100)
|
||||
}
|
||||
if len(bagItems) > 0 {
|
||||
items, err := NewItemService(s.userid).AddItems(bagItems)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
result.Items = items
|
||||
}
|
||||
|
||||
for _, rewardID := range cfg.ElfRewardIds {
|
||||
pet := configservice.NewPetRewardService().Get(rewardID)
|
||||
if pet == nil {
|
||||
continue
|
||||
}
|
||||
petInfo := model.GenPetInfo(int(pet.MonID), int(pet.DV), int(pet.Nature), int(pet.Effect), int(pet.Lv), nil, 0)
|
||||
if _, err := NewPetService(s.userid).PetAdd(petInfo, 0); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
result.PetIDs = append(result.PetIDs, uint32(pet.MonID))
|
||||
}
|
||||
|
||||
if cfg.TitleRewardIds != 0 {
|
||||
NewTitleService(s.userid).Give(cfg.TitleRewardIds)
|
||||
result.TitleIDs = append(result.TitleIDs, cfg.TitleRewardIds)
|
||||
}
|
||||
|
||||
return result, infoDirty, nil
|
||||
}
|
||||
|
||||
func (s *SignService) buildState(record *model.SignInRecord) *SignState {
|
||||
state := &SignState{
|
||||
Stages: make([]SignStageState, 0),
|
||||
}
|
||||
if record != nil {
|
||||
state.TotalDays = record.TotalDays
|
||||
state.ContinuousDays = record.ContinuousDays
|
||||
state.LastSignDate = record.LastSignDate
|
||||
state.TodaySigned = record.LastSignDate == currentDateString()
|
||||
}
|
||||
|
||||
cdkLogService := NewCdkService(s.userid)
|
||||
configs := configservice.NewSignInService().GetEnabled()
|
||||
for _, cfg := range configs {
|
||||
state.Stages = append(state.Stages, SignStageState{
|
||||
SignType: cfg.SignType,
|
||||
StageDays: cfg.StageDays,
|
||||
CdkID: cfg.CdkID,
|
||||
Reached: stageReached(cfg.SignType, cfg.StageDays, record),
|
||||
Claimed: !cdkLogService.CanGet(cfg.CdkID),
|
||||
})
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
func stageReached(signType, stageDays uint32, record *model.SignInRecord) bool {
|
||||
if record == nil || stageDays == 0 {
|
||||
return false
|
||||
}
|
||||
switch signType {
|
||||
case configmodel.SignTypeContinuous:
|
||||
return record.ContinuousDays >= stageDays
|
||||
default:
|
||||
return record.TotalDays >= stageDays
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignService) getRecord() (*model.SignInRecord, error) {
|
||||
var out *model.SignInRecord
|
||||
if err := s.dbm(s.Model).Where("sign_in_id", signRecordID).Scan(&out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *SignService) getOrInitRecord() (*model.SignInRecord, bool, error) {
|
||||
record, err := s.getRecord()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if record != nil {
|
||||
return record, false, nil
|
||||
}
|
||||
|
||||
return &model.SignInRecord{
|
||||
Base: model.Base{
|
||||
Model: cool.NewModel(),
|
||||
IsVip: cool.Config.ServerInfo.IsVip,
|
||||
},
|
||||
PlayerID: s.userid,
|
||||
SignInID: signRecordID,
|
||||
IsCompleted: false,
|
||||
ContinuousDays: 0,
|
||||
TotalDays: 0,
|
||||
LastSignDate: "",
|
||||
SignInProgress: []uint32{},
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
func (s *SignService) saveRecord(record *model.SignInRecord, isNew bool) error {
|
||||
data := map[string]any{
|
||||
"player_id": record.PlayerID,
|
||||
"sign_in_id": record.SignInID,
|
||||
"is_completed": false,
|
||||
"continuous_days": record.ContinuousDays,
|
||||
"total_days": record.TotalDays,
|
||||
"last_sign_date": record.LastSignDate,
|
||||
"sign_in_progress": []uint32{},
|
||||
"is_vip": cool.Config.ServerInfo.IsVip,
|
||||
}
|
||||
if isNew {
|
||||
_, err := cool.DBM(s.Model).Data(data).Insert()
|
||||
return err
|
||||
}
|
||||
_, err := s.dbm(s.Model).Where("sign_in_id", signRecordID).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
func currentDateString() string {
|
||||
return time.Now().Format("2006-01-02")
|
||||
}
|
||||
|
||||
func isYesterday(previousDate, currentDate string) bool {
|
||||
if previousDate == "" || currentDate == "" {
|
||||
return false
|
||||
}
|
||||
prev, err := time.ParseInLocation("2006-01-02", previousDate, time.Local)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
curr, err := time.ParseInLocation("2006-01-02", currentDate, time.Local)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return prev.Add(24 * time.Hour).Equal(curr)
|
||||
}
|
||||
1
modules/player/service/sign_test.go
Normal file
1
modules/player/service/sign_test.go
Normal file
@@ -0,0 +1 @@
|
||||
package service
|
||||
Reference in New Issue
Block a user