mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
331 lines
11 KiB
Lua
331 lines
11 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/
|
||
--]]
|
||
|
||
AddCSLuaFile()
|
||
|
||
ENT.Type = "anim"
|
||
ENT.Base = "base_entity"
|
||
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
|
||
|
||
ENT.PrintName = "Base Ammo"
|
||
ENT.Category = "ArcCW - Ammo"
|
||
ENT.ArcCW_Ammo = true
|
||
|
||
ENT.Spawnable = false
|
||
ENT.Model = "models/items/sniper_round_box.mdl"
|
||
ENT.MaxHealth = 40
|
||
ENT.Scale = 1
|
||
|
||
ENT.AmmoType = "SniperPenetratedRound"
|
||
ENT.AmmoCount = 5
|
||
ENT.DetonationDamage = 10 -- Per-round damage
|
||
ENT.DetonationRadius = 256
|
||
ENT.DetonationSound = "weapons/arccw/glock18/glock18-1.wav" -- string or table
|
||
|
||
ENT.ShellModel = "models/shells/shell_9mm.mdl"
|
||
ENT.ShellScale = 1.5
|
||
|
||
ENT.ResistanceMult = {
|
||
[DMG_BURN] = 3,
|
||
[DMG_DIRECT] = 3, -- This is also fire
|
||
[DMG_BLAST] = 2,
|
||
[DMG_BULLET] = 0.5,
|
||
[DMG_BUCKSHOT] = 0.5,
|
||
[DMG_CLUB] = 0.25,
|
||
[DMG_SLASH] = 0.25,
|
||
[DMG_CRUSH] = 0.25,
|
||
[DMG_NERVEGAS] = 0,
|
||
[DMG_POISON] = 0
|
||
}
|
||
|
||
function ENT:Initialize()
|
||
self:SetModel(self.Model)
|
||
self:SetHealth(math.max(math.ceil(self.MaxHealth * ArcCW.ConVars["mult_ammohealth"]:GetFloat()), 1))
|
||
self.AmmoCount = math.max(math.ceil(self.AmmoCount * ArcCW.ConVars["mult_ammoamount"]:GetFloat(), 1))
|
||
self.MaxAmmoCount = self.AmmoCount
|
||
|
||
if engine.ActiveGamemode() == "terrortown" and ArcCW.TTTReplaceTable then
|
||
self.AmmoType = ArcCW.TTTReplaceTable[self.AmmoType] or self.AmmoType
|
||
end
|
||
|
||
if self.Scale != 1 then
|
||
self:SetModelScale(self.Scale)
|
||
end
|
||
|
||
if self:SkinCount() > 1 and math.random() <= ArcCW.ConVars["ammo_rareskin"]:GetFloat() then
|
||
self:SetSkin(math.random(1, self:SkinCount() - 1))
|
||
end
|
||
|
||
if SERVER then
|
||
|
||
self:PhysicsInit(SOLID_VPHYSICS)
|
||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||
self:SetSolid(SOLID_VPHYSICS)
|
||
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
|
||
self:SetUseType(SIMPLE_USE)
|
||
self:PhysWake()
|
||
|
||
self:SetTrigger(true) -- Enables Touch() to be called even when not colliding
|
||
if ArcCW.ConVars["ammo_largetrigger"]:GetBool() then
|
||
self:UseTriggerBounds(true, 24)
|
||
end
|
||
end
|
||
end
|
||
|
||
-- Adapted from TTT's ammo - we don't use it otherwise
|
||
function ENT:TTT_PlayerCanPickup(ply)
|
||
if ply == self:GetOwner() then return false end
|
||
|
||
local result = hook.Call("TTTCanPickupAmmo", nil, ply, self)
|
||
if result then
|
||
return result
|
||
end
|
||
|
||
local ent = self
|
||
local phys = ent:GetPhysicsObject()
|
||
local spos = phys:IsValid() and phys:GetPos() or ent:OBBCenter()
|
||
local epos = ply:GetShootPos()
|
||
|
||
local tr = util.TraceLine({start = spos, endpos = epos, filter = {ply, ent}, mask = MASK_SOLID})
|
||
|
||
-- can pickup if trace was not stopped
|
||
return tr.Fraction == 1.0
|
||
end
|
||
|
||
-- Ditto - unused outside of TTT
|
||
function ENT:TTT_CheckForWeapon(ply)
|
||
--[[]
|
||
if !self.CachedWeapons then
|
||
local tbl = {}
|
||
for k,v in pairs(weapons.GetList()) do
|
||
if v and v.Primary.Ammo == self.AmmoType then
|
||
tbl[v.ClassName] = true -- WEPS.GetClass(v)
|
||
end
|
||
end
|
||
self.CachedWeapons = tbl
|
||
end
|
||
]]
|
||
|
||
-- Why does TTT not iterate over the player's weapons? This is obviously faster
|
||
for _, wep in ipairs(ply:GetWeapons()) do
|
||
--if self.CachedWeapons[wep:GetClass()] then return true end
|
||
-- Perform check for overwritten ammo types (attachments) and UBGLs
|
||
if wep.ArcCW and
|
||
(wep:GetBuff_Override("UBGL_Ammo") == self.AmmoType
|
||
or wep:GetBuff_Override("Override_Ammo", wep.Primary.Ammo) == self.AmmoType
|
||
or wep:GetBuff_Override("Akimbo_Ammo") == self.AmmoType) then
|
||
return true
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
function ENT:ApplyAmmo(ply)
|
||
if self.USED then return end
|
||
if engine.ActiveGamemode() == "terrortown" then
|
||
-- Stupid checks mate... but we'll play along unless an override exists
|
||
if !self.IgnoreTTTChecks and !self:TTT_PlayerCanPickup(ply) or !self:TTT_CheckForWeapon(ply) then return end
|
||
|
||
local giveCount = math.min(self.AmmoCount, ArcCW.TTTAmmoToClipMax[string.lower(self.AmmoType)] - ply:GetAmmoCount(self.AmmoType))
|
||
if giveCount <= 0 then return end
|
||
|
||
self.AmmoCount = self.AmmoCount - giveCount
|
||
ply:GiveAmmo(giveCount, self.AmmoType)
|
||
|
||
-- Ugly hack to let client update ammo count
|
||
-- Why not just use NWInts or NetworkVars to begin with? Good question!
|
||
self:SetNWInt("truecount", self.AmmoCount)
|
||
|
||
if self.AmmoCount <= 0 then
|
||
self.USED = true
|
||
self:Remove()
|
||
end
|
||
else
|
||
self.USED = true -- Prevent multiple uses
|
||
ply:GiveAmmo(self.AmmoCount, self.AmmoType)
|
||
self:Remove()
|
||
end
|
||
end
|
||
|
||
function ENT:DetonateRound()
|
||
local count = math.Clamp(math.random(1, self.MaxAmmoCount / 5), 1, self.AmmoCount)
|
||
-- Default function
|
||
self:FireBullets({
|
||
Attacker = self.Burner,
|
||
Damage = self.DetonationDamage,
|
||
Force = self.DetonationDamage / 5,
|
||
Num = count,
|
||
AmmoType = self.AmmoType,
|
||
Src = self:WorldSpaceCenter(),
|
||
Dir = self:GetUp(),
|
||
Spread = Vector(math.pi * 2, math.pi * 2, 0),
|
||
IgnoreEntity = self
|
||
})
|
||
self.AmmoCount = self.AmmoCount - count
|
||
|
||
self:GetPhysicsObject():AddVelocity(VectorRand() * math.random(30, 50) * self:GetPhysicsObject():GetMass())
|
||
self:GetPhysicsObject():AddAngleVelocity(VectorRand() * math.random(60, 300))
|
||
|
||
if self.DetonationSound then
|
||
self:EmitSound(istable(self.DetonationSound) and table.Random(self.DetonationSound) or self.DetonationSound)
|
||
end
|
||
end
|
||
|
||
function ENT:Detonate(wet, attacker)
|
||
if wet then
|
||
self:FireBullets({
|
||
Attacker = attacker,
|
||
Damage = self.DetonationDamage,
|
||
Force = self.DetonationDamage / 5,
|
||
Num = math.max(self.AmmoCount, 50),
|
||
AmmoType = self.AmmoType,
|
||
Src = self:WorldSpaceCenter(),
|
||
Dir = self:GetUp(),
|
||
Spread = Vector(math.pi * 2, math.pi * 2, 0),
|
||
IgnoreEntity = self
|
||
})
|
||
end
|
||
|
||
local e = EffectData()
|
||
e:SetOrigin(self:GetPos())
|
||
util.Effect("Explosion", e)
|
||
|
||
util.BlastDamage(self, attacker, self:GetPos(), self.DetonationRadius, self.DetonationDamage * (wet and 0.5 or 1))
|
||
self:Remove()
|
||
end
|
||
|
||
if SERVER then
|
||
|
||
function ENT:Use(ply)
|
||
if !ply:IsPlayer() then return end
|
||
self:ApplyAmmo(ply)
|
||
end
|
||
|
||
|
||
function ENT:Touch(ply)
|
||
if !ply:IsPlayer() or !ArcCW.ConVars["ammo_autopickup"]:GetBool() then return end
|
||
self:ApplyAmmo(ply)
|
||
end
|
||
|
||
function ENT:Burn(attacker)
|
||
self.Burning = true
|
||
self.Burner = attacker
|
||
self:Ignite(30)
|
||
self:SetHealth(-1)
|
||
end
|
||
|
||
function ENT:OnTakeDamage(dmginfo)
|
||
|
||
if self:Health() <= 0 or self.USED then return end
|
||
|
||
--self:TakePhysicsDamage(dmginfo)
|
||
self:SetHealth(self:Health() - dmginfo:GetDamage())
|
||
|
||
if self:Health() <= 0 then
|
||
|
||
self.USED = true
|
||
|
||
local cvar = ArcCW.ConVars["ammo_detonationmode"]:GetInt()
|
||
|
||
if cvar == -1 or (!ArcCW.ConVars["ammo_chaindet"]:GetBool() and dmginfo:GetInflictor().ArcCW_Ammo) or self.DetonationDamage <= 0 then
|
||
-- Go quietly
|
||
local e = EffectData()
|
||
e:SetOrigin(self:GetPos())
|
||
e:SetMagnitude(8)
|
||
e:SetScale(2)
|
||
util.Effect("Sparks", e)
|
||
self:EmitSound("physics/cardboard/cardboard_box_break2.wav", 80, 120)
|
||
self:Remove()
|
||
elseif cvar == 2 and (math.random() <= 0.25 or dmginfo:IsDamageType(DMG_BURN)) then
|
||
-- Fancy ammobox burning
|
||
self:Burn(dmginfo:GetAttacker())
|
||
else
|
||
-- Plain old explosion
|
||
self:Detonate(cvar >= 1, dmginfo:GetAttacker())
|
||
end
|
||
end
|
||
|
||
end
|
||
|
||
function ENT:Think()
|
||
if self.Burning then
|
||
|
||
if self.AmmoCount <= 0 then
|
||
self:Detonate(false, IsValid(self.Burner) and self.Burner or self)
|
||
else
|
||
self:DetonateRound()
|
||
end
|
||
|
||
self:NextThink(CurTime() + math.random() * 0.3 + 0.2)
|
||
return true
|
||
end
|
||
end
|
||
|
||
-- Do it during the hook so that hit damage numbers show up properly (yes, I am _that_ pedantic)
|
||
hook.Add("EntityTakeDamage", "ArcCW_Ammo", function(ent, dmginfo)
|
||
if ent.ArcCW_Ammo then
|
||
if ArcCW.ConVars["mult_ammohealth"]:GetFloat() < 0 then
|
||
dmginfo:ScaleDamage(0)
|
||
elseif ent.ResistanceMult then
|
||
-- Only apply one multiplier, and prioritize larger ones
|
||
for k, v in SortedPairsByValue(ent.ResistanceMult, true) do if dmginfo:IsDamageType(k) then dmginfo:ScaleDamage(v) break end end
|
||
end
|
||
end
|
||
end)
|
||
|
||
elseif CLIENT then
|
||
|
||
function ENT:DrawTranslucent()
|
||
self:Draw()
|
||
end
|
||
|
||
function ENT:Draw()
|
||
self:DrawModel()
|
||
|
||
local cvar2d3d = ArcCW.ConVars["2d3d"]:GetInt()
|
||
if cvar2d3d == 0 or (cvar2d3d == 1 and LocalPlayer():GetEyeTrace().Entity != self) then return end
|
||
|
||
if (EyePos() - self:GetPos()):LengthSqr() <= 262144 then -- 512^2
|
||
local ang = LocalPlayer():EyeAngles()
|
||
|
||
ang:RotateAroundAxis(ang:Forward(), 180)
|
||
ang:RotateAroundAxis(ang:Right(), 90)
|
||
ang:RotateAroundAxis(ang:Up(), 90)
|
||
|
||
cam.Start3D2D(self:WorldSpaceCenter() + Vector(0, 0, (self:OBBMaxs().z - self:OBBMins().z) * 0.5 + 8) , ang, 0.1)
|
||
surface.SetFont("ArcCW_32_Unscaled")
|
||
|
||
local w = surface.GetTextSize(self.PrintName)
|
||
|
||
surface.SetTextPos(-w / 2 + 2, 2)
|
||
surface.SetTextColor(0, 0, 0, 150)
|
||
surface.DrawText(self.PrintName)
|
||
|
||
surface.SetTextPos(-w / 2, 0)
|
||
surface.SetTextColor(255, 255, 255, 255)
|
||
surface.DrawText(self.PrintName)
|
||
|
||
local ammo = self:GetNWInt("truecount", -1) != -1 and self:GetNWInt("truecount", -1) or self.AmmoCount
|
||
if ammo then
|
||
w = surface.GetTextSize("×" .. ammo)
|
||
|
||
surface.SetTextColor(0, 0, 0, 150)
|
||
surface.SetTextPos(-w / 2 + 2, 27)
|
||
surface.DrawText("×" .. ammo)
|
||
|
||
surface.SetTextColor(255, 255, 255, 255)
|
||
surface.SetTextPos(-w / 2, 25)
|
||
surface.DrawText("×" .. ammo)
|
||
end
|
||
cam.End3D2D()
|
||
end
|
||
end
|
||
|
||
end |