Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions lua/acf/entities/missiles/acf_sweps_rockets.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
-- Rockets fired by the launcher SWEPs, loaded by ACF's own loader alongside its missile definitions
-- Every motor burns out in the tube (StarterPercent 1), so the whole impulse is the launch speed and Thrust only has to be non-zero
local Classes = ACF.Classes

Classes.DefineClass("ACF.Missiles.ShoulderFiredRocket", "ACF.Missiles.BaseMissile", function(CLASS)
CLASS.Name = "Shoulder-Fired Rockets"
CLASS.ID = "SFR"
CLASS.Description = "Rockets fired by infantry launchers."
CLASS.MenuHidden = true -- Only the SWEPs fire these, they have no rack
CLASS.Sound = "acf_missiles/missiles/missile_rocket.mp3"
-- No CLASS.Effect: the motors burn out in the tube, and these models have no "exhaust" attachment for the trail
CLASS.Spread = 0
CLASS.Blacklist = { ["ACF.Ammunition.AP"] = true, ["ACF.Ammunition.APHE"] = true, ["ACF.Ammunition.HP"] = true, ["ACF.Ammunition.FL"] = true, ["ACF.Ammunition.SM"] = true }
end)

Classes.DefineClass("ACF.Missiles.ShoulderFiredRocket.M6A3", "ACF.Missiles.ShoulderFiredRocket", function(CLASS)
CLASS.Name = "M6A3 Rocket"
CLASS.Description = "The 2.36 inch HEAT rocket of the M9A1 Bazooka."
CLASS.Model = "models/weapons/panzerschreck/w_panzerschreck_rocket.mdl"
CLASS.Caliber = 60
CLASS.Mass = 1.5
CLASS.Length = 49
CLASS.Year = 1944
CLASS.ReloadTime = 1
CLASS.Racks = {}
CLASS.Guidances = { ["ACF.Missiles.Guidance.Dumb"] = true }
CLASS.Navigation = "ACF.Missiles.Navigation.Chase"
CLASS.Fuzes = { ["ACF.Missiles.Fuze.Contact"] = true }
CLASS.Agility = 1
CLASS.ArmDelay = 0
CLASS.HitDeviate = false
CLASS.Round = {
Model = "models/weapons/panzerschreck/w_panzerschreck_rocket.mdl",
MaxLength = 22.3,
Armor = 1,
ProjLength = 19.7,
PropLength = 2.6,
Thrust = 115000, -- in kg*in/s^2
FuelConsumption = 0.01474, -- in g/s/f
StarterPercent = 1,
MaxAgilitySpeed = 1, -- in m/s
DragCoef = 0.0012,
FinMul = 0,
GLimit = 1,
TailFinMul = 0.06,
PenMul = 0.753,
FillerMul = 1,
LinerMassMul = 1,
Standoff = 6,
GhostingPeriod = 0.0125, -- in s
ActualLength = 49,
ActualWidth = 6
}
end)

Classes.DefineClass("ACF.Missiles.ShoulderFiredRocket.RPzBGr4322", "ACF.Missiles.ShoulderFiredRocket", function(CLASS)
CLASS.Name = "RPzB.Gr. 4322 Rocket"
CLASS.Description = "The 88mm HEAT rocket of the RPzB 54 Panzerschreck."
CLASS.Model = "models/weapons/panzerschreck/w_panzerschreck_rocket.mdl"
CLASS.Caliber = 88
CLASS.Mass = 3.25
CLASS.Length = 65
CLASS.Year = 1943
CLASS.ReloadTime = 1
CLASS.Racks = {}
CLASS.Guidances = { ["ACF.Missiles.Guidance.Dumb"] = true }
CLASS.Navigation = "ACF.Missiles.Navigation.Chase"
CLASS.Fuzes = { ["ACF.Missiles.Fuze.Contact"] = true }
CLASS.Agility = 1
CLASS.ArmDelay = 0
CLASS.HitDeviate = false
CLASS.Round = {
Model = "models/weapons/panzerschreck/w_panzerschreck_rocket.mdl",
MaxLength = 26.6,
Armor = 1,
ProjLength = 23.7,
PropLength = 2.9,
Thrust = 115000, -- in kg*in/s^2
FuelConsumption = 0.01219, -- in g/s/f
StarterPercent = 1,
MaxAgilitySpeed = 1, -- in m/s
DragCoef = 0.0026,
FinMul = 0,
GLimit = 1,
TailFinMul = 0.06,
PenMul = 1.206,
FillerMul = 1,
LinerMassMul = 1,
Standoff = 9,
GhostingPeriod = 0.0125, -- in s
ActualLength = 65,
ActualWidth = 9
}
end)

