From 7dfa9c297e1d1633a8252f4f0701f8521ce5e282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=94=E5=BF=B5?= <12574910+72wo@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:27:27 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(fight):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=96=B2=E6=83=AB=E7=8A=B6=E6=80=81=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=9D=A1=E7=9C=A0=E7=8A=B6=E6=80=81=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现疲惫状态(StatusTired),仅限制攻击技能,允许属性技能正常使用 - 重构睡眠状态,改为在被攻击且未miss时立即解除,而非技能使用后 - 修复寄生种子效果触发时机,改为回合开始时触发 - 调整寄生效果的目标为技能施放者而非对手 fix(fight): 修正战斗回合逻辑和技能持续时间处理 - 修复Effect2194中状态添加函数调用,使用带时间参数的版本 - 修正Effect13中技能持续时间计算,避免额外减1的问题 - 优化回合处理逻辑,当双方都未出手时跳过动作阶段 refactor(cdk): 重构CDK配置结构和服务器冠名功能 - 将CDKConfig中的CDKType字段重命名为Type以符合GORM映射 - 优化UseServerNamingCDK方法的上下文处理逻辑 - 修复服务器冠名CDK使用时的类型检查条件 feat(player): 完善宠物经验系统和CDK兑换功能 - 增强AddPetExp方法,处理宠物等级达到100级的情况 - 添加查询当前账号有效期内服务器冠名信息的API接口 - 实现服务器服务相关的数据模型和查询方法 fix(task): 任务查询支持启用和未启用状态 - 修改任务服务中的Get、GetDaily、GetWeek方法 - 当启用状态下无结果时,自动查询未启用状态的任务配置 ``` --- logic/controller/fight_boss野怪和地图怪.go | 137 ++++++++++++++------- logic/service/fight/fightc.go | 27 +++- logic/service/fight/unified_test.go | 36 ++++++ modules/config/service/server.go | 62 +++++----- 4 files changed, 184 insertions(+), 78 deletions(-) diff --git a/logic/controller/fight_boss野怪和地图怪.go b/logic/controller/fight_boss野怪和地图怪.go index 8e9a41b3a..3ec0f067e 100644 --- a/logic/controller/fight_boss野怪和地图怪.go +++ b/logic/controller/fight_boss野怪和地图怪.go @@ -41,6 +41,7 @@ func (Controller) PlayerFightBoss(req *ChallengeBossInboundInfo, p *player.Playe if err != 0 { return nil, err } + leadMonster := &monsterInfo.PetList[0] p.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC p.Fightinfo.Mode = resolveMapNodeFightMode(mapNode) @@ -52,12 +53,7 @@ func (Controller) PlayerFightBoss(req *ChallengeBossInboundInfo, p *player.Playe var fightC *fight.FightC fightC, err = startMapBossFight(mapNode, p, ai, func(foi model.FightOverInfo) { - if mapNode.WinBonusID == 0 { - return - } - if shouldGrantBossWinBonus(fightC, p.Info.UserID, bossConfigs[0], foi) { - p.SptCompletedTask(mapNode.WinBonusID, 1) - } + handleMapBossFightRewards(p, fightC, foi, mapNode, bossConfigs[0], leadMonster) }) if err != 0 { return nil, err @@ -234,6 +230,95 @@ func shouldGrantBossWinBonus(fightC *fight.FightC, playerID uint32, bossConfig c return true } +func handleMapBossFightRewards( + p *player.Player, + fightC *fight.FightC, + foi model.FightOverInfo, + mapNode *configmodel.MapNode, + bossConfig configmodel.BossConfig, + leadMonster *model.PetInfo, +) { + rewards := grantMonsterFightRewards(p, foi, leadMonster) + if mapNode != nil && mapNode.WinBonusID != 0 && shouldGrantBossWinBonus(fightC, p.Info.UserID, bossConfig, foi) { + appendBossTaskReward(p, mapNode.WinBonusID, 1, rewards) + } + if rewards != nil && rewards.HasReward() { + p.SendPackCmd(8004, rewards) + } +} + +func grantMonsterFightRewards(p *player.Player, foi model.FightOverInfo, monster *model.PetInfo) *fightinfo.S2C_GET_BOSS_MONSTER { + rewards := &fightinfo.S2C_GET_BOSS_MONSTER{} + if p == nil || monster == nil || foi.Reason != 0 || foi.WinnerId != p.Info.UserID || !p.CanGet() { + return rewards + } + + petCfg, ok := xmlres.PetMAP[int(monster.ID)] + if !ok { + return rewards + } + + exp := uint32(petCfg.YieldingExp) * monster.Level / 7 + addlevel, poolevel := p.CanGetExp() + addexp := gconv.Float32(addlevel * gconv.Float32(exp)) + poolexp := gconv.Float32(poolevel) * gconv.Float32(exp) + + p.ItemAdd(3, int64(poolexp+addexp)) + rewards.AddItem(rewardItemExpPool, uint32(poolexp)) + p.AddPetExp(foi.Winpet, int64(addexp)) + + if p.CanGetItem() { + itemID := p.GetSpace().GetDrop() + if itemID != 0 { + count := uint32(grand.N(1, 2)) + if p.ItemAdd(itemID, int64(count)) { + rewards.AddItem(uint32(itemID), count) + } + } + } + + petType := int64(petCfg.Type) + if monster.IsShiny() && p.CanGetXUAN() && petType < 16 { + xuanID := uint32(400686 + petType) + count := uint32(grand.N(1, 2)) + if p.ItemAdd(int64(xuanID), int64(count)) { + rewards.AddItem(xuanID, count) + } + } + + if foi.Winpet != nil { + foi.Winpet.AddEV(petCfg.YieldingEVValues) + } + return rewards +} + +func appendBossTaskReward(p *player.Player, taskID int, outState int, rewards *fightinfo.S2C_GET_BOSS_MONSTER) { + if p == nil || rewards == nil || !p.IsLogin || taskID <= 0 { + return + } + if p.Info.GetTask(taskID) == model.Completed { + return + } + + granted, err := p.ApplyTaskCompletion(uint32(taskID), outState, nil) + if err != 0 { + return + } + + p.Info.SetTask(taskID, model.Completed) + rewards.BonusID = uint32(taskID) + if granted == nil { + return + } + if granted.Pet != nil { + rewards.PetID = granted.Pet.ID + rewards.CaptureTm = granted.Pet.CatchTime + } + for _, item := range granted.Items { + rewards.AddItemInfo(item) + } +} + func buildNpcMonsterInfo(refPet player.OgrePetInfo, mapID uint32) (*model.PetInfo, *model.PlayerInfo, errorcode.ErrorCode) { if refPet.ID == 0 { return nil, nil, errorcode.ErrorCodes.ErrPokemonNotExists @@ -269,46 +354,8 @@ func buildNpcMonsterInfo(refPet player.OgrePetInfo, mapID uint32) (*model.PetInf } func handleNpcFightRewards(p *player.Player, foi model.FightOverInfo, monster *model.PetInfo) { - if foi.Reason != 0 || foi.WinnerId != p.Info.UserID || !p.CanGet() { - return - } - - petCfg, ok := xmlres.PetMAP[int(monster.ID)] - if !ok { - return - } - - exp := uint32(petCfg.YieldingExp) * monster.Level / 7 - addlevel, poolevel := p.CanGetExp() - addexp := gconv.Float32(addlevel * gconv.Float32(exp)) - poolexp := gconv.Float32(poolevel) * gconv.Float32(exp) - rewards := &fightinfo.S2C_GET_BOSS_MONSTER{} - - p.ItemAdd(3, int64(poolexp+addexp)) - rewards.AddItem(rewardItemExpPool, uint32(poolexp)) - p.AddPetExp(foi.Winpet, int64(addexp)) - - if p.CanGetItem() { - itemID := p.GetSpace().GetDrop() - if itemID != 0 { - count := uint32(grand.N(1, 2)) - if p.ItemAdd(itemID, int64(count)) { - rewards.AddItem(uint32(itemID), count) - } - } - } - - petType := int64(petCfg.Type) - if monster.IsShiny() && p.CanGetXUAN() && petType < 16 { - xuanID := uint32(400686 + petType) - count := uint32(grand.N(1, 2)) - if p.ItemAdd(int64(xuanID), int64(count)) { - rewards.AddItem(xuanID, count) - } - } - + rewards := grantMonsterFightRewards(p, foi, monster) if rewards.HasReward() { p.SendPackCmd(8004, rewards) } - foi.Winpet.AddEV(petCfg.YieldingEVValues) } diff --git a/logic/service/fight/fightc.go b/logic/service/fight/fightc.go index 1e39a77c7..04584091d 100644 --- a/logic/service/fight/fightc.go +++ b/logic/service/fight/fightc.go @@ -193,10 +193,23 @@ func (f *FightC) collectAttackValues(inputs []*input.Input) []model.AttackValue return values } -func (f *FightC) buildNoteUseSkillOutboundInfo() info.NoteUseSkillOutboundInfo { +func (f *FightC) collectAttackValueByAction(act *action.SelectSkillAction) []model.AttackValue { + if act == nil { + return nil + } + fighter := f.GetInputByAction(act, false) + if fighter == nil || fighter.AttackValue == nil || fighter.AttackValue.SkillID == 0 { + return nil + } + attackValue := *fighter.AttackValue + attackValue.ActorIndex = uint32(act.GetActorIndex()) + return []model.AttackValue{attackValue} +} + +func (f *FightC) buildNoteUseSkillOutboundInfo(firstAttack, secondAttack *action.SelectSkillAction) info.NoteUseSkillOutboundInfo { result := info.NoteUseSkillOutboundInfo{} - result.FirstAttackInfo = append(result.FirstAttackInfo, f.collectAttackValues(f.Our)...) - result.SecondAttackInfo = append(result.SecondAttackInfo, f.collectAttackValues(f.Opp)...) + result.FirstAttackInfo = append(result.FirstAttackInfo, f.collectAttackValueByAction(firstAttack)...) + result.SecondAttackInfo = append(result.SecondAttackInfo, f.collectAttackValueByAction(secondAttack)...) return result } @@ -302,12 +315,18 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) var currentAction *action.SelectSkillAction if i == 0 { currentAction = firstAttack + if currentAction == nil { + continue + } attacker, defender = f.getSkillParticipants(firstAttack) originalSkill = f.copySkill(firstAttack) //先手阶段,先修复后手效果 f.Second.RecoverEffect() } else { currentAction = secondAttack + if currentAction == nil { + continue + } attacker, defender = f.getSkillParticipants(secondAttack) originalSkill = f.copySkill(secondAttack) //取消后手历史效果 @@ -434,7 +453,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) f.sendLegacyRoundBroadcast(firstAttack, secondAttack) } - attackValueResult := f.buildNoteUseSkillOutboundInfo() + attackValueResult := f.buildNoteUseSkillOutboundInfo(firstAttack, secondAttack) //因为切完才能广播,所以必须和回合结束分开结算 f.BroadcastPlayers(func(p common.PlayerI) { for _, switchAction := range f.Switch { diff --git a/logic/service/fight/unified_test.go b/logic/service/fight/unified_test.go index dfd03d1fd..88459d58c 100644 --- a/logic/service/fight/unified_test.go +++ b/logic/service/fight/unified_test.go @@ -5,6 +5,7 @@ import ( "blazing/common/socket/errorcode" "blazing/logic/service/common" + "blazing/logic/service/fight/action" fightinfo "blazing/logic/service/fight/info" "blazing/logic/service/fight/input" spaceinfo "blazing/logic/service/space/info" @@ -111,3 +112,38 @@ func TestBuildFightStateStartEnvelope(t *testing.T) { t.Fatalf("unexpected right fighter snapshot: %+v", envelope.Right[0]) } } + +func TestBuildNoteUseSkillOutboundInfoUsesActionOrder(t *testing.T) { + ourPlayer := &stubPlayer{info: model.PlayerInfo{UserID: 1001}} + oppPlayer := &stubPlayer{info: model.PlayerInfo{UserID: 2002}} + + our := input.NewInput(nil, ourPlayer) + our.InitAttackValue() + our.AttackValue.SkillID = 111 + our.AttackValue.RemainHp = 80 + our.AttackValue.MaxHp = 100 + + opp := input.NewInput(nil, oppPlayer) + opp.InitAttackValue() + opp.AttackValue.SkillID = 222 + opp.AttackValue.RemainHp = 70 + opp.AttackValue.MaxHp = 100 + + fc := &FightC{ + Our: []*input.Input{our}, + Opp: []*input.Input{opp}, + } + + firstAttack := &action.SelectSkillAction{BaseAction: action.BaseAction{PlayerID: 2002, ActorIndex: 0}} + result := fc.buildNoteUseSkillOutboundInfo(firstAttack, nil) + + if len(result.FirstAttackInfo) != 1 { + t.Fatalf("expected only first attack info, got first=%d second=%d", len(result.FirstAttackInfo), len(result.SecondAttackInfo)) + } + if len(result.SecondAttackInfo) != 0 { + t.Fatalf("expected no second attack info, got %d", len(result.SecondAttackInfo)) + } + if result.FirstAttackInfo[0].UserID != 2002 || result.FirstAttackInfo[0].SkillID != 222 { + t.Fatalf("expected first attack info to belong to acting opponent, got %+v", result.FirstAttackInfo[0]) + } +} diff --git a/modules/config/service/server.go b/modules/config/service/server.go index 11761541c..99e8e5513 100644 --- a/modules/config/service/server.go +++ b/modules/config/service/server.go @@ -23,16 +23,16 @@ type ServerService struct { } type ServerShowInfo struct { - OnlineID uint32 `json:"online_id"` - Name string `json:"name"` - IP string `json:"ip"` - Port uint32 `json:"port"` - IsVip uint32 `json:"is_vip"` - IsDebug uint8 `json:"is_debug"` - IsOpen uint8 `json:"is_open"` - Owner uint32 `json:"owner"` - ExpireTime time.Time `json:"expire_time"` - ServerShow *model.ServerShow `json:"servershow,omitempty"` + OnlineID uint32 `json:"online_id"` + Name string `json:"name"` + IP string `json:"ip"` + Port uint32 `json:"port"` + IsVip uint32 `json:"is_vip"` + IsDebug uint8 `json:"is_debug"` + IsOpen uint8 `json:"is_open"` + //Owner uint32 `json:"owner"` + // ExpireTime time.Time `json:"expire_time"` + // ServerShow *model.ServerShow `json:"servershow,omitempty"` } type DonationOwnedServerInfo struct { @@ -112,29 +112,33 @@ func (s *ServerService) GetPort(DepartmentID uint) []ServerShowInfo { continue } items = append(items, ServerShowInfo{ - OnlineID: server.OnlineID, - Name: server.Name, - IP: server.IP, - Port: server.Port, - IsVip: server.IsVip, - IsDebug: server.IsDebug, - IsOpen: server.IsOpen, - Owner: 0, - ExpireTime: time.Time{}, + OnlineID: server.OnlineID, + Name: server.Name, + IP: server.IP, + Port: server.Port, + IsVip: server.IsVip, + IsDebug: server.IsDebug, + IsOpen: server.IsOpen, + //Owner: 0, + // ExpireTime: time.Time{}, }) for i := range showMap[server.OnlineID] { show := &showMap[server.OnlineID][i] + itemOnlineID := server.OnlineID + if show.ID > 0 { + itemOnlineID = uint32(show.ID) + } item := ServerShowInfo{ - OnlineID: server.OnlineID, - Name: server.Name, - IP: server.IP, - Port: server.Port, - IsVip: server.IsVip, - IsDebug: server.IsDebug, - IsOpen: server.IsOpen, - Owner: show.Owner, - ExpireTime: show.ExpireTime, - ServerShow: show, + OnlineID: itemOnlineID, + Name: server.Name, + IP: server.IP, + Port: server.Port, + IsVip: server.IsVip, + IsDebug: server.IsDebug, + IsOpen: server.IsOpen, + + // ExpireTime: show.ExpireTime, + // ServerShow: show, } if show.Name != "" { item.Name = show.Name