Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to drone controls #1775

Merged
merged 8 commits into from Dec 30, 2016
1 change: 1 addition & 0 deletions LuaRules/Configs/customcmds.h.lua
Expand Up @@ -68,6 +68,7 @@ CMD_ONECLICK_WEAPON = 35000
CMD_PLACE_BEACON = 35170
CMD_WAIT_AT_BEACON = 35171
CMD_ABANDON_PW = 35200
CMD_RECALL_DRONES = 35300
CMD_ANTINUKEZONE = 35130 -- ceasefire
CMD_UNIT_KILL_SUBORDINATES = 35821 -- unit_capture
CMD_UNIT_AI = 36214
Expand Down
161 changes: 135 additions & 26 deletions LuaRules/Gadgets/unit_carrier_drones.lua
Expand Up @@ -22,6 +22,8 @@ end
--24/6/2014 added carrier building drone on emit point.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
include("LuaRules/Configs/customcmds.h.lua")

local AddUnitDamage = Spring.AddUnitDamage
local CreateUnit = Spring.CreateUnit
local GetCommandQueue = Spring.GetCommandQueue
Expand All @@ -34,15 +36,21 @@ local SetUnitPosition = Spring.SetUnitPosition
local SetUnitNoSelect = Spring.SetUnitNoSelect
local TransferUnit = Spring.TransferUnit
local spGetUnitRulesParam = Spring.GetUnitRulesParam
local spSetUnitRulesParam = Spring.SetUnitRulesParam
local spGetGameFrame = Spring.GetGameFrame
local random = math.random
local CMD_ATTACK = CMD.ATTACK

local emptyTable = {}

-- thingsWhichAreDrones is an optimisation for AllowCommand
-- thingsWhichAreDrones is an optimisation for AllowCommand, no longer used but it'll stay here for now
local carrierDefs, thingsWhichAreDrones, unitRulesCarrierDefs, BUILD_UPDATE_INTERVAL = include "LuaRules/Configs/drone_defs.lua"

local DEFAULT_UPDATE_ORDER_FREQUENCY = 40 -- gameframes
local IDLE_DISTANCE = 120
local ACTIVE_DISTANCE = 180
local DRONE_HEIGHT = 120
local RECALL_TIMEOUT = 300

local carrierList = {}
local droneList = {}
Expand All @@ -51,8 +59,23 @@ local killList = {}

local GiveClampedOrderToUnit = Spring.Utilities.GiveClampedOrderToUnit

local recallDronesCmdDesc = {
id = CMD_RECALL_DRONES,
type = CMDTYPE.ICON,
name = 'Recall Drones',
cursor = 'Load units',
action = 'recalldrones',
tooltip = 'Recall any owned drones to the mothership.',
}

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function RandomPointInUnitCircle()
local angle = random(0, 2*math.pi)
local distance = math.pow(random(0, 1), 0.5)
return math.cos(angle)*distance, math.sin(angle)*distance
end