Classes.DefineClass("ACF.Missiles.ShoulderFiredRocket.Mk6HEAA", "ACF.Missiles.ShoulderFiredRocket", function(CLASS)
CLASS.Name = "Mk 6 HEAA Rocket"
CLASS.Description = "The 83mm anti-armor rocket of the Mk 153 SMAW."
CLASS.Model = "models/acf/munitions/projectile_mortar.mdl"
CLASS.Caliber = 83
CLASS.Mass = 4.5
CLASS.Length = 82
CLASS.Year = 1984
CLASS.ReloadTime = 1
CLASS.Racks = {}
CLASS.Guidances = { ["ACF.Missiles.Guidance.Dumb"] = true }
CLASS.Navigation = "ACF.Missiles.Navigation.Chase"
CLASS.Fuzes = { ["ACF.Missiles.Fuze.Contact"] = true }
CLASS.Agility = 1
CLASS.ArmDelay = 0
CLASS.HitDeviate = false
CLASS.Bodygroups = {
type = {
DataSource = function() return "HEAT" end,
HEAT = { OnRack = "HEAT.smd", OnLaunch = "HEAT.smd" },
},
fins = {
DataSource = function() return "Fins" end,
Fins = { OnRack = "fins.smd", OnLaunch = "fins.smd" },
},
}
CLASS.Round = {
Model = "models/acf/munitions/projectile_mortar.mdl",
MaxLength = 37.7,
Armor = 1,
ProjLength = 31.9,
PropLength = 5.8,
Thrust = 115000, -- in kg*in/s^2
FuelConsumption = 0.00838, -- in g/s/f
StarterPercent = 1,
MaxAgilitySpeed = 1, -- in m/s
DragCoef = 0.0024,
FinMul = 0,
GLimit = 1,
TailFinMul = 0.06,
PenMul = 1.705,
FillerMul = 2,
LinerMassMul = 1,
Standoff = 8,
GhostingPeriod = 0.0125, -- in s
ActualLength = 82,
ActualWidth = 8
}
end)
34 changes: 33 additions & 1 deletion lua/acf_sweps/acf_adapter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,34 @@ function Adapter.CreateMissileSpec(Params)
return ACF.Missiles.CreateSpec(Params)
end

-- The warhead's penetration, built the way Missiles.CreateSpec builds its round; that is server only and the
-- spawnmenu tooltip has no weapon entity to ask, so the client rebuilds it. Nil if the missile isn't loaded
function Adapter.GetMissilePenetration(SWEP)
local Classes = ACF.Classes
local MissileClass = Classes.GetSubtypeByName("ACF.Missiles.BaseMissile", SWEP.Missile)
local AmmoClass = Classes.GetSubtypeByName("ACF.Ammunition.BaseAmmo", SWEP.MissileAmmo)
if not (MissileClass and MissileClass.Round and AmmoClass) then return end

local Ammo = AmmoClass()
local Input = SWEP.MissileAmmoData or {}

Ammo.Weapon = MissileClass()

for _, Field in ipairs(Classes.GetTypeFields(AmmoClass)) do
if Field.Menu and Input[Field.Name] ~= nil then Ammo[Field.Name] = Input[Field.Name] end
end

if not (isnumber(Input.RoundLength) and Input.RoundLength > 0) then
Ammo.RoundLength = nil
Ammo.Projectile = MissileClass.Round.ProjLength
Ammo.Propellant = MissileClass.Round.PropLength
end

local Data = SERVER and Ammo:ServerConvert() or Ammo:ClientConvert()

return Ammo:GetPenetration(Data, Data.Standoff)
end

