package logic import ( "encoding/json" "fmt" "math/rand" "time" "wuziqi-server/core" "wuziqi-server/items" ) // ApplyDamage 处理伤害计算、减免和效果 func (e *GameEngine) ApplyDamage(state *core.GameState, target *core.Player, amount int, isItemEffect bool) { if target.HP <= 0 { return } // 1. 护盾减免 if target.Shield { target.Shield = false e.Logger.Info("Player %s (%s) blocked damage with shield", target.UserID, target.Character) return // Blocked } // 2. 角色特质修改 // 猫咪天赋: 伤害强制为1 // 树懒: 如果是定时炸弹(item effect logic handled in engine.go), here we just take raw amount // But CharacterManager.OnDamageTaken handles Cat logic amount = e.CharManager.OnDamageTaken(target, amount, isItemEffect) e.Logger.Info("ApplyDamage: PlayerID=%s, Char=%s, FinalAmount=%d", target.UserID, target.Character, amount) // 3. 诅咒修改 // 非猫角色受到诅咒伤害翻倍 if target.Curse && target.Character != "cat" { amount *= 2 target.Curse = false } else if target.Character == "cat" { target.Curse = false // 即使没有增加伤害也清除诅咒 } // 4. 应用伤害 target.HP -= amount // 5. 鸡的特质(受伤获得道具) if itemID, triggered := e.CharManager.TryTriggerChickenAbility(target); triggered { switch itemID { case "skip": target.SkipTurn = true target.Shield = true case "shield": target.Shield = true case "magnifier": // 触发放大镜逻辑 if e.ItemManager != nil { // 这里不方便使用 ItemStrategy,因为上下文需要 ItemContext // 如果构建上下文,我们可以直接执行逻辑或重用策略 // 为了简单起见,我们直接实现逻辑以避免开销 for i := 0; i < 100; i++ { cellIdx := rand.Intn(len(state.Grid)) if !state.Grid[cellIdx].Revealed { cellType := state.Grid[cellIdx].Type if state.Grid[cellIdx].Type == "item" { cellType = state.Grid[cellIdx].ItemID } target.RevealedCells[cellIdx] = cellType break } } } } e.Logger.Info("Chicken %s triggered ability, got %s", target.UserID, itemID) e.BroadcastEvent(core.GameEvent{ Type: "ability", PlayerID: target.UserID, PlayerName: target.Username, Message: fmt.Sprintf("🐔 鸡你太美!受伤触发天赋,获得了 %s!", itemID), }) } // 6. 死亡检查 if target.HP <= 0 { if target.Revive { target.Revive = false target.HP = 1 e.BroadcastEvent(core.GameEvent{ Type: "ability", PlayerID: target.UserID, PlayerName: target.Username, Message: "💖 复活甲生效,免疫死亡!", }) } else if e.CharManager.TryTriggerHippoResist(target) { target.HP = 1 e.BroadcastEvent(core.GameEvent{ Type: "ability", PlayerID: target.UserID, PlayerName: target.Username, Message: "🦛 河马皮糙肉厚,免疫了一次死亡!", }) } } if target.HP <= 0 { target.HP = 0 state.LastDeadPlayerID = target.UserID } } func (e *GameEngine) HealPlayer(p *core.Player, amount int) { if p.HP < p.MaxHP { p.HP += amount if p.HP > p.MaxHP { p.HP = p.MaxHP } } } // HandleMove 处理玩家的移动 func (e *GameEngine) HandleMove(state *core.GameState, userID string, cellIndex int) { e.Logger.Info("HandleMove: UserID=%s, Index=%d", userID, cellIndex) // 验证回合 if len(state.TurnOrder) == 0 { return } currentUserID := state.TurnOrder[state.CurrentTurnIndex] if userID != currentUserID { e.Logger.Warn("HandleMove rejected: Turn mismatch. Incoming=%s, Expected=%s", userID, currentUserID) return } // 验证格子 if cellIndex < 0 || cellIndex >= len(state.Grid) { return } cell := state.Grid[cellIndex] if cell.Revealed { return } player := state.Players[userID] if player == nil { return } // 执行移动 cell.Revealed = true state.GlobalTurnCount++ state.LastMoveTimestamp = time.Now().Unix() // 狗狗技能:基于狗狗自身的移动步数触发 if player.Character == "dog" { player.DogStepCount++ interval := 6 if len(state.Players) >= 6 { interval = 9 } if player.DogStepCount%interval == 0 { // 触发放大镜效果 for i := 0; i < 100; i++ { idx := rand.Intn(len(state.Grid)) if !state.Grid[idx].Revealed { cellType := state.Grid[idx].Type if state.Grid[idx].Type == "item" { cellType = state.Grid[idx].ItemID } player.RevealedCells[idx] = cellType coords := state.FormatCoordinates(idx) contentDesc := core.TranslateCellType(cellType) msg := fmt.Sprintf("🐶 狗狗触发嗅觉天赋(第%d步),发现 %s 格子是 [%s]!", player.DogStepCount, coords, contentDesc) e.SendPrivateEvent(player.UserID, core.GameEvent{ Type: "ability", PlayerID: player.UserID, PlayerName: player.Username, Message: msg, CellIndex: idx, CellType: cellType, }) break } } } } // 猴子技能 if player.Character == "monkey" && player.MonkeyBananaCount < 2 && rand.Float32() < 0.15 { e.HealPlayer(player, 1) player.MonkeyBananaCount++ e.BroadcastEvent(core.GameEvent{ Type: "ability", PlayerID: player.UserID, PlayerName: player.Username, Value: 1, Message: "🍌 猴子发现了香蕉,回复1点血量!", }) } // 处理内容 if cell.Type == "bomb" { dmg := 2 // 树懒踩炸弹伤害减半 if player.Character == "sloth" { dmg = 1 } e.Logger.Info("BombHit: UserID=%s, Char=%s, Dmg=%d", player.UserID, player.Character, dmg) e.BroadcastEvent(core.GameEvent{ Type: "damage", PlayerID: player.UserID, PlayerName: player.Username, Value: dmg, Message: fmt.Sprintf("💣 踩到炸弹,受到%d点伤害!", dmg), }) e.ApplyDamage(state, player, dmg, false) } else if cell.Type == "item" { if player.Character == "hippo" { e.BroadcastEvent(core.GameEvent{ Type: "ability", PlayerID: player.UserID, PlayerName: player.Username, ItemID: cell.ItemID, Message: "🦛 河马无法拾取道具!", }) } else { // 构建上下文 ctx := items.ItemContext{ Logger: e.Logger, Dispatcher: e.Dispatcher, Logic: e, } e.ItemManager.UseItem(state, player, cell.ItemID, ctx) } } else if cell.Type == "empty" { if cell.NeighborBombs == 0 { revealed := RevealSafeArea(state, cellIndex) if len(revealed) > 1 { e.BroadcastEvent(core.GameEvent{ Type: "ability", PlayerID: player.UserID, PlayerName: player.Username, Value: len(revealed), Message: fmt.Sprintf("🔓 发现安全区域,自动揭示了 %d 个格子!", len(revealed)), }) } } } // 游戏结束和回合推进 // 无论如何先尝试推进回合(特别是如果当前玩家刚刚死亡/跳过) e.AdvanceTurn(state) // 然后检查游戏是否结束 if e.CheckGameOver(state) { e.Logger.Info("Match %s ended during HandleMove for user %s", state.WinnerID, userID) return } // 广播常规状态更新 e.Logger.Debug("Broadcasting state update for match, current turn index: %d, alive players: %d", state.CurrentTurnIndex, len(state.Players)) updateData, _ := json.Marshal(state.Sanitize()) e.Dispatcher.BroadcastMessage(core.OpCodeUpdateState, updateData, nil, nil, true) }