287 lines
8.0 KiB
Lua
287 lines
8.0 KiB
Lua
if engine.ActiveGamemode() ~= "terrortown" then return end
|
|
|
|
AddCSLuaFile()
|
|
|
|
ENT.Type = "anim"
|
|
ENT.Base = "base_entity"
|
|
ENT.RenderGroup = RENDERGROUP_BOTH
|
|
|
|
ENT.PrintName = "tacrp_ammocrate_name"
|
|
|
|
ENT.Model = "models/weapons/tacint/ammoboxes/ammo_box-2b.mdl"
|
|
ENT.CanHavePrints = true
|
|
ENT.CanUseKey = true
|
|
ENT.MaxHealth = 250
|
|
|
|
ENT.MaxStored = 100
|
|
ENT.RechargeRate = 1
|
|
ENT.RechargeFreq = 2
|
|
|
|
ENT.AutomaticFrameAdvance = true
|
|
|
|
ENT.CollisionSoundsHard = {
|
|
"physics/metal/metal_box_impact_hard1.wav",
|
|
"physics/metal/metal_box_impact_hard2.wav",
|
|
"physics/metal/metal_box_impact_hard3.wav",
|
|
}
|
|
|
|
ENT.CollisionSoundsSoft = {
|
|
"physics/metal/metal_box_impact_soft1.wav",
|
|
"physics/metal/metal_box_impact_soft2.wav",
|
|
"physics/metal/metal_box_impact_soft3.wav",
|
|
}
|
|
|
|
ENT.ExplodeSounds = {
|
|
"^TacRP/weapons/grenade/frag_explode-1.wav",
|
|
"^TacRP/weapons/grenade/frag_explode-2.wav",
|
|
"^TacRP/weapons/grenade/frag_explode-3.wav",
|
|
}
|
|
|
|
ENT.AmmoInfo = {
|
|
-- ammo per use, max, cost per use
|
|
["357"] = {10, nil, 12}, -- 2 to fill
|
|
["smg1"] = {30, nil, 10}, -- 2 to fill
|
|
["pistol"] = {20, nil, 5}, -- 3 to fill
|
|
["alyxgun"] = {12, nil, 6}, -- 3 to fill
|
|
["buckshot"] = {8, nil, 8}, -- 3 to fill
|
|
|
|
["smg1_grenade"] = {1, 3, 34},
|
|
["rpg_round"] = {1, 2, 50},
|
|
["ti_sniper"] = {5, 10, 50},
|
|
}
|
|
|
|
if CLIENT then
|
|
|
|
ENT.Icon = "entities/tacrp_ammo_crate.png"
|
|
|
|
LANG.AddToLanguage("english", "tacrp_ammocrate_hint", "Press {usekey} to get ammo. Remaining charge: {num}")
|
|
LANG.AddToLanguage("english", "tacrp_ammocrate_broken", "Your Ammo Crate has been destroyed!")
|
|
LANG.AddToLanguage("english", "tacrp_ammocrate_subtitle", "Press {usekey} to receive ammo.")
|
|
LANG.AddToLanguage("english", "tacrp_ammocrate_charge", "Remaining charge: {charge}.")
|
|
LANG.AddToLanguage("english", "tacrp_ammocrate_empty", "No charge left")
|
|
|
|
LANG.AddToLanguage("english", "tacrp_ammocrate_short_desc", "The ammo crate recharges over time")
|
|
|
|
local TryT = LANG.TryTranslation
|
|
local ParT = LANG.GetParamTranslation
|
|
|
|
ENT.TargetIDHint = {
|
|
name = "tacrp_ammocrate_name",
|
|
hint = "tacrp_ammocrate_hint",
|
|
fmt = function(ent, txt)
|
|
return ParT(txt, {
|
|
usekey = Key("+use", "USE"),
|
|
num = ent:GetStoredAmmo() or 0
|
|
})
|
|
end
|
|
}
|
|
|
|
local key_params = {
|
|
usekey = Key("+use", "USE")
|
|
}
|
|
|
|
-- TTT2 does this
|
|
hook.Add("TTTRenderEntityInfo", "tacrp_ammo_crate", function(tData)
|
|
local client = LocalPlayer()
|
|
local ent = tData:GetEntity()
|
|
|
|
if not IsValid(client) or not client:IsTerror() or not client:Alive()
|
|
or not IsValid(ent) or tData:GetEntityDistance() > 100 or ent:GetClass() ~= "tacrp_ammo_crate_ttt" then
|
|
return
|
|
end
|
|
|
|
-- enable targetID rendering
|
|
tData:EnableText()
|
|
tData:EnableOutline()
|
|
tData:SetOutlineColor(client:GetRoleColor())
|
|
|
|
tData:SetTitle(TryT(ent.PrintName))
|
|
tData:SetSubtitle(ParT("tacrp_ammocrate_subtitle", key_params))
|
|
tData:SetKeyBinding("+use")
|
|
|
|
local charge = ent:GetStoredAmmo() or 0
|
|
|
|
tData:AddDescriptionLine(TryT("tacrp_ammocrate_short_desc"))
|
|
|
|
tData:AddDescriptionLine(
|
|
(charge > 0) and ParT("tacrp_ammocrate_charge", {charge = charge}) or TryT("tacrp_ammocrate_empty"),
|
|
(charge > 0) and roles.DETECTIVE.ltcolor or COLOR_ORANGE
|
|
)
|
|
end)
|
|
end
|
|
|
|
AccessorFuncDT(ENT, "StoredAmmo", "StoredAmmo")
|
|
AccessorFunc(ENT, "Placer", "Placer")
|
|
|
|
function ENT:SetupDataTables()
|
|
self:DTVar("Int", 0, "StoredAmmo")
|
|
end
|
|
|
|
function ENT:AddToStorage(amount)
|
|
self:SetStoredAmmo(math.min(self.MaxStored, self:GetStoredAmmo() + amount))
|
|
end
|
|
|
|
function ENT:TakeFromStorage(amount)
|
|
amount = math.min(amount, self:GetStoredAmmo())
|
|
self:SetStoredAmmo(math.max(0, self:GetStoredAmmo() - amount))
|
|
return amount
|
|
end
|
|
|
|
function ENT:Initialize()
|
|
if SERVER then
|
|
self:SetModel(self.Model)
|
|
|
|
self:PhysicsInit(SOLID_VPHYSICS)
|
|
self:SetMoveType(MOVETYPE_VPHYSICS)
|
|
self:SetSolid(SOLID_VPHYSICS)
|
|
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
|
|
self:SetUseType(CONTINUOUS_USE)
|
|
|
|
local phys = self:GetPhysicsObject()
|
|
if IsValid(phys) then
|
|
phys:SetMass(150)
|
|
phys:Wake()
|
|
end
|
|
|
|
if self.MaxHealth > 0 then
|
|
self:SetMaxHealth(self.MaxHealth)
|
|
self:SetHealth(self.MaxHealth)
|
|
end
|
|
|
|
self:SetStoredAmmo(self.MaxStored)
|
|
|
|
self:SetPlacer(nil)
|
|
|
|
self.fingerprints = {}
|
|
end
|
|
end
|
|
|
|
local function ClampedGiveAmmo(ply, ammo, amt, clamp)
|
|
local count = ply:GetAmmoCount(ammo)
|
|
|
|
if count >= clamp then
|
|
return 0
|
|
elseif count + amt > clamp then
|
|
amt = math.max(clamp - count, 0)
|
|
end
|
|
|
|
return amt
|
|
end
|
|
|
|
function ENT:ApplyAmmo(ply)
|
|
if (self.NextUse or 0) > CurTime() then return end
|
|
|
|
local wpn = ply:GetActiveWeapon()
|
|
|
|
local ammotype = string.lower(game.GetAmmoName(wpn:GetPrimaryAmmoType()) or "")
|
|
if !self.AmmoInfo[ammotype] then return end
|
|
|
|
local max = self.AmmoInfo[ammotype][2] or wpn.Primary.ClipMax
|
|
local amt = self.AmmoInfo[ammotype][1]
|
|
amt = ClampedGiveAmmo(ply, ammotype, amt, max) -- amount we need
|
|
local cost = self.AmmoInfo[ammotype][3] * (amt / self.AmmoInfo[ammotype][1])
|
|
local f = math.min(cost, self:GetStoredAmmo()) / cost -- fraction of cost we can afford
|
|
|
|
amt = math.floor(amt * f)
|
|
|
|
if amt > 0 then
|
|
self:TakeFromStorage(cost * f)
|
|
ply:GiveAmmo(amt, ammotype)
|
|
|
|
if !self.Open then
|
|
local seq = self:LookupSequence("open")
|
|
self:ResetSequence(seq)
|
|
self:EmitSound("items/ammocrate_open.wav")
|
|
|
|
self.Open = true
|
|
end
|
|
|
|
self.NextUse = CurTime() + 1
|
|
end
|
|
end
|
|
|
|
function ENT:PhysicsCollide(data)
|
|
if data.DeltaTime < 0.1 then return end
|
|
|
|
if data.Speed > 25 then
|
|
self:EmitSound(self.CollisionSoundsHard[math.random(#self.CollisionSoundsHard)])
|
|
else
|
|
self:EmitSound(self.CollisionSoundsSoft[math.random(#self.CollisionSoundsSoft)])
|
|
end
|
|
end
|
|
|
|
if SERVER then
|
|
|
|
function ENT:Use(ply)
|
|
if !ply:IsPlayer() then return end
|
|
self:ApplyAmmo(ply)
|
|
end
|
|
function ENT:Think()
|
|
if self.Open and (self.NextUse + 0.1) < CurTime() then
|
|
local seq = self:LookupSequence("close")
|
|
self:ResetSequence(seq)
|
|
self:EmitSound("items/ammocrate_close.wav")
|
|
|
|
self.Open = false
|
|
end
|
|
|
|
if (self.NextCharge or 0) < CurTime() then
|
|
self:AddToStorage(self.RechargeRate)
|
|
self.NextCharge = CurTime() + self.RechargeFreq
|
|
end
|
|
|
|
self:NextThink(CurTime())
|
|
return true
|
|
end
|
|
|
|
elseif CLIENT then
|
|
|
|
function ENT:DrawTranslucent()
|
|
self:Draw()
|
|
end
|
|
|
|
function ENT:Draw()
|
|
self:DrawModel()
|
|
end
|
|
|
|
end
|
|
|
|
function ENT:OnTakeDamage(dmginfo)
|
|
if self.BOOM then return end
|
|
|
|
self:TakePhysicsDamage(dmginfo)
|
|
self:SetHealth(self:Health() - dmginfo:GetDamage())
|
|
local att = dmginfo:GetAttacker()
|
|
local placer = self:GetPlacer()
|
|
|
|
if IsPlayer(att) then
|
|
DamageLog(Format("DMG: \t %s [%s] damaged ammo crate [%s] for %d dmg", att:Nick(), att:GetRoleString(), IsPlayer(placer) and placer:Nick() or "<disconnected>", dmginfo:GetDamage()))
|
|
end
|
|
|
|
if self:Health() < 0 then
|
|
self:Remove()
|
|
util.EquipmentDestroyed(self:GetPos())
|
|
|
|
if IsValid(self:GetPlacer()) then
|
|
LANG.Msg(self:GetPlacer(), "tacrp_ammocrate_broken")
|
|
end
|
|
|
|
self.BOOM = true
|
|
|
|
local dmg = self:GetStoredAmmo() * 2.25 + 75
|
|
|
|
util.BlastDamage(self, dmginfo:GetAttacker(), self:GetPos(), 400, dmg)
|
|
|
|
local fx = EffectData()
|
|
fx:SetOrigin(self:GetPos())
|
|
|
|
if self:WaterLevel() > 0 then
|
|
util.Effect("WaterSurfaceExplosion", fx)
|
|
else
|
|
util.Effect("Explosion", fx)
|
|
end
|
|
|
|
self:EmitSound(table.Random(self.ExplodeSounds), 125, 90)
|
|
end
|
|
end
|