《风暴》搜打撤游戏正式版(禁止改编)

leo1009 2026-09-26 11:00:50 2026-11-01 15:30:28

// RussianHardcoreExtraction.cpp // Dev-C++ C++14 兼容 // 编译选项: -std=c++14 // 版本: v4.0

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <random>
#include <ctime>
#include <cmath>
#include <limits>
#include <stdexcept>
#include <fstream>
#include <windows.h>

void pause() { Sleep(1800); }

std::mt19937 rng(static_cast<unsigned>(std::time(nullptr)));
int randInt(int min, int max) { std::uniform_int_distribution<int> d(min, max); return d(rng); }
double randDouble(double min, double max) { std::uniform_real_distribution<double> d(min, max); return d(rng); }

int readInt() {
    int v;
    while (!(std::cin >> v)) {
        if (std::cin.eof()) { std::cout << "\n[输入流已关闭,程序退出]" << std::endl; std::exit(0); }
        std::cin.clear(); std::cin.ignore(10000, '\n');
        std::cout << "输入无效,请输入数字: ";
    }
    return v;
}

enum class ArmorClass { NONE = 0, CLASS_2 = 2, CLASS_3 = 3, CLASS_4 = 4, CLASS_5 = 5, CLASS_6 = 6 };
enum class BodyPart { HEAD, CHEST, STOMACH, LEFT_ARM, RIGHT_ARM, LEFT_LEG, RIGHT_LEG, COUNT };
enum class AmmoType { FMJ, AP, HP, DUMDUM, TRACER };
enum class Role { KATYUSHA, TOKAREV, ANNA, ALEXANDER, IVAN, ENEMY };
enum class BuffType { PAIN, BLEEDING, HEAVY_BLEEDING, FRACTURE, STOMACH_INJURY, ADRENALINE, PAINKILLER, WEAK, STUN };
enum class ContainerType { WEAPON_BOX, AMMO_CRATE, MEDICAL_CABINET, SAFE, CORPSE, JACKET, TOOLBOX, FILING_CABINET, BOSS_ZONE };
enum class ItemCategory { MONEY, WEAPON, AMMO, ARMOR, MEDICAL, VALUABLE };

enum class MonoEvent {
    MAP_ENTER, TURN_IDLE, SEARCH_EMPTY, SEARCH_LOW, SEARCH_HIGH,
    SEARCH_LOCKED, SEARCH_NO_KEY, KEY_USE, EXTRACT_SUCCESS, LOW_ARMOR
};

std::map<std::pair<Role, MonoEvent>, std::vector<std::string>> monoPool;

void initMonologuePool() {
    using P = std::pair<Role, MonoEvent>;
    monoPool[P(Role::KATYUSHA, MonoEvent::MAP_ENTER)] = {"又是这种地方……", "母亲……弟弟……等我回去。", "得活着回去。", "冷……这风跟西伯利亚一样。"};
    monoPool[P(Role::KATYUSHA, MonoEvent::TURN_IDLE)] = {"呼……", "得小心点。", "别出声。", "这里太安静了。"};
    monoPool[P(Role::KATYUSHA, MonoEvent::SEARCH_EMPTY)] = {"什么都没有……", "白跑一趟。", "下次吧。", "得换个地方。"};
    monoPool[P(Role::KATYUSHA, MonoEvent::SEARCH_LOW)] = {"这些东西能换点钱。", "聊胜于无。", "收着吧。", "总比没有强。"};
    monoPool[P(Role::KATYUSHA, MonoEvent::SEARCH_HIGH)] = {"这个……能卖不少钱!", "运气不错。", "得赶紧收起来。", "回家有救了。"};
    monoPool[P(Role::KATYUSHA, MonoEvent::SEARCH_LOCKED)] = {"锁着的……得找钥匙。", "打不开。", "钥匙在哪?", "这里面一定有东西。"};
    monoPool[P(Role::KATYUSHA, MonoEvent::SEARCH_NO_KEY)] = {"没钥匙……先记着。", "得去买一把。", "下次再来。"};
    monoPool[P(Role::KATYUSHA, MonoEvent::KEY_USE)] = {"开了。", "咔哒……", "进去看看。"};
    monoPool[P(Role::KATYUSHA, MonoEvent::EXTRACT_SUCCESS)] = {"活下来了……", "又一天。", "回去了。"};
    monoPool[P(Role::KATYUSHA, MonoEvent::LOW_ARMOR)] = {"甲快废了……", "不能再挨一下了。"};

    monoPool[P(Role::TOKAREV, MonoEvent::MAP_ENTER)] = {"又是老一套。", "这地方我熟。", "跟车臣差不多。", "还没死呢,再打一场。"};
    monoPool[P(Role::TOKAREV, MonoEvent::TURN_IDLE)] = {"哼。", "稳着点。", "老规矩。"};
    monoPool[P(Role::TOKAREV, MonoEvent::SEARCH_EMPTY)] = {"空的。", "白瞎。", "没啥可拿的。", "换地方。"};
    monoPool[P(Role::TOKAREV, MonoEvent::SEARCH_LOW)] = {"破烂。", "凑合。", "能卖几个钱。"};
    monoPool[P(Role::TOKAREV, MonoEvent::SEARCH_HIGH)] = {"好东西!", "这下值了。", "老兵运气还在。"};
    monoPool[P(Role::TOKAREV, MonoEvent::SEARCH_LOCKED)] = {"锁着。", "得有钥匙。", "麻烦。"};
    monoPool[P(Role::TOKAREV, MonoEvent::SEARCH_NO_KEY)] = {"钥匙呢……", "记着这地方。"};
    monoPool[P(Role::TOKAREV, MonoEvent::KEY_USE)] = {"咔哒。", "进得去了。"};
    monoPool[P(Role::TOKAREV, MonoEvent::EXTRACT_SUCCESS)] = {"又一天。", "回去喝一杯。", "这活还得干。"};
    monoPool[P(Role::TOKAREV, MonoEvent::LOW_ARMOR)] = {"甲不行了。", "这身老肉挡着。"};

    monoPool[P(Role::ANNA, MonoEvent::MAP_ENTER)] = {"队友们……我来了。", "信号就在附近。", "一定要找到他们。", "冷静,安娜。"};
    monoPool[P(Role::ANNA, MonoEvent::TURN_IDLE)] = {"……", "没事。", "继续。", "小心。"};
    monoPool[P(Role::ANNA, MonoEvent::SEARCH_EMPTY)] = {"没有。", "空的……", "不是这里。", "继续找。"};
    monoPool[P(Role::ANNA, MonoEvent::SEARCH_LOW)] = {"有点东西。", "收着。", "先拿着。"};
    monoPool[P(Role::ANNA, MonoEvent::SEARCH_HIGH)] = {"这个……有用!", "找到了。", "运气不错。"};
    monoPool[P(Role::ANNA, MonoEvent::SEARCH_LOCKED)] = {"锁的……", "需要钥匙。", "后面一定有东西。"};
    monoPool[P(Role::ANNA, MonoEvent::SEARCH_NO_KEY)] = {"没钥匙。", "记下位置。", "之后再来。"};
    monoPool[P(Role::ANNA, MonoEvent::KEY_USE)] = {"开了。", "进去看看。"};
    monoPool[P(Role::ANNA, MonoEvent::EXTRACT_SUCCESS)] = {"活着出来了。", "还差一点。", "继续找他们。"};
    monoPool[P(Role::ANNA, MonoEvent::LOW_ARMOR)] = {"甲快不行了。", "不能再挨了。"};

    monoPool[P(Role::ALEXANDER, MonoEvent::MAP_ENTER)] = {"开始。", "按计划行事。", "这就是战场。", "进入战斗区域。"};
    monoPool[P(Role::ALEXANDER, MonoEvent::TURN_IDLE)] = {"……", "观察。", "继续。"};
    monoPool[P(Role::ALEXANDER, MonoEvent::SEARCH_EMPTY)] = {"空的。", "无收获。", "继续。"};
    monoPool[P(Role::ALEXANDER, MonoEvent::SEARCH_LOW)] = {"基础物资。", "收着。"};
    monoPool[P(Role::ALEXANDER, MonoEvent::SEARCH_HIGH)] = {"高价值目标。", "不错。", "这个有用。"};
    monoPool[P(Role::ALEXANDER, MonoEvent::SEARCH_LOCKED)] = {"上锁。", "需要钥匙。"};
    monoPool[P(Role::ALEXANDER, MonoEvent::SEARCH_NO_KEY)] = {"无钥匙。", "记录。"};
    monoPool[P(Role::ALEXANDER, MonoEvent::KEY_USE)] = {"解锁。", "进入。"};
    monoPool[P(Role::ALEXANDER, MonoEvent::EXTRACT_SUCCESS)] = {"撤离完成。", "任务结束。", "回去。"};
    monoPool[P(Role::ALEXANDER, MonoEvent::LOW_ARMOR)] = {"甲接近报废。"};

    monoPool[P(Role::IVAN, MonoEvent::MAP_ENTER)] = {"Here we go again.", "又是这鬼地方。", "Сука……又是这儿。", "行吧,干活。"};
    monoPool[P(Role::IVAN, MonoEvent::TURN_IDLE)] = {"哼。", "看看再说。", "别急。", "安静得诡异。"};
    monoPool[P(Role::IVAN, MonoEvent::SEARCH_EMPTY)] = {"空手而归。", "白跑。", "Да ну. 啥也没有。", "下一个。"};
    monoPool[P(Role::IVAN, MonoEvent::SEARCH_LOW)] = {"破烂。", "凑合。", "能卖钱就行。"};
    monoPool[P(Role::IVAN, MonoEvent::SEARCH_HIGH)] = {"Opa!好东西。", "这波不亏。", "发财了。"};
    monoPool[P(Role::IVAN, MonoEvent::SEARCH_LOCKED)] = {"锁的。", "钥匙在哪?", "麻烦。"};
    monoPool[P(Role::IVAN, MonoEvent::SEARCH_NO_KEY)] = {"没钥匙……记着。", "回头再说。"};
    monoPool[P(Role::IVAN, MonoEvent::KEY_USE)] = {"开了。", "进去。"};
    monoPool[P(Role::IVAN, MonoEvent::EXTRACT_SUCCESS)] = {"活着出来了。", "收工。", "回去喝酒。"};
    monoPool[P(Role::IVAN, MonoEvent::LOW_ARMOR)] = {"甲报废了。", "这下只能靠肉了。"};
}

void speakMono(Role role, MonoEvent ev, double chance = 1.0) {
    if (randDouble(0, 1) >= chance) return;
    auto it = monoPool.find(std::make_pair(role, ev));
    if (it == monoPool.end() || it->second.empty()) return;
    pause();
    int r = randInt(0, (int)it->second.size() - 1);
    std::cout << "  「" << it->second[r] << "」" << std::endl;
    pause();
}

struct Map {
    std::string name, description;
    int difficulty, minContainers, maxContainers, minDanger, maxDanger;
    int enemyArmorBonus, enemyWeaponBonus, commonThreshold, midThreshold;
    int maxEntryMoney; bool enableKeys;
    Map() : difficulty(1), minContainers(4), maxContainers(6), minDanger(5), maxDanger(20),
            enemyArmorBonus(0), enemyWeaponBonus(0), commonThreshold(70), midThreshold(92),
            maxEntryMoney(50000), enableKeys(true) {}
};

const double SHOTGUN_ARMOR_MULT_BLOCKED = 1.25;
const double SHOTGUN_ARMOR_MULT_PENETRATED = 1.25;
const double KATYUSHA_DODGE_MAX = 0.15;
const double KATYUSHA_DODGE_STOMACH_PEN = 0.05;
const double KATYUSHA_DODGE_SURGERY_GAIN = 0.05;
const double ANNA_HP_THRESHOLD = 0.25;
const double ANNA_DAMAGE_MULT = 1.15;
const double ANNA_DEFENSE_MULT = 0.80;
const double ANNA_REVIVE_DAMAGE_MULT = 1.10;
const int ANNA_REVIVE_HP = 15;
const int ANNA_WEAK_MAXHP = 80;
const int ANNA_WEAK_TURNS = 9999;
const double ALEXANDER_HP_THRESHOLD = 0.50;
const int ALEXANDER_FLASH_TURNS = 2;
const double PAIN_MISS_CHANCE = 0.10;
const double PAIN_DAMAGE_MIN = 0.85;
const double PAIN_DAMAGE_MAX = 0.90;
const double PAIN_DODGE_PENALTY = 0.70;
const double FRACTURE_SPEED_MULT = 0.60;
const double STOMACH_SPEED_MULT = 0.70;
const double SLOW_SEARCH_THRESHOLD = 0.80;
const double ENEMY_DEFAULT_SPEED = 0.95;
const int RAID_MAX_TURNS = 20;
const int HELEN_TREATMENT_CAP = 10000;
const int PERMA_BUFF_VALUE = 999999;
const int BOSS_SPAWN_CHANCE = 10;
const int DEFAULT_LOOT_CAPACITY = 4;
const int ANTONOV_REFRESH_COST = 10000;
const int BANK_LOAN_MAX = 100000;
const double BANK_INTEREST_RATE = 0.02;
const double BODY_LIGHT_THRESHOLD = 0.60;
const double BODY_HEAVY_THRESHOLD = 0.30;
const int BOLT_CYCLE_RATE_THRESHOLD = 100;
const int EXTRA_FIRE_RATE_THRESHOLD = 900;
const int BURST_RATE_THRESHOLD = 600;
const double BURST_2ND_HIT_PENALTY = 0.15;
const double EXTRA_FIRE_HIT_PENALTY = 0.10;

const int    IVAN_STUN_GRENADE_MAX = 2;
const int    IVAN_STUN_GRENADE_TRIGGER_DAMAGE = 15;
const int    IVAN_STUN_MIN_CHANCE = 10;
const int    IVAN_STUN_MAX_CHANCE = 30;
const int    IVAN_STUN_DAMAGE_MIN = 1;
const int    IVAN_STUN_DAMAGE_MAX = 5;
const int    IVAN_STUN_PAIN_TURNS = 5;
const int    IVAN_STUN_STUN_TURNS = 3;
const double IVAN_SUPPRESSION_CHANCE = 0.10;
const double IVAN_SUPPRESSION_DAMAGE_MULT = 0.85;
const double IVAN_DEATH_RUSH_HP = 0.15;
const double IVAN_DEATH_RUSH_SPEED = 1.10;
const double STUN_MISS_CHANCE = 0.05;
const int    KATYUSHA_INTERFERENCE_DELAY = 2;
const int    TOKAREV_LAST_STRIKE_ARMOR_DMG = 30;
const int    TOKAREV_LAST_STRIKE_BODY_DMG = 10;
const int    TOKAREV_LAST_STRIKE_HP_THRESHOLD = 15;
const double ANNA_MEDICAL_HEAL_MULT = 1.10;
const int    ANNA_BEACON_INSTANT_HEAL = 20;
const int    ALEXANDER_HEADSET_BONUS = 5;

// ==================== 头盔 ====================
struct Helmet {
    std::string name;
    ArmorClass armorClass;
    int maxDurability, currentDurability;
    double ricochetChance;
    double weight;
    int price;
    bool owned;

    Helmet() : armorClass(ArmorClass::NONE), maxDurability(0), currentDurability(0),
               ricochetChance(0.0), weight(0), price(0), owned(false) {}

    Helmet(std::string n, ArmorClass ac, int dur, double ric, double w, int p)
        : name(n), armorClass(ac), maxDurability(dur), currentDurability(dur),
          ricochetChance(ric), weight(w), price(p), owned(false) {}

    ArmorClass effectiveClass() const {
        if (maxDurability <= 0) return ArmorClass::NONE;
        double ratio = (double)currentDurability / maxDurability;
        if (ratio <= 0.20) { int l = (int)armorClass - 2; return l <= 0 ? ArmorClass::NONE : (ArmorClass)l; }
        if (ratio <= 0.40) { int l = (int)armorClass - 1; return l <= 0 ? ArmorClass::NONE : (ArmorClass)l; }
        return armorClass;
    }
    bool isDestroyed() const { return currentDurability <= 0; }
    void takeDamage(int d) { currentDurability = std::max(0, currentDurability - d); }
};

// ==================== 护甲 ====================
struct BodyArmor {
    std::string name;
    ArmorClass armorClass;
    int maxDurability, currentDurability;
    double weight; int price; bool owned, isRare;
    BodyArmor() : armorClass(ArmorClass::NONE), maxDurability(0), currentDurability(0),
                  weight(0), price(0), owned(false), isRare(false) {}
    BodyArmor(std::string n, ArmorClass ac, int dur, double w, int p, bool rare = false)
        : name(n), armorClass(ac), maxDurability(dur), currentDurability(dur), weight(w), price(p), owned(false), isRare(rare) {}
    ArmorClass effectiveClass() const {
        if (maxDurability <= 0) return ArmorClass::NONE;
        double ratio = (double)currentDurability / maxDurability;
        if (ratio <= 0.20) { int l = (int)armorClass - 2; return l <= 0 ? ArmorClass::NONE : (ArmorClass)l; }
        if (ratio <= 0.40) { int l = (int)armorClass - 1; return l <= 0 ? ArmorClass::NONE : (ArmorClass)l; }
        return armorClass;
    }
    bool isDestroyed() const { return currentDurability <= 0; }
    void takeDamage(int d) { currentDurability = std::max(0, currentDurability - d); }
};

// ==================== 药品 ====================
struct MedicalItem {
    std::string name, description;
    int healAmount;
    bool stopsBleeding, stopsHeavyBleeding, fixesFracture, fixesStomachInjury, givesPainkiller, blockedByFracture;
    int armorRepairAmount, uses, price;
    MedicalItem() : healAmount(0), stopsBleeding(false), stopsHeavyBleeding(false), fixesFracture(false),
                    fixesStomachInjury(false), givesPainkiller(false),
                    blockedByFracture(false), armorRepairAmount(0),
                    uses(1), price(0) {}
};

// ==================== 针剂 ====================
struct Stimulator {
    std::string name, description;
    int price;
    int instantHeal;
    bool stopAllBleeding, fixFracture, removePain;
    int healPerTurn, healPerTurnTurns;
    double speedBonus; int speedBonusTurns;
    double damageBonus; int damageBonusTurns;
    double damageResist; int damageResistTurns;
    Stimulator() : price(0), instantHeal(0), stopAllBleeding(false), fixFracture(false),
                   removePain(false), healPerTurn(0), healPerTurnTurns(0),
                   speedBonus(0.0), speedBonusTurns(0), damageBonus(0.0), damageBonusTurns(0),
                   damageResist(0.0), damageResistTurns(0) {}
};

struct Ammo {
    std::string name, caliber; AmmoType type;
    int damage, penetration;
    double armorDamagePercent, bluntDamageRatio;
    int pricePerRound, quantity; bool isRare;
    Ammo() : type(AmmoType::FMJ), damage(0), penetration(0), armorDamagePercent(0.0),
             bluntDamageRatio(0.0), pricePerRound(0), quantity(0), isRare(false) {}
    Ammo(const std::string& n, const std::string& c, AmmoType t, int d, int p, double adp, double bdr, int ppr, bool rare = false)
        : name(n), caliber(c), type(t), damage(d), penetration(p),
          armorDamagePercent(adp), bluntDamageRatio(bdr), pricePerRound(ppr), quantity(0), isRare(rare) {}
    bool canPenetrate(ArmorClass ac) const {
        if (ac == ArmorClass::NONE) return true;
        int lv = (int)ac; int ep = penetration - lv * 10;
        if (ep >= 15) return true; if (ep <= 0) return false;
        return randDouble(0, 1) < (double)ep / 15.0;
    }
    double damageReduction(ArmorClass ac) const {
        if (ac == ArmorClass::NONE) return 0.0;
        int lv = (int)ac; int ep = penetration - lv * 10;
        if (ep >= 13) return 0.0; if (ep <= 0) return 1.0;
        return std::max(0.0, std::min(1.0, 1.0 - (double)ep / 13.0));
    }
};

struct Weapon {
    std::string name, category, caliber;
    int magazineSize, currentAmmo, fireRate;
    double ergonomics; int price;
    std::vector<Ammo> compatibleAmmo;
    Weapon() : magazineSize(0), currentAmmo(0), fireRate(600), ergonomics(50.0), price(0) {}
    void reload() { currentAmmo = magazineSize; }
};

int getShotsPerFire(const Weapon& w) { return w.fireRate >= BURST_RATE_THRESHOLD ? 2 : 1; }
bool needsBoltCycle(const Weapon& w) { return w.fireRate < BOLT_CYCLE_RATE_THRESHOLD; }
bool canExtraFire(const Weapon& w) { return w.fireRate >= EXTRA_FIRE_RATE_THRESHOLD; }

struct LootItem {
    ItemCategory category; std::string name; int value, quantity;
    Weapon weaponData; Ammo ammoData; BodyArmor armorData; MedicalItem medData;
    LootItem() : category(ItemCategory::VALUABLE), value(0), quantity(1) {}
};

struct Container {
    std::string name; ContainerType type; bool searched; int dangerLevel; bool locked; std::string requiredKey;
    Container() : type(ContainerType::JACKET), searched(false), dangerLevel(10), locked(false) {}
};

WORD ammoPenColor(int pen) {
    if (pen >= 60) return FOREGROUND_RED | FOREGROUND_INTENSITY;
    if (pen >= 50) return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    if (pen >= 40) return FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
    if (pen >= 30) return FOREGROUND_BLUE | FOREGROUND_INTENSITY;
    if (pen >= 20) return FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    return FOREGROUND_INTENSITY;
}
WORD armorClassColor(ArmorClass ac) {
    switch (ac) {
        case ArmorClass::CLASS_6: return FOREGROUND_RED | FOREGROUND_INTENSITY;
        case ArmorClass::CLASS_5: return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
        case ArmorClass::CLASS_4: return FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
        case ArmorClass::CLASS_3: return FOREGROUND_BLUE | FOREGROUND_INTENSITY;
        case ArmorClass::CLASS_2: return FOREGROUND_GREEN | FOREGROUND_INTENSITY;
        default: return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
    }
}
void printColoredText(const std::string& text, WORD color) {
    HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
    if (h == INVALID_HANDLE_VALUE || h == NULL) { std::cout << text; return; }
    CONSOLE_SCREEN_BUFFER_INFO info;
    WORD old = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
    if (GetConsoleScreenBufferInfo(h, &info)) old = info.wAttributes;
    std::cout.flush(); SetConsoleTextAttribute(h, color);
    std::cout << text << std::flush; SetConsoleTextAttribute(h, old);
}
void printAmmoNameColored(const std::string& name, int pen) { printColoredText(name, ammoPenColor(pen)); }
void printArmorNameColored(const std::string& name, ArmorClass ac) { printColoredText(name, armorClassColor(ac)); }

WORD lootValueColor(int tv) {
    if (tv >= 400000) return FOREGROUND_RED | FOREGROUND_INTENSITY;
    if (tv >= 150000) return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    if (tv >= 60000) return FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
    if (tv >= 25000) return FOREGROUND_BLUE | FOREGROUND_INTENSITY;
    if (tv >= 8000) return FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
}

void printLootNameColored(const LootItem& it) {
    if (it.category == ItemCategory::AMMO && !it.ammoData.name.empty()) {
        printAmmoNameColored(it.ammoData.name, it.ammoData.penetration); std::cout << " 弹药"; return;
    }
    if (it.category == ItemCategory::ARMOR) { printArmorNameColored(it.name, it.armorData.armorClass); return; }
    printColoredText(it.name, lootValueColor(it.value * std::max(1, it.quantity)));
}

std::string armorClassName(ArmorClass ac) {
    switch (ac) {
        case ArmorClass::NONE: return "无";
        case ArmorClass::CLASS_2: return "2级";
        case ArmorClass::CLASS_3: return "3级";
        case ArmorClass::CLASS_4: return "4级";
        case ArmorClass::CLASS_5: return "5级";
        case ArmorClass::CLASS_6: return "6级";
    }
    return "未知";
}
std::string bodyPartName(BodyPart bp) {
    switch (bp) {
        case BodyPart::HEAD: return "头部";
        case BodyPart::CHEST: return "胸部";
        case BodyPart::STOMACH: return "腹部";
        case BodyPart::LEFT_ARM: return "左臂";
        case BodyPart::RIGHT_ARM: return "右臂";
        case BodyPart::LEFT_LEG: return "左腿";
        case BodyPart::RIGHT_LEG: return "右腿";
        default: return "未知";
    }
}
std::string ammoTypeName(AmmoType at) {
    switch (at) {
        case AmmoType::FMJ: return "全金属被甲弹";
        case AmmoType::AP: return "穿甲弹";
        case AmmoType::HP: return "空尖弹";
        case AmmoType::DUMDUM: return "达姆弹";
        case AmmoType::TRACER: return "曳光弹";
    }
    return "未知";
}
std::string buffTypeName(BuffType bt) {
    switch (bt) {
        case BuffType::PAIN: return "疼痛";
        case BuffType::BLEEDING: return "流血";
        case BuffType::HEAVY_BLEEDING: return "重度流血";
        case BuffType::FRACTURE: return "骨折";
        case BuffType::STOMACH_INJURY: return "腹部受伤";
        case BuffType::ADRENALINE: return "肾上腺素";
        case BuffType::PAINKILLER: return "止痛药效";
        case BuffType::WEAK: return "虚弱";
        case BuffType::STUN: return "眩晕";
    }
    return "未知";
}
std::string buffDurationStr(int dur) { if (dur >= PERMA_BUFF_VALUE) return "永久"; return std::to_string(dur); }
std::string containerTypeName(ContainerType ct) {
    switch (ct) {
        case ContainerType::WEAPON_BOX: return "武器箱";
        case ContainerType::AMMO_CRATE: return "弹药箱";
        case ContainerType::MEDICAL_CABINET: return "医疗柜";
        case ContainerType::SAFE: return "保险箱";
        case ContainerType::CORPSE: return "尸体";
        case ContainerType::JACKET: return "夹克";
        case ContainerType::TOOLBOX: return "工具箱";
        case ContainerType::FILING_CABINET: return "档案柜";
        case ContainerType::BOSS_ZONE: return "Boss占领区";
    }
    return "未知";
}
std::string roleName(Role r) {
    switch (r) {
        case Role::KATYUSHA: return "喀秋莎";
        case Role::TOKAREV: return "托卡列夫";
        case Role::ANNA: return "安娜";
        case Role::ALEXANDER: return "亚历山大";
        case Role::IVAN: return "伊万";
        case Role::ENEMY: return "敌方雇佣兵";
    }
    return "未知";
}
bool isPlayerRole(Role r) { return r == Role::KATYUSHA || r == Role::TOKAREV || r == Role::ANNA || r == Role::ALEXANDER || r == Role::IVAN; }
bool isShotgun(const std::string& c) { return c == "霰弹枪" || c == "泵动霰弹枪"; }

// ==================== 语音库 ====================
std::vector<std::string> katyushaDeathLines = {"妈妈……对不起……我没能回去……","弟弟……姐姐……真的走不动了……","好冷……西伯利亚的雪……在等我……","这次……真的……撑不住了……","我不想死……我不想……","妈妈……我尽力了……"};
std::vector<std::string> tokarevDeathLines = {"老骨头……终于还是散架了……","打过更硬的仗……这次……栽了……","回去……替我喝一杯……","告诉我的家人……我尽力了……","这地儿……埋过太多人……今天轮到我了……","呵……算你小子……走运……"};
std::vector<std::string> annaDeathLines = {"不……我还没找到他们……","妈妈……我回不去了……","队长……抱歉……我尽力了……","好黑……好冷……队友们……","还没……还没结束……","对不起……我……先走一步……"};
std::vector<std::string> alexanderDeathLines = {"冲……冲上去……别管我……","任务……还没完……","掩护……快走……","妈的……就这么……结束了……","再来……再来一次……","答应过你的……对不起……"};
std::vector<std::string> ivanDeathLines = {"Твою мать... 就这样吧……","算了……不值……","回家……我想回家了……","这破地方……就是我的终点……","сука……最后一口烟都没抽上……","替我……喝一杯伏特加……"};
std::vector<std::string> enemyDeathLines = {"За Родину...","сука...","不……不是今天……","告诉中尉……我完了……","妈……妈妈……","咳咳……好冷……","这笔账……记着了……","你们……赢不了……"};

std::vector<std::string> katyushaBleedingLines = {"血……血止不住……绷带……","嘶——流这么多……不行……","不能失血过多……不能……","手在抖……血一直在流……","该死的……绷带在哪……"};
std::vector<std::string> katyushaFractureLines = {"啊!我的腿!骨头……断了……","站不起来……腿完全使不上力……","嘶——每走一步都像刀割……","完了……走不动了……怎么办……","撑住……咬着牙也要撑住……"};
std::vector<std::string> katyushaStomachLines = {"呃……肚子……内脏……","痛得直不起腰……不能跑……","唔……血从指缝里渗出来……","不行……每呼吸一下都疼……","不能倒……不能倒在这里……"};
std::vector<std::string> katyushaPainLines = {"疼……好疼……眼前发黑……","咬紧牙关……忍住……忍住……","手在抖……扣扳机都困难……","疼得喘不过气……但还能动……","嘶……这算不了什么……算不了……"};
std::vector<std::string> tokarevBleedingLines = {"又流血了……老伤……又开了……","啧……流点血……不碍事……","这血……洗洗身上的晦气……","妈的……止不住……","再撑一会儿……就一会儿……"};
std::vector<std::string> tokarevFractureLines = {"老骨头又断了一根……习惯了……","腿废了……这下麻烦了……","哼……还能爬……","妈的……每走一步都疼得要命……","老伙计……你还能撑多久……"};
std::vector<std::string> tokarevStomachLines = {"肚子……被穿了……这下够呛……","唔……老毛病了……","血……从嘴里涌上来了……","不行……得找个掩护……","这一下……够狠……"};
std::vector<std::string> tokarevPainLines = {"疼得够呛……哼……小意思……","老子扛得住……还能打……","这点疼……阿富汗那会儿……","嘶……手有点抖……","没事……老兵不死……"};
std::vector<std::string> annaBleedingLines = {"唔!血止不住……绷带……快……","不行……血太多了……头晕……","撑住……要撑住……队友还在等我……","嘶……有点麻烦……得快点处理……","别慌……冷静……包扎……"};
std::vector<std::string> annaFractureLines = {"啊!我的腿……骨头……好痛……","站不起来了……完了……","唔!每动一下都钻心疼……","腿断了……但手还能扣扳机……","不能倒……不能……"};
std::vector<std::string> annaStomachLines = {"唔!肚子……不行……好痛……","撑不住……要倒了……","唔……又中弹……这次是腹部……","呼吸……呼吸都疼……","不能停下……不能……"};
std::vector<std::string> annaPainLines = {"唔……好痛……忍住……忍住……","嘶……好疼……但没用……","不行……要撑住……","眼前发黑……但不能停下……","疼……没关系……还能动……"};
std::vector<std::string> alexanderBleedingLines = {"血……我自己按着……继续……","不要紧……撑得住……","这点血……不算什么……","别管我……继续……","小伤……处理一下就好……"};
std::vector<std::string> alexanderFractureLines = {"唔!腿……断了……","哼……我还能跑……","撑着……还没到停下的时候……","能走……继续……","没事……小问题……"};
std::vector<std::string> alexanderStomachLines = {"腹部……唔……","撑住……要稳住……","这一下够狠……但没事……","不行……得继续……","别管我……继续推进……"};
std::vector<std::string> alexanderPainLines = {"疼……忍着……","小意思……","别管我……还能打……","没事……继续……","这点疼……不算什么……"};
std::vector<std::string> ivanBleedingLines = {"Кровь... 血止不住……妈的……","啧……流这么多……","还行……还能撑……","再撑一会儿……就一会儿……","Сука……绷带……"};
std::vector<std::string> ivanFractureLines = {"Сука!腿……断了……","还跑得动……妈的……","这下麻烦了……","老骨头……你还能撑多久……","Чёрт……每走一步都疼……"};
std::vector<std::string> ivanStomachLines = {"肚子……中了……这下麻烦了……","呃……血从嘴里……","撑住……不能倒在这……","Живот... 麻烦……","这一下……够狠……"};
std::vector<std::string> ivanPainLines = {"疼……Блин……","忍着……还能打……","这点疼……不算什么……","Сука……手在抖……","还能撑……还没死呢……"};

