diff --git a/src/client/component/bots.cpp b/src/client/component/bots.cpp index 019242fd..731f1c0d 100644 --- a/src/client/component/bots.cpp +++ b/src/client/component/bots.cpp @@ -2,9 +2,11 @@ #include "loader/component_loader.hpp" #include "command.hpp" +#include "console.hpp" #include "scheduler.hpp" #include "network.hpp" #include "party.hpp" +#include "scripting.hpp" #include "game/game.hpp" #include "game/scripting/execution.hpp" @@ -12,6 +14,7 @@ #include #include #include +#include namespace bots { @@ -53,10 +56,8 @@ namespace bots return; } - static auto first_bot = true; - - const auto bot_name = game::SV_BotGetRandomName(); - const auto bot_ent = game::SV_AddBot(bot_name); + const auto* const bot_name = game::SV_BotGetRandomName(); + const auto* bot_ent = game::SV_AddBot(bot_name); if (bot_ent) { @@ -70,6 +71,58 @@ namespace bots }, scheduler::pipeline::server, 100ms); } } + + utils::hook::detour get_bot_name_hook; + std::vector bot_names{}; + + void load_bot_data() + { + static const char* bots_json = "h1-mod/bots.json"; + + std::string bots_content; + + if (!utils::io::read_file(bots_json, &bots_content)) + { + console::error(utils::string::va("%s was not found.\n", bots_json)); + return; + } + + rapidjson::Document bots; + bots.Parse(bots_content.data()); + + // must be in a ["name1, "name2", "name3"] format + if (!bots.IsArray()) + { + console::error("bots.json must be a array of names.\n"); + return; + } + + const auto names = bots.GetArray(); + for (const auto& name : names) + { + bot_names.emplace_back(name.Get()); + } + } + + static size_t bot_id = 0; + + const char* get_random_bot_name() + { + if (bot_names.empty()) + { + load_bot_data(); + } + + // only use bot names once, no dupes in names + if (!bot_names.empty() && bot_id < bot_names.size()) + { + bot_id %= bot_names.size(); + const auto& entry = bot_names.at(bot_id++); + return utils::string::va("%.*s", static_cast(entry.size()), entry.data()); + } + + return get_bot_name_hook.invoke(); + } } class component final : public component_interface @@ -82,6 +135,8 @@ namespace bots return; } + get_bot_name_hook.create(game::SV_BotGetRandomName, get_random_bot_name); + command::add("spawnBot", [](const command::params& params) { if (!can_add()) @@ -102,6 +157,13 @@ namespace bots scheduler::once(add_bot, scheduler::pipeline::server, 100ms * i); } }); + + // Clear bot names and reset ID on game shutdown to allow new names to be added without restarting + scripting::on_shutdown([] + { + bot_names.clear(); + bot_id = 0; + }); } }; }