241 lines
5.8 KiB
Lua
241 lines
5.8 KiB
Lua
|
|
-- Copyright (C) 2017-2020 DBotThePony
|
|
|
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
-- of this software and associated documentation files (the "Software"), to deal
|
|
-- in the Software without restriction, including without limitation the rights
|
|
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
-- of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
-- subject to the following conditions:
|
|
|
|
-- The above copyright notice and this permission notice shall be included in all copies
|
|
-- or substantial portions of the Software.
|
|
|
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
-- DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
local IsValid = FindMetaTable('Entity').IsValid
|
|
local type = type
|
|
local NULL = NULL
|
|
local table = table
|
|
DLib.combat = DLib.combat or {}
|
|
local combat = DLib.combat
|
|
|
|
--[[
|
|
@doc
|
|
@fname DLib.combat.findWeapon
|
|
@args CTakeDamageInfo dmginfo
|
|
|
|
@returns
|
|
Weapon
|
|
Entity: attacker
|
|
Entity: inflictor
|
|
]]
|
|
function combat.findWeapon(dmginfo)
|
|
local attacker, inflictor = dmginfo:GetAttacker(), dmginfo:GetInflictor()
|
|
if not IsValid(attacker) or not IsValid(inflictor) then return NULL, attacker, inflictor end
|
|
if type(attacker) ~= 'Player' or not (type(inflictor) == 'Weapon' or attacker == inflictor) then return end
|
|
local weapon = type(inflictor) == 'Weapon' and inflictor or attacker:GetActiveWeapon()
|
|
return weapon, attacker, inflictor
|
|
end
|
|
|
|
local function interval(val, min, max)
|
|
return val > min and val <= max
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname DLib.combat.inPVS
|
|
@args Entity pointFrom, Entity pointTo, Angle eyes = pointFrom:EyeAnglesFixed(), number yawLimit = 60, number pitchLimit = 60
|
|
|
|
@desc
|
|
Despite function's name, this only checks whenever pointFrom can see on screen pointTo
|
|
using eye angles check assuming they have direct line of sight to each other
|
|
@enddesc
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function combat.inPVS(point1, point2, eyes, yawLimit, pitchLimit)
|
|
if type(point1) ~= 'Vector' then
|
|
if point1.EyeAnglesFixed then
|
|
eyes = eyes or point1:EyeAnglesFixed()
|
|
elseif point1.EyeAngles then
|
|
eyes = eyes or point1:EyeAngles()
|
|
end
|
|
|
|
point1 = point1:EyePos()
|
|
end
|
|
|
|
if type(point2) ~= 'Vector' then
|
|
point2 = point2:EyePos()
|
|
end
|
|
|
|
yawLimit = yawLimit or 60
|
|
pitchLimit = pitchLimit or 60
|
|
|
|
local ang = (point2 - point1):Angle()
|
|
local diffPith = ang.p:AngleDifference(eyes.p)
|
|
local diffYaw = ang.y:AngleDifference(eyes.y)
|
|
|
|
return interval(diffYaw, -yawLimit, yawLimit) and interval(diffPith, -pitchLimit, pitchLimit)
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname DLib.combat.turnAngle
|
|
@args Entity pointFrom, Entity pointTo, Angle eyes = pointFrom:EyeAnglesFixed()
|
|
|
|
@returns
|
|
number: pitch delta
|
|
number: yaw delta
|
|
]]
|
|
function combat.turnAngle(point1, point2, eyes)
|
|
if type(point1) ~= 'Vector' then
|
|
if point1.EyeAnglesFixed then
|
|
eyes = eyes or point1:EyeAnglesFixed()
|
|
elseif point1.EyeAngles then
|
|
eyes = eyes or point1:EyeAngles()
|
|
end
|
|
|
|
point1 = point1:EyePos()
|
|
end
|
|
|
|
if type(point2) ~= 'Vector' then
|
|
point2 = point2:EyePos()
|
|
end
|
|
|
|
local ang = (point2 - point1):Angle()
|
|
return ang.p:AngleDifference(eyes.p), ang.y:AngleDifference(eyes.y)
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname DLib.combat.findWeaponAlt
|
|
@args CTakeDamageInfo dmginfo
|
|
|
|
@returns
|
|
Weapon
|
|
Entity: attacker
|
|
Entity: inflictor
|
|
]]
|
|
function combat.findWeaponAlt(dmginfo)
|
|
local attacker, inflictor = dmginfo:GetAttacker(), dmginfo:GetInflictor()
|
|
local weapon = inflictor
|
|
|
|
if not IsValid(inflictor) and IsValid(attacker) then
|
|
inflictor = attacker
|
|
weapon = attacker
|
|
end
|
|
|
|
if not IsValid(attacker) or not IsValid(inflictor) then
|
|
return inflictor, attacker, inflictor
|
|
end
|
|
|
|
if type(inflictor) ~= 'Weapon' and attacker.GetActiveWeapon then
|
|
inflictor = attacker:GetActiveWeapon()
|
|
weapon = inflictor
|
|
end
|
|
|
|
return weapon, attacker, inflictor
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname DLib.combat.detect
|
|
@args CTakeDamageInfo dmginfo
|
|
|
|
@returns
|
|
Entity: attacker
|
|
Entity: weapon or inflictor
|
|
Entity: inflictor
|
|
]]
|
|
function combat.detect(dmginfo)
|
|
local weapon, attacker, inflictor = combat.findWeapon(dmginfo)
|
|
|
|
if not IsValid(weapon) then
|
|
weapon = inflictor
|
|
end
|
|
|
|
return attacker, weapon, inflictor
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname DLib.combat.findPlayers
|
|
@args Entity self
|
|
|
|
@desc
|
|
Attempts to find all players involved within certain entity. This can be a vehicle from another mod for example
|
|
(SCars or Simfphys or even Neurotec)
|
|
**This also include players who are spectating this entity**
|
|
@enddesc
|
|
|
|
@returns
|
|
table: of players found or false, if self is NULL
|
|
]]
|
|
function combat.findPlayers(self)
|
|
if not IsValid(self) then
|
|
return false
|
|
end
|
|
|
|
local specs = {}
|
|
|
|
for i, ply in ipairs(player.GetAll()) do
|
|
if ply ~= self and ply:GetObserverTarget() == self then
|
|
table.insert(specs, ply)
|
|
end
|
|
end
|
|
|
|
if type(self) == 'Player' then
|
|
table.insert(specs, self)
|
|
return specs
|
|
end
|
|
|
|
if type(self) == 'Vehicle' then
|
|
local driver = self:GetDriver()
|
|
|
|
if not IsValid(driver) then
|
|
return #specs ~= 0 and specs
|
|
end
|
|
|
|
table.insert(specs, driver)
|
|
return specs
|
|
end
|
|
|
|
local MEM = {}
|
|
local iterate = {self}
|
|
|
|
while #iterate > 0 do
|
|
local ent = table.remove(iterate)
|
|
|
|
if MEM[ent] then
|
|
goto CONTINUE
|
|
end
|
|
|
|
MEM[ent] = true
|
|
|
|
for i, ent2 in ipairs(self:GetChildren()) do
|
|
if type(ent2) == 'Player' then
|
|
table.insert(specs, ent2)
|
|
elseif type(ent2) == 'Vehicle' then
|
|
local driver = ent2:GetDriver()
|
|
|
|
if IsValid(driver) then
|
|
table.insert(specs, driver)
|
|
end
|
|
else
|
|
table.insert(iterate, ent2)
|
|
end
|
|
end
|
|
|
|
::CONTINUE::
|
|
end
|
|
|
|
return #specs ~= 0 and table.deduplicate(specs) or false
|
|
end
|