From 90f62dc956fa47ad9433f25ac161933adbc373d2 Mon Sep 17 00:00:00 2001 From: rt Date: Mon, 18 Dec 2017 02:32:52 +0100 Subject: [PATCH] fix #5854 / do fewer allocs in SelUnitsAI --- rts/Game/SelectedUnitsAI.cpp | 630 ++++++++++++++++++----------------- rts/Game/SelectedUnitsAI.h | 47 +-- rts/Game/UI/GuiHandler.cpp | 95 +++--- rts/Game/UI/GuiHandler.h | 2 +- 4 files changed, 406 insertions(+), 368 deletions(-) diff --git a/rts/Game/SelectedUnitsAI.cpp b/rts/Game/SelectedUnitsAI.cpp index 9e8b0cf3c0a..195c81d5dc2 100644 --- a/rts/Game/SelectedUnitsAI.cpp +++ b/rts/Game/SelectedUnitsAI.cpp @@ -22,32 +22,18 @@ const int CMDPARAM_MOVE_X = 0; const int CMDPARAM_MOVE_Y = 1; const int CMDPARAM_MOVE_Z = 2; +typedef std::vector TGroupVect; +typedef std::pair TGroupPair; + +static const auto ugPairComp = [](const TGroupPair& a, const TGroupPair& b) { return (a.first < b.first); }; +static const auto idPairComp = [](const std::pair& a, const std::pair& b) { return (a.first < b.first); }; + // Global object CSelectedUnitsHandlerAI selectedUnitsAI; -CSelectedUnitsHandlerAI::CSelectedUnitsHandlerAI() - : centerPos(ZeroVector) - , rightPos(ZeroVector) - , sumLength(0) - , avgLength(0.0f) - , frontLength(0.0f) - , addSpace(0.0f) - , minCoor(ZeroVector) - , maxCoor(ZeroVector) - , centerCoor(ZeroVector) - , minMaxSpeed(0.0f) - - , frontDir(ZeroVector) - , sideDir(ZeroVector) - , columnDist(64.0f) - , numColumns(0) -{ -} - -inline void CSelectedUnitsHandlerAI::AddUnitSetMaxSpeedCommand(CUnit* unit, - unsigned char options) +inline void CSelectedUnitsHandlerAI::AddUnitSetMaxSpeedCommand(CUnit* unit, unsigned char options) { // this sets the WANTED maximum speed of // (via the CommandAI --> MoveType chain) to be @@ -56,26 +42,23 @@ inline void CSelectedUnitsHandlerAI::AddUnitSetMaxSpeedCommand(CUnit* unit, // scripts) CCommandAI* cai = unit->commandAI; - if (cai->CanSetMaxSpeed()) { - Command c(CMD_SET_WANTED_MAX_SPEED, options, unit->moveType->GetMaxSpeed()); + if (!cai->CanSetMaxSpeed()) + return; - cai->GiveCommand(c, false); - } + cai->GiveCommand(Command(CMD_SET_WANTED_MAX_SPEED, options, unit->moveType->GetMaxSpeed()), false); } -inline void CSelectedUnitsHandlerAI::AddGroupSetMaxSpeedCommand(CUnit* unit, - unsigned char options) +inline void CSelectedUnitsHandlerAI::AddGroupSetMaxSpeedCommand(CUnit* unit, unsigned char options) { - // sets the wanted speed of this unit to that of - // the group's current-slowest member (minMaxSpeed + // sets the wanted speed of this unit to that of the + // group's current-slowest member (groupMinMaxSpeed // is derived from GetMaxSpeed, not GetMaxSpeedDef) CCommandAI* cai = unit->commandAI; - if (cai->CanSetMaxSpeed()) { - Command c(CMD_SET_WANTED_MAX_SPEED, options, minMaxSpeed); + if (!cai->CanSetMaxSpeed()) + return; - cai->GiveCommand(c, false); - } + cai->GiveCommand(Command(CMD_SET_WANTED_MAX_SPEED, options, groupMinMaxSpeed), false); } @@ -99,7 +82,6 @@ static inline bool MayRequireSetMaxSpeedCommand(const Command &c) void CSelectedUnitsHandlerAI::GiveCommandNet(Command &c, int player) { const std::vector& netSelected = selectedUnitsHandler.netSelected[player]; - std::vector::const_iterator ui; const int nbrOfSelectedUnits = netSelected.size(); const int cmd_id = c.GetID(); @@ -151,18 +133,20 @@ void CSelectedUnitsHandlerAI::GiveCommandNet(Command &c, int player) else if (((cmd_id == CMD_MOVE) || (cmd_id == CMD_FIGHT)) && (c.GetParamsCount() == 6)) { CalculateGroupData(player, !!(c.options & SHIFT_KEY)); - MakeFrontMove(&c, player); + MakeFormationFrontOrder(&c, player); const bool groupSpeed = !!(c.options & CONTROL_KEY); - for (ui = netSelected.begin(); ui != netSelected.end(); ++ui) { - CUnit* unit = unitHandler->GetUnit(*ui); - if (unit != nullptr) { - if (groupSpeed) { - AddGroupSetMaxSpeedCommand(unit, c.options); - } else { - AddUnitSetMaxSpeedCommand(unit, c.options); - } + for (const int unitID: netSelected) { + CUnit* unit = unitHandler->GetUnit(unitID); + + if (unit == nullptr) + nullptr; + + if (groupSpeed) { + AddGroupSetMaxSpeedCommand(unit, c.options); + } else { + AddUnitSetMaxSpeedCommand(unit, c.options); } } } @@ -171,29 +155,29 @@ void CSelectedUnitsHandlerAI::GiveCommandNet(Command &c, int player) // use the vector from the middle of group to new pos as forward dir const float3 pos(c.GetParam(0), c.GetParam(1), c.GetParam(2)); - float3 frontdir = pos - centerCoor; - frontdir.y = 0.0f; - frontdir.ANormalize(); - const float3 sideDir = frontdir.cross(UpVector); + const float3 frontDir = ((pos - groupCenterCoor) * XZVector).ANormalize(); + const float3 sideDir = frontDir.cross(UpVector); // calculate so that the units form in an aproximate square - float length = 100.0f + (math::sqrt((float)nbrOfSelectedUnits) * 32.0f); + const float length = 100.0f + (math::sqrt((float)nbrOfSelectedUnits) * 32.0f); // push back some extra params so it confer with a front move c.PushPos(pos + (sideDir * length)); - MakeFrontMove(&c, player); + MakeFormationFrontOrder(&c, player); const bool groupSpeed = !!(c.options & CONTROL_KEY); - for (ui = netSelected.begin(); ui != netSelected.end(); ++ui) { - CUnit* unit = unitHandler->GetUnit(*ui); - if (unit != nullptr) { - if (groupSpeed) { - AddGroupSetMaxSpeedCommand(unit, c.options); - } else { - AddUnitSetMaxSpeedCommand(unit, c.options); - } + for (const int unitID: netSelected) { + CUnit* unit = unitHandler->GetUnit(unitID); + + if (unit == nullptr) + continue; + + if (groupSpeed) { + AddGroupSetMaxSpeedCommand(unit, c.options); + } else { + AddUnitSetMaxSpeedCommand(unit, c.options); } } } @@ -202,50 +186,55 @@ void CSelectedUnitsHandlerAI::GiveCommandNet(Command &c, int player) CalculateGroupData(player, !!(c.options & SHIFT_KEY)); const bool groupSpeed = !(c.options & ALT_KEY); + const bool queueing = (c.options & SHIFT_KEY); - for (ui = netSelected.begin(); ui != netSelected.end(); ++ui) { - CUnit* unit = unitHandler->GetUnit(*ui); + for (const int unitID: netSelected) { + CUnit* unit = unitHandler->GetUnit(unitID); - if (unit != nullptr) { - // Modifying the destination relative to the center of the group - Command uc = c; - float3 midPos; - if (c.options & SHIFT_KEY) { - midPos = LastQueuePosition(unit); - } else { - midPos = unit->midPos; - } - uc.params[CMDPARAM_MOVE_X] += midPos.x - centerCoor.x; - uc.params[CMDPARAM_MOVE_Y] += midPos.y - centerCoor.y; - uc.params[CMDPARAM_MOVE_Z] += midPos.z - centerCoor.z; - unit->commandAI->GiveCommand(uc, false); - - if (groupSpeed) { - AddGroupSetMaxSpeedCommand(unit, c.options); - } else { - AddUnitSetMaxSpeedCommand(unit, c.options); - } + if (unit == nullptr) + continue; + + // modify the destination relative to the center of the group + Command uc = c; + + const float3 midPos = (queueing? LastQueuePosition(unit): float3(unit->midPos)); + const float3 difPos = midPos - groupCenterCoor; + + uc.params[CMDPARAM_MOVE_X] += difPos.x; + uc.params[CMDPARAM_MOVE_Y] += difPos.y; + uc.params[CMDPARAM_MOVE_Z] += difPos.z; + unit->commandAI->GiveCommand(uc, false); + + if (groupSpeed) { + AddGroupSetMaxSpeedCommand(unit, c.options); + } else { + AddUnitSetMaxSpeedCommand(unit, c.options); } } } else { - for (ui = netSelected.begin(); ui != netSelected.end(); ++ui) { - CUnit* unit = unitHandler->GetUnit(*ui); + for (const int unitID: netSelected) { + CUnit* unit = unitHandler->GetUnit(unitID); - if (unit != nullptr) { - // appending a CMD_SET_WANTED_MAX_SPEED command to - // every command is a little bit wasteful, n'est pas? - unit->commandAI->GiveCommand(c, false); - if (MayRequireSetMaxSpeedCommand(c)) { - AddUnitSetMaxSpeedCommand(unit, c.options); - } - } - } - if (cmd_id == CMD_WAIT) { - if (player == gu->myPlayerNum) { - waitCommandsAI.AcknowledgeCommand(c); - } + if (unit == nullptr) + continue; + + // appending a CMD_SET_WANTED_MAX_SPEED command to + // every command is a little bit wasteful, n'est pas? + unit->commandAI->GiveCommand(c, false); + + if (!MayRequireSetMaxSpeedCommand(c)) + continue; + + AddUnitSetMaxSpeedCommand(unit, c.options); } + + if (cmd_id != CMD_WAIT) + return; + if (player != gu->myPlayerNum) + return; + + waitCommandsAI.AcknowledgeCommand(c); } } @@ -255,245 +244,276 @@ void CSelectedUnitsHandlerAI::GiveCommandNet(Command &c, int player) // void CSelectedUnitsHandlerAI::CalculateGroupData(int player, bool queueing) { //Finding the highest, lowest and weighted central positional coordinates among the selected units. - float3 sumCoor, minCoor, maxCoor; - float3 mobileSumCoor = sumCoor; - sumLength = 0; + float3 sumCoor; + float3 minCoor = OnesVector * 100000.0f; + float3 maxCoor = -OnesVector * 100000.0f; + float3 mobileSumCoor; + int mobileUnits = 0; - minMaxSpeed = 1e9f; - for (auto ui = selectedUnitsHandler.netSelected[player].begin(); ui != selectedUnitsHandler.netSelected[player].end(); ++ui) { + groupSumLength = 0.0f; + groupMinMaxSpeed = 1e9f; + + const std::vector& playerUnitIDs = selectedUnitsHandler.netSelected[player]; + + for (auto ui = playerUnitIDs.begin(); ui != playerUnitIDs.end(); ++ui) { CUnit* unit = unitHandler->GetUnit(*ui); - if (unit != nullptr) { - const UnitDef* ud = unit->unitDef; - sumLength += (int)((ud->xsize + ud->zsize)/2); + if (unit == nullptr) + continue; - float3 unitPos; - if (queueing) { - unitPos = LastQueuePosition(unit); - } else { - unitPos = unit->midPos; - } - if(unitPos.x < minCoor.x) - minCoor.x = unitPos.x; - else if(unitPos.x > maxCoor.x) - maxCoor.x = unitPos.x; - if(unitPos.y < minCoor.y) - minCoor.y = unitPos.y; - else if(unitPos.y > maxCoor.y) - maxCoor.y = unitPos.y; - if(unitPos.z < minCoor.z) - minCoor.z = unitPos.z; - else if(unitPos.z > maxCoor.z) - maxCoor.z = unitPos.z; - sumCoor += unitPos; - - if (unit->commandAI->CanSetMaxSpeed()) { - mobileUnits++; - mobileSumCoor += unitPos; - - const float maxSpeed = unit->moveType->GetMaxSpeed(); - - if (maxSpeed < minMaxSpeed) { - minMaxSpeed = maxSpeed; - } - } - } + groupSumLength += ((unit->unitDef->xsize + unit->unitDef->zsize) * 0.5f); + + const float3 unitPos = (queueing? LastQueuePosition(unit): float3(unit->midPos)); + + minCoor = float3::min(minCoor, unitPos); + maxCoor = float3::max(maxCoor, unitPos); + sumCoor += unitPos; + + if (!unit->commandAI->CanSetMaxSpeed()) + continue; + + mobileUnits++; + mobileSumCoor += unitPos; + + groupMinMaxSpeed = std::min(groupMinMaxSpeed, unit->moveType->GetMaxSpeed()); } - avgLength = sumLength/selectedUnitsHandler.netSelected[player].size(); - //Weighted center - if(mobileUnits > 0) - centerCoor = mobileSumCoor / mobileUnits; + + groupAvgLength = groupSumLength / playerUnitIDs.size(); + + // weighted center + if (mobileUnits > 0) + groupCenterCoor = mobileSumCoor / mobileUnits; else - centerCoor = sumCoor / selectedUnitsHandler.netSelected[player].size(); + groupCenterCoor = sumCoor / playerUnitIDs.size(); } -void CSelectedUnitsHandlerAI::MakeFrontMove(Command* c,int player) +void CSelectedUnitsHandlerAI::MakeFormationFrontOrder(Command* c, int player) { - centerPos.x=c->params[0]; - centerPos.y=c->params[1]; - centerPos.z=c->params[2]; - rightPos.x=c->params[3]; - rightPos.y=c->params[4]; - rightPos.z=c->params[5]; - - // in "front" coordinates (rotated to real, moved by rightPos) + // called when releasing the mouse; accompanies GuiHandler::DrawFormationFrontOrder + formationCenterPos = c->GetPos(0); + formationRightPos = c->GetPos(3); + + // in "front" coordinates (rotated to real, moved by formationRightPos) float3 nextPos; - if (centerPos.distance(rightPos)& playerUnitIDs = selectedUnitsHandler.netSelected[player]; + + if (formationCenterPos.distance(formationRightPos) < playerUnitIDs.size() + 33) { + // if the front is not long enough, treat as a standard move + for (auto ui = playerUnitIDs.begin(); ui != playerUnitIDs.end(); ++ui) { CUnit* unit = unitHandler->GetUnit(*ui); - if (unit != nullptr) - unit->commandAI->GiveCommand(*c, false); + if (unit == nullptr) + continue; + + unit->commandAI->GiveCommand(*c, false); } return; } - frontLength=centerPos.distance(rightPos)*2; - addSpace = 0; - if (frontLength > sumLength*2*8) { - addSpace = (frontLength - sumLength*2*8)/(selectedUnitsHandler.netSelected[player].size() - 1); - } - sideDir=centerPos-rightPos; - sideDir.y=0; - float3 sd = sideDir; - sd.y=frontLength/2; - sideDir.ANormalize(); - frontDir=sideDir.cross(UpVector); - - numColumns=(int)(frontLength/columnDist); - if(numColumns==0) - numColumns=1; - - std::multimap orderedUnits; - CreateUnitOrder(orderedUnits,player); - - std::multimap > sortUnits; - std::vector > frontcmds; - for (std::multimap::iterator oi = orderedUnits.begin(); oi != orderedUnits.end();){ - bool newline; - nextPos = MoveToPos(oi->second, nextPos, sd, c, &frontcmds, &newline); - // mix units in each row to avoid weak flanks consisting solely of e.g. artillery units - std::multimap >::iterator si = sortUnits.find(oi->first); - if(si == sortUnits.end()) - si = sortUnits.insert(std::pair >(oi->first, std::vector())); - si->second.push_back(oi->second); - ++oi; - - if(oi != orderedUnits.end()) - MoveToPos(oi->second, nextPos, sd, c, NULL, &newline); - - if(oi == orderedUnits.end() || newline) { - std::vector*> sortUnitsVector; - for (std::multimap >::iterator ui = sortUnits.begin(); ui!=sortUnits.end(); ++ui) { - sortUnitsVector.push_back(&(*ui).second); - } + groupFrontLength = formationCenterPos.distance(formationRightPos) * 2.0f; + groupAddedSpace = 0.0f; - std::vector randunits; - int sz = sortUnitsVector.size(); - std::vector sortPos(sz, 0); - for (std::vector >::iterator fi = frontcmds.begin(); fi != frontcmds.end(); ++fi) { - int bestpos = 0; - float bestval = 1.0f; - for (int i = 0; i < sz; ++i) { - const int n = sortUnitsVector[i]->size(); - const int k = sortPos[i]; - if (k < n) { - const float val = (0.5f + k) / (float)n; - if (val < bestval) { - bestval = val; - bestpos = i; - } - } - } - int pos = sortPos[bestpos]; - randunits.push_back((*sortUnitsVector[bestpos])[pos]); - sortPos[bestpos] = pos + 1; + if (groupFrontLength > (groupSumLength * 2.0f * SQUARE_SIZE)) + groupAddedSpace = (groupFrontLength - (groupSumLength * 2.0f * SQUARE_SIZE)) / (playerUnitIDs.size() - 1); + + #if 0 + formationNumColumns = std::max(1, int(groupFrontLength / groupColumnDist)); + #endif + + const float3 formationSideDir = (formationCenterPos - formationRightPos) * XZVector + (UpVector * groupFrontLength * 0.5f); + + + sortedUnitGroups.clear(); + frontMoveCommands.clear(); + + CreateUnitOrder(sortedUnitPairs, player); + + for (size_t k = 0; k < sortedUnitPairs.size(); ) { + bool newFormationLine = false; + + + // convert flat vector of pairs + // to a vector of > pairs + const auto& suPair = sortedUnitPairs[k]; + + const auto suGroupPair = TGroupPair{suPair.first, {}}; + const auto suGroupIter = std::lower_bound(sortedUnitGroups.begin(), sortedUnitGroups.end(), suGroupPair, ugPairComp); + + if (suGroupIter == sortedUnitGroups.end() || suGroupIter->first != suPair.first) { + sortedUnitGroups.emplace_back(suPair.first, TGroupVect{suPair.second}); + + // swap into position + for (size_t i = sortedUnitGroups.size() - 1; i > 0; i--) { + if (ugPairComp(sortedUnitGroups[i - 1], sortedUnitGroups[i])) + break; + + std::swap(sortedUnitGroups[i - 1], sortedUnitGroups[i]); } - sortUnits.clear(); + } else { + suGroupIter->second.push_back(suPair.second); + } + + + nextPos = MoveToPos(nextPos, formationSideDir, unitHandler->GetUnit(suPair.second), c, &frontMoveCommands, &newFormationLine); + + if ((++k) < sortedUnitPairs.size()) { + MoveToPos(nextPos, formationSideDir, unitHandler->GetUnit(suPair.second), c, nullptr, &newFormationLine); + + if (!newFormationLine) + continue; + } + + mixedUnitIDs.clear(); + mixedUnitIDs.reserve(frontMoveCommands.size()); + mixedGroupSizes.clear(); + mixedGroupSizes.resize(sortedUnitGroups.size(), 0); + + + // mix units in each row to avoid weak flanks consisting solely of e.g. artillery + for (size_t j = 0; j < frontMoveCommands.size(); j++) { + size_t bestGroupNum = 0; + float bestGroupVal = 1.0f; + + for (size_t groupNum = 0; groupNum < sortedUnitGroups.size(); ++groupNum) { + const size_t maxGroupSize = sortedUnitGroups[groupNum].second.size(); + const size_t curGroupSize = mixedGroupSizes[groupNum]; - int i = 0; - for (auto fi = frontcmds.begin(); fi != frontcmds.end(); ++fi) { - unitHandler->GetUnit(randunits[i++])->commandAI->GiveCommand(fi->second, false); + if (curGroupSize >= maxGroupSize) + continue; + + const float groupVal = (0.5f + curGroupSize) / (1.0f * maxGroupSize); + + if (groupVal >= bestGroupVal) + continue; + + bestGroupVal = groupVal; + bestGroupNum = groupNum; } - frontcmds.clear(); + + // for each processed command, increase the count by 1 s.t. + // (at most) groupSize units are shuffled around per group + const size_t unitIndex = mixedGroupSizes[bestGroupNum]++; + + const auto& groupPair = sortedUnitGroups[bestGroupNum]; + const auto& groupUnitIDs = groupPair.second; + + mixedUnitIDs.push_back(groupUnitIDs[unitIndex]); } + + for (size_t i = 0; i < frontMoveCommands.size(); i++) { + CUnit* unit = unitHandler->GetUnit(mixedUnitIDs[i]); + CCommandAI* cai = unit->commandAI; + + cai->GiveCommand(frontMoveCommands[i].second, false); + } + + frontMoveCommands.clear(); + sortedUnitGroups.clear(); } } -void CSelectedUnitsHandlerAI::CreateUnitOrder(std::multimap& out,int player) +void CSelectedUnitsHandlerAI::CreateUnitOrder(std::vector< std::pair >& out, int player) { - const std::vector& netUnits = selectedUnitsHandler.netSelected[player]; + const std::vector& playerUnitIDs = selectedUnitsHandler.netSelected[player]; - for (auto ui = netUnits.cbegin(); ui != netUnits.cend(); ++ui) { - const CUnit* unit = unitHandler->GetUnit(*ui); + out.clear(); + out.reserve(playerUnitIDs.size()); - if (unit != nullptr) { - const UnitDef* ud = unit->unitDef; - float range = unit->maxRange; - if (range < 1) { - // give weaponless units a long range to make them go to the back - range = 2000; - } - const float value = ((ud->metal * 60) + ud->energy) / unit->unitDef->health * range; - out.insert(std::pair(value, *ui)); - } + for (const int unitID: playerUnitIDs) { + const CUnit* unit = unitHandler->GetUnit(unitID); + + if (unit == nullptr) + continue; + + const UnitDef* ud = unit->unitDef; + + // give weaponless units a long range to make them go to the back + const float range = (unit->maxRange < 1.0f)? 2000: unit->maxRange; + const float value = ((ud->metal * 60) + ud->energy) / ud->health * range; + + out.emplace_back(value, unitID); } + + std::sort(out.begin(), out.end(), idPairComp); } -float3 CSelectedUnitsHandlerAI::MoveToPos(int unit, float3 nextCornerPos, float3 dir, Command* command, std::vector >* frontcmds, bool* newline) -{ - //int lineNum=posNum/numColumns; - //int colNum=posNum-lineNum*numColumns; - //float side=(0.25f+colNum*0.5f)*columnDist*(colNum&1 ? -1:1); - *newline = false; - if ((nextCornerPos.x - addSpace) > frontLength) { - nextCornerPos.x = 0; nextCornerPos.z -= avgLength * 2 * 8; - *newline = true; +float3 CSelectedUnitsHandlerAI::MoveToPos( + float3 nextCornerPos, + float3 formationDir, + const CUnit* unit, + Command* command, + std::vector >* frontcmds, + bool* newline +) { + #if 0 + const int rowNum = posNum / formationNumColumns; + const int colNum = posNum - rowNum * formationNumColumns; + const float side = (0.25f + colNum * 0.5f) * groupColumnDist * ((colNum & 1)? -1: 1); + #endif + + if ((*newline = ((nextCornerPos.x - groupAddedSpace) > groupFrontLength))) { + nextCornerPos.x = 0.0f; + nextCornerPos.z -= (groupAvgLength * 2.0f * SQUARE_SIZE); } - if (!frontcmds) { + + if (frontcmds == nullptr) return nextCornerPos; + + if (unit == nullptr) + return nextCornerPos; + + const int unitSize = (unit->unitDef->xsize + unit->unitDef->zsize) / 2; + + float3 retPos(nextCornerPos.x + unitSize * SQUARE_SIZE * 2 + groupAddedSpace, 0, nextCornerPos.z); + float3 movePos(nextCornerPos.x + unitSize * SQUARE_SIZE + groupAddedSpace, 0, nextCornerPos.z); // posit in coordinates of "front" + + if (nextCornerPos.x == 0.0f) { + movePos.x = unitSize * SQUARE_SIZE; + retPos.x -= groupAddedSpace; } - int unitSize = 16; - const CUnit* u = unitHandler->GetUnit(unit); - if (u != nullptr) { - const UnitDef* ud = u->unitDef; - unitSize = (int)((ud->xsize + ud->zsize) / 2); - } - float3 retPos( nextCornerPos.x + unitSize * 8 * 2 + addSpace, 0, nextCornerPos.z); - float3 movePos(nextCornerPos.x + unitSize * 8 + addSpace, 0, nextCornerPos.z); // posit in coordinates of "front" - if (nextCornerPos.x == 0) { - movePos.x = unitSize * 8; - retPos.x -= addSpace; - } + float3 pos; - pos.x = rightPos.x + (movePos.x * (dir.x / dir.y)) - (movePos.z * (dir.z/dir.y)); - pos.z = rightPos.z + (movePos.x * (dir.z / dir.y)) + (movePos.z * (dir.x/dir.y)); + pos.x = formationRightPos.x + (movePos.x * (formationDir.x / formationDir.y)) - (movePos.z * (formationDir.z / formationDir.y)); + pos.z = formationRightPos.z + (movePos.x * (formationDir.z / formationDir.y)) + (movePos.z * (formationDir.x / formationDir.y)); pos.y = CGround::GetHeightAboveWater(pos.x, pos.z); - Command c(command->GetID(), command->options, pos); - - frontcmds->push_back(std::pair(unit, c)); - + frontcmds->emplace_back(unit->id, Command(command->GetID(), command->options, pos)); return retPos; } -struct DistInfo { - bool operator<(const DistInfo& di) const { return dist < di.dist; } - float dist; - int unitID; -}; - - void CSelectedUnitsHandlerAI::SelectAttack(const Command& cmd, int player) { - std::vector targets; + // reuse for sorting targets, no overlap with MakeFormationFrontOrder + sortedUnitPairs.clear(); + targetUnitIDs.clear(); if (cmd.params.size() == 4) { - SelectCircleUnits(cmd.GetPos(0), cmd.params[3], player, targets); + SelectCircleUnits(cmd.GetPos(0), cmd.params[3], player, targetUnitIDs); } else { - SelectRectangleUnits(cmd.GetPos(0), cmd.GetPos(3), player, targets); + SelectRectangleUnits(cmd.GetPos(0), cmd.GetPos(3), player, targetUnitIDs); } - if (targets.empty()) + if (targetUnitIDs.empty()) return; const bool queueing = !!(cmd.options & SHIFT_KEY); const std::vector& selected = selectedUnitsHandler.netSelected[player]; - const unsigned int targetsCount = targets.size(); + const unsigned int targetsCount = targetUnitIDs.size(); const unsigned int selectedCount = selected.size(); + unsigned int realCount = 0; if (selectedCount == 0) return; + Command attackCmd(CMD_ATTACK, cmd.options, 0.0f); // delete the attack commands and bail for CONTROL_KEY @@ -501,7 +521,7 @@ void CSelectedUnitsHandlerAI::SelectAttack(const Command& cmd, int player) attackCmd.options |= SHIFT_KEY; for (unsigned int s = 0; s < selectedCount; s++) { - const CUnit* unit = unitHandler->GetUnit( selected[s] ); + const CUnit* unit = unitHandler->GetUnit(selected[s]); if (unit == nullptr) continue; @@ -509,31 +529,29 @@ void CSelectedUnitsHandlerAI::SelectAttack(const Command& cmd, int player) CCommandAI* commandAI = unit->commandAI; for (unsigned int t = 0; t < targetsCount; t++) { - attackCmd.params[0] = targets[t]; + attackCmd.params[0] = targetUnitIDs[t]; - if (commandAI->WillCancelQueued(attackCmd)) { - commandAI->GiveCommand(attackCmd, false); - } + if (!commandAI->WillCancelQueued(attackCmd)) + continue; + + commandAI->GiveCommand(attackCmd, false); } } return; } + // get the group center float3 midPos; - unsigned int realCount = 0; + for (unsigned int s = 0; s < selectedCount; s++) { CUnit* unit = unitHandler->GetUnit(selected[s]); if (unit == nullptr) continue; - if (queueing) { - midPos += LastQueuePosition(unit); - } else { - midPos += unit->midPos; - } + midPos += (queueing? LastQueuePosition(unit): float3(unit->midPos)); realCount++; } @@ -543,26 +561,22 @@ void CSelectedUnitsHandlerAI::SelectAttack(const Command& cmd, int player) midPos /= realCount; - // sort the targets - std::vector distVec; + // sort the targets for (unsigned int t = 0; t < targetsCount; t++) { - const CUnit* unit = unitHandler->GetUnit( targets[t] ); + const CUnit* unit = unitHandler->GetUnit(targetUnitIDs[t]); const float3 unitPos = float3(unit->midPos); - DistInfo di; - di.unitID = targets[t]; - di.dist = (unitPos - midPos).SqLength2D(); - distVec.push_back(di); + sortedUnitPairs.emplace_back((unitPos - midPos).SqLength2D(), targetUnitIDs[t]); } - stable_sort(distVec.begin(), distVec.end()); - // give the commands + std::stable_sort(sortedUnitPairs.begin(), sortedUnitPairs.end(), idPairComp); + + + // give the commands; clear queueing-flag for the first for (unsigned int s = 0; s < selectedCount; s++) { - if (!queueing) { - // clear it for the first command + if (!queueing) attackCmd.options &= ~SHIFT_KEY; - } CUnit* unit = unitHandler->GetUnit(selected[s]); @@ -572,15 +586,16 @@ void CSelectedUnitsHandlerAI::SelectAttack(const Command& cmd, int player) CCommandAI* commandAI = unit->commandAI; for (unsigned t = 0; t < targetsCount; t++) { - attackCmd.params[0] = distVec[t].unitID; + attackCmd.params[0] = sortedUnitPairs[t].second; - if (!queueing || !commandAI->WillCancelQueued(attackCmd)) { - commandAI->GiveCommand(attackCmd, false); + if (queueing && commandAI->WillCancelQueued(attackCmd)) + continue; - AddUnitSetMaxSpeedCommand(unit, attackCmd.options); - // following commands are always queued - attackCmd.options |= SHIFT_KEY; - } + commandAI->GiveCommand(attackCmd, false); + + AddUnitSetMaxSpeedCommand(unit, attackCmd.options); + // following commands are always queued + attackCmd.options |= SHIFT_KEY; } } } @@ -599,7 +614,7 @@ void CSelectedUnitsHandlerAI::SelectCircleUnits( const CPlayer* p = playerHandler->Player(player); - if (p == NULL) + if (p == nullptr) return; QuadFieldQuery qfQuery; @@ -614,7 +629,7 @@ void CSelectedUnitsHandlerAI::SelectCircleUnits( for (unsigned int i = 0; i < count; i++) { CUnit* unit = (*qfQuery.units)[i]; - if (unit == NULL) + if (unit == nullptr) continue; if (unit->allyteam == allyTeam) continue; @@ -645,7 +660,7 @@ void CSelectedUnitsHandlerAI::SelectRectangleUnits( const CPlayer* p = playerHandler->Player(player); - if (p == NULL) + if (p == nullptr) return; const float3 mins(std::min(pos0.x, pos1.x), 0.0f, std::min(pos0.z, pos1.z)); @@ -662,7 +677,7 @@ void CSelectedUnitsHandlerAI::SelectRectangleUnits( for (unsigned int i = 0; i < count; i++) { const CUnit* unit = (*qfQuery.units)[i]; - if (unit == NULL) + if (unit == nullptr) continue; if (unit->allyteam == allyTeam) continue; @@ -677,12 +692,13 @@ void CSelectedUnitsHandlerAI::SelectRectangleUnits( float3 CSelectedUnitsHandlerAI::LastQueuePosition(const CUnit* unit) { const CCommandQueue& queue = unit->commandAI->commandQue; - CCommandQueue::const_reverse_iterator it; - for (it = queue.rbegin(); it != queue.rend(); ++it) { + + for (auto it = queue.rbegin(); it != queue.rend(); ++it) { const Command& cmd = *it; - if (cmd.params.size() >= 3) { + + if (cmd.params.size() >= 3) return cmd.GetPos(0); - } } + return unit->midPos; } diff --git a/rts/Game/SelectedUnitsAI.h b/rts/Game/SelectedUnitsAI.h index 87bf50167b7..5e3508aa7fb 100644 --- a/rts/Game/SelectedUnitsAI.h +++ b/rts/Game/SelectedUnitsAI.h @@ -5,8 +5,8 @@ #include "Sim/Units/CommandAI/Command.h" #include "System/float3.h" -#include -#include + +#include class CUnit; @@ -20,21 +20,13 @@ class CSelectedUnitsHandlerAI { void RemoveUnit(int unit); */ - CSelectedUnitsHandlerAI(); void GiveCommandNet(Command& c, int player); - float3 centerPos; - float3 rightPos; - int sumLength; - float avgLength; - float frontLength; - float addSpace; - private: void CalculateGroupData(int player, bool queueing); - void MakeFrontMove(Command* c, int player); - void CreateUnitOrder(std::multimap& out, int player); - float3 MoveToPos(int unit, float3 nextCornerPos, float3 dir, Command* command, std::vector >* frontcmds, bool* newline); + void MakeFormationFrontOrder(Command* c, int player); + void CreateUnitOrder(std::vector< std::pair >& out, int player); + float3 MoveToPos(float3 nextCornerPos, float3 dir, const CUnit* unit, Command* command, std::vector >* frontcmds, bool* newline); void AddUnitSetMaxSpeedCommand(CUnit* unit, unsigned char options); void AddGroupSetMaxSpeedCommand(CUnit* unit, unsigned char options); void SelectAttack(const Command& cmd, int player); @@ -42,13 +34,30 @@ class CSelectedUnitsHandlerAI { void SelectRectangleUnits(const float3& pos0, const float3& pos1, int player, std::vector& units); float3 LastQueuePosition(const CUnit* unit); - float3 minCoor, maxCoor, centerCoor; - float minMaxSpeed; +private: + float3 groupCenterCoor; + float3 formationCenterPos; + float3 formationRightPos; + + float groupSumLength = 0.0f; + float groupAvgLength = 0.0f; + float groupFrontLength = 0.0f; + float groupAddedSpace = 0.0f; + float groupMinMaxSpeed = 0.0f; + float groupColumnDist = 64.0f; + + int formationNumColumns = 0; + + + std::vector< std::pair > sortedUnitPairs; // + std::vector< std::pair> > sortedUnitGroups; + std::vector< std::pair > frontMoveCommands; + + std::vector mixedUnitIDs; + std::vector mixedGroupSizes; + - float3 frontDir; - float3 sideDir; - float columnDist; - int numColumns; + std::vector targetUnitIDs; }; extern CSelectedUnitsHandlerAI selectedUnitsAI; diff --git a/rts/Game/UI/GuiHandler.cpp b/rts/Game/UI/GuiHandler.cpp index e72114bc71e..c9c31a31354 100644 --- a/rts/Game/UI/GuiHandler.cpp +++ b/rts/Game/UI/GuiHandler.cpp @@ -2563,9 +2563,11 @@ size_t CGuiHandler::GetBuildPositions(const BuildInfo& startInfo, const BuildInf void CGuiHandler::ProcessFrontPositions(float3& pos0, const float3& pos1) { + // rotate around corner if (!frontByEnds) - return; // leave it centered + return; + // rotate around center pos0 = pos1 + ((pos0 - pos1) * 0.5f); pos0.y = CGround::GetHeightReal(pos0.x, pos0.z, false); } @@ -3505,7 +3507,7 @@ void CGuiHandler::DrawMapStuff(bool onMiniMap) if (cmdDesc.params.size() > 1) sizeDiv = atof(cmdDesc.params[1].c_str()); - DrawFront(button, maxSize, sizeDiv, onMiniMap, tracePos, traceDir); + DrawFormationFrontOrder(button, maxSize, sizeDiv, onMiniMap, tracePos, traceDir); } } break; @@ -3996,24 +3998,28 @@ void CGuiHandler::DrawArea(float3 pos, float radius, const float* color) } -void CGuiHandler::DrawFront(int button, float maxSize, float sizeDiv, bool onMinimap, const float3& cameraPos, const float3& mouseDir) -{ - CMouseHandler::ButtonPressEvt& bp = mouse->buttons[button]; - if (bp.movement < 5) - return; +void CGuiHandler::DrawFormationFrontOrder( + int button, + float maxSize, + float sizeDiv, + bool onMinimap, + const float3& cameraPos, + const float3& mouseDir +) { + const CMouseHandler::ButtonPressEvt& bp = mouse->buttons[button]; - float dist = CGround::LineGroundCol(bp.camPos, bp.camPos + bp.dir * globalRendering->viewRange * 1.4f, false); + const float buttonDist = CGround::LineGroundCol(bp.camPos, bp.camPos + bp.dir * globalRendering->viewRange * 1.4f, false); - if (dist < 0.0f) + if (buttonDist < 0.0f) return; - dist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false); + const float cameraDist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false); - if (dist < 0.0f) + if (cameraDist < 0.0f) return; - float3 pos1 = bp.camPos + ( bp.dir * dist); - float3 pos2 = cameraPos + (mouseDir * dist); + float3 pos1 = bp.camPos + ( bp.dir * buttonDist); + float3 pos2 = cameraPos + (mouseDir * cameraDist); ProcessFrontPositions(pos1, pos2); @@ -4041,40 +4047,47 @@ void CGuiHandler::DrawFront(int button, float maxSize, float sizeDiv, bool onMin glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_DEPTH_TEST); - glBegin(GL_QUADS); - glVertexf3(pos1+side*25); - glVertexf3(pos1-side*25); - glVertexf3(pos1-side*25+forward*50); - glVertexf3(pos1+side*25+forward*50); - - glVertexf3(pos1+side*40+forward*50); - glVertexf3(pos1-side*40+forward*50); - glVertexf3(pos1+forward*100); - glVertexf3(pos1+forward*100); - glEnd(); - glEnable(GL_DEPTH_TEST); + { + // direction arrow + glDisable(GL_DEPTH_TEST); + glBegin(GL_QUADS); + glVertexf3(pos1 + side * 25.0f ); + glVertexf3(pos1 - side * 25.0f ); + glVertexf3(pos1 - side * 25.0f + forward * 50.0f); + glVertexf3(pos1 + side * 25.0f + forward * 50.0f); + + glVertexf3(pos1 + side * 40.0f + forward * 50.0f); + glVertexf3(pos1 - side * 40.0f + forward * 50.0f); + glVertexf3(pos1 + forward * 100.0f); + glVertexf3(pos1 + forward * 100.0f); + glEnd(); + glEnable(GL_DEPTH_TEST); + } pos1 += (pos1 - pos2); - const int maxSteps = 256; + const float frontLen = (pos1 - pos2).Length2D(); + + const int maxSteps = 256; const int steps = std::min(maxSteps, std::max(1, int(frontLen / 16.0f))); - glDisable(GL_FOG); - glBegin(GL_QUAD_STRIP); - const float3 delta = (pos2 - pos1) / (float)steps; - for (int i = 0; i <= steps; i++) { - float3 p; - const float d = (float)i; - p.x = pos1.x + (d * delta.x); - p.z = pos1.z + (d * delta.z); - p.y = CGround::GetHeightAboveWater(p.x, p.z, false); - p.y -= 100.f; glVertexf3(p); - p.y += 200.f; glVertexf3(p); + { + // vertical quad + glDisable(GL_FOG); + glBegin(GL_QUAD_STRIP); + const float3 delta = (pos2 - pos1) / (float)steps; + for (int i = 0; i <= steps; i++) { + float3 p; + const float d = (float)i; + p.x = pos1.x + (d * delta.x); + p.z = pos1.z + (d * delta.z); + p.y = CGround::GetHeightAboveWater(p.x, p.z, false); + p.y -= 100.f; glVertexf3(p); + p.y += 200.f; glVertexf3(p); + } + glEnd(); + glEnable(GL_FOG); } - glEnd(); - - glEnable(GL_FOG); } diff --git a/rts/Game/UI/GuiHandler.h b/rts/Game/UI/GuiHandler.h index 637d153c8eb..bc604a7a13f 100644 --- a/rts/Game/UI/GuiHandler.h +++ b/rts/Game/UI/GuiHandler.h @@ -152,7 +152,7 @@ class CGuiHandler : public CInputReceiver { void DrawSelectionInfo(); void DrawNumberInput(); void DrawMiniMapMarker(const float3& cameraPos); - void DrawFront(int button, float maxSize, float sizeDiv, bool onMiniMap, const float3& cameraPos, const float3& mouseDir); + void DrawFormationFrontOrder(int button, float maxSize, float sizeDiv, bool onMiniMap, const float3& cameraPos, const float3& mouseDir); void DrawArea(float3 pos, float radius, const float* color); void DrawSelectBox(const float3& start, const float3& end, const float3& cameraPos); void DrawSelectCircle(const float3& pos, float radius, const float* color);