diff --git a/lua/pac3/core/client/parts/damage_zone.lua b/lua/pac3/core/client/parts/damage_zone.lua index 2b2e51829..e391d7351 100644 --- a/lua/pac3/core/client/parts/damage_zone.lua +++ b/lua/pac3/core/client/parts/damage_zone.lua @@ -21,6 +21,8 @@ local renderhooks = { "PreDrawViewModel" } +local recycle_hitmark = CreateConVar("pac_damage_zone_recycle_hitmarkers", "0", FCVAR_ARCHIVE, "Whether to use the experimental recycling system to save performance on spawning already created hit markers.\nIf this is 0, it will be more reliable but more costly because it creates new parts every time.") + BUILDER:StartStorableVars() :SetPropertyGroup("Targets") @@ -121,9 +123,10 @@ BUILDER:StartStorableVars() :GetSet("CriticalHealth",1, {editor_onchange = function(self,num) return math.floor(math.Clamp(num,0,65535)) end}) :GetSet("MaxHpScaling", 0, {editor_clamp = {0,1}}) :SetPropertyGroup("DamageOverTime") - :GetSet("DOTMode", false, {description = "Repeats your damage a few times. Subject to serverside convar."}) - :GetSet("DOTTime", 0, {editor_clamp = {0,32}}) - :GetSet("DOTCount", 0, {editor_onchange = function(self,num) return math.floor(math.Clamp(num,0,127)) end}) + :GetSet("DOTMode", false, {editor_friendly = "DoT mode", + description = "Repeats your damage a few times. Subject to serverside convar."}) + :GetSet("DOTTime", 0, {editor_friendly = "DoT time", editor_clamp = {0,32}, description = "delay between each repeated damage"}) + :GetSet("DOTCount", 0, {editor_friendly = "DoT count", editor_onchange = function(self,num) return math.floor(math.Clamp(num,0,127)) end, description = "number of repeated damage instances"}) :GetSet("NoInitialDOT", false, {description = "Skips the first instance (the instant one) of damage to achieve a delayed damage for example."}) :SetPropertyGroup("HitOutcome") :GetSetPart("HitSoundPart") @@ -649,7 +652,7 @@ function PART:SendNetMessage() net.WriteUInt(self.DOTCount, 7) net.WriteUInt(math.ceil(math.Clamp(64*self.DOTTime, 0, 2047)), 11) net.WriteString(string.sub(self.UniqueID,0,6)) - local using_hit_feedback = self.HitMarkerPart ~= nil or self.KillMarkerPart ~= nil + local using_hit_feedback = IsValid(self.HitMarkerPart) or IsValid(self.KillMarkerPart) net.WriteBool(using_hit_feedback) net.SendToServer() end @@ -699,6 +702,40 @@ function PART:SetAttachPartsToTargetEntity(b) end end +--revertable to projectile part's version which wastes time creating new parts but has less issues +local_hitmarks = {} +function PART:LegacyAttachToEntity(part, ent) + if not part:IsValid() then return false end + + ent.pac_draw_distance = 0 + + local tbl = part:ToTable() + + local group = pac.CreatePart("group", self:GetPlayerOwner()) + group:SetShowInEditor(false) + + local part_clone = pac.CreatePart(tbl.self.ClassName, self:GetPlayerOwner(), tbl, tostring(tbl)) + group:AddChild(part_clone) + + group:SetOwner(ent) + group.SetOwner = function(s) s.Owner = ent end + part_clone:SetHide(false) + + local id = group.Id + local owner_id = self:GetPlayerOwnerId() + if owner_id then + id = id .. owner_id + end + + ent:CallOnRemove("pac_hitmarker_" .. id, function() group:Remove() end) + group:CallRecursive("Think") + + ent.pac_hitmark_part = group + ent.pac_hitmark = self --that's just the launcher though + + return true +end + net.Receive("pac_hit_results", function(len) local uid = net.ReadString() or "" local self = part_partialUID_caches[uid] @@ -746,6 +783,30 @@ net.Receive("pac_hit_results", function(len) if part == self then return end --stop infinite feedback loops of using the damagezone as a hitmarker --what if people employ a more roundabout method? CRACKDOWN! + + if not recycle_hitmark:GetBool() then + local ent = parent_ent + if not self.AttachPartsToTargetEntity then + ent = pac.CreateEntity("models/props_junk/popcan01a.mdl") + ent:SetNoDraw(true) + ent:SetPos(pos) + end + self:LegacyAttachToEntity(killing and self.KillMarkerPart or self.HitMarkerPart, ent) + + timer.Simple(math.Clamp(killing and self.KillMarkerLifetime or self.HitMarkerLifetime, 0, 30), function() + if IsValid(ent) then + if ent.pac_hitmark_part and ent.pac_hitmark_part:IsValid() then + ent.pac_hitmark_part:Remove() + end + + timer.Simple(0.5, function() + SafeRemoveEntity(ent) + end) + end + end) + return + end + if not owner.hitparts then owner.hitparts = {} end if owner.stop_hit_markers_until then diff --git a/lua/pac3/extra/shared/net_combat.lua b/lua/pac3/extra/shared/net_combat.lua index 0ec38b10c..88690ee07 100644 --- a/lua/pac3/extra/shared/net_combat.lua +++ b/lua/pac3/extra/shared/net_combat.lua @@ -79,6 +79,7 @@ if SERVER then ["npc_satchel"] = true, ["func_breakable_surf"] = true, ["func_breakable"] = true, + ["func_physbox"] = true, ["physics_cannister"] = true } @@ -1068,7 +1069,7 @@ if SERVER then end --this may benefit from some flattening treatment, lotta pyramids over here - if tbl.DamageType == "heal" then + if tbl.DamageType == "heal" and ent.Health then if ent:Health() < ent:GetMaxHealth() then if tbl.ReverseDoNotKill then --don't heal if health is below critical if ent:Health() > tbl.CriticalHealth then --default behavior @@ -1084,7 +1085,7 @@ if SERVER then end end end - elseif tbl.DamageType == "armor" then + elseif tbl.DamageType == "armor" and ent.Armor then if ent:Armor() < ent:GetMaxArmor() then if tbl.ReverseDoNotKill then --don't heal if armor is below critical if ent:Armor() > tbl.CriticalHealth then --default behavior @@ -2166,6 +2167,7 @@ if SERVER then local prop_protected, reason = IsPropProtected(targ_ent, ply) local owner = Try_CPPIGetOwner(targ_ent) + if not IsValid(owner) then return end local unconsenting_owner = owner ~= ply and (grab_consents[owner] == false or (targ_ent:IsPlayer() and grab_consents[targ_ent] == false)) @@ -2467,7 +2469,7 @@ if SERVER then local fraction = math.Clamp(1 - (1-bulletinfo.DamageFalloffFraction)*(distance / bulletinfo.DamageFalloffDistance),bulletinfo.DamageFalloffFraction,1) local ent = trc.Entity - if bulletinfo.dmgtype_str == "heal" then + if bulletinfo.dmgtype_str == "heal" and ent.Health then dmg:SetDamageType(0) if ent:Health() < ent:GetMaxHealth() then @@ -2476,7 +2478,7 @@ if SERVER then dmg:SetDamage(0) return - elseif bulletinfo.dmgtype_str == "armor" then + elseif bulletinfo.dmgtype_str == "armor" and ent.Armor then dmg:SetDamageType(0) if ent:Armor() < ent:GetMaxArmor() then