local function InitCarrier(unitID, carrierData, teamID, maxDronesOverride)
local toReturn = {teamID = teamID, droneSets = {}, occupiedPieces={}, droneInQueue= {}}
local unitPieces = GetUnitPieceMap(unitID)
Expand Down Expand Up @@ -90,6 +113,7 @@ local function Drones_InitializeDynamicCarrier(unitID)
if drones then
carrierData[#carrierData + 1] = data
maxDronesOverride[#maxDronesOverride + 1] = drones
Spring.InsertUnitCmdDesc(unitID, recallDronesCmdDesc)
end
end
carrierList[unitID] = InitCarrier(unitID, carrierData, Spring.GetUnitTeam(unitID), maxDronesOverride)
Expand Down Expand Up @@ -144,9 +168,13 @@ local function NewDrone(unitID, droneName, setNum, droneBuiltExternally)
Spring.MoveCtrl.Disable(droneID)
Spring.SetUnitCOBValue(droneID, 82, (rot - math.pi)*65536/2/math.pi)


local states = Spring.GetUnitStates(unitID) or emptyTable
GiveOrderToUnit(droneID, CMD.MOVE_STATE, { 2 }, 0)
GiveOrderToUnit(droneID, CMD.FIRE_STATE, { states.movestate }, 0)
GiveOrderToUnit(droneID, CMD.IDLEMODE, { 0 }, 0)
GiveClampedOrderToUnit(droneID, CMD.FIGHT, {x + random(-300, 300), 60, z + random(-300, 300)}, {""})
local rx, rz = RandomPointInUnitCircle()
GiveClampedOrderToUnit(droneID, CMD.MOVE, {x + rx*IDLE_DISTANCE, y+DRONE_HEIGHT, z + rz*IDLE_DISTANCE}, {""})
GiveOrderToUnit(droneID, CMD.GUARD, {unitID} , {"shift"})

SetUnitNoSelect(droneID, true)
Expand Down Expand Up @@ -414,12 +442,27 @@ local function GetDistance(x1, x2, y1, y2)
return ((x1-x2)^2 + (y1-y2)^2)^0.5
end

local function UpdateCarrierTarget(carrierID)
local function UpdateCarrierTarget(carrierID, frame)
local cQueueC = GetCommandQueue(carrierID, 1)
local droneSendDistance = nil
local px, py, pz
local target
if cQueueC and cQueueC[1] and cQueueC[1].id == CMD_ATTACK then
local recallDrones = false
local attackOrder = false

--checks if there is an active recall order
local recallFrame = spGetUnitRulesParam(carrierID,"recall_frame_start")
if recallFrame then
if frame > recallFrame + RECALL_TIMEOUT then
--recall has expired
spSetUnitRulesParam(carrierID,"recall_frame_start",nil)
else
recallDrones = true
end
end

--Handles an attack order given to the carrier.
if not recallDrones and cQueueC and cQueueC[1] and cQueueC[1].id == CMD_ATTACK then
local ox, oy, oz = GetUnitPosition(carrierID)
local params = cQueueC[1].params
if #params == 1 then
Expand All @@ -431,29 +474,60 @@ local function UpdateCarrierTarget(carrierID)
if px then
droneSendDistance = GetDistance(ox, px, oz, pz)
end

attackOrder = true --attack order overrides set target
end

--Handles a setTarget order given to the carrier.
if not recallDrones and not attackOrder then
local targetType = spGetUnitRulesParam(carrierID,"target_type")
if targetType and targetType > 0 then
local ox, oy, oz = GetUnitPosition(carrierID)
if targetType == 1 then --targeting ground
px, py, pz = spGetUnitRulesParam(carrierID,"target_x"), spGetUnitRulesParam(carrierID,"target_y"), spGetUnitRulesParam(carrierID,"target_z")
end
if targetType == 2 then --targeting units
local target_id = spGetUnitRulesParam(carrierID,"target_id")
target = {target_id}
px, py, pz = GetUnitPosition(target_id)
end
if px then
droneSendDistance = GetDistance(ox, px, oz, pz)
end
end
end

local states = Spring.GetUnitStates(carrierID) or emptyTable
local holdfire = states.firestate == 0
local rx, rz

for i = 1, #carrierList[carrierID].droneSets do

local set = carrierList[carrierID].droneSets[i]
if droneSendDistance and droneSendDistance < set.config.range then
local tempCONTAINER --temporarily keep table when "droneList[]" is emptied and restored.
for droneID in pairs(set.drones) do
tempCONTAINER = droneList[droneID]
droneList[droneID] = nil -- to keep AllowCommand from blocking the order
local tempCONTAINER


for droneID in pairs(set.drones) do
tempCONTAINER = droneList[droneID]
droneList[droneID] = nil -- to keep AllowCommand from blocking the order

GiveOrderToUnit(droneID, CMD.FIRE_STATE, { states.firestate }, 0) -- update firestate

if recallDrones then
-- move drones to carrier
px, py, pz = GetUnitPosition(carrierID)
rx, rz = RandomPointInUnitCircle()
GiveClampedOrderToUnit(droneID, CMD.MOVE, {px + rx*IDLE_DISTANCE, py+DRONE_HEIGHT, pz + rz*IDLE_DISTANCE}, 0)
GiveOrderToUnit(droneID, CMD.GUARD, {carrierID} , {"shift"})
elseif droneSendDistance and droneSendDistance < set.config.range then
-- attacking
if target then
GiveOrderToUnit(droneID, CMD.ATTACK, target, 0)
else
GiveClampedOrderToUnit(droneID, CMD.FIGHT, {(px + (random(0, 300) - 150)), (py+120), (pz + (random(0, 300) - 150))} , 0)
rx, rz = RandomPointInUnitCircle()
GiveClampedOrderToUnit(droneID, CMD.FIGHT, {px + rx*ACTIVE_DISTANCE, py+DRONE_HEIGHT, pz + rz*ACTIVE_DISTANCE}, 0)
end
--GiveOrderToUnit(droneID, CMD.GUARD, {carrierID} , {"shift"})
droneList[droneID] = tempCONTAINER --restore original table
end
else
for droneID in pairs(set.drones) do
else
-- return to carrier unless in combat
local cQueue = GetCommandQueue(droneID, -1)
local engaged = false
for i=1, (cQueue and #cQueue or 0) do
Expand All @@ -464,15 +538,15 @@ local function UpdateCarrierTarget(carrierID)
end
if not engaged then
px, py, pz = GetUnitPosition(carrierID)

local temp = droneList[droneID]
droneList[droneID] = nil -- to keep AllowCommand from blocking the order
GiveClampedOrderToUnit(droneID, holdfire and CMD.MOVE or CMD.FIGHT, {px + random(-100, 100), (py+120), pz + random(-100, 100)} , 0)
rx, rz = RandomPointInUnitCircle()
GiveClampedOrderToUnit(droneID, holdfire and CMD.MOVE or CMD.FIGHT, {px + rx*IDLE_DISTANCE, py+DRONE_HEIGHT, pz + rz*IDLE_DISTANCE}, 0)
GiveOrderToUnit(droneID, CMD.GUARD, {carrierID} , {"shift"})
droneList[droneID] = temp
end
end
end

droneList[droneID] = tempCONTAINER
end

end
end

Expand All @@ -484,10 +558,37 @@ function gadget:AllowCommand_GetWantedCommand()
end

function gadget:AllowCommand_GetWantedUnitDefID()
return thingsWhichAreDrones
return true
end

function gadget:AllowCommand(unitID, unitDefID, teamID, cmdID, cmdParams, cmdOptions)
if (carrierList[unitID] ~= nil and (cmdID == CMD.ATTACK or cmdID == CMD.FIGHT or cmdID == CMD.PATROL or cmdID == CMD_UNIT_SET_TARGET or cmdID == CMD_UNIT_SET_TARGET_CIRCLE)) then
spSetUnitRulesParam(unitID,"recall_frame_start",nil)
return true
end
if (carrierList[unitID] ~= nil and cmdID == CMD_RECALL_DRONES) then

-- Gives drones a command to recall to the carrier
for i = 1, #carrierList[unitID].droneSets do
local set = carrierList[unitID].droneSets[i]

for droneID in pairs(set.drones) do
px, py, pz = GetUnitPosition(unitID)

local temp = droneList[droneID]
droneList[droneID] = nil -- to keep AllowCommand from blocking the order
local rx, rz = RandomPointInUnitCircle()
GiveClampedOrderToUnit(droneID, CMD.MOVE, {px + rx*IDLE_DISTANCE, py+DRONE_HEIGHT, pz + rz*IDLE_DISTANCE}, 0)
GiveOrderToUnit(droneID, CMD.GUARD, {unitID} , {"shift"})
droneList[droneID] = temp
end
end

frame = spGetGameFrame()
spSetUnitRulesParam(unitID,"recall_frame_start",frame)

return false
end
if (droneList[unitID] ~= nil) then
return false
else
Expand Down Expand Up @@ -540,6 +641,12 @@ function gadget:UnitDestroyed(unitID, unitDefID, unitTeam)
end
end

function gadget:UnitCreated(unitID, unitDefID, unitTeam)
if (carrierDefs[unitDefID]) then
Spring.InsertUnitCmdDesc(unitID, recallDronesCmdDesc)
end
end

function gadget:UnitFinished(unitID, unitDefID, unitTeam)
if Spring.GetUnitRulesParam(unitID, "comm_level") then
Drones_InitializeDynamicCarrier(unitID)
Expand Down Expand Up @@ -610,19 +717,21 @@ function gadget:GameFrame(n)
end
if ((n % DEFAULT_UPDATE_ORDER_FREQUENCY) == 0) then
for i, _ in pairs(carrierList) do
UpdateCarrierTarget(i)
UpdateCarrierTarget(i, n)
end
end
UpdateCoroutines() --maintain nanoframe position relative to carrier
end

function gadget:Initialize()
gadgetHandler:RegisterCMDID(CMD_RECALL_DRONES)
GG.Drones_InitializeDynamicCarrier = Drones_InitializeDynamicCarrier
for _, unitID in ipairs(Spring.GetAllUnits()) do
local unitDefID = Spring.GetUnitDefID(unitID)
local team = Spring.GetUnitTeam(unitID)
gadget:UnitCreated(unitID, unitDefID, team)
local build = select(5, Spring.GetUnitHealth(unitID))
if build == 1 then
local unitDefID = Spring.GetUnitDefID(unitID)
local team = Spring.GetUnitTeam(unitID)
gadget:UnitFinished(unitID, unitDefID, team)
end
end
Expand Down
2 changes: 2 additions & 0 deletions LuaUI/Configs/border_menu_commands.lua
Expand Up @@ -216,6 +216,8 @@ local overrides = {
[CMD_ABANDON_PW] = {caption= '', texture = 'LuaUI/Images/Crystal_Clear_action_flag_white.png'},

[CMD_PLACE_BEACON] = {caption= '', texture = imageDir .. 'Bold/drop_beacon.png'},

[CMD_RECALL_DRONES] = {caption= '', texture = imageDir .. 'Bold/recall_drones.png'},

-- states
[CMD.ONOFF] = { texture = {imageDir .. 'states/off.png', imageDir .. 'states/on.png'}, caption=''},
Expand Down
1 change: 1 addition & 0 deletions LuaUI/Configs/customCmdTypes.lua
Expand Up @@ -52,6 +52,7 @@ local custom_cmd_actions = {
cancelfirezone = {cmdType = 3, name = "Cancel Newton Fire Zone"},
radialmenu = {cmdType = 3, name = "Open Radial Build Menu"},
placebeacon = {cmdType = 1, name = "Place Teleport Beacon"},
recalldrones = {cmdType = 1, name = "Recall Drones to Carrier"},
buildprev = {cmdType = 1, name = "Build Previous"},
areaguard = {cmdType = 1, name = "Area Guard"},
dropflag = {cmdType = 3, name = "Drop Flag"},
Expand Down
2 changes: 2 additions & 0 deletions LuaUI/Configs/integral_menu_commands.lua
Expand Up @@ -352,6 +352,8 @@ local overrides = {
[CMD_STOP_PRODUCTION] = { texture = imageDir .. 'Bold/stopbuild.png'},
[CMD_GBCANCEL] = { texture = imageDir .. 'Bold/stopbuild.png'},

[CMD_RECALL_DRONES] = {texture = imageDir .. 'Bold/recall_drones.png'},

-- states
[CMD.ONOFF] = { texture = {imageDir .. 'states/off.png', imageDir .. 'states/on.png'}, text=''},
[CMD_UNIT_AI] = { texture = {imageDir .. 'states/bulb_off.png', imageDir .. 'states/bulb_on.png'}, text=''},
Expand Down
2 changes: 2 additions & 0 deletions LuaUI/Configs/integral_menu_commands_old.lua
Expand Up @@ -217,6 +217,8 @@ local overrides = {
[CMD_STOP_PRODUCTION] = { texture = imageDir .. 'Bold/stopbuild.png'},
[CMD_GBCANCEL] = { texture = imageDir .. 'Bold/stopbuild.png'},

[CMD_RECALL_DRONES] = {texture = imageDir .. 'Bold/recall_drones.png'},

-- states
[CMD.ONOFF] = { texture = {imageDir .. 'states/off.png', imageDir .. 'states/on.png'}, text=''},
[CMD_UNIT_AI] = { texture = {imageDir .. 'states/bulb_off.png', imageDir .. 'states/bulb_on.png'}, text=''},
Expand Down
Binary file added LuaUI/Images/commands/Bold/recall_drones.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.