std::vector<std::string> katyushaDamageLines = {"呃!","嘶——","好痛……","唔!","该死的……","疼……没事,继续。"};
std::vector<std::string> tokarevDamageLines = {"啧!","哼……还行……","小意思……","妈的……","这点伤……","来啊!"};
std::vector<std::string> annaDamageLines = {"唔!","嘶——","没事……","撑得住……","别停下……","唔……好痛……"};
std::vector<std::string> alexanderDamageLines = {"唔!","哼!","小伤……","还行……","没事……","继续……"};
std::vector<std::string> ivanDamageLines = {"Сука!","呃!","还行……","妈的……","这点伤……","Давай!"};

std::vector<std::string> katyushaHeavyDamageLines = {"呃啊——!好痛……","唔!肋骨……断了吗……","嘶——眼前发黑……","不……不能倒……","咳……咳……撑住……","妈的……好疼……"};
std::vector<std::string> tokarevHeavyDamageLines = {"咳——!老骨头……","这一下够劲……","妈的……差点见上帝……","唔……还行……还行……","老伙计……还撑得住……","哼……小意思……"};
std::vector<std::string> annaHeavyDamageLines = {"啊——!唔……好痛……","不行……要撑住……","咳……咳……队友还在等我……","唔……眼前一片白……","不能倒……不能倒在这里……","嘶——这一下……够狠……"};
std::vector<std::string> alexanderHeavyDamageLines = {"咳——!有点重……","唔……没到极限……","撑住……还能打……","这一下……还行……","小伤……继续……","别管我……还能动……"};
std::vector<std::string> ivanHeavyDamageLines = {"Чёрт!这一下够重……","咳——!妈的……","老骨头……还撑得住……","Сука……差点……","还行……还没死……","再来!Давай!"};

std::vector<std::string> katyushaCriticalDamageLines = {"啊——!!不要……不要在这里……","咳——!血……吐血了……","妈妈……我不想死……","撑住……撑住……求你了……","眼前……全黑了……"};
std::vector<std::string> tokarevCriticalDamageLines = {"咳——!老骨头……这次真散了……","妈的……差点……","唔……血……止不住……","老兵……还能打……","死不了……死不了……"};
std::vector<std::string> annaCriticalDamageLines = {"啊——!不行……我不能死在这里……","咳……队友们……对不起……","眼前……好黑……","撑住……求你了……撑住……","妈妈……我……"};
std::vector<std::string> alexanderCriticalDamageLines = {"咳——!还没……还没到终点……","唔……撑住……","不能倒……任务还没完……","这点伤……还行……","再来……再来一次……"};
std::vector<std::string> ivanCriticalDamageLines = {"Твою мать!还没死……还没死……","咳——!血……","Сука……站住……","最后一把……最后一把……","还没完……还没……"};

std::vector<std::string> katyushaArmorBrokenLines = {"防弹衣……撑不住了!甲碎了!","护甲废了……只能靠这身肉了……","嘶——甲板裂了……我得小心点……"};
std::vector<std::string> tokarevArmorBrokenLines = {"护甲碎了……真麻烦……","呵,甲板没了……只能靠这身老肉了……","这下得小心点了……"};
std::vector<std::string> annaArmorBrokenLines = {"护甲失效了……只能硬撑了……","唔……甲碎了……麻烦了……","没护甲了……得更加小心……"};
std::vector<std::string> alexanderArmorBrokenLines = {"护甲完了……无所谓,继续冲……","甲没了……但人还在……","没事……还能打……"};
std::vector<std::string> ivanArmorBrokenLines = {"甲碎了……Чёрт. 只能硬扛了……","这下麻烦了……没甲了……","Сука……只能靠肉了……"};

std::vector<std::string> katyushaLowHpLines = {"不能……不能倒下……妈妈还在等我……","母亲……弟弟……我还没回去……","咳……还、还能撑一会儿……","只要还有一口气……我就得往前走……","不能死在这里……不能……"};
std::vector<std::string> tokarevLowHpLines = {"老骨头……这回可能真要散架了……","呵……死就死吧……","还没……还没打完……","老兵不死……只是有点累……","再来……再来一场……"};
std::vector<std::string> annaLowHpLines = {"不行……我不能死在这里……队友们……","队友们……我还没找到你们……","还差一点……还差一点……","撑住……求你了……撑住……","不能倒……不能倒在这里……"};
std::vector<std::string> alexanderLowHpLines = {"还没完……我还能打……","答应过她的……不能食言……","再来……再来一次……","撑住……还没到终点……","这点伤……不算什么……"};
std::vector<std::string> ivanLowHpLines = {"还没死呢……Сука……","不能倒在这……不能……","再撑一下……就一下……","最后一把……最后一把……","Твою мать……还没完……"};

std::vector<std::string> katyushaEncounterLines = {"有人!……别紧张……别紧张……","发现敌人……稳住……稳住……","来吧……我不会倒下的……","被发现了……那就打吧!"};
std::vector<std::string> tokarevEncounterLines = {"来了……送上门的……","呵,终于有点意思了……","来吧,让老子看看你们几斤几两……","又一场……老规矩……"};
std::vector<std::string> annaEncounterLines = {"敌人!……准备战斗……","被发现了……冷静……冷静……","来吧……我没时间浪费……","必须活着……必须……"};
std::vector<std::string> alexanderEncounterLines = {"接敌!准备战斗……","看到敌人……开打……","来了……按计划行事……","保持冷静……继续……"};
std::vector<std::string> ivanEncounterLines = {"Хорошо. 正合我意……","来了……Let's dance……","终于有点意思了……","Сука……开打!"};

std::vector<std::string> katyushaKillLines = {"呼……对不起……","站着的……只能是我……","对不起……但我必须活下去……","下一个……"};
std::vector<std::string> tokarevKillLines = {"下一个……","老兵还没老……","躺好……别动了……","再来……"};
std::vector<std::string> annaKillLines = {"抱歉……","必须活着……必须……","下一个……","继续……队友还在等我……"};
std::vector<std::string> alexanderKillLines = {"清除……","目标消灭……","下一个……","干净……继续……"};
std::vector<std::string> ivanKillLines = {"Done. 下一个……","躺好……别动了……","Сдох. 终于……","继续……还没完……"};

std::vector<std::string> katyushaBossEncounterLines = {"这家伙……不好对付……","必须赢……不能死在这……","稳住……稳住……","来吧……我不会退缩的……"};
std::vector<std::string> tokarevBossEncounterLines = {"硬骨头……这可得费点劲……","呵,终于来个像样的了……","来吧……让老子看看你有多硬……","老骨头……又要忙了……"};
std::vector<std::string> annaBossEncounterLines = {"是那个……必须干掉他……","为了队友……必须赢……","来吧……我不会退缩……","冷静……冷静……"};
std::vector<std::string> alexanderBossEncounterLines = {"硬骨头……看来得费点劲……","没关系……按计划来……","来了……准备……","保持冷静……继续……"};
std::vector<std::string> ivanBossEncounterLines = {"大块头……这个有点意思……","Хорошо. 终于来个像样的……","干他……","Сука……来吧!"};

std::vector<std::string> katyushaBossKillLines = {"赢了……真的……赢了……","母亲……我做到了……","呼……活下来了……","终于……结束了……"};
std::vector<std::string> tokarevBossKillLines = {"老兵不死……","呼……干掉了……","呵……还算可以……","回去喝一杯……"};
std::vector<std::string> annaBossKillLines = {"结束了……终于……","接下来……找队友……","呼……活下来了……","还没完……还没……"};
std::vector<std::string> alexanderBossKillLines = {"任务完成……","还不错……","呼……继续……","干净利落……"};
std::vector<std::string> ivanBossKillLines = {"呼……搞定……","Сдох. 终于……","回去喝一杯……","Давай. 下一个……"};

std::vector<std::string> katyushaMissLines = {"打偏了……手抖了……","冷静……冷静……","再来……","该死……"};
std::vector<std::string> tokarevMissLines = {"偏了……手生了……","操……","再来……","哼……小失误……"};
std::vector<std::string> annaMissLines = {"偏了!……冷静……","手抖……不行……","再来……","唔……"};
std::vector<std::string> alexanderMissLines = {"打偏……修正……","……","再来……","冷静……"};
std::vector<std::string> ivanMissLines = {"偏了……手滑……","Блин……","再来……","Сука……"};

std::vector<std::string> katyushaReloadLines = {"换弹……快……","咔哒……好了……","再来……"};
std::vector<std::string> tokarevReloadLines = {"换……上膛……","好了……继续……","咔哒……"};
std::vector<std::string> annaReloadLines = {"换弹……快……","好了……继续……","快……快……"};
std::vector<std::string> alexanderReloadLines = {"换弹……上膛……","好了……","继续……"};
std::vector<std::string> ivanReloadLines = {"换弹……上膛……","Чёрт……好了……","继续……"};

std::vector<std::string> katyushaEmptyMagLines = {"空了!换弹匣!","没子弹了……快……","该死……换弹!"};
std::vector<std::string> tokarevEmptyMagLines = {"空了!换弹!","该死……","快……快……"};
std::vector<std::string> annaEmptyMagLines = {"空!换弹!","该死的……","快……"};
std::vector<std::string> alexanderEmptyMagLines = {"空……换弹……","快……","继续……"};
std::vector<std::string> ivanEmptyMagLines = {"空!换弹!","Чёрт……","快……"};

std::vector<std::string> katyushaItemLines = {"好多了……继续……","撑住……还能打……","呼……好一点了……"};
std::vector<std::string> tokarevItemLines = {"舒服点……","老办法……管用……","还行……继续……"};
std::vector<std::string> annaItemLines = {"好一点了……还得继续……","撑住……","呼……"};
std::vector<std::string> alexanderItemLines = {"处理完……继续……","好了……","继续……"};
std::vector<std::string> ivanItemLines = {"好多了……继续……","还行……","Сука……好一点了……"};

std::vector<std::string> katyushaArmorRepairLines = {"甲还能修……再撑一阵……","好了……继续……"};
std::vector<std::string> tokarevArmorRepairLines = {"甲还能用……","好了……"};
std::vector<std::string> annaArmorRepairLines = {"甲还能撑……","好了……"};
std::vector<std::string> alexanderArmorRepairLines = {"修复……","好了……"};
std::vector<std::string> ivanArmorRepairLines = {"甲还能用……","好了……"};

struct Character;
void printBodyFigure(const Character& ch);

struct Character {
    std::string name; Role role;
    int maxHealth, health; double speed, armorToughness, dodgeChance, debuffBonus;
    int money; bool hasRevived, isBoss; double bossDamageMult; bool immuneToDebuffs;
    std::string skillName, skillDesc;
    bool flashbangUsed; int stunnedTurns, lootCapacity, debt, safeDeposit, headsetLevel;
    bool lowHealthVoiceTriggered;
    int boltCooldown;
    int stunGrenades;
    int medicalDelay;
    bool interferenceTriggered;
    int skillCooldown;
    bool annaBeaconAvailable;
    bool annaBeaconActive;

    Helmet helmet;
    std::vector<Stimulator> stimulators;
    int stimHealPerTurn = 0; int stimHealTurns = 0;
    double stimSpeedBonus = 1.0; int stimSpeedTurns = 0;
    double stimDamageBonus = 1.0; int stimDamageTurns = 0;
    double stimDamageResist = 1.0; int stimDamageResistTurns = 0;

    std::map<BodyPart, int> partHealth, partMaxHealth;
    std::map<BuffType, int> buffs;
    std::map<std::string, int> keys;
    BodyArmor armor;
    std::vector<MedicalItem> inventory;
    std::vector<LootItem> lootBag;
    Weapon weapon;

    Character() : maxHealth(100), health(100), speed(1.0), armorToughness(1.0), dodgeChance(0.0),
                  debuffBonus(0.0), money(0), hasRevived(false), isBoss(false), bossDamageMult(1.0),
                  immuneToDebuffs(false), flashbangUsed(false), stunnedTurns(0), lootCapacity(DEFAULT_LOOT_CAPACITY),
                  debt(0), safeDeposit(0), headsetLevel(0), lowHealthVoiceTriggered(false), boltCooldown(0),
                  stunGrenades(0), medicalDelay(0), interferenceTriggered(false), skillCooldown(0),
                  annaBeaconAvailable(false), annaBeaconActive(false) {}

    void initPartHealth() {
        partMaxHealth[BodyPart::HEAD] = 35;
        partMaxHealth[BodyPart::CHEST] = 85;
        partMaxHealth[BodyPart::STOMACH] = 70;
        partMaxHealth[BodyPart::LEFT_ARM] = 60;
        partMaxHealth[BodyPart::RIGHT_ARM] = 60;
        partMaxHealth[BodyPart::LEFT_LEG] = 65;
        partMaxHealth[BodyPart::RIGHT_LEG] = 65;
        for (auto& kv : partMaxHealth) partHealth[kv.first] = kv.second;
    }
    bool isAlive() const {
        if (health <= 0) return false;
        auto h = partHealth.find(BodyPart::HEAD), c = partHealth.find(BodyPart::CHEST);
        if (h == partHealth.end() || c == partHealth.end()) return false;
        return h->second > 0 && c->second > 0;
    }
    void applyDamageToPart(BodyPart bp, int damage) {
        if (partHealth.find(bp) == partHealth.end()) return;
        partHealth[bp] = std::max(0, partHealth[bp] - damage);
        health = std::max(0, health - damage);
    }
    bool isAnnaDesperate() const { return role == Role::ANNA && health < maxHealth * ANNA_HP_THRESHOLD; }
    bool isAlexanderDesperate() const { return role == Role::ALEXANDER && health < maxHealth * ALEXANDER_HP_THRESHOLD; }
    bool isIvanDeathRush() const { return role == Role::IVAN && health < maxHealth * IVAN_DEATH_RUSH_HP; }
    bool canUseFlashbang() const { return role == Role::ALEXANDER && health < maxHealth * ALEXANDER_HP_THRESHOLD && !flashbangUsed; }
    bool isSuppressedByTokarev() const { for (auto& kv : buffs) if (kv.second >= PERMA_BUFF_VALUE) return true; return false; }
    bool hasKey(const std::string& name) const { auto it = keys.find(name); return it != keys.end() && it->second > 0; }
    void consumeKey(const std::string& name) { auto it = keys.find(name); if (it != keys.end() && it->second > 0) it->second--; }
    int getHeadsetWarningChance() const {
        int base = 0;
        if (headsetLevel >= 2) base = 15;
        else if (headsetLevel == 1) base = 10;
        if (base > 0 && role == Role::ALEXANDER) base += ALEXANDER_HEADSET_BONUS;
        return base;
    }
    std::string getHeadsetName() const { if (headsetLevel >= 2) return "高级战术耳机"; if (headsetLevel == 1) return "战术耳机"; return "未装备"; }

    void addBuff(BuffType bt, int turns) {
        if (turns <= 0 || immuneToDebuffs || isAlexanderDesperate()) return;
        if (isAnnaDesperate() && !hasRevived) return;
        auto it = buffs.find(bt);
        if (it == buffs.end()) buffs.insert(std::make_pair(bt, turns));
        else if (turns > it->second) it->second = turns;
    }
    bool hasBuff(BuffType bt) const { auto it = buffs.find(bt); return it != buffs.end() && it->second > 0; }
    bool hasPainPenalty() const { return hasBuff(BuffType::PAIN) && !hasBuff(BuffType::PAINKILLER); }
    double getEffectiveSpeed() const {
        double m = 1.0;
        if (hasBuff(BuffType::FRACTURE)) m = std::min(m, FRACTURE_SPEED_MULT);
        if (hasBuff(BuffType::STOMACH_INJURY)) m = std::min(m, STOMACH_SPEED_MULT);
        double base = speed * m;
        if (role == Role::IVAN && health < maxHealth * IVAN_DEATH_RUSH_HP) base *= IVAN_DEATH_RUSH_SPEED;
        base *= stimSpeedBonus;
        return base;
    }
    double getEffectiveDodge() const { double d = dodgeChance; if (hasPainPenalty()) d *= PAIN_DODGE_PENALTY; return d; }

    void processBuffs() {
        if (hasBuff(BuffType::BLEEDING)) { health = std::max(0, health - 3); if (buffs[BuffType::BLEEDING] < PERMA_BUFF_VALUE) buffs[BuffType::BLEEDING]--; }
        if (hasBuff(BuffType::HEAVY_BLEEDING)) { health = std::max(0, health - 6); if (buffs[BuffType::HEAVY_BLEEDING] < PERMA_BUFF_VALUE) buffs[BuffType::HEAVY_BLEEDING]--; }
        if (hasBuff(BuffType::PAIN) && buffs[BuffType::PAIN] < PERMA_BUFF_VALUE) buffs[BuffType::PAIN]--;
        if (hasBuff(BuffType::FRACTURE) && buffs[BuffType::FRACTURE] < PERMA_BUFF_VALUE) buffs[BuffType::FRACTURE]--;
        if (hasBuff(BuffType::STOMACH_INJURY) && buffs[BuffType::STOMACH_INJURY] < PERMA_BUFF_VALUE) buffs[BuffType::STOMACH_INJURY]--;
        if (hasBuff(BuffType::PAINKILLER)) buffs[BuffType::PAINKILLER]--;
        if (hasBuff(BuffType::WEAK)) buffs[BuffType::WEAK]--;
        if (hasBuff(BuffType::STUN)) buffs[BuffType::STUN]--;
        for (auto it = buffs.begin(); it != buffs.end();) { if (it->second <= 0) it = buffs.erase(it); else ++it; }
        if (skillCooldown > 0) skillCooldown--;
        if (medicalDelay > 0) medicalDelay--;
        if (stimHealTurns > 0) { health = std::min(maxHealth, health + stimHealPerTurn); stimHealTurns--; if (stimHealTurns == 0) stimHealPerTurn = 0; }
        if (stimSpeedTurns > 0) { stimSpeedTurns--; if (stimSpeedTurns == 0) stimSpeedBonus = 1.0; }
        if (stimDamageTurns > 0) { stimDamageTurns--; if (stimDamageTurns == 0) stimDamageBonus = 1.0; }
        if (stimDamageResistTurns > 0) { stimDamageResistTurns--; if (stimDamageResistTurns == 0) stimDamageResist = 1.0; }
    }

    void useMedicalItem(int index, int targetPart = -1) {
        if (!isAlive() || index < 0 || index >= (int)inventory.size()) return;
        if (inventory[index].uses <= 0) { inventory.erase(inventory.begin() + index); return; }
        MedicalItem item = inventory[index]; bool consumed = false;
        if (item.armorRepairAmount > 0) {
            if (!armor.owned) { std::cout << "  >>> 你身上没有护甲。" << std::endl; return; }
            if (armor.currentDurability >= armor.maxDurability) { std::cout << "  >>> 护甲耐久已满。" << std::endl; return; }
            int oldDur = armor.currentDurability;
            armor.currentDurability = std::min(armor.maxDurability, armor.currentDurability + item.armorRepairAmount);
            std::cout << "  >>> 使用「" << item.name << "」修理护甲:" << oldDur << " → " << armor.currentDurability << "/" << armor.maxDurability << std::endl;
            
            item.uses--; consumed = (item.uses <= 0);
            if (consumed) inventory.erase(inventory.begin() + index); else inventory[index].uses = item.uses;
            return;
        }
        if (hasBuff(BuffType::FRACTURE) && item.blockedByFracture && role != Role::KATYUSHA) {
            std::cout << "  >>> 骨折期间无法使用「" << item.name << "」。" << std::endl; return;
        }
        if (hasBuff(BuffType::FRACTURE) && item.blockedByFracture && role == Role::KATYUSHA) {
            std::cout << "  >>> 【重伤迟滞】喀秋莎无视骨折剧痛。" << std::endl;
        }
        if (item.healAmount > 0) {
            int healAmt = item.healAmount;
            if (role == Role::ANNA && annaBeaconActive) healAmt = (int)(healAmt * ANNA_MEDICAL_HEAL_MULT);
            health = std::min(maxHealth, health + healAmt);
            BodyPart targetBp;
            if (targetPart >= 0 && targetPart < (int)BodyPart::COUNT) targetBp = (BodyPart)targetPart;
            else {
                int lh = 9999; targetBp = BodyPart::CHEST;
                for (auto& kv : partHealth) if (kv.second < lh) { lh = kv.second; targetBp = kv.first; }
            }
            if (partHealth.find(targetBp) != partHealth.end() && partMaxHealth.find(targetBp) != partMaxHealth.end()) {
                partHealth[targetBp] = std::min(partMaxHealth[targetBp], partHealth[targetBp] + healAmt / 2);
                std::cout << "  >>> 药品主要作用于【" << bodyPartName(targetBp) << "】。" << std::endl;
            }
        }
        if (item.stopsHeavyBleeding) { buffs.erase(BuffType::BLEEDING); buffs.erase(BuffType::HEAVY_BLEEDING); }
        else if (item.stopsBleeding) { buffs.erase(BuffType::BLEEDING); }
        if (item.fixesFracture) buffs.erase(BuffType::FRACTURE);
        if (item.fixesStomachInjury) buffs.erase(BuffType::STOMACH_INJURY);
        if (item.givesPainkiller) buffs[BuffType::PAINKILLER] = 5;
        if (item.name == "外科手术包" && role == Role::KATYUSHA && dodgeChance < KATYUSHA_DODGE_MAX) {
            double old = dodgeChance;
            dodgeChance = std::min(KATYUSHA_DODGE_MAX, dodgeChance + KATYUSHA_DODGE_SURGERY_GAIN);
            std::cout << "  >>> " << name << " 闪避 " << (int)(old * 100) << "% → " << (int)(dodgeChance * 100) << "%" << std::endl;
        }
        item.uses--; consumed = (item.uses <= 0);
        if (consumed) inventory.erase(inventory.begin() + index); else inventory[index].uses = item.uses;
    }

    void useStimulator(int index) {
        if (index < 0 || index >= (int)stimulators.size()) return;
        Stimulator s = stimulators[index];
        if (s.instantHeal > 0) health = std::min(maxHealth, health + s.instantHeal);
        if (s.stopAllBleeding) { buffs.erase(BuffType::BLEEDING); buffs.erase(BuffType::HEAVY_BLEEDING); }
        if (s.fixFracture) buffs.erase(BuffType::FRACTURE);
        if (s.removePain) { buffs.erase(BuffType::PAIN); buffs[BuffType::PAINKILLER] = 10; }
        if (s.healPerTurn > 0) { stimHealPerTurn = s.healPerTurn; stimHealTurns = s.healPerTurnTurns; }
        if (s.speedBonus > 0) { stimSpeedBonus = 1.0 + s.speedBonus; stimSpeedTurns = s.speedBonusTurns; }
        if (s.damageBonus > 0) { stimDamageBonus = 1.0 + s.damageBonus; stimDamageTurns = s.damageBonusTurns; }
        if (s.damageResist > 0) { stimDamageResist = 1.0 - s.damageResist; stimDamageResistTurns = s.damageResistTurns; }
        stimulators.erase(stimulators.begin() + index);
    }

    int calculateTreatmentCost() const {
        int cost = 0;
        int lost = maxHealth - health; if (lost > 0) cost += lost * 20;
        for (auto& kv : partHealth) { auto mt = partMaxHealth.find(kv.first); if (mt != partMaxHealth.end()) { int l = mt->second - kv.second; if (l > 0) cost += l * 10; } }
        for (auto& kv : buffs) {
            if (kv.second <= 0) continue;
            switch (kv.first) {
                case BuffType::BLEEDING: cost += 800; break;
                case BuffType::HEAVY_BLEEDING: cost += 1500; break;
                case BuffType::FRACTURE: cost += 1200; break;
                case BuffType::STOMACH_INJURY: cost += 1000; break;
                case BuffType::PAIN: cost += 400; break;
                case BuffType::WEAK: cost += 1500; break;
                default: break;
            }
        }
        if (role == Role::ANNA && hasRevived) { cost += 2000; if (maxHealth < 100) cost += (100 - maxHealth) * 100; }
        if (cost > HELEN_TREATMENT_CAP) cost = HELEN_TREATMENT_CAP;
        if (cost < 0) cost = 0; return cost;
    }
    bool hasAnyInjury() const {
        if (health < maxHealth) return true;
        for (auto& kv : partHealth) { auto mt = partMaxHealth.find(kv.first); if (mt != partMaxHealth.end() && kv.second < mt->second) return true; }
        for (auto& kv : buffs) { if (kv.second <= 0) continue; if (kv.first == BuffType::PAINKILLER || kv.first == BuffType::ADRENALINE) continue; return true; }
        if (role == Role::ANNA && hasRevived) return true;
        return false;
    }

    void printStatus() const {
        std::cout << "\n===== " << name << " 状态 =====" << std::endl;
        std::cout << "角色: " << roleName(role) << std::endl;
        if (role == Role::KATYUSHA) {
            std::cout << "特性1【闪避】: " << (int)(dodgeChance * 100) << "% / 15%" << std::endl;
            std::cout << "特性2【重伤迟滞】: 骨折时仍可使用绷带、急救包" << std::endl;
            std::cout << "技能【电磁干扰】: 敌人首次使用药品需 2 回合" << std::endl;
        } else if (role == Role::ANNA) {
            std::cout << "特性1【绝境翻盘】: 生命 <25% 时免疫 debuff、伤害 +15%、减伤 20%" << std::endl;
            std::cout << "特性2【向死而生】: 自救后伤害额外 +10%" << std::endl;
            std::cout << "技能【救援信标】: 每场战斗 1 次,激活后药品恢复 ×1.1,立即回 " << ANNA_BEACON_INSTANT_HEAL << " 血" << std::endl;
            if (isAnnaDesperate()) std::cout << "  >>> 已进入绝境状态!" << std::endl;
            if (hasRevived) std::cout << "  >>> 【向死而生】已激活,伤害 +10%" << std::endl;
        } else if (role == Role::TOKAREV) {
            std::cout << "特性1【战场老兵】: 敌人负面 buff 概率 +15%" << std::endl;
            std::cout << "特性2【战场压制】: 减益永久保留,敌人无法自愈" << std::endl;
            std::cout << "技能【最后一击】: 战斗中 1 次,对敌护甲 30 点伤害,若敌血量≥15 额外 10 点" << std::endl;
        } else if (role == Role::ALEXANDER) {
            std::cout << "特性1【闪光突击】: 血量 <50% 时自动掷闪光弹" << std::endl;
            std::cout << "特性2【钢铁意志】: 血量 <50% 时免疫 debuff" << std::endl;
            std::cout << "技能【无可奉告】: 耳机预警概率 +5%" << std::endl;
            if (isAlexanderDesperate()) std::cout << "  >>> 已进入突击状态!" << std::endl;
        } else if (role == Role::IVAN) {
            std::cout << "特性1【战斗本能】: 受伤≥15 生成 1 枚震爆弹(最多 2 枚)" << std::endl;
            std::cout << "特性2【火力压制】: 低射速武器 10% 概率射两发,第二发 -15% 伤害" << std::endl;
            std::cout << "特性3【死亡突袭】: 血量 <15% 时速度 +10%" << std::endl;
            if (isIvanDeathRush()) std::cout << "  >>> 【死亡突袭】已激活!" << std::endl;
            std::cout << "震爆弹: " << stunGrenades << "/2" << std::endl;
        }
        std::cout << "生命值: " << health << "/" << maxHealth << std::endl;
        std::cout << "防弹衣: ";
        if (armor.owned) printArmorNameColored(armor.name, armor.effectiveClass());
        else std::cout << "未装备";
        std::cout << " [" << armorClassName(armor.effectiveClass()) << "] 耐久 " << armor.currentDurability << "/" << armor.maxDurability << std::endl;
        std::cout << "头盔: ";
        if (helmet.owned) {
            printArmorNameColored(helmet.name, helmet.effectiveClass());
            std::cout << " [" << armorClassName(helmet.effectiveClass()) << "] 耐久 " << helmet.currentDurability << "/" << helmet.maxDurability
                      << " 跳弹 " << (int)(helmet.ricochetChance * 100) << "%";
        } else std::cout << "未装备";
        std::cout << std::endl;
        if (weapon.magazineSize > 0) {
            std::cout << "武器: " << weapon.name << " [" << weapon.caliber << "] 弹药 " << weapon.currentAmmo << "/" << weapon.magazineSize;
            if (!weapon.compatibleAmmo.empty()) { std::cout << " | 备用 "; printAmmoNameColored(weapon.compatibleAmmo[0].name, weapon.compatibleAmmo[0].penetration); std::cout << " x" << weapon.compatibleAmmo[0].quantity; }
            std::cout << std::endl;
        }
        if (!stimulators.empty()) {
            std::cout << "针剂: ";
            for (auto& s : stimulators) std::cout << s.name << "  ";
            std::cout << std::endl;
        }
        std::cout << "耳机: " << getHeadsetName();
        if (headsetLevel > 0) std::cout << "(遇敌预警 " << getHeadsetWarningChance() << "%)";
        std::cout << std::endl;
        if (!keys.empty()) { std::cout << "钥匙: "; for (auto& kv : keys) if (kv.second > 0) std::cout << kv.first << "x" << kv.second << "  "; std::cout << std::endl; }
        std::cout << "资金: " << money << " 卢布";
        if (safeDeposit > 0) std::cout << "(保险柜 " << safeDeposit << ")";
        std::cout << std::endl;
        if (debt > 0) std::cout << "债务: " << debt << " 卢布" << std::endl;
        std::cout << "背包容量: " << lootBag.size() << " / " << lootCapacity << std::endl;
        printBodyFigure(*this);
        if (!buffs.empty()) {
            std::cout << "状态效果: ";
            for (auto& kv : buffs) if (kv.second > 0) std::cout << buffTypeName(kv.first) << "(" << buffDurationStr(kv.second) << ") ";
            std::cout << std::endl;
        }
    }
};

