diff --git a/.github/workflows/deploy-faf.yaml b/.github/workflows/deploy-faf.yaml index d06517b3cd..ddf54a8187 100644 --- a/.github/workflows/deploy-faf.yaml +++ b/.github/workflows/deploy-faf.yaml @@ -41,7 +41,15 @@ jobs: uses: actions/checkout@v4 with: repository: FAForever/fa - ref: develop + ref: master + + # Disable debugging statements + - name: Update references + run: | + sed -i 's/EnabledSpewing = true,/EnabledSpewing = false,/' "/lua/shared/components/DebugComponent.lua" + sed -i 's/EnabledLogging = true,/EnabledLogging = false,/' "/lua/shared/components/DebugComponent.lua" + sed -i 's/EnabledWarnings = true,/EnabledWarnings = false,/' "/lua/shared/components/DebugComponent.lua" + sed -i 's/EnabledDrawing = true,/EnabledDrawing = false,/' "/lua/shared/components/DebugComponent.lua" # Update the deploy/faf branch, we force push here because # we're not interested in fixing conflicts diff --git a/.github/workflows/deploy-fafbeta.yaml b/.github/workflows/deploy-fafbeta.yaml index 053ddd6a35..ca613b8e1f 100644 --- a/.github/workflows/deploy-fafbeta.yaml +++ b/.github/workflows/deploy-fafbeta.yaml @@ -44,6 +44,13 @@ jobs: repository: FAForever/fa ref: develop + # Disable debugging statements + # + # You can overwrite these adjustments by setting up a `Debug` folder + - name: Update references + run: | + sed -i 's/EnabledDrawing = true,/EnabledDrawing = false,/' "/lua/shared/components/DebugComponent.lua" + # Update the deploy/fafbeta branch, we force push here because # we're not interested in fixing conflicts - name: Update deploy/fafbeta diff --git a/.github/workflows/deploy-fafdevelop.yaml b/.github/workflows/deploy-fafdevelop.yaml index d6b5153b3d..c0dcbee647 100644 --- a/.github/workflows/deploy-fafdevelop.yaml +++ b/.github/workflows/deploy-fafdevelop.yaml @@ -44,6 +44,13 @@ jobs: repository: FAForever/fa ref: develop + # Disable debugging statements + # + # You can overwrite these adjustments by setting up a `Debug` folder + - name: Update references + run: | + sed -i 's/EnabledDrawing = true,/EnabledDrawing = false,/' "/lua/shared/components/DebugComponent.lua" + # Update the deploy/fafdevelop branch, we force push here because # we're not interested in fixing conflicts - name: Update deploy/fafdevelop diff --git a/docs/deployment.md b/docs/deployment.md index a578d8a99e..adc20cb33e 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -17,12 +17,16 @@ All three branches originate from the `develop` branch, which is the default bra ## Deployment procedures for the FAF game type -The following (manual) stepz are relevant to create a valid deployment to the FAF game type. +The following (manual) steps are relevant to create a valid deployment to the FAF game type. - (1) Update the game version in [mod_info.lua](https://github.com/FAForever/fa/blob/c36404675c7a95cda20fe867d78bd1c01c7df103/mod_info.lua) and [version.lua](https://github.com/FAForever/fa/blob/c36404675c7a95cda20fe867d78bd1c01c7df103/lua/version.lua). - (2) Update the game executable. This needs to be done by a server administrator. This is only required when there are changes to the executable. - (3) Update the changelog in [changelog.md](/changelog) and [changelogData.lua](https://github.com/FAForever/fa/blob/c36404675c7a95cda20fe867d78bd1c01c7df103/lua/ui/lobby/changelogData.lua). - (4) Update the game version in [changelogData.lua](https://github.com/FAForever/fa/blob/c36404675c7a95cda20fe867d78bd1c01c7df103/lua/ui/lobby/changelogData.lua). +- (5) Push everything that you want to release to the [master](https://github.com/FAForever/fa/tree/master) branch. +- (6) Use the [Deploy to FAF Workflow](https://github.com/FAForever/fa/actions/workflows/deploy-faf.yaml) to perform the deployment. + +The last step allows us to systematically post process what we deploy. You can learn more about this by inspecting the workflow file. ## Automated deployments diff --git a/init_fafbeta.lua b/init_fafbeta.lua index 676cacee5f..26d83cce28 100644 --- a/init_fafbeta.lua +++ b/init_fafbeta.lua @@ -640,4 +640,7 @@ MountDirectory(SHGetFolderPath('LOCAL_APPDATA') .. 'Gas Powered Games/Supreme Co MountDirectory(fa_path .. "/movies", '/movies') MountDirectory(fa_path .. "/sounds", '/sounds') MountDirectory(fa_path .. "/maps", '/maps') -MountDirectory(fa_path .. "/fonts", '/fonts') \ No newline at end of file +MountDirectory(fa_path .. "/fonts", '/fonts') + +-- Allows developers to embed code to debug a replay +table.insert(path, 1, { dir = InitFileDir .. '\\..\\Debug', mountpoint = '/' }) diff --git a/init_fafdevelop.lua b/init_fafdevelop.lua index 66a412ce48..3d4569c530 100644 --- a/init_fafdevelop.lua +++ b/init_fafdevelop.lua @@ -642,5 +642,5 @@ MountDirectory(fa_path .. "/sounds", '/sounds') MountDirectory(fa_path .. "/maps", '/maps') MountDirectory(fa_path .. "/fonts", '/fonts') --- Please do not delete these lines. This is necessary for testing. -table.insert(path, 1, { dir = InitFileDir .. '\\..\\DevData', mountpoint = '/' }) +-- Allows developers to embed code to debug a replay +table.insert(path, 1, { dir = InitFileDir .. '\\..\\Debug', mountpoint = '/' }) diff --git a/lua/shared/components/DebugComponent.lua b/lua/shared/components/DebugComponent.lua new file mode 100644 index 0000000000..fcaf61de3d --- /dev/null +++ b/lua/shared/components/DebugComponent.lua @@ -0,0 +1,30 @@ +--****************************************************************************************************** +--** 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. +--****************************************************************************************************** + +---@class DebugComponent +DebugComponent = ClassSimple { + EnabledSpewing = true, + EnabledLogging = true, + EnabledWarnings = true, + EnabledErrors = true, + EnabledDrawing = true, +} diff --git a/lua/sim/Projectile.lua b/lua/sim/Projectile.lua index fed47e296a..71df80d9cd 100644 --- a/lua/sim/Projectile.lua +++ b/lua/sim/Projectile.lua @@ -24,6 +24,8 @@ local CreateEmitterAtBone = CreateEmitterAtBone local CreateEmitterAtEntity = CreateEmitterAtEntity local EntityCategoryContains = EntityCategoryContains +local DebugProjectileComponent = import("/lua/sim/projectiles/components/DebugProjectileComponent.lua").DebugProjectileComponent + local ProjectileMethods = moho.projectile_methods local ProjectileMethodsCreateChildProjectile = ProjectileMethods.CreateChildProjectile local ProjectileMethodsGetMaxZigZag = ProjectileMethods.GetMaxZigZag @@ -82,7 +84,7 @@ local OnImpactPreviousZ = 0 local VectorCached = Vector(0, 0, 0) ----@class Projectile : moho.projectile_methods, InternalObject +---@class Projectile : moho.projectile_methods, InternalObject, DebugProjectileComponent ---@field Blueprint ProjectileBlueprint ---@field Army number ---@field Trash TrashBag @@ -93,7 +95,7 @@ local VectorCached = Vector(0, 0, 0) ---@field IsRedirected? boolean ---@field InnerRing? NukeAOE ---@field OuterRing? NukeAOE -Projectile = ClassProjectile(ProjectileMethods) { +Projectile = ClassProjectile(ProjectileMethods, DebugProjectileComponent) { IsProjectile = true, DestroyOnImpact = true, FxImpactTrajectoryAligned = true, diff --git a/lua/sim/Unit.lua b/lua/sim/Unit.lua index fabef4c92b..2e07017a23 100644 --- a/lua/sim/Unit.lua +++ b/lua/sim/Unit.lua @@ -23,6 +23,7 @@ local TransportShield = import("/lua/shield.lua").TransportShield local Weapon = import("/lua/sim/weapon.lua").Weapon local IntelComponent = import('/lua/defaultcomponents.lua').IntelComponent local VeterancyComponent = import('/lua/defaultcomponents.lua').VeterancyComponent +local DebugUnitComponent = import("/lua/sim/units/components/DebugUnitComponent.lua").DebugUnitComponent local GetBlueprintCaptureCost = import('/lua/shared/captureCost.lua').GetBlueprintCaptureCost @@ -112,7 +113,7 @@ SyncMeta = { local cUnit = moho.unit_methods local cUnitGetBuildRate = cUnit.GetBuildRate ----@class Unit : moho.unit_methods, InternalObject, IntelComponent, VeterancyComponent, AIUnitProperties, UnitBuffFields +---@class Unit : moho.unit_methods, InternalObject, IntelComponent, VeterancyComponent, AIUnitProperties, UnitBuffFields, DebugUnitComponent ---@field CDRHome? LocationType ---@field AIManagerIdentifier? string ---@field Repairers table @@ -145,7 +146,7 @@ local cUnitGetBuildRate = cUnit.GetBuildRate ---@field ReclaimTimeMultiplier? number ---@field CaptureTimeMultiplier? number ---@field PlatoonHandle? Platoon -Unit = ClassUnit(moho.unit_methods, IntelComponent, VeterancyComponent) { +Unit = ClassUnit(moho.unit_methods, IntelComponent, VeterancyComponent, DebugUnitComponent) { IsUnit = true, Weapons = {}, @@ -5440,12 +5441,12 @@ local EntityGetEntityId = _G.moho.entity_methods.GetEntityId local UnitGetCurrentLayer = _G.moho.unit_methods.GetCurrentLayer local UnitGetUnitId = _G.moho.unit_methods.GetUnitId ----@class DummyUnit : moho.unit_methods +---@class DummyUnit : moho.unit_methods, DebugUnitComponent ---@field EntityId EntityId ---@field Army Army ---@field Layer Layer ---@field Blueprint UnitBlueprint -DummyUnit = ClassDummyUnit(moho.unit_methods) { +DummyUnit = ClassDummyUnit(moho.unit_methods, DebugUnitComponent) { IsUnit = true, diff --git a/lua/sim/projectiles/components/DebugProjectileComponent.lua b/lua/sim/projectiles/components/DebugProjectileComponent.lua new file mode 100644 index 0000000000..1eb50eb9c4 --- /dev/null +++ b/lua/sim/projectiles/components/DebugProjectileComponent.lua @@ -0,0 +1,131 @@ +--****************************************************************************************************** +--** 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. +--****************************************************************************************************** + +local DebugComponent = import("/lua/shared/components/DebugComponent.lua").DebugComponent + +---@class DebugProjectileComponent: DebugComponent +DebugProjectileComponent = Class(DebugComponent) { + + ---@param self DebugProjectileComponent | Projectile + ---@param ... any + DebugSpew = function(self, ...) + if not self.EnabledSpewing then + return + end + + local launcher = self.Launcher + if launcher and IsUnit(launcher) and (not IsDestroyed(launcher)) then + -- allows the developer to track down the launcher + launcher:SetCustomName(launcher.EntityId or 'unknown') + self:DebugDraw('gray') + end + + SPEW(launcher.UnitId, launcher.EntityId, self.Blueprint.BlueprintId, unpack(arg)) + end, + + ---@param self DebugProjectileComponent | Projectile + ---@param ... any + DebugLog = function(self, ...) + if not self.EnabledLogging then + return + end + + local launcher = self.Launcher + if launcher and IsUnit(launcher) and (not IsDestroyed(launcher)) then + -- allows the developer to track down the launcher + launcher:SetCustomName(launcher.EntityId or 'unknown') + self:DebugDraw('white') + end + + _ALERT(launcher.UnitId, launcher.EntityId, self.Blueprint.BlueprintId, unpack(arg)) + end, + + ---@param self DebugProjectileComponent | Projectile + ---@param ... any + DebugWarn = function(self, ...) + if not self.EnabledWarnings then + return + end + + local launcher = self.Launcher + if launcher and IsUnit(launcher) and (not IsDestroyed(launcher)) then + -- allows the developer to track down the launcher + launcher:SetCustomName(launcher.EntityId or 'unknown') + self:DebugDraw('orange') + end + + WARN(launcher.UnitId, launcher.EntityId, self.Blueprint.BlueprintId, unpack(arg)) + end, + + ---@param self DebugProjectileComponent | Projectile + ---@param message any + DebugError = function(self, message) + if not self.EnabledErrors then + return + end + + local launcher = self.Launcher + if launcher and IsUnit(launcher) and (not IsDestroyed(launcher)) then + -- allows the developer to track down the launcher + launcher:SetCustomName(launcher.EntityId or 'unknown') + self:DebugDraw('red') + end + + error( + string.format( + "%s\t%s\t%s\t%s", + tostring(launcher.UnitId), + tostring(launcher.EntityId), + tostring(self.Blueprint.BlueprintId), + tostring(message) + ) + ) + end, + + ---@param self DebugProjectileComponent | Projectile + ---@param color? Color # Defaults to white + DebugDraw = function(self, color) + if not self.EnabledDrawing then + return + end + + -- we can't draw dead things + if IsDestroyed(self) then + return + end + + -- do not draw everything, just what the developer may be interested in + if not (GetFocusArmy() == -1 or GetFocusArmy() == self.Army) then + return + end + + color = color or 'ffffff' + + local launcher = self.Launcher + if launcher and not IsDestroyed(launcher) then + launcher:DebugDraw(color) + DrawLine(launcher:GetPosition(), self:GetPosition(), color) + end + + DrawCircle(self:GetPosition(), 0.25, color) + end, +} diff --git a/lua/sim/units/components/DebugUnitComponent.lua b/lua/sim/units/components/DebugUnitComponent.lua new file mode 100644 index 0000000000..226fecddec --- /dev/null +++ b/lua/sim/units/components/DebugUnitComponent.lua @@ -0,0 +1,121 @@ +--****************************************************************************************************** +--** 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. +--****************************************************************************************************** + +local DebugComponent = import("/lua/shared/components/DebugComponent.lua").DebugComponent + +---@class DebugUnitComponent : DebugComponent +DebugUnitComponent = Class(DebugComponent) { + + ---@param self DebugUnitComponent | Unit + ---@param ... any + DebugSpew = function(self, ...) + if not self.EnabledSpewing then + return + end + + SPEW(self.UnitId, self.EntityId, unpack(arg)) + + if IsDestroyed(self) then + return + end + + -- allows us the developer track down the unit + self:SetCustomName(tostring(self.EntityId)) + self:DebugDraw('gray') + end, + + ---@param self DebugUnitComponent | Unit + ---@param ... any + DebugLog = function(self, ...) + if not self.EnabledLogging then + return + end + + _ALERT(self.UnitId, self.EntityId, unpack(arg)) + + if IsDestroyed(self) then + return + end + + -- allows the developer to track down the unit + self:SetCustomName(tostring(self.EntityId)) + self:DebugDraw('white') + end, + + ---@param self DebugUnitComponent | Unit + ---@param ... any + DebugWarn = function(self, ...) + if not self.EnabledWarnings then + return + end + + WARN(self.UnitId, self.EntityId, unpack(arg)) + + if IsDestroyed(self) then + return + end + + -- allows the developer to track down the unit + self:SetCustomName(tostring(self.EntityId)) + self:DebugDraw('orange') + end, + + ---@param self DebugUnitComponent | Unit + ---@param message any + DebugError = function(self, message) + if not self.EnabledErrors then + return + end + + if not IsDestroyed(self) then + -- allows the developer to track down the unit + self:SetCustomName(tostring(self.EntityId)) + self:DebugDraw('red') + end + + error(string.format("%s\t%s\t%s", tostring(self.UnitId), tostring(self.EntityId), tostring(message))) + end, + + ---@param self DebugUnitComponent | Unit + ---@param color? Color # Defaults to white + DebugDraw = function(self, color) + if not self.EnabledDrawing then + return + end + + -- we can't draw dead units + if IsDestroyed(self) then + return + end + + -- do not draw everything, just what the developer may be interested in + if not (GetFocusArmy() == -1 or GetFocusArmy() == self.Army) then + return + end + + + color = color or 'ffffff' + + local blueprint = self.Blueprint + DrawCircle(self:GetPosition(), math.max(blueprint.SizeX, blueprint.SizeY, blueprint.SizeZ), color) + end, +} diff --git a/lua/sim/weapon.lua b/lua/sim/weapon.lua index 890d7ae8c9..f2b546b952 100644 --- a/lua/sim/weapon.lua +++ b/lua/sim/weapon.lua @@ -14,6 +14,8 @@ local ParseEntityCategoryProperly = import("/lua/sim/categoryutils.lua").ParseEn local cachedPriorities = false local RecycledPriTable = {} +local DebugWeaponComponent = import("/lua/sim/weapons/components/DebugWeaponComponent.lua").DebugWeaponComponent + local function ParsePriorities() local idlist = EntityCategoryGetUnitList(categories.ALLUNITS) local finalPriorities = {} @@ -47,7 +49,7 @@ end local WeaponMethods = moho.weapon_methods ----@class Weapon : moho.weapon_methods, InternalObject +---@class Weapon : moho.weapon_methods, InternalObject, DebugWeaponComponent ---@field AimControl? moho.AimManipulator ---@field AimLeft? moho.AimManipulator ---@field AimRight? moho.AimManipulator @@ -66,7 +68,7 @@ local WeaponMethods = moho.weapon_methods ---@field unit Unit ---@field MaxRadius? number ---@field MinRadius? number -Weapon = ClassWeapon(WeaponMethods) { +Weapon = ClassWeapon(WeaponMethods, DebugWeaponComponent) { -- stored here for mods compatibility, overridden in the inner table when written to DamageMod = 0, diff --git a/lua/sim/weapons/components/DebugWeaponComponent.lua b/lua/sim/weapons/components/DebugWeaponComponent.lua new file mode 100644 index 0000000000..688a2d901f --- /dev/null +++ b/lua/sim/weapons/components/DebugWeaponComponent.lua @@ -0,0 +1,139 @@ +--****************************************************************************************************** +--** 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. +--****************************************************************************************************** + +local DebugComponent = import("/lua/shared/components/DebugComponent.lua").DebugComponent + +---@class DebugWeaponComponent : DebugComponent +DebugWeaponComponent = Class(DebugComponent) { + + ---@param self DebugWeaponComponent | Weapon + ---@param ... any + DebugSpew = function(self, ...) + if not self.EnabledSpewing then + return + end + + local owner = self.unit + self:DebugDraw('gray') + + if owner and IsUnit(owner) and (not IsDestroyed(owner)) then + -- allows the developer to track down the owner + owner:SetCustomName(string.format("%s - %s", tostring(owner.EntityId), tostring(self.Label))) + self:DebugDraw('gray') + end + + SPEW(owner.UnitId, owner.EntityId, self.Label, unpack(arg)) + end, + + ---@param self DebugWeaponComponent | Weapon + ---@param ... any + DebugLog = function(self, ...) + if not self.EnabledLogging then + return + end + + local owner = self.unit + + if owner and IsUnit(owner) and (not IsDestroyed(owner)) then + -- allows the developer to track down the owner + owner:SetCustomName(string.format("%s - %s", tostring(owner.EntityId), tostring(self.Label))) + self:DebugDraw('white') + end + + _ALERT(owner.UnitId, owner.EntityId, self.Label, unpack(arg)) + end, + + ---@param self DebugWeaponComponent | Weapon + ---@param ... any + DebugWarn = function(self, ...) + if not self.EnabledWarnings then + return + end + + local owner = self.unit + + if owner and IsUnit(owner) and (not IsDestroyed(owner)) then + -- allows the developer to track down the owner + owner:SetCustomName(string.format("%s - %s", tostring(owner.EntityId), tostring(self.Label))) + self:DebugDraw('orange') + end + + WARN(owner.UnitId, owner.EntityId, self.Label, unpack(arg)) + end, + + ---@param self DebugWeaponComponent | Weapon + ---@param message any + DebugError = function(self, message) + if not self.EnabledErrors then + return + end + + local owner = self.unit + + if owner and IsUnit(owner) and (not IsDestroyed(owner)) then + -- allows the developer to track down the owner + owner:SetCustomName(string.format("%s - %s", tostring(owner.EntityId), tostring(self.Label))) + self:DebugDraw('red') + end + + error( + string.format( + "%s\t%s\t%s\t%s", + tostring(owner.UnitId), + tostring(owner.EntityId), + tostring(self.Label), + tostring(message) + ) + ) + end, + + ---@param self DebugWeaponComponent | Weapon + ---@param color? Color # Defaults to white + DebugDraw = function(self, color) + if not self.EnabledDrawing then + return + end + + -- we can't draw dead things + if IsDestroyed(self) then + return + end + + -- do not draw everything, just what the developer may be interested in + if not (GetFocusArmy() == -1 or GetFocusArmy() == self.Army) then + return + end + + color = color or 'ffffff' + + local owner = self.unit + if owner and not IsDestroyed(owner) then + owner:DebugDraw(color) + + local racks = self.Blueprint.RackBones + for k, rack in ipairs(racks) do + DrawLine(owner:GetPosition(), owner:GetPosition(rack.MuzzleBones[1]), color) + DrawCircle(owner:GetPosition(rack.MuzzleBones[1]), 0.25, color) + end + end + end, +}