-- What ACF's missiles ask the thing that fired them, see launcher_sv.lua in ACF
local WeaponLauncher = {}
WeaponLauncher.__index = WeaponLauncher
Expand Down Expand Up @@ -219,10 +247,14 @@ function Adapter.FireMissile(Weapon, Spec, Pos, Dir, Filter, Target)
if not Missile then return nil, Err end

Missiles.Launch(Missile, {
Velocity = Dir * Weapon.ACFMuzzleVel * 39.37 + Owner:GetVelocity(),
-- Missiles.Launch adds the motor's starting boost on top, so rockets whose motor burns out in the tube launch at 0
Velocity = Dir * (Weapon.MissileLaunchVel or Weapon.ACFMuzzleVel) * 39.37 + Owner:GetVelocity(),
Direction = Dir,
})

-- A freshly spawned missile's first think waits for the default think interval, holding it at the muzzle for ~0.1 s
Missile:NextThink(CurTime())

if IsValid(Target) then Missile.GuidanceData.Target = Target end

return Missile
Expand Down
45 changes: 44 additions & 1 deletion lua/weapons/weapon_acf3r_smaw.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
AddCSLuaFile()

local Adapter = include("acf_sweps/acf_adapter.lua")

SWEP.Base = "weapon_acf_base"
SWEP.PrintName = "ACF SMAW"

Expand Down Expand Up @@ -36,11 +38,19 @@ SWEP.UseHybrid = false
-- SMAW Mk 153 fires 83mm rockets
SWEP.Caliber = 83 -- mm diameter (SMAW is 83mm)
SWEP.ACFType = "HEAT"
SWEP.ACFMuzzleVel = 220 -- m/s (SMAW HEAA muzzle velocity)
SWEP.ACFMuzzleVel = 208 -- m/s (SMAW HEAA muzzle velocity, 220 is the HEDP round)
SWEP.ACFProjLen = 28 -- Longer projectile for 83mm
SWEP.ACFPropMass = 0.12 -- kg of propellant
SWEP.Tracer = 1 -- SMAW rounds have visible tracer

-- Passed to Missiles.CreateSpec, defined in lua/acf/entities/missiles/acf_sweps_rockets.lua
SWEP.Missile = "ACF.Missiles.ShoulderFiredRocket.Mk6HEAA"
SWEP.MissileAmmo = "ACF.Ammunition.HEAT"
SWEP.MissileAmmoData = {LinerAngleRatio = 0.25} -- The rocket's own lengths are the real round, only the liner is picked here
SWEP.MissileLaunchVel = 0 -- The motor burns out in the tube, its boost is the whole muzzle velocity
SWEP.MissileGuidance = "ACF.Missiles.Guidance.Dumb"
SWEP.MissileFuze = {Type = "ACF.Missiles.Fuze.Contact"}

-- Aim table adjusted for 220 m/s muzzle velocity (flatter trajectory)
SWEP.AimTable = {}
SWEP.AimTable[1] = {IronPos = Vector(-2.95, 0, -0.7), IronAng = Angle(0, 0, -5), PitchAdjust = 0, Text = "100m"} -- 100m
Expand All @@ -64,6 +74,39 @@ SWEP.RequiresAim = true
SWEP.AimHoldType = "rpg"
SWEP.PunchAmount = 0.225 -- Recoilless rifle: drastically reduced recoil

-- Server only, built once since every shot is the same missile
function SWEP:GetMissileSpec()
local Spec = self.MissileSpec

if not Spec then
local Err

Spec, Err = Adapter.CreateMissileSpec({
Missile = self.Missile,
AmmoType = self.MissileAmmo,
Ammo = self.MissileAmmoData,
Guidance = {Type = self.MissileGuidance},
Fuze = self.MissileFuze,
})

if not Spec then ErrorNoHalt(self:GetClass() .. ": " .. Err .. "\n") return end

self.MissileSpec = Spec
end

return Spec
end

function SWEP:FireShots()
local Spec = self:GetMissileSpec()
if not Spec then return end

local Owner = self:GetOwner()
local Missile, Err = Adapter.FireMissile(self, Spec, Owner:GetShootPos(), self:GetShotDirections()[1], {Owner, self})