WORD partHealthColor(const Character& ch, BodyPart bp) {
    if (ch.hasBuff(BuffType::FRACTURE) && (bp == BodyPart::LEFT_LEG || bp == BodyPart::RIGHT_LEG)) return FOREGROUND_INTENSITY;
    auto itH = ch.partHealth.find(bp), itM = ch.partMaxHealth.find(bp);
    if (itH == ch.partHealth.end() || itM == ch.partMaxHealth.end() || itM->second <= 0) return FOREGROUND_INTENSITY;
    int cur = itH->second, max = itM->second; double r = (double)cur / max;
    if (r >= 1.0) return FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    if (r >= BODY_LIGHT_THRESHOLD) return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    if (r >= BODY_HEAVY_THRESHOLD) return FOREGROUND_RED | FOREGROUND_INTENSITY;
    return FOREGROUND_RED;
}
void printPartColored(const Character& ch, BodyPart bp, const std::string& name) {
    auto itH = ch.partHealth.find(bp), itM = ch.partMaxHealth.find(bp);
    int cur = (itH != ch.partHealth.end()) ? itH->second : 0;
    int max = (itM != ch.partMaxHealth.end()) ? itM->second : 0;
    printColoredText("[" + name + " " + std::to_string(cur) + "/" + std::to_string(max) + "]", partHealthColor(ch, bp));
}
void printBodyFigure(const Character& ch) {
    std::cout << "\n          【身体状态】\n";
    std::cout << "             "; printColoredText("O", partHealthColor(ch, BodyPart::HEAD)); std::cout << "\n";
    std::cout << "           "; printColoredText("/", partHealthColor(ch, BodyPart::LEFT_ARM)); std::cout << " "; printColoredText("|", partHealthColor(ch, BodyPart::CHEST)); std::cout << " "; printColoredText("\\", partHealthColor(ch, BodyPart::RIGHT_ARM)); std::cout << "\n";
    std::cout << "             "; printColoredText("|", partHealthColor(ch, BodyPart::STOMACH)); std::cout << "\n";
    std::cout << "            "; printColoredText("/", partHealthColor(ch, BodyPart::LEFT_LEG)); std::cout << " "; printColoredText("\\", partHealthColor(ch, BodyPart::RIGHT_LEG)); std::cout << "\n";
    std::cout << "           "; printPartColored(ch, BodyPart::HEAD, "头"); std::cout << "  "; printPartColored(ch, BodyPart::CHEST, "胸"); std::cout << "  "; printPartColored(ch, BodyPart::STOMACH, "腹"); std::cout << "\n";
    std::cout << "          "; printPartColored(ch, BodyPart::LEFT_ARM, "左臂"); std::cout << " "; printPartColored(ch, BodyPart::RIGHT_ARM, "右臂"); std::cout << " "; printPartColored(ch, BodyPart::LEFT_LEG, "左腿"); std::cout << " "; printPartColored(ch, BodyPart::RIGHT_LEG, "右腿"); std::cout << "\n";
}

void printCredits() {
    std::cout << "\n\n==========================================\n";
    std::cout << "              <<风暴>>                     \n";
    std::cout << "        Russian Hardcore Extraction       \n";
    std::cout << "              —— 开发人员 ——               \n";
    std::cout << "              主编:leo1009                \n";
    std::cout << "              代码编写:AI                 \n";
    std::cout << "              感谢游玩                     \n";
    std::cout << "==========================================\n\n";
    pause();
}

class GameWorld {
public:
    std::vector<Ammo> ammoDatabase;
    std::vector<Weapon> weaponDatabase;
    std::vector<BodyArmor> armorShop, rareArmorList;
    std::vector<Helmet> helmetDatabase;
    std::vector<MedicalItem> medicalShop;
    std::vector<Stimulator> stimulatorShop;
    std::vector<Map> mapDatabase;
    std::vector<Container> mapContainers;
    std::vector<LootItem> warehouse;
    std::vector<Ammo> antonovAmmoStock;
    std::vector<BodyArmor> antonovArmorStock;
    bool antonovStockInitialized;
    struct KeyProduct { std::string name, description; int price; };
    std::vector<KeyProduct> keyShop;
    Character player;
    Map currentMap;
    bool inRaid, bossSpawned;
    std::string savePath;

    GameWorld() : antonovStockInitialized(false), inRaid(false), bossSpawned(false), savePath("storm_save.txt") {
        initMonologuePool();
        initAmmo(); initWeapons(); initArmorShop(); initHelmets(); initMedicalShop(); initStimulators();
        initKeyShop(); initMaps();
    }

    Weapon safeWeaponAt(int idx) { if (idx < 0 || idx >= (int)weaponDatabase.size()) return weaponDatabase.empty() ? Weapon() : weaponDatabase[0]; return weaponDatabase[idx]; }
    Weapon* findWeaponByName(const std::string& name) { for (auto& w : weaponDatabase) if (w.name == name) return &w; return nullptr; }
    Ammo* findAmmoByName(const std::string& name) { for (auto& a : ammoDatabase) if (a.name == name) return &a; return nullptr; }
    BodyArmor* findArmorByName(const std::string& name) { for (auto& a : armorShop) if (a.name == name) return &a; for (auto& a : rareArmorList) if (a.name == name) return &a; return nullptr; }
    Helmet* findHelmetByName(const std::string& name) { for (auto& h : helmetDatabase) if (h.name == name) return &h; return nullptr; }
    MedicalItem* findMedicalByName(const std::string& name) { for (auto& m : medicalShop) if (m.name == name) return &m; return nullptr; }
    Stimulator* findStimByName(const std::string& name) { for (auto& s : stimulatorShop) if (s.name == name) return &s; return nullptr; }

    void speakBattleMono(Role role, const std::string& category, double chance = 1.0) {
        if (randDouble(0, 1) >= chance) return;
        pause();
        std::vector<std::string>* pool = nullptr;
        if (category == "ENCOUNTER") {
            if (role == Role::KATYUSHA) pool = &katyushaEncounterLines;
            else if (role == Role::TOKAREV) pool = &tokarevEncounterLines;
            else if (role == Role::ANNA) pool = &annaEncounterLines;
            else if (role == Role::ALEXANDER) pool = &alexanderEncounterLines;
            else if (role == Role::IVAN) pool = &ivanEncounterLines;
        } else if (category == "KILL") {
            if (role == Role::KATYUSHA) pool = &katyushaKillLines;
            else if (role == Role::TOKAREV) pool = &tokarevKillLines;
            else if (role == Role::ANNA) pool = &annaKillLines;
            else if (role == Role::ALEXANDER) pool = &alexanderKillLines;
            else if (role == Role::IVAN) pool = &ivanKillLines;
        } else if (category == "BOSS_ENCOUNTER") {
            if (role == Role::KATYUSHA) pool = &katyushaBossEncounterLines;
            else if (role == Role::TOKAREV) pool = &tokarevBossEncounterLines;
            else if (role == Role::ANNA) pool = &annaBossEncounterLines;
            else if (role == Role::ALEXANDER) pool = &alexanderBossEncounterLines;
            else if (role == Role::IVAN) pool = &ivanBossEncounterLines;
        } else if (category == "BOSS_KILL") {
            if (role == Role::KATYUSHA) pool = &katyushaBossKillLines;
            else if (role == Role::TOKAREV) pool = &tokarevBossKillLines;
            else if (role == Role::ANNA) pool = &annaBossKillLines;
            else if (role == Role::ALEXANDER) pool = &alexanderBossKillLines;
            else if (role == Role::IVAN) pool = &ivanBossKillLines;
        } else if (category == "MISS") {
            if (role == Role::KATYUSHA) pool = &katyushaMissLines;
            else if (role == Role::TOKAREV) pool = &tokarevMissLines;
            else if (role == Role::ANNA) pool = &annaMissLines;
            else if (role == Role::ALEXANDER) pool = &alexanderMissLines;
            else if (role == Role::IVAN) pool = &ivanMissLines;
        } else if (category == "RELOAD") {
            if (role == Role::KATYUSHA) pool = &katyushaReloadLines;
            else if (role == Role::TOKAREV) pool = &tokarevReloadLines;
            else if (role == Role::ANNA) pool = &annaReloadLines;
            else if (role == Role::ALEXANDER) pool = &alexanderReloadLines;
            else if (role == Role::IVAN) pool = &ivanReloadLines;
        } else if (category == "EMPTY_MAG") {
            if (role == Role::KATYUSHA) pool = &katyushaEmptyMagLines;
            else if (role == Role::TOKAREV) pool = &tokarevEmptyMagLines;
            else if (role == Role::ANNA) pool = &annaEmptyMagLines;
            else if (role == Role::ALEXANDER) pool = &alexanderEmptyMagLines;
            else if (role == Role::IVAN) pool = &ivanEmptyMagLines;
        } else if (category == "ITEM") {
            if (role == Role::KATYUSHA) pool = &katyushaItemLines;
            else if (role == Role::TOKAREV) pool = &tokarevItemLines;
            else if (role == Role::ANNA) pool = &annaItemLines;
            else if (role == Role::ALEXANDER) pool = &alexanderItemLines;
            else if (role == Role::IVAN) pool = &ivanItemLines;
        } else if (category == "ARMOR_REPAIR") {
            if (role == Role::KATYUSHA) pool = &katyushaArmorRepairLines;
            else if (role == Role::TOKAREV) pool = &tokarevArmorRepairLines;
            else if (role == Role::ANNA) pool = &annaArmorRepairLines;
            else if (role == Role::ALEXANDER) pool = &alexanderArmorRepairLines;
            else if (role == Role::IVAN) pool = &ivanArmorRepairLines;
        }
        if (pool == nullptr || pool->empty()) return;
        int r = randInt(0, (int)pool->size() - 1);
        std::cout << "  「" << (*pool)[r] << "」" << std::endl;
        pause();
    }

    void speakDamageLine(Character& t, int damage) {
        if (!isPlayerRole(t.role)) return;
        pause();
        int r = 0;
        if (damage >= 36) {
            if (t.role == Role::KATYUSHA) { r = randInt(0, (int)katyushaCriticalDamageLines.size()-1); std::cout << "  「" << katyushaCriticalDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::TOKAREV) { r = randInt(0, (int)tokarevCriticalDamageLines.size()-1); std::cout << "  「" << tokarevCriticalDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::ANNA) { r = randInt(0, (int)annaCriticalDamageLines.size()-1); std::cout << "  「" << annaCriticalDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::ALEXANDER) { r = randInt(0, (int)alexanderCriticalDamageLines.size()-1); std::cout << "  「" << alexanderCriticalDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::IVAN) { r = randInt(0, (int)ivanCriticalDamageLines.size()-1); std::cout << "  「" << ivanCriticalDamageLines[r] << "」" << std::endl; }
        } else if (damage >= 21) {
            if (t.role == Role::KATYUSHA) { r = randInt(0, (int)katyushaHeavyDamageLines.size()-1); std::cout << "  「" << katyushaHeavyDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::TOKAREV) { r = randInt(0, (int)tokarevHeavyDamageLines.size()-1); std::cout << "  「" << tokarevHeavyDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::ANNA) { r = randInt(0, (int)annaHeavyDamageLines.size()-1); std::cout << "  「" << annaHeavyDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::ALEXANDER) { r = randInt(0, (int)alexanderHeavyDamageLines.size()-1); std::cout << "  「" << alexanderHeavyDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::IVAN) { r = randInt(0, (int)ivanHeavyDamageLines.size()-1); std::cout << "  「" << ivanHeavyDamageLines[r] << "」" << std::endl; }
        } else {
            if (t.role == Role::KATYUSHA) { r = randInt(0, (int)katyushaDamageLines.size()-1); std::cout << "  「" << katyushaDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::TOKAREV) { r = randInt(0, (int)tokarevDamageLines.size()-1); std::cout << "  「" << tokarevDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::ANNA) { r = randInt(0, (int)annaDamageLines.size()-1); std::cout << "  「" << annaDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::ALEXANDER) { r = randInt(0, (int)alexanderDamageLines.size()-1); std::cout << "  「" << alexanderDamageLines[r] << "」" << std::endl; }
            else if (t.role == Role::IVAN) { r = randInt(0, (int)ivanDamageLines.size()-1); std::cout << "  「" << ivanDamageLines[r] << "」" << std::endl; }
        }
        pause();
    }

    void speakDebuffLine(const Character& target, BuffType bt) {
        if (!isPlayerRole(target.role)) return;
        if (randDouble(0, 1) >= 0.5) return;
        pause(); int r = 0;
        BuffType baseBt = (bt == BuffType::HEAVY_BLEEDING) ? BuffType::BLEEDING : bt;
        if (target.role == Role::KATYUSHA) {
            switch (baseBt) {
                case BuffType::BLEEDING: r = randInt(0, (int)katyushaBleedingLines.size()-1); std::cout << "  「" << katyushaBleedingLines[r] << "」" << std::endl; break;
                case BuffType::FRACTURE: r = randInt(0, (int)katyushaFractureLines.size()-1); std::cout << "  「" << katyushaFractureLines[r] << "」" << std::endl; break;
                case BuffType::STOMACH_INJURY: r = randInt(0, (int)katyushaStomachLines.size()-1); std::cout << "  「" << katyushaStomachLines[r] << "」" << std::endl; break;
                case BuffType::PAIN: r = randInt(0, (int)katyushaPainLines.size()-1); std::cout << "  「" << katyushaPainLines[r] << "」" << std::endl; break;
                default: return;
            }
        } else if (target.role == Role::TOKAREV) {
            switch (baseBt) {
                case BuffType::BLEEDING: r = randInt(0, (int)tokarevBleedingLines.size()-1); std::cout << "  「" << tokarevBleedingLines[r] << "」" << std::endl; break;
                case BuffType::FRACTURE: r = randInt(0, (int)tokarevFractureLines.size()-1); std::cout << "  「" << tokarevFractureLines[r] << "」" << std::endl; break;
                case BuffType::STOMACH_INJURY: r = randInt(0, (int)tokarevStomachLines.size()-1); std::cout << "  「" << tokarevStomachLines[r] << "」" << std::endl; break;
                case BuffType::PAIN: r = randInt(0, (int)tokarevPainLines.size()-1); std::cout << "  「" << tokarevPainLines[r] << "」" << std::endl; break;
                default: return;
            }
        } else if (target.role == Role::ANNA) {
            switch (baseBt) {
                case BuffType::BLEEDING: r = randInt(0, (int)annaBleedingLines.size()-1); std::cout << "  「" << annaBleedingLines[r] << "」" << std::endl; break;
                case BuffType::FRACTURE: r = randInt(0, (int)annaFractureLines.size()-1); std::cout << "  「" << annaFractureLines[r] << "」" << std::endl; break;
                case BuffType::STOMACH_INJURY: r = randInt(0, (int)annaStomachLines.size()-1); std::cout << "  「" << annaStomachLines[r] << "」" << std::endl; break;
                case BuffType::PAIN: r = randInt(0, (int)annaPainLines.size()-1); std::cout << "  「" << annaPainLines[r] << "」" << std::endl; break;
                default: return;
            }
        } else if (target.role == Role::ALEXANDER) {
            switch (baseBt) {
                case BuffType::BLEEDING: r = randInt(0, (int)alexanderBleedingLines.size()-1); std::cout << "  「" << alexanderBleedingLines[r] << "」" << std::endl; break;
                case BuffType::FRACTURE: r = randInt(0, (int)alexanderFractureLines.size()-1); std::cout << "  「" << alexanderFractureLines[r] << "」" << std::endl; break;
                case BuffType::STOMACH_INJURY: r = randInt(0, (int)alexanderStomachLines.size()-1); std::cout << "  「" << alexanderStomachLines[r] << "」" << std::endl; break;
                case BuffType::PAIN: r = randInt(0, (int)alexanderPainLines.size()-1); std::cout << "  「" << alexanderPainLines[r] << "」" << std::endl; break;
                default: return;
            }
        } else if (target.role == Role::IVAN) {
            switch (baseBt) {
                case BuffType::BLEEDING: r = randInt(0, (int)ivanBleedingLines.size()-1); std::cout << "  「" << ivanBleedingLines[r] << "」" << std::endl; break;
                case BuffType::FRACTURE: r = randInt(0, (int)ivanFractureLines.size()-1); std::cout << "  「" << ivanFractureLines[r] << "」" << std::endl; break;
                case BuffType::STOMACH_INJURY: r = randInt(0, (int)ivanStomachLines.size()-1); std::cout << "  「" << ivanStomachLines[r] << "」" << std::endl; break;
                case BuffType::PAIN: r = randInt(0, (int)ivanPainLines.size()-1); std::cout << "  「" << ivanPainLines[r] << "」" << std::endl; break;
                default: return;
            }
        }
        pause();
    }

    void speakArmorBrokenLine(const Character& t) {
        if (!isPlayerRole(t.role)) return;
        pause(); int r = 0;
        if (t.role == Role::KATYUSHA) { r = randInt(0, (int)katyushaArmorBrokenLines.size()-1); std::cout << "  「" << katyushaArmorBrokenLines[r] << "」" << std::endl; }
        else if (t.role == Role::TOKAREV) { r = randInt(0, (int)tokarevArmorBrokenLines.size()-1); std::cout << "  「" << tokarevArmorBrokenLines[r] << "」" << std::endl; }
        else if (t.role == Role::ANNA) { r = randInt(0, (int)annaArmorBrokenLines.size()-1); std::cout << "  「" << annaArmorBrokenLines[r] << "」" << std::endl; }
        else if (t.role == Role::ALEXANDER) { r = randInt(0, (int)alexanderArmorBrokenLines.size()-1); std::cout << "  「" << alexanderArmorBrokenLines[r] << "」" << std::endl; }
        else if (t.role == Role::IVAN) { r = randInt(0, (int)ivanArmorBrokenLines.size()-1); std::cout << "  「" << ivanArmorBrokenLines[r] << "」" << std::endl; }
        pause();
    }

    void speakLowHealthLine(Character& t) {
        if (!isPlayerRole(t.role) || t.lowHealthVoiceTriggered) return;
        if (t.maxHealth <= 0 || !t.isAlive()) return;
        if ((double)t.health / t.maxHealth >= 0.20) return;
        t.lowHealthVoiceTriggered = true;
        pause(); int r = 0;
        if (t.role == Role::KATYUSHA) { r = randInt(0, (int)katyushaLowHpLines.size()-1); std::cout << "  「" << katyushaLowHpLines[r] << "」" << std::endl; }
        else if (t.role == Role::TOKAREV) { r = randInt(0, (int)tokarevLowHpLines.size()-1); std::cout << "  「" << tokarevLowHpLines[r] << "」" << std::endl; }
        else if (t.role == Role::ANNA) { r = randInt(0, (int)annaLowHpLines.size()-1); std::cout << "  「" << annaLowHpLines[r] << "」" << std::endl; }
        else if (t.role == Role::ALEXANDER) { r = randInt(0, (int)alexanderLowHpLines.size()-1); std::cout << "  「" << alexanderLowHpLines[r] << "」" << std::endl; }
        else if (t.role == Role::IVAN) { r = randInt(0, (int)ivanLowHpLines.size()-1); std::cout << "  「" << ivanLowHpLines[r] << "」" << std::endl; }
        pause();
    }

    void speakDeathLine(const Character& target, bool targetIsPlayer) {
        if (targetIsPlayer) {
            if (randDouble(0, 1) < 0.7) {
                pause(); int r = 0;
                if (target.role == Role::KATYUSHA) { r = randInt(0, (int)katyushaDeathLines.size()-1); std::cout << "  「" << katyushaDeathLines[r] << "」" << std::endl; }
                else if (target.role == Role::TOKAREV) { r = randInt(0, (int)tokarevDeathLines.size()-1); std::cout << "  「" << tokarevDeathLines[r] << "」" << std::endl; }
                else if (target.role == Role::ANNA) { r = randInt(0, (int)annaDeathLines.size()-1); std::cout << "  「" << annaDeathLines[r] << "」" << std::endl; }
                else if (target.role == Role::ALEXANDER) { r = randInt(0, (int)alexanderDeathLines.size()-1); std::cout << "  「" << alexanderDeathLines[r] << "」" << std::endl; }
                else if (target.role == Role::IVAN) { r = randInt(0, (int)ivanDeathLines.size()-1); std::cout << "  「" << ivanDeathLines[r] << "」" << std::endl; }
                pause();
            }
        } else {
            if (target.isBoss) { pause(); std::cout << "  「" << target.name << " 倒下了。」" << std::endl; pause(); return; }
            if (randDouble(0, 1) < 0.4) { pause(); int r = randInt(0, (int)enemyDeathLines.size()-1); std::cout << "  「" << enemyDeathLines[r] << "」" << std::endl; pause(); }
        }
    }

    void initMaps() {
        Map hut; hut.name = "木屋"; hut.description = "边境的废弃小村,物资零散,散兵游勇偶尔出没。新手熟悉战场的好去处。";
        hut.difficulty = 1; hut.minContainers = 5; hut.maxContainers = 7; hut.minDanger = 3; hut.maxDanger = 15;
        hut.enemyArmorBonus = -25; hut.enemyWeaponBonus = -20; hut.commonThreshold = 75; hut.midThreshold = 96;
        hut.maxEntryMoney = 50000; hut.enableKeys = true; mapDatabase.push_back(hut);
        Map depot; depot.name = "仓库区"; depot.description = "废弃的物流仓库,集装箱与货架间藏着不少好东西。";
        depot.difficulty = 2; depot.minContainers = 6; depot.maxContainers = 9; depot.minDanger = 8; depot.maxDanger = 28;
        depot.enemyArmorBonus = -5; depot.enemyWeaponBonus = 0; depot.commonThreshold = 65; depot.midThreshold = 90;
        depot.maxEntryMoney = 100000; depot.enableKeys = true; mapDatabase.push_back(depot);
        Map port; port.name = "港口码头"; port.description = "走私犯控制的码头,货物价值不菲。";
        port.difficulty = 3; port.minContainers = 7; port.maxContainers = 10; port.minDanger = 15; port.maxDanger = 38;
        port.enemyArmorBonus = 5; port.enemyWeaponBonus = 4; port.commonThreshold = 52; port.midThreshold = 85;
        port.maxEntryMoney = 200000; port.enableKeys = true; mapDatabase.push_back(port);
        Map base; base.name = "军事基地"; base.description = "军方的旧据点。装备精良的守卫在巡逻。";
        base.difficulty = 4; base.minContainers = 9; base.maxContainers = 13; base.minDanger = 20; base.maxDanger = 45;
        base.enemyArmorBonus = 20; base.enemyWeaponBonus = 15; base.commonThreshold = 38; base.midThreshold = 72;
        base.maxEntryMoney = 400000; base.enableKeys = true; mapDatabase.push_back(base);
        Map lab; lab.name = "实验室"; lab.description = "传闻中的禁区。最精锐的雇佣兵把守。";
        lab.difficulty = 5; lab.minContainers = 11; lab.maxContainers = 15; lab.minDanger = 28; lab.maxDanger = 55;
        lab.enemyArmorBonus = 35; lab.enemyWeaponBonus = 28; lab.commonThreshold = 22; lab.midThreshold = 56;
        lab.maxEntryMoney = 800000; lab.enableKeys = true; mapDatabase.push_back(lab);
    }

    void initKeyShop() {
        keyShop.push_back({"木屋地窖钥匙", "开启木屋地图的上锁容器", 3000});
        keyShop.push_back({"仓库办公室钥匙", "开启仓库区地图的上锁容器", 8000});
        keyShop.push_back({"码头海关钥匙", "开启港口码头地图的上锁容器", 15000});
        keyShop.push_back({"军械库钥匙", "开启军事基地地图的上锁容器", 30000});
        keyShop.push_back({"实验室机密室钥匙", "开启实验室地图的上锁容器", 60000});
    }

    void initAmmo() {
        // 5.45x39
        ammoDatabase.push_back(Ammo("5.45 PS","5.45x39",AmmoType::FMJ,46,20,0.35,0.15,45));
        ammoDatabase.push_back(Ammo("5.45 BT","5.45x39",AmmoType::AP,48,28,0.50,0.10,180));
        ammoDatabase.push_back(Ammo("5.45 US","5.45x39",AmmoType::HP,55,10,0.20,0.25,90));
        ammoDatabase.push_back(Ammo("5.45 BS","5.45x39",AmmoType::AP,50,41,0.70,0.05,950,true));
        ammoDatabase.push_back(Ammo("5.45 PP","5.45x39",AmmoType::FMJ,52,23,0.40,0.14,120));
        ammoDatabase.push_back(Ammo("5.45 7N10","5.45x39",AmmoType::AP,49,33,0.55,0.10,220));
        ammoDatabase.push_back(Ammo("5.45 7N22","5.45x39",AmmoType::AP,51,39,0.65,0.08,480,true));
        // 5.56x45
        ammoDatabase.push_back(Ammo("5.56 M193","5.56x45",AmmoType::FMJ,44,12,0.30,0.20,50));
        ammoDatabase.push_back(Ammo("5.56 Mk318","5.56x45",AmmoType::HP,58,12,0.30,0.30,120));
        ammoDatabase.push_back(Ammo("5.56 M855","5.56x45",AmmoType::FMJ,48,28,0.38,0.14,60));
        ammoDatabase.push_back(Ammo("5.56 M856","5.56x45",AmmoType::TRACER,49,22,0.40,0.12,90));
        ammoDatabase.push_back(Ammo("5.56 M855A1","5.56x45",AmmoType::AP,52,32,0.48,0.10,180));
        ammoDatabase.push_back(Ammo("5.56 M995","5.56x45",AmmoType::AP,50,45,0.55,0.08,480,true));
        // 7.62x39
        ammoDatabase.push_back(Ammo("7.62 PS","7.62x39",AmmoType::FMJ,50,37,0.42,0.18,80));
        ammoDatabase.push_back(Ammo("7.62 BP","7.62x39",AmmoType::AP,53,48,0.60,0.10,350,true));
        ammoDatabase.push_back(Ammo("7.62 HP","7.62x39",AmmoType::HP,65,10,0.25,0.30,100));
        ammoDatabase.push_back(Ammo("7.62 US","7.62x39",AmmoType::DUMDUM,71,6,0.18,0.42,140));
        ammoDatabase.push_back(Ammo("7.62 7N23","7.62x39",AmmoType::AP,55,42,0.65,0.08,420,true));
        // 7.62x51
        ammoDatabase.push_back(Ammo("7.62 M80","7.62x51",AmmoType::FMJ,54,44,0.45,0.20,180));
        ammoDatabase.push_back(Ammo("7.62 M61","7.62x51",AmmoType::AP,60,55,0.65,0.08,680,true));
        ammoDatabase.push_back(Ammo("7.62 M62","7.62x51",AmmoType::TRACER,56,38,0.48,0.12,260));
        // .277 Fury
        ammoDatabase.push_back(Ammo(".277 Fury FMJ",".277 Fury",AmmoType::FMJ,52,28,0.48,0.18,200));
        ammoDatabase.push_back(Ammo(".277 Fury HP",".277 Fury",AmmoType::HP,64,14,0.32,0.28,260));
        ammoDatabase.push_back(Ammo(".277 Fury AP",".277 Fury",AmmoType::AP,58,38,0.72,0.08,880,true));
        // 5.7x28
        ammoDatabase.push_back(Ammo("5.7x28 SS195","5.7x28",AmmoType::FMJ,38,21,0.35,0.15,90));
        ammoDatabase.push_back(Ammo("5.7x28 SS190","5.7x28",AmmoType::AP,34,31,0.50,0.08,320,true));
        ammoDatabase.push_back(Ammo("5.7x28 SS197","5.7x28",AmmoType::HP,46,12,0.28,0.28,140));
        // 9x39
        ammoDatabase.push_back(Ammo("9x39 SP-5","9x39",AmmoType::FMJ,45,22,0.45,0.18,220));
        ammoDatabase.push_back(Ammo("9x39 SP-6","9x39",AmmoType::AP,53,36,0.62,0.08,620,true));
        ammoDatabase.push_back(Ammo("9x39 PAB-9","9x39",AmmoType::AP,51,42,0.68,0.06,900,true));
        ammoDatabase.push_back(Ammo("9x39 SPP 7N9","9x39",AmmoType::AP,52,47,0.70,0.06,1050,true));
        ammoDatabase.push_back(Ammo("9x39 BP 7N12","9x39",AmmoType::AP,54,55,0.75,0.05,1400,true));
        // .338 LM
        ammoDatabase.push_back(Ammo(".338 LM FMJ",".338 LM",AmmoType::FMJ,130,59,0.60,0.15,550));
        ammoDatabase.push_back(Ammo(".338 LM AP",".338 LM",AmmoType::AP,190,68,0.78,0.05,1500,true));
        // .50 BMG
        ammoDatabase.push_back(Ammo(".50 BMG FMJ",".50 BMG",AmmoType::FMJ,150,65,0.70,0.15,2500,true));
        ammoDatabase.push_back(Ammo(".50 BMG AP",".50 BMG",AmmoType::AP,190,85,0.85,0.08,4500,true));
        // .50 AE
        ammoDatabase.push_back(Ammo(".50 AE FMJ",".50 AE",AmmoType::FMJ,50,18,0.42,0.22,180));
        ammoDatabase.push_back(Ammo(".50 AE AP",".50 AE",AmmoType::AP,51,22,0.62,0.10,480));
        // 7.62x54R
        ammoDatabase.push_back(Ammo("7.62x54R LPS","7.62x54R",AmmoType::FMJ,98,39,0.50,0.18,200));
        ammoDatabase.push_back(Ammo("7.62x54R SNB","7.62x54R",AmmoType::AP,95,46,0.68,0.08,750,true));
        ammoDatabase.push_back(Ammo("7.62x54R BT","7.62x54R",AmmoType::AP,96,54,0.72,0.06,1100,true));
        // 9x19
        ammoDatabase.push_back(Ammo("9x19 PST","9x19",AmmoType::FMJ,35,12,0.25,0.20,25));
        ammoDatabase.push_back(Ammo("9x19 AP 6.3","9x19",AmmoType::AP,30,22,0.40,0.12,200));
        ammoDatabase.push_back(Ammo("9x19 DumDum","9x19",AmmoType::DUMDUM,50,5,0.15,0.40,60));
        ammoDatabase.push_back(Ammo("9x19 P+","9x19",AmmoType::FMJ,38,15,0.28,0.18,45));
        ammoDatabase.push_back(Ammo("9x19 Subsonic","9x19",AmmoType::FMJ,32,8,0.20,0.25,35));
        // 4.6x30
        ammoDatabase.push_back(Ammo("4.6x30 FMJ","4.6x30",AmmoType::FMJ,41,28,0.30,0.15,50));
        ammoDatabase.push_back(Ammo("4.6x30 AP","4.6x30",AmmoType::AP,38,39,0.45,0.10,200,true));
        ammoDatabase.push_back(Ammo("4.6x30 HP","4.6x30",AmmoType::HP,50,18,0.20,0.28,90));
        // .45 ACP
        ammoDatabase.push_back(Ammo(".45 FMJ",".45 ACP",AmmoType::FMJ,40,10,0.25,0.20,40));
        ammoDatabase.push_back(Ammo(".45 AP",".45 ACP",AmmoType::AP,38,20,0.42,0.12,220));
        ammoDatabase.push_back(Ammo(".45 RIP",".45 ACP",AmmoType::DUMDUM,52,4,0.15,0.45,180));
        ammoDatabase.push_back(Ammo(".45 +P",".45 ACP",AmmoType::FMJ,44,12,0.28,0.22,55));
        // 12 Gauge
        ammoDatabase.push_back(Ammo("12/70 鹿弹","12 Gauge",AmmoType::HP,12,6,0.30,0.35,90));
        ammoDatabase.push_back(Ammo("12/70 独头弹","12 Gauge",AmmoType::FMJ,15,15,0.45,0.25,150));
        ammoDatabase.push_back(Ammo("12/70 AP-20","12 Gauge",AmmoType::AP,18,32,0.70,0.10,480,true));
        ammoDatabase.push_back(Ammo("12/70 龙息弹","12 Gauge",AmmoType::TRACER,20,4,0.25,0.30,220));
    }
    void initWeapons() {
        addWeapon("AKS-74U","短突击步枪","5.45x39",30,650,60.0,18000);
        addWeapon("AK-74M","突击步枪","5.45x39",30,650,52.0,28000);
        addWeapon("AK-12","突击步枪","5.45x39",30,700,55.0,55000);
        addWeapon("AN-94","突击步枪","5.45x39",30,1800,50.0,78000);
        addWeapon("M4A1","突击步枪","5.56x45",30,800,58.0,45000);
        addWeapon("M16A4","突击步枪","5.56x45",30,700,60.0,42000);
        addWeapon("HK416","突击步枪","5.56x45",30,850,56.0,62000);
        addWeapon("AUG A3","突击步枪","5.56x45",30,720,62.0,58000);
        addWeapon("G36C","突击步枪","5.56x45",30,750,59.0,52000);
        addWeapon("SCAR-L","突击步枪","5.56x45",30,650,57.0,64000);
        addWeapon("AKM","突击步枪","7.62x39",30,600,45.0,32000);
        addWeapon("AK-103","突击步枪","7.62x39",30,600,48.0,38000);
        addWeapon("SA-58","战斗步枪","7.62x51",20,700,42.0,68000);
        addWeapon("SCAR-H","战斗步枪","7.62x51",20,600,45.0,82000);
        addWeapon("G3","战斗步枪","7.62x51",20,550,40.0,58000);
        addWeapon("M7","战斗步枪",".277 Fury",20,700,45.0,72000);
        addWeapon("P90","冲锋枪","5.7x28",50,900,68.0,48000);
        addWeapon("Five-seveN","手枪","5.7x28",20,500,76.0,22000);
        addWeapon("VSS","半自动狙击枪","9x39",10,300,55.0,76000);
        addWeapon("AS Val 巨浪","突击步枪","9x39",20,900,55.0,88000);
        addWeapon("AWM","栓动狙击枪",".338 LM",5,30,65.0,125000);
        addWeapon("沙漠之鹰","手枪",".50 AE",7,280,80.0,28000);
        addWeapon("M700","栓动狙击枪","7.62x51",5,40,68.0,52000);
        addWeapon("SVD","半自动狙击枪","7.62x54R",10,220,40.0,85000);
        addWeapon("SV-98","栓动狙击枪","7.62x54R",10,30,62.0,78000);
        addWeapon("MP5","冲锋枪","9x19",30,800,65.0,18000);
        addWeapon("PP-19 Vityaz","冲锋枪","9x19",30,700,62.0,16000);
        addWeapon("Glock 17","手枪","9x19",17,450,75.0,9000);
        addWeapon("M1911","手枪",".45 ACP",7,350,78.0,12000);
        addWeapon("UMP45","冲锋枪",".45 ACP",25,600,58.0,25000);
        addWeapon("Vector","冲锋枪",".45 ACP",25,1100,60.0,42000);
        addWeapon("MP7","冲锋枪","4.6x30",40,950,68.0,26000);
        addWeapon("MP-153","霰弹枪","12 Gauge",6,120,55.0,22000);
        addWeapon("Saiga-12","霰弹枪","12 Gauge",8,180,50.0,35000);
        addWeapon("M870","泵动霰弹枪","12 Gauge",6,90,60.0,15000);
        addWeapon("KSG","泵动霰弹枪","12 Gauge",14,100,52.0,38000);
        addWeapon("Vepr-12","霰弹枪","12 Gauge",8,200,48.0,40000);
        addWeapon("巴雷特 M82A1","反器材狙击枪",".50 BMG",10,35,38.0,320000);
    }

