Skip to content

Commit

Permalink
Merge pull request #345 from LiddulBOFH/player-damage
Browse files Browse the repository at this point in the history
Player Damage Overhaul
  • Loading branch information
Stooberton authored Jul 4, 2023
2 parents 392203d + 40038d9 commit 35d00be
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 59 deletions.
129 changes: 129 additions & 0 deletions lua/acf/core/utilities/util_sv.lua
Original file line number Diff line number Diff line change
Expand Up @@ -468,4 +468,133 @@ do -- Extra overlay text

return Result
end
end

do -- Special squishy functions
local BoneList = {
head = {boneName = "ValveBiped.Bip01_Head1",group = "head",min = Vector(-6,-6,-4),max = Vector(8,4,4)},

spine = {boneName = "ValveBiped.Bip01_Spine",group = "chest",min = Vector(-6,-4,-9),max = Vector(18,10,9)},

lthigh = {boneName = "ValveBiped.Bip01_L_Thigh",group = "limb",min = Vector(0,-4,-4),max = Vector(18,4,4)},
lcalf = {boneName = "ValveBiped.Bip01_L_Calf",group = "limb",min = Vector(0,-4,-4),max = Vector(18,4,4)},

rthigh = {boneName = "ValveBiped.Bip01_R_Thigh",group = "limb",min = Vector(0,-3,-3),max = Vector(18,3,3)},
rcalf = {boneName = "ValveBiped.Bip01_R_Calf",group = "limb",min = Vector(0,-3,-3),max = Vector(18,3,3)},
}

local ArmorHitboxes = { -- only applied if the entity has armor greater than 0
helmet = {boneName = "ValveBiped.Bip01_Head1",group = "helmet",min = Vector(4.5,-6.5,-4.5),max = Vector(8.5,4.5,4.5)},
vest = {boneName = "ValveBiped.Bip01_Spine",group = "vest",min = Vector(-5,-5,-8),max = Vector(17,11,8)},
}

-- The goal of this is to provide a much sturdier way to get the part of a player that got hit with a bullet
-- This will ignore any bone manipulation too
function ACF.GetBestSquishyHitBox(Entity,RayStart,RayDir)
local Bones = {}
local CheckList = {}

for k,v in pairs(BoneList) do
CheckList[k] = v
end

if Entity:IsPlayer() and Entity:Armor() > 0 then
for k,v in pairs(ArmorHitboxes) do
CheckList[k] = v
end
end

--if true then return "none" end

for k,v in pairs(CheckList) do
local bone = Entity:LookupBone(v.boneName)
if bone then Bones[k] = bone end
end

if table.IsEmpty(Bones) then return "none" end

local HitBones = {}

for k,v in pairs(Bones) do
local BoneData = CheckList[k]
local BonePos,BoneAng = Entity:GetBonePosition(v)

local HitPos = util.IntersectRayWithOBB(RayStart, RayDir * 64, BonePos, BoneAng, BoneData.min, BoneData.max)
if HitPos ~= nil then
HitBones[k] = HitPos
end
end

if table.IsEmpty(HitBones) then return "none" end -- No boxes got hit, so return the default
if table.Count(HitBones) == 1 then return CheckList[next(HitBones)].group end -- Single box got hit, just return that

local BestChoice = next(HitBones)
local BestDist = HitBones[BestChoice]:DistToSqr(RayStart)

for k,_ in pairs(HitBones) do
if BestChoice == k then continue end
local BoxPosDist = HitBones[k]:DistToSqr(RayStart)
if BoxPosDist < BestDist then BestChoice = k BestDist = BoxPosDist end
end

return CheckList[BestChoice].group
end

ACF.SquishyFuncs = {}

function ACF.SquishyFuncs.DamageHelmet(Entity,HitRes,DmgResult)
local Damage = 0

DmgResult:SetThickness(12.5) -- helmet armor, sorta just shot in the dark for thickness
HitRes = DmgResult:Compute()

if HitRes.Overkill > 0 then -- Went through helmet
return ACF.SquishyFuncs.DamageHead(Entity,HitRes,DmgResult)
else return Damage,HitRes end
end

function ACF.SquishyFuncs.DamageHead(Entity,HitRes,DmgResult)
local Damage = 0
local Mass = Entity:GetPhysicsObject():GetMass() or 100

DmgResult:SetThickness(Mass * 0.075) -- skull is around 7-8mm on average for humans, but this gets thicker with bigger creatures

HitRes = DmgResult:Compute()
Damage = Damage + HitRes.Damage * 10

if HitRes.Overkill > 0 then -- Went through skull
DmgResult:SetThickness(0.01) -- squishy squishy brain matter, no resistance
HitRes = DmgResult:Compute()
Damage = Damage + (HitRes.Damage * 50 * math.max(1,HitRes.Overkill / 4)) -- yuge damage, yo brains just got scrambled by a BOOLET
return Damage,HitRes
else return Damage,HitRes end
end

function ACF.SquishyFuncs.DamageVest(Entity,HitRes,DmgResult)
local Damage = 0

DmgResult:SetThickness(15) -- Vest armor, also a shot in the dark for thickness
HitRes = DmgResult:Compute()

if HitRes.Overkill > 0 then -- Went through vest
return ACF.SquishyFuncs.DamageChest(Entity,HitRes,DmgResult)
else return Damage,HitRes end
end

