From 4eed87d6df67e9691b259eefb7326c7b4227c944 Mon Sep 17 00:00:00 2001 From: clyf Date: Sun, 9 Jun 2024 13:31:16 -0400 Subject: [PATCH] Rework `commandmode.lua` to make it easier to maintain (#6200) Functionally the file should be the same. --- engine/User.lua | 2 +- lua/ui/game/commandmeshes.lua | 1 + lua/ui/game/commandmode.lua | 332 +++++++++++++++++++--------------- 3 files changed, 184 insertions(+), 151 deletions(-) diff --git a/engine/User.lua b/engine/User.lua index a9c024a0a7..bd440a03b9 100644 --- a/engine/User.lua +++ b/engine/User.lua @@ -236,7 +236,7 @@ function DecreaseBuildCountInQueue(queueIndex, count) end ---Deletes a command from the player command queue. ----Each player has an array that holds all commands for all units, the commandID indexes to this array. +---Each player has an array that holds all commands for all units, the commandID indexes to that array. ---Note: this function doesn't receive any units as arguments--you will have to retrieve the commandId by UserUnit:GetCommandQueue()[commandIndex].ID ---@param commandId number commandId, from UserUnit:GetCommandQueue()[commandIndex].ID function DeleteCommand(commandId) diff --git a/lua/ui/game/commandmeshes.lua b/lua/ui/game/commandmeshes.lua index e20b2fc5c0..d1ebfe52b3 100644 --- a/lua/ui/game/commandmeshes.lua +++ b/lua/ui/game/commandmeshes.lua @@ -3,6 +3,7 @@ commandMeshResources = { Move = { '/meshes/game/Move_lod0.scm', '/meshes/game/Move_albedo.dds' }, Attack = { '/meshes/game/Attack_lod0.scm', '/meshes/game/Attack_albedo.dds' }, + AggressiveMove = { '/meshes/game/Attack_lod0.scm', '/meshes/game/Attack_albedo.dds' }, Guard = { '/meshes/game/Assist_lod0.scm', '/meshes/game/Assist_albedo.dds' }, Capture = { '/meshes/game/Capture_lod0.scm', '/meshes/game/Capture_albedo.dds' }, Ferry = { '/meshes/game/Ferry_lod0.scm', '/meshes/game/Ferry_albedo.dds' }, diff --git a/lua/ui/game/commandmode.lua b/lua/ui/game/commandmode.lua index 67982a5570..7dedf43c6b 100644 --- a/lua/ui/game/commandmode.lua +++ b/lua/ui/game/commandmode.lua @@ -46,8 +46,8 @@ local MathAtan = math.atan --- | 'BuildMobile' --- | 'Tactical' --- | 'Nuke' ---- | 'TransportReverseLoadUnits' # when you right click a transport ---- | 'TransportLoadUnits' # when you right click a unit with a transport in your selection +--- | 'TransportReverseLoadUnits' # when you select a transport and right click a unit +--- | 'TransportLoadUnits' # when you select a unit and right click a transport --- | 'TransportUnloadUnits' --- | 'TransportUnloadSpecificUnits' # when you click to unload specific units --- | 'Ferry' @@ -65,11 +65,7 @@ local MathAtan = math.atan --- | 'Pause' --- | 'Dock' --- | 'DetachFromTransport' - ----@class UserCommandTarget ----@field EntityId? EntityId ----@field Position Vector ----@field Type 'Position' | 'Entity' | 'None' +--- | 'Repair' ---@class UserCommand ---@field Blueprint UnitId @@ -78,6 +74,12 @@ local MathAtan = math.atan ---@field LuaParams table ---@field Target UserCommandTarget ---@field Units UserUnit[] +---@field SkipBlip? boolean # if we don't have a feedback blip defined, skips the default command blip + +---@class UserCommandTarget +---@field EntityId? EntityId +---@field Position Vector +---@field Type 'Position' | 'Entity' | 'None' ---@class MeshInfo ---@field Position Vector @@ -251,30 +253,8 @@ function InCommandMode() return commandMode ~= false end ---- A helper function to add the correct feedback animation. --- @param pos The position of the feedback animation. --- @param type The type of feedback animation. -function AddCommandFeedbackByType(pos, type) - if commandMeshResources[type] == nil then - return false; - else - AddCommandFeedbackBlip( - { - Position = pos, - MeshName = commandMeshResources[type][1], - TextureName = commandMeshResources[type][2], - ShaderName = 'CommandFeedback', - UniformScale = 0.125, - }, - 0.7 - ) - end - - return true; -end - ---- A helper function for a specific feedback animation. --- @param pos The position of the feedback animation. +---Helper function for a default feedback blip animation +---@param pos Vector Position of the feedback animation function AddDefaultCommandFeedbackBlips(pos) AddCommandFeedbackBlip( { @@ -299,6 +279,26 @@ function AddDefaultCommandFeedbackBlips(pos) ) end +---Helper function for a feedback blip animation based on the command type +---@param command UserCommand +function AddCommandFeedbackByType(command) + local meshResource = commandMeshResources[command.CommandType] + if meshResource then + AddCommandFeedbackBlip( + { + Position = command.Target.Position, + MeshName = meshResource[1], + TextureName = meshResource[2], + ShaderName = 'CommandFeedback', + UniformScale = 0.125, + }, + 0.7 + ) + elseif not command.SkipBlip then + AddDefaultCommandFeedbackBlips(command.Target.Position) + end +end + --- Creates a callback to spawn a unit triggered by the cheat menu. -- @param command Command that contains the position of the click -- @param data A shallow copy of the modeData to make the function pure data-wise @@ -483,67 +483,16 @@ local function OnGuardCopy(guardees, unit) end end ---- Is called when a unit receies a guard / assist order ----@param guardees UserUnit[] ----@param unit UserUnit -local function OnGuard(guardees, unit) - if unit:GetArmy() == GetFocusArmy() then - OnGuardUpgrade(guardees, unit) - OnGuardUnpause(guardees, unit) - OnGuardCopy(guardees, unit) - end -end - ---- Called by the engine when a new command has been issued by the player. --- @param command Information surrounding the command that has been issued, such as its CommandType or its Target. ---@param command UserCommand ----@return boolean -function OnCommandIssued(command) - - -- Area reclaim dragger, command mode only - if command.CommandType == 'Reclaim' and command.Target.EntityId and modeData.name == "RULEUCC_Reclaim" then - import("/lua/ui/game/hotkeys/area-reclaim-order.lua").AreaReclaimOrder(command) - end - - -- Area reclaim dragger, command mode only - if command.CommandType == 'Attack' and command.Target.Type == 'Position' and modeData.name == "RULEUCC_Attack" then - import("/lua/ui/game/hotkeys/area-attack-order.lua").AreaAttackOrder(command) - 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 - SimCallback({ Func = 'ImmediateHiveUpgrade', Args = { UpgradeTo = command.Blueprint } }, true) +local function OnGuardIssued(command) + if command.Target.EntityId then + local unit = GetUnitById(command.Target.EntityId) ---@cast unit UserUnit + local guards = command.Units + if unit:GetArmy() == GetFocusArmy() then + OnGuardUpgrade(guards, unit) + OnGuardUnpause(guards, unit) + OnGuardCopy(guards, unit) end - end - - -- unusual command, where we use the build interface - if modeData.callback and command.CommandType == "BuildMobile" and (not command.Units[1]) then - modeData.callback(modeData, command) - return false - end - - -- part of the cheat menu - if modeData.cheat and command.CommandType == "BuildMobile" and (not command.Units[1]) then - CheatSpawn(command, modeData) - command.Units = {} - return false - end - - -- is set when we hold shift, to queue up multiple commands. This is where the command mode stops - if not command.Clear then - issuedOneCommand = true - else - EndCommandMode(true) - end - - -- called when: - -- - a factory-like construction that is not finished is being continued - -- - a (finished) unit is being guarded (right clicked) - if command.CommandType == 'Guard' and command.Target.EntityId then - - local unit = GetUnitById(command.Target.EntityId) - OnGuard(command.Units, unit) -- Detect and fix a simulation freeze by clearing the command queue of all factories that take part in a cycle if EntityCategoryContains(categoriesFactories, command.Blueprint) then @@ -567,80 +516,163 @@ function OnCommandIssued(command) local units = command.Units --[[@as (UserUnit[])]] import("/lua/ui/game/hotkeys/capping.lua").AssistToCap(target, units) end + end +end - -- called when: - -- - a construction is started - elseif command.CommandType == 'BuildMobile' then - -- add a small animation (just change the 2nd argument to 5 and back) - AddCommandFeedbackBlip( - { - Position = command.Target.Position, - BlueprintID = command.Blueprint, - TextureName = '/meshes/game/flag02d_albedo.dds', - ShaderName = 'CommandFeedback', - UniformScale = 1, - }, - 0.7 - ) +---@param command UserCommand +local function OnBuildMobileIssued(command) + if not command.Units[1] then + if modeData.callback then -- unusual command, where we use the build interface + modeData.callback(modeData, command) + return true + elseif modeData.cheat then -- part of the cheat menu + CheatSpawn(command, modeData) + command.Units = {} + return true + end + end + -- We want our command feedback blip to match the blueprint, and we want to skip the default + command.SkipBlip = true + AddCommandFeedbackBlip( + { + Position = command.Target.Position, + BlueprintID = command.Blueprint, + TextureName = '/meshes/game/flag02d_albedo.dds', + ShaderName = 'CommandFeedback', + UniformScale = 1, + }, + 0.7 + ) +end - -- called when: - -- - a construction is being continued building (for non-factory units) - -- - a construction is being repaired - elseif command.CommandType == 'Repair' then +---@param command UserCommand +local function OnReclaimIssued(command) + -- Area reclaim dragger, command mode only + if command.Target.EntityId and modeData.name == "RULEUCC_Reclaim" then + import("/lua/ui/game/hotkeys/area-reclaim-order.lua").AreaReclaimOrder(command) + end +end - -- see if we can rebuild a structure - if command.Target.Type == 'Entity' then -- repair wreck to rebuild - local cb = { Func = "Rebuild", Args = { entity = command.Target.EntityId, Clear = command.Clear } } - SimCallback(cb, true) - end +---@param command UserCommand +local function OnRepairIssued(command) + -- see if we can rebuild a structure + if command.Target.Type == 'Entity' then -- repair wreck to rebuild + local cb = { Func = "Rebuild", Args = { entity = command.Target.EntityId, Clear = command.Clear } } + SimCallback(cb, true) + end +end - -- called when: - -- - ? - elseif command.CommandType == 'Script' and command.LuaParams.TaskName == 'AttackMove' then - local avgPoint = { 0, 0 } - for _, unit in command.Units do - avgPoint[1] = avgPoint[1] + unit:GetPosition()[1] - avgPoint[2] = avgPoint[2] + unit:GetPosition()[3] +---@param command UserCommand +local function OnAttackIssued(command) + -- Area attack dragger, command mode only + if command.Target.Type == 'Position' and modeData.name == "RULEUCC_Attack" then + import("/lua/ui/game/hotkeys/area-attack-order.lua").AreaAttackOrder(command) + end +end + +---@param command UserCommand +local function OnUpgradeIssued(command) + -- if we're trying to upgrade hives then this allows us to force the upgrade to happen immediately + if (command.Blueprint == "xrb0204" or command.Blueprint == "xrb0304") then + if not IsKeyDown('Shift') then + SimCallback({ Func = 'ImmediateHiveUpgrade', Args = { UpgradeTo = command.Blueprint } }, true) end - avgPoint[1] = avgPoint[1] / TableGetN(command.Units) - avgPoint[2] = avgPoint[2] / TableGetN(command.Units) + end +end + +---@param command UserCommand +local function OnScriptIssued(command) + if command.LuaParams then + if command.LuaParams.TaskName == 'AttackMove' then + local avgPoint = { 0, 0 } + for _, unit in command.Units do + avgPoint[1] = avgPoint[1] + unit:GetPosition()[1] + avgPoint[2] = avgPoint[2] + unit:GetPosition()[3] + end + avgPoint[1] = avgPoint[1] / TableGetN(command.Units) + avgPoint[2] = avgPoint[2] / TableGetN(command.Units) - avgPoint[1] = command.Target.Position[1] - avgPoint[1] - avgPoint[2] = command.Target.Position[3] - avgPoint[2] + avgPoint[1] = command.Target.Position[1] - avgPoint[1] + avgPoint[2] = command.Target.Position[3] - avgPoint[2] - local rotation = MathAtan(avgPoint[1] / avgPoint[2]) - rotation = rotation * 180 / MathPi - if avgPoint[2] < 0 then - rotation = rotation + 180 + local rotation = MathAtan(avgPoint[1] / avgPoint[2]) + rotation = rotation * 180 / MathPi + if avgPoint[2] < 0 then + rotation = rotation + 180 + end + local cb = { Func = "AttackMove", Args = { Target = command.Target.Position, Rotation = rotation, + Clear = command.Clear } } + SimCallback(cb, true) + elseif command.LuaParams.Enhancement then + EnhancementQueueFile.enqueueEnhancement(command.Units, command.LuaParams.Enhancement) end - local cb = { Func = "AttackMove", Args = { Target = command.Target.Position, Rotation = rotation, - Clear = command.Clear } } - SimCallback(cb, true) - AddDefaultCommandFeedbackBlips(command.Target.Position) + end +end - -- called when: - -- - ? - elseif command.Clear == true and command.CommandType ~= 'Stop' and TableGetN(command.Units) == 1 and - checkBadClean(command.Units[1]) then - watchForQueueChange(command.Units[1]) +---@param command UserCommand +local function OnStopIssued(command) + EnhancementQueueFile.clearEnhancements(command.Units) +end - -- called when: - -- - ? - elseif command.CommandType == 'Script' and command.LuaParams and command.LuaParams.Enhancement then - EnhancementQueueFile.enqueueEnhancement(command.Units, command.LuaParams.Enhancement) +-- Callbacks for different command types, nil values for reference to functions that don't exist yet +local OnCommandIssuedCallback = { + None = nil, + Stop = OnStopIssued, + Reclaim = OnReclaimIssued, + Move = nil, + Attack = OnAttackIssued, + Guard = OnGuardIssued, + AggressiveMove = nil, + Upgrade = OnUpgradeIssued, + Build = nil, + BuildMobile = OnBuildMobileIssued, + Tactical = nil, + Nuke = nil, + TransportReverseLoadUnits = nil, + TransportLoadUnits = nil, + TransportUnloadUnits = nil, + TransportUnloadSpecificUnits = nil, + Ferry = nil, + AssistMove = nil, + Script = OnScriptIssued, + Capture = nil, + FormMove = nil, + FormAggressiveMove = nil, + OverCharge = nil, + FormAttack = nil, + Teleport = nil, + Patrol = nil, + FormPatrol = nil, + Sacrifice = nil, + Pause = nil, + Dock = nil, + DetachFromTransport = nil, + Repair = OnRepairIssued, +} - -- called when: - -- - a generic stop command is issued - elseif command.CommandType == 'Stop' then - EnhancementQueueFile.clearEnhancements(command.Units) +--- Called by the engine when a new command has been issued by the player. +-- @param command Information surrounding the command that has been issued, such as its CommandType or its Target. +---@param command UserCommand +function OnCommandIssued(command) - -- called when: - -- - none of the above applies + -- If our callback returns true or we don't have a command type, we skip the rest of our logic + if (OnCommandIssuedCallback[command.CommandType] and OnCommandIssuedCallback[command.CommandType](command)) + or command.CommandType == 'None' then + return + end + -- is set when we hold shift, to queue up multiple commands. This is where the command mode stops + if not command.Clear then + issuedOneCommand = true else - if AddCommandFeedbackByType(command.Target.Position, command.CommandType) == false then - AddDefaultCommandFeedbackBlips(command.Target.Position) + EndCommandMode(true) + if command.CommandType ~= 'Stop' + and TableGetN(command.Units) == 1 + and checkBadClean(command.Units[1]) then + watchForQueueChange(command.Units[1]) end end + + AddCommandFeedbackByType(command) end --- ??? @@ -658,4 +690,4 @@ GameMain.AddBeatFunction(OnCommandModeBeat) local Dragger = import("/lua/maui/dragger.lua").Dragger local Construction = import("/lua/ui/game/construction.lua") local UIMain = import("/lua/ui/uimain.lua") -local Orders = import("/lua/ui/game/orders.lua") +local Orders = import("/lua/ui/game/orders.lua") \ No newline at end of file