    void addWeapon(const std::string& n, const std::string& cat, const std::string& cal, int mag, int fr, double erg, int price) {
        Weapon w; w.name = n; w.category = cat; w.caliber = cal;
        w.magazineSize = mag; w.currentAmmo = 0; w.fireRate = fr; w.ergonomics = erg; w.price = price;
        w.compatibleAmmo = getAmmoByCaliber(cal);
        weaponDatabase.push_back(w);
    }

    std::vector<Ammo> getAmmoByCaliber(const std::string& cal) {
        std::vector<Ammo> r; for (auto& a : ammoDatabase) if (a.caliber == cal) r.push_back(a);
        return r;
    }

    void initArmorShop() {
        // 2 级
        armorShop.push_back(BodyArmor("6B5-11 芳纶背心", ArmorClass::CLASS_2, 40, 3.0, 15000));
        armorShop.push_back(BodyArmor("PACA 软式防弹背心", ArmorClass::CLASS_2, 100, 3.5, 22000));
        armorShop.push_back(BodyArmor("BNTI Module-3M", ArmorClass::CLASS_2, 80, 6.0, 18000));
        armorShop.push_back(BodyArmor("6B2 防弹背心(林地)", ArmorClass::CLASS_2, 128, 5.4, 26000));
        // 3 级
        armorShop.push_back(BodyArmor("6B5-12 钛合金背心", ArmorClass::CLASS_3, 55, 5.5, 28000));
        armorShop.push_back(BodyArmor("MF-UNTAR 防弹背心", ArmorClass::CLASS_3, 100, 8.0, 35000));
        armorShop.push_back(BodyArmor("6B23-1 巴尔米查", ArmorClass::CLASS_3, 65, 6.8, 38000));
        armorShop.push_back(BodyArmor("Zhuk-3 防弹背心", ArmorClass::CLASS_3, 75, 7.5, 42000));
        // 4 级
        armorShop.push_back(BodyArmor("6B23 巴尔米查", ArmorClass::CLASS_4, 65, 6.8, 45000));
        armorShop.push_back(BodyArmor("6B23-2 巴尔米查-M", ArmorClass::CLASS_4, 80, 8.2, 62000));
        armorShop.push_back(BodyArmor("6B13 突击背心", ArmorClass::CLASS_4, 85, 9.0, 58000));
        armorShop.push_back(BodyArmor("Trooper TFO 背心", ArmorClass::CLASS_4, 90, 9.5, 75000));
        // 5 级
        rareArmorList.push_back(BodyArmor("6B43 战士-1", ArmorClass::CLASS_5, 80, 7.5, 72000, true));
        rareArmorList.push_back(BodyArmor("6B45 战士-2", ArmorClass::CLASS_5, 85, 7.8, 95000, true));
        rareArmorList.push_back(BodyArmor("FORT Redut-T5", ArmorClass::CLASS_5, 110, 12.0, 135000, true));
        // 6 级
        rareArmorList.push_back(BodyArmor("6B45-1 装甲增强", ArmorClass::CLASS_6, 100, 11.5, 150000, true));
        rareArmorList.push_back(BodyArmor("奥贝雷格 Obereg", ArmorClass::CLASS_6, 110, 11.5, 115000, true));
        rareArmorList.push_back(BodyArmor("Zhuk-6a 重型甲", ArmorClass::CLASS_6, 120, 13.0, 180000, true));
    }

    void initHelmets() {
        helmetDatabase.push_back(Helmet("SSh-68 钢盔", ArmorClass::CLASS_3, 30, 0.25, 1.5, 15000));
        helmetDatabase.push_back(Helmet("6B47 Ratnik-BSh", ArmorClass::CLASS_3, 35, 0.30, 1.3, 32000));
        helmetDatabase.push_back(Helmet("LZSh 轻型头盔", ArmorClass::CLASS_3, 30, 0.20, 0.95, 28000));
        helmetDatabase.push_back(Helmet("Kolpak-1S 镇暴盔", ArmorClass::CLASS_2, 45, 0.10, 1.9, 18000));
        helmetDatabase.push_back(Helmet("UNTAR 蓝盔", ArmorClass::CLASS_3, 25, 0.25, 2.2, 25000));
        helmetDatabase.push_back(Helmet("Tac-Kek FAST MT", ArmorClass::CLASS_3, 48, 0.35, 0.45, 45000));
        helmetDatabase.push_back(Helmet("TC-2001 高切盔", ArmorClass::CLASS_4, 50, 0.30, 1.1, 68000));
        helmetDatabase.push_back(Helmet("TC-2002 全护盔", ArmorClass::CLASS_4, 55, 0.25, 1.4, 85000));
        helmetDatabase.push_back(Helmet("阿尔金头盔", ArmorClass::CLASS_5, 60, 0.40, 2.0, 180000));
        helmetDatabase.push_back(Helmet("DEVTAC 浪人防御盔", ArmorClass::CLASS_4, 65, 0.15, 1.8, 120000));
        helmetDatabase.push_back(Helmet("BNTI LSHZ-2DTM", ArmorClass::CLASS_4, 55, 0.35, 1.6, 95000));
        helmetDatabase.push_back(Helmet("HighCom Striker ULACH", ArmorClass::CLASS_4, 52, 0.30, 1.0, 88000));
    }

    void initMedicalShop() {
        MedicalItem b; b.name="军用绷带"; b.description="止住轻度流血,小幅恢复生命(骨折期间无法使用)";
        b.healAmount=10; b.stopsBleeding=true; b.blockedByFracture=true; b.uses=1; b.price=3000; medicalShop.push_back(b);

        MedicalItem es; es.name="Esmarch 止血带"; es.description="止住所有流血(包括重度流血)";
        es.stopsBleeding=true; es.stopsHeavyBleeding=true; es.uses=1; es.price=5500; medicalShop.push_back(es);

        MedicalItem cal; cal.name="CALOK-B 止血剂"; cal.description="止住所有流血并小幅回血";
        cal.healAmount=15; cal.stopsBleeding=true; cal.stopsHeavyBleeding=true; cal.uses=1; cal.price=6000; medicalShop.push_back(cal);

        MedicalItem s; s.name="夹板"; s.description="固定骨折部位"; s.fixesFracture=true; s.uses=1; s.price=4500; medicalShop.push_back(s);

        MedicalItem als; als.name="铝制夹板"; als.description="固定骨折部位,可多次使用"; als.fixesFracture=true; als.uses=3; als.price=12000; medicalShop.push_back(als);

        MedicalItem p; p.name="止痛药"; p.description="5 回合内忽略疼痛惩罚"; p.givesPainkiller=true; p.uses=1; p.price=2500; medicalShop.push_back(p);

        MedicalItem ib; ib.name="Ibuprofen 布洛芬"; ib.description="较长时间止痛"; ib.givesPainkiller=true; ib.uses=1; ib.price=6000; medicalShop.push_back(ib);

        MedicalItem an; an.name="Analgin 安乃近"; an.description="强力止痛"; an.givesPainkiller=true; an.uses=1; an.price=8000; medicalShop.push_back(an);

        MedicalItem m; m.name="AI-2 急救包"; m.description="苏制急救包(骨折期间无法使用)"; m.healAmount=40; m.blockedByFracture=true; m.uses=1; m.price=8000; medicalShop.push_back(m);

        MedicalItem sa; sa.name="Salewa 急救包"; sa.description="止血(含重度流血)并恢复大量生命"; sa.healAmount=60; sa.stopsBleeding=true; sa.stopsHeavyBleeding=true; sa.uses=1; sa.price=18000; medicalShop.push_back(sa);

        MedicalItem ifak; ifak.name="IFAK 个人急救包"; ifak.description="止住轻度流血并恢复生命"; ifak.healAmount=50; ifak.stopsBleeding=true; ifak.uses=1; ifak.price=12000; medicalShop.push_back(ifak);

        MedicalItem gri; gri.name="Grizzly 医疗包"; gri.description="恢复大量生命,止血(含重度)、正骨、修腹";
        gri.healAmount=100; gri.stopsBleeding=true; gri.stopsHeavyBleeding=true; gri.fixesFracture=true; gri.fixesStomachInjury=true; gri.uses=1; gri.price=45000; medicalShop.push_back(gri);

        MedicalItem sk; sk.name="外科手术包"; sk.description="恢复生命、止血、正骨、修复腹部伤,喀秋莎 +5% 闪避";
        sk.healAmount=70; sk.stopsBleeding=true; sk.stopsHeavyBleeding=true; sk.fixesFracture=true; sk.fixesStomachInjury=true; sk.uses=1; sk.price=25000; medicalShop.push_back(sk);

        MedicalItem cms; cms.name="CMS 手术包"; cms.description="修复部位损伤,正骨、修腹";
        cms.healAmount=30; cms.fixesFracture=true; cms.fixesStomachInjury=true; cms.uses=1; cms.price=35000; medicalShop.push_back(cms);

        MedicalItem surv; surv.name="Surv12 野战手术包"; surv.description="高级手术包,恢复更多生命,止血(含重度)";
        surv.healAmount=50; surv.stopsBleeding=true; surv.stopsHeavyBleeding=true; surv.fixesFracture=true; surv.fixesStomachInjury=true; surv.uses=1; surv.price=55000; medicalShop.push_back(surv);

        MedicalItem fr; fr.name="野战甲修工具"; fr.description="恢复 40 点护甲耐久"; fr.armorRepairAmount=40; fr.uses=1; fr.price=5000; medicalShop.push_back(fr);
        MedicalItem mr; mr.name="军用甲修工具"; mr.description="恢复 100 点护甲耐久"; mr.armorRepairAmount=100; mr.uses=1; mr.price=15000; medicalShop.push_back(mr);
        MedicalItem ar; ar.name="高级甲修工具"; ar.description="恢复 200 点护甲耐久"; ar.armorRepairAmount=200; ar.uses=1; ar.price=35000; medicalShop.push_back(ar);
        MedicalItem bd; bd.name="Body Armor Repair Kit"; bd.description="恢复 150 点护甲耐久"; bd.armorRepairAmount=150; bd.uses=1; bd.price=22000; medicalShop.push_back(bd);
    }

    void initStimulators() {
        Stimulator p; p.name="Propital 战斗针剂"; p.description="止痛,每回合回 1 血,持续 5 回合"; p.price=25000;
        p.removePain=true; p.healPerTurn=1; p.healPerTurnTurns=5; stimulatorShop.push_back(p);

        Stimulator e; e.name="eTG-c 兴奋剂"; e.description="立即回 15 血,随后每回合回 6 血,持续 3 回合"; e.price=38000;
        e.instantHeal=15; e.healPerTurn=6; e.healPerTurnTurns=3; stimulatorShop.push_back(e);

        Stimulator z; z.name="Zagustin 止血针"; z.description="立即止住所有流血"; z.price=32000;
        z.stopAllBleeding=true; stimulatorShop.push_back(z);

        Stimulator sj6; sj6.name="SJ6 TGLabs 战斗兴奋剂"; sj6.description="速度 +15%,持续 5 回合"; sj6.price=28000;
        sj6.speedBonus=0.15; sj6.speedBonusTurns=5; stimulatorShop.push_back(sj6);

        Stimulator sj1; sj1.name="SJ1 TGLabs 战斗兴奋剂"; sj1.description="伤害 +10%、减伤 +10%,持续 4 回合"; sj1.price=35000;
        sj1.damageBonus=0.10; sj1.damageBonusTurns=4; sj1.damageResist=0.10; sj1.damageResistTurns=4; stimulatorShop.push_back(sj1);

        Stimulator a; a.name="肾上腺素注射器"; a.description="立即回 10 血,速度 +10%、伤害 +5%,持续 3 回合"; a.price=22000;
        a.instantHeal=10; a.speedBonus=0.10; a.speedBonusTurns=3; a.damageBonus=0.05; a.damageBonusTurns=3; stimulatorShop.push_back(a);

        Stimulator mo; mo.name="吗啡注射器"; mo.description="止痛 10 回合,速度 +5%,持续 5 回合"; mo.price=18000;
        mo.removePain=true; mo.speedBonus=0.05; mo.speedBonusTurns=5; stimulatorShop.push_back(mo);

        Stimulator pr; pr.name="Propit 战斗兴奋剂"; pr.description="立即回 20 血,速度 +5%,持续 3 回合"; pr.price=26000;
        pr.instantHeal=20; pr.speedBonus=0.05; pr.speedBonusTurns=3; stimulatorShop.push_back(pr);

        Stimulator mel; mel.name="Melatonin 褪黑素"; mel.description="止痛并恢复少量生命"; mel.price=15000;
        mel.removePain=true; mel.instantHeal=5; stimulatorShop.push_back(mel);
    }

    void initPlayer() {
        std::cout << "\n===== 角色选择 =====\n";
        std::cout << "1. 喀秋莎 (Katyusha)\n2. 托卡列夫 (Tokarev)\n3. 安娜 (Anna)\n4. 亚历山大 (Alexander)\n5. 伊万 (Ivan)\n请选择: ";
        int choice = readInt();
        if (choice == 2) { player.role = Role::TOKAREV; player.name = "托卡列夫"; player.maxHealth = 100; player.health = 100; player.speed = 0.95; player.armorToughness = 1.15; player.dodgeChance = 0.0; player.debuffBonus = 0.15; }
        else if (choice == 3) { player.role = Role::ANNA; player.name = "安娜"; player.maxHealth = 100; player.health = 100; player.speed = 1.05; player.armorToughness = 1.0; player.dodgeChance = 0.0; player.debuffBonus = 0.0; }
        else if (choice == 4) { player.role = Role::ALEXANDER; player.name = "亚历山大"; player.maxHealth = 100; player.health = 100; player.speed = 1.0; player.armorToughness = 1.0; player.dodgeChance = 0.0; player.debuffBonus = 0.0; }
        else if (choice == 5) { player.role = Role::IVAN; player.name = "伊万"; player.maxHealth = 100; player.health = 100; player.speed = 1.05; player.armorToughness = 1.0; player.dodgeChance = 0.0; player.debuffBonus = 0.0; }
        else { player.role = Role::KATYUSHA; player.name = "喀秋莎"; player.maxHealth = 100; player.health = 100; player.speed = 1.0; player.armorToughness = 1.0; player.dodgeChance = KATYUSHA_DODGE_MAX; player.debuffBonus = 0.0; }
        player.initPartHealth();
        player.money = 50000; player.weapon = Weapon(); player.armor = BodyArmor(); player.helmet = Helmet();
        player.inventory.clear(); player.lootBag.clear(); player.buffs.clear(); player.stimulators.clear();
        player.hasRevived = false; player.flashbangUsed = false; player.stunnedTurns = 0;
        player.lootCapacity = DEFAULT_LOOT_CAPACITY; player.debt = 0; player.safeDeposit = 0;
        player.headsetLevel = 0; player.lowHealthVoiceTriggered = false; player.boltCooldown = 0;
        player.stunGrenades = 0; player.medicalDelay = 0; player.interferenceTriggered = false; player.skillCooldown = 0;
        player.annaBeaconAvailable = false; player.annaBeaconActive = false;
        player.stimHealPerTurn = 0; player.stimHealTurns = 0;
        player.stimSpeedBonus = 1.0; player.stimSpeedTurns = 0;
        player.stimDamageBonus = 1.0; player.stimDamageTurns = 0;
        player.stimDamageResist = 1.0; player.stimDamageResistTurns = 0;
        player.keys.clear(); warehouse.clear(); antonovAmmoStock.clear(); antonovArmorStock.clear();
        antonovStockInitialized = false;
    }

    void rebuildLootMeta(LootItem& loot) {
        std::string n = loot.name;
        const std::string sfx = " 弹药";
        if (n.size() >= sfx.size() && n.compare(n.size() - sfx.size(), sfx.size(), sfx) == 0) {
            std::string base = n.substr(0, n.size() - sfx.size());
            Ammo* a = findAmmoByName(base);
            if (a) { loot.category = ItemCategory::AMMO; loot.ammoData = *a; return; }
        }
        BodyArmor* ar = findArmorByName(n); if (ar) { loot.category = ItemCategory::ARMOR; loot.armorData = *ar; return; }
        Weapon* w = findWeaponByName(n); if (w) { loot.category = ItemCategory::WEAPON; loot.weaponData = *w; return; }
        MedicalItem* m = findMedicalByName(n); if (m) { loot.category = ItemCategory::MEDICAL; loot.medData = *m; return; }
        loot.category = ItemCategory::VALUABLE;
    }

    void saveGame() {
        std::ofstream out(savePath.c_str());
        if (!out.is_open()) { std::cout << "存档失败。" << std::endl; return; }
        out << (int)player.role << "\n" << player.dodgeChance << "\n" << player.money << "\n"
            << player.health << "\n" << player.maxHealth << "\n" << (player.hasRevived ? 1 : 0) << "\n"
            << player.lootCapacity << "\n" << player.debt << "\n" << player.safeDeposit << "\n" << player.headsetLevel << "\n";
        if (player.weapon.magazineSize > 0) out << player.weapon.name << "\n"; else out << "NONE\n";
        out << player.weapon.currentAmmo << "\n";
        if (!player.weapon.compatibleAmmo.empty()) out << player.weapon.compatibleAmmo[0].name << "\n" << player.weapon.compatibleAmmo[0].quantity << "\n";
        else out << "NONE\n0\n";
        out << (player.armor.owned ? 1 : 0) << "\n" << (player.armor.owned ? player.armor.name : "NONE") << "\n" << player.armor.currentDurability << "\n";
        out << (player.helmet.owned ? 1 : 0) << "\n" << (player.helmet.owned ? player.helmet.name : "NONE") << "\n" << player.helmet.currentDurability << "\n";
        out << player.inventory.size() << "\n";
        for (size_t i = 0; i < player.inventory.size(); ++i) out << player.inventory[i].name << "\n";
        out << player.stimulators.size() << "\n";
        for (size_t i = 0; i < player.stimulators.size(); ++i) out << player.stimulators[i].name << "\n";
        out << player.lootBag.size() << "\n";
        for (size_t i = 0; i < player.lootBag.size(); ++i) out << player.lootBag[i].name << "\n" << player.lootBag[i].value << "\n" << player.lootBag[i].quantity << "\n";
        out << player.keys.size() << "\n";
        for (auto& kv : player.keys) out << kv.first << "\n" << kv.second << "\n";
        out << warehouse.size() << "\n";
        for (size_t i = 0; i < warehouse.size(); ++i) out << warehouse[i].name << "\n" << warehouse[i].value << "\n" << warehouse[i].quantity << "\n";
        out.close();
        std::cout << "游戏已保存到 " << savePath << std::endl;
    }

    bool loadGame() {
        std::ifstream in(savePath.c_str());
        if (!in.is_open()) { std::cout << "没有找到存档。" << std::endl; return false; }
        try {
            std::string line;
            std::getline(in, line); int ri = std::stoi(line);
            if (ri == 0) player.role = Role::KATYUSHA;
            else if (ri == 1) player.role = Role::TOKAREV;
            else if (ri == 2) player.role = Role::ANNA;
            else if (ri == 3) player.role = Role::ALEXANDER;
            else if (ri == 4) player.role = Role::IVAN;
            else player.role = Role::KATYUSHA;
            player.name = roleName(player.role);
            std::getline(in, line); player.dodgeChance = std::stod(line);
            std::getline(in, line); player.money = std::stoi(line);
            std::getline(in, line); player.health = std::stoi(line);
            std::getline(in, line); player.maxHealth = std::stoi(line);
            std::getline(in, line); player.hasRevived = (line == "1");
            std::getline(in, line); player.lootCapacity = std::stoi(line);
            if (player.lootCapacity < DEFAULT_LOOT_CAPACITY) player.lootCapacity = DEFAULT_LOOT_CAPACITY;
            if (std::getline(in, line) && !line.empty()) { try { player.debt = std::stoi(line); } catch (...) { player.debt = 0; } } else player.debt = 0;
            if (player.debt < 0) player.debt = 0;
            if (std::getline(in, line) && !line.empty()) { try { player.safeDeposit = std::stoi(line); } catch (...) { player.safeDeposit = 0; } } else player.safeDeposit = 0;
            if (player.safeDeposit < 0) player.safeDeposit = 0;
            if (std::getline(in, line) && !line.empty()) { try { player.headsetLevel = std::stoi(line); } catch (...) { player.headsetLevel = 0; } } else player.headsetLevel = 0;
            if (player.headsetLevel < 0 || player.headsetLevel > 2) player.headsetLevel = 0;
            player.flashbangUsed = false; player.stunnedTurns = 0; player.lowHealthVoiceTriggered = false; player.boltCooldown = 0;
            player.stunGrenades = 0; player.medicalDelay = 0; player.interferenceTriggered = false; player.skillCooldown = 0;
            player.annaBeaconAvailable = false; player.annaBeaconActive = false;
            player.stimHealPerTurn = 0; player.stimHealTurns = 0;
            player.stimSpeedBonus = 1.0; player.stimSpeedTurns = 0;
            player.stimDamageBonus = 1.0; player.stimDamageTurns = 0;
            player.stimDamageResist = 1.0; player.stimDamageResistTurns = 0;
            if (player.role == Role::KATYUSHA) {
                player.speed = 1.0; player.armorToughness = 1.0; player.debuffBonus = 0.0;
                if (player.dodgeChance > KATYUSHA_DODGE_MAX) player.dodgeChance = KATYUSHA_DODGE_MAX;
                if (player.dodgeChance < 0.0) player.dodgeChance = 0.0;
            } else if (player.role == Role::TOKAREV) { player.speed = 0.95; player.armorToughness = 1.15; player.dodgeChance = 0.0; player.debuffBonus = 0.15; }
            else if (player.role == Role::ANNA) { player.speed = 1.05; player.armorToughness = 1.0; player.dodgeChance = 0.0; player.debuffBonus = 0.0; }
            else if (player.role == Role::IVAN) { player.speed = 1.05; player.armorToughness = 1.0; player.dodgeChance = 0.0; player.debuffBonus = 0.0; }
            else { player.speed = 1.0; player.armorToughness = 1.0; player.dodgeChance = 0.0; player.debuffBonus = 0.0; }
            player.partMaxHealth.clear(); player.partHealth.clear(); player.initPartHealth();
            if (player.maxHealth > 0 && player.health < player.maxHealth) {
                double r = (double)player.health / player.maxHealth;
                for (auto it = player.partHealth.begin(); it != player.partHealth.end(); ++it) { int nh = (int)(it->second * r); if (nh < 1) nh = 1; it->second = nh; }
            }
            player.buffs.clear();
            std::string wName; std::getline(in, wName);
            std::getline(in, line); int wAmmo = std::stoi(line);
            if (wName != "NONE") { Weapon* w = findWeaponByName(wName); if (w) { player.weapon = *w; player.weapon.currentAmmo = wAmmo; } else player.weapon = Weapon(); }
            else player.weapon = Weapon();
            std::string ammoName; std::getline(in, ammoName);
            std::getline(in, line); int ammoQty = std::stoi(line);
            player.weapon.compatibleAmmo.clear();
            if (ammoName != "NONE") { Ammo* a = findAmmoByName(ammoName); if (a) { Ammo c = *a; c.quantity = ammoQty; player.weapon.compatibleAmmo.push_back(c); } }
            std::getline(in, line); bool ao = (line == "1");
            std::string armorName; std::getline(in, armorName);
            std::getline(in, line); int ad = std::stoi(line);
            if (ao && armorName != "NONE") { BodyArmor* a = findArmorByName(armorName); if (a) { player.armor = *a; player.armor.owned = true; player.armor.currentDurability = ad; } else player.armor = BodyArmor(); }
            else player.armor = BodyArmor();

            bool ho = false; std::string helmetName = "NONE"; int hd = 0;
            if (std::getline(in, line) && !line.empty()) { try { ho = (std::stoi(line) == 1); } catch (...) { ho = false; } }
            std::getline(in, helmetName);
            if (std::getline(in, line) && !line.empty()) { try { hd = std::stoi(line); } catch (...) { hd = 0; } }
            if (ho && helmetName != "NONE") { Helmet* h = findHelmetByName(helmetName); if (h) { player.helmet = *h; player.helmet.owned = true; player.helmet.currentDurability = hd; } else player.helmet = Helmet(); }
            else player.helmet = Helmet();

            std::getline(in, line); int mc = std::stoi(line);
            player.inventory.clear();
            for (int i = 0; i < mc; ++i) { std::string mn; std::getline(in, mn); MedicalItem* m = findMedicalByName(mn); if (m) player.inventory.push_back(*m); }

            int sc = 0;
            if (std::getline(in, line) && !line.empty()) { try { sc = std::stoi(line); } catch (...) { sc = 0; } }
            player.stimulators.clear();
            for (int i = 0; i < sc; ++i) { std::string sn; std::getline(in, sn); Stimulator* s = findStimByName(sn); if (s) player.stimulators.push_back(*s); }

            std::getline(in, line); int lc = std::stoi(line);
            player.lootBag.clear();
            for (int i = 0; i < lc; ++i) {
                LootItem loot; std::getline(in, loot.name);
                std::getline(in, line); loot.value = std::stoi(line);
                std::getline(in, line); loot.quantity = std::stoi(line);
                rebuildLootMeta(loot); player.lootBag.push_back(loot);
            }
            player.keys.clear();
            if (std::getline(in, line) && !line.empty()) {
                int kc = 0; try { kc = std::stoi(line); } catch (...) { kc = 0; }
                for (int i = 0; i < kc; ++i) { std::string kn; std::getline(in, kn); std::getline(in, line); int kv = 0; try { kv = std::stoi(line); } catch (...) { kv = 0; } player.keys[kn] = kv; }
            }
            std::getline(in, line); int wc = std::stoi(line);
            warehouse.clear();
            for (int i = 0; i < wc; ++i) {
                LootItem loot; std::getline(in, loot.name);
                std::getline(in, line); loot.value = std::stoi(line);
                std::getline(in, line); loot.quantity = std::stoi(line);
                rebuildLootMeta(loot); warehouse.push_back(loot);
            }
            in.close();
            std::cout << "存档已加载。" << std::endl;
            return true;
        } catch (const std::exception& e) {
            std::cout << "存档读取失败:" << e.what() << std::endl; in.close(); return false;
        }
    }

    void mainMenu() {
        while (true) {
            std::cout << "\n==========================================\n";
            std::cout << "  硬核搜打撤 —— <<风暴>> v4.0\n";
            std::cout << "  Russian Hardcore Extraction\n";
            std::cout << "==========================================\n";
            std::cout << "1. 新游戏\n2. 读取存档\n3. 退出游戏\n请选择: ";
            int c = readInt();
            if (c == 1) { initPlayer(); safeHouseMenu(); return; }
            else if (c == 2) { if (loadGame()) { safeHouseMenu(); return; } }
            else if (c == 3) { printCredits(); return; }
        }
    }

    void refreshAntonovStock() {
        antonovAmmoStock.clear(); antonovArmorStock.clear();
        std::vector<Ammo> rare;
        for (auto& a : ammoDatabase) if (a.isRare) rare.push_back(a);
        if (!rare.empty()) {
            std::shuffle(rare.begin(), rare.end(), rng);
            int n = std::min(randInt(3, 5), (int)rare.size());
            for (int i = 0; i < n; ++i) { Ammo a = rare[i]; a.quantity = randInt(25, 90); antonovAmmoStock.push_back(a); }
        }
        if (!rareArmorList.empty()) {
            std::vector<BodyArmor> sh = rareArmorList;
            std::shuffle(sh.begin(), sh.end(), rng);
            int n = std::min(randInt(1, 2), (int)sh.size());
            for (int i = 0; i < n; ++i) antonovArmorStock.push_back(sh[i]);
        }
    }
    int antonovAmmoPrice(const Ammo& a) const { return a.pricePerRound * 2; }
    int antonovArmorPrice(const BodyArmor& ar) const { return ar.price + ar.price * 4 / 5; }

