From df7150503b49e672eb3ecdd132243299537cbf36 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 18 Mar 2026 03:31:40 -0700 Subject: [PATCH] feat: /assist now accepts name and macro conditional arguments /assist TankName targets whoever TankName is targeting; /assist [target=focus] assists your focus target. Mirrors /target and /focus conditional support. --- src/ui/game_screen.cpp | 74 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 274c0e89..2e23ac1c 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -6088,7 +6088,79 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) { // /assist command if (cmdLower == "assist") { - gameHandler.assistTarget(); + // /assist → assist current target (use their target) + // /assist PlayerName → find PlayerName, target their target + // /assist [target=X] → evaluate conditional, target that entity's target + auto assistEntityTarget = [&](uint64_t srcGuid) { + auto srcEnt = gameHandler.getEntityManager().getEntity(srcGuid); + if (!srcEnt) { gameHandler.assistTarget(); return; } + uint64_t atkGuid = 0; + const auto& flds = srcEnt->getFields(); + auto iLo = flds.find(game::fieldIndex(game::UF::UNIT_FIELD_TARGET_LO)); + if (iLo != flds.end()) { + atkGuid = iLo->second; + auto iHi = flds.find(game::fieldIndex(game::UF::UNIT_FIELD_TARGET_HI)); + if (iHi != flds.end()) atkGuid |= (static_cast(iHi->second) << 32); + } + if (atkGuid != 0) { + gameHandler.setTarget(atkGuid); + } else { + std::string sn = getEntityName(srcEnt); + game::MessageChatData msg; + msg.type = game::ChatType::SYSTEM; + msg.language = game::ChatLanguage::UNIVERSAL; + msg.message = (sn.empty() ? "Target" : sn) + " has no target."; + gameHandler.addLocalChatMessage(msg); + } + }; + + if (spacePos != std::string::npos) { + std::string assistArg = command.substr(spacePos + 1); + while (!assistArg.empty() && assistArg.front() == ' ') assistArg.erase(assistArg.begin()); + + // Evaluate conditionals if present + uint64_t assistOver = static_cast(-1); + if (!assistArg.empty() && assistArg.front() == '[') { + assistArg = evaluateMacroConditionals(assistArg, gameHandler, assistOver); + if (assistArg.empty() && assistOver == static_cast(-1)) { + chatInputBuffer[0] = '\0'; return; // no condition matched + } + while (!assistArg.empty() && assistArg.front() == ' ') assistArg.erase(assistArg.begin()); + while (!assistArg.empty() && assistArg.back() == ' ') assistArg.pop_back(); + } + + if (assistOver != static_cast(-1) && assistOver != 0) { + assistEntityTarget(assistOver); + } else if (!assistArg.empty()) { + // Name search + std::string argLow = assistArg; + for (char& c : argLow) c = static_cast(std::tolower(static_cast(c))); + uint64_t bestGuid = 0; float bestDist = std::numeric_limits::max(); + const auto& pmi = gameHandler.getMovementInfo(); + for (const auto& [guid, ent] : gameHandler.getEntityManager().getEntities()) { + if (!ent || ent->getType() == game::ObjectType::OBJECT) continue; + std::string nm = getEntityName(ent); + std::string nml = nm; + for (char& c : nml) c = static_cast(std::tolower(static_cast(c))); + if (nml.find(argLow) != 0) continue; + float d2 = (ent->getX()-pmi.x)*(ent->getX()-pmi.x) + + (ent->getY()-pmi.y)*(ent->getY()-pmi.y); + if (d2 < bestDist) { bestDist = d2; bestGuid = guid; } + } + if (bestGuid) assistEntityTarget(bestGuid); + else { + game::MessageChatData msg; + msg.type = game::ChatType::SYSTEM; + msg.language = game::ChatLanguage::UNIVERSAL; + msg.message = "No unit matching '" + assistArg + "' found."; + gameHandler.addLocalChatMessage(msg); + } + } else { + gameHandler.assistTarget(); + } + } else { + gameHandler.assistTarget(); + } chatInputBuffer[0] = '\0'; return; }