Files
OnlineRpg/战斗系统修复说明.md
2025-10-26 20:44:58 +08:00

7.1 KiB
Raw Permalink Blame History

战斗系统输入处理修复

🐛 问题描述

症状1战斗中输入被错误处理

>>> 你的回合 <<<
【战斗】> attack
无效选项。请输入1-5。  ❌ 错误:被大厅菜单处理器处理

症状2不断显示战斗菜单

【战斗】> 1
================================
  战斗中
================================
等待你的回合...
================================
【战斗】> 2
================================
  战斗中
================================
等待你的回合...

症状3连接崩溃

terminate called without an active exception
Aborted

🔍 根本原因

主循环逻辑问题

修复前的代码

while (m_isRunning && m_isConnected) {
    if (!m_isAuthenticated) {
        showMainMenu();
    } else if (m_inBattle) {
        showBattleMenu();  // ❌ 不管是否轮到玩家都调用
    } else {
        showLobbyMenu();
    }
    
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

问题分析

  1. m_inBattle = truem_waitingForTurn = false
  2. showBattleMenu() 被调用 → 等待输入
  3. 用户输入后,handleBattleInput() 检查 !m_waitingForTurn 直接返回
  4. 循环继续100ms 后再次调用 showBattleMenu()
  5. 无限显示菜单

状态机混乱

战斗有三个状态:

  • 未认证 → 显示主菜单
  • 在大厅 → 显示大厅菜单
  • 在战斗中
    • 轮到玩家 → 显示战斗菜单,接受输入
    • 不是玩家回合 → 静默等待,不显示菜单

原来的代码只区分了前两个状态,导致第三个状态处理错误。

修复方案

1. 改进主循环逻辑

void GameClient::run() {
    if (!connect()) {
        return;
    }
    
    while (m_isRunning && m_isConnected) {
        if (!m_isAuthenticated) {
            showMainMenu();
        } else if (m_inBattle && m_waitingForTurn) {
            // ✓ 只在战斗中且轮到玩家时才显示战斗菜单
            showBattleMenu();
        } else if (!m_inBattle) {
            // ✓ 不在战斗中,显示大厅菜单
            showLobbyMenu();
        } else {
            // ✓ 在战斗中但不是玩家回合,静默等待
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    }
}

关键改进

  • 添加 m_waitingForTurn 检查
  • 只在 m_inBattle && m_waitingForTurn 时显示战斗菜单
  • 不是玩家回合时进入等待状态,不显示任何菜单

2. 简化战斗菜单

void GameClient::showBattleMenu() {
    printTitle("战斗中");
    std::cout << "输入 'attack' 或 '攻击' 使用普通攻击" << std::endl;
    printSeparator();
    
    std::string input;
    showPrompt();
    std::getline(std::cin, input);
    handleBattleInput(input);
}

移除了不必要的检查,因为主循环已经保证只在正确时机调用。

3. 改进输入处理

void GameClient::handleBattleInput(const std::string& input) {
    if (input.empty()) {
        return;
    }
    
    if (input == "attack" || input == "攻击") {
        std::string msg = Protocol::buildMessage(Protocol::C2S_BATTLE_ACTION, {"0", "opponent"});
        sendMessage(msg);
        m_waitingForTurn = false;  // 行动后设置为 false
    } else {
        std::cout << "无效命令。请输入 'attack' 或 '攻击'" << std::endl;
    }
}

关键点

  • 执行攻击后设置 m_waitingForTurn = false
  • 添加友好的错误提示

📊 状态转换流程

正确的战斗流程

1. 玩家A邀请玩家B
   ↓
2. 玩家B接受邀请
   ↓
3. 服务器发送 S2C_BATTLE_START
   → m_inBattle = true
   ↓
4. 服务器发送 S2C_BATTLE_TURN (YOUR_TURN)
   → m_waitingForTurn = true
   ↓
5. 主循环检测到 (m_inBattle && m_waitingForTurn)
   → 调用 showBattleMenu()
   ↓
6. 玩家输入 "attack"
   → handleBattleInput() 处理
   → 发送 C2S_BATTLE_ACTION
   → m_waitingForTurn = false
   ↓
7. 主循环检测到 (m_inBattle && !m_waitingForTurn)
   → 进入等待状态(不显示菜单)
   ↓
8. 服务器处理回合,发送战斗日志
   → 显示战斗结果
   ↓
9. 服务器发送 S2C_BATTLE_TURN (OPP_TURN)
   → 保持 m_waitingForTurn = false
   → 继续等待
   ↓
10. 对手行动完毕,服务器发送 S2C_BATTLE_TURN (YOUR_TURN)
    → m_waitingForTurn = true
    → 回到步骤 5

🧪 测试步骤

步骤1重启客户端

cd /mnt/e/50425/Documents/Github/OnlineRpg
./build/bin/client

步骤2登录两个玩家

  • 终端1玩家A登录
  • 终端2玩家B登录

步骤3发起战斗邀请

玩家A

【玩家A】> 3
目标玩家用户名玩家B用户名

玩家B

*** 玩家A 邀请你战斗!***
输入 'accept 玩家A' 接受,或 'reject 玩家A' 拒绝
【玩家B】> accept 玩家A

步骤4战斗测试

预期行为

*** 战斗开始!***
对手玩家BMage
====================

>>> 你的回合 <<<
输入 'attack' 使用普通攻击
================================
  战斗中
================================
输入 'attack' 或 '攻击' 使用普通攻击
================================
【战斗】> attack          ← 输入攻击命令
【战斗】玩家A HP:150      ← 显示战斗日志
【战斗】玩家A 使用 NormalAttack 攻击 玩家B造成 15 点伤害!
                          ← 自动进入等待状态,不显示菜单
>>> 对手的回合...
【战斗】玩家B HP:85
【战斗】玩家B 使用 Fireball 攻击 玩家A造成 28 点伤害!

>>> 你的回合 <<<          ← 再次轮到玩家
================================
  战斗中
================================
输入 'attack' 或 '攻击' 使用普通攻击
================================
【战斗】> attack

成功标志

  • 输入 attack 后正确执行攻击
  • 不是自己回合时不显示菜单
  • 没有"无效选项。请输入1-5。"错误
  • 战斗日志正常显示
  • 连接保持稳定,不崩溃

🔧 相关文件

  • src/client/GameClient.cpp - 主要修复文件
  • src/client/GameClient.h - 状态变量定义
  • include/common/Protocol.h - 协议定义

📈 后续优化建议

  1. 添加更多技能选择

    【战斗】> skill
    可用技能:
    0. 普通攻击
    1. 强力打击 (消耗10MP)
    2. 防御姿态 (消耗5MP)
    选择技能编号:
    
  2. 显示双方状态

    玩家A (Warrior) HP:122/150 MP:15/20
    对手玩家B (Mage) HP:85/100 MP:10/50
    
  3. 战斗历史查看

    【战斗】> history
    第1回合玩家A 使用 普通攻击 造成 15 伤害
    第2回合玩家B 使用 火球术 造成 28 伤害
    第3回合玩家A 使用 强力打击 造成 30 伤害
    
  4. 逃跑选项

    【战斗】> flee
    你逃离了战斗!
    

总结

修复了战斗系统的输入处理逻辑,通过改进主循环状态判断,确保:

  • 战斗菜单只在正确时机显示
  • 输入被正确的处理器处理
  • 不是玩家回合时静默等待
  • 避免了菜单无限显示和连接崩溃问题

现在战斗系统应该能够正常工作了!