Skip to content

feat(guard): implement area guard functionality#7399

Open
WybrenKoelmans wants to merge 2 commits intobeyond-all-reason:masterfrom
WybrenKoelmans:guard-area
Open

feat(guard): implement area guard functionality#7399
WybrenKoelmans wants to merge 2 commits intobeyond-all-reason:masterfrom
WybrenKoelmans:guard-area

Conversation

@WybrenKoelmans
Copy link
Copy Markdown
Contributor

@WybrenKoelmans WybrenKoelmans commented Apr 11, 2026

Work done

Added Guard Area (unit targets) command, works kinda like repair area.

Test steps

  • Check for regression with normal guard right click unit
  • Right click drag on an allied unit to start guard area command
  • Queueing stuff
  • Constructors guard behavior is different for some mysterious reason, not worth it to fix it with this PR

AI / LLM usage statement:

Vibes for initial prototype then just manually fixed and optimized.

Screen.Recording.2026-04-11.155422.mp4

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 11, 2026

Integration Test Results

16 tests  ±0   8 ✅ ±0   3s ⏱️ ±0s
 1 suites ±0   8 💤 ±0 
 1 files   ±0   0 ❌ ±0 

Results for commit 5b2174c. ± Comparison against base commit 8136703.

♻️ This comment has been updated with latest results.

@WybrenKoelmans
Copy link
Copy Markdown
Contributor Author

I know that there are many many more things we can do with this, and also fix bugs around it and such, but lets just keep things small for once and merge it iteratively. IMO this is already a huge improvement and brings many new strategic moves to the table.

@WybrenKoelmans WybrenKoelmans marked this pull request as ready for review April 11, 2026 14:01
Comment on lines +165 to +167
if cmdID == CMD_GUARD then
return true
end
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why "handle" native guard if you're going to always let it through anyway?

Comment on lines +202 to +208
function gadget:DefaultCommand(type, id)
if type ~= "unit" then return end
if not spIsUnitAllied(id) then return end
for unitDefID in pairs(spGetSelectedUnitsCounts()) do
if canGuardDefs[unitDefID] then
return CMD_AREA_GUARD
end
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is supposed to implement "return area guard whenever the native ui would return native guard" then

Suggested change
function gadget:DefaultCommand(type, id)
if type ~= "unit" then return end
if not spIsUnitAllied(id) then return end
for unitDefID in pairs(spGetSelectedUnitsCounts()) do
if canGuardDefs[unitDefID] then
return CMD_AREA_GUARD
end
function gadget:DefaultCommand(type, id, defaultCmd)
if defaultCmd == CMD_GUARD
return CMD_AREA_GUARD

-- Direct click on a unit via AREA_GUARD button: forward as engine CMD.GUARD
local targetID = cmdParams[1]
if spValidUnitID(targetID) and isAllied(unitID, targetID) then
spGiveOrderToUnit(unitID, CMD_GUARD, { targetID }, cmdOptions.coded or 0)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
spGiveOrderToUnit(unitID, CMD_GUARD, { targetID }, cmdOptions.coded or 0)
spGiveOrderToUnit(unitID, CMD_GUARD, targetID, cmdOptions.coded or 0)

local allyTeamID = spGetUnitAllyTeam(unitID)

local unitsInArea = spGetUnitsInCylinder(x, z, radius)
if not unitsInArea then return {} end
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this return nil?


for _, targetID in ipairs(targets) do
if not alreadyGuarded[targetID] then
spGiveOrderToUnit(unitID, CMD_GUARD, { targetID }, { "shift" })
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. avoid tables (CMD.OPT_SHIFT, plain targetID)
  2. avoid multiple GiveOrder calls (-> GiveOrderArrayToUnit)
  3. shouldn't the first command be given without shift? right now it looks like it always appends
  4. consider copying alt/ctrl/etc from the area guard command (i don't remember off the top of my head whether they do anything for guard, but people will inevitably copypaste this snippet to other commands that might)

@PtaQQ PtaQQ added the Awaiting GDT Approval Awaiting approval from the Game Design Team (game mechanics, balance only) label Apr 11, 2026
@PtaQQ
Copy link
Copy Markdown
Collaborator

PtaQQ commented Apr 11, 2026

Auto-approved per Game Design Doc, @beyond-all-reason/game-design-team see.

@PtaQQ PtaQQ added Approved Approved for implementation by the Game Design Team and removed Awaiting GDT Approval Awaiting approval from the Game Design Team (game mechanics, balance only) labels Apr 12, 2026
Comment on lines +26 to +27
if unitDef.modCategories and unitDef.modCategories['object']
or (unitDef.customParams and unitDef.customParams.objectify) then
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modcategories and customparams cannot be nil

end

if truncated then
spEcho(string.format("Area guard: attempted %d guard targets, truncated to %d.", attempted, MAX_GUARD_ORDERS))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will display publicly for everybody in the game

end
end

-- Use UnitFinished to ensure the engine has added its default commands first
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that a real worry? engine should probably add its stuff before UnitCreated runs

end
end

-- Handle transfer of units between teams (capture/share)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these can happen before UnitFinished, also engine probably isn't supposed to drop command capabilities upon transfer

end
end

function gadget:UnitTaken(unitID, unitDefID, oldTeam, newTeam)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do it on both taken and given?

end

local function giveGuardOrders(unitID, targets, append)
if not targets or #targets == 0 then return end
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this behave consistently regarding dropping the existing command queue when given without shift on an area with 0 targets?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Approved Approved for implementation by the Game Design Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants