327 lines
7.9 KiB
Lua
327 lines
7.9 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.
|
|
|
|
|
|
AddCSLuaFile()
|
|
|
|
local PICKUP_RANGE = CreateConVar('dlib_spawner_range', '128', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Entity spawner spawn trigger range in Hu')
|
|
local RESET_TIMER = CreateConVar('dlib_spawner_timer', '10', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Entity spawner reset timer in seconds')
|
|
|
|
ENT.Type = 'anim'
|
|
ENT.Author = 'DBot'
|
|
ENT.Base = 'base_entity'
|
|
ENT.PrintName = 'Enitity Spawner Base'
|
|
ENT.RenderGroup = RENDERGROUP_BOTH
|
|
ENT.DefaultModel = 'models/items/item_item_crate.mdl'
|
|
ENT.SPAWN_HOOK_CALL = 'PlayerSpawnSENT'
|
|
ENT.SPAWNED_HOOK_CALL = 'PlayerSpawnedSENT'
|
|
ENT.IS_SPAWNER = true
|
|
|
|
ENT.PICKUP_RANGE = PICKUP_RANGE
|
|
ENT.RESET_TIMER = RESET_TIMER
|
|
|
|
function ENT:SetupDataTables()
|
|
self:NetworkVar('Float', 0, 'NextSpawn')
|
|
self:SetNextSpawn(0)
|
|
end
|
|
|
|
function ENT:SpawnFunction(ply, tr, class)
|
|
if not tr.Hit then return end
|
|
|
|
local can = hook.Run(self.SPAWN_HOOK_CALL, ply, self.CLASS)
|
|
if can == false then return end
|
|
|
|
if not self.TABLE.eSpawnerInfo then
|
|
local newEnt = ents.Create(self.CLASS)
|
|
newEnt:SetPos(tr.HitPos)
|
|
newEnt:Spawn()
|
|
newEnt:Activate()
|
|
|
|
local mdl = newEnt:GetModel()
|
|
local skin = newEnt:GetSkin()
|
|
local color = newEnt:GetColor()
|
|
local material = newEnt:GetMaterial()
|
|
local bg = newEnt:GetBodyGroups()
|
|
|
|
self.TABLE.eSpawnerInfo = {}
|
|
self.TABLE.eSpawnerInfo.mdl = util.IsValidModel(mdl) and mdl or self.DefaultModel
|
|
self.TABLE.eSpawnerInfo.skin = skin
|
|
self.TABLE.eSpawnerInfo.color = color
|
|
self.TABLE.eSpawnerInfo.material = material
|
|
self.TABLE.eSpawnerInfo.bg = bg
|
|
self.TABLE.eSpawnerInfo.bginfo = {}
|
|
|
|
if bg then
|
|
for k, v in pairs(bg) do
|
|
self.TABLE.eSpawnerInfo.bginfo[v.id] = newEnt:GetBodygroup(v.id)
|
|
end
|
|
end
|
|
|
|
newEnt:Remove()
|
|
end
|
|
|
|
local ent = ents.Create(class)
|
|
ent:SetPos(tr.HitPos + tr.HitNormal)
|
|
|
|
local mdl, skin, color, material = self.TABLE.eSpawnerInfo.mdl, self.TABLE.eSpawnerInfo.skin, self.TABLE.eSpawnerInfo.color, self.TABLE.eSpawnerInfo.material
|
|
|
|
if mdl then
|
|
ent.Model = mdl
|
|
ent:SetModel(mdl)
|
|
else
|
|
ent:SetModel(self.DefaultModel)
|
|
end
|
|
|
|
if skin then
|
|
ent:SetSkin(skin)
|
|
end
|
|
|
|
for k, v in pairs(self.TABLE.eSpawnerInfo.bginfo) do
|
|
ent:SetBodygroup(k, v)
|
|
end
|
|
|
|
ent:Spawn()
|
|
ent:Activate()
|
|
|
|
return ent
|
|
end
|
|
|
|
local MINS, MAXS = Vector(-10, -10, 0), Vector(10, 10, 10)
|
|
|
|
function ENT:GetIsSpawned()
|
|
return self:GetNextSpawn() < CurTimeL()
|
|
end
|
|
|
|
function ENT:Initialize()
|
|
timer.Simple(0.3, function()
|
|
if not self:IsValid() then return end
|
|
self:PhysicsInit(SOLID_NONE)
|
|
self:SetSolid(SOLID_NONE)
|
|
self:SetMoveType(MOVETYPE_NONE)
|
|
end)
|
|
|
|
self:DrawShadow(false)
|
|
|
|
self.CurrentClip = 0
|
|
|
|
if CLIENT then
|
|
self:ClientsideEntity()
|
|
self.CurrAngle = Angle(0, 0, 0)
|
|
end
|
|
end
|
|
|
|
function ENT:ClientsideEntity()
|
|
if IsValid(self.CModel) then self.CModel:Remove() end
|
|
|
|
local ent = octolib.createDummy(self:GetModel())
|
|
self.CModel = ent
|
|
ent:SetPos(self:GetPos())
|
|
ent:Spawn()
|
|
ent:Activate()
|
|
ent:SetNoDraw(true)
|
|
|
|
if IsValid(self.CModel2) then self.CModel2:Remove() end
|
|
|
|
local ent1 = ent
|
|
local ent = octolib.createDummy(self:GetModel())
|
|
local ent2 = ent
|
|
self.CModel2 = ent
|
|
ent:SetPos(self:GetPos())
|
|
ent:Spawn()
|
|
ent:Activate()
|
|
ent:SetNoDraw(true)
|
|
|
|
local bg = self:GetBodyGroups()
|
|
|
|
ent1:SetSkin(self:GetSkin() or 0)
|
|
ent1:SetColor(self:GetColor())
|
|
ent1:SetMaterial(self:GetMaterial() or '')
|
|
|
|
ent2:SetSkin(self:GetSkin() or 0)
|
|
ent2:SetColor(self:GetColor())
|
|
ent2:SetMaterial(self:GetMaterial() or '')
|
|
|
|
if bg then
|
|
for k, v in pairs(bg) do
|
|
ent1:SetBodygroup(v.id, self:GetBodygroup(v.id))
|
|
end
|
|
|
|
for k, v in pairs(bg) do
|
|
ent2:SetBodygroup(v.id, self:GetBodygroup(v.id))
|
|
end
|
|
end
|
|
end
|
|
|
|
function ENT:DoSpawn(ply)
|
|
if ply.EntSpawnerCooldown and ply.EntSpawnerCooldown > CurTimeL() then return false end
|
|
|
|
local try = hook.Run(self.SPAWN_HOOK_CALL, ply, self.CLASS)
|
|
if try == false then return false end
|
|
|
|
if not ply:CheckLimit('sents') then
|
|
ply.EntSpawnerCooldown = CurTimeL() + 10
|
|
return false
|
|
end
|
|
|
|
local sfunc = self.TABLE.SpawnFunction
|
|
local ent
|
|
local hpos = self:GetPos() + Vector(0, 0, 40)
|
|
|
|
if sfunc then
|
|
local fakeTrace = {
|
|
Hit = true,
|
|
HitPos = hpos,
|
|
StartPos = hpos,
|
|
EndPos = hpos,
|
|
HitWorld = true,
|
|
HitSky = false,
|
|
PhysicsBone = 0,
|
|
HitNoDraw = false,
|
|
HitNonWorld = false,
|
|
HitTexture = '',
|
|
HitGroup = HITGROUP_GENERIC,
|
|
MatType = MAT_GRASS, -- heh
|
|
HitNormal = Vector(0, 0, 1),
|
|
Entity = game.GetWorld()
|
|
}
|
|
|
|
ent = sfunc(self.TABLE, ply, fakeTrace, self.CLASS)
|
|
if not ent then return end
|
|
else
|
|
ent = ents.Create(self.CLASS)
|
|
end
|
|
|
|
ent:SetPos(hpos)
|
|
ent:Spawn()
|
|
|
|
ent:PhysWake()
|
|
|
|
hook.Run(self.SPAWNED_HOOK_CALL, ply, ent)
|
|
|
|
ent:Activate()
|
|
|
|
DoPropSpawnedEffect(ent)
|
|
|
|
self.LastEntity = ent
|
|
self.LastPly = ply
|
|
|
|
self:SetNextSpawn(CurTimeL() + (self.ResetTimer or RESET_TIMER:GetFloat()))
|
|
|
|
undo.Create('SENT')
|
|
undo.AddEntity(ent)
|
|
undo.SetPlayer(ply)
|
|
|
|
if ent.PrintName then
|
|
undo.SetCustomUndoText('Undone ' .. ent.PrintName)
|
|
end
|
|
|
|
undo.Finish()
|
|
|
|
return true
|
|
end
|
|
|
|
function ENT:Think()
|
|
if CLIENT then return end
|
|
|
|
if self:GetIsSpawned() then
|
|
local lpos = self:GetPos()
|
|
local dist = PICKUP_RANGE:GetInt()
|
|
|
|
for k, v in ipairs(player.GetAll()) do
|
|
if v:GetPos():Distance(lpos) < dist and self:DoSpawn(v) then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local debugwtite = Material('models/debug/debugwhite')
|
|
local glow = Color(0, 255, 255)
|
|
|
|
function ENT:Draw()
|
|
if not IsValid(self.CModel) then self:ClientsideEntity() end
|
|
if not IsValid(self.CModel2) then self:ClientsideEntity() end
|
|
|
|
local mdl = self:GetModel()
|
|
self.CModel:SetModel(mdl)
|
|
self.CModel2:SetModel(mdl)
|
|
|
|
local ang = self.CurrAngle or Angle(0, 0, 0)
|
|
local pos = self:GetPos()
|
|
|
|
ang.y = ang.y + FrameTime() * 33
|
|
pos.z = pos.z + math.sin(CurTimeL() * 2) * 10 + 20
|
|
|
|
ang:Normalize()
|
|
|
|
self.CModel:SetAngles(ang)
|
|
self.CModel2:SetAngles(ang)
|
|
self.CModel:SetPos(pos)
|
|
self.CModel2:SetPos(pos)
|
|
|
|
if self:GetIsSpawned() then
|
|
self.CModel:DrawModel()
|
|
-- God how i hate this part
|
|
-- GMod functions have documented the best
|
|
self.CurrentClip = (self.CurrentClip or 0) + FrameTime() * 22
|
|
|
|
if self.CurrentClip > 150 then
|
|
self.CurrentClip = -150
|
|
end
|
|
|
|
local Vec = ang:Forward()
|
|
|
|
local First = pos + Vec * self.CurrentClip
|
|
local Second = pos + Vec * self.CurrentClip + Vec * 5
|
|
local dot1 = Vec:Dot(First)
|
|
local dot2 = (-Vec):Dot(Second)
|
|
|
|
render.SuppressEngineLighting(true)
|
|
render.ModelMaterialOverride(debugwtite)
|
|
render.SetColorModulation(0, 1, 1)
|
|
render.ResetModelLighting(1, 1, 1)
|
|
|
|
local old = render.EnableClipping(true)
|
|
render.PushCustomClipPlane(Vec, dot1)
|
|
render.PushCustomClipPlane(-Vec, dot2)
|
|
|
|
self.CModel2:DrawModel()
|
|
|
|
render.PopCustomClipPlane()
|
|
render.PopCustomClipPlane()
|
|
render.EnableClipping(old)
|
|
|
|
render.SetColorModulation(1, 1, 1)
|
|
render.ModelMaterialOverride()
|
|
render.SuppressEngineLighting(false)
|
|
end
|
|
|
|
self.CurrAngle = ang
|
|
end
|
|
|
|
function ENT:OnRemove()
|
|
if CLIENT and IsValid(self.CModel) then
|
|
self.CModel:Remove()
|
|
end
|
|
|
|
if CLIENT and IsValid(self.CModel2) then
|
|
self.CModel2:Remove()
|
|
end
|
|
end
|