diff --git a/lua/acf/server/sv_acfbase.lua b/lua/acf/server/sv_acfbase.lua index e79b97933..6a8927461 100644 --- a/lua/acf/server/sv_acfbase.lua +++ b/lua/acf/server/sv_acfbase.lua @@ -42,7 +42,7 @@ function ACF_Activate ( Entity , Recalc ) else local Size = Entity.OBBMaxs(Entity) - Entity.OBBMins(Entity) if not Entity.ACF.Aera then - Entity.ACF.Aera = ((Size.x * Size.y)+(Size.x * Size.z)+(Size.y * Size.z)) * 6.45 + Entity.ACF.Aera = ((Size.x * Size.y)+(Size.x * Size.z)+(Size.y * Size.z)) * 6.45 --^ 1.15 end --if not Entity.ACF.Volume then -- Entity.ACF.Volume = Size.x * Size.y * Size.z * 16.38 @@ -56,7 +56,7 @@ function ACF_Activate ( Entity , Recalc ) local Armour = ACF_CalcArmor( Area, Ductility, Entity:GetPhysicsObject():GetMass() ) -- So we get the equivalent thickness of that prop in mm if all its weight was a steel plate local Health = ( Area / ACF.Threshold ) * ( 1 + Ductility ) -- Setting the threshold of the prop aera gone - local Percent = 1 + local Percent = 1 if Recalc and Entity.ACF.Health and Entity.ACF.MaxHealth then Percent = Entity.ACF.Health/Entity.ACF.MaxHealth @@ -64,8 +64,8 @@ function ACF_Activate ( Entity , Recalc ) Entity.ACF.Health = Health * Percent Entity.ACF.MaxHealth = Health - Entity.ACF.Armour = Armour * (0.5 + Percent/2) - Entity.ACF.MaxArmour = Armour * ACF.ArmorMod + Entity.ACF.Armour = (Armour) * (0.5 + Percent/2) + Entity.ACF.MaxArmour = (Armour) * ACF.ArmorMod Entity.ACF.Type = nil Entity.ACF.Mass = PhysObj:GetMass() --Entity.ACF.Density = (PhysObj:GetMass()*1000)/Entity.ACF.Volume @@ -133,8 +133,21 @@ function ACF_CalcDamage( Entity , Energy , FrAera , Angle ) local maxPenetration = (Energy.Penetration / FrAera) * ACF.KEtoRHA --RHA Penetration local HitRes = {} + + + if istable(Entity) == false then -- ACF feeds us a table for some reason when we shoot living things. + if (!Entity:IsNPC() and !Entity:IsPlayer()) and (!Entity:IsNextBot()) then -- Don't do this to living things! + + if Entity:GetCollisionBounds() != Entity:GetModelBounds() then -- Did they do the makespherical cheat? + armor = 1 + losArmor = 1 + end + end + end + + local dmul = 1--This actually controls overall prop damage from rounds. It should be in globals but i'm suck's at Coding's -karb + --BNK Stuff - local dmul = 1 if (ISBNK) then local cvar = GetConVarNumber("sbox_godmode") @@ -192,14 +205,16 @@ end function ACF_PropDamage( Entity , Energy , FrAera , Angle , Inflictor , Bone ) local HitRes = ACF_CalcDamage( Entity , Energy , FrAera , Angle ) + HitRes.Kill = false if HitRes.Damage >= Entity.ACF.Health then HitRes.Kill = true else Entity.ACF.Health = Entity.ACF.Health - HitRes.Damage - Entity.ACF.Armour = Entity.ACF.MaxArmour * (0.5 + Entity.ACF.Health/Entity.ACF.MaxHealth/2) --Simulating the plate weakening after a hit + Entity.ACF.Armour = math.Clamp(Entity.ACF.MaxArmour * (0.5 + Entity.ACF.Health/Entity.ACF.MaxHealth/2)^1.7, Entity.ACF.MaxArmour*0.25, Entity.ACF.MaxArmour) --Simulating the plate weakening after a hit + --math.Clamp( Entity.ACF.Ductility, -0.8, 0.8 ) if Entity.ACF.PrHealth then ACF_UpdateVisualHealth(Entity) end @@ -305,7 +320,7 @@ function ACF_SquishyDamage( Entity , Energy , FrAera , Angle , Inflictor , Bone, end - local dmul = 2.5 + local dmul = 1 --BNK stuff if (ISBNK) then diff --git a/lua/acf/server/sv_acfdamage.lua b/lua/acf/server/sv_acfdamage.lua index 3123fafce..63360a252 100644 --- a/lua/acf/server/sv_acfdamage.lua +++ b/lua/acf/server/sv_acfdamage.lua @@ -301,7 +301,7 @@ function ACF_RoundImpact( Bullet, Speed, Energy, Target, HitPos, HitNormal , Bon -- Checking for ricochet if ricoProb > math.random() and Angle < 90 then Ricochet = math.Clamp(Angle / 90, 0.05, 1) -- atleast 5% of energy is kept - HitRes.Loss = 1 - Ricochet + HitRes.Loss = 0.25 - Ricochet Energy.Kinetic = Energy.Kinetic * HitRes.Loss end end diff --git a/lua/acf/shared/guns/autocannon.lua b/lua/acf/shared/guns/autocannon.lua index 0b5cbc8b6..71b2fb809 100644 --- a/lua/acf/shared/guns/autocannon.lua +++ b/lua/acf/shared/guns/autocannon.lua @@ -4,7 +4,7 @@ ACF_defineGunClass("AC", { name = "Autocannon", desc = "Autocannons have a rather high weight and bulk for the ammo they fire, but they can fire it extremely fast.", muzzleflash = "30mm_muzzleflash_noscale", - rofmod = 0.35, + rofmod = 0.85, sound = "weapons/ACF_Gun/ac_fire4.wav", soundDistance = " ", soundNormal = " " @@ -19,7 +19,7 @@ ACF_defineGun("20mmAC", { --id gunclass = "AC", weight = 225, year = 1930, - rofmod = 1.8, + rofmod = 0.7, magsize = 100, magreload = 3, round = { @@ -36,7 +36,7 @@ ACF_defineGun("30mmAC", { caliber = 3.0, weight = 960, year = 1935, - rofmod = 1, + rofmod = 0.5, magsize = 75, magreload = 3, round = { @@ -53,7 +53,7 @@ ACF_defineGun("40mmAC", { caliber = 4.0, weight = 1500, year = 1940, - rofmod = 0.92, + rofmod = 0.48, magsize = 30, magreload = 3, round = { @@ -70,7 +70,7 @@ ACF_defineGun("50mmAC", { caliber = 5.0, weight = 2130, year = 1965, - rofmod = 0.9, + rofmod = 0.4, magsize = 20, magreload = 3, round = { diff --git a/lua/acf/shared/guns/cannon.lua b/lua/acf/shared/guns/cannon.lua index ef2406fda..2c27cfe9e 100644 --- a/lua/acf/shared/guns/cannon.lua +++ b/lua/acf/shared/guns/cannon.lua @@ -4,7 +4,7 @@ ACF_defineGunClass("C", { name = "Cannon", desc = "High velocity guns that can fire very powerful ammunition, but are rather slow to reload.", muzzleflash = "120mm_muzzleflash_noscale", - rofmod = 1.5, + rofmod = 2, sound = "weapons/ACF_Gun/cannon_new.wav", soundDistance = "Cannon.Fire", soundNormal = " " @@ -55,6 +55,22 @@ ACF_defineGun("75mmC", { propweight = 3.8 } } ) + + +ACF_defineGun("85mmC", { + name = "85mm Cannon", + desc = "You can stop asking, we've got it now.", + model = "models/tankgun/tankgun_85mm.mdl", + gunclass = "C", + caliber = 8.5, + weight = 2085, + year = 1944, + round = { + maxlength = 85.5, + propweight = 6.65 + } +} ) + ACF_defineGun("100mmC", { name = "100mm Cannon", diff --git a/lua/acf/shared/guns/howitzer.lua b/lua/acf/shared/guns/howitzer.lua index 39dcccd20..870355c9e 100644 --- a/lua/acf/shared/guns/howitzer.lua +++ b/lua/acf/shared/guns/howitzer.lua @@ -4,7 +4,7 @@ ACF_defineGunClass("HW", { name = "Howitzer", desc = "Howitzers are limited to rather mediocre muzzle velocities, but can fire extremely heavy projectiles with large useful payload capacities.", muzzleflash = "120mm_muzzleflash_noscale", - rofmod = 1.3, + rofmod = 1.8, sound = "weapons/ACF_Gun/howitzer_new2.wav", soundDistance = "Howitzer.Fire", soundNormal = " " diff --git a/lua/acf/shared/guns/machinegun.lua b/lua/acf/shared/guns/machinegun.lua index cfef811d0..5dfb1b106 100644 --- a/lua/acf/shared/guns/machinegun.lua +++ b/lua/acf/shared/guns/machinegun.lua @@ -38,7 +38,7 @@ ACF_defineGun("12.7mmMG", { caliber = 1.27, weight = 30, year = 1910, - rofmod = 0.766, + rofmod = 1, --0.766 magsize = 150, magreload = 6, round = { @@ -56,7 +56,7 @@ ACF_defineGun("14.5mmMG", { caliber = 1.45, weight = 45, year = 1932, - rofmod = 0.72, + rofmod = 1, --0.722 magsize = 90, magreload = 5, round = { @@ -73,7 +73,7 @@ ACF_defineGun("20mmMG", { caliber = 2.0, weight = 95, year = 1935, - rofmod = 0.55, + rofmod = 0.3, magsize = 50, magreload = 4, round = { diff --git a/lua/acf/shared/guns/mortar.lua b/lua/acf/shared/guns/mortar.lua index bb4c0c764..463b2b951 100644 --- a/lua/acf/shared/guns/mortar.lua +++ b/lua/acf/shared/guns/mortar.lua @@ -4,7 +4,7 @@ ACF_defineGunClass("MO", { name = "Mortar", desc = "Mortars are able to fire shells with usefull payloads from a light weight gun, at the price of limited velocities.", muzzleflash = "40mm_muzzleflash_noscale", - rofmod = 2, + rofmod = 2.5, sound = "weapons/ACF_Gun/mortar_new.wav", soundDistance = "Mortar.Fire", soundNormal = " " diff --git a/lua/acf/shared/guns/semiauto.lua b/lua/acf/shared/guns/semiauto.lua index 35683ea8d..396fcbd7a 100644 --- a/lua/acf/shared/guns/semiauto.lua +++ b/lua/acf/shared/guns/semiauto.lua @@ -4,7 +4,7 @@ ACF_defineGunClass("SA", { name = "Semiautomatic Cannon", desc = "Semiautomatic cannons offer better payloads than autocannons and less weight at the cost of rate of fire.", muzzleflash = "30mm_muzzleflash_noscale", - rofmod = 0.5, + rofmod = 0.85, sound = "acf_extra/tankfx/gnomefather/25mm1.wav", soundDistance = " ", soundNormal = " " @@ -19,7 +19,7 @@ ACF_defineGun("25mmSA", { --id caliber = 2.5, weight = 200, year = 1935, - rofmod = 1.224, + rofmod = 1, magsize = 5, magreload = 2, round = { @@ -36,7 +36,7 @@ ACF_defineGun("37mmSA", { caliber = 3.7, weight = 540, year = 1940, - rofmod = 1.063, + rofmod = 0.7, magsize = 5, magreload = 3.5, round = { @@ -53,7 +53,7 @@ ACF_defineGun("45mmSA", { caliber = 4.5, weight = 870, year = 1965, - rofmod = 1, + rofmod = 0.72, magsize = 5, magreload = 4, round = { @@ -70,7 +70,7 @@ ACF_defineGun("57mmSA", { caliber = 5.7, weight = 1560, year = 1965, - rofmod = 1, + rofmod = 0.8, magsize = 5, magreload = 4.5, round = { @@ -87,7 +87,7 @@ ACF_defineGun("76mmSA", { caliber = 7.62, weight = 2990, year = 1984, - rofmod = 1, + rofmod = 0.85, magsize = 5, magreload = 5, round = { diff --git a/lua/acf/shared/guns/shortcannon.lua b/lua/acf/shared/guns/shortcannon.lua index ce994be77..19086fa24 100644 --- a/lua/acf/shared/guns/shortcannon.lua +++ b/lua/acf/shared/guns/shortcannon.lua @@ -4,7 +4,7 @@ ACF_defineGunClass("SC", { name = "Short-Barrel Cannon", desc = "Short cannons trade muzzle velocity and accuracy for lighter weight and smaller size, with more penetration than howitzers and lighter than cannons.", muzzleflash = "120mm_muzzleflash_noscale", - rofmod = 1.3, + rofmod = 1.7, sound = "weapons/ACF_Gun/cannon_new.wav", soundDistance = "Cannon.Fire", soundNormal = " " @@ -34,6 +34,7 @@ ACF_defineGun("50mmSC", { gunclass = "SC", caliber = 5.0, weight = 330, + rofmod = 1.4, year = 1915, sound = "weapons/ACF_Gun/ac_fire4.wav", round = { @@ -56,6 +57,21 @@ ACF_defineGun("75mmSC", { } } ) +ACF_defineGun("85mmSC", { + name = "85mm Short Cannon", + desc = "Like the 85mm Cannon except shorter and mildly angrier.", + model = "models/tankgun/tankgun_short_85mm.mdl", + gunclass = "SC", + caliber = 8.5, + weight = 1250, + year = 1942, + round = { + maxlength = 84.5, + propweight = 3.25 + } +} ) + + ACF_defineGun("100mmSC", { name = "100mm Short Cannon", desc = "The 100mm is an effective infantry-support or antitank weapon, with a lot of uses and surprising lethality.", diff --git a/lua/acf/shared/guns/smoothbore.lua b/lua/acf/shared/guns/smoothbore.lua new file mode 100644 index 000000000..38dd7b880 --- /dev/null +++ b/lua/acf/shared/guns/smoothbore.lua @@ -0,0 +1,72 @@ +--define the class +ACF_defineGunClass("SB", { + spread = 0.08, + name = "Smoothbore Cannon", + desc = "More modern smoothbore cannons that can only fire munitions that do not rely on spinning for accuracy.", + muzzleflash = "120mm_muzzleflash_noscale", + rofmod = 1.72, + sound = "weapons/ACF_Gun/cannon_new.wav", + soundDistance = "Cannon.Fire", + soundNormal = " " +} ) + +--add a gun to the class + + +ACF_defineGun("105mmSB", { + name = "105mm Smoothbore Cannon", + desc = "The 105mm was a benchmark for the early cold war period, and has great muzzle velocity and hitting power, while still boasting a respectable, if small, payload.", + model = "models/tankgun_old/tankgun_100mm.mdl", + gunclass = "SB", + caliber = 10.5, + weight = 3550, + year = 1970, + round = { + maxlength = 93+8, + propweight = 9 + } +} ) + +ACF_defineGun("120mmSB", { + name = "120mm Smoothbore Cannon", + desc = "Often found in MBTs, the 120mm shreds lighter armor with utter impunity, and is formidable against even the big boys.", + model = "models/tankgun_old/tankgun_120mm.mdl", + gunclass = "SB", + caliber = 12.0, + weight = 6000, + year = 1975, + round = { + maxlength = 110+13, + propweight = 18 + } +} ) + +ACF_defineGun("140mmSB", { + name = "140mm Smoothbore Cannon", + desc = "The 140mm fires a massive shell with enormous penetrative capability, but has a glacial reload speed and a very hefty weight.", + model = "models/tankgun_old/tankgun_140mm.mdl", + gunclass = "SB", + caliber = 14.0, + weight = 8980, + year = 1990, + round = { + maxlength = 127+18, + propweight = 28 + } +} ) + +--[[ +ACF_defineGun("170mmC", { + name = "170mm Cannon", + desc = "The 170mm fires a gigantic shell with ginormous penetrative capability, but has a glacial reload speed and an extremely hefty weight.", + model = "models/tankgun/tankgun_170mm.mdl", + gunclass = "C", + caliber = 17.0, + weight = 12350, + year = 1990, + round = { + maxlength = 154, + propweight = 34 + } +} ) +]]-- diff --git a/lua/acf/shared/rounds/roundap.lua b/lua/acf/shared/rounds/roundap.lua index 6ce5562b2..fa68441c2 100644 --- a/lua/acf/shared/rounds/roundap.lua +++ b/lua/acf/shared/rounds/roundap.lua @@ -1,7 +1,7 @@ AddCSLuaFile() -ACF.AmmoBlacklist.AP = { "MO", "SL" } +ACF.AmmoBlacklist.AP = { "MO", "SL", "SB" } local Round = {} diff --git a/lua/acf/shared/rounds/roundapcr.lua b/lua/acf/shared/rounds/roundapcr.lua new file mode 100644 index 000000000..eeaa6a51c --- /dev/null +++ b/lua/acf/shared/rounds/roundapcr.lua @@ -0,0 +1,266 @@ + +AddCSLuaFile() + +ACF.AmmoBlacklist.APCR = { "MO", "SL", "HW" , "C", "AC", "SA", "MG" , "SB", "RAC", "GL", "HMG", "AAM", "ARTY", "ASM", "BOMB", "GBU", "POD", "SAM", "UAR", "FFAR", "FGL" } + +local Round = {} +local PenMod = 1.5 + +Round.type = "Ammo" --Tells the spawn menu what entity to spawn +Round.name = "Armor Piercing, Composite Rigid (APCR)" --Human readable name +Round.model = "models/munitions/round_100mm_shot.mdl" --Shell flight model +Round.desc = "A hardened core munition designed for weapons in the 1940s. Short Cannons only." +Round.netid = 11 --Unique ammotype ID for network transmission + +function Round.create( Gun, BulletData ) + + ACF_CreateBullet( BulletData ) + +end + +-- Function to convert the player's slider data into the complete round data +function Round.convert( Crate, PlayerData ) + + local Data = {} + local ServerData = {} + local GUIData = {} + + + if not PlayerData.PropLength then PlayerData.PropLength = 0 end + if not PlayerData.ProjLength then PlayerData.ProjLength = 0 end + if not PlayerData.Data10 then PlayerData.Data10 = 0 end + + PlayerData, Data, ServerData, GUIData = ACF_RoundBaseGunpowder( PlayerData, Data, ServerData, GUIData ) + + Data.ProjMass = (Data.FrAera/5) * (Data.ProjLength*7.9/1000) --Volume of the projectile as a cylinder * density of steel + Data.ShovePower = 0.2 + Data.PenAera = (Data.FrAera^ACF.PenAreaMod)/3.1 + Data.DragCoef = ((Data.FrAera/10000)/Data.ProjMass) + Data.LimitVel = 500 --Most efficient penetration speed in m/s + Data.KETransfert = 0.1 --Kinetic energy transfert to the target for movement purposes + Data.Ricochet = 60 --Base ricochet angle + Data.MuzzleVel = ACF_MuzzleVelocity( Data.PropMass, Data.ProjMass, Data.Caliber ) + + Data.BoomPower = Data.PropMass + + if SERVER then --Only the crates need this part + ServerData.Id = PlayerData.Id + ServerData.Type = PlayerData.Type + return table.Merge(Data,ServerData) + end + + if CLIENT then --Only tthe GUI needs this part + GUIData = table.Merge(GUIData, Round.getDisplayData(Data)) + return table.Merge(Data,GUIData) + end + +end + + +function Round.getDisplayData(Data) + local GUIData = {} + local Energy = ACF_Kinetic( Data.MuzzleVel*(39.37) , Data.ProjMass, Data.LimitVel ) + GUIData.MaxPen = (Energy.Penetration/(Data.PenAera))*ACF.KEtoRHA + return GUIData +end + + + +function Round.network( Crate, BulletData ) + + Crate:SetNWString( "AmmoType", "APCR" ) + Crate:SetNWString( "AmmoID", BulletData.Id ) + Crate:SetNWFloat( "Caliber", BulletData.Caliber ) + Crate:SetNWFloat( "ProjMass", BulletData.ProjMass ) + Crate:SetNWFloat( "PropMass", BulletData.PropMass ) + Crate:SetNWFloat( "DragCoef", BulletData.DragCoef ) + Crate:SetNWFloat( "MuzzleVel", BulletData.MuzzleVel ) + Crate:SetNWFloat( "Tracer", BulletData.Tracer ) + +end + +function Round.cratetxt( BulletData ) + + --local FrAera = BulletData.FrAera + local DData = Round.getDisplayData(BulletData) + + --fakeent.ACF.Armour = DData.MaxPen or 0 + --fakepen.Penetration = (DData.MaxPen * FrAera) / ACF.KEtoRHA + --local fakepen = ACF_Kinetic( BulletData.SlugMV*39.37 , BulletData.SlugMass, 9999999 ) + --local MaxHP = ACF_CalcDamage( fakeent , fakepen , FrAera , 0 ) + + --[[ + local TotalMass = BulletData.ProjMass + BulletData.PropMass + local MassUnit + + if TotalMass < 0.1 then + TotalMass = TotalMass * 1000 + MassUnit = " g" + else + MassUnit = " kg" + end + ]]-- + + local str = + { + --"Cartridge Mass: ", math.Round(TotalMass, 2), MassUnit, "\n", + "Muzzle Velocity: ", math.Round(BulletData.MuzzleVel, 1), " m/s\n", + "Max Penetration: ", math.floor(DData.MaxPen), " mm" + --"Max Pen. Damage: ", math.Round(MaxHP.Damage, 1), " HP\n", + } + + return table.concat(str) + +end + +function Round.propimpact( Index, Bullet, Target, HitNormal, HitPos, Bone ) + + if ACF_Check( Target ) then + + local Speed = Bullet.Flight:Length() / ACF.VelScale + local Energy = ACF_Kinetic( Speed , Bullet.ProjMass, Bullet.LimitVel ) + local HitRes = ACF_RoundImpact( Bullet, Speed, Energy, Target, HitPos, HitNormal , Bone ) + + if HitRes.Overkill > 0 then + table.insert( Bullet.Filter , Target ) --"Penetrate" (Ingoring the prop for the retry trace) + ACF_Spall( HitPos , Bullet.Flight , Bullet.Filter , Energy.Kinetic*HitRes.Loss , Bullet.Caliber , Target.ACF.Armour , Bullet.Owner ) --Do some spalling + Bullet.Flight = Bullet.Flight:GetNormalized() * (Energy.Kinetic*(1-HitRes.Loss)*2000/Bullet.ProjMass)^0.5 * 39.37 + return "Penetrated" + elseif HitRes.Ricochet then + return "Ricochet" + else + return false + end + else + table.insert( Bullet.Filter , Target ) + return "Penetrated" end + +end + +function Round.worldimpact( Index, Bullet, HitPos, HitNormal ) + + local Energy = ACF_Kinetic( Bullet.Flight:Length() / ACF.VelScale, Bullet.ProjMass, Bullet.LimitVel ) + local HitRes = ACF_PenetrateGround( Bullet, Energy, HitPos, HitNormal ) + if HitRes.Penetrated then + return "Penetrated" + elseif HitRes.Ricochet then + return "Ricochet" + else + return false + end + +end + +function Round.endflight( Index, Bullet, HitPos ) + + ACF_RemoveBullet( Index ) + +end + +-- Bullet stops here +function Round.endeffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Impact", Spall ) + +end + +-- Bullet penetrated something +function Round.pierceeffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Penetration", Spall ) + +end + +-- Bullet ricocheted off something +function Round.ricocheteffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Ricochet", Spall ) + +end + +function Round.guicreate( Panel, Table ) + + acfmenupanel:AmmoSelect( ACF.AmmoBlacklist.APCR ) + + acfmenupanel:CPanelText("BonusDisplay", "") + + acfmenupanel:CPanelText("Desc", "") --Description (Name, Desc) + acfmenupanel:CPanelText("LengthDisplay", "") --Total round length (Name, Desc) + + acfmenupanel:AmmoSlider("PropLength",0,0,1000,3, "Propellant Length", "") --Propellant Length Slider (Name, Value, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("ProjLength",0,0,1000,3, "Penetrator Length", "") --Projectile Length Slider (Name, Value, Min, Max, Decimals, Title, Desc) + + acfmenupanel:AmmoCheckbox("Tracer", "Tracer", "") --Tracer checkbox (Name, Title, Desc) + + acfmenupanel:CPanelText("VelocityDisplay", "") --Proj muzzle velocity (Name, Desc) + --acfmenupanel:CPanelText("RicoDisplay", "") --estimated rico chance + acfmenupanel:CPanelText("PenetrationDisplay", "") --Proj muzzle penetration (Name, Desc) + + Round.guiupdate( Panel, Table ) + +end + +function Round.guiupdate( Panel, Table ) + + local PlayerData = {} + PlayerData.Id = acfmenupanel.AmmoData.Data.id --AmmoSelect GUI + PlayerData.Type = "APCR" --Hardcoded, match ACFRoundTypes table index + PlayerData.PropLength = acfmenupanel.AmmoData.PropLength --PropLength slider + PlayerData.ProjLength = acfmenupanel.AmmoData.ProjLength --ProjLength slider + local Tracer = 0 + if acfmenupanel.AmmoData.Tracer then Tracer = 1 end + PlayerData.Data10 = Tracer --Tracer + + local Data = Round.convert( Panel, PlayerData ) + + RunConsoleCommand( "acfmenu_data1", acfmenupanel.AmmoData.Data.id ) + RunConsoleCommand( "acfmenu_data2", PlayerData.Type ) + RunConsoleCommand( "acfmenu_data3", Data.PropLength ) --For Gun ammo, Data3 should always be Propellant + RunConsoleCommand( "acfmenu_data4", Data.ProjLength ) --And Data4 total round mass + RunConsoleCommand( "acfmenu_data10", Data.Tracer ) + + local vol = ACF.Weapons.Ammo[acfmenupanel.AmmoData["Id"]].volume + local Cap, CapMul, RoFMul = ACF_CalcCrateStats( vol, Data.RoundVolume ) + + acfmenupanel:CPanelText("BonusDisplay", "Crate info: +"..(math.Round((CapMul-1)*100,1)).."% capacity, +"..(math.Round((RoFMul-1)*-100,1)).."% RoF\nContains "..Cap.." rounds") + + acfmenupanel:AmmoSlider("PropLength",Data.PropLength,Data.MinPropLength,Data.MaxTotalLength,3, "Propellant Length", "Propellant Mass : "..(math.floor(Data.PropMass*1000)).." g" ) --Propellant Length Slider (Name, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("ProjLength",Data.ProjLength,Data.MinProjLength,Data.MaxTotalLength,3, "Penetrator Length", "Projectile Mass : "..(math.floor(Data.ProjMass*1000)).." g") --Projectile Length Slider (Name, Min, Max, Decimals, Title, Desc) + + acfmenupanel:AmmoCheckbox("Tracer", "Tracer : "..(math.floor(Data.Tracer*10)/10).."cm\n", "" ) --Tracer checkbox (Name, Title, Desc) + + acfmenupanel:CPanelText("Desc", ACF.RoundTypes[PlayerData.Type].desc) --Description (Name, Desc) + acfmenupanel:CPanelText("LengthDisplay", "Round Length : "..(math.floor((Data.PropLength+Data.ProjLength+Data.Tracer)*100)/100).."/"..(Data.MaxTotalLength).." cm") --Total round length (Name, Desc) + acfmenupanel:CPanelText("VelocityDisplay", "Muzzle Velocity : "..math.floor(Data.MuzzleVel*ACF.VelScale).." m\\s") --Proj muzzle velocity (Name, Desc) + + --local RicoAngs = ACF_RicoProbability( Data.Ricochet, Data.MuzzleVel*ACF.VelScale ) + --acfmenupanel:CPanelText("RicoDisplay", "Ricochet probability vs impact angle:\n".." 0% @ "..RicoAngs.Min.." degrees\n 50% @ "..RicoAngs.Mean.." degrees\n100% @ "..RicoAngs.Max.." degrees") + + local R1V, R1P = ACF_PenRanging( Data.MuzzleVel, Data.DragCoef, Data.ProjMass, Data.PenAera, Data.LimitVel, 300 ) + local R2V, R2P = ACF_PenRanging( Data.MuzzleVel, Data.DragCoef, Data.ProjMass, Data.PenAera, Data.LimitVel, 800 ) + + acfmenupanel:CPanelText("PenetrationDisplay", "Maximum Penetration : "..math.floor(Data.MaxPen).." mm RHA\n\n300m pen: "..math.Round(R1P,0).."mm @ "..math.Round(R1V,0).." m\\s\n800m pen: "..math.Round(R2P,0).."mm @ "..math.Round(R2V,0).." m\\s\n\nThe range data is an approximation and may not be entirely accurate.") --Proj muzzle penetration (Name, Desc) + + +end + +list.Set( "ACFRoundTypes", "APCR", Round ) --Set the round properties +list.Set( "ACFIdRounds", Round.netid, "APCR" ) --Index must equal the ID entry in the table above, Data must equal the index of the table above diff --git a/lua/acf/shared/rounds/roundapds.lua b/lua/acf/shared/rounds/roundapds.lua new file mode 100644 index 000000000..342eef5d5 --- /dev/null +++ b/lua/acf/shared/rounds/roundapds.lua @@ -0,0 +1,266 @@ + +AddCSLuaFile() + +ACF.AmmoBlacklist.APDS = { "MO", "SL", "HW" , "SC", "MG" , "SB", "RAC", "GL", "HMG", "AAM", "ARTY", "ASM", "BOMB", "GBU", "POD", "SAM", "UAR", "FFAR", "FGL" } + +local Round = {} +local PenMod = 1.5 + +Round.type = "Ammo" --Tells the spawn menu what entity to spawn +Round.name = "Armor Piercing, Discarding Sabot (APDS)" --Human readable name +Round.model = "models/munitions/round_100mm_shot.mdl" --Shell flight model +Round.desc = "A subcaliber munition designed to trade damage for penetration. Loses energy quickly over distance." +Round.netid = 10 --Unique ammotype ID for network transmission + +function Round.create( Gun, BulletData ) + + ACF_CreateBullet( BulletData ) + +end + +-- Function to convert the player's slider data into the complete round data +function Round.convert( Crate, PlayerData ) + + local Data = {} + local ServerData = {} + local GUIData = {} + + + if not PlayerData.PropLength then PlayerData.PropLength = 0 end + if not PlayerData.ProjLength then PlayerData.ProjLength = 0 end + if not PlayerData.Data10 then PlayerData.Data10 = 0 end + + PlayerData, Data, ServerData, GUIData = ACF_RoundBaseGunpowder( PlayerData, Data, ServerData, GUIData ) + + Data.ProjMass = (Data.FrAera/5) * (Data.ProjLength*7.9/1000) --Volume of the projectile as a cylinder * density of steel + Data.ShovePower = 0.2 + Data.PenAera = (Data.FrAera^ACF.PenAreaMod)/3 + Data.DragCoef = ((Data.FrAera/20000)/Data.ProjMass) + Data.LimitVel = 800 --Most efficient penetration speed in m/s + Data.KETransfert = 0.1 --Kinetic energy transfert to the target for movement purposes + Data.Ricochet = 60 --Base ricochet angle + Data.MuzzleVel = ACF_MuzzleVelocity( Data.PropMass, Data.ProjMass, Data.Caliber ) + + Data.BoomPower = Data.PropMass + + if SERVER then --Only the crates need this part + ServerData.Id = PlayerData.Id + ServerData.Type = PlayerData.Type + return table.Merge(Data,ServerData) + end + + if CLIENT then --Only tthe GUI needs this part + GUIData = table.Merge(GUIData, Round.getDisplayData(Data)) + return table.Merge(Data,GUIData) + end + +end + + +function Round.getDisplayData(Data) + local GUIData = {} + local Energy = ACF_Kinetic( Data.MuzzleVel*(39.37) , Data.ProjMass, Data.LimitVel ) + GUIData.MaxPen = (Energy.Penetration/(Data.PenAera))*ACF.KEtoRHA + return GUIData +end + + + +function Round.network( Crate, BulletData ) + + Crate:SetNWString( "AmmoType", "APDS" ) + Crate:SetNWString( "AmmoID", BulletData.Id ) + Crate:SetNWFloat( "Caliber", BulletData.Caliber ) + Crate:SetNWFloat( "ProjMass", BulletData.ProjMass ) + Crate:SetNWFloat( "PropMass", BulletData.PropMass ) + Crate:SetNWFloat( "DragCoef", BulletData.DragCoef ) + Crate:SetNWFloat( "MuzzleVel", BulletData.MuzzleVel ) + Crate:SetNWFloat( "Tracer", BulletData.Tracer ) + +end + +function Round.cratetxt( BulletData ) + + --local FrAera = BulletData.FrAera + local DData = Round.getDisplayData(BulletData) + + --fakeent.ACF.Armour = DData.MaxPen or 0 + --fakepen.Penetration = (DData.MaxPen * FrAera) / ACF.KEtoRHA + --local fakepen = ACF_Kinetic( BulletData.SlugMV*39.37 , BulletData.SlugMass, 9999999 ) + --local MaxHP = ACF_CalcDamage( fakeent , fakepen , FrAera , 0 ) + + --[[ + local TotalMass = BulletData.ProjMass + BulletData.PropMass + local MassUnit + + if TotalMass < 0.1 then + TotalMass = TotalMass * 1000 + MassUnit = " g" + else + MassUnit = " kg" + end + ]]-- + + local str = + { + --"Cartridge Mass: ", math.Round(TotalMass, 2), MassUnit, "\n", + "Muzzle Velocity: ", math.Round(BulletData.MuzzleVel, 1), " m/s\n", + "Max Penetration: ", math.floor(DData.MaxPen), " mm" + --"Max Pen. Damage: ", math.Round(MaxHP.Damage, 1), " HP\n", + } + + return table.concat(str) + +end + +function Round.propimpact( Index, Bullet, Target, HitNormal, HitPos, Bone ) + + if ACF_Check( Target ) then + + local Speed = Bullet.Flight:Length() / ACF.VelScale + local Energy = ACF_Kinetic( Speed , Bullet.ProjMass, Bullet.LimitVel ) + local HitRes = ACF_RoundImpact( Bullet, Speed, Energy, Target, HitPos, HitNormal , Bone ) + + if HitRes.Overkill > 0 then + table.insert( Bullet.Filter , Target ) --"Penetrate" (Ingoring the prop for the retry trace) + ACF_Spall( HitPos , Bullet.Flight , Bullet.Filter , Energy.Kinetic*HitRes.Loss , Bullet.Caliber , Target.ACF.Armour , Bullet.Owner ) --Do some spalling + Bullet.Flight = Bullet.Flight:GetNormalized() * (Energy.Kinetic*(1-HitRes.Loss)*2000/Bullet.ProjMass)^0.5 * 39.37 + return "Penetrated" + elseif HitRes.Ricochet then + return "Ricochet" + else + return false + end + else + table.insert( Bullet.Filter , Target ) + return "Penetrated" end + +end + +function Round.worldimpact( Index, Bullet, HitPos, HitNormal ) + + local Energy = ACF_Kinetic( Bullet.Flight:Length() / ACF.VelScale, Bullet.ProjMass, Bullet.LimitVel ) + local HitRes = ACF_PenetrateGround( Bullet, Energy, HitPos, HitNormal ) + if HitRes.Penetrated then + return "Penetrated" + elseif HitRes.Ricochet then + return "Ricochet" + else + return false + end + +end + +function Round.endflight( Index, Bullet, HitPos ) + + ACF_RemoveBullet( Index ) + +end + +-- Bullet stops here +function Round.endeffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Impact", Spall ) + +end + +-- Bullet penetrated something +function Round.pierceeffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Penetration", Spall ) + +end + +-- Bullet ricocheted off something +function Round.ricocheteffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Ricochet", Spall ) + +end + +function Round.guicreate( Panel, Table ) + + acfmenupanel:AmmoSelect( ACF.AmmoBlacklist.APDS ) + + acfmenupanel:CPanelText("BonusDisplay", "") + + acfmenupanel:CPanelText("Desc", "") --Description (Name, Desc) + acfmenupanel:CPanelText("LengthDisplay", "") --Total round length (Name, Desc) + + acfmenupanel:AmmoSlider("PropLength",0,0,1000,3, "Propellant Length", "") --Propellant Length Slider (Name, Value, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("ProjLength",0,0,1000,3, "Penetrator Length", "") --Projectile Length Slider (Name, Value, Min, Max, Decimals, Title, Desc) + + acfmenupanel:AmmoCheckbox("Tracer", "Tracer", "") --Tracer checkbox (Name, Title, Desc) + + acfmenupanel:CPanelText("VelocityDisplay", "") --Proj muzzle velocity (Name, Desc) + --acfmenupanel:CPanelText("RicoDisplay", "") --estimated rico chance + acfmenupanel:CPanelText("PenetrationDisplay", "") --Proj muzzle penetration (Name, Desc) + + Round.guiupdate( Panel, Table ) + +end + +function Round.guiupdate( Panel, Table ) + + local PlayerData = {} + PlayerData.Id = acfmenupanel.AmmoData.Data.id --AmmoSelect GUI + PlayerData.Type = "APDS" --Hardcoded, match ACFRoundTypes table index + PlayerData.PropLength = acfmenupanel.AmmoData.PropLength --PropLength slider + PlayerData.ProjLength = acfmenupanel.AmmoData.ProjLength --ProjLength slider + local Tracer = 0 + if acfmenupanel.AmmoData.Tracer then Tracer = 1 end + PlayerData.Data10 = Tracer --Tracer + + local Data = Round.convert( Panel, PlayerData ) + + RunConsoleCommand( "acfmenu_data1", acfmenupanel.AmmoData.Data.id ) + RunConsoleCommand( "acfmenu_data2", PlayerData.Type ) + RunConsoleCommand( "acfmenu_data3", Data.PropLength ) --For Gun ammo, Data3 should always be Propellant + RunConsoleCommand( "acfmenu_data4", Data.ProjLength ) --And Data4 total round mass + RunConsoleCommand( "acfmenu_data10", Data.Tracer ) + + local vol = ACF.Weapons.Ammo[acfmenupanel.AmmoData["Id"]].volume + local Cap, CapMul, RoFMul = ACF_CalcCrateStats( vol, Data.RoundVolume ) + + acfmenupanel:CPanelText("BonusDisplay", "Crate info: +"..(math.Round((CapMul-1)*100,1)).."% capacity, +"..(math.Round((RoFMul-1)*-100,1)).."% RoF\nContains "..Cap.." rounds") + + acfmenupanel:AmmoSlider("PropLength",Data.PropLength,Data.MinPropLength,Data.MaxTotalLength,3, "Propellant Length", "Propellant Mass : "..(math.floor(Data.PropMass*1000)).." g" ) --Propellant Length Slider (Name, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("ProjLength",Data.ProjLength,Data.MinProjLength,Data.MaxTotalLength,3, "Penetrator Length", "Projectile Mass : "..(math.floor(Data.ProjMass*1000)).." g") --Projectile Length Slider (Name, Min, Max, Decimals, Title, Desc) + + acfmenupanel:AmmoCheckbox("Tracer", "Tracer : "..(math.floor(Data.Tracer*10)/10).."cm\n", "" ) --Tracer checkbox (Name, Title, Desc) + + acfmenupanel:CPanelText("Desc", ACF.RoundTypes[PlayerData.Type].desc) --Description (Name, Desc) + acfmenupanel:CPanelText("LengthDisplay", "Round Length : "..(math.floor((Data.PropLength+Data.ProjLength+Data.Tracer)*100)/100).."/"..(Data.MaxTotalLength).." cm") --Total round length (Name, Desc) + acfmenupanel:CPanelText("VelocityDisplay", "Muzzle Velocity : "..math.floor(Data.MuzzleVel*ACF.VelScale).." m\\s") --Proj muzzle velocity (Name, Desc) + + --local RicoAngs = ACF_RicoProbability( Data.Ricochet, Data.MuzzleVel*ACF.VelScale ) + --acfmenupanel:CPanelText("RicoDisplay", "Ricochet probability vs impact angle:\n".." 0% @ "..RicoAngs.Min.." degrees\n 50% @ "..RicoAngs.Mean.." degrees\n100% @ "..RicoAngs.Max.." degrees") + + local R1V, R1P = ACF_PenRanging( Data.MuzzleVel, Data.DragCoef, Data.ProjMass, Data.PenAera, Data.LimitVel, 300 ) + local R2V, R2P = ACF_PenRanging( Data.MuzzleVel, Data.DragCoef, Data.ProjMass, Data.PenAera, Data.LimitVel, 800 ) + + acfmenupanel:CPanelText("PenetrationDisplay", "Maximum Penetration : "..math.floor(Data.MaxPen).." mm RHA\n\n300m pen: "..math.Round(R1P,0).."mm @ "..math.Round(R1V,0).." m\\s\n800m pen: "..math.Round(R2P,0).."mm @ "..math.Round(R2V,0).." m\\s\n\nThe range data is an approximation and may not be entirely accurate.") --Proj muzzle penetration (Name, Desc) + + +end + +list.Set( "ACFRoundTypes", "APDS", Round ) --Set the round properties +list.Set( "ACFIdRounds", Round.netid, "APDS" ) --Index must equal the ID entry in the table above, Data must equal the index of the table above diff --git a/lua/acf/shared/rounds/roundapfsds.lua b/lua/acf/shared/rounds/roundapfsds.lua new file mode 100644 index 000000000..467200e05 --- /dev/null +++ b/lua/acf/shared/rounds/roundapfsds.lua @@ -0,0 +1,266 @@ + +AddCSLuaFile() + +ACF.AmmoBlacklist.APFSDS = { "MO", "SL", "C" , "HW" , "AC", "SC" , "SA" , "MG" , "AL" , "RAC", "GL", "HMG", "AAM", "ARTY", "ASM", "BOMB", "GBU", "POD", "SAM", "UAR", "FFAR", "FGL" } + +local Round = {} +local PenMod = 1.5 + +Round.type = "Ammo" --Tells the spawn menu what entity to spawn +Round.name = "AP Fin Stabilized, Discarding Sabot (APFSDS)" --Human readable name +Round.model = "models/munitions/round_100mm_shot.mdl" --Shell flight model +Round.desc = "A fin stabilized sabot munition designed to trade damage for superior penetration and long range effectiveness." +Round.netid = 9 --Unique ammotype ID for network transmission + +function Round.create( Gun, BulletData ) + + ACF_CreateBullet( BulletData ) + +end + +-- Function to convert the player's slider data into the complete round data +function Round.convert( Crate, PlayerData ) + + local Data = {} + local ServerData = {} + local GUIData = {} + + + if not PlayerData.PropLength then PlayerData.PropLength = 0 end + if not PlayerData.ProjLength then PlayerData.ProjLength = 0 end + if not PlayerData.Data10 then PlayerData.Data10 = 0 end + + PlayerData, Data, ServerData, GUIData = ACF_RoundBaseGunpowder( PlayerData, Data, ServerData, GUIData ) + + Data.ProjMass = (Data.FrAera/22) * (Data.ProjLength*7.9/1000) --Volume of the projectile as a cylinder * density of steel + Data.ShovePower = 0.2 + Data.PenAera = (Data.FrAera^ACF.PenAreaMod)/8.5 + Data.DragCoef = ((Data.FrAera/60000)/Data.ProjMass) + Data.LimitVel = 1000 --Most efficient penetration speed in m/s + Data.KETransfert = 0.1 --Kinetic energy transfert to the target for movement purposes + Data.Ricochet = 60 --Base ricochet angle + Data.MuzzleVel = ACF_MuzzleVelocity( Data.PropMass, Data.ProjMass, Data.Caliber ) + + Data.BoomPower = Data.PropMass + + if SERVER then --Only the crates need this part + ServerData.Id = PlayerData.Id + ServerData.Type = PlayerData.Type + return table.Merge(Data,ServerData) + end + + if CLIENT then --Only tthe GUI needs this part + GUIData = table.Merge(GUIData, Round.getDisplayData(Data)) + return table.Merge(Data,GUIData) + end + +end + + +function Round.getDisplayData(Data) + local GUIData = {} + local Energy = ACF_Kinetic( Data.MuzzleVel*(39.37) , Data.ProjMass, Data.LimitVel ) + GUIData.MaxPen = (Energy.Penetration/(Data.PenAera))*ACF.KEtoRHA + return GUIData +end + + + +function Round.network( Crate, BulletData ) + + Crate:SetNWString( "AmmoType", "APFSDS" ) + Crate:SetNWString( "AmmoID", BulletData.Id ) + Crate:SetNWFloat( "Caliber", BulletData.Caliber ) + Crate:SetNWFloat( "ProjMass", BulletData.ProjMass ) + Crate:SetNWFloat( "PropMass", BulletData.PropMass ) + Crate:SetNWFloat( "DragCoef", BulletData.DragCoef ) + Crate:SetNWFloat( "MuzzleVel", BulletData.MuzzleVel ) + Crate:SetNWFloat( "Tracer", BulletData.Tracer ) + +end + +function Round.cratetxt( BulletData ) + + --local FrAera = BulletData.FrAera + local DData = Round.getDisplayData(BulletData) + + --fakeent.ACF.Armour = DData.MaxPen or 0 + --fakepen.Penetration = (DData.MaxPen * FrAera) / ACF.KEtoRHA + --local fakepen = ACF_Kinetic( BulletData.SlugMV*39.37 , BulletData.SlugMass, 9999999 ) + --local MaxHP = ACF_CalcDamage( fakeent , fakepen , FrAera , 0 ) + + --[[ + local TotalMass = BulletData.ProjMass + BulletData.PropMass + local MassUnit + + if TotalMass < 0.1 then + TotalMass = TotalMass * 1000 + MassUnit = " g" + else + MassUnit = " kg" + end + ]]-- + + local str = + { + --"Cartridge Mass: ", math.Round(TotalMass, 2), MassUnit, "\n", + "Muzzle Velocity: ", math.Round(BulletData.MuzzleVel, 1), " m/s\n", + "Max Penetration: ", math.floor(DData.MaxPen), " mm" + --"Max Pen. Damage: ", math.Round(MaxHP.Damage, 1), " HP\n", + } + + return table.concat(str) + +end + +function Round.propimpact( Index, Bullet, Target, HitNormal, HitPos, Bone ) + + if ACF_Check( Target ) then + + local Speed = Bullet.Flight:Length() / ACF.VelScale + local Energy = ACF_Kinetic( Speed , Bullet.ProjMass, Bullet.LimitVel ) + local HitRes = ACF_RoundImpact( Bullet, Speed, Energy, Target, HitPos, HitNormal , Bone ) + + if HitRes.Overkill > 0 then + table.insert( Bullet.Filter , Target ) --"Penetrate" (Ingoring the prop for the retry trace) + ACF_Spall( HitPos , Bullet.Flight , Bullet.Filter , Energy.Kinetic*HitRes.Loss , Bullet.Caliber , Target.ACF.Armour , Bullet.Owner ) --Do some spalling + Bullet.Flight = Bullet.Flight:GetNormalized() * (Energy.Kinetic*(1-HitRes.Loss)*2000/Bullet.ProjMass)^0.5 * 39.37 + return "Penetrated" + elseif HitRes.Ricochet then + return "Ricochet" + else + return false + end + else + table.insert( Bullet.Filter , Target ) + return "Penetrated" end + +end + +function Round.worldimpact( Index, Bullet, HitPos, HitNormal ) + + local Energy = ACF_Kinetic( Bullet.Flight:Length() / ACF.VelScale, Bullet.ProjMass, Bullet.LimitVel ) + local HitRes = ACF_PenetrateGround( Bullet, Energy, HitPos, HitNormal ) + if HitRes.Penetrated then + return "Penetrated" + elseif HitRes.Ricochet then + return "Ricochet" + else + return false + end + +end + +function Round.endflight( Index, Bullet, HitPos ) + + ACF_RemoveBullet( Index ) + +end + +-- Bullet stops here +function Round.endeffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Impact", Spall ) + +end + +-- Bullet penetrated something +function Round.pierceeffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Penetration", Spall ) + +end + +-- Bullet ricocheted off something +function Round.ricocheteffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Ricochet", Spall ) + +end + +function Round.guicreate( Panel, Table ) + + acfmenupanel:AmmoSelect( ACF.AmmoBlacklist.APFSDS ) + + acfmenupanel:CPanelText("BonusDisplay", "") + + acfmenupanel:CPanelText("Desc", "") --Description (Name, Desc) + acfmenupanel:CPanelText("LengthDisplay", "") --Total round length (Name, Desc) + + acfmenupanel:AmmoSlider("PropLength",0,0,1000,3, "Propellant Length", "") --Propellant Length Slider (Name, Value, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("ProjLength",0,0,1000,3, "Penetrator Length", "") --Projectile Length Slider (Name, Value, Min, Max, Decimals, Title, Desc) + + acfmenupanel:AmmoCheckbox("Tracer", "Tracer", "") --Tracer checkbox (Name, Title, Desc) + + acfmenupanel:CPanelText("VelocityDisplay", "") --Proj muzzle velocity (Name, Desc) + --acfmenupanel:CPanelText("RicoDisplay", "") --estimated rico chance + acfmenupanel:CPanelText("PenetrationDisplay", "") --Proj muzzle penetration (Name, Desc) + + Round.guiupdate( Panel, Table ) + +end + +function Round.guiupdate( Panel, Table ) + + local PlayerData = {} + PlayerData.Id = acfmenupanel.AmmoData.Data.id --AmmoSelect GUI + PlayerData.Type = "APFSDS" --Hardcoded, match ACFRoundTypes table index + PlayerData.PropLength = acfmenupanel.AmmoData.PropLength --PropLength slider + PlayerData.ProjLength = acfmenupanel.AmmoData.ProjLength --ProjLength slider + local Tracer = 0 + if acfmenupanel.AmmoData.Tracer then Tracer = 1 end + PlayerData.Data10 = Tracer --Tracer + + local Data = Round.convert( Panel, PlayerData ) + + RunConsoleCommand( "acfmenu_data1", acfmenupanel.AmmoData.Data.id ) + RunConsoleCommand( "acfmenu_data2", PlayerData.Type ) + RunConsoleCommand( "acfmenu_data3", Data.PropLength ) --For Gun ammo, Data3 should always be Propellant + RunConsoleCommand( "acfmenu_data4", Data.ProjLength ) --And Data4 total round mass + RunConsoleCommand( "acfmenu_data10", Data.Tracer ) + + local vol = ACF.Weapons.Ammo[acfmenupanel.AmmoData["Id"]].volume + local Cap, CapMul, RoFMul = ACF_CalcCrateStats( vol, Data.RoundVolume ) + + acfmenupanel:CPanelText("BonusDisplay", "Crate info: +"..(math.Round((CapMul-1)*100,1)).."% capacity, +"..(math.Round((RoFMul-1)*-100,1)).."% RoF\nContains "..Cap.." rounds") + + acfmenupanel:AmmoSlider("PropLength",Data.PropLength,Data.MinPropLength,Data.MaxTotalLength-80,3, "Propellant Length", "Propellant Mass : "..(math.floor(Data.PropMass*1000)).." g" ) --Propellant Length Slider (Name, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("ProjLength",Data.ProjLength,Data.MinProjLength,Data.MaxTotalLength,3, "Penetrator Length", "Projectile Mass : "..(math.floor(Data.ProjMass*1000)).." g") --Projectile Length Slider (Name, Min, Max, Decimals, Title, Desc) + + acfmenupanel:AmmoCheckbox("Tracer", "Tracer : "..(math.floor(Data.Tracer*10)/10).."cm\n", "" ) --Tracer checkbox (Name, Title, Desc) + + acfmenupanel:CPanelText("Desc", ACF.RoundTypes[PlayerData.Type].desc) --Description (Name, Desc) + acfmenupanel:CPanelText("LengthDisplay", "Round Length : "..(math.floor((Data.PropLength+Data.ProjLength+Data.Tracer)*100)/100).."/"..(Data.MaxTotalLength).." cm") --Total round length (Name, Desc) + acfmenupanel:CPanelText("VelocityDisplay", "Muzzle Velocity : "..math.floor(Data.MuzzleVel*ACF.VelScale).." m\\s") --Proj muzzle velocity (Name, Desc) + + --local RicoAngs = ACF_RicoProbability( Data.Ricochet, Data.MuzzleVel*ACF.VelScale ) + --acfmenupanel:CPanelText("RicoDisplay", "Ricochet probability vs impact angle:\n".." 0% @ "..RicoAngs.Min.." degrees\n 50% @ "..RicoAngs.Mean.." degrees\n100% @ "..RicoAngs.Max.." degrees") + + local R1V, R1P = ACF_PenRanging( Data.MuzzleVel, Data.DragCoef, Data.ProjMass, Data.PenAera, Data.LimitVel, 300 ) + local R2V, R2P = ACF_PenRanging( Data.MuzzleVel, Data.DragCoef, Data.ProjMass, Data.PenAera, Data.LimitVel, 800 ) + + acfmenupanel:CPanelText("PenetrationDisplay", "Maximum Penetration : "..math.floor(Data.MaxPen).." mm RHA\n\n300m pen: "..math.Round(R1P,0).."mm @ "..math.Round(R1V,0).." m\\s\n800m pen: "..math.Round(R2P,0).."mm @ "..math.Round(R2V,0).." m\\s\n\nThe range data is an approximation and may not be entirely accurate.") --Proj muzzle penetration (Name, Desc) + + +end + +list.Set( "ACFRoundTypes", "APFSDS", Round ) --Set the round properties +list.Set( "ACFIdRounds", Round.netid, "APFSDS" ) --Index must equal the ID entry in the table above, Data must equal the index of the table above diff --git a/lua/acf/shared/rounds/roundheat.lua b/lua/acf/shared/rounds/roundheat.lua index 2a0623d23..113da50b1 100644 --- a/lua/acf/shared/rounds/roundheat.lua +++ b/lua/acf/shared/rounds/roundheat.lua @@ -1,7 +1,7 @@ AddCSLuaFile() -ACF.AmmoBlacklist.HEAT = { "MG", "HMG", "RAC", "AC", "SL" } +ACF.AmmoBlacklist.HEAT = { "MG", "HMG", "RAC", "AC", "SL" , "SB" } local Round = {} diff --git a/lua/acf/shared/rounds/roundheatfs.lua b/lua/acf/shared/rounds/roundheatfs.lua new file mode 100644 index 000000000..ecc2d4682 --- /dev/null +++ b/lua/acf/shared/rounds/roundheatfs.lua @@ -0,0 +1,440 @@ + +AddCSLuaFile() + +ACF.AmmoBlacklist.HEATFS = { "MO", "SL", "C" , "HW" , "AC", "SC" , "SA" , "MG" , "AL" , "RAC", "GL", "HMG", "AAM", "ARTY", "ASM", "BOMB", "GBU", "POD", "SAM", "UAR", "FFAR", "FGL" } + + +local Round = {} + +Round.type = "Ammo" --Tells the spawn menu what entity to spawn +Round.name = "High Explosive Anti-Tank Fin Stabilized (HEAT-FS)" --Human readable name +Round.model = "models/munitions/round_100mm_shot.mdl" --Shell flight model +Round.desc = "HEAT, but fin stabilized with a fixed minimum propellant charge. Smoothbores only." +Round.netid = 11 --Unique ammotype ID for network transmission + +function Round.create( Gun, BulletData ) + + ACF_CreateBullet( BulletData ) + +end + +function Round.ConeCalc( ConeAngle, Radius, Length ) + + local ConeLength = math.tan(math.rad(ConeAngle))*Radius + local ConeAera = 3.1416 * Radius * (Radius^2 + ConeLength^2)^0.5 + local ConeVol = (3.1416 * Radius^2 * ConeLength)/3 + + return ConeLength, ConeAera, ConeVol + +end + +-- calculates conversion of filler from powering HEAT jet to raw HE based on crush vel +-- above a threshold vel, HEAT jet doesn't have time to form properly, converting to raw HE proportionally +-- Vel needs to be in m/s (gmu*0.0254) +function Round.CrushCalc( Vel, FillerMass ) + local Crushed = math.Clamp( (Vel - ACF.HEATMinCrush) / (ACF.HEATMaxCrush - ACF.HEATMinCrush), 0,1) + local HE_Filler = Lerp(Crushed, FillerMass*ACF.HEATBoomConvert, FillerMass) + local HEAT_Filler = Lerp(Crushed, FillerMass, 0) + --local HE_Filler = FillerMass * ACF.HEATBoomConvert + Crushed * FillerMass * (1-ACF.HEATBoomConvert) + --local HEAT_Filler = (1-Crushed) * FillerMass + return Crushed, HEAT_Filler, HE_Filler +end + +-- coneang now required for slug recalculation at detonation, defaults to 55 if not present +function Round.CalcSlugMV( Data, HEATFillerMass ) + --keep fillermass/2 so that penetrator stays the same. + return ( HEATFillerMass/2 * ACF.HEPower * math.sin(math.rad(10+(Data.ConeAng or 55))/2) /Data.SlugMass)^ACF.HEATMVScale +end + +-- Function to convert the player's slider data into the complete round data +function Round.convert( Crate, PlayerData ) + + local Data = {} + local ServerData = {} + local GUIData = {} + + if not PlayerData.PropLength then PlayerData.PropLength = Data.Caliber*3.75 end + if not PlayerData.ProjLength then PlayerData.ProjLength = 0 end + --PlayerData.PropLength = math.min(PlayerData.PropLength, PlayerData.ProjLength *2) + PlayerData.Data5 = math.max(PlayerData.Data5 or 0, 0) + if not PlayerData.Data6 then PlayerData.Data6 = 0 end + if not PlayerData.Data7 then PlayerData.Data7 = 0 end + if not PlayerData.Data10 then PlayerData.Data10 = 0 end + + PlayerData, Data, ServerData, GUIData = ACF_RoundBaseGunpowder( PlayerData, Data, ServerData, GUIData ) + + local ConeThick = Data.Caliber/50 + local ConeLength = 0 + local ConeAera = 0 + local AirVol = 0 + ConeLength, ConeAera, AirVol = Round.ConeCalc( PlayerData.Data6, Data.Caliber/2, PlayerData.ProjLength ) + Data.ProjMass = math.max(GUIData.ProjVolume-PlayerData.Data5,0)*7.9/1000 + math.min(PlayerData.Data5,GUIData.ProjVolume)*ACF.HEDensity/1000 + ConeAera*ConeThick*7.9/1000 --Volume of the projectile as a cylinder - Volume of the filler - Volume of the crush cone * density of steel + Volume of the filler * density of TNT + Aera of the cone * thickness * density of steel + Data.MuzzleVel = ACF_MuzzleVelocity( Data.PropMass, Data.ProjMass, Data.Caliber ) + local Energy = ACF_Kinetic( Data.MuzzleVel*39.37 , Data.ProjMass, Data.LimitVel ) + + local MaxVol = 0 + local MaxLength = 0 + local MaxRadius = 0 + MaxVol, MaxLength, MaxRadius = ACF_RoundShellCapacity( Energy.Momentum, Data.FrAera, Data.Caliber, Data.ProjLength ) + + GUIData.MinConeAng = 0 + GUIData.MaxConeAng = math.deg( math.atan((Data.ProjLength - ConeThick )/(Data.Caliber/2)) ) + Data.ConeAng = math.Clamp(PlayerData.Data6*1, GUIData.MinConeAng, GUIData.MaxConeAng) + ConeLength, ConeAera, AirVol = Round.ConeCalc( Data.ConeAng, Data.Caliber/2, Data.ProjLength ) + local ConeVol = ConeAera * ConeThick + + GUIData.MinFillerVol = 0 + GUIData.MaxFillerVol = math.max(MaxVol - AirVol - ConeVol,GUIData.MinFillerVol) + GUIData.FillerVol = math.Clamp(PlayerData.Data5*1,GUIData.MinFillerVol,GUIData.MaxFillerVol) + + -- fillermass used for shell mass calcs + -- heatfillermass is how much fillermass is used to power heat jet + -- boomfillermass is how much fillermass creates HE damage on detonation. technically get 1/3 extra fillermass free as HE with no crushing, but screw trying to rebalance heat pen to properly use 1/3 of filler for HE and 2/3 for jet + -- distribution of heat and boom fillermass is calculated at detonation, or for GUI stuff + + Data.FillerMass = GUIData.FillerVol * ACF.HEDensity/1450 + Data.ProjMass = math.max(GUIData.ProjVolume-GUIData.FillerVol- AirVol-ConeVol,0)*7.9/1000 + Data.FillerMass + ConeVol*7.9/1000 + Data.MuzzleVel = ACF_MuzzleVelocity( Data.PropMass, Data.ProjMass, Data.Caliber ) + local Energy = ACF_Kinetic( Data.MuzzleVel*39.37 , Data.ProjMass, Data.LimitVel ) + + --Let's calculate the actual HEAT slug + Data.SlugMass = ConeVol*7.9/1000 + local Rad = math.rad(Data.ConeAng/2) + Data.SlugCaliber = Data.Caliber - Data.Caliber * (math.sin(Rad)*0.5+math.cos(Rad)*1.5)/2 + + local SlugFrAera = 3.1416 * (Data.SlugCaliber/2)^2 + Data.SlugPenAera = (SlugFrAera^ACF.PenAreaMod)/1.25 + Data.SlugDragCoef = ((SlugFrAera/10000)/Data.SlugMass) + Data.SlugRicochet = 500 --Base ricochet angle (The HEAT slug shouldn't ricochet at all) + + -- these are only for compatibility with other stuff. it's recalculated when the round is detonated + local crush, heatfiller, boomfiller = Round.CrushCalc(Data.MuzzleVel, Data.FillerMass) + Data.BoomFillerMass = boomfiller + Data.SlugMV = Round.CalcSlugMV( Data, heatfiller ) + + --Random bullshit left + Data.CasingMass = Data.ProjMass - Data.FillerMass - ConeVol*7.9/1000 + Data.ShovePower = 0.1 + Data.PenAera = (Data.FrAera^ACF.PenAreaMod) + Data.DragCoef = ((Data.FrAera/10000)/Data.ProjMass) + Data.LimitVel = 100 --Most efficient penetration speed in m/s + Data.KETransfert = 0.1 --Kinetic energy transfert to the target for movement purposes + Data.Ricochet = 60 --Base ricochet angle + Data.DetonatorAngle = 75 + + Data.Detonated = false + Data.NotFirstPen = false + Data.BoomPower = Data.PropMass + Data.FillerMass -- for when a crate is cooking off + + if SERVER then --Only the crates need this part + ServerData.Id = PlayerData.Id + ServerData.Type = PlayerData.Type + return table.Merge(Data,ServerData) + end + + if CLIENT then --Only the GUI needs this part + GUIData = table.Merge(GUIData, Round.getDisplayData(Data)) + return table.Merge(Data, GUIData) + end + +end + + +function Round.getDisplayData(Data) + local GUIData = {} + + -- these are only GUI info, it's recalculated when the round is detonated since it's vel dependent + GUIData.Crushed, GUIData.HEATFillerMass, GUIData.BoomFillerMass = Round.CrushCalc(Data.MuzzleVel, Data.FillerMass) + GUIData.SlugMV = Round.CalcSlugMV( Data, GUIData.HEATFillerMass ) * (Data.SlugPenMul or 1) -- slugpenmul is a missiles thing + GUIData.SlugMassUsed = Data.SlugMass * (1-GUIData.Crushed) + + local SlugEnergy = ACF_Kinetic( Data.MuzzleVel*39.37 + GUIData.SlugMV*39.37 ,GUIData.SlugMassUsed , 999999 ) + GUIData.MaxPen = (SlugEnergy.Penetration/Data.SlugPenAera)*ACF.KEtoRHA + + GUIData.TotalFragMass = Data.CasingMass + Data.SlugMass * GUIData.Crushed + GUIData.BlastRadius = (GUIData.BoomFillerMass)^0.33*8--*39.37 + GUIData.Fragments = math.max(math.floor((GUIData.BoomFillerMass/GUIData.TotalFragMass)*ACF.HEFrag),2) + GUIData.FragMass = GUIData.TotalFragMass / GUIData.Fragments + GUIData.FragVel = (GUIData.BoomFillerMass*ACF.HEPower*1000/GUIData.TotalFragMass)^0.5 + + return GUIData +end + + +function Round.network( Crate, BulletData ) + + Crate:SetNWString( "AmmoType", "HEATFS" ) + Crate:SetNWString( "AmmoID", BulletData.Id ) + Crate:SetNWFloat( "Caliber", BulletData.Caliber ) + Crate:SetNWFloat( "ProjMass", BulletData.ProjMass ) + Crate:SetNWFloat( "FillerMass", BulletData.FillerMass ) + Crate:SetNWFloat( "PropMass", BulletData.PropMass ) + Crate:SetNWFloat( "DragCoef", BulletData.DragCoef ) + Crate:SetNWFloat( "SlugMass", BulletData.SlugMass ) + Crate:SetNWFloat( "SlugCaliber", BulletData.SlugCaliber ) + Crate:SetNWFloat( "SlugDragCoef", BulletData.SlugDragCoef ) + Crate:SetNWFloat( "MuzzleVel", BulletData.MuzzleVel ) + Crate:SetNWFloat( "Tracer", BulletData.Tracer ) + +end + + +--local fakeent = {ACF = {Armour = 0}} +--local fakepen = {Penetration = 999999999} +function Round.cratetxt( BulletData, builtFullData ) + + local DData = Round.getDisplayData(BulletData) + + local str = + { + "Muzzle Velocity: ", math.Round(BulletData.MuzzleVel, 1), " m/s\n", + "Max Penetration: ", math.floor(DData.MaxPen), " mm\n", + "Blast Radius: ", math.Round(DData.BlastRadius, 1), " m\n", + "Blast Energy: ", math.floor((DData.BoomFillerMass) * ACF.HEPower), " KJ" + } + + return table.concat(str) + +end + +function Round.detonate( Index, Bullet, HitPos, HitNormal ) + + local Crushed, HEATFillerMass, BoomFillerMass = Round.CrushCalc(Bullet.Flight:Length()*0.0254, Bullet.FillerMass) + + ACF_HE( HitPos - Bullet.Flight:GetNormalized()*3, HitNormal, BoomFillerMass, Bullet.CasingMass + Bullet.SlugMass * Crushed, Bullet.Owner, nil, Bullet.Gun ) + + if Crushed == 1 then return false end -- no HEAT jet to fire off, it was all converted to HE + + local SlugMV = Round.CalcSlugMV( Bullet, HEATFillerMass ) + + Bullet.Detonated = true + Bullet.InitTime = SysTime() + Bullet.Flight = Bullet.Flight + Bullet.Flight:GetNormalized() * Round.CalcSlugMV( Bullet, HEATFillerMass ) * 39.37 + Bullet.FuseLength = 0.005 + 40/(Bullet.Flight:Length()*0.0254) + Bullet.Pos = HitPos + Bullet.DragCoef = Bullet.SlugDragCoef + Bullet.ProjMass = Bullet.SlugMass * (1-Crushed) + Bullet.Caliber = Bullet.SlugCaliber + Bullet.PenAera = Bullet.SlugPenAera + Bullet.Ricochet = Bullet.SlugRicochet + + local DeltaTime = SysTime() - Bullet.LastThink + Bullet.StartTrace = Bullet.Pos - Bullet.Flight:GetNormalized()*math.min(ACF.PhysMaxVel*DeltaTime,Bullet.FlightTime*Bullet.Flight:Length()) + Bullet.NextPos = Bullet.Pos + (Bullet.Flight * ACF.VelScale * DeltaTime) --Calculates the next shell position + + return true +end + +function Round.propimpact( Index, Bullet, Target, HitNormal, HitPos, Bone ) + + if ACF_Check( Target ) then + + if Bullet.Detonated then + Bullet.NotFirstPen = true + + local Speed = Bullet.Flight:Length() / ACF.VelScale + local Energy = ACF_Kinetic( Speed , Bullet.ProjMass, 999999 ) + local HitRes = ACF_RoundImpact( Bullet, Speed, Energy, Target, HitPos, HitNormal , Bone ) + + if HitRes.Overkill > 0 then + table.insert( Bullet.Filter , Target ) --"Penetrate" (Ingoring the prop for the retry trace) + ACF_Spall( HitPos , Bullet.Flight , Bullet.Filter , Energy.Kinetic*HitRes.Loss , Bullet.Caliber , Target.ACF.Armour , Bullet.Owner ) --Do some spalling + Bullet.Flight = Bullet.Flight:GetNormalized() * math.sqrt(Energy.Kinetic * (1 - HitRes.Loss) * ((Bullet.NotFirstPen and ACF.HEATPenLayerMul) or 1) * 2000 / Bullet.ProjMass) * 39.37 + + return "Penetrated" + else + return false + end + + else + + local Speed = Bullet.Flight:Length() / ACF.VelScale + local Energy = ACF_Kinetic( Speed , Bullet.ProjMass - Bullet.FillerMass, Bullet.LimitVel ) + local HitRes = ACF_RoundImpact( Bullet, Speed, Energy, Target, HitPos, HitNormal , Bone ) + + if HitRes.Ricochet then + return "Ricochet" + else + local jet = Round.detonate( Index, Bullet, HitPos, HitNormal ) + if jet then + return "Penetrated" + else + return false + end + end + + end + else + table.insert( Bullet.Filter , Target ) + return "Penetrated" + end + + return false + +end + +function Round.worldimpact( Index, Bullet, HitPos, HitNormal ) + + if not Bullet.Detonated then + local jet = Round.detonate( Index, Bullet, HitPos, HitNormal ) + if jet then + return "Penetrated" + else + return false + end + end + + local Energy = ACF_Kinetic( Bullet.Flight:Length() / ACF.VelScale, Bullet.ProjMass, 999999 ) + local HitRes = ACF_PenetrateGround( Bullet, Energy, HitPos, HitNormal ) + if HitRes.Penetrated then + return "Penetrated" + --elseif HitRes.Ricochet then --penetrator won't ricochet + -- return "Ricochet" + else + return false + end + +end + +function Round.endflight( Index, Bullet, HitPos, HitNormal ) + + ACF_RemoveBullet( Index ) + +end + +function Round.endeffect( Effect, Bullet ) + + local Impact = EffectData() + Impact:SetEntity( Bullet.Crate ) + Impact:SetOrigin( Bullet.SimPos ) + Impact:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Impact:SetScale( Bullet.SimFlight:Length() ) + Impact:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Impact", Impact ) + +end + +function Round.pierceeffect( Effect, Bullet ) + + if Bullet.Detonated then + + local Spall = EffectData() + Spall:SetEntity( Bullet.Crate ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Penetration", Spall ) + + else + + local Crushed, HEATFillerMass, BoomFillerMass = Round.CrushCalc(Bullet.SimFlight:Length()*0.0254, Bullet.FillerMass) + local Radius = (BoomFillerMass)^0.33*8*39.37 + local Flash = EffectData() + Flash:SetOrigin( Bullet.SimPos ) + Flash:SetNormal( Bullet.SimFlight:GetNormalized() ) + Flash:SetRadius( math.max( Radius, 1 ) ) + util.Effect( "ACF_HEAT_Explosion", Flash ) + + Bullet.Detonated = true + Effect:SetModel("models/Gibs/wood_gib01e.mdl") + + end + +end + +function Round.ricocheteffect( Effect, Bullet ) + + local Spall = EffectData() + Spall:SetEntity( Bullet.Gun ) + Spall:SetOrigin( Bullet.SimPos ) + Spall:SetNormal( (Bullet.SimFlight):GetNormalized() ) + Spall:SetScale( Bullet.SimFlight:Length() ) + Spall:SetMagnitude( Bullet.RoundMass ) + util.Effect( "ACF_AP_Ricochet", Spall ) + +end + +function Round.guicreate( Panel, Table ) + + acfmenupanel:AmmoSelect( ACF.AmmoBlacklist.HEATFS ) + + acfmenupanel:CPanelText("BonusDisplay", "") + + acfmenupanel:CPanelText("Desc", "") --Description (Name, Desc) + acfmenupanel:CPanelText("LengthDisplay", "") --Total round length (Name, Desc) + + --Slider (Name, Value, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("PropLength",0,0,1000,3, "Propellant Length", "") + acfmenupanel:AmmoSlider("ProjLength",0,0,1000,3, "Projectile Length", "") + acfmenupanel:AmmoSlider("ConeAng",0,0,1000,3, "HEAT Cone Angle", "") + acfmenupanel:AmmoSlider("FillerVol",0,0,1000,3, "Total HEAT Warhead volume", "") + + acfmenupanel:AmmoCheckbox("Tracer", "Tracer", "") --Tracer checkbox (Name, Title, Desc) + + acfmenupanel:CPanelText("VelocityDisplay", "") --Proj muzzle velocity (Name, Desc) + acfmenupanel:CPanelText("BlastDisplay", "") --HE Blast data (Name, Desc) + acfmenupanel:CPanelText("FragDisplay", "") --HE Fragmentation data (Name, Desc) + + --acfmenupanel:CPanelText("RicoDisplay", "") --estimated rico chance + acfmenupanel:CPanelText("SlugDisplay", "") --HEAT Slug data (Name, Desc) + + Round.guiupdate( Panel, Table ) + +end + +function Round.guiupdate( Panel, Table ) + + local PlayerData = {} + PlayerData.Id = acfmenupanel.AmmoData.Data.id --AmmoSelect GUI + PlayerData.Type = "HEATFS" --Hardcoded, match ACFRoundTypes table index + PlayerData.PropLength = acfmenupanel.AmmoData.PropLength --PropLength slider + PlayerData.ProjLength = acfmenupanel.AmmoData.ProjLength --ProjLength slider + PlayerData.Data5 = acfmenupanel.AmmoData.FillerVol + PlayerData.Data6 = acfmenupanel.AmmoData.ConeAng + local Tracer = 0 + if acfmenupanel.AmmoData.Tracer then Tracer = 1 end + PlayerData.Data10 = Tracer --Tracer + + local Data = Round.convert( Panel, PlayerData ) + + RunConsoleCommand( "acfmenu_data1", acfmenupanel.AmmoData.Data.id ) + RunConsoleCommand( "acfmenu_data2", PlayerData.Type ) + RunConsoleCommand( "acfmenu_data3", Data.PropLength ) --For Gun ammo, Data3 should always be Propellant + RunConsoleCommand( "acfmenu_data4", Data.ProjLength ) + RunConsoleCommand( "acfmenu_data5", Data.FillerVol ) + RunConsoleCommand( "acfmenu_data6", Data.ConeAng ) + RunConsoleCommand( "acfmenu_data10", Data.Tracer ) + + local vol = ACF.Weapons.Ammo[acfmenupanel.AmmoData["Id"]].volume + local Cap, CapMul, RoFMul = ACF_CalcCrateStats( vol, Data.RoundVolume ) + + acfmenupanel:CPanelText("BonusDisplay", "Crate info: +"..(math.Round((CapMul-1)*100,1)).."% capacity, +"..(math.Round((RoFMul-1)*-100,1)).."% RoF\nContains "..Cap.." rounds") + + acfmenupanel:AmmoSlider("PropLength",Data.PropLength,Data.MinPropLength+(Data.Caliber*3.75),Data.MaxTotalLength,3, "Propellant Length", "Propellant Mass : "..(math.floor(Data.PropMass*1000)).." g" ) --Propellant Length Slider (Name, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("ProjLength",Data.ProjLength,Data.MinProjLength,Data.MaxTotalLength,3, "Projectile Length", "Projectile Mass : "..(math.floor(Data.ProjMass*1000)).." g") --Projectile Length Slider (Name, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("ConeAng",Data.ConeAng,Data.MinConeAng,Data.MaxConeAng,0, "Crush Cone Angle", "") --HE Filler Slider (Name, Min, Max, Decimals, Title, Desc) + acfmenupanel:AmmoSlider("FillerVol",Data.FillerVol,Data.MinFillerVol,Data.MaxFillerVol,3, "HE Filler Volume", "HE Filler Mass : "..(math.floor(Data.FillerMass*1000)).." g") --HE Filler Slider (Name, Min, Max, Decimals, Title, Desc) + + acfmenupanel:AmmoCheckbox("Tracer", "Tracer : "..(math.floor(Data.Tracer*10)/10).."cm\n", "" ) --Tracer checkbox (Name, Title, Desc) + + acfmenupanel:CPanelText("Desc", ACF.RoundTypes[PlayerData.Type].desc) --Description (Name, Desc) + acfmenupanel:CPanelText("LengthDisplay", "Round Length : "..(math.floor((Data.PropLength+Data.ProjLength+Data.Tracer)*100)/100).."/"..(Data.MaxTotalLength).." cm") --Total round length (Name, Desc) + acfmenupanel:CPanelText("VelocityDisplay", "Muzzle Velocity : "..math.floor(Data.MuzzleVel*ACF.VelScale).." m/s") --Proj muzzle velocity (Name, Desc) + acfmenupanel:CPanelText("BlastDisplay", "Blast Radius : "..(math.floor(Data.BlastRadius*100)/100).." m") --Proj muzzle velocity (Name, Desc) + acfmenupanel:CPanelText("FragDisplay", "Fragments : "..(Data.Fragments).."\n Average Fragment Weight : "..(math.floor(Data.FragMass*10000)/10).." g \n Average Fragment Velocity : "..math.floor(Data.FragVel).." m/s") --Proj muzzle penetration (Name, Desc) + + --local RicoAngs = ACF_RicoProbability( Data.Ricochet, Data.MuzzleVel*ACF.VelScale ) + --acfmenupanel:CPanelText("RicoDisplay", "Ricochet probability vs impact angle:\n".." 0% @ "..RicoAngs.Min.." degrees\n 50% @ "..RicoAngs.Mean.." degrees\n100% @ "..RicoAngs.Max.." degrees") + + local R1V, R1P = ACF_PenRanging( Data.MuzzleVel, Data.DragCoef, Data.ProjMass, Data.PenAera, Data.LimitVel, 300 ) + R1P = (ACF_Kinetic( (R1V + Data.SlugMV) * 39.37, Data.SlugMassUsed, 999999 ).Penetration/Data.SlugPenAera)*ACF.KEtoRHA + local R2V, R2P = ACF_PenRanging( Data.MuzzleVel, Data.DragCoef, Data.ProjMass, Data.PenAera, Data.LimitVel, 800 ) + R2P = (ACF_Kinetic( (R2V + Data.SlugMV) * 39.37, Data.SlugMassUsed, 999999 ).Penetration/Data.SlugPenAera)*ACF.KEtoRHA + + acfmenupanel:CPanelText("SlugDisplay", "Penetrator Mass : "..(math.floor(Data.SlugMassUsed*10000)/10).." g \n Penetrator Caliber : "..(math.floor(Data.SlugCaliber*100)/10).." mm \n Penetrator Velocity : "..math.floor(Data.MuzzleVel + Data.SlugMV).." m/s \n Penetrator Maximum Penetration : "..math.floor(Data.MaxPen).." mm RHA\n\n300m pen: "..math.Round(R1P,0).."mm @ "..math.Round(R1V,0).." m\\s\n800m pen: "..math.Round(R2P,0).."mm @ "..math.Round(R2V,0).." m\\s\n\nThe range data is an approximation and may not be entirely accurate.") --Proj muzzle penetration (Name, Desc) + +end + +list.Set( "ACFRoundTypes", "HEATFS", Round ) --Set the round properties +list.Set( "ACFIdRounds", Round.netid, "HEATFS" ) --Index must equal the ID entry in the table above, Data must equal the index of the table above diff --git a/models/tankgun/tankgun_85mm.dx80.vtx b/models/tankgun/tankgun_85mm.dx80.vtx new file mode 100644 index 000000000..50fe556c0 Binary files /dev/null and b/models/tankgun/tankgun_85mm.dx80.vtx differ diff --git a/models/tankgun/tankgun_85mm.dx90.vtx b/models/tankgun/tankgun_85mm.dx90.vtx new file mode 100644 index 000000000..ba6be0772 Binary files /dev/null and b/models/tankgun/tankgun_85mm.dx90.vtx differ diff --git a/models/tankgun/tankgun_85mm.mdl b/models/tankgun/tankgun_85mm.mdl new file mode 100644 index 000000000..5b10a95e2 Binary files /dev/null and b/models/tankgun/tankgun_85mm.mdl differ diff --git a/models/tankgun/tankgun_85mm.phy b/models/tankgun/tankgun_85mm.phy new file mode 100644 index 000000000..10f4d3b21 Binary files /dev/null and b/models/tankgun/tankgun_85mm.phy differ diff --git a/models/tankgun/tankgun_85mm.sw.vtx b/models/tankgun/tankgun_85mm.sw.vtx new file mode 100644 index 000000000..7e92266ee Binary files /dev/null and b/models/tankgun/tankgun_85mm.sw.vtx differ diff --git a/models/tankgun/tankgun_85mm.vvd b/models/tankgun/tankgun_85mm.vvd new file mode 100644 index 000000000..b4df947ed Binary files /dev/null and b/models/tankgun/tankgun_85mm.vvd differ diff --git a/models/tankgun/tankgun_85mm_physics.smd b/models/tankgun/tankgun_85mm_physics.smd new file mode 100644 index 000000000..869b4f125 --- /dev/null +++ b/models/tankgun/tankgun_85mm_physics.smd @@ -0,0 +1,262 @@ +version 1 +nodes +0 "static_prop" -1 +1 "barrel" 0 +2 "breechblock" 1 +end +skeleton +time 0 +0 0.000000 0.000000 0.000000 -1.570796 -0.000000 0.000000 +1 26.437500 -0.000035 0.012322 0.000000 -0.000000 0.000000 +2 -54.561928 -0.000541 -0.025444 0.000000 -0.000000 0.000000 +end +triangles + phy +0 1.904209 -1.099289 129.466553 0.803822 -0.051745 -0.592615 0.000000 0.000000 1 0 1.000000 +0 1.099413 -1.904085 129.466553 0.518675 -0.668855 -0.532550 0.000000 0.000000 1 0 1.000000 +0 3.297621 -1.903874 11.651689 -0.318022 -0.322223 -0.891647 0.000000 0.000000 1 0 1.000000 + phy +0 1.099413 -1.904085 129.466553 0.518675 -0.668855 -0.532550 0.000000 0.000000 1 0 1.000000 +0 1.903885 -3.297611 11.651690 -0.720850 -0.618106 -0.313560 0.000000 0.000000 1 0 1.000000 +0 3.297621 -1.903874 11.651689 -0.318022 -0.322223 -0.891647 0.000000 0.000000 1 0 1.000000 + phy +0 -1.099335 -1.904086 129.466553 -0.249060 -0.928432 0.275649 0.000000 0.000000 1 0 1.000000 +0 0.000039 -2.198662 129.466553 0.018673 -0.996101 -0.086215 0.000000 0.000000 1 0 1.000000 +0 -1.904129 -1.099289 129.466553 0.591379 -0.559291 0.580917 0.000000 0.000000 1 0 1.000000 + phy +0 0.000039 -2.198662 129.466553 0.018673 -0.996101 -0.086215 0.000000 0.000000 1 0 1.000000 +0 1.099413 -1.904085 129.466553 0.518675 -0.668855 -0.532550 0.000000 0.000000 1 0 1.000000 +0 -1.904129 -1.099289 129.466553 0.591379 -0.559291 0.580917 0.000000 0.000000 1 0 1.000000 + phy +0 1.099413 -1.904085 129.466553 0.518675 -0.668855 -0.532550 0.000000 0.000000 1 0 1.000000 +0 1.904209 -1.099289 129.466553 0.803822 -0.051745 -0.592615 0.000000 0.000000 1 0 1.000000 +0 -1.904129 -1.099289 129.466553 0.591379 -0.559291 0.580917 0.000000 0.000000 1 0 1.000000 + phy +0 1.904209 -1.099289 129.466553 0.803822 -0.051745 -0.592615 0.000000 0.000000 1 0 1.000000 +0 -2.198707 0.000085 129.466553 0.712485 -0.639965 0.287768 0.000000 0.000000 1 0 1.000000 +0 -1.904129 -1.099289 129.466553 0.591379 -0.559291 0.580917 0.000000 0.000000 1 0 1.000000 + phy +0 1.904209 -1.099289 129.466553 0.803822 -0.051745 -0.592615 0.000000 0.000000 1 0 1.000000 +0 -1.904129 1.099458 129.466553 0.609643 -0.518956 0.599183 0.000000 0.000000 1 0 1.000000 +0 -2.198707 0.000085 129.466553 0.712485 -0.639965 0.287768 0.000000 0.000000 1 0 1.000000 + phy +0 1.904209 -1.099289 129.466553 0.803822 -0.051745 -0.592615 0.000000 0.000000 1 0 1.000000 +0 1.904209 1.099458 129.466553 0.868229 0.032676 -0.495086 0.000000 0.000000 1 0 1.000000 +0 -1.904129 1.099458 129.466553 0.609643 -0.518956 0.599183 0.000000 0.000000 1 0 1.000000 + phy +0 1.904209 1.099458 129.466553 0.868229 0.032676 -0.495086 0.000000 0.000000 1 0 1.000000 +0 0.000039 2.198831 129.466553 0.696458 -0.709164 -0.109692 0.000000 0.000000 1 0 1.000000 +0 -1.904129 1.099458 129.466553 0.609643 -0.518956 0.599183 0.000000 0.000000 1 0 1.000000 + phy +0 1.904209 1.099458 129.466553 0.868229 0.032676 -0.495086 0.000000 0.000000 1 0 1.000000 +0 1.099414 1.904254 129.466553 0.320474 0.923155 -0.212324 0.000000 0.000000 1 0 1.000000 +0 0.000039 2.198831 129.466553 0.696458 -0.709164 -0.109692 0.000000 0.000000 1 0 1.000000 + phy +0 0.000039 2.198831 129.466553 0.696458 -0.709164 -0.109692 0.000000 0.000000 1 0 1.000000 +0 -1.099336 1.904254 129.466553 -0.301248 -0.580504 0.756482 0.000000 0.000000 1 0 1.000000 +0 -1.904129 1.099458 129.466553 0.609643 -0.518956 0.599183 0.000000 0.000000 1 0 1.000000 + phy +0 1.904209 -1.099289 129.466553 0.803822 -0.051745 -0.592615 0.000000 0.000000 1 0 1.000000 +0 3.297621 -1.903874 11.651689 -0.318022 -0.322223 -0.891647 0.000000 0.000000 1 0 1.000000 +0 3.807765 0.000008 11.651688 -0.827675 -0.232858 -0.510619 0.000000 0.000000 1 0 1.000000 + phy +0 3.807765 0.000008 11.651688 -0.827675 -0.232858 -0.510619 0.000000 0.000000 1 0 1.000000 +0 1.904209 1.099458 129.466553 0.868229 0.032676 -0.495086 0.000000 0.000000 1 0 1.000000 +0 1.904209 -1.099289 129.466553 0.803822 -0.051745 -0.592615 0.000000 0.000000 1 0 1.000000 + phy +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 +0 1.099414 1.904254 129.466553 0.320474 0.923155 -0.212324 0.000000 0.000000 1 0 1.000000 +0 3.297621 1.903889 11.651688 -0.570014 0.629846 -0.527616 0.000000 0.000000 1 0 1.000000 + phy +0 1.099414 1.904254 129.466553 0.320474 0.923155 -0.212324 0.000000 0.000000 1 0 1.000000 +0 1.904209 1.099458 129.466553 0.868229 0.032676 -0.495086 0.000000 0.000000 1 0 1.000000 +0 3.297621 1.903889 11.651688 -0.570014 0.629846 -0.527616 0.000000 0.000000 1 0 1.000000 + phy +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 +0 0.000039 2.198831 129.466553 0.696458 -0.709164 -0.109692 0.000000 0.000000 1 0 1.000000 +0 1.099414 1.904254 129.466553 0.320474 0.923155 -0.212324 0.000000 0.000000 1 0 1.000000 + phy +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 +0 0.000002 3.807771 11.651686 -0.569963 -0.815333 0.101855 0.000000 0.000000 1 0 1.000000 +0 0.000039 2.198831 129.466553 0.696458 -0.709164 -0.109692 0.000000 0.000000 1 0 1.000000 + phy +0 -1.903882 3.297626 11.651687 -0.534251 -0.514552 0.670680 0.000000 0.000000 1 0 1.000000 +0 0.000002 3.807771 11.651686 -0.569963 -0.815333 0.101855 0.000000 0.000000 1 0 1.000000 +0 -3.297614 1.903889 11.651690 -0.747200 -0.217576 0.627975 0.000000 0.000000 1 0 1.000000 + phy +0 -3.297614 1.903889 11.651690 -0.747200 -0.217576 0.627975 0.000000 0.000000 1 0 1.000000 +0 0.000002 3.807771 11.651686 -0.569963 -0.815333 0.101855 0.000000 0.000000 1 0 1.000000 +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 + phy +0 -3.297614 1.903889 11.651690 -0.747200 -0.217576 0.627975 0.000000 0.000000 1 0 1.000000 +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 +0 -3.807760 0.000008 11.651691 0.015644 -0.789848 0.613103 0.000000 0.000000 1 0 1.000000 + phy +0 -3.807760 0.000008 11.651691 0.015644 -0.789848 0.613103 0.000000 0.000000 1 0 1.000000 +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 +0 -3.297614 -1.903874 11.651691 0.599223 -0.575603 0.556429 0.000000 0.000000 1 0 1.000000 + phy +0 -3.297614 -1.903874 11.651691 0.599223 -0.575603 0.556429 0.000000 0.000000 1 0 1.000000 +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 +0 -1.903882 -3.297611 11.651693 0.710622 -0.695822 0.104152 0.000000 0.000000 1 0 1.000000 + phy +0 -1.903882 -3.297611 11.651693 0.710622 -0.695822 0.104152 0.000000 0.000000 1 0 1.000000 +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 +0 0.000002 -3.807755 11.651693 -0.230539 -0.971359 -0.057562 0.000000 0.000000 1 0 1.000000 + phy +0 0.000002 -3.807755 11.651693 -0.230539 -0.971359 -0.057562 0.000000 0.000000 1 0 1.000000 +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 +0 3.807765 0.000008 11.651688 -0.827675 -0.232858 -0.510619 0.000000 0.000000 1 0 1.000000 + phy +0 3.807765 0.000008 11.651688 -0.827675 -0.232858 -0.510619 0.000000 0.000000 1 0 1.000000 +0 3.297621 -1.903874 11.651689 -0.318022 -0.322223 -0.891647 0.000000 0.000000 1 0 1.000000 +0 1.903885 -3.297611 11.651690 -0.720850 -0.618106 -0.313560 0.000000 0.000000 1 0 1.000000 + phy +0 3.807765 0.000008 11.651688 -0.827675 -0.232858 -0.510619 0.000000 0.000000 1 0 1.000000 +0 1.903885 -3.297611 11.651690 -0.720850 -0.618106 -0.313560 0.000000 0.000000 1 0 1.000000 +0 0.000002 -3.807755 11.651693 -0.230539 -0.971359 -0.057562 0.000000 0.000000 1 0 1.000000 + phy +0 3.807765 0.000008 11.651688 -0.827675 -0.232858 -0.510619 0.000000 0.000000 1 0 1.000000 +0 1.903885 3.297626 11.651686 -0.662779 0.628722 -0.406734 0.000000 0.000000 1 0 1.000000 +0 3.297621 1.903889 11.651688 -0.570014 0.629846 -0.527616 0.000000 0.000000 1 0 1.000000 + phy +0 3.807765 0.000008 11.651688 -0.827675 -0.232858 -0.510619 0.000000 0.000000 1 0 1.000000 +0 3.297621 1.903889 11.651688 -0.570014 0.629846 -0.527616 0.000000 0.000000 1 0 1.000000 +0 1.904209 1.099458 129.466553 0.868229 0.032676 -0.495086 0.000000 0.000000 1 0 1.000000 + phy +0 1.903885 -3.297611 11.651690 -0.720850 -0.618106 -0.313560 0.000000 0.000000 1 0 1.000000 +0 1.099413 -1.904085 129.466553 0.518675 -0.668855 -0.532550 0.000000 0.000000 1 0 1.000000 +0 0.000002 -3.807755 11.651693 -0.230539 -0.971359 -0.057562 0.000000 0.000000 1 0 1.000000 + phy +0 0.000002 -3.807755 11.651693 -0.230539 -0.971359 -0.057562 0.000000 0.000000 1 0 1.000000 +0 1.099413 -1.904085 129.466553 0.518675 -0.668855 -0.532550 0.000000 0.000000 1 0 1.000000 +0 0.000039 -2.198662 129.466553 0.018673 -0.996101 -0.086215 0.000000 0.000000 1 0 1.000000 + phy +0 -1.903882 -3.297611 11.651693 0.710622 -0.695822 0.104152 0.000000 0.000000 1 0 1.000000 +0 -1.099335 -1.904086 129.466553 -0.249060 -0.928432 0.275649 0.000000 0.000000 1 0 1.000000 +0 -3.297614 -1.903874 11.651691 0.599223 -0.575603 0.556429 0.000000 0.000000 1 0 1.000000 + phy +0 -3.297614 -1.903874 11.651691 0.599223 -0.575603 0.556429 0.000000 0.000000 1 0 1.000000 +0 -1.099335 -1.904086 129.466553 -0.249060 -0.928432 0.275649 0.000000 0.000000 1 0 1.000000 +0 -1.904129 -1.099289 129.466553 0.591379 -0.559291 0.580917 0.000000 0.000000 1 0 1.000000 + phy +0 0.000039 -2.198662 129.466553 0.018673 -0.996101 -0.086215 0.000000 0.000000 1 0 1.000000 +0 -1.099335 -1.904086 129.466553 -0.249060 -0.928432 0.275649 0.000000 0.000000 1 0 1.000000 +0 0.000002 -3.807755 11.651693 -0.230539 -0.971359 -0.057562 0.000000 0.000000 1 0 1.000000 + phy +0 0.000002 -3.807755 11.651693 -0.230539 -0.971359 -0.057562 0.000000 0.000000 1 0 1.000000 +0 -1.099335 -1.904086 129.466553 -0.249060 -0.928432 0.275649 0.000000 0.000000 1 0 1.000000 +0 -1.903882 -3.297611 11.651693 0.710622 -0.695822 0.104152 0.000000 0.000000 1 0 1.000000 + phy +0 -1.904129 -1.099289 129.466553 0.591379 -0.559291 0.580917 0.000000 0.000000 1 0 1.000000 +0 -2.198707 0.000085 129.466553 0.712485 -0.639965 0.287768 0.000000 0.000000 1 0 1.000000 +0 -3.807760 0.000008 11.651691 0.015644 -0.789848 0.613103 0.000000 0.000000 1 0 1.000000 + phy +0 -1.904129 -1.099289 129.466553 0.591379 -0.559291 0.580917 0.000000 0.000000 1 0 1.000000 +0 -3.807760 0.000008 11.651691 0.015644 -0.789848 0.613103 0.000000 0.000000 1 0 1.000000 +0 -3.297614 -1.903874 11.651691 0.599223 -0.575603 0.556429 0.000000 0.000000 1 0 1.000000 + phy +0 -2.198707 0.000085 129.466553 0.712485 -0.639965 0.287768 0.000000 0.000000 1 0 1.000000 +0 -1.904129 1.099458 129.466553 0.609643 -0.518956 0.599183 0.000000 0.000000 1 0 1.000000 +0 -3.807760 0.000008 11.651691 0.015644 -0.789848 0.613103 0.000000 0.000000 1 0 1.000000 + phy +0 -3.807760 0.000008 11.651691 0.015644 -0.789848 0.613103 0.000000 0.000000 1 0 1.000000 +0 -1.904129 1.099458 129.466553 0.609643 -0.518956 0.599183 0.000000 0.000000 1 0 1.000000 +0 -3.297614 1.903889 11.651690 -0.747200 -0.217576 0.627975 0.000000 0.000000 1 0 1.000000 + phy +0 -1.904129 1.099458 129.466553 0.609643 -0.518956 0.599183 0.000000 0.000000 1 0 1.000000 +0 -1.099336 1.904254 129.466553 -0.301248 -0.580504 0.756482 0.000000 0.000000 1 0 1.000000 +0 -3.297614 1.903889 11.651690 -0.747200 -0.217576 0.627975 0.000000 0.000000 1 0 1.000000 + phy +0 -3.297614 1.903889 11.651690 -0.747200 -0.217576 0.627975 0.000000 0.000000 1 0 1.000000 +0 -1.099336 1.904254 129.466553 -0.301248 -0.580504 0.756482 0.000000 0.000000 1 0 1.000000 +0 -1.903882 3.297626 11.651687 -0.534251 -0.514552 0.670680 0.000000 0.000000 1 0 1.000000 + phy +0 0.000039 2.198831 129.466553 0.696458 -0.709164 -0.109692 0.000000 0.000000 1 0 1.000000 +0 0.000002 3.807771 11.651686 -0.569963 -0.815333 0.101855 0.000000 0.000000 1 0 1.000000 +0 -1.099336 1.904254 129.466553 -0.301248 -0.580504 0.756482 0.000000 0.000000 1 0 1.000000 + phy +0 0.000002 3.807771 11.651686 -0.569963 -0.815333 0.101855 0.000000 0.000000 1 0 1.000000 +0 -1.903882 3.297626 11.651687 -0.534251 -0.514552 0.670680 0.000000 0.000000 1 0 1.000000 +0 -1.099336 1.904254 129.466553 -0.301248 -0.580504 0.756482 0.000000 0.000000 1 0 1.000000 + phy +0 -4.128338 4.108698 11.651688 0.921224 0.225753 0.316830 0.000000 0.000000 1 0 1.000000 +0 4.128343 -4.125410 11.651690 0.783367 -0.278449 -0.555700 0.000000 0.000000 1 0 1.000000 +0 4.128343 4.108699 11.651685 0.735846 0.478852 -0.478781 0.000000 0.000000 1 0 1.000000 + phy +0 4.128343 4.108699 11.651685 0.735846 0.478852 -0.478781 0.000000 0.000000 1 0 1.000000 +0 5.650825 5.617879 5.651689 0.195185 0.785853 -0.586803 0.000000 0.000000 1 0 1.000000 +0 -4.128338 4.108698 11.651688 0.921224 0.225753 0.316830 0.000000 0.000000 1 0 1.000000 + phy +0 4.128343 -4.125410 11.651690 0.783367 -0.278449 -0.555700 0.000000 0.000000 1 0 1.000000 +0 5.650825 5.617879 5.651689 0.195185 0.785853 -0.586803 0.000000 0.000000 1 0 1.000000 +0 4.128343 4.108699 11.651685 0.735846 0.478852 -0.478781 0.000000 0.000000 1 0 1.000000 + phy +0 -4.128338 -4.125410 11.651694 0.689395 -0.712969 0.128101 0.000000 0.000000 1 0 1.000000 +0 -4.128338 4.108698 11.651688 0.921224 0.225753 0.316830 0.000000 0.000000 1 0 1.000000 +0 -5.650821 -5.634597 5.651700 0.226433 -0.841463 0.490579 0.000000 0.000000 1 0 1.000000 + phy +0 -5.650821 -5.634597 5.651700 0.226433 -0.841463 0.490579 0.000000 0.000000 1 0 1.000000 +0 -4.128338 4.108698 11.651688 0.921224 0.225753 0.316830 0.000000 0.000000 1 0 1.000000 +0 -5.650821 5.617878 5.651692 0.159503 0.360319 0.919092 0.000000 0.000000 1 0 1.000000 + phy +0 -4.128338 4.108698 11.651688 0.921224 0.225753 0.316830 0.000000 0.000000 1 0 1.000000 +0 -4.128338 -4.125410 11.651694 0.689395 -0.712969 0.128101 0.000000 0.000000 1 0 1.000000 +0 4.128343 -4.125410 11.651690 0.783367 -0.278449 -0.555700 0.000000 0.000000 1 0 1.000000 + phy +0 -4.128338 4.108698 11.651688 0.921224 0.225753 0.316830 0.000000 0.000000 1 0 1.000000 +0 5.650825 5.617879 5.651689 0.195185 0.785853 -0.586803 0.000000 0.000000 1 0 1.000000 +0 -5.650821 5.617878 5.651692 0.159503 0.360319 0.919092 0.000000 0.000000 1 0 1.000000 + phy +0 5.650825 -5.634597 5.651696 0.172534 -0.693229 -0.699761 0.000000 0.000000 1 0 1.000000 +0 -5.650821 -5.634597 5.651700 0.226433 -0.841463 0.490579 0.000000 0.000000 1 0 1.000000 +0 5.650811 -5.634627 -37.923862 -0.666583 -0.666751 -0.333332 0.000000 0.000000 1 0 1.000000 + phy +0 5.650811 -5.634627 -37.923862 -0.666583 -0.666751 -0.333332 0.000000 0.000000 1 0 1.000000 +0 -5.650821 -5.634597 5.651700 0.226433 -0.841463 0.490579 0.000000 0.000000 1 0 1.000000 +0 -5.650835 -5.634627 -37.923859 -0.474057 -0.632584 0.612460 0.000000 0.000000 1 0 1.000000 + phy +0 -5.650821 -5.634597 5.651700 0.226433 -0.841463 0.490579 0.000000 0.000000 1 0 1.000000 +0 -5.650821 5.617878 5.651692 0.159503 0.360319 0.919092 0.000000 0.000000 1 0 1.000000 +0 -5.650835 5.617849 -37.923862 -0.637468 0.470675 0.609999 0.000000 0.000000 1 0 1.000000 + phy +0 -5.650821 -5.634597 5.651700 0.226433 -0.841463 0.490579 0.000000 0.000000 1 0 1.000000 +0 -5.650835 5.617849 -37.923862 -0.637468 0.470675 0.609999 0.000000 0.000000 1 0 1.000000 +0 -5.650835 -5.634627 -37.923859 -0.474057 -0.632584 0.612460 0.000000 0.000000 1 0 1.000000 + phy +0 -5.650821 -5.634597 5.651700 0.226433 -0.841463 0.490579 0.000000 0.000000 1 0 1.000000 +0 5.650825 -5.634597 5.651696 0.172534 -0.693229 -0.699761 0.000000 0.000000 1 0 1.000000 +0 -4.128338 -4.125410 11.651694 0.689395 -0.712969 0.128101 0.000000 0.000000 1 0 1.000000 + phy +0 5.650825 -5.634597 5.651696 0.172534 -0.693229 -0.699761 0.000000 0.000000 1 0 1.000000 +0 4.128343 -4.125410 11.651690 0.783367 -0.278449 -0.555700 0.000000 0.000000 1 0 1.000000 +0 -4.128338 -4.125410 11.651694 0.689395 -0.712969 0.128101 0.000000 0.000000 1 0 1.000000 + phy +0 5.650825 -5.634597 5.651696 0.172534 -0.693229 -0.699761 0.000000 0.000000 1 0 1.000000 +0 5.650825 5.617879 5.651689 0.195185 0.785853 -0.586803 0.000000 0.000000 1 0 1.000000 +0 4.128343 -4.125410 11.651690 0.783367 -0.278449 -0.555700 0.000000 0.000000 1 0 1.000000 + phy +0 5.650825 5.617879 5.651689 0.195185 0.785853 -0.586803 0.000000 0.000000 1 0 1.000000 +0 -5.650835 5.617849 -37.923862 -0.637468 0.470675 0.609999 0.000000 0.000000 1 0 1.000000 +0 -5.650821 5.617878 5.651692 0.159503 0.360319 0.919092 0.000000 0.000000 1 0 1.000000 + phy +0 5.650825 5.617879 5.651689 0.195185 0.785853 -0.586803 0.000000 0.000000 1 0 1.000000 +0 5.650811 5.617851 -37.923866 -0.408223 0.407798 -0.816735 0.000000 0.000000 1 0 1.000000 +0 -5.650835 5.617849 -37.923862 -0.637468 0.470675 0.609999 0.000000 0.000000 1 0 1.000000 + phy +0 5.650825 -5.634597 5.651696 0.172534 -0.693229 -0.699761 0.000000 0.000000 1 0 1.000000 +0 5.650811 5.617851 -37.923866 -0.408223 0.407798 -0.816735 0.000000 0.000000 1 0 1.000000 +0 5.650825 5.617879 5.651689 0.195185 0.785853 -0.586803 0.000000 0.000000 1 0 1.000000 + phy +0 5.650825 -5.634597 5.651696 0.172534 -0.693229 -0.699761 0.000000 0.000000 1 0 1.000000 +0 5.650811 -5.634627 -37.923862 -0.666583 -0.666751 -0.333332 0.000000 0.000000 1 0 1.000000 +0 5.650811 5.617851 -37.923866 -0.408223 0.407798 -0.816735 0.000000 0.000000 1 0 1.000000 + phy +0 5.650811 -5.634627 -37.923862 -0.666583 -0.666751 -0.333332 0.000000 0.000000 1 0 1.000000 +0 -5.650835 -5.634627 -37.923859 -0.474057 -0.632584 0.612460 0.000000 0.000000 1 0 1.000000 +0 -5.650835 5.617849 -37.923862 -0.637468 0.470675 0.609999 0.000000 0.000000 1 0 1.000000 + phy +0 5.650811 -5.634627 -37.923862 -0.666583 -0.666751 -0.333332 0.000000 0.000000 1 0 1.000000 +0 -5.650835 5.617849 -37.923862 -0.637468 0.470675 0.609999 0.000000 0.000000 1 0 1.000000 +0 5.650811 5.617851 -37.923866 -0.408223 0.407798 -0.816735 0.000000 0.000000 1 0 1.000000 +end diff --git a/models/tankgun/tankgun_short_85mm.dx80.vtx b/models/tankgun/tankgun_short_85mm.dx80.vtx new file mode 100644 index 000000000..5901e6516 Binary files /dev/null and b/models/tankgun/tankgun_short_85mm.dx80.vtx differ diff --git a/models/tankgun/tankgun_short_85mm.dx90.vtx b/models/tankgun/tankgun_short_85mm.dx90.vtx new file mode 100644 index 000000000..b56210b0b Binary files /dev/null and b/models/tankgun/tankgun_short_85mm.dx90.vtx differ diff --git a/models/tankgun/tankgun_short_85mm.mdl b/models/tankgun/tankgun_short_85mm.mdl new file mode 100644 index 000000000..a70d071a7 Binary files /dev/null and b/models/tankgun/tankgun_short_85mm.mdl differ diff --git a/models/tankgun/tankgun_short_85mm.phy b/models/tankgun/tankgun_short_85mm.phy new file mode 100644 index 000000000..0e541431a Binary files /dev/null and b/models/tankgun/tankgun_short_85mm.phy differ diff --git a/models/tankgun/tankgun_short_85mm.sw.vtx b/models/tankgun/tankgun_short_85mm.sw.vtx new file mode 100644 index 000000000..d74bd94e8 Binary files /dev/null and b/models/tankgun/tankgun_short_85mm.sw.vtx differ diff --git a/models/tankgun/tankgun_short_85mm.vvd b/models/tankgun/tankgun_short_85mm.vvd new file mode 100644 index 000000000..6adf890c7 Binary files /dev/null and b/models/tankgun/tankgun_short_85mm.vvd differ diff --git a/test.txt b/test.txt new file mode 100644 index 000000000..e69de29bb