    void antonovShopMenu() {
        std::cout << "\n============ 黑市商人 · 安东诺夫 ============\n";
        if (!antonovStockInitialized) { refreshAntonovStock(); antonovStockInitialized = true; }
        while (true) {
            std::cout << "\n资金: " << player.money << " 卢布\n\n【稀有弹药】\n";
            if (antonovAmmoStock.empty()) std::cout << "  (暂无)\n";
            else for (size_t i = 0; i < antonovAmmoStock.size(); ++i) {
                const Ammo& a = antonovAmmoStock[i];
                std::cout << "  " << i + 1 << ". "; printAmmoNameColored(a.name, a.penetration);
                std::cout << " | 穿透 " << a.penetration << " | 库存 " << a.quantity << " 发 | 单价 " << antonovAmmoPrice(a) << "\n";
            }
            std::cout << "\n【稀有护甲】\n";
            if (antonovArmorStock.empty()) std::cout << "  (暂无)\n";
            else for (size_t i = 0; i < antonovArmorStock.size(); ++i) {
                const BodyArmor& ar = antonovArmorStock[i];
                std::cout << "  A" << i + 1 << ". "; printArmorNameColored(ar.name, ar.armorClass);
                std::cout << " | " << armorClassName(ar.armorClass) << " | " << ar.maxDurability << " | " << antonovArmorPrice(ar) << "\n";
            }
            std::cout << "\n【扩容背包】\n  B1. 简易行军包(容量 6)| 40000\n  B2. 军用背囊(容量 8)| 100000\n  B3. 特种作战背包(容量 12)| 250000\n";
            std::cout << "\n【高级装备】\n";
            if (player.headsetLevel < 2) {
                int price = (player.headsetLevel == 0) ? 40000 : 28000;
                std::cout << "  H. 高级战术耳机(遇敌预警 15%)| " << price << "\n";
            } else std::cout << "  已装备:高级战术耳机\n";
            std::cout << "\n操作: [数字]买弹药 | [A数字]买护甲 | [B数字]买包 | [H]耳机 | [R]刷新(" << ANTONOV_REFRESH_COST << ") | [0]离开\n请选择: ";
            std::string input; std::cin >> input;
            if (input == "0") break;
            else if (input == "H" || input == "h") {
                if (player.headsetLevel >= 2) { std::cout << "你已经有了最好的。\n"; pause(); continue; }
                int price = (player.headsetLevel == 0) ? 40000 : 28000;
                if (player.money < price) { std::cout << "钱不够。\n"; pause(); continue; }
                player.money -= price; player.headsetLevel = 2;
                std::cout << "已装备高级战术耳机。\n"; pause();
            } else if (input == "R" || input == "r") {
                if (player.money < ANTONOV_REFRESH_COST) { std::cout << "钱不够。\n"; pause(); continue; }
                player.money -= ANTONOV_REFRESH_COST; refreshAntonovStock();
                std::cout << "已刷新库存。\n"; pause();
            } else if (input.size() >= 2 && (input[0] == 'A' || input[0] == 'a')) {
                int idx = std::stoi(input.substr(1)) - 1;
                if (idx < 0 || idx >= (int)antonovArmorStock.size()) { std::cout << "无效。\n"; continue; }
                BodyArmor ar = antonovArmorStock[idx]; int price = antonovArmorPrice(ar);
                if (player.money < price) { std::cout << "钱不够。\n"; pause(); continue; }
                player.money -= price; player.armor = ar; player.armor.owned = true;
                std::cout << "已装备 "; printArmorNameColored(ar.name, ar.armorClass); std::cout << "。\n"; pause();
            } else if (input.size() >= 2 && (input[0] == 'B' || input[0] == 'b')) {
                int idx = std::stoi(input.substr(1)); int nc = 0, price = 0;
                if (idx == 1) { nc = 6; price = 40000; }
                else if (idx == 2) { nc = 8; price = 100000; }
                else if (idx == 3) { nc = 12; price = 250000; }
                else { std::cout << "无效。\n"; continue; }
                if (player.lootCapacity >= nc) { std::cout << "已有更好的包。\n"; pause(); continue; }
                if (player.money < price) { std::cout << "钱不够。\n"; pause(); continue; }
                player.money -= price; player.lootCapacity = nc;
                std::cout << "已换包,容量 " << nc << " 件。\n"; pause();
            } else {
                try {
                    int idx = std::stoi(input) - 1;
                    if (idx < 0 || idx >= (int)antonovAmmoStock.size()) { std::cout << "无效。\n"; continue; }
                    Ammo& a = antonovAmmoStock[idx]; int unit = antonovAmmoPrice(a);
                    std::cout << "购买多少发?(0取消): ";
                    int qty = readInt();
                    if (qty <= 0 || qty > a.quantity) continue;
                    int total = qty * unit;
                    if (player.money < total) { std::cout << "资金不足。\n"; pause(); continue; }
                    player.money -= total; a.quantity -= qty;
                    if (player.weapon.magazineSize > 0 && player.weapon.caliber == a.caliber) {
                        int sp = player.weapon.magazineSize - player.weapon.currentAmmo;
                        int load = std::min(sp, qty); player.weapon.currentAmmo += load;
                        int backup = qty - load;
                            if (!player.weapon.compatibleAmmo.empty() && player.weapon.compatibleAmmo[0].name == a.name)
                                player.weapon.compatibleAmmo[0].quantity += backup;
                            else { Ammo c = a; c.quantity = backup; player.weapon.compatibleAmmo.clear(); player.weapon.compatibleAmmo.push_back(c); }
                        std::cout << "已买 " << qty << " 发,装填 " << load << ",备用 " << backup << "。\n";
                    } else {
                        std::cout << "已买 " << qty << " 发(记入备用)。\n";
                        Ammo c = a; c.quantity = qty; player.weapon.compatibleAmmo.clear(); player.weapon.compatibleAmmo.push_back(c);
                    }
                    if (a.quantity <= 0) antonovAmmoStock.erase(antonovAmmoStock.begin() + idx);
                    pause();
                } catch (...) { std::cout << "无效输入。\n"; }
            }
        }
    }

    void bankMenu() {
        std::cout << "\n============ 边境信贷银行 ============\n";
        while (true) {
            int avail = BANK_LOAN_MAX - player.debt; if (avail < 0) avail = 0;
            std::cout << "资金: " << player.money << " | 债务: " << player.debt << " | 可再借: " << avail << "\n1. 借款 | 2. 还款 | 0. 离开\n请选择: ";
            int c = readInt();
            if (c == 0) break;
            else if (c == 1) {
                if (avail <= 0) { std::cout << "已到上限。\n"; pause(); continue; }
                std::cout << "借款金额(1~" << avail << ",0取消): ";
                int amount = readInt();
                if (amount <= 0 || amount > avail) continue;
                player.money += amount; player.debt += amount;
                std::cout << "已借款 " << amount << ",债务 " << player.debt << "。\n"; pause();
            } else if (c == 2) {
                if (player.debt <= 0) { std::cout << "无债务。\n"; pause(); continue; }
                int mp = std::min(player.debt, player.money);
                if (mp <= 0) { std::cout << "没有钱可还。\n"; pause(); continue; }
                std::cout << "还款金额(1~" << mp << ",0取消): ";
                int amount = readInt();
                if (amount <= 0 || amount > mp) continue;
                player.money -= amount; player.debt -= amount;
                std::cout << "已还款 " << amount << ",剩余债务 " << player.debt << "。\n"; pause();
            }
        }
    }
    void settleBankInterest() {
        if (player.debt <= 0) return;
        int interest = (int)std::ceil(player.debt * BANK_INTEREST_RATE);
        if (interest < 1) interest = 1;
        player.debt += interest;
        std::cout << "\n[银行] 利息 +" << interest << ",当前债务 " << player.debt << "。\n"; pause();
    }

    void dialogueWhyHere() {
        std::cout << std::endl;
        if (player.role == Role::KATYUSHA) {
            std::cout << "喀秋莎沉默了一下,手指无意识地摩挲着枪背带。\n\"为了活下去。\"\n\"家里的暖气停了,母亲病了,弟弟还小。有人说这里有活路……所以我来了。\"\n\"风暴把所有人都卷了进来,我也一样。\"" << std::endl;
        } else if (player.role == Role::TOKAREV) {
            std::cout << "托卡列夫咧嘴一笑,眼角的皱纹挤在一起。\n\"我?老兵不死,只是换个地方挣钱。\"\n\"阿富汗、车臣、叙利亚……战场是我的老地方。\"\n\"但这地方不一样。这里每个人都是猎物,也是猎人。\"" << std::endl;
        } else if (player.role == Role::ANNA) {
            std::cout << "安娜的眼神变得锐利,手指停在背包的搭扣上。\n\"我在找我的队友。\"\n\"他们进了这个区域,然后就再没出来。\"\n\"定位器最后的信号就在这片区域,我不能放弃他们。\"\n\"……而且,有些事我必须搞清楚。\"" << std::endl;
        } else if (player.role == Role::ALEXANDER) {
            std::cout << "亚历山大扯了扯手套,语气平静得像是在报天气。\n\"有人出钱,让我来这里把'风暴'搅个天翻地覆。\"\n\"突击位嘛,第一个冲进去,最后一个退出来。\"\n\"这里的每一颗子弹,都会记在账上。\"\n\"至于私事……我答应过一个人,会活着回去。\"" << std::endl;
        } else if (player.role == Role::IVAN) {
            std::cout << "伊万吐出一口烟,把AK往肩上一扛。\n\"钱。就这么简单。\"\n\"我在美国待了十年,拿了绿卡,学了一口烂英语。\"\n\"然后发现——哪儿都一样。有活就干,有钱就赚。\"\n\"风暴?Хорошо. 这地方活儿多,钱也多。\"" << std::endl;
        }
        pause();
    }

    void dialogueWhereIsThis() {
        std::cout << std::endl;
        if (player.role == Role::KATYUSHA) {
            std::cout << "喀秋莎抬起头,看了看四周破败的建筑。\n\"边境的废墟——废弃的村庄、仓库、码头……\"\n\"这些地方曾经有人住,现在只剩枪声和弹壳。\"\n\"我从西伯利亚一路南下,走的都是这种地方。\"\n\"这里……是风暴的中心吧。\"" << std::endl;
        } else if (player.role == Role::TOKAREV) {
            std::cout << "托卡列夫从口袋里掏出一支没点的烟,在指尖转了一圈。\n\"苏联解体后烂掉的边境。没人管的地方。\"\n\"走私犯、逃兵、亡命徒……都往这儿钻。\"\n\"我们管它叫'风暴'——进了这儿,就别想干干净净地出去。\"" << std::endl;
        } else if (player.role == Role::ANNA) {
            std::cout << "安娜压低声音,像是怕被人听见。\n\"我们管它叫'风暴'。一个被战争撕碎的地方。\"\n\"这里曾经是苏联的实验区。机密文件、武器、人体实验……全都埋在地底下。\"\n\"那些雇佣兵不是在抢物资——他们在掩盖什么。\"" << std::endl;
        } else if (player.role == Role::ALEXANDER) {
            std::cout << "亚历山大靠着墙,扫视了一圈远处的地平线。\n\"风暴——军事承包商、走私集团、情报掮客……\"\n\"没人真正拥有这里,谁拳头硬谁说了算。\"\n\"有人说这里是坟场,也有人说这里是金矿。\"\n\"在我看来——这里是个靶场。\"" << std::endl;
        } else if (player.role == Role::IVAN) {
            std::cout << "伊万环顾四周,嗤笑了一声。\n\"Посмотри на это. 跟车臣郊区差不多。\"\n\"战争结束后的烂摊子,永远有人捡。\"\n\"俄罗斯人、美国人、本地人……都往这儿挤。\"\n\"我只关心一件事——谁付钱,我打谁。\"" << std::endl;
        }
        pause();
    }

    void dialogueHowAreYou() {
        std::cout << std::endl;
        double ratio = (player.maxHealth > 0) ? (double)player.health / player.maxHealth : 0.0;
        if (player.role == Role::KATYUSHA) {
            if (ratio >= 0.9) { std::cout << "喀秋莎活动了一下肩膀,检查了一下弹匣。\n\"我没事,还能继续。\"" << std::endl; }
            else if (ratio >= 0.6) { std::cout << "喀秋莎按了按肋部,轻轻皱了下眉。\n\"皮外伤而已,子弹擦过的时候我躲得快。\"" << std::endl; }
            else if (ratio >= 0.3) { std::cout << "喀秋莎靠着墙,呼吸有些急促。\n\"……疼。但只要还能站起来,我就得往前走。\"" << std::endl; }
            else { std::cout << "喀秋莎的手在微微发抖,但她仍然紧握着武器。\n\"我……我还能撑住。别管我,继续。\"" << std::endl; }
        } else if (player.role == Role::TOKAREV) {
            if (ratio >= 0.9) { std::cout << "托卡列夫拍了拍自己的胸口。\n\"老骨头还硬着呢。\"" << std::endl; }
            else if (ratio >= 0.6) { std::cout << "托卡列夫吐了口唾沫,擦掉脸上的血。\n\"这点伤……在阿富汗,那是休息的借口。\"" << std::endl; }
            else if (ratio >= 0.3) { std::cout << "托卡列夫扶着墙,努力让自己站直。\n\"该死……我大概真老了。\"" << std::endl; }
            else { std::cout << "托卡列夫勉强挤出一个笑容,但嘴角的抽搐出卖了他。\n\"我……我还能打。别小看老兵。\"" << std::endl; }
        } else if (player.role == Role::ANNA) {
            if (ratio >= 0.9) { std::cout << "安娜简单做了几个拉伸动作。\n\"状态良好,随时可以行动。\"" << std::endl; }
            else if (ratio >= 0.6) { std::cout << "安娜检查了一下伤口,简单包扎了一下。\n\"轻伤,不影响战斗。\"" << std::endl; }
            else if (ratio >= 0.3) { std::cout << "安娜的呼吸变得粗重,眼神却依然坚定。\n\"我知道我撑不了多久了。但只要还有一口气,我就不会停下。\"" << std::endl; }
            else { std::cout << "安娜闭了闭眼,再次睁开时瞳孔已经收紧。\n\"我还活着……就够了。\"" << std::endl; }
        } else if (player.role == Role::ALEXANDER) {
            if (ratio >= 0.9) { std::cout << "亚历山大敲了敲自己的胸甲,发出闷响。\n\"完好。随时可以上。\"" << std::endl; }
            else if (ratio >= 0.6) { std::cout << "亚历山大用手背抹掉脸上的尘土。\n\"小伤,不碍事。突击位,皮实。\"" << std::endl; }
            else if (ratio >= 0.3) { std::cout << "亚历山大呼吸急促,但从怀里摸出闪光弹,看了一眼。\n\"快到了……该我出手了。\"" << std::endl; }
            else { std::cout << "亚历山大单膝跪地,拇指按在闪光弹保险环上。\n\"还没……还没到终点。\"" << std::endl; }
        } else if (player.role == Role::IVAN) {
            if (ratio >= 0.9) { std::cout << "伊万活动了一下脖子,骨节咔咔响。\n\"Отлично. 没问题。\"" << std::endl; }
            else if (ratio >= 0.6) { std::cout << "伊万拍了拍身上的灰。\n\"还行。打过更狠的。\"" << std::endl; }
            else if (ratio >= 0.3) { std::cout << "伊万靠着墙,喘了口气。\n\"Сука……这一下有点重。不过还能干。\"" << std::endl; }
            else { std::cout << "伊万咳出一口血,咧开嘴笑了。\n\"还没死呢。Давай, 继续。\"" << std::endl; }
        }
        pause();
        std::cout << "\n【伤势检查】" << std::endl;
        bool any = false;
        if (player.hasBuff(BuffType::BLEEDING)) {
            any = true;
            if (player.role == Role::KATYUSHA) std::cout << "  喀秋莎低头看了看渗血的绷带:\"血止不住……但我还有几卷绷带。\"" << std::endl;
            else if (player.role == Role::TOKAREV) std::cout << "  托卡列夫嗤了一声:\"流点血……能洗洗身上的晦气。\"" << std::endl;
            else if (player.role == Role::ANNA) std::cout << "  安娜按住了伤口:\"血在流……我不想浪费绷带。再撑一会儿。\"" << std::endl;
            else if (player.role == Role::IVAN) std::cout << "  伊万低头看了一眼:\"Кровь... 流点血不要紧。\"" << std::endl;
            else std::cout << "  亚历山大用力压住伤口:\"小伤。继续。\"" << std::endl;
            pause();
        }
        if (player.hasBuff(BuffType::HEAVY_BLEEDING)) {
            any = true;
            if (player.role == Role::KATYUSHA) std::cout << "  喀秋莎按着喷涌的伤口:\"血……止不住……止不住……\"" << std::endl;
            else if (player.role == Role::TOKAREV) std::cout << "  托卡列夫脸色发白:\"这血……堵不住……\"" << std::endl;
            else if (player.role == Role::ANNA) std::cout << "  安娜喘着气:\"严重出血……快……快包扎……\"" << std::endl;
            else if (player.role == Role::IVAN) std::cout << "  伊万低声骂了一句:\"Сука……血太多了……\"" << std::endl;
            else std::cout << "  亚历山大咬着牙:\"严重出血……得赶紧处理。\"" << std::endl;
            pause();
        }
        if (player.hasBuff(BuffType::FRACTURE)) {
            any = true;
            if (player.role == Role::KATYUSHA) std::cout << "  喀秋莎试探性地活动了一下腿:\"……不碍事。我咬着牙也能跑。\"" << std::endl;
            else if (player.role == Role::TOKAREV) std::cout << "  托卡列夫摸了摸断腿:\"老骨头又断了一根。习惯了。\"" << std::endl;
            else if (player.role == Role::ANNA) std::cout << "  安娜低头看了一眼腿:\"腿断了,但手还能扣扳机。\"" << std::endl;
            else if (player.role == Role::IVAN) std::cout << "  伊万拍了拍断腿:\"Сука... 骨头断了。不过还能走。\"" << std::endl;
            else std::cout << "  亚历山大试了试腿:\"能走。还不到停下的时候。\"" << std::endl;
            pause();
        }
        if (player.hasBuff(BuffType::STOMACH_INJURY)) {
            any = true;
            if (player.role == Role::KATYUSHA) std::cout << "  喀秋莎捂着小腹:\"肚子……被穿透了。走两步就喘。\"" << std::endl;
            else if (player.role == Role::TOKAREV) std::cout << "  托卡列夫皱着眉头:\"肚子上的伤最烦人。吃不下饭。\"" << std::endl;
            else if (player.role == Role::ANNA) std::cout << "  安娜咬紧牙关:\"腹部的伤……我不能再跑了。只能打。\"" << std::endl;
            else if (player.role == Role::IVAN) std::cout << "  伊万按住肚子:\"Живот... 麻烦。不过还能撑。\"" << std::endl;
            else std::cout << "  亚历山大按住腹部:\"……忍着。\"" << std::endl;
            pause();
        }
        if (player.hasBuff(BuffType::PAIN) && !player.hasBuff(BuffType::PAINKILLER)) {
            any = true;
            if (player.role == Role::KATYUSHA) std::cout << "  喀秋莎脸上渗出冷汗:\"疼……但忍得住。\"" << std::endl;
            else if (player.role == Role::TOKAREV) std::cout << "  托卡列夫倒吸一口凉气:\"疼得够呛……哼,小意思。\"" << std::endl;
            else if (player.role == Role::ANNA) std::cout << "  安娜皱着眉:\"……很痛。但没用。\"" << std::endl;
            else if (player.role == Role::IVAN) std::cout << "  伊万咬着牙:\"Больно... 不过死不了。\"" << std::endl;
            else std::cout << "  亚历山大咬着牙:\"疼,但没到叫出来的程度。\"" << std::endl;
            pause();
        }
        if (player.hasBuff(BuffType::STUN)) {
            any = true;
            std::cout << "  " << player.name << " 摇了摇头,眼前还在发花:\"……眩晕还没消。\"" << std::endl;
            pause();
        }
        if (player.role == Role::ANNA && player.hasRevived) {
            any = true;
            std::cout << "  安娜沉默了一会儿:\"你看到的是我第二次站起来了。\"\n  \"下一次……我不知道还能不能。\"" << std::endl; pause();
        }
        if (player.role == Role::ALEXANDER && player.isAlexanderDesperate()) {
            any = true;
            std::cout << "  亚历山大低声笑了笑:\"血少了,反而清醒了。\"\n  \"钢铁意志已激活——现在,什么debuff都拦不住我。\"" << std::endl;
            if (!player.flashbangUsed) std::cout << "  \"闪光弹还在。下一次交火,就是我的回合。\"" << std::endl;
            else std::cout << "  \"闪光弹用过了……但拳头还在。\"" << std::endl;
            pause();
        }
        if (player.role == Role::IVAN && player.isIvanDeathRush()) {
            any = true;
            std::cout << "  伊万低吼了一声:\"死亡突袭……现在才开始。\"" << std::endl; pause();
        }
        if (!any) std::cout << "  " << player.name << " 检查了一下全身:\"没有明显的外伤。\"" << std::endl;
        bool low = false;
        for (int i = 0; i < (int)BodyPart::COUNT; ++i) {
            BodyPart bp = (BodyPart)i;
            auto it = player.partHealth.find(bp), mt = player.partMaxHealth.find(bp);
            if (it != player.partHealth.end() && mt != player.partMaxHealth.end() && mt->second > 0 && (double)it->second / mt->second < 0.30) {
                if (!low) { std::cout << "\n【部位严重受损】" << std::endl; low = true; }
                std::cout << "  " << bodyPartName(bp) << ":" << it->second << "/" << mt->second << "(极度危险)" << std::endl;
            }
        }
        if (low) { std::cout << player.name << " 深吸了一口气:\"……我得小心点。\"" << std::endl; pause(); }
        if (player.hasAnyInjury()) {
            int cost = player.calculateTreatmentCost();
            if (cost > 0) std::cout << "\n  【提示】当前伤势找海伦医生治疗需要约 " << cost << " 卢布。" << std::endl;
        }
        pause();
    }

    void characterDialogueMenu() {
        std::cout << "\n============ 与 " << player.name << " 的对话 ============\n";
        while (true) {
            std::cout << "\n你想说什么?\n1. \"你为什么来到这种地方?\"\n2. \"这里到底是哪里?\"\n3. \"你现在感觉怎么样?\"(查看伤势)\n0. 结束对话\n请选择: ";
            int c = readInt();
            if (c == 0) { std::cout << player.name << " 点了点头,转身去整理装备了。" << std::endl; break; }
            else if (c == 1) dialogueWhyHere();
            else if (c == 2) dialogueWhereIsThis();
            else if (c == 3) dialogueHowAreYou();
        }
    }

    void helenDoctorMenu() {
        std::cout << "\n============ 海伦医生的诊所 ============\n";
        std::cout << "海伦医生抬起头:\"又是你啊。坐吧,让我看看你的伤。\"" << std::endl; pause();
        while (true) {
            int cost = player.calculateTreatmentCost();
            std::cout << "\n--------- 海伦医生的诊断 ---------\n";
            std::cout << "资金: " << player.money << " 卢布 | 生命: " << player.health << "/" << player.maxHealth << "\n伤势报告:\n";
            bool any = false;
            if (player.health < player.maxHealth) { std::cout << "  - 全身血量损失 " << (player.maxHealth - player.health) << " 点\n"; any = true; }
            for (int i = 0; i < (int)BodyPart::COUNT; ++i) {
                BodyPart bp = (BodyPart)i;
                auto it = player.partHealth.find(bp), mt = player.partMaxHealth.find(bp);
                if (it != player.partHealth.end() && mt != player.partMaxHealth.end() && it->second < mt->second) {
                    std::cout << "  - " << bodyPartName(bp) << ":" << it->second << "/" << mt->second << "\n"; any = true;
                }
            }
            if (!player.buffs.empty()) {
                std::cout << "  - 状态异常: ";
                for (auto& kv : player.buffs) { if (kv.second <= 0) continue; if (kv.first == BuffType::PAINKILLER || kv.first == BuffType::ADRENALINE) continue; std::cout << buffTypeName(kv.first) << "(" << buffDurationStr(kv.second) << ") "; }
                std::cout << std::endl; any = true;
            }
            if (player.role == Role::ANNA && player.hasRevived) { std::cout << "  - 【安娜·自救后遗症】\n"; any = true; }
            if (!any) std::cout << "  (无明显伤势)\n";
            printBodyFigure(player);
            if (cost == 0) {
                std::cout << "\n海伦医生: \"你没什么事。下次小心点。\"\n\n[0] 离开\n请选择: ";
                int c = readInt(); if (c == 0) break; continue;
            }
            std::cout << "\n海伦医生: \"全部治好的话," << cost << " 卢布。\"\n1. 接受治疗 | 0. 离开\n请选择: ";
            int c = readInt();
            if (c == 0) { pause(); break; }
            if (c == 1) {
                if (player.money < cost) { std::cout << "海伦医生: \"你的钱不够。\"" << std::endl; pause(); continue; }
                player.money -= cost;
                std::cout << "\n海伦医生戴上手套,动作利落地处理着每一处伤口。" << std::endl; pause();
                player.health = player.maxHealth;
                for (auto& kv : player.partHealth) { auto mt = player.partMaxHealth.find(kv.first); if (mt != player.partMaxHealth.end()) kv.second = mt->second; }
                player.buffs.clear();
                if (player.role == Role::ANNA && player.hasRevived) {
                    player.maxHealth = 100; player.health = 100; player.hasRevived = false;
                    std::cout << "海伦医生: \"你的身体被透支得太厉害了……不过现在,总算是修补好了。\"" << std::endl; pause();
                }
                std::cout << "你支付了 " << cost << " 卢布。当前资金: " << player.money << " 卢布。" << std::endl; pause();
            }
        }
    }

    void shopKeysAndGear() {
        while (true) {
            std::cout << "\n============ 钥匙与装备商店 ============\n资金: " << player.money << " 卢布\n\n【钥匙】\n";
            for (size_t i = 0; i < keyShop.size(); ++i)
                std::cout << "  " << i + 1 << ". " << keyShop[i].name << " | " << keyShop[i].description << " | " << keyShop[i].price << " 卢布\n";
            std::cout << "\n【耳机】\n";
            if (player.headsetLevel == 0) {
                std::cout << "  H1. 战术耳机(遇敌预警 10%)| 12000 卢布\n  (高级耳机只在安东诺夫黑市出售)\n";
            } else if (player.headsetLevel == 1) {
                std::cout << "  已装备:战术耳机(遇敌预警 10%)\n  (高级耳机只在安东诺夫黑市出售)\n";
            } else std::cout << "  已装备:高级战术耳机(遇敌预警 15%)\n";
            std::cout << "\n操作: [数字]买钥匙 | [H1]买战术耳机 | [0]离开\n请选择: ";
            std::string input; std::cin >> input;
            if (input == "0") break;
            else if (input == "H1" || input == "h1") {
                if (player.headsetLevel != 0) { std::cout << "你已经装备耳机了。" << std::endl; pause(); continue; }
                if (player.money < 12000) { std::cout << "资金不足。" << std::endl; pause(); continue; }
                player.money -= 12000; player.headsetLevel = 1;
                std::cout << "已装备战术耳机(遇敌预警 10%)。" << std::endl; pause();
            } else {
                try {
                    int idx = std::stoi(input) - 1;
                    if (idx < 0 || idx >= (int)keyShop.size()) { std::cout << "无效。" << std::endl; continue; }
                    KeyProduct& k = keyShop[idx];
                    if (player.money < k.price) { std::cout << "资金不足。" << std::endl; pause(); continue; }
                    player.money -= k.price; player.keys[k.name]++;
                    std::cout << "已购买 " << k.name << " x1。当前持有 " << player.keys[k.name] << " 把。" << std::endl; pause();
                } catch (...) { std::cout << "无效输入。" << std::endl; }
            }
        }
    }

    void shopHelmets() {
        while (true) {
            std::cout << "\n===== 头盔商店 =====\n资金: " << player.money << "\n";
            for (size_t i = 0; i < helmetDatabase.size(); ++i) {
                const Helmet& h = helmetDatabase[i];
                std::cout << i + 1 << ". "; printArmorNameColored(h.name, h.armorClass);
                std::cout << " | " << armorClassName(h.armorClass)
                          << " | 耐久 " << h.maxDurability
                          << " | 跳弹 " << (int)(h.ricochetChance * 100) << "%"
                          << " | " << h.price << "\n";
            }
            std::cout << "0. 返回 | 选择: ";
            int c = readInt();
            if (c == 0) break;
            if (c < 1 || c > (int)helmetDatabase.size()) continue;
            Helmet& h = helmetDatabase[c - 1];
            if (player.money < h.price) { std::cout << "资金不足。\n"; continue; }
            player.money -= h.price;
            player.helmet = h;
            player.helmet.owned = true;
            std::cout << "已装备 " << h.name << "。\n";
        }
    }

    void shopStimulators() {
        while (true) {
            std::cout << "\n===== 针剂商店 =====\n资金: " << player.money << "\n";
            for (size_t i = 0; i < stimulatorShop.size(); ++i) {
                const Stimulator& s = stimulatorShop[i];
                std::cout << i + 1 << ". " << s.name << " - " << s.description << " | " << s.price << "\n";
            }
            std::cout << "0. 返回 | 选择: ";
            int c = readInt();
            if (c == 0) break;
            if (c < 1 || c > (int)stimulatorShop.size()) continue;
            Stimulator& s = stimulatorShop[c - 1];
            if (player.money < s.price) { std::cout << "资金不足。\n"; continue; }
            player.money -= s.price;
            player.stimulators.push_back(s);
            std::cout << "已购买 " << s.name << "。\n";
        }
    }

