Files
wnsrc/lua/entities/arccw_ammo.lua

331 lines
11 KiB
Lua
Raw Normal View History

2024-08-04 22:55:00 +03:00
--[[
| 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