// 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;
}
粤公网安备44010602015266号
共 9 条回复
终于有main函数了
顶顶顶
main函数在最底下...
因为你要打开工具,选择编译选项,勾选编译时加入命令:-std=c++14,才可以运行
没有main函数是啥阴
不是,又针对Mac
为啥运行不了
ddd
顶