    void safeHouseMenu() {
        while (true) {
            std::cout << "\n============ 藏身处 ============\n资金: " << player.money << " 卢布";
            if (player.safeDeposit > 0) std::cout << "(保险柜 " << player.safeDeposit << ")";
            std::cout << "\n生命: " << player.health << "/" << player.maxHealth;
            if (player.hasAnyInjury()) std::cout << "  【有伤在身】";
            std::cout << std::endl;
            printBodyFigure(player);
            std::cout << "背包: " << player.lootBag.size() << " / " << player.lootCapacity << std::endl;
            if (player.debt > 0) std::cout << "债务: " << player.debt << " 卢布(利率 2%/出击前结算)" << std::endl;
            if (player.role == Role::KATYUSHA) std::cout << "闪避: " << (int)(player.dodgeChance * 100) << "% / 15%" << std::endl;
            if (player.role == Role::ANNA && player.hasRevived) std::cout << "【向死而生】已激活(伤害 +10%),血量上限 80,需治疗" << std::endl;
            if (player.role == Role::IVAN) std::cout << "震爆弹: " << player.stunGrenades << "/2(战斗中受伤≥15 生成)" << std::endl;
            if (player.headsetLevel > 0) std::cout << "耳机: " << player.getHeadsetName() << "(遇敌预警 " << player.getHeadsetWarningChance() << "%)" << std::endl;
            std::cout << "1. 武器商店\n2. 弹药商店\n3. 防弹衣商店\n4. 头盔商店\n5. 医疗商店\n6. 针剂商店\n7. 出售战利品\n8. 海伦医生(治疗)\n9. 黑市商人 · 安东诺夫\n10. 边境信贷银行\n11. 查看状态\n12. 与 " << player.name << " 对话\n13. 进入战区\n14. 仓库\n15. 保存游戏\n16. 钥匙与装备商店\n17. 返回主菜单\n请选择: ";
            int choice = readInt();
            if (choice == 1) shopWeapons();
            else if (choice == 2) shopAmmo();
            else if (choice == 3) shopArmor();
            else if (choice == 4) shopHelmets();
            else if (choice == 5) shopMedical();
            else if (choice == 6) shopStimulators();
            else if (choice == 7) sellLoot();
            else if (choice == 8) helenDoctorMenu();
            else if (choice == 9) antonovShopMenu();
            else if (choice == 10) bankMenu();
            else if (choice == 11) { player.printStatus(); printLootBag(); }
            else if (choice == 12) characterDialogueMenu();
            else if (choice == 13) {
                if (player.weapon.magazineSize == 0) { std::cout << "你没有武器!" << std::endl; continue; }
                if (player.weapon.currentAmmo == 0 && (player.weapon.compatibleAmmo.empty() || player.weapon.compatibleAmmo[0].quantity == 0)) { std::cout << "你没有弹药!" << std::endl; continue; }
                if (player.health <= 10) { std::cout << "你伤得太重,先去找海伦医生治疗吧。" << std::endl; continue; }
                settleBankInterest();
                int mapIdx = chooseMap();
                if (mapIdx < 0) continue;
                currentMap = mapDatabase[mapIdx];
                if (player.money > currentMap.maxEntryMoney) {
                    int ov = player.money - currentMap.maxEntryMoney;
                    player.safeDeposit += ov; player.money = currentMap.maxEntryMoney;
                    std::cout << "\n[入局资金管理] 携带上限 " << currentMap.maxEntryMoney << " 卢布," << ov << " 卢布已自动存入保险柜。" << std::endl;
                    std::cout << "  当前携带: " << player.money << " | 保险柜: " << player.safeDeposit << std::endl; pause();
                }
                raid();
                resetForSafeHouse();
            }
            else if (choice == 14) warehouseMenu();
            else if (choice == 15) saveGame();
            else if (choice == 16) shopKeysAndGear();
            else if (choice == 17) { std::cout << "返回主菜单。" << std::endl; return; }
        }
    }

    int chooseMap() {
        std::cout << "\n============ 选择战区 ============\n";
        for (size_t i = 0; i < mapDatabase.size(); ++i) {
            const Map& m = mapDatabase[i];
            std::cout << "\n[" << i + 1 << "] " << m.name << "\n   难度: " << m.difficulty << "/5\n   描述: " << m.description
                      << "\n   容器数: " << m.minContainers << "-" << m.maxContainers << " | 危险度: " << m.minDanger << "-" << m.maxDanger
                      << "\n   【携带资金上限】" << m.maxEntryMoney << " 卢布\n";
            if (m.difficulty >= 3) std::cout << "   【Boss 刷新概率】" << BOSS_SPAWN_CHANCE << "%\n";
        }
        std::cout << "\n[0] 返回藏身处\n选择要前往的地图: ";
        int c = readInt();
        if (c == 0 || c < 1 || c > (int)mapDatabase.size()) return -1;
        return c - 1;
    }

    void printLootBag() {
        if (player.lootBag.empty()) { std::cout << "口袋: 空" << std::endl; return; }
        std::cout << "口袋 (" << player.lootBag.size() << " / " << player.lootCapacity << "):" << std::endl;
        for (size_t i = 0; i < player.lootBag.size(); ++i) {
            std::cout << "  [" << i + 1 << "] "; printLootNameColored(player.lootBag[i]);
            if (player.lootBag[i].quantity > 1) std::cout << " x" << player.lootBag[i].quantity;
            std::cout << "  价值 " << player.lootBag[i].value << std::endl;
        }
    }

    void warehouseMenu() {
        while (true) {
            std::cout << "\n============ 仓库 ============\n1. 查看仓库 | 2. 存入 | 3. 取出 | 0. 返回\n请选择: ";
            int c = readInt();
            if (c == 0) break;
            else if (c == 1) printWarehouse();
            else if (c == 2) depositToWarehouse();
            else if (c == 3) withdrawFromWarehouse();
        }
    }
    void printWarehouse() {
        if (warehouse.empty()) { std::cout << "仓库: 空" << std::endl; return; }
        std::cout << "仓库 (" << warehouse.size() << " 件):" << std::endl;
        for (size_t i = 0; i < warehouse.size(); ++i) {
            std::cout << "  [" << i + 1 << "] "; printLootNameColored(warehouse[i]);
            if (warehouse[i].quantity > 1) std::cout << " x" << warehouse[i].quantity;
            std::cout << "  价值 " << warehouse[i].value << std::endl;
        }
    }
    void depositToWarehouse() {
        if (player.lootBag.empty()) { std::cout << "口袋是空的。" << std::endl; return; }
        printLootBag();
        std::cout << "存入编号(0=全部,-1取消): ";
        int c = readInt();
        if (c == 0) { for (auto& l : player.lootBag) warehouse.push_back(l); player.lootBag.clear(); }
        else if (c >= 1 && c <= (int)player.lootBag.size()) { warehouse.push_back(player.lootBag[c - 1]); player.lootBag.erase(player.lootBag.begin() + c - 1); }
    }
    void withdrawFromWarehouse() {
        if (warehouse.empty()) { std::cout << "仓库是空的。" << std::endl; return; }
        printWarehouse();
        std::cout << "取出编号(0=全部,-1取消): ";
        int c = readInt();
        if (c == 0) {
            if ((int)player.lootBag.size() + (int)warehouse.size() > player.lootCapacity) { std::cout << "背包容量不足。" << std::endl; return; }
            for (auto& l : warehouse) player.lootBag.push_back(l); warehouse.clear();
        } else if (c >= 1 && c <= (int)warehouse.size()) {
            if ((int)player.lootBag.size() >= player.lootCapacity) { std::cout << "背包已满。" << std::endl; return; }
            player.lootBag.push_back(warehouse[c - 1]); warehouse.erase(warehouse.begin() + c - 1);
        }
    }

    void shopWeapons() {
        while (true) {
            std::cout << "\n===== 武器商店 =====\n资金: " << player.money << std::endl;
            for (size_t i = 0; i < weaponDatabase.size(); ++i) {
                std::cout << i + 1 << ". ";
                printColoredText(weaponDatabase[i].name, lootValueColor(weaponDatabase[i].price));
                std::cout << " | " << weaponDatabase[i].category << " | " << weaponDatabase[i].caliber
                          << " | 射速 " << weaponDatabase[i].fireRate << " | " << weaponDatabase[i].price << std::endl;
            }
            std::cout << "0. 返回 | 选择: ";
            int c = readInt();
            if (c == 0) break;
            if (c < 1 || c > (int)weaponDatabase.size()) continue;
            Weapon& w = weaponDatabase[c - 1];
            if (player.money < w.price) { std::cout << "资金不足。" << std::endl; continue; }
            player.money -= w.price; player.weapon = w; player.weapon.currentAmmo = 0;
            player.boltCooldown = 0;
            std::cout << "已购买 "; printColoredText(w.name, lootValueColor(w.price));
            std::cout << "。当前资金 " << player.money << " 卢布。" << std::endl;
        }
    }

    void shopAmmo() {
        if (player.weapon.magazineSize == 0) { std::cout << "你还没有武器。" << std::endl; return; }
        while (true) {
            std::cout << "\n===== 弹药商店 (" << player.weapon.name << " / " << player.weapon.caliber << ") =====\n";
            std::cout << "资金: " << player.money << "(稀有子弹只在安东诺夫处出售)\n";
            std::vector<Ammo> av;
            for (auto& a : getAmmoByCaliber(player.weapon.caliber)) if (!a.isRare) av.push_back(a);
            if (av.empty()) { std::cout << "(没有普通弹药出售。)\n0. 返回 | 选择: "; int c = readInt(); if (c == 0) break; continue; }
            for (size_t i = 0; i < av.size(); ++i) {
                std::cout << i + 1 << ". "; printAmmoNameColored(av[i].name, av[i].penetration);
                std::cout << " | " << ammoTypeName(av[i].type) << " | 伤害 " << av[i].damage << " | 穿透 " << av[i].penetration
                          << " | " << av[i].pricePerRound << "/发\n";
            }
            std::cout << "0. 返回 | 选择弹药: ";
            int c = readInt();
            if (c == 0) break;
            if (c < 1 || c > (int)av.size()) continue;
            Ammo sel = av[c - 1];
            int ma = player.money / std::max(1, sel.pricePerRound);
            std::cout << "买多少发?(最多 " << ma << ",0取消): ";
            int qty = readInt();
            if (qty <= 0) continue;
            int cost = qty * sel.pricePerRound;
            if (cost > player.money) { std::cout << "资金不足。" << std::endl; continue; }
            player.money -= cost;
            int sp = player.weapon.magazineSize - player.weapon.currentAmmo;
            int load = std::min(sp, qty); player.weapon.currentAmmo += load;
            int backup = qty - load;
                if (!player.weapon.compatibleAmmo.empty() && player.weapon.compatibleAmmo[0].name == sel.name)
                    player.weapon.compatibleAmmo[0].quantity += backup;
                else { player.weapon.compatibleAmmo.clear(); sel.quantity = backup; player.weapon.compatibleAmmo.push_back(sel); }
            std::cout << "已买 " << qty << " 发 "; printAmmoNameColored(sel.name, sel.penetration);
            std::cout << ",装填 " << load << ",备用 " << backup << " 发。" << std::endl;
        }
    }

    void shopArmor() {
        while (true) {
            std::cout << "\n===== 防弹衣商店(普通) =====\n资金: " << player.money << "(5 级以上只在安东诺夫处出售)\n";
            for (size_t i = 0; i < armorShop.size(); ++i) {
                std::cout << i + 1 << ". "; printArmorNameColored(armorShop[i].name, armorShop[i].armorClass);
                std::cout << " | 等级 " << armorClassName(armorShop[i].armorClass) << " | 耐久 " << armorShop[i].maxDurability << " | " << armorShop[i].price << "\n";
            }
            std::cout << "0. 返回 | 选择: ";
            int c = readInt();
            if (c == 0) break;
            if (c < 1 || c > (int)armorShop.size()) continue;
            BodyArmor& a = armorShop[c - 1];
            if (player.money < a.price) { std::cout << "资金不足。" << std::endl; continue; }
            player.money -= a.price; player.armor = a; player.armor.owned = true;
            std::cout << "已装备 "; printArmorNameColored(a.name, a.armorClass); std::cout << "。" << std::endl;
        }
    }

    void shopMedical() {
        while (true) {
            std::cout << "\n===== 医疗商店 =====\n资金: " << player.money << std::endl;
            for (size_t i = 0; i < medicalShop.size(); ++i) {
                std::cout << i + 1 << ". "; printColoredText(medicalShop[i].name, lootValueColor(medicalShop[i].price));
                std::cout << " - " << medicalShop[i].description << " | " << medicalShop[i].price << "\n";
            }
            std::cout << "0. 返回 | 选择: ";
            int c = readInt();
            if (c == 0) break;
            if (c < 1 || c > (int)medicalShop.size()) continue;
            MedicalItem& m = medicalShop[c - 1];
            if (player.money < m.price) { std::cout << "资金不足。" << std::endl; continue; }
            player.money -= m.price; player.inventory.push_back(m);
            std::cout << "已买 "; printColoredText(m.name, lootValueColor(m.price)); std::cout << "。" << std::endl;
        }
    }

    void sellLoot() {
        if (player.lootBag.empty()) { std::cout << "口袋是空的。" << std::endl; return; }
        std::cout << "\n===== 出售战利品 =====\n";
        int total = 0;
        for (auto& l : player.lootBag) {
            std::cout << "- "; printLootNameColored(l);
            if (l.quantity > 1) std::cout << " x" << l.quantity;
            std::cout << "  售价 " << (l.value * l.quantity) << std::endl;
            total += l.value * l.quantity;
        }
        std::cout << "总售价: " << total << " | 全部出售?(1/0): ";
        int c = readInt();
        if (c == 1) { player.money += total; player.lootBag.clear(); std::cout << "已出售,当前资金 " << player.money << "。" << std::endl; }
    }

    void resetForSafeHouse() {
        if (player.safeDeposit > 0) {
            player.money += player.safeDeposit;
            std::cout << "\n[保险柜] 取回 " << player.safeDeposit << " 卢布。" << std::endl;
            player.safeDeposit = 0;
        }
        if (player.isAlive()) {
            std::cout << "\n你拖着疲惫的身躯回到安全屋。" << std::endl;
        } else {
            std::cout << "\n你阵亡了。所有携带装备和战利品全部丢失。" << std::endl;
            player.lootBag.clear(); player.weapon = Weapon(); player.armor = BodyArmor(); player.helmet = Helmet();
            player.inventory.clear(); player.buffs.clear(); player.stimulators.clear();
            player.maxHealth = 100; player.hasRevived = false; player.health = 10;
            for (auto& kv : player.partHealth) {
                auto mt = player.partMaxHealth.find(kv.first);
                int mx = (mt != player.partMaxHealth.end()) ? mt->second : 50;
                int nh = mx / 8; if (nh < 1) nh = 1; kv.second = nh;
            }
            std::cout << "你被送回安全屋,只剩 10 点生命。" << std::endl;
            if (player.debt > 0) std::cout << "[银行] 债务仍在: " << player.debt << " 卢布。" << std::endl;
        }
        player.flashbangUsed = false; player.stunnedTurns = 0;
        player.lowHealthVoiceTriggered = false; player.boltCooldown = 0;
        player.stunGrenades = 0; player.medicalDelay = 0; player.interferenceTriggered = false; player.skillCooldown = 0;
        player.annaBeaconAvailable = false; player.annaBeaconActive = false;
        player.stimHealPerTurn = 0; player.stimHealTurns = 0;
        player.stimSpeedBonus = 1.0; player.stimSpeedTurns = 0;
        player.stimDamageBonus = 1.0; player.stimDamageTurns = 0;
        player.stimDamageResist = 1.0; player.stimDamageResistTurns = 0;
    }

    void simulateShot(Character& attacker, Character& target, const Ammo& ammo, BodyPart hitPart, const Weapon& weapon, bool targetIsPlayer, double damageMultiplier = 1.0) {
        std::cout << "\n>>> " << attacker.name << " 瞄准 " << target.name << " 的 " << bodyPartName(hitPart) << ",扣动扳机..." << std::endl;
        pause();
        if (attacker.hasBuff(BuffType::STUN) && randDouble(0, 1) < STUN_MISS_CHANCE) {
            std::cout << "  [眩晕未消——枪口晃了一下,打偏了!]" << std::endl; pause(); return;
        }
        bool tokarevPressure = (attacker.role == Role::TOKAREV && !targetIsPlayer);
        if (attacker.hasPainPenalty() && randDouble(0, 1) < PAIN_MISS_CHANCE) {
            std::cout << "  [手臂一抖——子弹打偏了!]" << std::endl; pause();
            std::cout << "  " << attacker.name << " 因疼痛未能命中。" << std::endl; pause();
            if (targetIsPlayer) speakBattleMono(attacker.role, "MISS", 0.4);
            return;
        }
        double effDodge = target.getEffectiveDodge();
        if (effDodge > 0.0 && randDouble(0, 1) < effDodge) {
            std::cout << "  [身影一闪,子弹擦着衣角飞过]" << std::endl; pause();
            std::cout << "  " << target.name << " 闪避了这一枪!" << std::endl; pause(); return;
        }
        int baseDamage = ammo.damage;
        if (isShotgun(weapon.category)) { baseDamage = 0; for (int i = 0; i < 7; ++i) baseDamage += randInt(0, ammo.damage); }
        if (attacker.isBoss && attacker.bossDamageMult > 1.0) baseDamage = (int)(baseDamage * attacker.bossDamageMult);
        if (attacker.role == Role::ANNA && attacker.health < attacker.maxHealth * ANNA_HP_THRESHOLD) baseDamage = (int)(baseDamage * ANNA_DAMAGE_MULT);
        if (attacker.role == Role::ANNA && attacker.hasRevived) baseDamage = (int)(baseDamage * ANNA_REVIVE_DAMAGE_MULT);
        if (attacker.hasPainPenalty()) { double m = randDouble(PAIN_DAMAGE_MIN, PAIN_DAMAGE_MAX); baseDamage = (int)(baseDamage * m); }
        baseDamage = (int)(baseDamage * attacker.stimDamageBonus);
        baseDamage = (int)(baseDamage * damageMultiplier);
        if (target.role == Role::ANNA && target.health < target.maxHealth * ANNA_HP_THRESHOLD && targetIsPlayer) {
            int old = baseDamage; baseDamage = (int)(baseDamage * ANNA_DEFENSE_MULT);
            std::cout << "  [安娜咬紧牙关撑住了——受伤 " << old << " → " << baseDamage << "]" << std::endl; pause();
        }
        baseDamage = (int)(baseDamage * target.stimDamageResist);

        int finalDamage = 0;
        double bonus = attacker.debuffBonus;
        int dN = 5, dH = 8, dF = 8, dS = 8, dP = 4;
        if (tokarevPressure && !target.immuneToDebuffs) { dN = dH = dF = dS = dP = PERMA_BUFF_VALUE; }

        if (hitPart == BodyPart::HEAD) {
            if (target.helmet.owned && !target.helmet.isDestroyed()) {
                if (randDouble(0, 1) < target.helmet.ricochetChance) {
                    std::cout << "  叮——!子弹被头盔弹开了!" << std::endl; pause(); return;
                }
                ArmorClass hc = target.helmet.effectiveClass();
                bool pen = ammo.canPenetrate(hc);
                if (!pen) {
                    double br = ammo.bluntDamageRatio * 0.5;
                    finalDamage = std::max(1, (int)(baseDamage * br));
                    std::cout << "  头盔挡住了子弹!钝伤 " << finalDamage << " 点。" << std::endl; pause();
                    target.helmet.takeDamage(std::max(1, (int)(baseDamage * ammo.armorDamagePercent * 0.5)));
                    target.applyDamageToPart(hitPart, finalDamage);
                } else {
                    double red = ammo.damageReduction(hc);
                    finalDamage = (int)(baseDamage * (1.0 - red));
                    std::cout << "  子弹穿透头盔!减伤 " << (int)(red * 100) << "%,造成 " << finalDamage << " 点伤害。" << std::endl; pause();
                    target.helmet.takeDamage(std::max(1, (int)(baseDamage * ammo.armorDamagePercent * 0.6)));
                    target.applyDamageToPart(hitPart, finalDamage);
                }
                if (target.helmet.currentDurability <= 0) { std::cout << "  头盔碎了!" << std::endl; pause(); }
            } else {
                finalDamage = baseDamage;
                std::cout << "  头部没有防护!造成 " << finalDamage << " 点伤害!" << std::endl; pause();
                target.applyDamageToPart(hitPart, finalDamage);
            }
            if (targetIsPlayer && finalDamage > 0) speakDamageLine(target, finalDamage);
        } else {
            ArmorClass effClass = target.armor.effectiveClass();
            bool armorProtects = (effClass != ArmorClass::NONE) && target.armor.owned && !target.armor.isDestroyed();
            bool penetrated = true;
            if (armorProtects) penetrated = ammo.canPenetrate(effClass);
            if (!armorProtects || penetrated) {
                std::cout << "  [子弹 " << ammo.name << " 飞向目标...]" << std::endl; pause();
                double red = 0.0;
                if (armorProtects && penetrated) red = ammo.damageReduction(effClass);
                finalDamage = (int)(baseDamage * (1.0 - red));
                std::cout << "  子弹击穿!";
                if (armorProtects) std::cout << " 护甲减伤 " << (int)(red * 100) << "%";
                std::cout << " 造成 " << finalDamage << " 点伤害" << std::endl; pause();
                target.applyDamageToPart(hitPart, finalDamage);
                if (targetIsPlayer && finalDamage > 0) speakDamageLine(target, finalDamage);
                if (armorProtects) {
                    double am = isShotgun(weapon.category) ? SHOTGUN_ARMOR_MULT_PENETRATED : 1.0;
                    int ad = std::max(1, (int)(baseDamage * ammo.armorDamagePercent * am * 0.6));
                    int od = target.armor.currentDurability;
                    target.armor.takeDamage(ad);
                    std::cout << "  护甲耐久 -" << ad << " (剩余 " << target.armor.currentDurability << "/" << target.armor.maxDurability << ")" << std::endl;
                    if (od > 0 && target.armor.currentDurability <= 0 && targetIsPlayer) speakArmorBrokenLine(target);
                    pause();
                }
                // 流血:按伤害区分轻重
                if (hitPart != BodyPart::HEAD) {
                    if (finalDamage >= 20 && randDouble(0, 1) < 0.15 + bonus) {
                        if (!target.immuneToDebuffs && !target.isAlexanderDesperate()) {
                            target.addBuff(BuffType::HEAVY_BLEEDING, dH);
                            std::cout << "  " << target.name << " 严重出血!";
                            if (tokarevPressure) std::cout << "(战场压制:永久)";
                            std::cout << std::endl;
                            if (targetIsPlayer) speakDebuffLine(target, BuffType::HEAVY_BLEEDING);
                        }
                        pause();
                    } else if (randDouble(0, 1) < 0.25 + bonus) {
                        if (!target.immuneToDebuffs && !target.isAlexanderDesperate()) {
                            target.addBuff(BuffType::BLEEDING, dN);
                            std::cout << "  " << target.name << " 开始流血!";
                            if (tokarevPressure) std::cout << "(战场压制:永久)";
                            std::cout << std::endl;
                            if (targetIsPlayer) speakDebuffLine(target, BuffType::BLEEDING);
                        }
                        pause();
                    }
                }
                if ((hitPart == BodyPart::LEFT_LEG || hitPart == BodyPart::RIGHT_LEG) && randDouble(0, 1) < 0.15 + bonus) {
                    if (!target.immuneToDebuffs && !target.isAlexanderDesperate()) {
                        target.addBuff(BuffType::FRACTURE, dF);
                        std::cout << "  " << target.name << " 腿部骨折!";
                        if (tokarevPressure) std::cout << "(战场压制:永久)";
                        std::cout << std::endl;
                        if (targetIsPlayer) speakDebuffLine(target, BuffType::FRACTURE);
                    }
                    pause();
                }
                if (hitPart == BodyPart::STOMACH && randDouble(0, 1) < 0.50 + bonus) {
                    if (!target.immuneToDebuffs && !target.isAlexanderDesperate()) {
                        target.addBuff(BuffType::STOMACH_INJURY, dS);
                        std::cout << "  " << target.name << " 腹部受伤!";
                        if (tokarevPressure) std::cout << "(战场压制:永久)";
                        std::cout << std::endl;
                        if (target.role == Role::KATYUSHA) {
                            double old = target.dodgeChance;
                            target.dodgeChance = std::max(0.0, target.dodgeChance - KATYUSHA_DODGE_STOMACH_PEN);
                        }
                        if (targetIsPlayer) speakDebuffLine(target, BuffType::STOMACH_INJURY);
                    }
                    pause();
                }
            } else {
                std::cout << "  [子弹撞在护甲上,火花四溅...]" << std::endl; pause();
                double br = ammo.bluntDamageRatio * target.armorToughness;
                finalDamage = std::max(1, (int)(baseDamage * br));
                std::cout << "  护甲挡住了子弹!钝伤 " << finalDamage << " 点(命中 " << bodyPartName(hitPart) << ")" << std::endl;
                target.applyDamageToPart(hitPart, finalDamage);
                if (targetIsPlayer && finalDamage > 0) speakDamageLine(target, finalDamage);
                double am = isShotgun(weapon.category) ? SHOTGUN_ARMOR_MULT_BLOCKED : 1.0;
                int ad = std::max(1, (int)(baseDamage * ammo.armorDamagePercent * am));
                int od = target.armor.currentDurability;
                target.armor.takeDamage(ad);
                std::cout << "  护甲耐久 -" << ad << " (剩余 " << target.armor.currentDurability << "/" << target.armor.maxDurability << ")" << std::endl;
                if (od > 0 && target.armor.currentDurability <= 0 && targetIsPlayer) speakArmorBrokenLine(target);
                pause();
                if (randDouble(0, 1) < 0.30 + bonus) {
                    if (!target.immuneToDebuffs && !target.isAlexanderDesperate()) {
                        target.addBuff(BuffType::PAIN, dP);
                        std::cout << "  " << target.name << " 感到疼痛!";
                        if (tokarevPressure) std::cout << "(战场压制:永久)";
                        std::cout << std::endl;
                        if (targetIsPlayer) speakDebuffLine(target, BuffType::PAIN);
                    }
                    pause();
                }
                if (hitPart == BodyPart::STOMACH && randDouble(0, 1) < 0.30 + bonus) {
                    if (!target.immuneToDebuffs && !target.isAlexanderDesperate()) {
                        target.addBuff(BuffType::STOMACH_INJURY, dS);
                        std::cout << "  " << target.name << " 腹部受伤!";
                        if (tokarevPressure) std::cout << "(战场压制:永久)";
                        std::cout << std::endl;
                        if (target.role == Role::KATYUSHA) {
                            double old = target.dodgeChance;
                            target.dodgeChance = std::max(0.0, target.dodgeChance - KATYUSHA_DODGE_STOMACH_PEN);
                        }
                        if (targetIsPlayer) speakDebuffLine(target, BuffType::STOMACH_INJURY);
                    }
                    pause();
                }
            }
        }

        if (targetIsPlayer && target.role == Role::IVAN && finalDamage >= IVAN_STUN_GRENADE_TRIGGER_DAMAGE) {
            if (target.stunGrenades < IVAN_STUN_GRENADE_MAX) {
                target.stunGrenades++;
                std::cout << "  >>> 【战斗本能】剧痛中,伊万摸出一枚震爆弹(" << target.stunGrenades << "/2)。" << std::endl;
                pause();
            }
        }

        if (!target.isAlive()) {
            std::cout << "  >>> " << target.name << " 倒下了。" << std::endl;
            speakDeathLine(target, targetIsPlayer);
        } else {
            speakLowHealthLine(target);
        }
    }

    void autoUseMedkit(Character& ch) {
        if (!ch.isAlive()) return;
        if (ch.role == Role::ENEMY && ch.isSuppressedByTokarev()) {
            std::cout << "  >>> 【战场压制】" << ch.name << " 无法自愈!" << std::endl; pause(); return;
        }
        if (ch.role == Role::ENEMY && player.role == Role::KATYUSHA) {
            bool needsMed = ch.hasBuff(BuffType::FRACTURE) || ch.hasBuff(BuffType::BLEEDING) || ch.hasBuff(BuffType::HEAVY_BLEEDING);
            if (needsMed && ch.medicalDelay > 0) {
                ch.medicalDelay--;
                std::cout << "  >>> 【电磁干扰】" << ch.name << " 的电子医疗包还在重启……(剩余 " << ch.medicalDelay << " 回合)" << std::endl;
                pause();
                return;
            }
            if (needsMed && !ch.interferenceTriggered && !ch.inventory.empty()) {
                ch.interferenceTriggered = true;
                ch.medicalDelay = KATYUSHA_INTERFERENCE_DELAY;
                std::cout << "  >>> 【电磁干扰】" << ch.name << " 的医疗电子设备被干扰,暂时无法使用!" << std::endl;
                pause();
                return;
            }
        }
        if (ch.hasBuff(BuffType::FRACTURE)) {
            for (size_t i = 0; i < ch.inventory.size(); ++i)
                if (ch.inventory[i].fixesFracture && ch.inventory[i].uses > 0) { ch.useMedicalItem((int)i); break; }
        }
        if (ch.hasBuff(BuffType::HEAVY_BLEEDING)) {
            for (size_t i = 0; i < ch.inventory.size(); ++i)
                if (ch.inventory[i].stopsHeavyBleeding && ch.inventory[i].uses > 0) { ch.useMedicalItem((int)i); break; }
        }
        if (ch.hasBuff(BuffType::BLEEDING)) {
            bool hf = ch.hasBuff(BuffType::FRACTURE);
            for (size_t i = 0; i < ch.inventory.size(); ++i) {
                if (hf && ch.inventory[i].blockedByFracture && ch.role != Role::KATYUSHA) continue;
                if (ch.inventory[i].stopsBleeding && ch.inventory[i].uses > 0) { ch.useMedicalItem((int)i); break; }
            }
        }
    }

    Character createEnemy(int difficulty) {
        Character enemy;
        enemy.name = "敌方雇佣兵"; enemy.role = Role::ENEMY;
        enemy.speed = ENEMY_DEFAULT_SPEED; enemy.dodgeChance = 0.0; enemy.debuffBonus = 0.0;
        enemy.hasRevived = false; enemy.initPartHealth();
        int ar = randInt(1, 100) + currentMap.enemyArmorBonus;
        if (ar < 1) ar = 1; if (ar > 100) ar = 100;
        if (ar <= 45) { }
        else if (ar <= 65) { enemy.armor = BodyArmor("6B5-11 芳纶背心",ArmorClass::CLASS_2,40,3.0,15000); enemy.armor.owned = true; }
        else if (ar <= 80) { enemy.armor = BodyArmor("6B5-12 钛合金背心",ArmorClass::CLASS_3,55,5.5,28000); enemy.armor.owned = true; }
        else if (ar <= 92) { enemy.armor = BodyArmor("6B23 巴尔米查",ArmorClass::CLASS_4,65,6.8,45000); enemy.armor.owned = true; }
        else if (ar <= 98) { enemy.armor = BodyArmor("6B43 战士-1",ArmorClass::CLASS_5,80,7.5,72000,true); enemy.armor.owned = true; }
        else { enemy.armor = BodyArmor("6B45-1 装甲增强",ArmorClass::CLASS_6,100,11.5,150000,true); enemy.armor.owned = true; }
        if (randInt(1, 100) <= 40) {
            int hr = randInt(1, 100);
            if (hr <= 40) { enemy.helmet = Helmet("SSh-68 钢盔",ArmorClass::CLASS_3,30,0.25,1.5,0); enemy.helmet.owned = true; }
            else if (hr <= 70) { enemy.helmet = Helmet("6B47 Ratnik-BSh",ArmorClass::CLASS_3,35,0.30,1.3,0); enemy.helmet.owned = true; }
            else if (hr <= 90) { enemy.helmet = Helmet("TC-2001 高切盔",ArmorClass::CLASS_4,50,0.30,1.1,0); enemy.helmet.owned = true; }
            else { enemy.helmet = Helmet("阿尔金头盔",ArmorClass::CLASS_5,60,0.40,2.0,0); enemy.helmet.owned = true; }
        }
        int wr = randInt(1, 100) + currentMap.enemyWeaponBonus;
        if (wr < 1) wr = 1; if (wr > 100) wr = 100;
        Weapon w;
        if (wr <= 30) {
            int r = randInt(0, 5);
            if (r == 0) w = safeWeaponAt(24); else if (r == 1) w = safeWeaponAt(25);
            else if (r == 2) w = safeWeaponAt(26); else if (r == 3) w = safeWeaponAt(27);
            else if (r == 4) w = safeWeaponAt(28); else w = safeWeaponAt(30);
        } else if (wr <= 55) {
            int r = randInt(0, 2);
            if (r == 0) w = safeWeaponAt(31); else if (r == 1) w = safeWeaponAt(32); else w = safeWeaponAt(33);
        } else if (wr <= 90) {
            int r = randInt(0, 9); w = safeWeaponAt(r);
        } else {
            std::vector<std::string> high = {"SVD","SV-98","M700","AWM","VSS","AS Val 巨浪"};
            Weapon* wp = findWeaponByName(high[randInt(0, (int)high.size() - 1)]);
            if (wp) w = *wp; else w = safeWeaponAt(0);
        }
        w.currentAmmo = w.magazineSize; enemy.weapon = w;
        std::vector<Ammo> list = getAmmoByCaliber(w.caliber);
        if (!list.empty()) {
            Ammo a;
            if (list.size() >= 2 && randInt(1, 100) <= 30) a = list[randInt(1, (int)list.size() - 1)]; else a = list[0];
            a.quantity = w.magazineSize * 3;
            enemy.weapon.compatibleAmmo.clear(); enemy.weapon.compatibleAmmo.push_back(a);
        }
        enemy.maxHealth = 100; enemy.health = 100;
        if (randInt(1, 100) <= 35) enemy.inventory.push_back(medicalShop[0]);
        return enemy;
    }

