From 35c0a4a874c72aad9854d888b04b28b6b07de8f5 Mon Sep 17 00:00:00 2001 From: Shaman <48730089+Arch-Shaman@users.noreply.github.com> Date: Thu, 1 Apr 2021 02:02:46 -0400 Subject: [PATCH 1/7] Add an automated grey goo command This automates grey goo for grey goo units. * A command issued on a wreck will consume it before performing the next command. (This is useful if that Dante wreck or whatever is nearby) * An area command will consume all wrecks in an area (starting with nearby ones then working to the next closest one) Checks for updates at 5hz. Attempts to be optimized by checking for nearby wrecks, etc. --- LuaRules/Gadgets/cmd_greygoo.lua | 203 +++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 LuaRules/Gadgets/cmd_greygoo.lua diff --git a/LuaRules/Gadgets/cmd_greygoo.lua b/LuaRules/Gadgets/cmd_greygoo.lua new file mode 100644 index 0000000000..011d73ada4 --- /dev/null +++ b/LuaRules/Gadgets/cmd_greygoo.lua @@ -0,0 +1,203 @@ +function gadget:GetInfo() + return { + name = "Area Grey Goo Handler", + desc = "Units will consume all wreckage in an area", + author = "Shaman", + date = "April 1, 2021", + license = "CC-0", + layer = 5, + enabled = true, + } +end + +if (not gadgetHandler:IsSyncedCode()) then + return +end +local IterableMap = VFS.Include("LuaRules/Gadgets/Include/IterableMap.lua") + +local handled = IterableMap.New() +local wantedUnitDefs = {} +local featurecache = {} + +local areaGreyGooDesc = { + id = CMD_GREYGOO, + type = CMDTYPE.ICON_UNIT_FEATURE_OR_AREA, + name = 'Reclaim (Grey Goo)', -- TODO: better name. Marketing was out today. + action = 'reclaim', -- this may break things for modders with cons that grey goo! + tooltip = 'Marks an area or wreckage for grey goo.', +} + +-- Speed ups -- +local spGetUnitPosition = Spring.GetUnitPosition +local spGetFeaturePosition = Spring.GetFeaturePosition +local spGetFeaturesInCylinder = Spring.GetFeaturesInCylinder +local spGiveOrderToUnit = Spring.GiveOrderToUnit +local spSetUnitMoveGoal = Spring.SetUnitMoveGoal +local spGetFeatureDefID = Spring.GetFeatureDefID +local spValidFeatureID = Spring.ValidFeatureID +local spValidUnitID = Spring.ValidUnitID +local spGetUnitCommands = Spring.GetUnitCommands +local spInsertUnitCmdDesc = Spring.InsertUnitCmdDesc +local CommandOrder = 123456 +local sqrt = math.sqrt + +local validFeatures = {} +local _, GooDefs = VFS.Include("LuaRules/Configs/grey_goo_defs.lua") + +for def, _ in pairs(GooDefs) do + wantedUnitDefs[def] = true +end + +for i = 1, #FeatureDefs do + local fdef = FeatureDefs[i] + if fdef.customParams and fdef.customParams.fromunit then + validFeatures[i] = true + end +end + +local function Distance(x1, x2, y1, y2) + return sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1))) +end + +local function GetEligiableWrecksInArea(x, z, radius) + local check = spGetFeaturesInCylinder(x, z, radius) + local ret = {} + for i = 1, #check do + if featurecache[check[i]] then + ret[#ret + 1] = check[i] + end + end + return ret +end + +local function GetClosestWreck(x, z, cx, cz, radius) + local wrecks = GetEligiableWrecksInArea(cx, cz, radius) + if #wrecks == 0 then -- double safety. + return nil + end + local lowestDistance = math.huge + local lowestID + for i = 1, #wrecks do + local id = wrecks[i] + local x2, _, z2 = spGetFeaturePosition(id) + local d = Distance(x, x2, z, z2) + if d < lowestDistance then + lowestID = id + lowestDistance = d + end + end + return lowestID +end + +local function IsThereEligiableWreckNearby(x, z, radius) + local check = spGetFeaturesInCylinder(x, z, radius) + for i = 1, #check do + if featurecache[check[i]] then + return check[i] -- return the lowest one in range + end + end + return nil +end + +function gadget:FeatureCreated(featureID, allyTeamID) + local featuredef = spGetFeatureDefID(featureID) + featurecache[featureID] = validFeatures[featuredef] +end + +function gadget:FeatureDestroyed(featureID) + featurecache[featureID] = nil +end + +function gadget:AllowCommand(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOptions, cmdTag, synced) + if cmdID == CMD_GREYGOO and not wantedUnitDefs[unitDefID] then -- screen against non-grey gooers using area greygoo. + return false + else + return true + end +end + +function gadget:UnitCreated(unitID, unitDefID, unitTeam, builderID) + if wantedUnitDefs[unitDefID] then + spInsertUnitCmdDesc(unitID, CommandOrder, areaGreyGooDesc) + end +end + +function gadget:CommandFallback(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOptions, cmdTag) -- used for "free" command management since we have a command that isn't an engine command. + if cmdID ~= CMD_GREYGOO then + return false, false -- don't care (not ours) + else + local data = IterableMap.Get(handled, unitID) + if data and data.done then + IterableMap.Remove(handled, unitID) + return true, true -- we're done with this command. + elseif not data then + if #cmdParams == 1 then -- this is a single feature command. + local id = cmdParams[1] + if id < Game.maxUnits then + return true, true -- this is invalid since it's targeting a unit (wtf) + else + local id = cmdParams[1] - Game.MaxUnits -- expects: (unitid or Game.maxUnits+featureid) so to get featureID we need to subtract Game.maxUnits. + cmdParams = id -- we set this to just a number so our gameframe check can differentiate between a single command and an area command. + if not spValidFeatureID(id) then + return true, true + else -- check if this is a good def to target. + if not featurecache[id] then + return true, true + end + end + end + elseif not IsThereEligiableWreckNearby(cmdParams[1], cmdParams[3], cmdParams[4]) then -- there's nothing that we can use in the radius. + return true, true + end + IterableMap.Add(handled, unitID, {def = unitDefID, done = false, params = cmdParams, goal = -9999} -- we found a new unit! + end + return true, false -- we're still not done here. + end +end + +function gadget:GameFrame(f) + if f%5 == 0 then -- 6hz + for unitID, data in IterableMap.Iterator(handled) do + if not data.done then -- don't handle things that are done. + local unitDef = data.def + local greygooconfig = GooDefs[data.def] + local currentcmd = spGetUnitCommands(unitID, 1) + if spValidUnitID(unitID) or #currentcmd == 0 or currentcmd[1].id ~= CMD_GREYGOO then -- safety. first we check if we have any commands, then if we're not doing grey goo anymore. + IterableMap.Remove(handled, unitID) + else + local params = data.params + local range = greygooconfig.range + local wantedrange = range * 0.1 -- puts us clearly in range, and gives us bonus targets, potentially. + if type(params) ~= "table" then -- this is a single command + if spValidFeatureID(params) then + if data.goal ~= params then -- we haven't set the move goal yet. + local x, y, z = spGetFeaturePosition(params) -- find the location of our target. + spSetUnitMoveGoal(unitID, x, y, z, wantedrange) + data.goal = params + end + else + data.done = true + end + else -- this is an area command. + if data.goal == -9999 or not spValidFeatureID(data.goal) then -- haven't set a goal yet or our current greygoo task is complete. + local x, y, z = spGetUnitPosition(unitID) + local newgoal = IsThereEligiableWreckNearby(x, z, range) + if newgoal then + data.goal = newgoal -- set our new goal to a nearby wreck in range (we're still eating something, no sense moving onto other things yet) + else + local id = GetClosestWreck(x, z, params[1], params[3], params[4]) + if id then + local gx, gy, gz = spGetFeaturePosition(id) + spSetUnitMoveGoal(unitID, gx, gy, gz, wantedrange) + data.goal = id + else + data.done = true + end + end + end + end + end + end + end + end +end From 6959085ce1050257afccd89cd86d874557093aed Mon Sep 17 00:00:00 2001 From: Shaman <48730089+Arch-Shaman@users.noreply.github.com> Date: Thu, 1 Apr 2021 02:24:48 -0400 Subject: [PATCH 2/7] Add the customcmd id --- LuaRules/Configs/customcmds.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/LuaRules/Configs/customcmds.lua b/LuaRules/Configs/customcmds.lua index 3de2a2e599..3e83bce973 100644 --- a/LuaRules/Configs/customcmds.lua +++ b/LuaRules/Configs/customcmds.lua @@ -77,6 +77,7 @@ local cmds = { ABANDON_PW = 35200, RECALL_DRONES = 35300, TOGGLE_DRONES = 35301, + GREYGOO = 35600, GOO_GATHER = 35646, PUSH_PULL = 35666, -- weapon_impulse WANT_ONOFF = 35667, From 8b88e8962b13e4999b7d5ab78169061b471d9d00 Mon Sep 17 00:00:00 2001 From: Shaman <48730089+Arch-Shaman@users.noreply.github.com> Date: Fri, 2 Apr 2021 02:18:01 -0400 Subject: [PATCH 3/7] Add LOS requirement Also includes UI work. --- LuaRules/Gadgets/cmd_greygoo.lua | 115 +++++++++++++++-------- LuaUI/Configs/integral_menu_commands.lua | 1 + LuaUI/Configs/integral_menu_config.lua | 4 +- LuaUI/Configs/integral_menu_culling.lua | 1 + 4 files changed, 79 insertions(+), 42 deletions(-) diff --git a/LuaRules/Gadgets/cmd_greygoo.lua b/LuaRules/Gadgets/cmd_greygoo.lua index 011d73ada4..a240fc35b4 100644 --- a/LuaRules/Gadgets/cmd_greygoo.lua +++ b/LuaRules/Gadgets/cmd_greygoo.lua @@ -13,17 +13,20 @@ end if (not gadgetHandler:IsSyncedCode()) then return end + +include("LuaRules/Configs/customcmds.h.lua") local IterableMap = VFS.Include("LuaRules/Gadgets/Include/IterableMap.lua") local handled = IterableMap.New() -local wantedUnitDefs = {} local featurecache = {} +local needLOS = true local areaGreyGooDesc = { id = CMD_GREYGOO, type = CMDTYPE.ICON_UNIT_FEATURE_OR_AREA, - name = 'Reclaim (Grey Goo)', -- TODO: better name. Marketing was out today. - action = 'reclaim', -- this may break things for modders with cons that grey goo! + name = 'Grey Goo', -- TODO: better name. Marketing was out today. + cursor = 'Reclaim', + action = 'reclaim', tooltip = 'Marks an area or wreckage for grey goo.', } @@ -38,16 +41,14 @@ local spValidFeatureID = Spring.ValidFeatureID local spValidUnitID = Spring.ValidUnitID local spGetUnitCommands = Spring.GetUnitCommands local spInsertUnitCmdDesc = Spring.InsertUnitCmdDesc +local spIsPosInLos = Spring.IsPosInLos +local spGetUnitAllyTeam = Spring.GetUnitAllyTeam local CommandOrder = 123456 local sqrt = math.sqrt local validFeatures = {} local _, GooDefs = VFS.Include("LuaRules/Configs/grey_goo_defs.lua") -for def, _ in pairs(GooDefs) do - wantedUnitDefs[def] = true -end - for i = 1, #FeatureDefs do local fdef = FeatureDefs[i] if fdef.customParams and fdef.customParams.fromunit then @@ -59,19 +60,27 @@ local function Distance(x1, x2, y1, y2) return sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1))) end -local function GetEligiableWrecksInArea(x, z, radius) +local function GetEligiableWrecksInArea(x, z, radius, allyID) -- Looks for wrecks in LOS that are nearby. local check = spGetFeaturesInCylinder(x, z, radius) local ret = {} for i = 1, #check do - if featurecache[check[i]] then - ret[#ret + 1] = check[i] + local featureID = check[i] + if featurecache[featureID] then + if needLOS then + local x, y, z = spGetFeaturePosition(featureID) + if needLOS and spIsPosInLos(x, y, z, allyID) then + ret[#ret + 1] = featureID + end + else + ret[#ret + 1] = featureID + end end end return ret end -local function GetClosestWreck(x, z, cx, cz, radius) - local wrecks = GetEligiableWrecksInArea(cx, cz, radius) +local function GetClosestWreck(x, z, cx, cz, radius, ally) -- + local wrecks = GetEligiableWrecksInArea(cx, cz, radius, ally) if #wrecks == 0 then -- double safety. return nil end @@ -79,7 +88,7 @@ local function GetClosestWreck(x, z, cx, cz, radius) local lowestID for i = 1, #wrecks do local id = wrecks[i] - local x2, _, z2 = spGetFeaturePosition(id) + local x2, y2, z2 = spGetFeaturePosition(id) local d = Distance(x, x2, z, z2) if d < lowestDistance then lowestID = id @@ -89,11 +98,19 @@ local function GetClosestWreck(x, z, cx, cz, radius) return lowestID end -local function IsThereEligiableWreckNearby(x, z, radius) +local function IsThereEligiableWreckNearby(x, z, radius, allyteam) local check = spGetFeaturesInCylinder(x, z, radius) for i = 1, #check do - if featurecache[check[i]] then - return check[i] -- return the lowest one in range + local featureID = check[i] + if featurecache[featureID] then + if needLOS then + local x, y, z = spGetFeaturePosition(featureID) + if spIsPosInLos(x, y, z, allyteam) then + return featureID -- return the lowest one in range + end + else + return featureID + end end end return nil @@ -109,47 +126,52 @@ function gadget:FeatureDestroyed(featureID) end function gadget:AllowCommand(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOptions, cmdTag, synced) - if cmdID == CMD_GREYGOO and not wantedUnitDefs[unitDefID] then -- screen against non-grey gooers using area greygoo. + if cmdID == CMD_GREYGOO and not GooDefs[unitDefID] then -- screen against non-grey gooers using area greygoo. return false else return true end end -function gadget:UnitCreated(unitID, unitDefID, unitTeam, builderID) - if wantedUnitDefs[unitDefID] then +function gadget:UnitCreated(unitID, unitDefID) + if GooDefs[unitDefID] then + --Spring.Echo("Injecting Command to " .. unitID .. "(Cmd: " .. tostring(CMD_GREYGOO) .. ")") spInsertUnitCmdDesc(unitID, CommandOrder, areaGreyGooDesc) end end function gadget:CommandFallback(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOptions, cmdTag) -- used for "free" command management since we have a command that isn't an engine command. if cmdID ~= CMD_GREYGOO then + --Spring.Echo("GREYGOO: CMDFALLBACK: bad cmd") return false, false -- don't care (not ours) else local data = IterableMap.Get(handled, unitID) if data and data.done then + --Spring.Echo("GREYGOO: DONE") IterableMap.Remove(handled, unitID) return true, true -- we're done with this command. elseif not data then + --Spring.Echo("GREYGOO: INITIALIZING with params: " .. tostring(cmdParams[1]) .. ", " .. tostring(cmdParams[2]) .. ", " .. tostring(cmdParams[3]) .. ", " .. tostring(cmdParams[4])) + local allyteam = spGetUnitAllyTeam(unitID) if #cmdParams == 1 then -- this is a single feature command. local id = cmdParams[1] - if id < Game.maxUnits then + if id < Game.maxUnits then -- unitID instead of featureID + return true, true + end + id = id - Game.maxUnits + local valid = spValidFeatureID(id) + --Spring.Echo("ID: " .. id .. " (Valid: " .. tostring(valid) .. ")") + if not valid then return true, true -- this is invalid since it's targeting a unit (wtf) else - local id = cmdParams[1] - Game.MaxUnits -- expects: (unitid or Game.maxUnits+featureid) so to get featureID we need to subtract Game.maxUnits. cmdParams = id -- we set this to just a number so our gameframe check can differentiate between a single command and an area command. - if not spValidFeatureID(id) then - return true, true - else -- check if this is a good def to target. - if not featurecache[id] then - return true, true - end - end + -- Note: We don't need to worry about LOS as I think it won't work out of LOS (returns a map position, probably) end - elseif not IsThereEligiableWreckNearby(cmdParams[1], cmdParams[3], cmdParams[4]) then -- there's nothing that we can use in the radius. + elseif not IsThereEligiableWreckNearby(cmdParams[1], cmdParams[3], cmdParams[4], allyteam) then -- there's nothing that we can use in the radius. + --Spring.Echo("GREYGOO: No eligible wrecks nearby") return true, true end - IterableMap.Add(handled, unitID, {def = unitDefID, done = false, params = cmdParams, goal = -9999} -- we found a new unit! + IterableMap.Add(handled, unitID, {def = unitDefID, done = false, params = cmdParams, goal = -9999}) -- we found a new unit! end return true, false -- we're still not done here. end @@ -159,33 +181,39 @@ function gadget:GameFrame(f) if f%5 == 0 then -- 6hz for unitID, data in IterableMap.Iterator(handled) do if not data.done then -- don't handle things that are done. - local unitDef = data.def + --Spring.Echo("GreyGoo: Update " .. unitID .. ": Goal: " .. data.goal) local greygooconfig = GooDefs[data.def] local currentcmd = spGetUnitCommands(unitID, 1) - if spValidUnitID(unitID) or #currentcmd == 0 or currentcmd[1].id ~= CMD_GREYGOO then -- safety. first we check if we have any commands, then if we're not doing grey goo anymore. + if not spValidUnitID(unitID) or #currentcmd == 0 or currentcmd[1].id ~= CMD_GREYGOO then -- safety. first we check if we have any commands, then if we're not doing grey goo anymore. + --Spring.Echo("Invalid unit or not working") IterableMap.Remove(handled, unitID) else local params = data.params + local commandRadius = data.params[4] + local commandX = data.params[1] + local commandZ = data.params[3] local range = greygooconfig.range - local wantedrange = range * 0.1 -- puts us clearly in range, and gives us bonus targets, potentially. - if type(params) ~= "table" then -- this is a single command - if spValidFeatureID(params) then - if data.goal ~= params then -- we haven't set the move goal yet. - local x, y, z = spGetFeaturePosition(params) -- find the location of our target. + local wantedrange = range * 0.1 -- puts us clearly in range, and gives us bonus targets (for mostly "free"), potentially. + if type(data.params) ~= "table" then -- this is a single command + local featureID = data.params + if spValidFeatureID(featureID) then + if data.goal ~= featureID then -- we haven't set the move goal yet. + local x, y, z = spGetFeaturePosition(featureID) -- find the location of our target. spSetUnitMoveGoal(unitID, x, y, z, wantedrange) - data.goal = params + data.goal = featureID end else data.done = true end else -- this is an area command. + local allyTeam = spGetUnitAllyTeam(unitID) if data.goal == -9999 or not spValidFeatureID(data.goal) then -- haven't set a goal yet or our current greygoo task is complete. local x, y, z = spGetUnitPosition(unitID) - local newgoal = IsThereEligiableWreckNearby(x, z, range) + local newgoal = IsThereEligiableWreckNearby(x, z, range, allyTeam) if newgoal then data.goal = newgoal -- set our new goal to a nearby wreck in range (we're still eating something, no sense moving onto other things yet) else - local id = GetClosestWreck(x, z, params[1], params[3], params[4]) + local id = GetClosestWreck(x, z, commandX, commandZ, commandRadius, allyTeam) if id then local gx, gy, gz = spGetFeaturePosition(id) spSetUnitMoveGoal(unitID, gx, gy, gz, wantedrange) @@ -201,3 +229,10 @@ function gadget:GameFrame(f) end end end + +function gadget:Initalize() + gadgetHandler:RegisterCMDID(CMD_GREYGOO) + for _, unitID in pairs(Spring.GetAllUnits()) do + gadget:UnitCreated(unitID, Spring.GetUnitDefID(unitID)) + end +end diff --git a/LuaUI/Configs/integral_menu_commands.lua b/LuaUI/Configs/integral_menu_commands.lua index 3461128e5e..4b3982bc15 100644 --- a/LuaUI/Configs/integral_menu_commands.lua +++ b/LuaUI/Configs/integral_menu_commands.lua @@ -43,6 +43,7 @@ local cmdPosDef = { [CMD_AREA_MEX] = {pos = 7, priority = 1}, [CMD.REPAIR] = {pos = 7, priority = 2}, [CMD.RECLAIM] = {pos = 7, priority = 3}, + [CMD_GREYGOO] = {pos = 7, priority = 3}, [CMD.RESURRECT] = {pos = 7, priority = 4}, [CMD.WAIT] = {pos = 7, priority = 5}, [CMD_FIND_PAD] = {pos = 7, priority = 6}, diff --git a/LuaUI/Configs/integral_menu_config.lua b/LuaUI/Configs/integral_menu_config.lua index fafa47c411..b6403ec69b 100644 --- a/LuaUI/Configs/integral_menu_config.lua +++ b/LuaUI/Configs/integral_menu_config.lua @@ -72,7 +72,7 @@ local commandDisplayConfig = { [CMD_BUMPY] = {texture = imageDir .. 'bumpy.png'}, [CMD_AREA_GUARD] = { texture = imageDir .. 'Bold/guard.png', tooltip = "Guard: Protect the target and assist its production."}, - + [CMD_AREA_MEX] = {texture = imageDir .. 'Bold/mex.png'}, [CMD_JUMP] = {texture = imageDir .. 'Bold/jump.png'}, @@ -96,7 +96,7 @@ local commandDisplayConfig = { [CMD_GBCANCEL] = { texture = imageDir .. 'Bold/stopbuild.png'}, [CMD_RECALL_DRONES] = {texture = imageDir .. 'Bold/recall_drones.png'}, - + [CMD_GREYGOO] = {texture = imageDir .. 'states/goo_on.png', tooltip = "Reclaim (Grey Goo): Consumes wreck(s) in an area (or a single wreck)."}, -- states [CMD_WANT_ONOFF] = { texture = {imageDir .. 'states/off.png', imageDir .. 'states/on.png'}, diff --git a/LuaUI/Configs/integral_menu_culling.lua b/LuaUI/Configs/integral_menu_culling.lua index 60f494d421..3c3da34177 100644 --- a/LuaUI/Configs/integral_menu_culling.lua +++ b/LuaUI/Configs/integral_menu_culling.lua @@ -15,6 +15,7 @@ local configList = { {cmdID = CMD.PATROL , default = true, name = "Patrol"}, {cmdID = CMD_RECALL_DRONES , default = true, name = "Recall Drones"}, {cmdID = CMD.RECLAIM , default = true, name = "Reclaim"}, + {cmdID = CMD_GREYGOO , default = true, name = "Reclaim (Grey Goo)"}, {cmdID = CMD.REPAIR , default = true, name = "Repair"}, {cmdID = CMD_FIND_PAD , default = true, name = "Resupply"}, {cmdID = CMD.RESURRECT , default = true, name = "Resurrect"}, From 9aef964180e671370696b4ed1fb292c7be228581 Mon Sep 17 00:00:00 2001 From: Shaman <48730089+Arch-Shaman@users.noreply.github.com> Date: Sat, 3 Apr 2021 16:58:56 -0400 Subject: [PATCH 4/7] Propagate command --- LuaRules/Gadgets/cmd_greygoo.lua | 78 ++++++++++++++---------------- LuaRules/Gadgets/unit_grey_goo.lua | 9 +++- 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/LuaRules/Gadgets/cmd_greygoo.lua b/LuaRules/Gadgets/cmd_greygoo.lua index a240fc35b4..1291135b36 100644 --- a/LuaRules/Gadgets/cmd_greygoo.lua +++ b/LuaRules/Gadgets/cmd_greygoo.lua @@ -10,11 +10,12 @@ function gadget:GetInfo() } end +include("LuaRules/Configs/customcmds.h.lua") + if (not gadgetHandler:IsSyncedCode()) then return end -include("LuaRules/Configs/customcmds.h.lua") local IterableMap = VFS.Include("LuaRules/Gadgets/Include/IterableMap.lua") local handled = IterableMap.New() @@ -25,8 +26,7 @@ local areaGreyGooDesc = { id = CMD_GREYGOO, type = CMDTYPE.ICON_UNIT_FEATURE_OR_AREA, name = 'Grey Goo', -- TODO: better name. Marketing was out today. - cursor = 'Reclaim', - action = 'reclaim', + action = 'greygoo', tooltip = 'Marks an area or wreckage for grey goo.', } @@ -79,7 +79,7 @@ local function GetEligiableWrecksInArea(x, z, radius, allyID) -- Looks for wreck return ret end -local function GetClosestWreck(x, z, cx, cz, radius, ally) -- +local function GetClosestWreck(x, z, cx, cz, radius, ally) local wrecks = GetEligiableWrecksInArea(cx, cz, radius, ally) if #wrecks == 0 then -- double safety. return nil @@ -98,22 +98,9 @@ local function GetClosestWreck(x, z, cx, cz, radius, ally) -- return lowestID end -local function IsThereEligiableWreckNearby(x, z, radius, allyteam) - local check = spGetFeaturesInCylinder(x, z, radius) - for i = 1, #check do - local featureID = check[i] - if featurecache[featureID] then - if needLOS then - local x, y, z = spGetFeaturePosition(featureID) - if spIsPosInLos(x, y, z, allyteam) then - return featureID -- return the lowest one in range - end - else - return featureID - end - end - end - return nil +local function IsThereEligiableWreckNearby(x, z, radius, allyteam) -- stupid check. (for when we don't want the closest wreck) + local check = GetEligiableWrecksInArea(x, z, radius, allyteam) + return #check > 0 and check[1] or nil end function gadget:FeatureCreated(featureID, allyTeamID) @@ -126,8 +113,11 @@ function gadget:FeatureDestroyed(featureID) end function gadget:AllowCommand(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOptions, cmdTag, synced) - if cmdID == CMD_GREYGOO and not GooDefs[unitDefID] then -- screen against non-grey gooers using area greygoo. - return false + if cmdID == CMD_GREYGOO then -- screen against bad things + if not GooDefs[unitDefID] or not (#cmdParams == 1 or #cmdParams == 4) then + Spring.Echo("Invalid params? " .. tostring(cmdParams[1]) .. ", " .. tostring(cmdParams[2]) .. ", " .. tostring(cmdParams[3]) .. ", " .. tostring(cmdParams[4])) + return false + end else return true end @@ -171,7 +161,8 @@ function gadget:CommandFallback(unitID, unitDefID, unitTeam, cmdID, cmdParams, c --Spring.Echo("GREYGOO: No eligible wrecks nearby") return true, true end - IterableMap.Add(handled, unitID, {def = unitDefID, done = false, params = cmdParams, goal = -9999}) -- we found a new unit! + local persistent = cmdOptions.alt and #cmdParams > 1 + IterableMap.Add(handled, unitID, {def = unitDefID, done = false, params = cmdParams, goal = -9999, updates = 0, persistent = persistent}) -- we found a new unit! end return true, false -- we're still not done here. end @@ -181,19 +172,15 @@ function gadget:GameFrame(f) if f%5 == 0 then -- 6hz for unitID, data in IterableMap.Iterator(handled) do if not data.done then -- don't handle things that are done. + data.updates = data.updates + 1 --Spring.Echo("GreyGoo: Update " .. unitID .. ": Goal: " .. data.goal) - local greygooconfig = GooDefs[data.def] local currentcmd = spGetUnitCommands(unitID, 1) if not spValidUnitID(unitID) or #currentcmd == 0 or currentcmd[1].id ~= CMD_GREYGOO then -- safety. first we check if we have any commands, then if we're not doing grey goo anymore. --Spring.Echo("Invalid unit or not working") IterableMap.Remove(handled, unitID) else - local params = data.params - local commandRadius = data.params[4] - local commandX = data.params[1] - local commandZ = data.params[3] - local range = greygooconfig.range - local wantedrange = range * 0.1 -- puts us clearly in range, and gives us bonus targets (for mostly "free"), potentially. + local range = GooDefs[data.def].range + local wantedrange = range * 0.1 -- puts us clearly in range if type(data.params) ~= "table" then -- this is a single command local featureID = data.params if spValidFeatureID(featureID) then @@ -206,21 +193,25 @@ function gadget:GameFrame(f) data.done = true end else -- this is an area command. + local commandRadius = data.params[4] + local commandX = data.params[1] + local commandZ = data.params[3] local allyTeam = spGetUnitAllyTeam(unitID) + local x, y, z = spGetUnitPosition(unitID) if data.goal == -9999 or not spValidFeatureID(data.goal) then -- haven't set a goal yet or our current greygoo task is complete. - local x, y, z = spGetUnitPosition(unitID) - local newgoal = IsThereEligiableWreckNearby(x, z, range, allyTeam) - if newgoal then - data.goal = newgoal -- set our new goal to a nearby wreck in range (we're still eating something, no sense moving onto other things yet) - else - local id = GetClosestWreck(x, z, commandX, commandZ, commandRadius, allyTeam) - if id then - local gx, gy, gz = spGetFeaturePosition(id) - spSetUnitMoveGoal(unitID, gx, gy, gz, wantedrange) - data.goal = id - else - data.done = true - end + local id = GetClosestWreck(x, z, commandX, commandZ, commandRadius, allyTeam) + if id then + local gx, gy, gz = spGetFeaturePosition(id) + spSetUnitMoveGoal(unitID, gx, gy, gz, wantedrange) + data.goal = id + elseif not data.persistent then + data.done = true + end + elseif data.updates == 3 then -- check if we're within range (fires off every 3rd update, this staggers the check) + data.updates = 0 + local px, py, pz = spGetFeaturePosition(data.goal) + if Distance(px, x, pz, z) > range * 0.98 then + spSetUnitMoveGoal(unitID, px, py, pz, wantedrange) -- we may have been pushed by allies or other grey gooers. prevents softlock. end end end @@ -232,6 +223,7 @@ end function gadget:Initalize() gadgetHandler:RegisterCMDID(CMD_GREYGOO) + Spring.SetCustomCommandDrawData(CMD_GREYGOO, "Reclaim", {0.8, 0.3, 0.3, 0.7}, true) for _, unitID in pairs(Spring.GetAllUnits()) do gadget:UnitCreated(unitID, Spring.GetUnitDefID(unitID)) end diff --git a/LuaRules/Gadgets/unit_grey_goo.lua b/LuaRules/Gadgets/unit_grey_goo.lua index 6fe4eb32ac..0c6d05716c 100644 --- a/LuaRules/Gadgets/unit_grey_goo.lua +++ b/LuaRules/Gadgets/unit_grey_goo.lua @@ -42,6 +42,7 @@ local spSetUnitRulesParam = Spring.SetUnitRulesParam local spGetUnitRulesParam = Spring.GetUnitRulesParam local spGetFeatureDefID = Spring.GetFeatureDefID local spSpawnCEG = Spring.SpawnCEG +local spGetUnitCommands = Spring.GetUnitCommands local CMD_FIRE_STATE = CMD.FIRE_STATE local CMD_MOVE_STATE = CMD.MOVE_STATE @@ -275,7 +276,13 @@ function gadget:GameFrame(f) spGiveOrderToUnit(newId, CMD_FIRE_STATE, {firestate}, 0) spGiveOrderToUnit(newId, CMD_MOVE_STATE, {movestate}, 0) - spGiveOrderToUnit(newId, CMD_GUARD , {unitIndex[i]} , 0) + local currentcmd = spGetUnitCommands(unitIndex[i], 1) + if currentcmd[1].id == CMD_GREYGOO then + spGiveOrderToUnit(newId, CMD_GREYGOO, currentcmd[1].params, 0) + spGiveOrderToUnit(newId, CMD_GUARD, {unitIndex[i]}, CMD.OPT_SHIFT) + else + spGiveOrderToUnit(newId, CMD_GUARD , {unitIndex[i]} , 0) + end spSpawnCEG(CEG_SPAWN, x, y, z, From 8059d87a283d8f62f625d818c6422c6f47e5f859 Mon Sep 17 00:00:00 2001 From: Shaman <48730089+Arch-Shaman@users.noreply.github.com> Date: Sun, 4 Apr 2021 01:23:40 -0400 Subject: [PATCH 5/7] Deduplicate value --- LuaUI/Configs/integral_menu_commands.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LuaUI/Configs/integral_menu_commands.lua b/LuaUI/Configs/integral_menu_commands.lua index 4b3982bc15..e293e55bf7 100644 --- a/LuaUI/Configs/integral_menu_commands.lua +++ b/LuaUI/Configs/integral_menu_commands.lua @@ -43,7 +43,6 @@ local cmdPosDef = { [CMD_AREA_MEX] = {pos = 7, priority = 1}, [CMD.REPAIR] = {pos = 7, priority = 2}, [CMD.RECLAIM] = {pos = 7, priority = 3}, - [CMD_GREYGOO] = {pos = 7, priority = 3}, [CMD.RESURRECT] = {pos = 7, priority = 4}, [CMD.WAIT] = {pos = 7, priority = 5}, [CMD_FIND_PAD] = {pos = 7, priority = 6}, @@ -51,6 +50,7 @@ local cmdPosDef = { [CMD.LOAD_UNITS] = {pos = 7, priority = 7}, [CMD.UNLOAD_UNITS] = {pos = 7, priority = 8}, [CMD_RECALL_DRONES] = {pos = 7, priority = 10}, + [CMD_GREYGOO] = {pos = 7, priority = 11}, [CMD_UNIT_SET_TARGET_CIRCLE] = {pos = 13, priority = 2}, [CMD_UNIT_CANCEL_TARGET] = {pos = 13, priority = 2}, From b5356c5a3ad44c0b02546da200e8c8490cbc788c Mon Sep 17 00:00:00 2001 From: Shaman <48730089+Arch-Shaman@users.noreply.github.com> Date: Wed, 26 May 2021 01:25:11 -0400 Subject: [PATCH 6/7] Render Grey Goo CMD properly --- LuaUI/Widgets/gui_command_alpha.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/LuaUI/Widgets/gui_command_alpha.lua b/LuaUI/Widgets/gui_command_alpha.lua index d3954cb508..b24b5b9320 100644 --- a/LuaUI/Widgets/gui_command_alpha.lua +++ b/LuaUI/Widgets/gui_command_alpha.lua @@ -38,5 +38,6 @@ function widget:Initialize() Spring.SetCustomCommandDrawData(CMD_RESTORE, "Restore2", terraformColor, false) Spring.SetCustomCommandDrawData(CMD_EXTENDED_LOAD, CMD.LOAD_UNITS, {0,0.6,0.6,cmdAlpha},true) Spring.SetCustomCommandDrawData(CMD_EXTENDED_UNLOAD, CMD.UNLOAD_UNITS, {0.6,0.6,0,cmdAlpha}) - Spring.SetCustomCommandDrawData(CMD_TURN, "Patrol", {0,1,0,cmdAlpha}) + Spring.SetCustomCommandDrawData(CMD_TURN, "Patrol", {0, 1, 0, cmdAlpha}) + Spring.SetCustomCommandDrawData(CMD_GREYGOO, "Reclaim", {0.8, 0.3, 0.3, cmdAlpha}, true) end From 2189cf47efdcb310341cdff8953b277821c64b65 Mon Sep 17 00:00:00 2001 From: Shaman <48730089+Arch-Shaman@users.noreply.github.com> Date: Wed, 26 May 2021 18:31:27 -0400 Subject: [PATCH 7/7] Bunch of fixes --- LuaRules/Gadgets/cmd_greygoo.lua | 39 +++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/LuaRules/Gadgets/cmd_greygoo.lua b/LuaRules/Gadgets/cmd_greygoo.lua index 1291135b36..f543df9d1e 100644 --- a/LuaRules/Gadgets/cmd_greygoo.lua +++ b/LuaRules/Gadgets/cmd_greygoo.lua @@ -3,19 +3,18 @@ function gadget:GetInfo() name = "Area Grey Goo Handler", desc = "Units will consume all wreckage in an area", author = "Shaman", - date = "April 1, 2021", + date = "April 1st, 2021", license = "CC-0", layer = 5, enabled = true, } end -include("LuaRules/Configs/customcmds.h.lua") - if (not gadgetHandler:IsSyncedCode()) then return end +include("LuaRules/Configs/customcmds.h.lua") local IterableMap = VFS.Include("LuaRules/Gadgets/Include/IterableMap.lua") local handled = IterableMap.New() @@ -26,7 +25,8 @@ local areaGreyGooDesc = { id = CMD_GREYGOO, type = CMDTYPE.ICON_UNIT_FEATURE_OR_AREA, name = 'Grey Goo', -- TODO: better name. Marketing was out today. - action = 'greygoo', + cursor = 'Reclaim', + action = 'reclaim', tooltip = 'Marks an area or wreckage for grey goo.', } @@ -103,6 +103,7 @@ local function IsThereEligiableWreckNearby(x, z, radius, allyteam) -- stupid che return #check > 0 and check[1] or nil end + function gadget:FeatureCreated(featureID, allyTeamID) local featuredef = spGetFeatureDefID(featureID) featurecache[featureID] = validFeatures[featuredef] @@ -113,14 +114,10 @@ function gadget:FeatureDestroyed(featureID) end function gadget:AllowCommand(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOptions, cmdTag, synced) - if cmdID == CMD_GREYGOO then -- screen against bad things - if not GooDefs[unitDefID] or not (#cmdParams == 1 or #cmdParams == 4) then - Spring.Echo("Invalid params? " .. tostring(cmdParams[1]) .. ", " .. tostring(cmdParams[2]) .. ", " .. tostring(cmdParams[3]) .. ", " .. tostring(cmdParams[4])) - return false - end - else - return true + if cmdID == CMD_GREYGOO and not GooDefs[unitDefID] then -- screen against non-grey gooers using area greygoo. + return false end + return true end function gadget:UnitCreated(unitID, unitDefID) @@ -162,6 +159,7 @@ function gadget:CommandFallback(unitID, unitDefID, unitTeam, cmdID, cmdParams, c return true, true end local persistent = cmdOptions.alt and #cmdParams > 1 + --Spring.Echo("Persistent: " .. tostring(persistent)) IterableMap.Add(handled, unitID, {def = unitDefID, done = false, params = cmdParams, goal = -9999, updates = 0, persistent = persistent}) -- we found a new unit! end return true, false -- we're still not done here. @@ -179,14 +177,14 @@ function gadget:GameFrame(f) --Spring.Echo("Invalid unit or not working") IterableMap.Remove(handled, unitID) else - local range = GooDefs[data.def].range - local wantedrange = range * 0.1 -- puts us clearly in range + local wantedrange = 16 + local wantedspeed = UnitDefs[data.def].speed if type(data.params) ~= "table" then -- this is a single command local featureID = data.params if spValidFeatureID(featureID) then if data.goal ~= featureID then -- we haven't set the move goal yet. local x, y, z = spGetFeaturePosition(featureID) -- find the location of our target. - spSetUnitMoveGoal(unitID, x, y, z, wantedrange) + spSetUnitMoveGoal(unitID, x, y, z, wantedrange, wantedspeed, true) data.goal = featureID end else @@ -202,16 +200,19 @@ function gadget:GameFrame(f) local id = GetClosestWreck(x, z, commandX, commandZ, commandRadius, allyTeam) if id then local gx, gy, gz = spGetFeaturePosition(id) - spSetUnitMoveGoal(unitID, gx, gy, gz, wantedrange) + spSetUnitMoveGoal(unitID, gx, gy, gz, wantedrange, wantedspeed, true) data.goal = id elseif not data.persistent then + spSetUnitMoveGoal(unitID, commandX, Spring.GetGroundHeight(commandX, commandZ), commandZ, wantedrange) -- don't allow them to run off to fuckall. data.done = true end elseif data.updates == 3 then -- check if we're within range (fires off every 3rd update, this staggers the check) data.updates = 0 + --Spring.Echo("Check Distance. Wanted Distance: " .. wantedrange) local px, py, pz = spGetFeaturePosition(data.goal) - if Distance(px, x, pz, z) > range * 0.98 then - spSetUnitMoveGoal(unitID, px, py, pz, wantedrange) -- we may have been pushed by allies or other grey gooers. prevents softlock. + if Distance(px, x, pz, z) >= wantedrange * 2 then + --Spring.Echo("Move to") + spSetUnitMoveGoal(unitID, px, py, pz, wantedrange, wantedspeed, true) -- we may have been pushed by allies or other grey gooers. prevents softlock. end end end @@ -223,7 +224,9 @@ end function gadget:Initalize() gadgetHandler:RegisterCMDID(CMD_GREYGOO) - Spring.SetCustomCommandDrawData(CMD_GREYGOO, "Reclaim", {0.8, 0.3, 0.3, 0.7}, true) + --Spring.SetCustomCommandDrawData(CMD_GREYGOO, "Reclaim", {0.8, 0.3, 0.3, 0.7}, true) + -- It should be noted that Gadgets cannot set the custom command draw data for whatever reason. + -- Use gui_command_alpha widget instead. for _, unitID in pairs(Spring.GetAllUnits()) do gadget:UnitCreated(unitID, Spring.GetUnitDefID(unitID)) end