function ACF.SquishyFuncs.DamageChest(Entity,HitRes,DmgResult)
local Damage = 0
local Size = Entity:BoundingRadius()

DmgResult:SetThickness(Size * 0.25 * 0.02) -- the SKIN and SKELETON, just some generic trashy "armor"

HitRes = DmgResult:Compute()
Damage = Damage + HitRes.Damage * 10

if HitRes.Overkill > 0 then -- Went through body surface
DmgResult:SetThickness(0.05) -- fleshy organs, ain't much here
HitRes = DmgResult:Compute()
Damage = Damage + (HitRes.Damage * 25 * math.max(1,HitRes.Overkill / 5)) -- some decent damage, vital organs got hurt for sure
return Damage,HitRes
else return Damage,HitRes end
end
end
76 changes: 17 additions & 59 deletions lua/acf/damage/damage_sv.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,77 +47,35 @@ end
-- @param DmgInfo A DamageInfo object.
-- @return The output of the DamageResult object.
function Damage.doSquishyDamage(Entity, DmgResult, DmgInfo)
local Bone = DmgInfo:GetHitGroup()
local Hitbox = ACF.GetBestSquishyHitBox(Entity,DmgInfo:GetHitPos(),(DmgInfo:GetHitPos() - DmgInfo:GetOrigin()):GetNormalized())
local Size = Entity:BoundingRadius()
local Mass = Entity:GetPhysicsObject():GetMass()
local HitRes = DmgResult:GetBlank()
local Damage = 0

DmgResult:SetFactor(1) -- We don't care about the penetration factor on squishy targets

if Bone then
--This means we hit the head
if Bone == 1 then
--Set the skull thickness as a percentage of Squishy weight, this gives us 2mm for a player, about 22mm for an Antlion Guard. Seems about right
DmgResult:SetThickness(Mass * 0.02)

HitRes = DmgResult:Compute()
Damage = HitRes.Damage * 20

--If we manage to penetrate the skull, then MASSIVE DAMAGE
if HitRes.Overkill > 0 then
--A quarter the bounding radius seems about right for most critters head size
DmgResult:SetThickness(Size * 0.25 * 0.01)

HitRes = DmgResult:Compute()
Damage = Damage + HitRes.Damage * 100
end

-- Then to check if we can get out of the other side, 2x skull + 1x brains
DmgResult:SetThickness(Mass * 0.065)

HitRes = DmgResult:Compute()
Damage = Damage + HitRes.Damage * 20
elseif Bone == 0 or Bone == 2 or Bone == 3 then
-- This means we hit the torso. We are assuming body armour/tough exoskeleton/zombie don't give fuck here, so it's tough
-- Set the armour thickness as a percentage of Squishy weight, this gives us 8mm for a player, about 90mm for an Antlion Guard. Seems about right
DmgResult:SetThickness(Mass * 0.08)

HitRes = DmgResult:Compute()
Damage = HitRes.Damage * 5

if HitRes.Overkill > 0 then
-- Half the bounding radius seems about right for most critters torso size
DmgResult:SetThickness(Size * 0.5 * 0.02)

HitRes = DmgResult:Compute()
Damage = Damage + HitRes.Damage * 50 -- If we penetrate the armour then we get into the important bits inside, so DAMAGE
end

--Then to check if we can get out of the other side, 2x armour + 1x guts
DmgResult:SetThickness(Mass * 0.185)

HitRes = DmgResult:Compute()
elseif Bone == 10 then
-- This means we hit a backpack or something
-- Arbitrary size, most of the gear carried is pretty small
DmgResult:SetThickness(Size * 0.1 * 0.02)
if Hitbox == "none" then -- Default damage
DmgResult:SetThickness(Size * 0.2 * 0.02)

HitRes = DmgResult:Compute()
Damage = HitRes.Damage * 2 -- Damage is going to be fright and shrapnel, nothing much
HitRes = DmgResult:Compute()
Damage = HitRes.Damage * 20
else
-- Using player armor for fake armor works decently, as even if you don't take actual damage, the armor takes 1 point of damage, so it can potentially wear off
-- These funcs are also done on a hierarchy sort of system, so if the helmet is penetrated, then DamageHead is called, same for Vest -> Chest
if Hitbox == "helmet" then
Damage,HitRes = ACF.SquishyFuncs.DamageHelmet(Entity,HitRes,DmgResult)
elseif Hitbox == "head" then
Damage,HitRes = ACF.SquishyFuncs.DamageHead(Entity,HitRes,DmgResult)
elseif Hitbox == "vest" then
Damage,HitRes = ACF.SquishyFuncs.DamageVest(Entity,HitRes,DmgResult)
elseif Hitbox == "chest" then
Damage,HitRes = ACF.SquishyFuncs.DamageChest(Entity,HitRes,DmgResult)
else
--Just in case we hit something not standard
DmgResult:SetThickness(Size * 0.2 * 0.02)

HitRes = DmgResult:Compute()
Damage = HitRes.Damage * 30
Damage = HitRes.Damage * 10
end
else
-- Just in case we hit something not standard
DmgResult:SetThickness(Size * 0.2 * 0.02)

HitRes = DmgResult:Compute()
Damage = HitRes.Damage * 10
end

Entity:TakeDamage(Damage, DmgInfo:GetAttacker(), DmgInfo:GetInflictor())
Expand Down

0 comments on commit 35d00be

Please sign in to comment.