    LootItem generateWeaponLoot() {
        LootItem l; l.category = ItemCategory::WEAPON;
        if (weaponDatabase.empty()) return l;
        int r = randInt(0, (int)weaponDatabase.size() - 1);
        l.weaponData = weaponDatabase[r]; l.name = l.weaponData.name;
        l.value = l.weaponData.price * 0.7; l.quantity = 1; return l;
    }
    LootItem generateAmmoLoot() {
        LootItem l; l.category = ItemCategory::AMMO;
        if (ammoDatabase.empty()) return l;
        int r = randInt(0, (int)ammoDatabase.size() - 1);
        l.ammoData = ammoDatabase[r]; l.name = l.ammoData.name + " 弹药";
        l.value = l.ammoData.pricePerRound * 0.6; l.quantity = randInt(20, 90); return l;
    }
    LootItem generateArmorLoot() {
        LootItem l; l.category = ItemCategory::ARMOR;
        std::vector<BodyArmor> all;
        for (auto& a : armorShop) all.push_back(a);
        for (auto& a : rareArmorList) all.push_back(a);
        if (all.empty()) return l;
        int r = randInt(0, (int)all.size() - 1);
        l.armorData = all[r]; l.name = l.armorData.name;
        l.value = l.armorData.price * 0.6; l.quantity = 1; return l;
    }
    LootItem generateMedicalLoot() {
        LootItem l; l.category = ItemCategory::MEDICAL;
        if (medicalShop.empty()) return l;
        int r = randInt(0, (int)medicalShop.size() - 1);
        l.medData = medicalShop[r]; l.name = l.medData.name;
        l.value = l.medData.price * 0.8; l.quantity = 1; return l;
    }
    LootItem generateValuableLoot() {
        LootItem l; l.category = ItemCategory::VALUABLE; l.quantity = 1;
        int tier = randInt(1, 100);
        int cm = currentMap.commonThreshold, mm = currentMap.midThreshold;
        if (tier <= cm) {
            std::string names[] = {"螺栓","电线","火花塞","胶带","螺丝刀","罐头","伏特加","香烟","打火机","破布","木柴","汽油罐","旧手机","机械零件","废金属","图钉","圆珠笔","笔记本","水壶","急救员卡片"};
            int n = sizeof(names) / sizeof(names[0]);
            l.name = names[randInt(0, n - 1)]; l.value = randInt(1500, 6000);
        } else if (tier <= mm) {
            std::string names[] = {"酒精","电池","工具箱","军用文件","地图","指南针","望远镜","军用水壶","战术手电","对讲机","医疗记录","弹药箱零件","军官徽章","军牌","加密硬盘"};
            int n = sizeof(names) / sizeof(names[0]);
            l.name = names[randInt(0, n - 1)]; l.value = randInt(8000, 25000);
        } else {
            std::string names[] = {"黄金链","白银戒指","手表","钻石","保险箱钥匙","金币","稀有邮票","战斗勋章","将军密令","实验室样本"};
            int n = sizeof(names) / sizeof(names[0]);
            l.name = names[randInt(0, n - 1)]; l.value = randInt(30000, 80000);
        }
        return l;
    }
    LootItem generateMoneyLoot() {
        LootItem l; l.category = ItemCategory::MONEY; l.name = "卢布"; l.value = 1;
        int bm = randInt(5000, 50000); bm = (int)(bm * (0.7 + currentMap.difficulty * 0.15));
        l.quantity = bm; return l;
    }
    std::vector<LootItem> rollLoot(ContainerType type) {
        std::vector<LootItem> result;
        int n = randInt(2, 4);
        if (currentMap.difficulty >= 4) n = randInt(3, 5);
        if (currentMap.difficulty >= 5) n = randInt(4, 6);
        for (int i = 0; i < n; ++i) {
            int roll = randInt(1, 100);
            switch (type) {
                case ContainerType::WEAPON_BOX:
                    if (roll <= 35) result.push_back(generateWeaponLoot());
                    else if (roll <= 75) result.push_back(generateAmmoLoot());
                    else result.push_back(generateValuableLoot()); break;
                case ContainerType::AMMO_CRATE:
                    if (roll <= 80) result.push_back(generateAmmoLoot());
                    else if (roll <= 92) result.push_back(generateWeaponLoot());
                    else result.push_back(generateValuableLoot()); break;
                case ContainerType::MEDICAL_CABINET:
                    if (roll <= 85) result.push_back(generateMedicalLoot());
                    else result.push_back(generateValuableLoot()); break;
                case ContainerType::SAFE:
                    if (roll <= 45) result.push_back(generateMoneyLoot());
                    else if (roll <= 70) result.push_back(generateWeaponLoot());
                    else if (roll <= 88) result.push_back(generateValuableLoot());
                    else result.push_back(generateArmorLoot()); break;
                case ContainerType::CORPSE:
                    if (roll <= 35) result.push_back(generateMoneyLoot());
                    else if (roll <= 65) result.push_back(generateAmmoLoot());
                    else if (roll <= 85) result.push_back(generateMedicalLoot());
                    else result.push_back(generateWeaponLoot()); break;
                case ContainerType::JACKET:
                    if (roll <= 65) result.push_back(generateMoneyLoot());
                    else if (roll <= 88) result.push_back(generateValuableLoot());
                    else result.push_back(generateMedicalLoot()); break;
                case ContainerType::TOOLBOX:
                    if (roll <= 55) result.push_back(generateValuableLoot());
                    else if (roll <= 82) result.push_back(generateAmmoLoot());
                    else result.push_back(generateWeaponLoot()); break;
                case ContainerType::FILING_CABINET:
                    if (roll <= 75) result.push_back(generateValuableLoot());
                    else result.push_back(generateMoneyLoot()); break;
                case ContainerType::BOSS_ZONE: break;
            }
        }
        return result;
    }
    std::vector<std::string> getMapContainerNames() {
        std::vector<std::string> n;
        if (currentMap.name == "木屋") n = {"木屋主厅","厨房","卧室","储物间","车库","阁楼","地下窖","柴房"};
        else if (currentMap.name == "仓库区") n = {"A区货架","B区货架","集装箱","物流办公室","装卸区","工具间","档案室","冷藏间","值班室","叉车旁"};
        else if (currentMap.name == "港口码头") n = {"码头仓库","渔船","吊车控制室","海关办公室","冷藏库","集装箱堆场","船舶修理棚","船长室","货轮底舱","信号塔"};
        else if (currentMap.name == "军事基地") n = {"军械库","指挥所","兵营","军医站","弹药库","雷达站","油料库","直升机坪","作战室","值班岗亭","装甲车库","通讯站"};
        else n = {"实验舱","培养室","控制中心","样本库","手术室","服务器机房","化学间","隔离区","研究员宿舍","标本室","反应堆走廊","机密档案室"};
        return n;
    }
    void generateMap() {
        mapContainers.clear(); bossSpawned = false;
        std::vector<std::string> names = getMapContainerNames();
        ContainerType types[] = {ContainerType::JACKET,ContainerType::WEAPON_BOX,ContainerType::MEDICAL_CABINET,
            ContainerType::SAFE,ContainerType::AMMO_CRATE,ContainerType::CORPSE,ContainerType::TOOLBOX,
            ContainerType::FILING_CABINET,ContainerType::WEAPON_BOX,ContainerType::SAFE,
            ContainerType::AMMO_CRATE,ContainerType::MEDICAL_CABINET,ContainerType::TOOLBOX,ContainerType::JACKET};
        int tc = sizeof(types) / sizeof(types[0]);
        int n = randInt(currentMap.minContainers, currentMap.maxContainers);
        if (n > (int)names.size()) n = (int)names.size();
        for (int i = 0; i < n; ++i) {
            Container c; c.name = names[i]; c.type = types[randInt(0, tc - 1)];
            c.searched = false; c.dangerLevel = randInt(currentMap.minDanger, currentMap.maxDanger);
            mapContainers.push_back(c);
        }
        if (currentMap.enableKeys && !mapContainers.empty()) {
            std::string kn;
            if (currentMap.name == "木屋") kn = "木屋地窖钥匙";
            else if (currentMap.name == "仓库区") kn = "仓库办公室钥匙";
            else if (currentMap.name == "港口码头") kn = "码头海关钥匙";
            else if (currentMap.name == "军事基地") kn = "军械库钥匙";
            else if (currentMap.name == "实验室") kn = "实验室机密室钥匙";
            if (!kn.empty()) {
                int lc = randInt(1, 2);
                for (int i = 0; i < lc; ++i) {
                    int idx = randInt(0, (int)mapContainers.size() - 1);
                    if (mapContainers[idx].type == ContainerType::BOSS_ZONE || mapContainers[idx].locked) continue;
                    mapContainers[idx].locked = true; mapContainers[idx].requiredKey = kn;
                }
            }
        }
        if (currentMap.difficulty >= 3 && randInt(1, 100) <= BOSS_SPAWN_CHANCE) {
            bossSpawned = true;
            Container bz; bz.name = "Boss占领区"; bz.type = ContainerType::BOSS_ZONE;
            bz.searched = false; bz.dangerLevel = 100;
            mapContainers.push_back(bz);
        }
    }

    bool tryPickupItem(const LootItem& item) {
        if (item.category == ItemCategory::MONEY) { player.money += item.quantity; std::cout << "  - " << item.name << " +" << item.quantity << " 卢布" << std::endl; return true; }
        if (item.category == ItemCategory::WEAPON || item.category == ItemCategory::ARMOR ||
            item.category == ItemCategory::AMMO || item.category == ItemCategory::MEDICAL) {
            std::cout << "  - 发现可用物品: "; printLootNameColored(item);
            if (item.quantity > 1) std::cout << " x" << item.quantity;
            std::cout << "\n    立即使用/装备?(1=是 / 0=放入背包): ";
            int c = readInt(); if (c == 1) { useOrEquipItem(item); return true; }
        }
        if ((int)player.lootBag.size() >= player.lootCapacity) {
            std::cout << "  !背包已满,无法拾取 "; printLootNameColored(item); std::cout << "。" << std::endl;
            pause(); return false;
        }
        player.lootBag.push_back(item);
        std::cout << "  + "; printLootNameColored(item);
        if (item.quantity > 1) std::cout << " x" << item.quantity;
        std::cout << "(背包 " << player.lootBag.size() << "/" << player.lootCapacity << ")" << std::endl;
        return true;
    }

    void useOrEquipItem(const LootItem& item) {
        if (item.category == ItemCategory::WEAPON) {
            Weapon* w = findWeaponByName(item.name);
            if (w) {
                int oa = player.weapon.currentAmmo;
                std::string oc = player.weapon.caliber;
                std::vector<Ammo> ob = player.weapon.compatibleAmmo;
                player.weapon = *w; player.weapon.currentAmmo = 0; player.weapon.compatibleAmmo.clear();
                player.boltCooldown = 0;
                if (!oc.empty() && oc == player.weapon.caliber) {
                    int load = std::min(oa, player.weapon.magazineSize);
                    player.weapon.currentAmmo = load; int ov = oa - load;
                    player.weapon.compatibleAmmo = ob;
                    if (ov > 0) {
                        if (!player.weapon.compatibleAmmo.empty()) player.weapon.compatibleAmmo[0].quantity += ov;
                        else {
                            std::vector<Ammo> l = getAmmoByCaliber(player.weapon.caliber);
                            if (!l.empty()) { Ammo a = l[0]; a.quantity = ov; player.weapon.compatibleAmmo.push_back(a); }
                        }
                    }
                    std::cout << "  >>> 换装 " << player.weapon.name << "(同口径,弹药已保留):弹匣 " << player.weapon.currentAmmo << "/" << player.weapon.magazineSize;
                    if (!player.weapon.compatibleAmmo.empty()) std::cout << " | 备用 " << player.weapon.compatibleAmmo[0].quantity;
                    std::cout << std::endl;
                } else {
                    std::cout << "  >>> 换装 " << player.weapon.name << "(口径不同,旧弹药未保留)。" << std::endl;
                }
            } else if ((int)player.lootBag.size() < player.lootCapacity) player.lootBag.push_back(item);
        } else if (item.category == ItemCategory::ARMOR) {
            BodyArmor* a = findArmorByName(item.name);
            if (a) {
                player.armor = *a; player.armor.owned = true;
                std::cout << "  >>> 已装备 "; printArmorNameColored(player.armor.name, player.armor.armorClass);
                std::cout << "(" << armorClassName(player.armor.armorClass) << ")" << std::endl;
            } else if ((int)player.lootBag.size() < player.lootCapacity) player.lootBag.push_back(item);
        } else if (item.category == ItemCategory::AMMO) {
            std::string an = item.name; const std::string sfx = " 弹药";
            if (an.size() >= sfx.size() && an.compare(an.size() - sfx.size(), sfx.size(), sfx) == 0) an = an.substr(0, an.size() - sfx.size());
            Ammo* a = findAmmoByName(an);
            if (a && player.weapon.magazineSize > 0 && player.weapon.caliber == a->caliber) {
                int sp = player.weapon.magazineSize - player.weapon.currentAmmo;
                int load = std::min(sp, item.quantity); player.weapon.currentAmmo += load;
                int bk = item.quantity - load;
                if (bk > 0) {
                    if (!player.weapon.compatibleAmmo.empty() && player.weapon.compatibleAmmo[0].name == a->name) player.weapon.compatibleAmmo[0].quantity += bk;
                    else { Ammo c = *a; c.quantity = bk; player.weapon.compatibleAmmo.clear(); player.weapon.compatibleAmmo.push_back(c); }
                }
                std::cout << "  >>> 装填 " << load << " 发 "; printAmmoNameColored(a->name, a->penetration);
                std::cout << ",备用 " << bk << " 发。" << std::endl;
            } else if ((int)player.lootBag.size() < player.lootCapacity) {
                player.lootBag.push_back(item);
                std::cout << "  >>> 口径不匹配或暂无对应武器,已放入背包。" << std::endl;
            } else std::cout << "  >>> 背包已满,无法拾取。" << std::endl;
        } else if (item.category == ItemCategory::MEDICAL) {
            MedicalItem* m = findMedicalByName(item.name);
            if (m) {
                player.inventory.push_back(*m);
                std::cout << "  >>> 已收入药品背包: "; printColoredText(m->name, lootValueColor(m->price)); std::cout << std::endl;
            } else if ((int)player.lootBag.size() < player.lootCapacity) player.lootBag.push_back(item);
        }
    }

    void searchContainer(int index) {
        if (index < 0 || index >= (int)mapContainers.size()) return;
        if (mapContainers[index].type == ContainerType::BOSS_ZONE) {
            if (mapContainers[index].searched) { std::cout << "已经安静了。" << std::endl; return; }
            mapContainers[index].searched = true;
            std::cout << "\n你走进 Boss 占领区……" << std::endl; pause();
            battleWithBoss(); return;
        }
        if (mapContainers[index].searched) { std::cout << "这个 " << mapContainers[index].name << " 已经被搜过了。" << std::endl; return; }
        if (mapContainers[index].locked) {
            std::cout << "\n【" << mapContainers[index].name << "】被锁住了。\n需要钥匙:" << mapContainers[index].requiredKey << std::endl;
            speakMono(player.role, MonoEvent::SEARCH_LOCKED, 0.8);
            if (!player.hasKey(mapContainers[index].requiredKey)) {
                std::cout << "你没有这把钥匙。去钥匙商店买,或者在其他地方找找。" << std::endl; pause();
                speakMono(player.role, MonoEvent::SEARCH_NO_KEY, 0.8);
                return;
            }
            std::cout << "使用 " << mapContainers[index].requiredKey << "?(1=是 / 0=否): ";
            int c = readInt(); if (c != 1) return;
            player.consumeKey(mapContainers[index].requiredKey);
            std::cout << "咔哒一声,锁开了。" << std::endl; pause();
            speakMono(player.role, MonoEvent::KEY_USE, 0.5);
        }
        std::cout << "\n搜索 " << mapContainers[index].name << "(" << containerTypeName(mapContainers[index].type) << ")..." << std::endl;
        pause();
        bool willEncounter = (randInt(1, 100) <= mapContainers[index].dangerLevel);
        if (willEncounter) {
            int wc = player.getHeadsetWarningChance();
            if (wc > 0 && randInt(1, 100) <= wc) {
                std::cout << "\n  (耳机里传来压低的呼吸声和金属摩擦声……)" << std::endl; pause();
                std::cout << "  【预警】附近有人。真的要搜索这个容器吗?\n  1. 继续搜索(准备战斗)\n  0. 悄悄退开(放弃这次搜索,容器保持未搜)\n  请选择: ";
                int c = readInt();
                if (c != 1) {
                    std::cout << "\n  你屏住呼吸,慢慢退回阴影里。\n  对方没有发现你。" << std::endl; pause();
                    std::cout << "  (这次搜索浪费了时间,但保住了命)" << std::endl; pause(); return;
                }
                std::cout << "\n  你握紧武器,继续搜索。" << std::endl; pause();
            }
            std::cout << "\n!!!被敌人发现了!" << std::endl; pause();
            Character enemy = createEnemy(currentMap.difficulty + randInt(0, 1));
            battleWith(enemy);
            if (index < 0 || index >= (int)mapContainers.size()) return;
            if (!player.isAlive()) { mapContainers[index].searched = true; return; }
        }
        if (index < 0 || index >= (int)mapContainers.size()) return;
        mapContainers[index].searched = true;
        std::vector<LootItem> loot;
        if (mapContainers[index].locked) {
            loot = rollLoot(ContainerType::SAFE);
            int extra = randInt(1, 2);
            for (int i = 0; i < extra; ++i) loot.push_back(generateValuableLoot());
        } else loot = rollLoot(mapContainers[index].type);
        if (loot.empty()) {
            std::cout << "空空如也。" << std::endl;
            speakMono(player.role, MonoEvent::SEARCH_EMPTY, 0.6);
            return;
        }
        std::cout << "你找到了:" << std::endl;
        int highest = 0;
        for (size_t i = 0; i < loot.size(); ++i) {
            tryPickupItem(loot[i]);
            pause();
            if (loot[i].value * loot[i].quantity > highest) highest = loot[i].value * loot[i].quantity;
        }
        if (highest >= 50000) speakMono(player.role, MonoEvent::SEARCH_HIGH, 0.8);
        else if (highest < 8000) speakMono(player.role, MonoEvent::SEARCH_LOW, 0.4);
    }

    Character createBoss(int difficulty) {
        Character b; b.isBoss = true; b.role = Role::ENEMY;
        b.dodgeChance = 0.0; b.debuffBonus = 0.25; b.hasRevived = false;
        b.speed = 1.0; b.armorToughness = 1.0; b.immuneToDebuffs = false; b.bossDamageMult = 1.0;
        b.initPartHealth();
        if (difficulty <= 3) {
            b.name = "「破船者」伊万"; b.maxHealth = 100; b.health = 100; b.speed = 1.05;
            b.armor = BodyArmor("走私犯特制背心",ArmorClass::CLASS_4,110,8.5,0); b.armor.owned = true;
            b.helmet = Helmet("走私犯钢盔",ArmorClass::CLASS_3,40,0.20,1.5,0); b.helmet.owned = true;
            Weapon* w = findWeaponByName("AKM"); if (w) b.weapon = *w;
            b.weapon.magazineSize = 30; b.weapon.currentAmmo = 30; b.weapon.compatibleAmmo.clear();
            Ammo* bp = findAmmoByName("7.62 BP");
            if (bp) { Ammo a = *bp; a.quantity = 120; b.weapon.compatibleAmmo.push_back(a); }
            b.skillName = "亡命突袭"; b.skillDesc = "疯狂连射,枪法精准,对护甲耐久伤害极大"; b.bossDamageMult = 1.15;
        } else if (difficulty == 4) {
            b.name = "「铁壁」谢尔盖"; b.maxHealth = 100; b.health = 100; b.speed = 0.80; b.armorToughness = 1.5;
            b.armor = BodyArmor("实验型复合装甲",ArmorClass::CLASS_6,160,13.0,0); b.armor.owned = true;
            b.helmet = Helmet("实验型全护盔",ArmorClass::CLASS_6,80,0.30,2.5,0); b.helmet.owned = true;
            Weapon* w = findWeaponByName("HK416"); if (w) b.weapon = *w;
            b.weapon.magazineSize = 30; b.weapon.currentAmmo = 30; b.weapon.compatibleAmmo.clear();
            Ammo* m = findAmmoByName("5.56 M995");
            if (m) { Ammo a = *m; a.quantity = 150; b.weapon.compatibleAmmo.push_back(a); }
            b.skillName = "钢铁壁垒"; b.skillDesc = "全身复合装甲,穿甲弹都难以击穿;单发伤害极高"; b.bossDamageMult = 1.30;
        } else {
            b.name = "「猎手」黑鸦"; b.maxHealth = 100; b.health = 100; b.speed = 1.10; b.armorToughness = 1.2;
            b.immuneToDebuffs = true;
            b.armor = BodyArmor("奥贝雷格 Obereg",ArmorClass::CLASS_6,150,11.5,0); b.armor.owned = true;
            b.helmet = Helmet("黑鸦特制头套",ArmorClass::CLASS_4,50,0.35,1.0,0); b.helmet.owned = true;
            Weapon* w = findWeaponByName("M700"); if (w) b.weapon = *w;
            b.weapon.magazineSize = 5; b.weapon.currentAmmo = 5; b.weapon.compatibleAmmo.clear();
            Ammo* ap = findAmmoByName("7.62 M61");
            if (ap) { Ammo a = *ap; a.quantity = 30; b.weapon.compatibleAmmo.push_back(a); }
            b.skillName = "致命猎杀"; b.skillDesc = "免疫所有减益;精准射击,头部命中率极高"; b.bossDamageMult = 1.20;
        }
        std::cout << "  >>> " << b.name << " —— 【" << b.skillName << "】" << std::endl;
        std::cout << "      " << b.skillDesc << std::endl;
        std::cout << "      武器: " << b.weapon.name << " | 护甲: "; printArmorNameColored(b.armor.name, b.armor.armorClass);
        std::cout << " | 生命: " << b.maxHealth << std::endl;
        return b;
    }

    std::vector<LootItem> generateBossLoot() {
        std::vector<LootItem> r;
        std::vector<std::string> names = {"军用加密硬盘","实验室机密文件","黄金战利品箱","将军密令原件","稀有战斗勋章","黑市钻石袋","走私金条","未登记实验样本","绝密作战地图","国际刑警档案","高级军官怀表","钛合金保险箱钥匙"};
        std::shuffle(names.begin(), names.end(), rng);
        int cnt = randInt(3, 5); if (cnt > (int)names.size()) cnt = (int)names.size();
        for (int i = 0; i < cnt; ++i) {
            LootItem it; it.category = ItemCategory::VALUABLE; it.name = names[i];
            it.value = randInt(400000, 1000000); it.quantity = 1; r.push_back(it);
        }
        return r;
    }

    void battleWithBoss() {
        Character b = createBoss(currentMap.difficulty);
        std::cout << "\n################################\n#         BOSS 遭遇战          #\n################################\n"; pause();
        std::cout << "「" << b.name << "」 从阴影中走出……" << std::endl; pause();
        battleWith(b);
    }

    void playAnnaBossDefeatCG(const Character& boss) {
        std::cout << "\n\n========================================\n           安 娜 · 战 败 处 决            \n========================================\n"; pause(); pause();
        int v = randInt(1, 2);
        if (boss.name == "「破船者」伊万") {
            if (v == 1) {
                std::cout << "码头咸涩的海风里,伊万把她拖到边缘。" << std::endl; pause();
                std::cout << "\"别怪我,孩子。谁先眨眼谁就死。\"" << std::endl; pause();
                std::cout << "一个用力,她被扔进冰冷的水里。\n装备太重,她沉了下去。" << std::endl; pause();
            } else {
                std::cout << "伊万绕到她身后,拉了一下枪机。" << std::endl; pause();
                std::cout << "\"你的队友,是不是也这么死的?\"" << std::endl; pause();
                std::cout << "砰。" << std::endl; pause();
            }
        } else if (boss.name == "「铁壁」谢尔盖") {
            if (v == 1) {
                std::cout << "谢尔盖的装甲靴踩上她的胸口。\n肋骨断裂的声音。\n一下。然后什么都没有了。" << std::endl; pause();
            } else {
                std::cout << "谢尔盖掐住她的脖子把她提起来。" << std::endl; pause();
                std::cout << "\"你的队友死的时候,也在找妈妈吗?\"" << std::endl; pause();
                std::cout << "她放弃了。手指从他的手臂上滑落——" << std::endl; pause();
            }
        } else if (boss.name == "「猎手」黑鸦") {
            if (v == 1) {
                std::cout << "黑鸦没靠近。只是站着,M700 稳稳指向她。\n呼吸。瞄准。呼出。\n砰。7.62 M61 穿甲弹穿过头盔缝隙。" << std::endl; pause();
            } else {
                std::cout << "黑鸦蹲下,看着她的眼睛。" << std::endl; pause();
                std::cout << "\"你队友比你死得还快。\"\n手枪抵住额头。砰。" << std::endl; pause();
            }
        }
        std::cout << "\n========================================\n           安 娜 · 就 此 长 眠            \n========================================\n"; pause(); pause();
    }

    void throwStunGrenade(Character& enemy) {
        player.stunGrenades--;
        std::cout << "\n>>> 伊万拉开保险环,把震爆弹甩向敌人……" << std::endl;
        pause();
        int hitChance = randInt(IVAN_STUN_MIN_CHANCE, IVAN_STUN_MAX_CHANCE);
        std::cout << "  (命中率判定: " << hitChance << "%)" << std::endl;
        pause();
        if (randInt(1, 100) <= hitChance) {
            std::cout << "    轰——!震爆弹在敌人脚边炸开!" << std::endl;
            pause();
            enemy.addBuff(BuffType::PAIN, IVAN_STUN_PAIN_TURNS);
            enemy.addBuff(BuffType::STUN, IVAN_STUN_STUN_TURNS);
            int totalDmg = 0;
            for (int i = 0; i < (int)BodyPart::COUNT; ++i) {
                BodyPart bp = (BodyPart)i;
                int dmg = randInt(IVAN_STUN_DAMAGE_MIN, IVAN_STUN_DAMAGE_MAX);
                if (enemy.partHealth.find(bp) != enemy.partHealth.end())
                    enemy.partHealth[bp] = std::max(0, enemy.partHealth[bp] - dmg);
                totalDmg += dmg;
            }
            enemy.health = std::max(0, enemy.health - totalDmg);
            std::cout << "    敌人被震得踉跄后退,全身受到冲击(合计 " << totalDmg << " 点钝伤)。" << std::endl;
            std::cout << "    敌人获得【疼痛】" << IVAN_STUN_PAIN_TURNS << " 回合、【眩晕】" << IVAN_STUN_STUN_TURNS << " 回合。" << std::endl;
            pause();
            if (!enemy.isAlive()) {
                std::cout << "  >>> " << enemy.name << " 倒下了。" << std::endl;
                speakDeathLine(enemy, false);
            }
        } else {
            std::cout << "    震爆弹在墙角炸开,闪光被建筑挡住了,敌人没受到多少影响。" << std::endl;
            pause();
        }
    }

    void useLastStrike(Character& enemy) {
        player.skillCooldown = 999;
        std::cout << "\n>>> 【最后一击】托卡列夫一记重拳砸向敌人的护甲!" << std::endl;
        pause();
        if (enemy.armor.owned && !enemy.armor.isDestroyed()) {
            int oldDur = enemy.armor.currentDurability;
            enemy.armor.takeDamage(TOKAREV_LAST_STRIKE_ARMOR_DMG);
            std::cout << "    护甲耐久 -" << TOKAREV_LAST_STRIKE_ARMOR_DMG << "(" << oldDur << " → " << enemy.armor.currentDurability << ")" << std::endl;
            pause();
            if (enemy.armor.currentDurability <= 0) {
                std::cout << "    敌人的护甲碎了!" << std::endl;
                pause();
            }
        } else {
            std::cout << "    敌人身上没有护甲,拳头砸了个空。" << std::endl;
            pause();
        }
        if (enemy.health >= TOKAREV_LAST_STRIKE_HP_THRESHOLD) {
            enemy.health = std::max(0, enemy.health - TOKAREV_LAST_STRIKE_BODY_DMG);
            std::cout << "    敌人踉跄了一下,额外受到 " << TOKAREV_LAST_STRIKE_BODY_DMG << " 点肉体伤害。" << std::endl;
            pause();
        } else {
            std::cout << "    敌人已经摇摇欲坠,没再受到额外伤害。" << std::endl;
            pause();
        }
        if (!enemy.isAlive()) {
            std::cout << "  >>> " << enemy.name << " 倒下了。" << std::endl;
            speakDeathLine(enemy, false);
        }
    }

    void playerFire(Character& enemy) {
        if (player.boltCooldown > 0) {
            std::cout << "\n>>> 你还在拉栓,本回合无法开火。" << std::endl;
            player.boltCooldown--;
            pause();
            return;
        }
        if (player.weapon.currentAmmo <= 0) {
            std::cout << "!弹匣是空的,你扣了一下扳机,只听到咔哒声。" << std::endl;
            speakBattleMono(player.role, "EMPTY_MAG", 1.0);
            pause();
            return;
        }
        int shots = getShotsPerFire(player.weapon);
        bool suppression = false;
        if (player.role == Role::IVAN && player.weapon.fireRate < BURST_RATE_THRESHOLD && randDouble(0, 1) < IVAN_SUPPRESSION_CHANCE) {
            shots = 2;
            suppression = true;
            std::cout << "\n>>> 【火力压制】伊万把扳机一扣到底,枪口喷出第二道火舌!" << std::endl;
            pause();
        }
        shots = std::min(shots, player.weapon.currentAmmo);
        for (int i = 0; i < shots; ++i) {
            if (!enemy.isAlive() || player.weapon.currentAmmo <= 0) break;
            if (i > 0 && !suppression) {
                std::cout << "\n  [连发第 " << i + 1 << " 发]" << std::endl;
                if (randDouble(0, 1) < BURST_2ND_HIT_PENALTY * i) {
                    std::cout << "  枪口跳动,这一发打偏了!" << std::endl;
                    player.weapon.currentAmmo--;
                    pause();
                    continue;
                }
            }
            Ammo ammo = player.weapon.compatibleAmmo.empty() ? ammoDatabase[0] : player.weapon.compatibleAmmo[0];
            int pr = randInt(0, 6);
            BodyPart hp = (BodyPart)pr;
            if (hp == BodyPart::HEAD && randDouble(0, 1) < 0.7) hp = BodyPart::CHEST;
            double dmgMult = (suppression && i == 1) ? IVAN_SUPPRESSION_DAMAGE_MULT : 1.0;
            simulateShot(player, enemy, ammo, hp, player.weapon, false, dmgMult);
            player.weapon.currentAmmo--;
            if (enemy.isAlive() && player.weapon.currentAmmo > 0) pause();
        }
        if (enemy.isAlive() && canExtraFire(player.weapon) && player.weapon.currentAmmo > 0) {
            std::cout << "\n>>> 【快速射击】敌人还没倒!可以再补一枪。" << std::endl;
            std::cout << "    1. 追加开火\n    0. 不追加\n    请选择: ";
            int c = readInt();
            if (c == 1) {
                if (randDouble(0, 1) < EXTRA_FIRE_HIT_PENALTY) {
                    std::cout << "  [追加射击打偏了]" << std::endl;
                    player.weapon.currentAmmo--;
                    speakBattleMono(player.role, "MISS", 0.6);
                    pause();
                } else {
                    Ammo ammo = player.weapon.compatibleAmmo.empty() ? ammoDatabase[0] : player.weapon.compatibleAmmo[0];
                    int pr = randInt(0, 6);
                    BodyPart hp = (BodyPart)pr;
                    if (hp == BodyPart::HEAD && randDouble(0, 1) < 0.7) hp = BodyPart::CHEST;
                    simulateShot(player, enemy, ammo, hp, player.weapon, false);
                    player.weapon.currentAmmo--;
                }
            }
        }
        if (needsBoltCycle(player.weapon)) {
            player.boltCooldown = 1;
            std::cout << "\n  [需要拉栓,下一回合无法开火]" << std::endl;
        }
    }

