Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bot_core/bot_ctx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ static std::variant<nlohmann::json, const char*> LoadConfig(const char* const co
ErrorLog() << "LoadConfig set game '" << game_name << "' option failed: " << option_str;
}
}
if (game_json.contains("is_formal")) {
locked_option->generic_options_.is_formal_ = game_json["is_formal"].get<bool>();
InfoLog() << "LoadConfig set game '" << game_name << "' is_formal: " << static_cast<int>(locked_option->generic_options_.is_formal_);
}
}
return j;
}
Expand Down
6 changes: 5 additions & 1 deletion bot_core/match.cc
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,12 @@ void Match::Eliminate(const PlayerID pid)
{
if (std::exchange(players_[pid].state_, Player::State::ELIMINATED) != Player::State::ELIMINATED) {
Tell(pid) << "很遗憾,您被淘汰了,可以通过「" META_COMMAND_SIGN "退出」以退出游戏";
is_in_deduction_ = std::ranges::all_of(players_,
// Enter deduction mode only if all players are eliminated AND there is at least one alive computer player
const bool all_players_eliminated = std::ranges::all_of(players_,
[](const auto& p) { return std::get_if<ComputerID>(&p.id_) || p.state_ == Player::State::ELIMINATED; });
const bool has_alive_computer = std::ranges::any_of(players_,
[](const auto& p) { return std::get_if<ComputerID>(&p.id_) && p.state_ != Player::State::ELIMINATED; });
is_in_deduction_ = all_players_eliminated && has_alive_computer;
MatchLog_(InfoLog()) << "Eliminate player pid=" << pid << " is_in_deduction=" << Bool2Str(is_in_deduction_);
}
}
Expand Down
3 changes: 2 additions & 1 deletion game_framework/stage_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ void PublicStageUtility::StopTimer()
void PublicStageUtility::Leave(const PlayerID pid)
{
masker_.SetPermanentInactive(pid);
if (IsInDeduction()) {
if (IsInDeduction() && !is_in_deduction_) {
is_in_deduction_ = true;
match_.GroupMsgSender()() << "所有玩家都失去了行动能力,于是游戏将直接推演至终局";
}
}
Expand Down
4 changes: 3 additions & 1 deletion game_framework/stage_utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ class PublicStageUtility

private:
// TODO: Stage should identify which players are computers. `match_.IsInDeduction()` condition should be removed.
bool IsInDeduction() const { return match_.IsInDeduction() || masker_.IsAllPermanentInactive(); }
// bool IsInDeduction() const { return match_.IsInDeduction() || masker_.IsAllPermanentInactive(); }
bool IsInDeduction() const { return match_.IsInDeduction(); }

void Leave(const PlayerID pid);

Expand All @@ -110,6 +111,7 @@ class PublicStageUtility
int32_t bot_message_id_{0}; // the ID of each bot message
int32_t saved_image_no_{0};
std::optional<std::chrono::time_point<std::chrono::steady_clock>> timer_finish_time_;
bool is_in_deduction_{false};
};

class AtomicStage;
Expand Down
7 changes: 7 additions & 0 deletions games/escape_building/achievements.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// 游戏成就(未实现)
// EXTEND_ACHIEVEMENT(闪电终结, "在到达第 10 楼前提前获得胜利")
// EXTEND_ACHIEVEMENT(致命精准, "杀手出刀全数命中,无一次落空")
// EXTEND_ACHIEVEMENT(风平浪静, "全程未看到任何黑影,独自撤离大楼")
// EXTEND_ACHIEVEMENT(超额解救, "成功解救 4 名人质并安全撤离大楼")
// EXTEND_ACHIEVEMENT(满载而归, "成功解救 5 名人质并安全撤离大楼")
// EXTEND_ACHIEVEMENT(完美救援, "成功接到杀手弹出的所有人质并撤离大楼")
122 changes: 122 additions & 0 deletions games/escape_building/hints.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* ========== 游戏文案 ========== */
std::string GetRandomHint(std::span<const std::string_view> hints) {
return std::string(hints[rand() % hints.size()]);
}

// 黑影
const std::array<std::string_view, 8> shadow_hints = {
"电梯门缓缓开启……一道【黑影】疾冲而来,空气像被利刃划开!",
"电梯门缓缓开启……一道【黑影】闪了过来!",
"电梯门缓缓开启……一道【黑影】从走廊深处扑向门口,速度快得令人心悸!",
"电梯门像缓慢呼吸般开启……一道【黑影】一闪而过!",
"电梯门缓缓开启……【黑影】从走廊尽头疾冲而来,黑暗里隐藏着谜一样的意图。",
"电梯门缓缓开启……视线晃动,一道【黑影】迎面扑来!",
"电梯门缓缓开启……走廊尽头的【黑影】突然加速,直冲而来!",
"电梯门缓缓开启……【黑影】在灯光下拉长,随即冲进你的视线。",
};

// 烟雾弹
const std::array<std::string_view, 6> smoke_hints = {
"电梯门缓缓开启……浓烟瞬间吞没视线,什么也看不清。",
"电梯门缓缓开启……烟雾弥漫,视线被封锁。",
"电梯门缓缓开启……眼前骤起浓烟,世界瞬间模糊。",
"电梯门缓缓开启……浓烟弥漫,眼前一片模糊。",
"电梯门缓缓开启……烟雾翻滚,所有轮廓都被抹去。",
"电梯门缓缓开启……灰白色的烟雾占据了一切。",
};

// 无事发生
const std::array<std::string_view, 8> empty_hints = {
"电梯门缓缓开启……走廊空无一人,只有死一般的寂静。",
"电梯门缓缓开启……空气凝固,却没有任何人出现。",
"电梯门缓缓开启……你屏住呼吸,却什么也没发生。",
"电梯门缓缓开启……黑暗静止不动,一切归于沉默。",
"电梯门缓缓开启……紧张蔓延开来,却没有任何动静。",
"电梯门缓缓开启……你盯着前方,却只看到空荡的走廊。",
"电梯门缓缓开启……走廊安静得令人不安。",
"电梯门缓缓开启……没有危险,也没有答案。",
};

// 击杀三个人质
const std::array<std::string_view, 7> police_lose_hints = {
"枪声再次响起,人质倒下。这是第三个。\n你站在原地,却已经无法分辨敌我。\n任务失败。",
"第三名人质倒下。电梯内一片死寂。\n黑影不再闪现,它们已经住进你的脑海。\n任务失败。",
"又一声枪响,人质倒地。\n判断彻底崩溃,你失去了继续行动的资格。\n任务失败。",
"人质伤亡已无法挽回。\n你的选择导致了不可逆的后果。\n行动被强制终止。",
"第三名人质倒下。\n这一刻,你终于意识到自己已经无法控制局面。\n任务失败。",
"枪声在走廊里回荡。\n这是最后一根防线的崩塌。\n你已无法继续执行任务。",
"人质连续倒下。\n恐惧与误判占据了一切。\n行动宣告失败。",
};

// 开枪命中杀手
const std::array<std::string_view, 6> shoot_killer_hints = {
"枪声炸响!子弹命中目标,黑影中弹后踉跄后退。你击中了杀手。",
"枪火撕裂黑暗。子弹准确命中正在逼近的杀手,他发出一声闷哼。",
"你果断开枪。黑影应声受创,杀手被迫停下脚步。",
"子弹击中目标。黑影剧烈晃动,杀手显然受了伤。",
"枪声回荡。这一次,你的判断是正确的——杀手中弹。",
"黑暗被枪火照亮。子弹命中杀手,他的攻势被阻止。"
};

// 开枪误杀人质
const std::array<std::string_view, 5> shoot_hostage_hints = {
"枪声回荡。黑影倒下后,你才意识到那是一名人质。",
"你扣下扳机。人质倒地,走廊里只剩下回声。",
"子弹命中目标。短暂的沉默后,你发现自己犯下了错误。",
"枪火熄灭。倒在地上的,并不是杀手。",
"你选择了射击。这一枪,击中了不该被击中的人。",
};

// 开枪误杀更多人质
const std::array<std::string_view, 6> shoot_more_hostage_hints = {
"你再次扣下扳机。又一名人质倒下,黑影不再消失,仿佛就在你身边。",
"枪声响起。人质倒地,你的视线开始被挥之不去的黑影占据。",
"判断再次出错。人质死亡,黑影在走廊里频繁闪现。",
"你试图控制局面,却再次失手。人质倒下,幻觉开始侵蚀你的视线。",
"枪火亮起。这一刻,你已经无法确定自己看见的是否真实。",
"人质死亡。恐惧和幻影交织在一起,你的判断愈发混乱。",
};

// 开枪没有命中
const std::array<std::string_view, 5> shoot_empty_hints = {
"枪声回荡。子弹射入黑暗,没有命中任何目标。",
"你开枪了。子弹消失在走廊深处,什么也没有发生。",
"枪火短暂亮起。黑暗没有给予任何回应。",
"子弹击中远处的墙壁。这一枪没有改变任何局势。",
"枪声渐渐消散,什么也没有击中。但危险并未因此减少。",
};

// 接被杀手刺伤
const std::array<std::string_view, 4> killer_stab_hints = {
"你放下了枪。寒光一闪,利刃入体!你被杀手刺伤!",
"你来不及反应。黑影贴近,利刃狠狠刺入身体。",
"距离被瞬间拉近。下一秒,疼痛袭来——杀手得手了。",
"你试图后退,但已经太迟。杀手的刀锋划破了你的防线。",
};

// 解救人质
const std::array<std::string_view, 4> rescue_hostage_hint = {
"你伸开手臂,将对方拉回电梯。成功解救了一名人质。",
"黑影停下了脚步,那是一名惊魂未定的人质。",
"你确认了对方的身份。人质被安全带离危险区域。",
"紧张的气氛中,你做出了正确的选择。人质获救。",
};

// 什么都没有接到
const std::array<std::string_view, 5> get_empty_hint = {
"你放下了枪。外面什么也没有,电梯内一片死寂。",
"你等待着。走廊空无一人,只有自己的呼吸声。",
"什么都没有发生。紧张却没有随之消散。",
"电梯门外一片安静。你什么也没接到。",
"你站在原地,什么也没有发生,只剩下心跳声在回荡。",
};

// 无子弹被杀手刺伤
const std::array<std::string_view, 1> no_bullet_killer_stab_hints = {
"你尝试扣动扳机,却只听见空响——子弹已经耗尽,杀手的利刃刺入了你的身体!",
};

// 无子弹遇到烟雾弹
const std::array<std::string_view, 1> no_bullet_smoke_hints = {
"浓烟翻滚,却没有任何人出现。",
};
Binary file added games/escape_building/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading