game/server/logic/combat.go
2026-04-20 16:07:22 +08:00

243 lines
7.1 KiB
Go
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}