--[[ | 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