diff --git a/plugins/arena/Arena.cpp b/plugins/arena/Arena.cpp index daaf294bf..68dd82562 100644 --- a/plugins/arena/Arena.cpp +++ b/plugins/arena/Arena.cpp @@ -42,7 +42,7 @@ namespace Plugins void Arena::ClearClientInfo(ClientId& client) { transferFlags[client] = ClientState::None; } /// Load the configuration - void Arena::LoadSettings() {} + void Arena::LoadSettings() { config = Serializer::LoadFromJson(L"config/arena.json"); } /** @ingroup Arena * @brief Returns true if the client is docked, returns false otherwise. diff --git a/plugins/arena/Arena.hpp b/plugins/arena/Arena.hpp index 5c0a3108e..f96f8b120 100644 --- a/plugins/arena/Arena.hpp +++ b/plugins/arena/Arena.hpp @@ -22,8 +22,6 @@ namespace Plugins std::string targetBase; std::string targetSystem; std::string restrictedSystem; - - }; std::unique_ptr config; // Non-reflectable fields diff --git a/plugins/autobuy/Autobuy.cpp b/plugins/autobuy/Autobuy.cpp index a72fbae67..9be7e7bd5 100644 --- a/plugins/autobuy/Autobuy.cpp +++ b/plugins/autobuy/Autobuy.cpp @@ -29,502 +29,513 @@ */ // Includes -#include "Autobuy.h" +#include "PCH.hpp" -namespace Plugins::Autobuy -{ - const std::unique_ptr global = std::make_unique(); - - void LoadPlayerAutobuy(ClientId client) - { - AutobuyInfo playerAutobuyInfo {}; - playerAutobuyInfo.missiles = Hk::Ini::GetCharacterIniBool(client, L"autobuy.missiles"); - playerAutobuyInfo.mines = Hk::Ini::GetCharacterIniBool(client, L"autobuy.mines"); - playerAutobuyInfo.torps = Hk::Ini::GetCharacterIniBool(client, L"autobuy.torps"); - playerAutobuyInfo.cd = Hk::Ini::GetCharacterIniBool(client, L"autobuy.cd"); - playerAutobuyInfo.cm = Hk::Ini::GetCharacterIniBool(client, L"autobuy.cm"); - playerAutobuyInfo.bb = Hk::Ini::GetCharacterIniBool(client, L"autobuy.bb"); - playerAutobuyInfo.repairs = Hk::Ini::GetCharacterIniBool(client, L"autobuy.repairs"); - global->autobuyInfo[client] = playerAutobuyInfo; - } - - void ClearClientInfo(ClientId& client) - { - global->autobuyInfo.erase(client); - } - - int PlayerGetAmmoCount(const std::list& cargoList, uint itemArchId) - { - if (const auto foundCargo = std::ranges::find_if(cargoList, [itemArchId](const CargoInfo& cargo) { return cargo.archId == itemArchId; }); - foundCargo != cargoList.end()) - { - return foundCargo->count; - } - - return 0; - } - - void handleRepairs(ClientId& client) - { - auto repairCost = static_cast(Archetype::GetShip(Players[client].shipArchetype)->hitPoints * (1 - Players[client].relativeHealth) / 3); - - std::set eqToFix; - - for (const auto& item : Players[client].equipDescList.equip) - { - if (!item.mounted || item.health == 1) - continue; - - const GoodInfo* info = GoodList_get()->find_by_archetype(item.archId); - if (!info) - continue; - - repairCost += static_cast(info->price * (1.0f - item.health) / 3); - eqToFix.insert(item.id); - } - - if (const uint playerCash = Hk::Player::GetCash(client).value(); playerCash < repairCost) - { - PrintUserCmdText(client, L"Insufficient Cash"); - return; - } - - if (repairCost) - { - PrintUserCmdText(client, std::format(L"Auto-Buy: Ship repair costed {}$", repairCost)); - Hk::Player::RemoveCash(client, repairCost); - } - - if (!eqToFix.empty()) - { - for (auto& item : Players[client].equipDescList.equip) - { - if (eqToFix.contains(item.id)) - item.health = 1.0f; - } - - auto& equip = Players[client].equipDescList.equip; - - if (&equip != &Players[client].lShadowEquipDescList.equip) - Players[client].lShadowEquipDescList.equip = equip; - - st6::vector eqVector; - for (auto& eq : equip) - { - if (eq.mounted) - eq.health = 1.0f; - eqVector.push_back(eq); - } - - HookClient->Send_FLPACKET_SERVER_SETEQUIPMENT(client, eqVector); - } - - if (auto& playerCollision = Players[client].collisionGroupDesc.data; !playerCollision.empty()) - { - st6::list componentList; - for (auto& colGrp : playerCollision) - { - auto* newColGrp = reinterpret_cast(colGrp.data); - newColGrp->componentHp = 1.0f; - componentList.push_back(*newColGrp); - } - PrintUserCmdText(client, std::format(L"Attempting to repair {} components.", playerCollision.size())); - HookClient->Send_FLPACKET_SERVER_SETCOLLISIONGROUPS(client, componentList); - } - - if (Players[client].relativeHealth < 1.0f) - { - Players[client].relativeHealth = 1.0f; - HookClient->Send_FLPACKET_SERVER_SETHULLSTATUS(client, 1.0f); - } - } - - void AddEquipToCart(const Archetype::Launcher* launcher, const std::list& cargo, std::list& cart, AutobuyCartItem& item, - const std::wstring_view& desc) - { - // TODO: Update to per-weapon ammo limits once implemented - item.archId = launcher->projectileArchId; - item.count = MAX_PLAYER_AMMO - PlayerGetAmmoCount(cargo, item.archId); - item.description = desc; - cart.emplace_back(item); - } - - AutobuyInfo& LoadAutobuyInfo(ClientId& client) - { - if (!global->autobuyInfo.contains(client)) - { - LoadPlayerAutobuy(client); - } - - return global->autobuyInfo[client]; - } - - void OnBaseEnter(BaseId& baseId, ClientId& client) - { - const AutobuyInfo& clientInfo = LoadAutobuyInfo(client); - - Archetype::Ship const* ship = Archetype::GetShip(Players[client].shipArchetype); - - // player cargo - int remHoldSize; - const auto cargo = Hk::Player::EnumCargo(client, remHoldSize); - if (cargo.has_error()) - { - return; - } - - // shopping cart - std::list cartList; - - if (clientInfo.bb) - { - // shield bats & nanobots - - uint nanobotsId; - pub::GetGoodID(nanobotsId, global->config->nanobot_nickname.c_str()); - uint shieldBatsId; - pub::GetGoodID(shieldBatsId, global->config->shield_battery_nickname.c_str()); - bool nanobotsFound = false; - bool shieldBattsFound = false; - for (auto& item : cargo.value()) - { - AutobuyCartItem aci; - if (item.archId == nanobotsId) - { - aci.archId = nanobotsId; - aci.count = ship->maxNanobots - item.count; - aci.description = L"Nanobots"; - cartList.push_back(aci); - nanobotsFound = true; - } - else if (item.archId == shieldBatsId) - { - aci.archId = shieldBatsId; - aci.count = ship->maxShieldBats - item.count; - aci.description = L"Shield Batteries"; - cartList.push_back(aci); - shieldBattsFound = true; - } - } - - if (!nanobotsFound) - { // no nanos found -> add all - AutobuyCartItem aci; - aci.archId = nanobotsId; - aci.count = ship->maxNanobots; - aci.description = L"Nanobots"; - cartList.push_back(aci); - } - - if (!shieldBattsFound) - { // no batts found -> add all - AutobuyCartItem aci; - aci.archId = shieldBatsId; - aci.count = ship->maxShieldBats; - aci.description = L"Shield Batteries"; - cartList.push_back(aci); - } - } - - if (clientInfo.cd || clientInfo.cm || clientInfo.mines || clientInfo.missiles || clientInfo.torps) - { - // add mounted equip to a new list and eliminate double equipment(such - // as 2x lancer etc) - std::list mountedList; - for (auto& item : cargo.value()) - { - if (!item.mounted) - continue; - - bool found = false; - for (const auto& mounted : mountedList) - { - if (mounted.archId == item.archId) - { - found = true; - break; - } - } - - if (!found) - mountedList.push_back(item); - } - - // check mounted equip - for (const auto& mounted : mountedList) - { - AutobuyCartItem aci; - Archetype::Equipment* eq = Archetype::GetEquipment(mounted.archId); - auto eqType = Hk::Client::GetEqType(eq); - - switch (eqType) - { - case ET_MINE: { - if (clientInfo.mines) - AddEquipToCart(static_cast(eq), cargo.value(), cartList, aci, L"Mines"); - - break; - } - case ET_CM: { - if (clientInfo.cm) - AddEquipToCart(static_cast(eq), cargo.value(), cartList, aci, L"Countermeasures"); - - break; - } - case ET_TORPEDO: { - if (clientInfo.torps) - AddEquipToCart(static_cast(eq), cargo.value(), cartList, aci, L"Torpedoes"); - - break; - } - case ET_CD: { - if (clientInfo.cd) - AddEquipToCart(static_cast(eq), cargo.value(), cartList, aci, L"Cruise Disrupters"); - - break; - } - case ET_MISSILE: { - if (clientInfo.missiles) - AddEquipToCart(static_cast(eq), cargo.value(), cartList, aci, L"Missiles"); - - break; - } - - default: - break; - } - } - } - - if (clientInfo.repairs) - { - handleRepairs(client); - } - - // search base in base-info list - BaseInfo const* bi = nullptr; - - if (auto foundBase = std::ranges::find_if(CoreGlobals::c()->allBases, [baseId](const BaseInfo& base) { return base.baseId == baseId; }); - foundBase != CoreGlobals::c()->allBases.end()) - { - bi = std::to_address(foundBase); - } - - if (!bi) - return; // base not found - - const auto cashErr = Hk::Player::GetCash(client); - if (cashErr.has_error()) - { - return; - } - - auto cash = cashErr.value(); - - for (auto& buy : cartList) - { - if (!buy.count || !Arch2Good(buy.archId)) - continue; - - // check if good is available and if player has the neccessary rep - bool goodAvailable = false; - for (const auto& available : bi->MarketMisc) - { - if (available.archId == buy.archId) - { - auto baseRep = Hk::Solar::GetAffiliation(bi->objectId); - if (baseRep.has_error()) - PrintUserCmdText(client, Hk::Err::ErrGetText(baseRep.error())); - - const auto playerRep = Hk::Player::GetRep(client, baseRep.value()); - if (playerRep.has_error()) - PrintUserCmdText(client, Hk::Err::ErrGetText(playerRep.error())); - - // good rep, allowed to buy - if (playerRep.value() >= available.rep) - goodAvailable = true; - break; - } - } - - if (!goodAvailable) - continue; // base does not sell this item or bad rep - auto goodPrice = Hk::Solar::GetCommodityPrice(baseId, buy.archId); - if (goodPrice.has_error()) - continue; // good not available - - const Archetype::Equipment* eq = Archetype::GetEquipment(buy.archId); - // will always fail for volume == 0, no need to worry about potential div by 0 - if (static_cast(remHoldSize) < std::ceil(eq->volume * static_cast(buy.count))) - { - // round to the nearest possible - auto newCount = static_cast(static_cast(remHoldSize) / eq->volume); - if (!newCount) - { - PrintUserCmdText(client, std::format(L"Auto-Buy({}): FAILED! Insufficient Cargo Space", buy.description)); - continue; - } - else - buy.count = newCount; - } - - if (uint uCost = (static_cast(goodPrice.value()) * buy.count); cash < uCost) - PrintUserCmdText(client, std::format(L"Auto-Buy({}): FAILED! Insufficient Credits", buy.description)); - else - { - Hk::Player::RemoveCash(client, uCost); - remHoldSize -= ((int)eq->volume * buy.count); - - // add the item, dont use addcargo for performance/bug reasons - // assume we only mount multicount goods (missiles, ammo, bots - Hk::Player::AddCargo(client, buy.archId, buy.count, false); - - PrintUserCmdText(client, std::format(L"Auto-Buy({}): Bought {} unit(s), cost: {}$", buy.description, buy.count, ToMoneyStr(uCost))); - } - } - Hk::Player::SaveChar(client); - } - - void UserCmdAutobuy(ClientId& client, const std::wstring& param) - { - AutobuyInfo& autobuyInfo = LoadAutobuyInfo(client); - - const std::wstring autobuyType = GetParam(param, ' ', 0); - const std::wstring newState = GetParam(param, ' ', 1); - - if (autobuyType.empty()) - { - PrintUserCmdText(client, L"Error: Invalid parameters"); - PrintUserCmdText(client, L"Usage: /autobuy []"); - PrintUserCmdText(client, L":"); - PrintUserCmdText(client, L"| info - display current autobuy-settings"); - PrintUserCmdText(client, L"| missiles - enable/disable autobuy for missiles"); - PrintUserCmdText(client, L"| torps - enable/disable autobuy for torpedos"); - PrintUserCmdText(client, L"| mines - enable/disable autobuy for mines"); - PrintUserCmdText(client, L"| cd - enable/disable autobuy for cruise disruptors"); - PrintUserCmdText(client, L"| cm - enable/disable autobuy for countermeasures"); - PrintUserCmdText(client, L"| bb - enable/disable autobuy for nanobots/shield batteries"); - PrintUserCmdText(client, L"| repairs - enable/disable automatic repair of ship and equipment"); - PrintUserCmdText(client, L"| all: enable/disable autobuy for all of the above"); - PrintUserCmdText(client, L"Examples:"); - PrintUserCmdText(client, L"| \"/autobuy missiles on\" enable autobuy for missiles"); - PrintUserCmdText(client, L"| \"/autobuy all off\" completely disable autobuy"); - PrintUserCmdText(client, L"| \"/autobuy info\" show autobuy info"); - } - - if (autobuyType == L"info") - { - PrintUserCmdText(client, std::format(L"Missiles: {}", autobuyInfo.missiles ? L"On" : L"Off")); - PrintUserCmdText(client, std::format(L"Mines: {}", autobuyInfo.mines ? L"On" : L"Off")); - PrintUserCmdText(client, std::format(L"Torpedos: {}", autobuyInfo.torps ? L"On" : L"Off")); - PrintUserCmdText(client, std::format(L"Cruise Disruptors: {}", autobuyInfo.cd ? L"On" : L"Off")); - PrintUserCmdText(client, std::format(L"Countermeasures: {}", autobuyInfo.cm ? L"On" : L"Off")); - PrintUserCmdText(client, std::format(L"Nanobots/Shield Batteries: {}", autobuyInfo.bb ? L"On" : L"Off")); - PrintUserCmdText(client, std::format(L"Repairs: {}", autobuyInfo.repairs ? L"On" : L"Off")); - return; - } - - if (newState.empty() || (newState != L"on" && newState != L"off")) - { - PrintUserCmdText(client, L"ERR invalid parameters"); - return; - } - - const auto fileName = Hk::Client::GetCharFileName(client); - std::string Section = "autobuy_" + StringUtils::wstos(fileName.value()); - - const bool enable = newState == L"on"; - if (autobuyType == L"all") - { - autobuyInfo.missiles = enable; - autobuyInfo.mines = enable; - autobuyInfo.torps = enable; - autobuyInfo.cd = enable; - autobuyInfo.cm = enable; - autobuyInfo.bb = enable; - autobuyInfo.repairs = enable; - Hk::Ini::SetCharacterIni(client, L"autobuy.missiles", StringUtils::stows(enable ? "true" : "false")); - Hk::Ini::SetCharacterIni(client, L"autobuy.mines", StringUtils::stows(enable ? "true" : "false")); - Hk::Ini::SetCharacterIni(client, L"autobuy.torps", StringUtils::stows(enable ? "true" : "false")); - Hk::Ini::SetCharacterIni(client, L"autobuy.cd", StringUtils::stows(enable ? "true" : "false")); - Hk::Ini::SetCharacterIni(client, L"autobuy.cm", StringUtils::stows(enable ? "true" : "false")); - Hk::Ini::SetCharacterIni(client, L"autobuy.bb", StringUtils::stows(enable ? "true" : "false")); - Hk::Ini::SetCharacterIni(client, L"autobuy.repairs", StringUtils::stows(enable ? "true" : "false")); - } - else if (autobuyType == L"missiles") - { - autobuyInfo.missiles = enable; - Hk::Ini::SetCharacterIni(client, L"autobuy.missiles", StringUtils::stows(enable ? "true" : "false")); - } - else if (autobuyType == L"mines") - { - autobuyInfo.mines = enable; - Hk::Ini::SetCharacterIni(client, L"autobuy.mines", StringUtils::stows(enable ? "true" : "false")); - } - else if (autobuyType == L"torps") - { - autobuyInfo.torps = enable; - Hk::Ini::SetCharacterIni(client, L"autobuy.torps", StringUtils::stows(enable ? "true" : "false")); - } - else if (autobuyType == L"cd") - { - autobuyInfo.cd = enable; - Hk::Ini::SetCharacterIni(client, L"autobuy.cd", StringUtils::stows(enable ? "true" : "false")); - } - else if (autobuyType == L"cm") - { - autobuyInfo.cm = enable; - Hk::Ini::SetCharacterIni(client, L"autobuy.cm", StringUtils::stows(enable ? "true" : "false")); - } - else if (autobuyType == L"bb") - { - autobuyInfo.bb = enable; - Hk::Ini::SetCharacterIni(client, L"autobuy.bb", StringUtils::stows(enable ? "true" : "false")); - } - else if (autobuyType == L"repairs") - { - autobuyInfo.repairs = enable; - Hk::Ini::SetCharacterIni(client, L"autobuy.repairs", StringUtils::stows(enable ? "true" : "false")); - } - else - { - PrintUserCmdText(client, L"ERR invalid parameters"); - return; - } - - Hk::Player::SaveChar(client); - PrintUserCmdText(client, L"OK"); - } - - // Define usable chat commands here - const std::vector commands = {{ - CreateUserCommand(L"/autobuy", L" ", UserCmdAutobuy, L"Sets up automatic purchases for consumables."), - }}; - - // Load Settings - void LoadSettings() - { - auto config = Serializer::JsonToObject(); - global->config = std::make_unique(config); - } -} // namespace Plugins::Autobuy - -using namespace Plugins::Autobuy; - -REFL_AUTO(type(Config), field(nanobot_nickname), field(shield_battery_nickname)) - -DefaultDllMainSettings(LoadSettings); - -extern "C" EXPORT void ExportPluginInfo(PluginInfo* pi) +#include "API/API.hpp" +#include "Autobuy.hpp" +#include "FLHook.hpp" + +namespace Plugins { - pi->name("Autobuy"); - pi->shortName("autobuy"); - pi->mayUnload(true); - pi->commands(&commands); - pi->returnCode(&global->returnCode); - pi->versionMajor(PluginMajorVersion::VERSION_04); - pi->versionMinor(PluginMinorVersion::VERSION_00); - pi->emplaceHook(HookedCall::FLHook__LoadSettings, &LoadSettings, HookStep::After); - pi->emplaceHook(HookedCall::FLHook__ClearClientInfo, &ClearClientInfo, HookStep::After); - pi->emplaceHook(HookedCall::IServerImpl__BaseEnter, &OnBaseEnter, HookStep::After); -} \ No newline at end of file + void Autobuy::LoadPlayerAutobuy(ClientId client) + { + AutobuyInfo playerAutobuyInfo{}; + playerAutobuyInfo.missiles = StringUtils::Cast(Hk::IniUtils::c()->GetFromPlayerFile(client, L"autobuy.missiles").value()); + playerAutobuyInfo.torps = StringUtils::Cast(Hk::IniUtils::c()->GetFromPlayerFile(client, L"autobuy.torps").value()); + playerAutobuyInfo.cd = StringUtils::Cast(Hk::IniUtils::c()->GetFromPlayerFile(client, L"autobuy.cd").value()); + playerAutobuyInfo.cm = StringUtils::Cast(Hk::IniUtils::c()->GetFromPlayerFile(client, L"autobuy.cm").value()); + playerAutobuyInfo.bb = StringUtils::Cast(Hk::IniUtils::c()->GetFromPlayerFile(client, L"autobuy.bb").value()); + playerAutobuyInfo.repairs = StringUtils::Cast(Hk::IniUtils::c()->GetFromPlayerFile(client, L"autobuy.repairs").value()); + autobuyInfo[client] = playerAutobuyInfo; + } + + void Autobuy::ClearClientInfo(ClientId& client) { autobuyInfo.erase(client); } + + int PlayerGetAmmoCount(const std::list& cargoList, uint itemArchId) + { + if (const auto foundCargo = std::ranges::find_if(cargoList, [itemArchId](const CargoInfo& cargo) { return cargo.archId == itemArchId; }); + foundCargo != cargoList.end()) + { + return foundCargo->count; + } + + return 0; + } + + void handleRepairs(ClientId& client) + { + auto repairCost = static_cast(Archetype::GetShip(Players[client].shipArchetype)->hitPoints * (1 - Players[client].relativeHealth) / 3); + + std::set eqToFix; + + for (const auto& item : Players[client].equipDescList.equip) + { + if (!item.mounted || item.health == 1) + { + continue; + } + + const GoodInfo* info = GoodList_get()->find_by_archetype(item.archId); + if (!info) + { + continue; + } + + repairCost += static_cast(info->price * (1.0f - item.health) / 3); + eqToFix.insert(item.id); + } + + if (const uint playerCash = Hk::Player::GetCash(client).Unwrap(); playerCash < repairCost) + { + PrintUserCmdText(client, L"Insufficient Cash"); + return; + } + + if (repairCost) + { + PrintUserCmdText(client, std::format(L"Auto-Buy: Ship repair costed {}$", repairCost)); + Hk::Player::RemoveCash(client, repairCost); + } + + if (!eqToFix.empty()) + { + for (auto& item : Players[client].equipDescList.equip) + { + if (eqToFix.contains(item.id)) + { + item.health = 1.0f; + } + } + + auto& equip = Players[client].equipDescList.equip; + + if (&equip != &Players[client].lShadowEquipDescList.equip) + { + Players[client].lShadowEquipDescList.equip = equip; + } + + st6::vector eqVector; + for (auto& eq : equip) + { + if (eq.mounted) + { + eq.health = 1.0f; + } + eqVector.push_back(eq); + } + + HookClient->Send_FLPACKET_SERVER_SETEQUIPMENT(client, eqVector); + } + + if (auto& playerCollision = Players[client].collisionGroupDesc.data; !playerCollision.empty()) + { + st6::list componentList; + for (auto& colGrp : playerCollision) + { + auto* newColGrp = reinterpret_cast(colGrp.data); + newColGrp->componentHp = 1.0f; + componentList.push_back(*newColGrp); + } + PrintUserCmdText(client, std::format(L"Attempting to repair {} components.", playerCollision.size())); + HookClient->Send_FLPACKET_SERVER_SETCOLLISIONGROUPS(client, componentList); + } + + if (Players[client].relativeHealth < 1.0f) + { + Players[client].relativeHealth = 1.0f; + HookClient->Send_FLPACKET_SERVER_SETHULLSTATUS(client, 1.0f); + } + } + + void Autobuy::AddEquipToCart(const Archetype::Launcher* launcher, const std::list& cargo, std::list& cart, AutobuyCartItem& item, + const std::wstring_view& desc) + { + // TODO: Update to per-weapon ammo limits once implemented + item.archId = launcher->projectileArchId; + item.count = MAX_PLAYER_AMMO - PlayerGetAmmoCount(cargo, item.archId); + item.description = desc; + cart.emplace_back(item); + } + + AutobuyInfo& Autobuy::LoadAutobuyInfo(ClientId& client) + { + if (!autobuyInfo.contains(client)) + { + LoadPlayerAutobuy(client); + } + + return autobuyInfo[client]; + } + + void Autobuy::OnBaseEnter(BaseId& baseId, ClientId& client) + { + const AutobuyInfo& clientInfo = LoadAutobuyInfo(client); + + const Archetype::Ship* ship = Archetype::GetShip(Players[client].shipArchetype); + + // player cargo + int remHoldSize; + const auto cargo = Hk::Player::EnumCargo(client, remHoldSize).Handle(); + + // shopping cart + std::list cartList; + + if (clientInfo.bb) + { + // shield bats & nanobots + + uint nanobotsId; + pub::GetGoodID(nanobotsId, config->nanobot_nickname.c_str()); + uint shieldBatsId; + pub::GetGoodID(shieldBatsId, config->shield_battery_nickname.c_str()); + bool nanobotsFound = false; + bool shieldBattsFound = false; + for (auto& item : cargo) + { + AutobuyCartItem aci; + if (item.archId == nanobotsId) + { + aci.archId = nanobotsId; + aci.count = ship->maxNanobots - item.count; + aci.description = L"Nanobots"; + cartList.push_back(aci); + nanobotsFound = true; + } + else if (item.archId == shieldBatsId) + { + aci.archId = shieldBatsId; + aci.count = ship->maxShieldBats - item.count; + aci.description = L"Shield Batteries"; + cartList.push_back(aci); + shieldBattsFound = true; + } + } + + if (!nanobotsFound) + { // no nanos found -> add all + AutobuyCartItem aci; + aci.archId = nanobotsId; + aci.count = ship->maxNanobots; + aci.description = L"Nanobots"; + cartList.push_back(aci); + } + + if (!shieldBattsFound) + { // no batts found -> add all + AutobuyCartItem aci; + aci.archId = shieldBatsId; + aci.count = ship->maxShieldBats; + aci.description = L"Shield Batteries"; + cartList.push_back(aci); + } + } + + if (clientInfo.cd || clientInfo.cm || clientInfo.mines || clientInfo.missiles || clientInfo.torps) + { + // add mounted equip to a new list and eliminate double equipment(such + // as 2x lancer etc) + std::list mountedList; + for (auto& item : cargo) + { + if (!item.mounted) + { + continue; + } + + bool found = false; + for (const auto& mounted : mountedList) + { + if (mounted.archId == item.archId) + { + found = true; + break; + } + } + + if (!found) + { + mountedList.push_back(item); + } + } + + // check mounted equip + for (const auto& mounted : mountedList) + { + AutobuyCartItem aci; + Archetype::Equipment* eq = Archetype::GetEquipment(mounted.archId); + auto eqType = Hk::Client::GetEqType(eq); + + switch (eqType) + { + case ET_MINE: + { + if (clientInfo.mines) + { + AddEquipToCart(static_cast(eq), cargo, cartList, aci, L"Mines"); + } + + break; + } + case ET_CM: + { + if (clientInfo.cm) + { + AddEquipToCart(static_cast(eq), cargo, cartList, aci, L"Countermeasures"); + } + + break; + } + case ET_TORPEDO: + { + if (clientInfo.torps) + { + AddEquipToCart(static_cast(eq), cargo, cartList, aci, L"Torpedoes"); + } + + break; + } + case ET_CD: + { + if (clientInfo.cd) + { + AddEquipToCart(static_cast(eq), cargo, cartList, aci, L"Cruise Disrupters"); + } + + break; + } + case ET_MISSILE: + { + if (clientInfo.missiles) + { + AddEquipToCart(static_cast(eq), cargo, cartList, aci, L"Missiles"); + } + + break; + } + + default: break; + } + } + } + + if (clientInfo.repairs) + { + handleRepairs(client); + } + + // search base in base-info list + const BaseInfo* bi = nullptr; + + if (auto foundBase = std::ranges::find_if(CoreGlobals::c()->allBases, [baseId](const BaseInfo& base) { return base.baseId == baseId; }); + foundBase != CoreGlobals::c()->allBases.end()) + { + bi = std::to_address(foundBase); + } + + if (!bi) + { + return; // base not found + } + + const auto cash= Hk::Player::GetCash(client).Handle(); + + for (auto& buy : cartList) + { + if (!buy.count || !Arch2Good(buy.archId)) + { + continue; + } + + // check if good is available and if player has the neccessary rep + bool goodAvailable = false; + for (const auto& available : bi->MarketMisc) + { + if (available.archId == buy.archId) + { + auto baseRep = Hk::Solar::GetAffiliation(bi->objectId).Handle(); + const auto playerRep = Hk::Player::GetRep(client, baseRep).Handle(); + + // good rep, allowed to buy + if (playerRep >= available.rep) + { + goodAvailable = true; + } + break; + } + } + + if (!goodAvailable) + { + continue; // base does not sell this item or bad rep + } + auto goodPrice = Hk::Solar::GetCommodityPrice(baseId, buy.archId).Raw(); + if (goodPrice.has_error()) + { + continue; // good not available + } + + const Archetype::Equipment* eq = Archetype::GetEquipment(buy.archId); + // will always fail for volume == 0, no need to worry about potential div by 0 + if (static_cast(remHoldSize) < std::ceil(eq->volume * static_cast(buy.count))) + { + // round to the nearest possible + auto newCount = static_cast(static_cast(remHoldSize) / eq->volume); + if (!newCount) + { + PrintUserCmdText(client, std::format(L"Auto-Buy({}): FAILED! Insufficient Cargo Space", buy.description)); + continue; + } + else + { + buy.count = newCount; + } + } + + if (uint uCost = (static_cast(goodPrice.value()) * buy.count); cash < uCost) + { + PrintUserCmdText(client, std::format(L"Auto-Buy({}): FAILED! Insufficient Credits", buy.description)); + } + else + { + Hk::Player::RemoveCash(client, uCost); + remHoldSize -= ((int)eq->volume * buy.count); + + // add the item, dont use addcargo for performance/bug reasons + // assume we only mount multicount goods (missiles, ammo, bots + Hk::Player::AddCargo(client, buy.archId, buy.count, false); + + PrintUserCmdText(client, std::format(L"Auto-Buy({}): Bought {} unit(s), cost: {}$", buy.description, buy.count, StringUtils::ToMoneyStr(uCost))); + } + } + Hk::Player::SaveChar(client); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // USER COMMANDS + /////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + + void Autobuy::UserCmdAutobuy(std::wstring_view autobuyType, std::wstring_view newState) + { + AutobuyInfo& autobuyInfo = LoadAutobuyInfo(client); + if (autobuyType.empty()) + { + PrintUserCmdText(client, L"Error: Invalid parameters"); + PrintUserCmdText(client, L"Usage: /autobuy []"); + PrintUserCmdText(client, L":"); + PrintUserCmdText(client, L"| info - display current autobuy-settings"); + PrintUserCmdText(client, L"| missiles - enable/disable autobuy for missiles"); + PrintUserCmdText(client, L"| torps - enable/disable autobuy for torpedos"); + PrintUserCmdText(client, L"| mines - enable/disable autobuy for mines"); + PrintUserCmdText(client, L"| cd - enable/disable autobuy for cruise disruptors"); + PrintUserCmdText(client, L"| cm - enable/disable autobuy for countermeasures"); + PrintUserCmdText(client, L"| bb - enable/disable autobuy for nanobots/shield batteries"); + PrintUserCmdText(client, L"| repairs - enable/disable automatic repair of ship and equipment"); + PrintUserCmdText(client, L"| all: enable/disable autobuy for all of the above"); + PrintUserCmdText(client, L"Examples:"); + PrintUserCmdText(client, L"| \"/autobuy missiles on\" enable autobuy for missiles"); + PrintUserCmdText(client, L"| \"/autobuy all off\" completely disable autobuy"); + PrintUserCmdText(client, L"| \"/autobuy info\" show autobuy info"); + } + + if (autobuyType == L"info") + { + PrintUserCmdText(client, std::format(L"Missiles: {}", autobuyInfo.missiles ? L"On" : L"Off")); + PrintUserCmdText(client, std::format(L"Mines: {}", autobuyInfo.mines ? L"On" : L"Off")); + PrintUserCmdText(client, std::format(L"Torpedos: {}", autobuyInfo.torps ? L"On" : L"Off")); + PrintUserCmdText(client, std::format(L"Cruise Disruptors: {}", autobuyInfo.cd ? L"On" : L"Off")); + PrintUserCmdText(client, std::format(L"Countermeasures: {}", autobuyInfo.cm ? L"On" : L"Off")); + PrintUserCmdText(client, std::format(L"Nanobots/Shield Batteries: {}", autobuyInfo.bb ? L"On" : L"Off")); + PrintUserCmdText(client, std::format(L"Repairs: {}", autobuyInfo.repairs ? L"On" : L"Off")); + return; + } + + if (newState.empty() || (newState != L"on" && newState != L"off")) + { + PrintUserCmdText(client, L"ERR invalid parameters"); + return; + } + + const auto fileName = Hk::Client::GetCharFileName(client).Handle(); + std::string Section = "autobuy_" + StringUtils::wstos(fileName); + + const bool enable = newState == L"on"; + if (autobuyType == L"all") + { + autobuyInfo.missiles = enable; + autobuyInfo.mines = enable; + autobuyInfo.torps = enable; + autobuyInfo.cd = enable; + autobuyInfo.cm = enable; + autobuyInfo.bb = enable; + autobuyInfo.repairs = enable; + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.missiles", StringUtils::stows(enable ? "true" : "false")); + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.mines", StringUtils::stows(enable ? "true" : "false")); + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.torps", StringUtils::stows(enable ? "true" : "false")); + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.cd", StringUtils::stows(enable ? "true" : "false")); + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.cm", StringUtils::stows(enable ? "true" : "false")); + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.bb", StringUtils::stows(enable ? "true" : "false")); + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.repairs", StringUtils::stows(enable ? "true" : "false")); + } + else if (autobuyType == L"missiles") + { + autobuyInfo.missiles = enable; + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.missiles", StringUtils::stows(enable ? "true" : "false")); + } + else if (autobuyType == L"mines") + { + autobuyInfo.mines = enable; + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.mines", StringUtils::stows(enable ? "true" : "false")); + } + else if (autobuyType == L"torps") + { + autobuyInfo.torps = enable; + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.torps", StringUtils::stows(enable ? "true" : "false")); + } + else if (autobuyType == L"cd") + { + autobuyInfo.cd = enable; + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.cd", StringUtils::stows(enable ? "true" : "false")); + } + else if (autobuyType == L"cm") + { + autobuyInfo.cm = enable; + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.cm", StringUtils::stows(enable ? "true" : "false")); + } + else if (autobuyType == L"bb") + { + autobuyInfo.bb = enable; + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.bb", StringUtils::stows(enable ? "true" : "false")); + } + else if (autobuyType == L"repairs") + { + autobuyInfo.repairs = enable; + Hk::IniUtils::c()->SetCharacterIni(client, L"autobuy.repairs", StringUtils::stows(enable ? "true" : "false")); + } + else + { + PrintUserCmdText(client, L"ERR invalid parameters"); + return; + } + + Hk::Player::SaveChar(client); + PrintUserCmdText(client, L"OK"); + } + + + using namespace Plugins; + + DefaultDllMain(); + + const PluginInfo Info(L"Autobuy", L"autobuy", PluginMajorVersion::VERSION_04, PluginMinorVersion::VERSION_01); + void Autobuy::LoadSettings() { config = Serializer::LoadFromJson(L"config/autobuy.json"); } + Autobuy::Autobuy(const PluginInfo info) : Plugin(Info) + { + EmplaceHook(HookedCall::FLHook__LoadSettings, &Autobuy::LoadSettings, HookStep::After); + EmplaceHook(HookedCall::FLHook__ClearClientInfo, &Autobuy::ClearClientInfo, HookStep::After); + EmplaceHook(HookedCall::IServerImpl__BaseEnter, &Autobuy::OnBaseEnter, HookStep::After); + } + SetupPlugin(Autobuy, Info); + +} // namespace Plugins diff --git a/plugins/autobuy/Autobuy.h b/plugins/autobuy/Autobuy.h deleted file mode 100644 index 488381a0a..000000000 --- a/plugins/autobuy/Autobuy.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include - -namespace Plugins::Autobuy -{ - //! A struct to represent each client - class AutobuyInfo - { - public: - AutobuyInfo() = default; - - bool missiles; - bool mines; - bool torps; - bool cd; - bool cm; - bool bb; - bool repairs; - }; - - struct AutobuyCartItem - { - uint archId = 0; - uint count = 0; - std::wstring description; - }; - - //! Configurable fields for this plugin - struct Config final : Reflectable - { - std::string File() override { return "config/autobuy.json"; } - - // Reflectable fields - //! Nickname of the nanobot item being used when performing the automatic purchase - std::string nanobot_nickname = "ge_s_repair_01"; - //! Nickname of the shield battery item being used when performing the automatic purchase - std::string shield_battery_nickname = "ge_s_battery_01"; - }; - - struct Global final - { - std::unique_ptr config = nullptr; - std::map autobuyInfo; - ReturnCode returnCode = ReturnCode::Default; - }; - -} // namespace Plugins::Autobuy \ No newline at end of file diff --git a/plugins/autobuy/Autobuy.hpp b/plugins/autobuy/Autobuy.hpp new file mode 100644 index 000000000..d83748276 --- /dev/null +++ b/plugins/autobuy/Autobuy.hpp @@ -0,0 +1,61 @@ +#pragma once + +namespace Plugins +{ + struct AutobuyInfo + { + bool missiles; + bool mines; + bool torps; + bool cd; + bool cm; + bool bb; + bool repairs; + }; + + class Autobuy final : public Plugin, public AbstractUserCommandProcessor + { + //! A struct to represent each client + + struct AutobuyCartItem + { + uint archId = 0; + uint count = 0; + std::wstring description; + }; + + //! Configurable fields for this plugin + struct Config + { + // Reflectable fields + //! Nickname of the nanobot item being used when performing the automatic purchase + std::string nanobot_nickname = "ge_s_repair_01"; + //! Nickname of the shield battery item being used when performing the automatic purchase + std::string shield_battery_nickname = "ge_s_battery_01"; + }; + + void UserCmdAutobuy(std::wstring_view autobuyType, std::wstring_view newState); + + constexpr inline static std::array, 1> commands = { + { + AddCommand(Autobuy, L"/autobuy", UserCmdAutobuy, L"/autobuy ", + L"Sets up automatic purchases for consumables."), + } + }; + + SetupUserCommandHandler(Autobuy, commands) + + std::unique_ptr config; + std::map autobuyInfo; + void LoadSettings(); + void OnBaseEnter(BaseId& baseId, ClientId& client); + void ClearClientInfo(ClientId& client); + void LoadPlayerAutobuy(ClientId client); + void AddEquipToCart(const Archetype::Launcher* launcher, const std::list& cargo, std::list& cart, AutobuyCartItem& item, + const std::wstring_view& desc); + AutobuyInfo& LoadAutobuyInfo(ClientId& client); + + public: + explicit Autobuy(const PluginInfo info); + }; +} // namespace Plugins diff --git a/plugins/autobuy/autobuy.vcxproj b/plugins/autobuy/autobuy.vcxproj index 7741677a2..ed2573d91 100644 --- a/plugins/autobuy/autobuy.vcxproj +++ b/plugins/autobuy/autobuy.vcxproj @@ -72,7 +72,7 @@ - +