    void enemyFire(Character& enemy, bool& wasInDesperate) {
        if (enemy.boltCooldown > 0) {
            std::cout << "\n敌人正在拉栓,本回合无法开火。" << std::endl;
            enemy.boltCooldown--;
            pause();
            return;
        }
        if (enemy.weapon.currentAmmo <= 0) {
            std::cout << "敌人手忙脚乱地换弹..." << std::endl;
            pause();
            enemy.weapon.reload();
            return;
        }
        int shots = getShotsPerFire(enemy.weapon);
        shots = std::min(shots, enemy.weapon.currentAmmo);
        for (int i = 0; i < shots; ++i) {
            if (!player.isAlive() || enemy.weapon.currentAmmo <= 0) break;
            if (i > 0) {
                std::cout << "\n  [敌人连发第 " << i + 1 << " 发]" << std::endl;
                if (randDouble(0, 1) < BURST_2ND_HIT_PENALTY * i) {
                    std::cout << "  敌人枪口跳动,打偏了!" << std::endl;
                    enemy.weapon.currentAmmo--;
                    pause();
                    continue;
                }
            }
            Ammo ammo = enemy.weapon.compatibleAmmo.empty() ? ammoDatabase[0] : enemy.weapon.compatibleAmmo[0];
            int pr = randInt(0, 6);
            BodyPart hp = (BodyPart)pr;
            double avoidHead = enemy.isBoss ? 0.30 : 0.75;
            if (hp == BodyPart::HEAD && randDouble(0, 1) < avoidHead) hp = BodyPart::CHEST;
            simulateShot(enemy, player, ammo, hp, enemy.weapon, true);
            enemy.weapon.currentAmmo--;
            if (player.role == Role::ANNA && player.health < player.maxHealth * ANNA_HP_THRESHOLD) wasInDesperate = true;
            if (player.isAlive() && enemy.weapon.currentAmmo > 0) pause();
        }
        if (player.isAlive() && canExtraFire(enemy.weapon) && enemy.weapon.currentAmmo > 0) {
            std::cout << "\n  [敌人快速补枪!]" << std::endl;
            if (randDouble(0, 1) < EXTRA_FIRE_HIT_PENALTY) {
                std::cout << "  追加射击打偏了!" << std::endl;
                enemy.weapon.currentAmmo--;
                pause();
            } else {
                Ammo ammo = enemy.weapon.compatibleAmmo.empty() ? ammoDatabase[0] : enemy.weapon.compatibleAmmo[0];
                int pr = randInt(0, 6);
                BodyPart hp = (BodyPart)pr;
                double avoidHead = enemy.isBoss ? 0.30 : 0.75;
                if (hp == BodyPart::HEAD && randDouble(0, 1) < avoidHead) hp = BodyPart::CHEST;
                simulateShot(enemy, player, ammo, hp, enemy.weapon, true);
                enemy.weapon.currentAmmo--;
                if (player.role == Role::ANNA && player.health < player.maxHealth * ANNA_HP_THRESHOLD) wasInDesperate = true;
            }
        }
        if (needsBoltCycle(enemy.weapon)) {
            enemy.boltCooldown = 1;
        }
    }

    void battleWith(Character& enemy) {
        player.flashbangUsed = false; player.stunnedTurns = 0; enemy.stunnedTurns = 0;
        player.lowHealthVoiceTriggered = false; player.boltCooldown = 0; enemy.boltCooldown = 0;
        player.stunGrenades = 0; player.skillCooldown = 0;
        player.annaBeaconAvailable = (player.role == Role::ANNA);
        player.annaBeaconActive = false;
        enemy.medicalDelay = 0; enemy.interferenceTriggered = false;
        std::cout << "\n========== " << (enemy.isBoss ? "BOSS 战" : "遭遇战") << " ==========" << std::endl; pause();
        speakBattleMono(player.role, enemy.isBoss ? "BOSS_ENCOUNTER" : "ENCOUNTER", 1.0);
        std::cout << "对方持着 " << enemy.weapon.name << "(" << enemy.weapon.category << ",射速 " << enemy.weapon.fireRate << ")" << std::endl; pause();
        if (enemy.armor.owned) {
            std::cout << "身上穿着 "; printArmorNameColored(enemy.armor.name, enemy.armor.armorClass);
            std::cout << "(" << armorClassName(enemy.armor.armorClass) << ")" << std::endl;
        } else std::cout << "身上没有护甲。" << std::endl;
        if (enemy.helmet.owned) {
            std::cout << "头上戴着 "; printArmorNameColored(enemy.helmet.name, enemy.helmet.armorClass);
            std::cout << "(" << armorClassName(enemy.helmet.armorClass) << ",跳弹 " << (int)(enemy.helmet.ricochetChance * 100) << "%)" << std::endl;
        }
        pause();
        std::cout << "你握紧了 " << player.weapon.name << "(射速 " << player.weapon.fireRate << ")。" << std::endl; pause();
        double ps = player.getEffectiveSpeed(), es = enemy.getEffectiveSpeed();
        bool pif = (ps > es), eif = (ps < es);
        if (pif) std::cout << "\n  [你移速更快——由你开第一枪]" << std::endl;
        else if (eif) std::cout << "\n  [敌人移速更快——他先出手]" << std::endl;
        else std::cout << "\n  [双方速度相当——由你先出手]" << std::endl;
        pause();

        bool wasInDesperate = false;
        int round = 0;
        while (player.isAlive() && enemy.isAlive() && round < 30) {
            round++;
            if (player.role == Role::ANNA && player.health < player.maxHealth * ANNA_HP_THRESHOLD) wasInDesperate = true;
            bool pActs;
            if (round == 1) pActs = pif ? true : (eif ? false : true);
            else pActs = pif ? (round % 2 == 1) : (eif ? (round % 2 == 0) : (round % 2 == 1));
            std::cout << "\n---------- 第 " << round << " 回合 ----------" << std::endl;
            std::cout << (pActs ? "【你的回合】" : "【敌人的回合】") << std::endl;
            std::cout << "你: " << player.health << "/" << player.maxHealth
                      << " | 弹匣 " << player.weapon.currentAmmo << "/" << player.weapon.magazineSize;
            if (!player.weapon.compatibleAmmo.empty()) std::cout << " | 备用 " << player.weapon.compatibleAmmo[0].quantity << " 发";
            if (player.role == Role::KATYUSHA) std::cout << " | 闪避 " << (int)(player.getEffectiveDodge() * 100) << "%";
            if (player.role == Role::IVAN) std::cout << " | 震爆弹 " << player.stunGrenades << "/2";
            if (player.role == Role::IVAN && player.isIvanDeathRush()) std::cout << " | 死亡突袭!";
            if (player.role == Role::TOKAREV && player.skillCooldown == 0) std::cout << " | 最后一击待用";
            if (player.role == Role::ANNA && player.annaBeaconAvailable) std::cout << " | 信标可用";
            if (player.role == Role::ANNA && player.annaBeaconActive) std::cout << " | 信标激活";
            if (player.hasPainPenalty()) std::cout << " | 疼痛发作";
            if (player.isAnnaDesperate()) std::cout << " | 绝境翻盘!";
            if (player.isAlexanderDesperate()) std::cout << " | 突击状态!";
            if (player.boltCooldown > 0) std::cout << " | [需拉栓]";
            std::cout << std::endl;
            std::cout << (enemy.isBoss ? "【BOSS】" : "敌") << ": " << enemy.health << "/" << enemy.maxHealth;
            if (enemy.armor.owned) std::cout << " | 护甲 " << enemy.armor.currentDurability << "/" << enemy.armor.maxDurability;
            if (enemy.helmet.owned) std::cout << " | 头盔 " << enemy.helmet.currentDurability << "/" << enemy.helmet.maxDurability;
            if (enemy.isSuppressedByTokarev()) std::cout << " | 【被压制】";
            if (enemy.isBoss) std::cout << " | 【" << enemy.skillName << "】";
            if (enemy.stunnedTurns > 0) std::cout << " | 【闪光眩晕 " << enemy.stunnedTurns << " 回合】";
            if (enemy.hasBuff(BuffType::STUN)) std::cout << " | 【眩晕】";
            if (enemy.boltCooldown > 0) std::cout << " | [拉栓中]";
            if (enemy.medicalDelay > 0) std::cout << " | [医疗干扰 " << enemy.medicalDelay << "]";
            std::cout << std::endl;
            if (!player.buffs.empty()) {
                std::cout << "你的状态: ";
                for (auto& kv : player.buffs) if (kv.second > 0) std::cout << buffTypeName(kv.first) << "(" << buffDurationStr(kv.second) << ") ";
                std::cout << std::endl;
            }
            if (!enemy.buffs.empty()) {
                std::cout << "敌方状态: ";
                for (auto& kv : enemy.buffs) if (kv.second > 0) std::cout << buffTypeName(kv.first) << "(" << buffDurationStr(kv.second) << ") ";
                std::cout << std::endl;
            }
            printBodyFigure(player);
            pause();

            if (pActs) {
                if (player.canUseFlashbang()) {
                    player.flashbangUsed = true; enemy.stunnedTurns = ALEXANDER_FLASH_TURNS;
                    std::cout << "\n>>> 【闪光突击】自动触发!" << std::endl; pause();
                    std::cout << "    亚历山大咬紧牙关,猛地拉掉闪光弹的保险环。\n    金属拉环弹飞,安全杆脱落,握片弹开——\n    '闭眼!'" << std::endl; pause();
                    std::cout << "    他侧身一甩,闪光弹划出一道弧线飞向敌人。\n    轰——!!" << std::endl; pause();
                    std::cout << "    刺眼的白光瞬间炸开。敌人惨叫一声,双手捂着眼睛踉跄后退。" << std::endl; pause();
                    std::cout << "  >>>【敌人被致盲," << ALEXANDER_FLASH_TURNS << " 回合无法行动】" << std::endl; pause();
                }
                std::cout << "\n你的行动:" << std::endl;
                std::cout << "  1. 开火" << std::endl;
                std::cout << "  2. 换弹" << std::endl;
                if (!player.inventory.empty()) std::cout << "  3. 使用药品/甲修" << std::endl;
                if (player.role == Role::IVAN && player.stunGrenades > 0) std::cout << "  4. 投掷震爆弹(剩余 " << player.stunGrenades << "/2)" << std::endl;
                if (player.role == Role::TOKAREV && player.skillCooldown == 0) std::cout << "  5. 【最后一击】" << std::endl;
                if (player.role == Role::ANNA && player.annaBeaconAvailable) std::cout << "  6. 【救援信标】(本场战斗一次)" << std::endl;
                if (!player.stimulators.empty()) std::cout << "  7. 使用针剂" << std::endl;
                std::cout << "请选择: ";
                int act = readInt();
                if (act == 2) {
                    std::cout << "你退出弹匣,插上新的..." << std::endl; pause();
                    speakBattleMono(player.role, "RELOAD", 0.3);
                    if (!player.weapon.compatibleAmmo.empty() && player.weapon.compatibleAmmo[0].quantity > 0) {
                        int sp = player.weapon.magazineSize - player.weapon.currentAmmo;
                        int h = player.weapon.compatibleAmmo[0].quantity;
                        int load = std::min(sp, h);
                        player.weapon.currentAmmo += load;
                        player.weapon.compatibleAmmo[0].quantity -= load;
                        std::cout << "装填 " << load << " 发 "; printAmmoNameColored(player.weapon.compatibleAmmo[0].name, player.weapon.compatibleAmmo[0].penetration);
                        std::cout << ",剩余备用 " << player.weapon.compatibleAmmo[0].quantity << " 发。" << std::endl;
                    } else std::cout << "没有备用弹药!" << std::endl;
                    pause();
                } else if (act == 3 && !player.inventory.empty()) {
                    std::cout << "选择使用哪件物品:" << std::endl;
                    for (size_t i = 0; i < player.inventory.size(); ++i) {
                        if (player.inventory[i].uses <= 0) continue;
                        std::cout << "  " << i + 1 << ". " << player.inventory[i].name << " (剩 " << player.inventory[i].uses << " 次)" << std::endl;
                    }
                    std::cout << "  0. 取消" << std::endl;
                    int m = readInt();
                    if (m >= 1 && m <= (int)player.inventory.size()) {
                        int partChoice = -1;
                        if (player.inventory[m-1].healAmount > 0) {
                            std::cout << "\n选择治疗部位:" << std::endl;
                            for (int i = 0; i < (int)BodyPart::COUNT; ++i) {
                                BodyPart bp = (BodyPart)i;
                                auto it = player.partHealth.find(bp);
                                auto mt = player.partMaxHealth.find(bp);
                                int cur = (it != player.partHealth.end()) ? it->second : 0;
                                int mx = (mt != player.partMaxHealth.end()) ? mt->second : 0;
                                std::cout << "  " << i + 1 << ". " << bodyPartName(bp) << " (" << cur << "/" << mx << ")" << std::endl;
                            }
                            std::cout << "  0. 自动(最低部位)" << std::endl;
                            int pc = readInt();
                            if (pc >= 1 && pc <= (int)BodyPart::COUNT) partChoice = pc - 1;
                        }
                        player.useMedicalItem(m - 1, partChoice);
                        std::cout << "已使用。" << std::endl;
                        pause();
                    }
                } else if (act == 4 && player.role == Role::IVAN && player.stunGrenades > 0) {
                    throwStunGrenade(enemy);
                } else if (act == 5 && player.role == Role::TOKAREV && player.skillCooldown == 0) {
                    useLastStrike(enemy);
                } else if (act == 6 && player.role == Role::ANNA && player.annaBeaconAvailable) {
                    player.annaBeaconAvailable = false;
                    player.annaBeaconActive = true;
                    player.health = std::min(player.maxHealth, player.health + ANNA_BEACON_INSTANT_HEAL);
                    std::cout << "\n>>> 【救援信标】安娜启动腕部信标,绿色脉冲扫过全身。" << std::endl; pause();
                    std::cout << "    恢复 " << ANNA_BEACON_INSTANT_HEAL << " 点生命,本场战斗内药品恢复量 ×" << ANNA_MEDICAL_HEAL_MULT << "。" << std::endl; pause();
                } else if (act == 7 && !player.stimulators.empty()) {
                    std::cout << "选择针剂:\n";
                    for (size_t i = 0; i < player.stimulators.size(); ++i)
                        std::cout << "  " << i + 1 << ". " << player.stimulators[i].name << " - " << player.stimulators[i].description << "\n";
                    std::cout << "  0. 取消\n请选择: ";
                    int si = readInt();
                    if (si >= 1 && si <= (int)player.stimulators.size()) {
                        player.useStimulator(si - 1);
                        std::cout << "已注射。\n"; pause();
                    }
                } else {
                    playerFire(enemy);
                }
            } else {
                if (enemy.stunnedTurns > 0) {
                    std::cout << "\n敌人被闪光弹致盲,捂着眼睛胡乱挥舞……" << std::endl; pause();
                    enemy.stunnedTurns--;
                    if (enemy.stunnedTurns > 0) std::cout << "  【敌人仍被眩晕,无法行动】(剩余眩晕回合:" << enemy.stunnedTurns << ")" << std::endl;
                    else std::cout << "  【敌人恢复视力】" << std::endl;
                    pause();
                } else {
                    std::cout << "\n敌人举起武器..." << std::endl; pause();
                    enemyFire(enemy, wasInDesperate);
                }
            }
            player.processBuffs();
            enemy.processBuffs();
            speakLowHealthLine(player);

            if (!player.isAlive() && player.role == Role::ANNA && wasInDesperate && !player.hasRevived) {
                std::cout << "\n>>> 安娜倒下了……视线在旋转,耳边的枪声变得遥远。" << std::endl; pause();
                std::cout << ">>> 鲜血从指缝间渗出,冰冷的地面贴着半边脸。" << std::endl; pause();
                std::cout << "挣扎着尝试自救?(1=是 / 0=否): ";
                int c = readInt();
                if (c == 1) {
                    std::cout << "\n>>> 【第一步】她用力撕开胸前的战术急救包。\n    金属搭扣应声弹开,里面是几件仅剩的急救物资。\n    手指在微微发抖,但每一个动作都是训练过千百次的。" << std::endl; pause();
                    std::cout << "\n>>> 【第二步】她抓起自动注射器,狠狠扎进大腿。\n    肾上腺素灌入血管,心脏像被人攥住后又猛地松开——\n    '呃啊——!'\n    剧痛,但意识被强行拽回了现实。" << std::endl; pause();
                    std::cout << "\n>>> 【第三步】她哆嗦着从急救包底层掏出便携AED。\n    撕开电极片,贴在自己胸口裸露的皮肤上。\n    屏幕亮起,一行红色小字在跳动:【可电击心率】。\n    'Clear……'\n    她用尽最后的力气按下按钮。\n    啪!——胸口猛地弓起,心脏重新开始跳动。\n    '咳……咳咳……!'" << std::endl; pause();
                    std::cout << "\n>>> 【第四步】她扯开止血带缠住伤口,用夹板草草固定断腿。\n    每一个动作都疼得眼前发黑,但她咬着牙没有停下。\n    腹部还在往外渗血,她只能用绷带胡乱缠了几圈。" << std::endl; pause();
                    std::cout << "\n>>> 【第五步】她挣扎着撑起上半身。" << std::endl; pause();
                    player.hasRevived = true; player.maxHealth = ANNA_WEAK_MAXHP; player.health = ANNA_REVIVE_HP;
                    player.partHealth[BodyPart::HEAD] = 5; player.partHealth[BodyPart::CHEST] = 12;
                    player.partHealth[BodyPart::STOMACH] = 10; player.partHealth[BodyPart::LEFT_ARM] = 8;
                    player.partHealth[BodyPart::RIGHT_ARM] = 8; player.partHealth[BodyPart::LEFT_LEG] = 9;
                    player.partHealth[BodyPart::RIGHT_LEG] = 9;
                    player.addBuff(BuffType::BLEEDING, 5); player.addBuff(BuffType::FRACTURE, 8);
                    player.addBuff(BuffType::STOMACH_INJURY, 8); player.addBuff(BuffType::PAIN, 4);
                    player.addBuff(BuffType::WEAK, ANNA_WEAK_TURNS);
                    std::cout << ">>> 安娜撑着膝盖,从血泊里缓缓站起。\n    血量上限降至 " << ANNA_WEAK_MAXHP << ",当前 " << ANNA_REVIVE_HP << " 血。\n    她获得了全部负面效果,且进入【虚弱】状态。" << std::endl;
                    std::cout << "  >>> 【向死而生】已激活——伤害额外 +10%!" << std::endl; pause();
                    std::cout << "\n>>> 她抬起头——\n    敌人早就不见了踪影。\n    地上只剩下一排弹壳、几滴血迹,和远处越走越远的脚步声。\n    '……走了啊。'" << std::endl; pause();
                    std::cout << "    安娜松了口气,身体一软,单膝跪在地上,大口喘着气。\n    '还没……还没结束。'" << std::endl; pause();
                    return;
                } else {
                    std::cout << ">>> 安娜的手垂了下去……\n    视野彻底黑了下去。" << std::endl; pause();
                    break;
                }
            }
            if (!player.isAlive() || !enemy.isAlive()) break;
            autoUseMedkit(player); autoUseMedkit(enemy);
            if (!player.isAlive() || !enemy.isAlive()) break;
        }

        if (!enemy.isAlive() && player.isAlive()) {
            speakBattleMono(player.role, enemy.isBoss ? "BOSS_KILL" : "KILL", 1.0);
            std::cout << "\n你喘着粗气,站在原地。\n面前的" << (enemy.isBoss ? "强敌" : "敌人") << "不再动弹。" << std::endl; pause();
            std::cout << "\n【战利品】敌方装备散落一地。" << std::endl; pause();
            std::vector<LootItem> drops;
            if (enemy.weapon.magazineSize > 0) {
                LootItem wl; wl.category = ItemCategory::WEAPON; wl.name = enemy.weapon.name;
                wl.value = enemy.weapon.price * 0.6; wl.quantity = 1; wl.weaponData = enemy.weapon; drops.push_back(wl);
            }
            if (enemy.armor.owned && enemy.armor.currentDurability > 0) {
                LootItem al; al.category = ItemCategory::ARMOR; al.name = enemy.armor.name;
                al.value = enemy.armor.price * 0.5; al.quantity = 1; al.armorData = enemy.armor; drops.push_back(al);
            }
            if (!enemy.weapon.compatibleAmmo.empty()) {
                LootItem am; am.category = ItemCategory::AMMO;
                am.name = enemy.weapon.compatibleAmmo[0].name + " 弹药";
                am.value = enemy.weapon.compatibleAmmo[0].pricePerRound * 0.5;
                am.quantity = enemy.weapon.compatibleAmmo[0].quantity + enemy.weapon.currentAmmo;
                am.ammoData = enemy.weapon.compatibleAmmo[0]; drops.push_back(am);
            }
            for (size_t mi = 0; mi < enemy.inventory.size(); ++mi) {
                LootItem ml; ml.category = ItemCategory::MEDICAL; ml.name = enemy.inventory[mi].name;
                ml.value = enemy.inventory[mi].price * 0.7; ml.quantity = 1; ml.medData = enemy.inventory[mi]; drops.push_back(ml);
            }
            for (size_t i = 0; i < drops.size(); ++i) {
                std::cout << "\n【"; printLootNameColored(drops[i]); std::cout << "】";
                if (drops[i].quantity > 1) std::cout << " x" << drops[i].quantity;
                std::cout << "\n  1. 拿取\n  2. 使用/装备\n  0. 不要\n请选择: ";
                int c = readInt();
                if (c == 1) {
                    if ((int)player.lootBag.size() >= player.lootCapacity) { std::cout << "  !背包已满,无法拾取。" << std::endl; pause(); }
                    else { player.lootBag.push_back(drops[i]); std::cout << "  已放入背包(" << player.lootBag.size() << "/" << player.lootCapacity << ")" << std::endl; pause(); }
                } else if (c == 2) { useOrEquipItem(drops[i]); pause(); }
            }
            if (enemy.isBoss) {
                std::cout << "\n################################\n#      BOSS 击破!            #\n################################\n"; pause();
                std::cout << "你击败了「" << enemy.name << "」!\n从对方身上搜出了一批高价值物资——" << std::endl; pause();
                std::vector<LootItem> bl = generateBossLoot();
                for (size_t i = 0; i < bl.size(); ++i) {
                    if ((int)player.lootBag.size() >= player.lootCapacity) { std::cout << "  !背包已满,无法拾取 "; printLootNameColored(bl[i]); std::cout << std::endl; pause(); continue; }
                    player.lootBag.push_back(bl[i]);
                    std::cout << "  + "; printLootNameColored(bl[i]);
                    std::cout << "(价值 " << bl[i].value << " 卢布)" << std::endl; pause();
                }
                std::cout << "\n战利品已放入口袋。别忘了撤离后找商人出售。" << std::endl; pause();
            }
            Container corp; corp.name = enemy.isBoss ? "Boss 的尸体" : "敌人尸体";
            corp.type = ContainerType::CORPSE; corp.searched = false;
            corp.dangerLevel = enemy.isBoss ? 30 : 5;
            mapContainers.push_back(corp);
            std::cout << "\n战场上多了一具可以搜刮的尸体。" << std::endl; pause();
        } else if (!player.isAlive()) {
            bool acr = (player.role == Role::ANNA && wasInDesperate && !player.hasRevived);
            if (player.role == Role::ANNA && enemy.isBoss && acr) playAnnaBossDefeatCG(enemy);
            else {
                std::cout << "\n你的视野开始模糊...\n膝盖撞上地面,手里的枪滑落。\n黑暗吞没了一切。" << std::endl; pause();
            }
        } else {
            std::cout << "\n战斗拖得太久了,双方各自拉开距离。" << std::endl; pause();
        }
    }

    void raid() {
        inRaid = true;
        std::cout << "\n################################\n#   进入 " << currentMap.name << "   #\n################################\n";
        std::cout << currentMap.description << "\n撤离时间窗口:" << RAID_MAX_TURNS << " 回合\n本局携带资金上限:" << currentMap.maxEntryMoney << " 卢布\n"; pause();
        generateMap();
        speakMono(player.role, MonoEvent::MAP_ENTER, 1.0);
        if (bossSpawned) {
            std::cout << "\n################################\n#   !!!警告!!!            #\n#   检测到 Boss 信号            #\n#   地图新增【Boss占领区】       #\n################################\n"; pause();
        }
        int turn = 0;
        while (player.isAlive() && inRaid) {
            if (turn >= RAID_MAX_TURNS) { std::cout << "\n[撤离时间窗口关闭]" << std::endl; pause(); break; }
            player.processBuffs();
            if (!player.isAlive()) { std::cout << "\n伤势过重,倒在了战区里……" << std::endl; pause(); break; }
            speakLowHealthLine(player);
            speakMono(player.role, MonoEvent::TURN_IDLE, 0.10);
            std::cout << "\n========== " << currentMap.name << " (剩余 " << (RAID_MAX_TURNS - turn) << ") ==========\n";
            std::cout << "生命 " << player.health << "/" << player.maxHealth << " | 弹匣 " << player.weapon.currentAmmo << "/" << player.weapon.magazineSize;
            if (!player.weapon.compatibleAmmo.empty()) std::cout << " (备用 " << player.weapon.compatibleAmmo[0].quantity << ")";
            if (player.role == Role::KATYUSHA) std::cout << " | 闪避 " << (int)(player.getEffectiveDodge() * 100) << "%";
            std::cout << " | 资金 " << player.money << " | 背包 " << player.lootBag.size() << "/" << player.lootCapacity << std::endl;
            if (!player.buffs.empty()) {
                std::cout << "状态: ";
                for (auto& kv : player.buffs) if (kv.second > 0) std::cout << buffTypeName(kv.first) << "(" << buffDurationStr(kv.second) << ") ";
                std::cout << std::endl;
            }
            printBodyFigure(player);
            std::cout << "\n可搜索目标:" << std::endl;
            for (size_t i = 0; i < mapContainers.size(); ++i) {
                std::cout << "  " << i + 1 << ". " << mapContainers[i].name << " (" << containerTypeName(mapContainers[i].type) << ")";
                if (mapContainers[i].type == ContainerType::BOSS_ZONE) std::cout << " ★★★ Boss ★★★";
                else std::cout << " 危险度 " << mapContainers[i].dangerLevel;
                if (mapContainers[i].locked) std::cout << " 【上锁:" << mapContainers[i].requiredKey << "】";
                if (mapContainers[i].searched) std::cout << " [已搜]";
                std::cout << std::endl;
            }
            std::cout << "\n[数字] 搜索 | [0] 查看状态 | [-1] 药品/甲修 | [-2] 撤离\n选择: ";
            int choice = readInt();
            if (choice == -2) {
                std::cout << "\n撤离中..." << std::endl; pause();
                int ac = 15 + currentMap.difficulty * 4;
                if (randInt(1, 100) <= ac) {
                    std::cout << "撤离点有埋伏!" << std::endl; pause();
                    Character enemy = createEnemy(currentMap.difficulty + 1);
                    battleWith(enemy);
                    if (!player.isAlive()) break;
                }
                std::cout << "成功撤离!" << std::endl;
                speakMono(player.role, MonoEvent::EXTRACT_SUCCESS, 1.0);
                pause();
                inRaid = false; break;
            } else if (choice == 0) { player.printStatus(); printLootBag(); }
            else if (choice == -1) {
                if (player.inventory.empty()) { std::cout << "没有药品。" << std::endl; continue; }
                for (size_t i = 0; i < player.inventory.size(); ++i) { if (player.inventory[i].uses <= 0) continue; std::cout << "  " << i + 1 << ". " << player.inventory[i].name << std::endl; }
                std::cout << "  0. 取消 | 选择: ";
                int m = readInt();
                if (m >= 1 && m <= (int)player.inventory.size()) {
                    int partChoice = -1;
                    if (player.inventory[m-1].healAmount > 0) {
                        std::cout << "\n选择治疗部位:" << std::endl;
                        for (int i = 0; i < (int)BodyPart::COUNT; ++i) {
                            BodyPart bp = (BodyPart)i;
                            auto it = player.partHealth.find(bp);
                            auto mt = player.partMaxHealth.find(bp);
                            int cur = (it != player.partHealth.end()) ? it->second : 0;
                            int mx = (mt != player.partMaxHealth.end()) ? mt->second : 0;
                            std::cout << "  " << i + 1 << ". " << bodyPartName(bp) << " (" << cur << "/" << mx << ")" << std::endl;
                        }
                        std::cout << "  0. 自动(最低部位)" << std::endl;
                        int pc = readInt();
                        if (pc >= 1 && pc <= (int)BodyPart::COUNT) partChoice = pc - 1;
                    }
                    player.useMedicalItem(m - 1, partChoice);
                }
            } else if (choice >= 1 && choice <= (int)mapContainers.size()) {
                int cost = 1;
                if (player.getEffectiveSpeed() < SLOW_SEARCH_THRESHOLD) cost = 2;
                if (turn + cost > RAID_MAX_TURNS) { std::cout << "剩余时间不够,请撤离。" << std::endl; continue; }
                turn += cost;
                searchContainer(choice - 1);
            }
        }
        if (!player.isAlive()) std::cout << "\n########## 你在" << currentMap.name << "阵亡了 ##########" << std::endl;
        inRaid = false;
    }
};

int main() {
    GameWorld world;
    world.mainMenu();
    std::cout << "\n按回车键退出..." << std::endl;
    std::cin.ignore(); std::cin.get();
    return 0;
}

共 9 条回复

Bobby

终于有main函数了

leo1009

顶顶顶

leo1009

main函数在最底下...

leo1009

因为你要打开工具,选择编译选项,勾选编译时加入命令:-std=c++14,才可以运行

Bobby

没有main函数是啥阴

huangpeizhou

不是,又针对Mac

zhaotianyu

为啥运行不了

leo1009

ddd

leo1009

顶