if not Missile and Err then ErrorNoHalt(self:GetClass() .. ": " .. Err .. "\n") end
end

function SWEP:CalcView( _, pos, ang, _ )
local ViewModel = self:GetOwner():GetViewModel()
if not IsValid(ViewModel) then return pos, ang end
Expand Down
7 changes: 7 additions & 0 deletions lua/weapons/weapon_acf_base/cl_selection.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ local function KeyName(Binding)
end

function SWEP:GetPenetrationText()
-- Launchers fire a missile, so its warhead is what penetrates, not the bullet settings
local MissilePen = self.Missile and Adapter.GetMissilePenetration(self)

if MissilePen then
return math.Round(MissilePen, 1) .. "mm @ All distances"
end

local Bullet = self:GetBullet()

if Bullet.Type == "HEAT" then
Expand Down
46 changes: 45 additions & 1 deletion lua/weapons/weapon_acf_bazooka.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
AddCSLuaFile()

local Adapter = include("acf_sweps/acf_adapter.lua")

SWEP.Base = "weapon_acf_base"
SWEP.PrintName = "ACF Bazooka"

Expand Down Expand Up @@ -32,13 +34,22 @@ SWEP.Primary.Delay = 0.2
SWEP.CarrySpeedMul = 0.6
SWEP.UseHybrid = false

-- The 60mm M6 rocket of the M9A1 Bazooka, these only describe the warhead for the info panel
SWEP.Caliber = 60 -- mm diameter of bullet
SWEP.ACFType = "HEAT"
SWEP.ACFMuzzleVel = 80 -- m/s of bullet leaving the barrel
SWEP.ACFMuzzleVel = 82 -- m/s of bullet leaving the barrel
SWEP.ACFProjLen = 18.5
SWEP.ACFPropMass = 0.040291 -- kg of propellant
SWEP.Tracer = 0

-- Passed to Missiles.CreateSpec, defined in lua/acf/entities/missiles/acf_sweps_rockets.lua
SWEP.Missile = "ACF.Missiles.ShoulderFiredRocket.M6A3"
SWEP.MissileAmmo = "ACF.Ammunition.HEAT"
SWEP.MissileAmmoData = {LinerAngleRatio = 0.25} -- The rocket's own lengths are the real round, only the liner is picked here
SWEP.MissileLaunchVel = 0 -- The motor burns out in the tube, its boost is the whole muzzle velocity
SWEP.MissileGuidance = "ACF.Missiles.Guidance.Dumb"
SWEP.MissileFuze = {Type = "ACF.Missiles.Fuze.Contact"}

SWEP.AimTable = {}
SWEP.AimTable[1] = {IronPos = Vector(-3.83, -8, -0.4), IronAng = Angle(2.9, -0.5, 0), PitchAdjust = -1.55, Text = "100yd"} -- 100yd
SWEP.AimTable[2] = {IronPos = Vector(-3.79, -8, -1.9), IronAng = Angle(6.9, -0.4, 0), PitchAdjust = -4.2, Text = "200yd"} -- 200yd
Expand Down Expand Up @@ -66,3 +77,36 @@ SWEP.Zoom = 1.2
SWEP.Recovery = 0.5

SWEP.RequiresAim = true

-- Server only, built once since every shot is the same missile
function SWEP:GetMissileSpec()
local Spec = self.MissileSpec

if not Spec then
local Err

Spec, Err = Adapter.CreateMissileSpec({
Missile = self.Missile,
AmmoType = self.MissileAmmo,
Ammo = self.MissileAmmoData,
Guidance = {Type = self.MissileGuidance},
Fuze = self.MissileFuze,
})

if not Spec then ErrorNoHalt(self:GetClass() .. ": " .. Err .. "\n") return end

self.MissileSpec = Spec
end

return Spec
end

function SWEP:FireShots()
local Spec = self:GetMissileSpec()
if not Spec then return end

local Owner = self:GetOwner()
local Missile, Err = Adapter.FireMissile(self, Spec, Owner:GetShootPos(), self:GetShotDirections()[1], {Owner, self})

if not Missile and Err then ErrorNoHalt(self:GetClass() .. ": " .. Err .. "\n") end
end
Loading
Loading