Skip to content

Commit

Permalink
Add shooter cap to tactical missiles (FAForever#3893)
Browse files Browse the repository at this point in the history
Prevents overkilling of tactical missiles, causing a lot of tactical missile defenses to waste their shots. In turn, tactical missiles are a lot more proficient at taking down missiles. Buffs the hitpoints of cruiser / aircraft carrier missiles by 1 to compensate
  • Loading branch information
Garanas authored Jul 12, 2022
1 parent 3be7ff1 commit 3cede31
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 24 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
"ScenarioInfo"
],
}

67 changes: 53 additions & 14 deletions lua/sim/Projectile.lua
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,18 @@ Projectile = Class(moho.projectile_methods) {
local alliedCheck = not (self.CollideFriendly and IsAlly(self.Army, other.Army))

-- torpedoes can only be taken down by anti torpedo
if self.Blueprint.CategoriesHash['TORPEDO'] then
if other.Blueprint.CategoriesHash["ANTITORPEDO"] then
return alliedCheck
else
return false
if self.Blueprint.CategoriesHash['TORPEDO'] then
if other.Blueprint.CategoriesHash["ANTITORPEDO"] then
return alliedCheck
else
return false
end
end

-- missiles can only be taken down by anti missiles
if self.Blueprint.CategoriesHash["TACTICAL"] or self.Blueprint.CategoriesHash["STRATEGIC"] then
if other.Blueprint.CategoriesHash["ANTIMISSILE"] then
return alliedCheck
else
return false
if self.Blueprint.CategoriesHash["TACTICAL"] or self.Blueprint.CategoriesHash["STRATEGIC"] then
if other.Blueprint.CategoriesHash["ANTIMISSILE"] then
return other.OriginalTarget == self
end
end

Expand Down Expand Up @@ -214,6 +212,17 @@ Projectile = Class(moho.projectile_methods) {

--- Called by the engine when the projectile is killed, in other words: intercepted
OnKilled = function(self, instigator, type, overkillRatio)

-- callbacks for launcher to have an idea what is going on for AIs
if not IsDestroyed(self.Launcher) then
self.Launcher:OnMissileIntercepted(self:GetCurrentTargetPosition(), instigator, self:GetPosition())

-- keep track of the number of intercepted missiles
if not IsDestroyed(instigator) then
instigator:SetStat('KILLS', instigator:GetStat('KILLS', 0).Value + 1)
end
end

self:CreateImpactEffects(self.Army, self.FxOnKilled, self.FxOnKilledScale)
self:Destroy()
end,
Expand All @@ -222,16 +231,46 @@ Projectile = Class(moho.projectile_methods) {
-- @param targetType
-- @param targetEntity
OnImpact = function(self, targetType, targetEntity)

-- in case the OnImpact crashes it guarantees that it gets destroyed at some point, useful for mods
self.Impacts = (self.Impacts or 0) + 1
if self.Impacts > 3 then
WARN("Faulty projectile destroyed manually: " .. tostring(self.Blueprint.BlueprintId))
self:Destroy()
return
end

-- localize information for performance
local position = self:GetPosition()
local damageData = self.DamageData
local radius = damageData.DamageRadius or 0
local bp = self.Blueprint

-- callbacks for launcher to have an idea what is going on for AIs
local categoriesHash = self.Blueprint.CategoriesHash
if categoriesHash['TACTICAL'] or categoriesHash['STRATEGICAL'] then
-- we have a target, but got caught by terrain
if targetType == 'Terrain' then
if not IsDestroyed(self.Launcher) then
self.Launcher:OnMissileImpactTerrain(self:GetCurrentTargetPosition(), position)
end

-- we have a target, but got caught by an (unexpected) shield
elseif targetType == 'Shield' then
if not IsDestroyed(self.Launcher) then
self.Launcher:OnMissileImpactShield(self:GetCurrentTargetPosition(), targetEntity.Owner, position)
end
end
end

-- Try to use the launcher as instigator first. If its been deleted, use ourselves (this
-- projectile is still associated with an army)
local instigator = self.Launcher or self
local instigator = self.Launcher or self

-- localize information for performance
local vc = VectorCached
vc[1], vc[2], vc[3] = EntityGetPositionXYZ(self)
local damageData = self.DamageData
local radius = damageData.DamageRadius or 0
local bp = self.Blueprint


-- do the projectile damage
self:DoDamage(instigator, damageData, targetEntity, vc)
Expand Down
79 changes: 78 additions & 1 deletion lua/sim/Unit.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4274,10 +4274,87 @@ Unit = Class(moho.unit_methods) {
--- support factories.
---@param self Unit A reference to the unit itself, automatically set when you use the ':' notation
---@param unitBeingBuilt Unit A flag to determine whether our consumption should be active
GetUpgradeAnimation = function(self, unitBeingBuilt)
GetUpgradeAnimation = function(self, unitBeingBuilt)
return self.Blueprint.Display.AnimationUpgrade
end,

--- Called when a missile launched by this unit is intercepted
---@param self Unit
---@param target Vector
---@param defense Unit Requires an `IsDestroyed` check as the defense may have been destroyed when the missile is intercepted
---@param position Vector Location where the missile got intercepted
OnMissileIntercepted = function(self, target, defense, position)
-- try and run callbacks
if self.Callbacks['OnMissileIntercepted'] then
for k, callback in self.Callbacks['OnMissileIntercepted'] do
local ok, msg = pcall(callback, target, defense, position)
if not ok then
WARN("OnMissileIntercepted callback triggered an error:")
WARN(msg)
end
end
end
end,

--- Called when a missile launched by this unit hits a shield
---@param self Unit
---@param target Vector
---@param shield Unit Requires an `IsDestroyed` check when using as the shield may have been destroyed when the missile impacts
---@param position Vector Location where the missile hit the shield
OnMissileImpactShield = function(self, target, shield, position)
-- try and run callbacks
if self.Callbacks['OnMissileImpactShield'] then
for k, callback in self.Callbacks['OnMissileImpactShield'] do
local ok, msg = pcall(callback, target, shield, position)
if not ok then
WARN("OnMissileImpactShield callback triggered an error:")
WARN(msg)
end
end
end
end,

--- Called when a missile launched by this unit hits the terrain, note that this can be the same location as the target
---@param self Unit
---@param target Vector
---@param position Vector Location where the missile hit the terrain
OnMissileImpactTerrain = function(self, target, position)
-- try and run callbacks
if self.Callbacks['OnMissileImpactTerrain'] then
for k, callback in self.Callbacks['OnMissileImpactTerrain'] do
local ok, msg = pcall(callback, target, position)
if not ok then
WARN("OnMissileImpactTerrain callback triggered an error:")
WARN(msg)
end
end
end
end,

--- Add a callback when a missile launched by this unit is intercepted
---@param self Unit
---@param callback function<Vector, Unit, Vector>
AddMissileInterceptedCallback = function(self, callback)
self.Callbacks['OnMissileIntercepted'] = self.Callbacks['OnMissileIntercepted'] or { }
table.insert(self.Callbacks['OnMissileIntercepted'], callback)
end,

--- Add a callback when a missile launched by this unit hits a shield
---@param self Unit
---@param callback function<Vector, Unit, Vector>
AddMissileImpactShieldCallback = function(self, callback)
self.Callbacks['OnMissileImpactShield'] = self.Callbacks['OnMissileImpactShield'] or { }
table.insert(self.Callbacks['OnMissileImpactShield'], callback)
end,

--- Add a callback when a missile launched by this unit hits the terrain, note that this can be the same location as the target
---@param self Unit
---@param callback function<Vector, Vector>
AddMissileImpactTerrainCallback = function(self, callback)
self.Callbacks['OnMissileImpactTerrain'] = self.Callbacks['OnMissileImpactTerrain'] or { }
table.insert(self.Callbacks['OnMissileImpactTerrain'], callback)
end,

--- Various callback-like functions

-- Called when the C function unit.SetConsumptionActive is called
Expand Down
8 changes: 7 additions & 1 deletion lua/sim/weapon.lua
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,14 @@ Weapon = Class(moho.weapon_methods) {

CreateProjectileForWeapon = function(self, bone)
local proj = self:CreateProjectile(bone)
local damageTable = self:GetDamageTable()

-- store the original target, can be nil if ground firing
proj.OriginalTarget = self:GetCurrentTarget()
if proj.OriginalTarget.GetSource then
proj.OriginalTarget = proj.OriginalTarget:GetSource()
end

local damageTable = self:GetDamageTable()
if proj and not proj:BeenDestroyed() then
proj:PassMetaDamage(damageTable)
local bp = self.Blueprint
Expand Down
16 changes: 14 additions & 2 deletions lua/system/blueprints-projectiles.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
--- Post-processes the provided projectile blueprint
local function PostProcessProjectile(projectile)

-- -- create hash tables for quick lookup
-- # create hash tables for quick lookup

projectile.CategoriesCount = 0
projectile.CategoriesHash = { }
Expand All @@ -32,7 +32,7 @@ local function PostProcessProjectile(projectile)
end
end

-- -- create hash tables for quick lookup
-- # create hash tables for quick lookup

projectile.DoNotCollideListCount = 0
projectile.DoNotCollideListHash = { }
Expand All @@ -42,6 +42,18 @@ local function PostProcessProjectile(projectile)
projectile.DoNotCollideListHash[category] = true
end
end

-- # fix desired shooter cap for missiles

if projectile.CategoriesHash['MISSILE'] and (not projectile.CategoriesHash['STRATEGIC']) then
if not projectile.DesiredShooterCap then
projectile.DesiredShooterCap = projectile.Defense.Health or 1
else
if projectile.DesiredShooterCap != (projectile.Defense.Health or 1) then
WARN(string.format("Inconsistent shooter cap defined for projectile %s, it should match its health", projectile.BlueprintId))
end
end
end
end

--- Post-processes all the provided projectile blueprints
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ ProjectileBlueprint {
'NOSPLASHDAMAGE',
},
Defense = {
Health = 1,
MaxHealth = 1,
Health = 2,
MaxHealth = 2,
},
Display = {
CameraFollowTimeout = 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ ProjectileBlueprint {
'NOSPLASHDAMAGE',
},
Defense = {
Health = 1,
MaxHealth = 1,
Health = 2,
MaxHealth = 2,
},
Display = {
CameraFollowTimeout = 2,
Expand Down
4 changes: 2 additions & 2 deletions projectiles/TIFMissileCruise04/TIFMissileCruise04_proj.bp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ ProjectileBlueprint {
'NOSPLASHDAMAGE',
},
Defense = {
Health = 2,
MaxHealth = 2,
Health = 3,
MaxHealth = 3,
},
Display = {
CameraFollowTimeout = 2,
Expand Down

0 comments on commit 3cede31

Please sign in to comment.