Skip to content

Commit

Permalink
Implement a basic area reclaim command (FAForever#6049)
Browse files Browse the repository at this point in the history
  • Loading branch information
Garanas committed Apr 4, 2024
1 parent 593251c commit c635c94
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 2 deletions.
3 changes: 1 addition & 2 deletions engine/Sim.lua
Original file line number Diff line number Diff line change
Expand Up @@ -626,8 +626,7 @@ function GetMapSize()
end

---@overload fun(x0: number, z0: number, x1: number, z1: number): ReclaimObject[] | nil
--- Returns the reclaimable objects inside the given rectangle.
--- This includes props, units, wreckages.
--- Returns the reclaimable objects inside the given rectangle. This includes props, units and wrecks. Unlike the brain functions, this function uses either the collision box (OO) or the visual box (AAB) for the query and is therefore much more accurate.
---@param rectangle Rectangle
---@return ReclaimObject[] | nil
function GetReclaimablesInRect(rectangle)
Expand Down
27 changes: 27 additions & 0 deletions lua/SimCallbacks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,33 @@ do
end
end

do

---@param data table
---@param selection Unit[]
Callbacks.ExtendReclaimOrder = function(data, selection)
-- verify selection
selection = SecureUnits(selection)
if (not selection) or TableEmpty(selection) then
return
end

-- verify the command queue
local unit = selection[1]
local queue = unit:GetCommandQueue()
local lastCommand = queue[table.getn(queue)]

reprsl(lastCommand)
if not (lastCommand and lastCommand.commandType == 19 and lastCommand.target) then
return
end

local target = lastCommand.target --[[@as Unit | Prop]]
import("/lua/sim/commands/area-reclaim-order.lua").AreaReclaimOrder(selection, target, true)
end

end

--#endregion

-------------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions lua/proptree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ local EffectSetEmitterCurveParam = EffectMethods.SetEmitterCurveParam
---@field NoBurn? boolean
Tree = Class(Prop) {

IsTree = true,

---@param self Tree
OnDestroy = function(self)
Prop.OnDestroy(self)
Expand Down Expand Up @@ -269,6 +271,9 @@ Tree = Class(Prop) {
---@class TreeGroup : Prop
TreeGroup = Class(Prop) {

IsTree = true,
IsTreeGroup = true,

--- Break when colliding with a projectile of some sort
---@param self TreeGroup
---@param other string
Expand Down
128 changes: 128 additions & 0 deletions lua/sim/commands/area-reclaim-order.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
--******************************************************************************************************
--** Copyright (c) 2024 Willem 'Jip' Wijnia
--**
--** Permission is hereby granted, free of charge, to any person obtaining a copy
--** of this software and associated documentation files (the "Software"), to deal
--** in the Software without restriction, including without limitation the rights
--** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
--** copies of the Software, and to permit persons to whom the Software is
--** furnished to do so, subject to the following conditions:
--**
--** The above copyright notice and this permission notice shall be included in all
--** copies or substantial portions of the Software.
--**
--** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
--** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
--** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
--** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
--** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
--** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
--** SOFTWARE.
--******************************************************************************************************

-- upvalue scope for performance
local TableGetn = table.getn
local TableSetn = table.setn
local TableInsert = table.insert
local TableRemove = table.remove

local StringFormat = string.format

local IssueReclaim = IssueReclaim

---@type table<EntityId, boolean>
local seen = { }

---@type Unit[]
local stack = { }

--- Applies a reclaim order to all adjacent units
---@param units Unit[]
---@param target StructureUnit
---@param doPrint boolean
local function ReclaimAdjacentUnits (units, target, doPrint)
-- clean up previous iterations
TableSetn(stack, 0)
for entityId, _ in seen do
seen[entityId] = nil
end

-- prepare the stack
seen[target.EntityId] = true
TableInsert(stack, target)

local processed = 0

-- exclude the expansion and faction identifier
local blueprintIdPostfix = string.sub(target.Blueprint.BlueprintId, 2)

while TableGetn(stack) > 0 do
local current = TableRemove(stack)
if current != target then
IssueReclaim(units, current)
end

local adjacentUnits = current.AdjacentUnits
if adjacentUnits then
for _, unit in adjacentUnits do
if blueprintIdPostfix == string.sub(target.Blueprint.BlueprintId, 2) then
if not seen[unit.EntityId] then
seen[unit.EntityId] = true
TableInsert(stack, unit)
processed = processed + 1
end
end
end
end
end

if doPrint and processed > 0 and (GetFocusArmy() == GetCurrentCommandSource()) then
print(StringFormat("Reclaiming %d adjacent units", processed))
end
end

--- Applies a reclaim order to all nearby props
---@param units Unit[]
---@param target Prop
---@param doPrint boolean
local function ReclaimNearbyProps (units, target, doPrint)
local radius = 1.5
if target.IsTreeGroup then
radius = 0.1
end

local px, _, pz = target:GetPositionXYZ()
local adjacentReclaim = GetReclaimablesInRect(px - radius, pz - radius, px + radius, pz + radius)
local processed = 0

if adjacentReclaim then
for k = 1, TableGetn(adjacentReclaim) do
local entity = adjacentReclaim[k] --[[@as Prop]]
if target != entity and IsProp(entity) and entity.MaxMassReclaim > 0 and (entity.IsTree == target.IsTree) then
IssueReclaim(units, entity)
processed = processed + 1
end
end
end

if doPrint and processed > 0 and (GetFocusArmy() == GetCurrentCommandSource()) then
print(StringFormat("Reclaiming %d nearby props", processed))
end
end

--- Applies additional reclaim orders to nearby similar entities that are similar to the target
---@param units Unit[]
---@param target Unit | Prop
---@param doPrint boolean # if true, prints information about the order
function AreaReclaimOrder(units, target, doPrint)
local unitCount = TableGetn(units)
if unitCount == 0 then
return
end

if IsUnit(target) and EntityCategoryContains(categories.STRUCTURE, target) then
return ReclaimAdjacentUnits(units, target, doPrint)
elseif IsProp(target) then
return ReclaimNearbyProps(units, target, doPrint)
end
end
4 changes: 4 additions & 0 deletions lua/ui/game/commandmode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,10 @@ end
---@return boolean
function OnCommandIssued(command)

if command.CommandType == 'Reclaim' and command.Target.EntityId then
SimCallback({ Func = 'ExtendReclaimOrder', Args = { TargetId = command.Target.EntityId } }, true)
end

-- if we're trying to upgrade hives then this allows us to force the upgrade to happen immediately
if command.CommandType == "Upgrade" and (command.Blueprint == "xrb0204" or command.Blueprint == "xrb0304") then
if not IsKeyDown('Shift') then
Expand Down

0 comments on commit c635c94

Please sign in to comment.