mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
355 lines
9.3 KiB
Lua
355 lines
9.3 KiB
Lua
--[[
|
|
| This file was obtained through the combined efforts
|
|
| of Madbluntz & Plymouth Antiquarian Society.
|
|
|
|
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
|
| Maloy, DrPepper10 @ RIP, Atle!
|
|
|
|
|
| Visit for more: https://plymouth.thetwilightzone.ru/
|
|
--]]
|
|
|
|
language.Add("pac_projectile", "Projectile")
|
|
|
|
local BUILDER, PART = pac.PartTemplate("base_movable")
|
|
|
|
PART.ClassName = "projectile"
|
|
PART.Group = 'advanced'
|
|
PART.Icon = 'icon16/bomb.png'
|
|
|
|
BUILDER:StartStorableVars()
|
|
BUILDER:GetSet("Speed", 1)
|
|
BUILDER:GetSet("AddOwnerSpeed", false)
|
|
BUILDER:GetSet("Damping", 0)
|
|
BUILDER:GetSet("Gravity", true)
|
|
BUILDER:GetSet("Collisions", true)
|
|
BUILDER:GetSet("Sphere", false)
|
|
BUILDER:GetSet("Radius", 1)
|
|
BUILDER:GetSet("DamageRadius", 50)
|
|
BUILDER:GetSet("LifeTime", 5)
|
|
BUILDER:GetSet("AimDir", false)
|
|
BUILDER:GetSet("Sticky", false)
|
|
BUILDER:GetSet("Bounce", 0)
|
|
BUILDER:GetSet("BulletImpact", false)
|
|
BUILDER:GetSet("Damage", 0)
|
|
BUILDER:GetSet("DamageType", "generic", {enums = {
|
|
generic = 0, --generic damage
|
|
crush = 1, --caused by physics interaction
|
|
bullet = 2, --bullet damage
|
|
slash = 4, --sharp objects, such as manhacks or other npcs attacks
|
|
burn = 8, --damage from fire
|
|
vehicle = 16, --hit by a vehicle
|
|
fall = 32, --fall damage
|
|
blast = 64, --explosion damage
|
|
club = 128, --crowbar damage
|
|
shock = 256, --electrical damage, shows smoke at the damage position
|
|
sonic = 512, --sonic damage,used by the gargantua and houndeye npcs
|
|
energybeam = 1024, --laser
|
|
nevergib = 4096, --don't create gibs
|
|
alwaysgib = 8192, --always create gibs
|
|
drown = 16384, --drown damage
|
|
paralyze = 32768, --same as dmg_poison
|
|
nervegas = 65536, --neurotoxin damage
|
|
poison = 131072, --poison damage
|
|
acid = 1048576, --
|
|
airboat = 33554432, --airboat gun damage
|
|
blast_surface = 134217728, --this won't hurt the player underwater
|
|
buckshot = 536870912, --the pellets fired from a shotgun
|
|
direct = 268435456, --
|
|
dissolve = 67108864, --forces the entity to dissolve on death
|
|
drownrecover = 524288, --damage applied to the player to restore health after drowning
|
|
physgun = 8388608, --damage done by the gravity gun
|
|
plasma = 16777216, --
|
|
prevent_physics_force = 2048, --
|
|
radiation = 262144, --radiation
|
|
removenoragdoll = 4194304, --don't create a ragdoll on death
|
|
slowburn = 2097152, --
|
|
|
|
explosion = -1, -- util.BlastDamage
|
|
fire = -1, -- ent:Ignite(5)
|
|
|
|
-- env_entity_dissolver
|
|
dissolve_energy = 0,
|
|
dissolve_heavy_electrical = 1,
|
|
dissolve_light_electrical = 2,
|
|
dissolve_core_effect = 3,
|
|
|
|
heal = -1,
|
|
armor = -1,
|
|
}
|
|
})
|
|
BUILDER:GetSet("Spread", 0)
|
|
BUILDER:GetSet("Delay", 0)
|
|
BUILDER:GetSet("Maximum", 0)
|
|
BUILDER:GetSet("Mass", 100)
|
|
BUILDER:GetSet("Attract", 0)
|
|
BUILDER:GetSet("AttractMode", "projectile_nearest", {enums = {
|
|
hitpos = "hitpos",
|
|
hitpos_radius = "hitpos_radius",
|
|
closest_to_projectile = "closest_to_projectile",
|
|
closest_to_hitpos = "closest_to_hitpos",
|
|
}})
|
|
BUILDER:GetSet("AttractRadius", 200)
|
|
BUILDER:GetSetPart("OutfitPart")
|
|
BUILDER:GetSet("Physical", false)
|
|
BUILDER:GetSet("CollideWithOwner", false)
|
|
BUILDER:GetSet("CollideWithSelf", false)
|
|
BUILDER:GetSet("RemoveOnCollide", false)
|
|
BUILDER:EndStorableVars()
|
|
|
|
PART.Translucent = false
|
|
|
|
function PART:OnShow(from_rendering)
|
|
if not from_rendering then
|
|
-- TODO:
|
|
-- this makes sure all the parents of this movable have an up-to-date draw position
|
|
-- GetBonePosition implicitly uses ent:GetPos() as the parent origin which is really bad,
|
|
-- it should instead be using what pac considers to be the position
|
|
--self:GetRootPart():CallRecursive("Draw", "opaque")
|
|
local parents = self:GetParentList()
|
|
-- call draw from root to the current part only on direct parents to update the position hiearchy
|
|
for i = #parents, 1, -1 do
|
|
local part = parents[i]
|
|
if part.Draw then
|
|
part:Draw("opaque")
|
|
end
|
|
end
|
|
self:Shoot(self:GetDrawPosition())
|
|
end
|
|
end
|
|
|
|
function PART:AttachToEntity(ent)
|
|
if not self.OutfitPart:IsValid() then return false end
|
|
|
|
ent.pac_draw_distance = 0
|
|
|
|
local tbl = self.OutfitPart:ToTable()
|
|
|
|
local group = pac.CreatePart("group", self:GetPlayerOwner())
|
|
group:SetShowInEditor(false)
|
|
|
|
local part = pac.CreatePart(tbl.self.ClassName, self:GetPlayerOwner(), tbl, tostring(tbl))
|
|
group:AddChild(part)
|
|
|
|
group:SetOwner(ent)
|
|
group.SetOwner = function(s) s.Owner = ent end
|
|
part:SetHide(false)
|
|
|
|
local id = group.Id
|
|
local owner_id = self:GetPlayerOwnerId()
|
|
if owner_id then
|
|
id = id .. owner_id
|
|
end
|
|
|
|
ent:CallOnRemove("pac_projectile_" .. id, function() group:Remove() end)
|
|
group:CallRecursive("Think")
|
|
|
|
ent.RenderOverride = ent.RenderOverride or function()
|
|
if self.AimDir then
|
|
ent:SetRenderAngles(ent:GetVelocity():Angle())
|
|
end
|
|
end
|
|
|
|
ent.pac_projectile_part = group
|
|
ent.pac_projectile = self
|
|
|
|
return true
|
|
end
|
|
|
|
local enable = CreateClientConVar("pac_sv_projectiles", 0, true)
|
|
|
|
function PART:Shoot(pos, ang)
|
|
local physics = self.Physical
|
|
|
|
if physics then
|
|
if pac.LocalPlayer ~= self:GetPlayerOwner() then return end
|
|
|
|
local tbl = {}
|
|
for key in pairs(self:GetStorableVars()) do
|
|
tbl[key] = self[key]
|
|
end
|
|
|
|
net.Start("pac_projectile")
|
|
net.WriteVector(pos)
|
|
net.WriteAngle(ang)
|
|
net.WriteTable(tbl)
|
|
net.SendToServer()
|
|
else
|
|
self.projectiles = self.projectiles or {}
|
|
|
|
local count = 0
|
|
|
|
for key, ent in pairs(self.projectiles) do
|
|
if not ent:IsValid() then
|
|
self.projectiles[key] = nil
|
|
else
|
|
count = count + 1
|
|
end
|
|
end
|
|
|
|
local max = math.min(self.Maximum, 100)
|
|
|
|
if max == 0 then
|
|
max = 100
|
|
end
|
|
|
|
if count > max then
|
|
return
|
|
end
|
|
|
|
local function spawn()
|
|
|
|
if not self:IsValid() then return end
|
|
|
|
local ent = pac.CreateEntity("models/props_junk/popcan01a.mdl")
|
|
if not ent:IsValid() then return end
|
|
|
|
local idx = table.insert(self.projectiles, ent)
|
|
|
|
ent:AddCallback("PhysicsCollide", function(ent, data)
|
|
local phys = ent:GetPhysicsObject()
|
|
if self.Bounce > 0 then
|
|
timer.Simple(0, function()
|
|
if phys:IsValid() then
|
|
phys:SetVelocity(data.OurOldVelocity - 2 * (data.HitNormal:Dot(data.OurOldVelocity) * data.HitNormal) * self.Bounce)
|
|
end
|
|
end)
|
|
elseif self.Sticky then
|
|
phys:SetVelocity(Vector(0,0,0))
|
|
phys:EnableMotion(false)
|
|
ent.pac_stuck = data.OurOldVelocity
|
|
end
|
|
|
|
if self.BulletImpact then
|
|
ent:FireBullets{
|
|
Attacker = ent:GetOwner(),
|
|
Damage = 0,
|
|
Force = 0,
|
|
Num = 1,
|
|
Src = data.HitPos - data.HitNormal,
|
|
Dir = data.HitNormal,
|
|
Distance = 10,
|
|
}
|
|
end
|
|
|
|
if self.RemoveOnCollide then
|
|
timer.Simple(0.01, function() SafeRemoveEntity(ent) end)
|
|
end
|
|
end)
|
|
|
|
ent:SetOwner(self:GetPlayerOwner(true))
|
|
ent:SetPos(pos)
|
|
ent:SetAngles(ang)
|
|
ent:SetCollisionGroup(COLLISION_GROUP_PROJECTILE)
|
|
|
|
if self.Sphere then
|
|
ent:PhysicsInitSphere(math.Clamp(self.Radius, 1, 30))
|
|
else
|
|
ent:PhysicsInitBox(Vector(1,1,1) * - math.Clamp(self.Radius, 1, 30), Vector(1,1,1) * math.Clamp(self.Radius, 1, 30))
|
|
end
|
|
|
|
ent.RenderOverride = function()
|
|
if not self:IsValid() then
|
|
return
|
|
end
|
|
|
|
if not self:GetRootPart():GetOwner():IsValid() then
|
|
timer.Simple(0, function() SafeRemoveEntity(ent) end)
|
|
end
|
|
|
|
if self.AimDir then
|
|
if ent.pac_stuck then
|
|
ent:SetRenderAngles(ent.pac_stuck:Angle())
|
|
else
|
|
local angle = ent:GetVelocity():Angle()
|
|
ent:SetRenderAngles(angle)
|
|
ent.last_angle = angle
|
|
end
|
|
end
|
|
end
|
|
|
|
local phys = ent:GetPhysicsObject()
|
|
phys:EnableGravity(self.Gravity)
|
|
phys:AddVelocity((ang:Forward() + (VectorRand():Angle():Forward() * self.Spread)) * self.Speed * 1000)
|
|
if self.AddOwnerSpeed and ent:GetOwner():IsValid() then
|
|
phys:AddVelocity(ent:GetOwner():GetVelocity())
|
|
end
|
|
phys:EnableCollisions(self.Collisions)
|
|
phys:SetDamping(self.Damping, 0)
|
|
|
|
ent:SetCollisionGroup(COLLISION_GROUP_PROJECTILE)
|
|
|
|
if self:AttachToEntity(ent) then
|
|
timer.Simple(math.Clamp(self.LifeTime, 0, 10), function()
|
|
if ent:IsValid() then
|
|
if ent.pac_projectile_part and ent.pac_projectile_part:IsValid() then
|
|
ent.pac_projectile_part:Remove()
|
|
end
|
|
|
|
timer.Simple(0.5, function()
|
|
SafeRemoveEntity(ent)
|
|
end)
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
if self.Delay == 0 then
|
|
spawn()
|
|
else
|
|
timer.Simple(self.Delay, spawn)
|
|
end
|
|
end
|
|
end
|
|
|
|
function PART:OnRemove()
|
|
if not self.Physical and self.projectiles then
|
|
for key, ent in pairs(self.projectiles) do
|
|
SafeRemoveEntity(ent)
|
|
end
|
|
|
|
self.projectiles = {}
|
|
end
|
|
end
|
|
--[[
|
|
function PART:OnHide()
|
|
if self.RemoveOnHide then
|
|
self:OnRemove()
|
|
end
|
|
end
|
|
]]
|
|
do -- physical
|
|
local Entity = Entity
|
|
local projectiles = {}
|
|
pac.AddHook("Think", "pac_projectile", function()
|
|
for key, data in pairs(projectiles) do
|
|
if not data.ply:IsValid() then
|
|
projectiles[key] = nil
|
|
goto CONTINUE
|
|
end
|
|
|
|
local ent = Entity(data.ent_id)
|
|
|
|
if ent:IsValid() and ent:GetClass() == "pac_projectile" then
|
|
local part = pac.GetPartFromUniqueID(pac.Hash(data.ply), data.partuid)
|
|
if part:IsValid() and part:GetPlayerOwner() == data.ply then
|
|
part:AttachToEntity(ent)
|
|
end
|
|
projectiles[key] = nil
|
|
end
|
|
::CONTINUE::
|
|
end
|
|
end)
|
|
|
|
net.Receive("pac_projectile_attach", function()
|
|
local ply = net.ReadEntity()
|
|
local ent_id = net.ReadInt(16)
|
|
local partuid = net.ReadString()
|
|
|
|
if ply:IsValid() then
|
|
table.insert(projectiles, {ply = ply, ent_id = ent_id, partuid = partuid})
|
|
end
|
|
end)
|
|
end
|
|
|
|
BUILDER:Register()
|