Skip to content

Commit

Permalink
Generalize projectile behavior shield specific damage (#6107)
Browse files Browse the repository at this point in the history
  • Loading branch information
lL1l1 committed Jun 18, 2024
1 parent be3329b commit a7ccd80
Show file tree
Hide file tree
Showing 19 changed files with 111 additions and 122 deletions.
7 changes: 7 additions & 0 deletions changelog/snippets/balance.5869.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- (#5869, #6178, #6107) Introduce the Pulsar, a Cybran version of the Absolver, to help Cybran counteract the shields of other factions.
Pulsar: T3 Mobile EMP Missile Launcher
- Cost (same as Absolver): 420 mass, 4800 energy, 3600 buildtime (40 seconds from a T3 land factory)
- Damage: 325x4 damage against shields every 2 seconds (same DPS as Absolver), with a 1.5 second stun against T1 and T2 units
- Range: 60 compared to Absolver's 65
- The missiles are a bit slower than the Absolver's projectile, but have mild tracking
- It can walk on land and swim on water at a high speed of 3.5.
1 change: 1 addition & 0 deletions changelog/snippets/balance.6107.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- (#6107) Absolver can no longer damage shields while they are disabled and preventing them from recharging.
1 change: 0 additions & 1 deletion changelog/snippets/other.6178.md

This file was deleted.

2 changes: 1 addition & 1 deletion engine/Core/Blueprints/WeaponBlueprint.lua
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
---@field DamageFriendly boolean
--- blast radius
---@field DamageRadius number
--- used by the Absolver script to pass how much damage is done to shields, instead of `Damage`
--- how much additional damage is dealt to shields using the "FAF_AntiShield" damagetype
---@field DamageToShields? number
--- the type of damage the unit will do
---@field DamageType DamageType
Expand Down
4 changes: 4 additions & 0 deletions lua/armordefinition.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@
---| "TacticalMissile"
---| "TreeFire"
---| "TreeForce"
---| "Disintegrate" # Used by tree props
---| "Force" # Used by tree props
---| "Fire" # Used by tree props
---| "WallOverspill"
---| "TransportDamage" # Skips visual effects in OnKilled
---| "FAF_AntiShield" # Only deals damage to shields

---@alias ArmorType
---| "ASF"
Expand Down
2 changes: 1 addition & 1 deletion lua/proptree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Tree = Class(Prop) {
---@param direction number
---@param type DamageType
OnDamage = function(self, instigator, amount, direction, type)
if self:BeenDestroyed() then
if self:BeenDestroyed() or type == 'FAF_AntiShield' then
return
end

Expand Down
2 changes: 1 addition & 1 deletion lua/shield.lua
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ Shield = ClassShield(moho.shield_methods, Entity) {
amount = amount * (self.Owner:GetArmorMult(type))
amount = amount * (1.0 - ArmyGetHandicap(self.Army))
local finalVal = amount - EntityGetHealth(self)
if finalVal < 0 then
if finalVal < 0 or type == "FAF_AntiShield" then
finalVal = 0
end
return finalVal
Expand Down
129 changes: 76 additions & 53 deletions lua/sim/Projectile.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
------------------------------------------------------------------

local DefaultDamage = import("/lua/sim/defaultdamage.lua")
local UnitDoTThread = DefaultDamage.UnitDoTThread
local AreaDoTThread = DefaultDamage.AreaDoTThread
local Flare = import("/lua/defaultantiprojectile.lua").Flare
local DepthCharge = import("/lua/defaultantiprojectile.lua").DepthCharge

Expand Down Expand Up @@ -627,109 +629,129 @@ Projectile = ClassProjectile(ProjectileMethods) {
---@param self Projectile
---@param instigator Unit
---@param DamageData table
---@param targetEntity Unit | Prop
---@param targetEntity Unit | Prop | nil
---@param cachedPosition Vector
DoDamage = function(self, instigator, DamageData, targetEntity, cachedPosition)

-- this may be a cached vector, we can not send this to threads or use after waiting statements!
cachedPosition = cachedPosition or self:GetPosition()

local damage = DamageData.DamageAmount
if damage and damage > 0 then
if damage > 0 then

-- check for radius
-- deal damage in a radius
local radius = DamageData.DamageRadius
if radius and radius > 0 then
if radius > 0 then
local damageType = DamageData.DamageType
local damageFriendly = DamageData.DamageFriendly
local damageSelf = DamageData.DamageSelf or false

-- check for damage-over-time
if not DamageData.DoTTime or DamageData.DoTTime <= 0 then
local DoTTime = DamageData.DoTTime
if DoTTime <= 0 then
-- no damage over time, do radius-based damage
DamageArea(
instigator,
cachedPosition,
radius,
damage,
DamageData.DamageType,
DamageData.DamageFriendly,
DamageData.DamageSelf or false
damageType,
damageFriendly,
damageSelf
)

local damageToShields = DamageData.DamageToShields
if damageToShields then
DamageArea(
instigator,
cachedPosition,
radius,
damageToShields,
"FAF_AntiShield",
damageFriendly,
damageSelf
)
end
else
-- check for initial damage
local initialDmg = DamageData.InitialDamageAmount or 0
local initialDmg = DamageData.InitialDamageAmount
if initialDmg > 0 then
if radius > 0 then
DamageArea(
instigator,
cachedPosition,
radius,
initialDmg,
DamageData.DamageType,
DamageData.DamageFriendly,
DamageData.DamageSelf or false
)
elseif targetEntity then
Damage(
instigator,
cachedPosition,
targetEntity,
initialDmg,
DamageData.DamageType
)
end
DamageArea(
instigator,
cachedPosition,
radius,
initialDmg,
damageType,
damageFriendly,
damageSelf
)
end

-- apply damage over time
local DoTPulses = DamageData.DoTPulses or 1
ForkThread(
DefaultDamage.AreaDoTThread,
AreaDoTThread,
instigator,
self:GetPosition(), -- can't use cachedPosition here: breaks invariant
DamageData.DoTPulses or 1,
(DamageData.DoTTime / (DamageData.DoTPulses or 1)),
DoTPulses,
(DoTTime / (DoTPulses)),
radius,
damage,
DamageData.DamageType,
DamageData.DamageFriendly
damageType,
damageFriendly
)
end

-- check for entity-specific damage
elseif DamageData.DamageAmount and targetEntity then
-- damage a single entity
elseif targetEntity then
local damageType = DamageData.DamageType

local damageToShields = DamageData.DamageToShields
if damageToShields then
Damage(
instigator,
cachedPosition,
targetEntity,
damageToShields,
"FAF_AntiShield"
)
end

-- check for damage-over-time
if not DamageData.DoTTime or DamageData.DoTTime <= 0 then
local DoTTime = DamageData.DoTTime
if DoTTime <= 0 then

-- no damage over time, do single target damage
Damage(
instigator,
cachedPosition,
targetEntity,
DamageData.DamageAmount,
DamageData.DamageType
damage,
damageType
)
else
-- check for initial damage
local initialDmg = DamageData.InitialDamageAmount or 0
if initialDmg > 0 then
if targetEntity then
Damage(
instigator,
cachedPosition,
targetEntity,
initialDmg,
DamageData.DamageType
)
end
Damage(
instigator,
cachedPosition,
targetEntity,
initialDmg,
damageType
)
end

-- apply damage over time
local DoTPulses = DamageData.DoTPulses or 1
ForkThread(
DefaultDamage.UnitDoTThread,
UnitDoTThread,
instigator,
targetEntity,
DamageData.DoTPulses or 1,
(DamageData.DoTTime / (DamageData.DoTPulses or 1)),
DoTPulses,
(DoTTime / (DoTPulses)),
damage,
DamageData.DamageType,
damageType,
DamageData.DamageFriendly
)
end
Expand All @@ -738,20 +760,21 @@ Projectile = ClassProjectile(ProjectileMethods) {

-- related to strategic missiles
if self.InnerRing and self.OuterRing then
local damageType = DamageData.DamageType or 'Nuke'
self.InnerRing:DoNukeDamage(
self.Launcher,
self:GetPosition(), -- can't use cachedPosition here: breaks invariant
self.Brain,
self.Army,
DamageData.DamageType or 'Nuke'
damageType
)

self.OuterRing:DoNukeDamage(
self.Launcher,
self:GetPosition(), -- can't use cachedPosition here: breaks invariant
self.Brain,
self.Army,
DamageData.DamageType or 'Nuke'
damageType
)
end
end,
Expand Down
4 changes: 2 additions & 2 deletions lua/sim/Prop.lua
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ Prop = Class(moho.prop_methods) {
---@param direction Vector
---@param damageType DamageType
OnDamage = function(self, instigator, amount, direction, damageType)
-- only applies to trees
if damageType == "TreeForce" or damageType == "TreeFire" then
-- only applies to trees and units
if damageType == "TreeForce" or damageType == "TreeFire" or damageType == "FAF_AntiShield" then
return
end

Expand Down
8 changes: 5 additions & 3 deletions lua/sim/Unit.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1307,12 +1307,14 @@ Unit = ClassUnit(moho.unit_methods, IntelComponent, VeterancyComponent) {
end

if self.CanTakeDamage then
self:DoOnDamagedCallbacks(instigator)

-- Pass damage to an active personal shield, as personal shields no longer have collisions
if self:GetShieldType() == 'Personal' and self:ShieldIsOn() and not self.MyShield.Charging then
local myShield = self.MyShield
if myShield.ShieldType == "Personal" and myShield:IsUp() then
self:DoOnDamagedCallbacks(instigator)
self.MyShield:ApplyDamage(instigator, amount, vector, damageType)
else
elseif damageType ~= "FAF_AntiShield" then
self:DoOnDamagedCallbacks(instigator)
self:DoTakeDamage(instigator, amount, vector, damageType)
end
end
Expand Down
2 changes: 2 additions & 0 deletions lua/sim/weapon.lua
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,8 @@ Weapon = ClassWeapon(WeaponMethods) {
GetDamageTableInternal = function(self)
local weaponBlueprint = self.Blueprint
local damageTable = {}

damageTable.DamageToShields = weaponBlueprint.DamageToShields
damageTable.InitialDamageAmount = weaponBlueprint.InitialDamage or 0
damageTable.DamageRadius = weaponBlueprint.DamageRadius + self.DamageRadiusMod
damageTable.DamageAmount = weaponBlueprint.Damage + self.DamageMod
Expand Down
2 changes: 1 addition & 1 deletion lua/ui/game/unitviewDetail.lua
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ function WrapAndPlaceText(bp, builder, descID, control)
--DPS Calculations
local Damage = info.Damage
if info.DamageToShields then
Damage = math.max(Damage, info.DamageToShields)
Damage = Damage + info.DamageToShields
end
if info.BeamLifetime > 0 then
Damage = Damage * (1 + MathFloor(MATH_IRound(info.BeamLifetime*10)/(MATH_IRound(info.BeamCollisionDelay*10)+1)))
Expand Down
5 changes: 5 additions & 0 deletions lua/wreckage.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ Wreckage = Class(Prop) {
---@param vector Vector
---@param damageType DamageType
OnDamage = function(self, instigator, amount, vector, damageType)
-- only applies to trees and units
if damageType == "TreeForce" or damageType == "TreeFire" or damageType == "FAF_AntiShield" then
return
end

self:DoTakeDamage(instigator, amount, vector, damageType)
end,

Expand Down
29 changes: 1 addition & 28 deletions projectiles/ADFShieldDisruptor01/ADFShieldDisruptor01_script.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,5 @@ local ADisruptorProjectileOnImpact = ADisruptorProjectile.OnImpact

--- Aeon Shield Disruptor Projectile, DAL0310
---@class ADFShieldDisruptor01 : AShieldDisruptorProjectile
ADFShieldDisruptor01 = ClassProjectile(ADisruptorProjectile) {

---@param self ADFShieldDisruptor01
---@param targetType string
---@param targetEntity Unit
OnImpact = function(self, targetType, targetEntity)
ADisruptorProjectileOnImpact(self, targetType, targetEntity)

-- try to find the shield that we hit
if targetType ~= 'Shield' then
targetEntity = targetEntity.MyShield
end

if not targetEntity then
return
end

-- we directly damage the shield to prevent generating overspill damage
local damage = targetEntity:GetHealth()
if damage > 1300 then -- TODO: find a better way to pass this damage
damage = 1300
elseif damage < 1 then
damage = 1
end

Damage(self, self:GetPosition(), targetEntity, damage, 'Normal')
end,
}
ADFShieldDisruptor01 = ClassProjectile(ADisruptorProjectile) {}
TypeClass = ADFShieldDisruptor01
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,6 @@ CDFRocketIridium03 = Class(CIridiumRocketProjectile) {
local army = self:GetArmy()
CreateLightParticle( self, -1, army, 2, 1, 'glow_03', 'ramp_red_06' )
CreateLightParticle( self, -1, army, 1, 3, 'glow_03', 'ramp_antimatter_02' )
if targetType == 'Shield' then
Damage(
self,
{0,0,0},
targetEntity,
self.Data,
'Normal'
)
end
end,
}

Expand Down
13 changes: 1 addition & 12 deletions units/DAL0310/DAL0310_Script.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,7 @@ local ADFDisruptorCannonWeapon = import("/lua/aeonweapons.lua").ADFDisruptorWeap
---@class DAL0310 : AHoverLandUnit
DAL0310 = ClassUnit(AHoverLandUnit) {
Weapons = {
MainGun = ClassWeapon(ADFDisruptorCannonWeapon) {
CreateProjectileAtMuzzle = function(self, muzzle)
local bp = self.Blueprint

local proj = ADFDisruptorCannonWeapon.CreateProjectileAtMuzzle(self, muzzle)
local data = bp.DamageToShields
if proj and not proj:BeenDestroyed() then
proj:PassData(data)
end
return proj
end,
}
MainGun = ClassWeapon(ADFDisruptorCannonWeapon) {}
},
}
TypeClass = DAL0310
Expand Down
Loading

0 comments on commit a7ccd80

Please sign in to comment.