diff --git a/luarules/gadgets.lua b/luarules/gadgets.lua index cc33341079..fdbf7bb2b2 100644 --- a/luarules/gadgets.lua +++ b/luarules/gadgets.lua @@ -1662,14 +1662,21 @@ function gadgetHandler:TerraformComplete(unitID, unitDefID, unitTeam, end function gadgetHandler:AllowWeaponTargetCheck(attackerID, attackerWeaponNum, attackerWeaponDefID) - for _, g in ipairs(self.AllowWeaponTargetCheckList) do - if not g:AllowWeaponTargetCheck(attackerID, attackerWeaponNum, attackerWeaponDefID) then - return false +local ignore = true +for _, g in ipairs(self.AllowWeaponTargetCheckList) do + local allowCheck, ignoreCheck = g:AllowWeaponTargetCheck(attackerID, attackerWeaponNum, attackerWeaponDefID) + if not ignoreCheck then + ignore = false + if not allowCheck then + return 0 end end - return true end +return ((ignore and -1) or 1) +end + + function gadgetHandler:AllowWeaponTarget(attackerID, targetID, attackerWeaponNum, attackerWeaponDefID, defPriority) local allowed = true local priority = 1.0 diff --git a/luarules/gadgets/unit_weapon_smart_select_helper.lua b/luarules/gadgets/unit_weapon_smart_select_helper.lua new file mode 100644 index 0000000000..109f2d78b1 --- /dev/null +++ b/luarules/gadgets/unit_weapon_smart_select_helper.lua @@ -0,0 +1,111 @@ +function gadget:GetInfo() + return { + name = "Weapon Smart Select Helper", + desc = "Prevents auto-target units from blocking manual command fire orders for lower priority weapons.", + author = "SethDGamre", + date = "2024.11.16", + license = "GNU GPL, v2 or later", + layer = 2, + enabled = true + } +end + +if not gadgetHandler:IsSyncedCode() then return end + +--use customparams.smart_weapon_select_priority to define which weapon number is preferred over the other(s) and enable auto-targetting override. + +--static +local frameCheckModulo = Game.gameSpeed +local cmdAttack = CMD.ATTACK +local innitializeExpirationFrames = Game.gameSpeed * 10 + +--variables +local gameFrame = 0 + +--functions +local ggGetUnitTarget = GG.GetUnitTarget +local spGetUnitCurrentCommand = Spring.GetUnitCurrentCommand +local spCallCOBScript = Spring.CallCOBScript +local spGetUnitWeaponHaveFreeLineOfFire = Spring.GetUnitWeaponHaveFreeLineOfFire +local spGetUnitCommands = Spring.GetUnitCommands + +--tables +local unitSuspendAutoAiming = {} +local unitDefsWithSmartWeapons = {} + +for unitDefID, def in ipairs(UnitDefs) do + if def.customParams.smart_weapon_select_priority then + unitDefsWithSmartWeapons[unitDefID] = {preferredWeapon = tonumber(def.customParams.smart_weapon_select_priority)} + for weaponNumber, weaponData in ipairs(def.weapons) do + if weaponNumber ~= unitDefsWithSmartWeapons[unitDefID].preferredWeapon then + unitDefsWithSmartWeapons[unitDefID].deferredWeapon = tonumber(weaponNumber) + end + end + end +end + +local function weaponTargettingCheck(attackerID, targetData) + if not attackerID or not targetData then return false end + if #targetData == 1 then + local canShoot = spGetUnitWeaponHaveFreeLineOfFire(attackerID, unitSuspendAutoAiming[attackerID].preferredWeapon, targetData[1]) + if not canShoot then + spCallCOBScript(attackerID, unitSuspendAutoAiming[attackerID].overrideScriptID, 0, unitSuspendAutoAiming[attackerID].deferredWeapon) + return false + elseif canShoot and gameFrame > unitSuspendAutoAiming[attackerID].overrideExpirationFrame then + spCallCOBScript(attackerID, unitSuspendAutoAiming[attackerID].overrideScriptID, 0, unitSuspendAutoAiming[attackerID].preferredWeapon) + unitSuspendAutoAiming[attackerID].overrideExpirationFrame = gameFrame + innitializeExpirationFrames + return true + end + elseif #targetData > 1 then + local canShoot = spGetUnitWeaponHaveFreeLineOfFire(attackerID, unitSuspendAutoAiming[attackerID].preferredWeapon, _, _, _, targetData[1], targetData[2], targetData[3]) + if not canShoot then + spCallCOBScript(attackerID, unitSuspendAutoAiming[attackerID].overrideScriptID, 0, unitSuspendAutoAiming[attackerID].deferredWeapon) + return true + elseif canShoot then + spCallCOBScript(attackerID, unitSuspendAutoAiming[attackerID].overrideScriptID, 0, unitSuspendAutoAiming[attackerID].preferredWeapon) + return false + end + end +end + +local function manualCommandIssued(attackerID) + local returnTargetTable = {} + if spGetUnitCurrentCommand(attackerID) == cmdAttack then + local attackTarget = spGetUnitCommands(attackerID, 1) + returnTargetTable = attackTarget[1].params + return returnTargetTable + end + local setTargetData = ggGetUnitTarget(attackerID) or {} + if type(setTargetData) == "number" then + returnTargetTable[1] = setTargetData + return returnTargetTable + else + return setTargetData + end +end + +function gadget:UnitFinished(unitID, unitDefID, unitTeam) + if unitDefsWithSmartWeapons[unitDefID] then + unitSuspendAutoAiming[unitID] = { + unitDefID = unitDefID, + preferredWeapon = unitDefsWithSmartWeapons[unitDefID].preferredWeapon, + deferredWeapon = unitDefsWithSmartWeapons[unitDefID].deferredWeapon, + overrideScriptID = Spring.GetCOBScriptID(unitID, "overrideAimingState"), + overrideExpirationFrame = gameFrame + innitializeExpirationFrames + } + end +end + +function gadget:UnitDestroyed(unitID, unitDefID, unitTeam, attackerID, attackerDefID, attackerTeam) + unitSuspendAutoAiming[unitID] = nil +end + +function gadget:GameFrame(frame) + if frame % frameCheckModulo == 3 then + gameFrame = frame + for attackerID in pairs(unitSuspendAutoAiming) do + local targetData = manualCommandIssued(attackerID) + weaponTargettingCheck(attackerID, targetData) + end + end +end \ No newline at end of file diff --git a/scripts/Units/armguard.bos b/scripts/Units/armguard.bos index d4146a604f..586c3c19c9 100644 --- a/scripts/Units/armguard.bos +++ b/scripts/Units/armguard.bos @@ -5,11 +5,16 @@ piece flare1, flare2, base, turret, barrel1, barrel2, sleeves; -static-var nextBarrel, trajectoryMode, lastHeading; +//smasel remove debugGameFrame +static-var nextBarrel, trajectoryMode, lastHeading, aimingState, switchAimModeFrame, queueLowFrame, firedLowFailed, gameFrame; -#define SIGNAL_AIM 1 -#define LOW_TRAJECTORY 0 -#define HIGH_TRAJECTORY 1 +#define SIGNAL_AIM 1 +#define RESET_LOW_DELAY_FRAMES 15 +#define RESET_HIGH_DELAY_FRAMES 450 +#define RESET_HIGH_ERRORSTATE_FRAMES 900 +#define AIMING_NEITHER 0 +#define AIMING_LOW 1 +#define AIMING_HIGH 2 Create() @@ -31,32 +36,83 @@ Create() dont-shade sleeves; dont-shade turret; nextBarrel = 0; + //start-script debugTimer(); } #define SMOKEPIECE base #include "smokeunit_thread_nohit.h" -RequestState(requestedState) +overrideAimingState(weaponNumber) { - trajectoryMode = requestedstate; + if (weaponNumber == AIMING_LOW){ + switchAimModeFrame = (gameFrame + RESET_LOW_DELAY_FRAMES); + aimingState = AIMING_LOW; + } else if ((weaponNumber == AIMING_HIGH)){ + switchAimModeFrame = (gameFrame + RESET_HIGH_DELAY_FRAMES); + aimingState = AIMING_HIGH; + } } -Activate() +setAimingState(weaponNumber) { - signal SIGNAL_AIM; - start-script RequestState(HIGH_TRAJECTORY); + if (weaponNumber == AIMING_LOW){ + switchAimModeFrame = (gameFrame + RESET_LOW_DELAY_FRAMES); + aimingState = AIMING_LOW; + } else if ((weaponNumber == AIMING_HIGH) && (queueLowFrame < gameFrame)){ + if (firedLowFailed == TRUE){ + //if low aimed but failed to fire, aim high for longer. + switchAimModeFrame = (gameFrame + RESET_HIGH_ERRORSTATE_FRAMES); + } else{ + switchAimModeFrame = (gameFrame + RESET_HIGH_DELAY_FRAMES); + } + aimingState = AIMING_HIGH; + } } -Deactivate() +smartAimSelect(weaponNumber) { - signal SIGNAL_AIM; - start-script RequestState(LOW_TRAJECTORY); + var highReloadState, lowReloadState, greatestReloadState;; + gameFrame = (get GAME_FRAME); + + //define a period where low is given priority to steal. + if (weaponNumber == AIMING_LOW){ + queueLowFrame = (switchAimModeFrame + RESET_LOW_DELAY_FRAMES); + } + + if (switchAimModeFrame < gameFrame){ + highReloadState = (get WEAPON_RELOADSTATE(AIMING_HIGH)); + lowReloadState = (get WEAPON_RELOADSTATE(AIMING_LOW)); + + //prevent bonus shots, prevent unintentional + if (highReloadState > lowReloadState ){ + greatestReloadState = highReloadState; + } else{ + greatestReloadState = lowReloadState; + } + if (greatestReloadState > switchAimModeFrame){ + switchAimModeFrame = greatestReloadState; + } + + //check if the low weapon aimed but didn't fire. + if (((lowReloadState + RESET_LOW_DELAY_FRAMES) < gameFrame) && (queueLowFrame > switchAimModeFrame)){ + firedLowFailed = TRUE; + } else{ + firedLowFailed = FALSE; + } + aimingState = AIMING_NEITHER; + } + + if (aimingState == AIMING_NEITHER){ + call-script setAimingState(weaponNumber); + return (0); + } } restoreAfterDelay() { set-signal-mask SIGNAL_AIM; sleep 3500; + aimingState = AIMING_NEITHER; if(lastHeading > <-180.000000>) { turn turret to y-axis <180.000000> speed <30.000000>; @@ -92,36 +148,32 @@ aimCommon(heading, pitch) AimPrimary(heading, pitch) { - if( trajectoryMode != LOW_TRAJECTORY ) - { + start-script smartAimSelect(AIMING_LOW); + if (aimingState != AIMING_LOW){ return(0); } signal SIGNAL_AIM; set-signal-mask SIGNAL_AIM; - call-script aimCommon(heading, pitch); - // Prevent low-trajectory "bonus shots" while high-trajectory is reloading - var reloading; - reloading = (get GAME_FRAME) < (get WEAPON_RELOADSTATE(2)); - if( reloading ) - { - return(0); - } + call-script aimCommon(heading, pitch); return (1); } AimSecondary(heading, pitch) { - if( trajectoryMode != HIGH_TRAJECTORY ) - { - return(0); + start-script smartAimSelect(AIMING_HIGH); + if (aimingState != AIMING_HIGH){ + return (0); } - signal SIGNAL_AIM; set-signal-mask SIGNAL_AIM; + + + call-script aimCommon(heading, pitch); + return (1); } @@ -240,4 +292,4 @@ Killed(severity, corpsetype) explode barrel1 type EXPLODE_ON_HIT | FIRE | SMOKE | FALL | NOHEATCLOUD; explode flare1 type EXPLODE_ON_HIT | FIRE | SMOKE | FALL | NOHEATCLOUD; return corpsetype; -} +} \ No newline at end of file diff --git a/scripts/Units/armguard.cob b/scripts/Units/armguard.cob index a7f4bc7dd8..1a8cf47514 100644 Binary files a/scripts/Units/armguard.cob and b/scripts/Units/armguard.cob differ diff --git a/units/ArmBuildings/LandDefenceOffence/armguard.lua b/units/ArmBuildings/LandDefenceOffence/armguard.lua index 55b3cab137..de5a1fe71c 100644 --- a/units/ArmBuildings/LandDefenceOffence/armguard.lua +++ b/units/ArmBuildings/LandDefenceOffence/armguard.lua @@ -23,7 +23,6 @@ return { metalcost = 1250, nochasecategory = "MOBILE", objectname = "Units/ARMGUARD.s3o", - onoffable = true, script = "Units/ARMGUARD.cob", seismicsignature = 0, selfdestructas = "mediumBuildingExplosionGenericSelfd", @@ -36,7 +35,6 @@ return { buildinggrounddecaltype = "decals/armguard_aoplane.dds", model_author = "Beherith", normaltex = "unittextures/Arm_normal.dds", - onoffname = "trajectory", subfolder = "ArmBuildings/LandDefenceOffence", unitgroup = "weapon", usebuildinggrounddecal = true, @@ -135,30 +133,29 @@ return { }, plasma_high = { accuracy = 75, - areaofeffect = 192, + areaofeffect = 100, avoidfeature = false, - cegtag = "arty-large", - craterareaofeffect = 192, + cegtag = "arty-medium", craterboost = 0, cratermult = 0, - edgeeffectiveness = 0.65, - explosiongenerator = "custom:genericshellexplosion-medium-bomb", + edgeeffectiveness = 0.4, + explosiongenerator = "custom:genericshellexplosion-medium", gravityaffected = "true", hightrajectory = 1, impulsefactor = 1.4, - name = "High-trajectory g2g long-range AoE plasma cannon", + mygravity = 0.289, + name = "Heavy g2g long range plasma cannon", noselfdamage = true, - proximitypriority = -1, range = 1220, - reloadtime = 7, + reloadtime = 2.85, soundhit = "xplomed2", - soundhitwet = "splslrg", + soundhitwet = "splsmed", soundstart = "cannhvy5", turret = true, weapontype = "Cannon", - weaponvelocity = 440, + weaponvelocity = 600, damage = { - default = 600, + default = 300, subs = 150, vtol = 90, }, diff --git a/units/CorBuildings/LandDefenceOffence/corpun.lua b/units/CorBuildings/LandDefenceOffence/corpun.lua index 3b8ce0f933..c23f1224ed 100644 --- a/units/CorBuildings/LandDefenceOffence/corpun.lua +++ b/units/CorBuildings/LandDefenceOffence/corpun.lua @@ -23,7 +23,6 @@ return { metalcost = 1300, nochasecategory = "MOBILE", objectname = "Units/CORPUN.s3o", - onoffable = true, script = "Units/armguard.cob", seismicsignature = 0, selfdestructas = "largeBuildingExplosionGenericSelfd", @@ -36,10 +35,10 @@ return { buildinggrounddecaltype = "decals/corpun_aoplane.dds", model_author = "Mr Bob", normaltex = "unittextures/cor_normal.dds", - onoffname = "trajectory", subfolder = "CorBuildings/LandDefenceOffence", unitgroup = "weapon", usebuildinggrounddecal = true, + smart_weapon_select_priority = 1, }, featuredefs = { dead = { @@ -136,31 +135,31 @@ return { }, plasma_high = { accuracy = 75, - areaofeffect = 208, + areaofeffect = 120, avoidfeature = false, cegtag = "arty-medium", - craterareaofeffect = 208, craterboost = 0, cratermult = 0, - edgeeffectiveness = 0.65, + edgeeffectiveness = 0.4, explosiongenerator = "custom:genericshellexplosion-medium-bomb", gravityaffected = "true", hightrajectory = 1, impulsefactor = 1.4, - name = "Long-range high-trajectory g2g AoE plasma cannon", + mygravity = 0.289, + name = "Long-range g2g plasma cannon", noselfdamage = true, - proximitypriority = -1, range = 1245, - reloadtime = 7.5, + reloadtime = 3.16667, soundhit = "xplomed2", - soundhitwet = "splslrg", + soundhitwet = "splsmed", soundstart = "cannhvy5", turret = true, weapontype = "Cannon", - weaponvelocity = 440, + weaponvelocity = 600, damage = { - default = 650, - subs = 95, + default = 350, + lboats = 350, + subs = 90, vtol = 95, }, },