Initial commit

This commit is contained in:
Haodong Mo 2022-01-15 17:28:08 +10:00
commit 651d37c160
62 changed files with 6541 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

134
README.md Normal file
View File

@ -0,0 +1,134 @@
# ARC9
### A successor to the ArcCW base that focuses on stability and ease of use.
ARC9 is so called for being the ninth public Arctic base. It is designed to be a long-term sustainable successor to ArcCW.
## FAQ
### Why should I use this over ArcCW?
It's far better designed, better networked, and in the long run, will be more sustainable. It is easier to develop for, easier to use, has more features, and is more comfortable to use in multiplayer.
### Why ARC9? Where's ARC2-8? Why not just ArcCW 2?
Because I don't want people to think it has anything to do with CW 2.0.
### Are ArcCW 1 weapons compatible with this?
No, they aren't, and without some effort, automatic porting isn't going to be possible. However, manual porting should be doable, and will be a lot easier than creating a new weapon from scratch.
**New features include:**
- Overhauled standardized recoil mechanics
- "Supply Limit" system
- Magazine dropping
- Attachments on top of attachments
- Free aim
- Free sway
- Reworked standardized weapon stat handling
- Blind fire for all weapons
- Looping sounds
- Indoor audio
- Reworked RHIK system with greater compatibility, better performance, and multiple targets
- RHIK supports right hands too
- Snazzy new UI
- Better standard weapon ecosystem
- Pose Parameter handler
### New Recoil
Weapons can be set with a Recoil Seed, unique to that gun, or a lookup table of recoil values. This gives them a recoil pattern which is consistent and predictible every time you shoot.
Weapons also have an amount of random recoil, which is added onto the predictible recoil. Random recoil cannot be predicted, meaning the pattern will vary a little bit every time.
### Supply Limit
The main way to get ammunition is through using Supply Crates or Supply Pickups. These will only resupply you up to your supply limit multiplied by your current magazine capacity worth of ammo. Some attachments will increase your Supply Limit while others will decrease it.
### Subslots
Now, attachments can add new slots, which more attachments can be placed onto. Due to internal structure features of ArcCW, this was completely impossible previously.
### Free Aim and Free Sway
While hip-firing, your weapon's point of aim will sway around the screen. This means no more perfect center-screen hip shooting. This is another mechanic that can be used to balance attachments and weapons.
### New Stat Handling
Each stat is handled more automatically than ever. Each stat has a "Base", such as Speed or ReloadTime or Sway. To create a new modifier to it, we add "Mult", e.g. SpeedMult. To make it conditional, we can then add a condition onto it, e.g. SpeedMultSights. This will make adding new conditions easier than ever, as it only needs to be modified in one place - the condition handler.
Each condition is handled individually according to the specific condition. For instance, the MidAir condition is binary - so modifiers with this condition are applied only if the player is in mind-air. However, the Sighted condition will interpolate between 0 and 1 multiplied by the total multiplier. So if the player is 50% aimed down their sights, and they have an attachment with SpreadMultSighted of 2, their spread will be multiplied by 1.5x. Mults are multiplied together before this happens - if the player has two attachments, one with SpreadMultSighted of 2 and one with 3, ARC9 will only save "6" and thus if the player is halfway sighted, their spread is multiplied by 3.
Modifier classes are Override, Mult, Add, and Hook, processed in that order, as in ArcCW.
Override and Hook modifiers also support _Priority values being assigned to the same attachment or the base weapon for greater control over the order in which they are run. For instance. SpreadOverrideSighted_Priority = 2 on an attachment will cause that attachment's SpreadOverrideSighted to take precedence over all SpreadOverrideSighted values without a priority set or with priority less than 2. Unset priority counts as 1.
Examples:
SpeedMult
AimDownSightTimeHook
AimDownSightOverrideCrouch
ArcCW: Mult_SpeedMult
ARC9: SpeedMult
ArcCW: Override_Firemodes
ARC9: FiremodesOverride
Hooks make a return outside of stat modifications. Hooks (Like "HookT_MinimapPing") are now separated into different types based on how they accept return data. Hooks that do not have a type ("Hook_Whatever") do not accept any return data and are only present for signalling. Both Hooks and Overrides accept \_Priority, just like in ArcCW. Higher priority will be run with precedence. For instance, Hook_RecoilSeed_Priority = 3 means that attachment's Hook_RecoilSeed will be run before any other hook that belongs to an attachment with Hook_RecoilSeed_Priority = 2.
The base weapon is also considered.
HookT: Table hooks. Return a {table} of values. Every hook's returned values are gathered into one big table for later use. Every returned value is used.
HookA: Accumulator hooks. Return an Angle, Vector, or Number - the resulting value is made up of all of these values added together.
HookP: Pipeline hooks. Accept previously returned data, return new data. Each function modifies the data given. Return nil to make no change. Used for things like animation translate.
Hooks run in an order that can be compared as such:
1. If A's \[Hook_*\]_Priority value is higher than B (Unspecified is treated as 1) then run it first.
2. If A belongs to a top level slot with lower value than B, run it first.
3. If A and B belong to the same top level slot, the one with the lowest depth will be run first.
4. If A and B have the same depth, the one that belongs to the lowest level sub-slot will be run first.
This is, essentially, a breadth-first tree unwrap which treats each top level slot as its own tree and processes them in numerical order.
### RHIK
RHIK is a successor to LHIK. Now, RHIK supports multiple targets, using the RHIK stack. Whenever we request an animation to be played, we put it on top of the stack. Giving priority to the top animation, we blend it seamlessly with every other animation in the stack, giving us the ability to play multiple RHIK animations in quick succession to different targets with maximum smoothness.
RHIK is also able to accept an attachment that moves the base weapon, allowing for more fluid animations as opposed to the procedural system of ArcCW.
Some applications of this include the new built-in system for toggleable hybrid optics, underbarrel grenade launchers, and grip options with different poses.
### New UI
A new heads-up display with minimap support and cooler design.
### New Weapon Ecosystem
Designed from the ground up to be extensible and standardized, so that creators can work together better. New slots better represent what they are meant to accomplish.
### Pose Parameter Handler
Allows animations to alter pose param values and keep them there. Several other weapon statistics are also directly tied to pose parameters, allowing for greater animation-based control.
## How To Contribute
Whenever you add a feature, please document any additional parameters or variables you've added in shared.lua if they're applicable to the base weapon or default.lua if they're mainly to do with attachments. Please make sure to place your parameter under the appropriate section.
SWEP and ATT variables should be in CamelCase, as is the Gmod standard. ARC9.ENUMs should be all upper case, as is the Gmod standard. Prefer descriptive variables when possible. Adhere to the Nomenclature guide.
**Nomenclature:**
Only terminology that may cause confusion is laid out. Any terms that are standard to the community or engine are assumed to be used the same way.
- Sequence: A Source animation on a gun's viewmodel.
- Animation Table: A table for defining custom sequence data.
- Attachment: A thing that goes on your gun.
- QC Attachment: Attachment point that follows a bone used by Source engine, defined in .qc files.
- Slot: Something you can put an attachment on.
- Predictible Recoil: Recoil that is part of the pattern, which is the same every single time you shoot a burst.
- Random Recoil: Recoil that cannot be predicted, added on to predictible recoil.
- Precision: The mechanical accuracy of a gun.
- Spread: The amount that a bullet can deviate from the aim point, in 1/10ths of a full circle.
- Chamber: An area in a firearm that holds a round before it is fired, separate from the magazine. Only some guns have a chamber in this sense of the word. Allows guns to "+1". Revolvers and open bolt guns lack this ability and cannot chamber.
- Clip: The same thing as a magazine.
- Stripper Clip: An item that holds rounds that can be used to quickly refill a magazine. NOT the same thing as just a "clip".
- VM: View Model.
- WM: World Model.
- BG: Bodygroup.

BIN
hud mockup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,25 @@
hook.Add("PlayerBindPress", "ARC9_Binds", function(ply, bind, pressed, code)
local wpn = ply:GetActiveWeapon()
if !wpn or !IsValid(wpn) or !wpn.ARC9 then return end
if !pressed then return end
-- print(bind)
if bind == "+menu_context" then
if !LocalPlayer():KeyDown(IN_USE) then
if wpn:GetCustomize() then
net.Start("ARC9_togglecustomize")
net.WriteBool(false)
net.SendToServer()
else
net.Start("ARC9_togglecustomize")
net.WriteBool(true)
net.SendToServer()
end
return true
end
end
end)

View File

@ -0,0 +1,60 @@
local sizes_to_make = {
6,
8,
10,
12,
16,
32
}
local unscaled_sizes_to_make = {
}
local font = "HD44780A00 5x8"
local function generatefonts()
for _, i in pairs(sizes_to_make) do
surface.CreateFont( "ARC9_" .. tostring(i), {
font = font,
size = ScreenScale(i),
weight = 500,
antialias = true,
extended = true, -- Required for non-latin fonts
} )
surface.CreateFont( "ARC9_" .. tostring(i) .. "_Glow", {
font = font,
size = ScreenScale(i),
weight = 500,
antialias = true,
blursize = 6,
extended = true,
} )
end
for _, i in pairs(unscaled_sizes_to_make) do
surface.CreateFont( "ARC9_" .. tostring(i) .. "_Unscaled", {
font = font,
size = i,
weight = 500,
antialias = true,
extended = true,
} )
end
end
generatefonts()
function ARC9.Regen(full)
if full then
generatefonts()
end
end
hook.Add( "OnScreenSizeChanged", "ARC9.Regen", function() ARC9.Regen(true) end)

View File

@ -0,0 +1,60 @@
ARC9.CSModelPile = {} -- { {Model = NULL, Weapon = NULL} }
ARC9.FlashlightPile = {}
function ARC9.CollectGarbage()
local removed = 0
local newpile = {}
for _, k in pairs(ARC9.CSModelPile) do
if IsValid(k.Weapon) then
table.insert(newpile, k)
continue
end
SafeRemoveEntity(k.Model)
removed = removed + 1
end
ARC9.CSModelPile = newpile
if GetConVar("developer"):GetBool() and removed > 0 then
print("Removed " .. tostring(removed) .. " CSModels")
end
end
hook.Add("PostCleanupMap", "ARC9.CleanGarbage", function()
ARC9.CollectGarbage()
end)
timer.Create("ARC9 CSModel Garbage Collector", 5, 0, ARC9.CollectGarbage)
hook.Add("PostDrawEffects", "ARC9_CleanFlashlights", function()
local newflashlightpile = {}
for _, k in pairs(ARC9.FlashlightPile) do
if IsValid(k.Weapon) and k.Weapon == LocalPlayer():GetActiveWeapon() then
table.insert(newflashlightpile, k)
continue
end
if k.ProjectedTexture and k.ProjectedTexture:IsValid() then
k.ProjectedTexture:Remove()
end
end
ARC9.FlashlightPile = newflashlightpile
local wpn = LocalPlayer():GetActiveWeapon()
if !wpn then return end
if !IsValid(wpn) then return end
if !wpn.ARC9 then return end
if GetViewEntity() == LocalPlayer() then return end
wpn:KillFlashlightsVM()
end)

View File

@ -0,0 +1,18 @@
local hide = {
["CHudHealth"] = true,
["CHudBattery"] = true,
["CHudAmmo"] = true,
["CHudSecondaryAmmo"] = true,
}
hook.Add("HUDShouldDraw", "ARC9_HideHUD", function(name)
if !IsValid(LocalPlayer()) then return end
local wpn = LocalPlayer():GetActiveWeapon()
if !wpn.ARC9 then return end
if wpn:GetCustomize() then
if hide[name] then return false end
end
end)

View File

@ -0,0 +1,7 @@
net.Receive("ARC9_networkweapon", function(len, ply)
local wpn = net.ReadEntity()
if !wpn.ARC9 then return end
wpn:ReceiveWeapon()
end)

View File

@ -0,0 +1,27 @@
hook.Add("PreRender", "ARC9_PreRender", function()
if GetConVar("ARC9_cheapscopes"):GetBool() then return end
local wpn = LocalPlayer():GetActiveWeapon()
if !wpn.ARC9 then return end
local sight = wpn:GetSight()
if sight.atttbl and sight.atttbl.RTScope and wpn:GetSightAmount() > 0 then
wpn:DoRT(sight.atttbl.RTScopeFOV)
end
end)
hook.Add("PreDrawViewModels", "ARC9_PreDrawViewModels", function()
if !GetConVar("ARC9_cheapscopes"):GetBool() then return end
local wpn = LocalPlayer():GetActiveWeapon()
if !wpn.ARC9 then return end
local sight = wpn:GetSight()
if sight.atttbl and sight.atttbl.RTScope and wpn:GetSightAmount() > 0 then
wpn:DoCheapScope(sight.atttbl.RTScopeFOV)
end
end)

View File

@ -0,0 +1,75 @@
ATT.PrintName = "Konstantin Red Dot"
ATT.CompactName = "RDSx1"
ATT.Icon = Material("")
ATT.Description = [[Collimated red dot optic sight.]]
ATT.SortOrder = 0
ATT.AdminOnly = false
ATT.Free = false
ATT.Ignore = true
ATT.Model = ""
ATT.WorldModel = "" // optional
ATT.Scale = 1
ATT.ModelOffset = Vector(0, 0, 0)
ATT.InvAtt = "" // Having this other attachment will grant access to this one.
ATT.Category = "" // can be "string" or {"list", "of", "strings"}
ATT.ActivateElements = {"plum_stock"}
ATT.ToggleStats = {
["On"] = {
SpreadAddHipFire = -0.1,
},
["Off"] = {}
}
// max of 256 togglestats
ATT.MuzzleDevice = false // set to true if you want to use this to emit particles
ATT.Flashlight = false
ATT.FlashlightColor = Color(255, 255, 255)
ATT.FlashlightMaterial = Material("")
ATT.FlashlightDistance = 1024
ATT.FlashlightFOV = 70
ATT.Laser = false
ATT.LaserColor = Color(255, 0, 0)
// Allows a custom sight position to be defined
ATT.Sights = {
{
Pos = Vector(0, 0, 0),
Ang = Angle(0, 0, 0)
}
}
ATT.HoloSight = false
ATT.HoloSightReticle = Material("")
ATT.HoloSightSize = 32
ATT.HoloSightColor = Color(255, 255, 255)
ATT.RTScope = true
ATT.RTScopeSubmatIndex = 1
ATT.RTScopeFOV = 2.5
ATT.RTScopeRes = 512
ATT.RTScopeSurface = Material("effects/ARC9_rt")
ATT.RTScopeReticle = Material("")
ATT.Attachments = {
{
PrintName = "",
DefaultIcon = Material(""),
InstalledElements = "", // single or list of elements to activate when something is installed here
UnInstalledElements = "",
Integral = false, // cannot be removed
Category = "", // single or {"list", "of", "values"}
Bone = "",
Pos = Vector(0, 0, 0),
Ang = Angle(0, 0, 0),
KeepBaseIrons = false,
}
}

View File

@ -0,0 +1,75 @@
ARC9.PenTable = {
[MAT_ANTLION] = 1,
[MAT_BLOODYFLESH] = 1,
[MAT_CONCRETE] = 1,
[MAT_DIRT] = 1,
[MAT_EGGSHELL] = 1,
[MAT_FLESH] = 1,
[MAT_GRATE] = 1,
[MAT_ALIENFLESH] = 1,
[MAT_CLIP] = 1,
[MAT_SNOW] = 1,
[MAT_PLASTIC] = 1,
[MAT_METAL] = 2,
[MAT_SAND] = 1,
[MAT_FOLIAGE] = 1,
[MAT_COMPUTER] = 1,
[MAT_SLOSH] = 1,
[MAT_TILE] = 1,
[MAT_GRASS] = 1,
[MAT_VENT] = 1,
[MAT_WOOD] = 0.5,
[MAT_DEFAULT] = 1,
[MAT_GLASS] = 0.25,
[MAT_WARPSHIELD] = 1,
}
ARC9.PresetPath = "ARC9_presets/"
ARC9.OverDraw = false
ARC9.LHIKBones = {
"ValveBiped.Bip01_L_UpperArm",
"ValveBiped.Bip01_L_Forearm",
"ValveBiped.Bip01_L_Wrist",
"ValveBiped.Bip01_L_Ulna",
"ValveBiped.Bip01_L_Hand",
"ValveBiped.Bip01_L_Finger4",
"ValveBiped.Bip01_L_Finger41",
"ValveBiped.Bip01_L_Finger42",
"ValveBiped.Bip01_L_Finger3",
"ValveBiped.Bip01_L_Finger31",
"ValveBiped.Bip01_L_Finger32",
"ValveBiped.Bip01_L_Finger2",
"ValveBiped.Bip01_L_Finger21",
"ValveBiped.Bip01_L_Finger22",
"ValveBiped.Bip01_L_Finger1",
"ValveBiped.Bip01_L_Finger11",
"ValveBiped.Bip01_L_Finger12",
"ValveBiped.Bip01_L_Finger0",
"ValveBiped.Bip01_L_Finger01",
"ValveBiped.Bip01_L_Finger02"
}
ARC9.RHIKBones = {
"ValveBiped.Bip01_R_UpperArm",
"ValveBiped.Bip01_R_Forearm",
"ValveBiped.Bip01_R_Wrist",
"ValveBiped.Bip01_R_Ulna",
"ValveBiped.Bip01_R_Hand",
"ValveBiped.Bip01_R_Finger4",
"ValveBiped.Bip01_R_Finger41",
"ValveBiped.Bip01_R_Finger42",
"ValveBiped.Bip01_R_Finger3",
"ValveBiped.Bip01_R_Finger31",
"ValveBiped.Bip01_R_Finger32",
"ValveBiped.Bip01_R_Finger2",
"ValveBiped.Bip01_R_Finger21",
"ValveBiped.Bip01_R_Finger22",
"ValveBiped.Bip01_R_Finger1",
"ValveBiped.Bip01_R_Finger11",
"ValveBiped.Bip01_R_Finger12",
"ValveBiped.Bip01_R_Finger0",
"ValveBiped.Bip01_R_Finger01",
"ValveBiped.Bip01_R_Finger02"
}

View File

@ -0,0 +1,203 @@
// the 0 is for load order!!!
local conVars = {
{
name = "truenames",
default = "0",
},
{
name = "maxatts",
default = "100",
},
{
name = "autosave",
default = "0",
client = true
},
{
name = "bodydamagecancel",
default = "1",
replicated = true
},
{
name = "free_atts",
default = "0",
replicated = true
},
{
name = "lock_atts",
default = "0",
replicated = true
},
{
name = "loseattsondie",
default = "1",
},
{
name = "generateattentities",
default = "1",
replicated = true
},
{
name = "npc_equality",
default = "0",
},
{
name = "npc_atts",
default = "1",
},
{
name = "penetration",
default = "1",
replicated = true
},
{
name = "freeaim",
default = "1",
replicated = true
},
{
name = "sway",
default = "1",
replicated = true
},
{
name = "benchgun",
default = "0",
},
{
name = "ricochet",
default = "1",
replicated = true
},
{
name = "bullet_physics",
default = "1",
replicated = true
},
{
name = "bullet_gravity",
default = "1",
replicated = true
},
{
name = "bullet_drag",
default = "1",
replicated = true
},
{
name = "bullet_imaginary",
default = "1",
replicated = true
},
{
name = "bullet_lifetime",
default = "10",
replicated = true
},
{
name = "cheapscopes",
default = "0"
},
{
name = "truenames",
default = "1",
replicated = true
},
{
name = "compensate_sens",
default = "1"
},
{
name = "freeaim",
default = "1"
}
}
local prefix = "arc9_"
for _, var in pairs(conVars) do
local convar_name = prefix .. var.name
if var.client and CLIENT then
CreateClientConVar(convar_name, var.default, true)
else
local flags = FCVAR_ARCHIVE
if var.replicated then
flags = flags + FCVAR_REPLICATED
end
CreateConVar(convar_name, var.default, flags)
end
end
if CLIENT then
local function menu_client_ti(panel)
panel:AddControl("checkbox", {
label = "Reload Automatically",
command = "ARC9_autoreload"
})
panel:AddControl("checkbox", {
label = "Auto-Save Weapon",
command = "ARC9_autosave"
})
panel:AddControl("checkbox", {
label = "Compensate Sensitivity",
command = "ARC9_compensate_sens"
})
end
local function menu_server_ti(panel)
panel:AddControl("checkbox", {
label = "Free Attachments",
command = "ARC9_free_atts"
})
panel:AddControl("checkbox", {
label = "Attachment Locking",
command = "ARC9_lock_atts"
})
panel:AddControl("checkbox", {
label = "Lose Attachments On Death",
command = "ARC9_loseattsondie"
})
panel:AddControl("checkbox", {
label = "Generate Attachment Entities",
command = "ARC9_generateattentities"
})
panel:AddControl("checkbox", {
label = "Enable Penetration",
command = "ARC9_penetration"
})
panel:AddControl("checkbox", {
label = "NPCs Deal Equal Damage",
command = "ARC9_npc_equality"
})
panel:AddControl("checkbox", {
label = "NPCs Get Random Attachments",
command = "ARC9_npc_atts"
})
panel:AddControl("label", {
text = "Disable body damage cancel only if you have another addon that will override the hl2 limb damage multipliers."
})
panel:AddControl("checkbox", {
label = "Default Body Damage Cancel",
command = "ARC9_bodydamagecancel"
})
end
local clientmenus_ti = {
{
text = "Client", func = menu_client_ti
},
{
text = "Server", func = menu_server_ti
},
}
hook.Add("PopulateToolMenu", "ARC9_MenuOptions", function()
for smenu, data in pairs(clientmenus_ti) do
spawnmenu.AddToolMenuOption("Options", "STALKER+ Weapons", "ARC9_" .. tostring(smenu), data.text, "", "", data.func)
end
end)
end

View File

@ -0,0 +1,34 @@
ARC9.CLASS_PISTOL = 0
ARC9.CLASS_RIFLE = 1
ARC9.CLASS_SHOTGUN = 2
ARC9.CLASS_PDW = 3
ARC9.CLASS_CARBINE = 4
ARC9.CLASS_DMR = 5
ARC9.CLASS_SNIPER = 6
ARC9.CLASS_LAUNCHER = 7
ARC9.CLASS_GRENADE = 8
ARC9.CLASS_DEPLOYABLE = 9
ARC9.CLASS_TOOL = 10
ARC9.CLASS_OTHER = 31 -- 11-30 are reserved
ARC9.HITPROFILE_AR = {
[HITGROUP_HEAD] = 1.25,
[HITGROUP_CHEST] = 1,
[HITGROUP_LEFTARM] = 0.9,
[HITGROUP_RIGHTARM] = 0.9,
}
ARC9.HITPROFILE_PISTOL = {
[HITGROUP_HEAD] = 2,
[HITGROUP_CHEST] = 1,
[HITGROUP_LEFTARM] = 0.9,
[HITGROUP_RIGHTARM] = 0.9,
}
ARC9.HITPROFILE_SNIPER = {
[HITGROUP_HEAD] = 5,
[HITGROUP_CHEST] = 1.1,
[HITGROUP_STOMACH] = 1.0,
[HITGROUP_LEFTARM] = 0.95,
[HITGROUP_RIGHTARM] = 0.95,
}

View File

@ -0,0 +1,11 @@
L("CLASS_PISTOL", "Pistol")
L("CLASS_RIFLE", "Rifle")
L("CLASS_SHOTGUN", "Shotgun")
L("CLASS_PDW", "PDW")
L("CLASS_CARBINE", "Carbine")
L("CLASS_DMR", "DMR")
L("CLASS_LAUNCHER", "Launcher")
L("CLASS_GRENADE", "Grenade")
L("CLASS_DEPLOYABLE", "Deployable")
L("CLASS_TOOL", "Tool")
L("CLASS_OTHER", "Other")

View File

@ -0,0 +1,8 @@
L("FIREMODE_AUTO", "Full Auto")
L("FIREMODE_SEMI", "Semi Auto")
L("FIREMODE_PUMP", "Pump-Action")
L("FIREMODE_LEVER", "Lever-Action")
L("FIREMODE_BOLT", "Bolt-Action")
L("FIREMODE_MANUAL", "Manual")
L("FIREMODE_BURST", " Round Burst")
L("FIREMODE_SPUTTER", "Sputter")

View File

@ -0,0 +1,22 @@
util.AddNetworkString("ARC9_togglecustomize")
util.AddNetworkString("ARC9_networkweapon")
util.AddNetworkString("ARC9_sendattinv")
util.AddNetworkString("ARC9_sendbullet")
net.Receive("ARC9_togglecustomize", function(len, ply)
local bf = net.ReadBool()
local wpn = ply:GetActiveWeapon()
if !wpn or !IsValid(wpn) or !wpn.ARC9 then return end
wpn:ToggleCustomize(bf)
end)
net.Receive("ARC9_networkweapon", function(len, ply)
local wpn = net.ReadEntity()
if !wpn.ARC9 then return end
wpn:ReceiveWeapon()
end)

View File

@ -0,0 +1,151 @@
function ARC9:PlayerGetAtts(ply, att)
if !IsValid(ply) then return 0 end
if GetConVar("ARC9_free_atts"):GetBool() then return 999 end
if att == "" then return 999 end
local atttbl = ARC9.GetAttTable(att)
if !atttbl then return 0 end
if atttbl.Free then return 999 end
if !IsValid(ply) then return 0 end
if !ply:IsAdmin() and atttbl.AdminOnly then
return 0
end
if atttbl.InvAtt then att = atttbl.InvAtt end
if !ply.ARC9_AttInv then return 0 end
if !ply.ARC9_AttInv[att] then return 0 end
return ply.ARC9_AttInv[att]
end
function ARC9:PlayerGiveAtt(ply, att, amt)
amt = amt or 1
if !IsValid(ply) then return end
if !ply.ARC9_AttInv then
ply.ARC9_AttInv = {}
end
local atttbl = ARC9.GetAttTable(att)
if !atttbl then print("Invalid att " .. att) return end
if atttbl.Free then return end -- You can't give a free attachment, silly
if atttbl.AdminOnly and !(ply:IsPlayer() and ply:IsAdmin()) then return false end
if atttbl.InvAtt then att = atttbl.InvAtt end
if GetConVar("ARC9_lock_atts"):GetBool() then
if ply.ARC9_AttInv[att] == 1 then return end
ply.ARC9_AttInv[att] = 1
else
ply.ARC9_AttInv[att] = (ply.ARC9_AttInv[att] or 0) + amt
end
end
function ARC9:PlayerTakeAtt(ply, att, amt)
amt = amt or 1
if GetConVar("ARC9_lock_atts"):GetBool() then return end
if !IsValid(ply) then return end
if !ply.ARC9_AttInv then
ply.ARC9_AttInv = {}
end
local atttbl = ARC9.GetAttTable(att)
if !atttbl or atttbl.Free then return end
if atttbl.InvAtt then att = atttbl.InvAtt end
ply.ARC9_AttInv[att] = ply.ARC9_AttInv[att] or 0
if ply.ARC9_AttInv[att] < amt then
return false
end
ply.ARC9_AttInv[att] = ply.ARC9_AttInv[att] - amt
if ply.ARC9_AttInv[att] <= 0 then
ply.ARC9_AttInv[att] = nil
end
return true
end
if CLIENT then
net.Receive("ARC9_sendattinv", function(len, ply)
LocalPlayer().ARC9_AttInv = {}
local count = net.ReadUInt(32)
for i = 1, count do
local attid = net.ReadUInt(ARC9.Attachments_Bits)
local acount = net.ReadUInt(32)
local att = ARC9.Attachments_Index[attid]
LocalPlayer().ARC9_AttInv[att] = acount
end
end)
elseif SERVER then
hook.Add("PlayerDeath", "ARC9_DeathAttInv", function(ply)
-- if GetConVar("ARC9_loseattsondie"):GetBool() then
-- ply.ARC9_AttInv = ply.ARC9_AttInv or {}
-- end
-- if table.Count(ply.ARC9_AttInv) > 0
-- and GetConVar("ARC9_attinv_loseondie"):GetInt() >= 2
-- and !GetConVar("ARC9_free_atts"):GetBool() then
-- local boxEnt = ents.Create("ARC9_att_dropped")
-- boxEnt:SetPos(ply:GetPos() + Vector(0, 0, 4))
-- boxEnt.GiveAttachments = ply.ARC9_AttInv
-- boxEnt:Spawn()
-- boxEnt:SetNWString("boxname", ply:GetName() .. "'s Death Box")
-- local count = 0
-- for i, v in pairs(boxEnt.GiveAttachments) do count = count + v end
-- boxEnt:SetNWInt("boxcount", count)
-- end
end)
hook.Add("PlayerSpawn", "ARC9_SpawnAttInv", function(ply, trans)
if trans then return end
if GetConVar("ARC9_loseattsondie"):GetInt() >= 1 then
ply.ARC9_AttInv = {}
ARC9:PlayerSendAttInv(ply)
end
end)
function ARC9:PlayerSendAttInv(ply)
if GetConVar("ARC9_free_atts"):GetBool() then return end
if !IsValid(ply) then return end
if !ply.ARC9_AttInv then return end
net.Start("ARC9_sendattinv")
net.WriteUInt(table.Count(ply.ARC9_AttInv), 32)
for att, count in pairs(ply.ARC9_AttInv) do
local atttbl = ARC9.GetAttTable(att)
local attid = atttbl.ID
net.WriteUInt(attid, ARC9.Attachments_Bits)
net.WriteUInt(count, 32)
end
net.Send(ply)
end
end

129
lua/arc9/shared/sh_atts.lua Normal file
View File

@ -0,0 +1,129 @@
ARC9.Attachments = {}
ARC9.Attachments_Index = {}
ARC9.Attachments_Count = 0
ARC9.Attachments_Bits = 16
function ARC9.LoadAtts()
ARC9.Attachments_Count = 0
ARC9.Attachments = {}
ARC9.Attachments_Index = {}
local searchdir = "ARC9/common/attachments/"
local files = file.Find(searchdir .. "/*.lua", "LUA")
for _, filename in pairs(files) do
AddCSLuaFile(searchdir .. filename)
end
files = file.Find(searchdir .. "/*.lua", "LUA")
for _, filename in pairs(files) do
if filename == "default.lua" then continue end
ATT = {}
local shortname = string.sub(filename, 1, -5)
include(searchdir .. filename)
if ATT.Ignore then continue end
ARC9.Attachments_Count = ARC9.Attachments_Count + 1
ATT.ShortName = shortname
ATT.ID = ARC9.Attachments_Count
ARC9.Attachments[shortname] = ATT
ARC9.Attachments_Index[ARC9.Attachments_Count] = shortname
if GetConVar("ARC9_generateattentities"):GetBool() and !ATT.DoNotRegister and !ATT.InvAtt and !ATT.Free then
local attent = {}
attent.Base = "ARC9_att"
attent.Icon = ATT.Icon
attent.PrintName = ATT.PrintName or shortname
attent.Spawnable = true
attent.AdminOnly = ATT.AdminOnly or false
attent.AttToGive = shortname
attent.Category = "STALKER+ - Attachments"
print("Registering entity for " .. shortname)
scripted_ents.Register(attent, "ARC9_att_" .. shortname)
end
end
ARC9.Attachments_Bits = math.min(math.ceil(math.log(ARC9.Attachments_Count + 1, 2)), 32)
end
ARC9.LoadAtts()
function ARC9.GetAttTable(name)
local shortname = name
if isnumber(shortname) then
shortname = ARC9.Attachments_Index[name]
end
if ARC9.Attachments[shortname] then
return ARC9.Attachments[shortname]
else
assert(false, "!!!! ARC9 tried to access invalid attachment " .. (shortname or "NIL") .. "!!!")
return {}
end
end
function ARC9.GetAttsForCats(cats)
if !istable(cats) then
cats = {cats}
end
local atts = {}
for i, k in pairs(ARC9.Attachments) do
local attcats = k.Category
if !istable(attcats) then
attcats = {attcats}
end
for _, cat in pairs(cats) do
if table.HasValue(attcats, cat) then
table.insert(atts, k.ShortName)
break
end
end
end
return atts
end
function ARC9.GetMaxAtts()
return GetConVar("ARC9_maxatts"):GetInt()
end
if CLIENT then
concommand.Add("ARC9_reloadatts", function()
if !LocalPlayer():IsSuperAdmin() then return end
net.Start("ARC9_reloadatts")
net.SendToServer()
end)
net.Receive("ARC9_reloadatts", function(len, ply)
ARC9.LoadAtts()
end)
elseif SERVER then
net.Receive("ARC9_reloadatts", function(len, ply)
if !ply:IsSuperAdmin() then return end
ARC9.LoadAtts()
net.Start("ARC9_reloadatts")
net.Broadcast()
end)
end

View File

@ -0,0 +1,28 @@
game.AddParticles( "particles/muzzleflashes_test.pcf" )
game.AddParticles( "particles/muzzleflashes_test_b.pcf" )
PrecacheParticleSystem( "muzzleflash_smg" )
PrecacheParticleSystem( "muzzleflash_smg_bizon" )
PrecacheParticleSystem( "muzzleflash_shotgun" )
PrecacheParticleSystem( "muzzleflash_slug" )
PrecacheParticleSystem( "muzzleflash_slug_flame" )
PrecacheParticleSystem( "muzzleflash_pistol" )
PrecacheParticleSystem( "muzzleflash_pistol_cleric" )
PrecacheParticleSystem( "muzzleflash_pistol_deagle" )
PrecacheParticleSystem( "muzzleflash_suppressed" )
PrecacheParticleSystem( "muzzleflash_mp5" )
PrecacheParticleSystem( "muzzleflash_MINIMI" )
PrecacheParticleSystem( "muzzleflash_m79" )
PrecacheParticleSystem( "muzzleflash_m14" )
PrecacheParticleSystem( "muzzleflash_ak47" )
PrecacheParticleSystem( "muzzleflash_ak74" )
PrecacheParticleSystem( "muzzleflash_m82" )
PrecacheParticleSystem( "muzzleflash_m3" )
PrecacheParticleSystem( "muzzleflash_famas" )
PrecacheParticleSystem( "muzzleflash_g3" )
PrecacheParticleSystem( "muzzleflash_1" )
PrecacheParticleSystem( "muzzleflash_3" )
PrecacheParticleSystem( "muzzleflash_4" )
PrecacheParticleSystem( "muzzleflash_5" )
PrecacheParticleSystem( "muzzleflash_6" )

View File

@ -0,0 +1,94 @@
ARC9.LastEyeAngles = Angle(0, 0, 0)
ARC9.RecoilRise = Angle(0, 0, 0)
function ARC9.Move(ply, mv, cmd)
local wpn = ply:GetActiveWeapon()
if !wpn.ARC9 then return end
local basespd = (Vector(cmd:GetForwardMove(), cmd:GetUpMove(), cmd:GetSideMove())):Length()
basespd = math.min(basespd, mv:GetMaxClientSpeed())
local mult = wpn:GetProcessedValue("Speed", 1)
if wpn:GetSightAmount() > 0 then
if ply:KeyDown(IN_SPEED) then
mult = mult / Lerp(wpn:GetSightAmount(), 1, ply:GetRunSpeed() / ply:GetWalkSpeed())
end
end
mv:SetMaxSpeed(basespd * mult)
mv:SetMaxClientSpeed(basespd * mult)
end
hook.Add("SetupMove", "ARC9.SetupMove", ARC9.Move)
function ARC9.StartCommand(ply, cmd)
local wpn = ply:GetActiveWeapon()
if !wpn.ARC9 then return end
local diff = ARC9.LastEyeAngles - cmd:GetViewAngles()
local recrise = ARC9.RecoilRise
if recrise.p > 0 then
recrise.p = math.Clamp(recrise.p, 0, recrise.p - diff.p)
elseif recrise.p < 0 then
recrise.p = math.Clamp(recrise.p, recrise.p - diff.p, 0)
end
if recrise.y > 0 then
recrise.y = math.Clamp(recrise.y, 0, recrise.y - diff.y)
elseif recrise.y < 0 then
recrise.y = math.Clamp(recrise.y, recrise.y - diff.y, 0)
end
recrise:Normalize()
ARC9.RecoilRise = recrise
if math.abs(wpn:GetRecoilUp()) > 0 or math.abs(wpn:GetRecoilSide()) > 0 then
local eyeang = cmd:GetViewAngles()
local uprec = FrameTime() * wpn:GetRecoilUp() * 20
local siderec = FrameTime() * wpn:GetRecoilSide() * 20
eyeang.p = eyeang.p + uprec
eyeang.y = eyeang.y + siderec
recrise = ARC9.RecoilRise
recrise = recrise + Angle(uprec, siderec, 0)
ARC9.RecoilRise = recrise
cmd:SetViewAngles(eyeang)
-- local aim_kick_v = rec * math.sin(CurTime() * 15) * FrameTime() * (1 - sightdelta)
-- local aim_kick_h = rec * math.sin(CurTime() * 12.2) * FrameTime() * (1 - sightdelta)
-- wpn:SetFreeAimAngle(wpn:GetFreeAimAngle() - Angle(aim_kick_v, aim_kick_h, 0))
end
recrise = ARC9.RecoilRise
local recreset = recrise * FrameTime() * 3.5 * wpn:GetProcessedValue("RecoilAutoControl")
recrise = recrise - recreset
recrise:Normalize()
local eyeang = cmd:GetViewAngles()
-- eyeang.p = math.AngleDifference(eyeang.p, recreset.p)
-- eyeang.y = math.AngleDifference(eyeang.y, recreset.y)
eyeang = eyeang - recreset
cmd:SetViewAngles(eyeang)
ARC9.RecoilRise = recrise
ARC9.LastEyeAngles = cmd:GetViewAngles()
end
hook.Add("StartCommand", "ARC9_StartCommand", ARC9.StartCommand)

View File

@ -0,0 +1,388 @@
ARC9.PhysBullets = {
}
function ARC9:SendBullet(bullet, attacker)
net.Start("ARC9_sendbullet", true)
net.WriteVector(bullet.Pos)
net.WriteAngle(bullet.Vel:Angle())
net.WriteFloat(bullet.Vel:Length())
net.WriteFloat(bullet.Drag)
net.WriteFloat(bullet.Gravity)
net.WriteEntity(bullet.Weapon)
if attacker and attacker:IsValid() and attacker:IsPlayer() and !game.SinglePlayer() then
net.SendOmit(attacker)
else
if game.SinglePlayer() then
net.WriteEntity(attacker)
end
net.Broadcast()
end
end
function ARC9:ShootPhysBullet(wep, pos, vel, tbl)
tbl = tbl or {}
local bullet = {
Penleft = wep:GetProcessedValue("Penetration"),
Gravity = wep:GetProcessedValue("PhysBulletGravity"),
Pos = pos,
Vel = vel,
Drag = wep:GetProcessedValue("PhysBulletDrag"),
Travelled = 0,
StartTime = CurTime(),
Imaginary = false,
Underwater = false,
Weapon = wep,
Attacker = wep:GetOwner(),
Filter = {wep:GetOwner()},
Damaged = {},
Dead = false,
}
for i, k in pairs(tbl) do
bullet[i] = k
end
if bit.band( util.PointContents( pos ), CONTENTS_WATER ) == CONTENTS_WATER then
bullet.Underwater = true
end
table.insert(ARC9.PhysBullets, bullet)
if !game.SinglePlayer() then
local owner = wep:GetOwner()
if owner:IsPlayer() and SERVER then
local latency = engine.TickCount() - owner:GetCurrentCommand():TickCount()
local timestep = engine.TickInterval()
latency = math.max(latency, 300) // can't let people cheat TOO hard
while latency > 0 do
ARC9:ProgressPhysBullet(bullet, timestep)
latency = latency - 1
end
end
end
if SERVER then
-- ARC9:ProgressPhysBullet(bullet, FrameTime())
ARC9:SendBullet(bullet, wep:GetOwner())
end
end
if CLIENT then
net.Receive("ARC9_sendbullet", function(len, ply)
local pos = net.ReadVector()
local ang = net.ReadAngle()
local vel = net.ReadFloat()
local drag = net.ReadFloat()
local grav = net.ReadFloat()
local weapon = net.ReadEntity()
local ent = nil
if game.SinglePlayer() then
ent = net.ReadEntity()
end
local bullet = {
Pos = pos,
Vel = ang:Forward() * vel,
Travelled = 0,
StartTime = CurTime(),
Imaginary = false,
Underwater = false,
Dead = false,
Damaged = {},
Drag = drag,
Attacker = ent,
Gravity = grav,
Weapon = weapon,
Color = weapon:GetProcessedValue("TracerColor"),
Fancy = weapon:GetProcessedValue("FancyBullets")
}
if bit.band( util.PointContents( pos ), CONTENTS_WATER ) == CONTENTS_WATER then
bullet.Underwater = true
end
table.insert(ARC9.PhysBullets, bullet)
end)
end
function ARC9:DoPhysBullets()
local new = {}
for _, i in pairs(ARC9.PhysBullets) do
ARC9:ProgressPhysBullet(i, FrameTime())
if !i.Dead then
table.insert(new, i)
end
end
ARC9.PhysBullets = new
end
hook.Add("Think", "ARC9_DoPhysBullets", ARC9.DoPhysBullets)
local function indim(vec, maxdim)
if math.abs(vec.x) > maxdim or math.abs(vec.y) > maxdim or math.abs(vec.z) > maxdim then
return false
else
return true
end
end
function ARC9:ProgressPhysBullet(bullet, timestep)
timestep = timestep or FrameTime()
if bullet.Dead then return end
local oldpos = bullet.Pos
local oldvel = bullet.Vel
local dir = bullet.Vel:GetNormalized()
local spd = bullet.Vel:Length() * timestep
local drag = bullet.Drag * spd * spd * (1 / 150000)
local gravity = timestep * GetConVar("ARC9_bullet_gravity"):GetFloat() * (bullet.Gravity or 1) * 600
local attacker = bullet.Attacker
local weapon = bullet.Weapon
-- if !IsValid(attacker) then
-- bullet.Dead = true
-- return
-- end
if !IsValid(weapon) then
bullet.Dead = true
return
end
if bullet.Fancy then
weapon:RunHook("HookP_ModifyBullet", bullet)
if bullet.Dead then return end
end
if bullet.Underwater then
drag = drag * 3
end
drag = drag * GetConVar("ARC9_bullet_drag"):GetFloat()
if spd <= 0.001 then bullet.Dead = true return end
local newpos = oldpos + (oldvel * timestep)
local newvel = oldvel - (dir * drag)
newvel = newvel - (Vector(0, 0, 1) * gravity)
if bullet.Imaginary then
-- the bullet has exited the map, but will continue being visible.
bullet.Pos = newpos
bullet.Vel = newvel
bullet.Travelled = bullet.Travelled + spd
if CLIENT and !GetConVar("ARC9_bullet_imaginary"):GetBool() then
bullet.Dead = true
end
else
if attacker:IsPlayer() then
attacker:LagCompensation(true)
end
local tr = util.TraceLine({
start = oldpos,
endpos = newpos,
filter = bullet.Filter,
mask = MASK_SHOT
})
if attacker:IsPlayer() then
attacker:LagCompensation(false)
end
if SERVER then
debugoverlay.Line(oldpos, tr.HitPos, 5, Color(100,100,255), true)
else
debugoverlay.Line(oldpos, tr.HitPos, 5, Color(255,200,100), true)
end
if tr.HitSky then
if CLIENT and GetConVar("ARC9_bullet_imaginary"):GetBool() then
bullet.Imaginary = true
else
bullet.Dead = true
end
bullet.Pos = newpos
bullet.Vel = newvel
bullet.Travelled = bullet.Travelled + spd
if SERVER then
bullet.Dead = true
end
elseif tr.Hit then
bullet.Travelled = bullet.Travelled + (oldpos - tr.HitPos):Length()
bullet.Pos = tr.HitPos
-- if we're the client, we'll get the bullet back when it exits.
if attacker:IsPlayer() then
attacker:LagCompensation(true)
end
if SERVER then
debugoverlay.Cross(tr.HitPos, 5, 5, Color(100,100,255), true)
else
debugoverlay.Cross(tr.HitPos, 5, 5, Color(255,200,100), true)
end
local eid = tr.Entity:EntIndex()
if CLIENT then
-- do an impact effect and forget about it
if !game.SinglePlayer() then
attacker:FireBullets({
Src = oldpos,
Dir = dir,
Distance = spd + 16,
Tracer = 0,
Damage = 0,
IgnoreEntity = bullet.Attacker
})
end
bullet.Dead = true
return
elseif SERVER then
bullet.Damaged[eid] = true
bullet.Dead = true
bullet.Attacker:FireBullets({
Damage = weapon:GetProcessedValue("Damage_Max"),
Force = 8,
Tracer = 0,
Num = 1,
Dir = bullet.Vel:GetNormalized(),
Src = oldpos,
Spread = Vector(0, 0, 0),
Callback = function(att, btr, dmg)
local range = bullet.Travelled
weapon:AfterShotFunction(btr, dmg, range, bullet.Penleft, bullet.Damaged)
end
})
end
if attacker:IsPlayer() then
attacker:LagCompensation(false)
end
else
-- bullet did not impact anything
-- break glass in the way
-- attacker:FireBullets({
-- Src = oldpos,
-- Dir = dir,
-- Distance = spd,
-- Tracer = 0,
-- Damage = 0,
-- IgnoreEntity = bullet.Attacker
-- })
bullet.Pos = tr.HitPos
bullet.Vel = newvel
bullet.Travelled = bullet.Travelled + spd
if bullet.Underwater then
if bit.band( util.PointContents( tr.HitPos ), CONTENTS_WATER ) != CONTENTS_WATER then
local utr = util.TraceLine({
start = tr.HitPos,
endpos = oldpos,
filter = bullet.Attacker,
mask = MASK_WATER
})
if utr.Hit then
local fx = EffectData()
fx:SetOrigin(utr.HitPos)
fx:SetScale(5)
fx:SetFlags(0)
util.Effect("gunshotsplash", fx)
end
bullet.Underwater = false
end
else
if bit.band( util.PointContents( tr.HitPos ), CONTENTS_WATER ) == CONTENTS_WATER then
local utr = util.TraceLine({
start = oldpos,
endpos = tr.HitPos,
filter = bullet.Attacker,
mask = MASK_WATER
})
if utr.Hit then
local fx = EffectData()
fx:SetOrigin(utr.HitPos)
fx:SetScale(5)
fx:SetFlags(0)
util.Effect("gunshotsplash", fx)
end
bullet.Underwater = true
end
end
end
end
local MaxDimensions = 16384 * 4
local WorldDimensions = 16384
if bullet.StartTime <= (CurTime() - GetConVar("ARC9_bullet_lifetime"):GetFloat()) then
bullet.Dead = true
elseif !indim(bullet.Pos, MaxDimensions) then
bullet.Dead = true
elseif !indim(bullet.Pos, WorldDimensions) then
bullet.Imaginary = true
end
end
local head = Material("particle/particle_glow_04")
local tracer = Material("effects/tracer_middle")
function ARC9.DrawPhysBullets()
cam.Start3D()
for _, i in pairs(ARC9.PhysBullets) do
if i.Travelled <= (i.Vel:Length() * 0.1) then continue end
local size = 1
size = size * math.log(EyePos():DistToSqr(i.Pos) - math.pow(256, 2))
size = math.Clamp(size, 0, math.huge)
-- local delta = (EyePos():DistToSqr(i.Pos) / math.pow(20000, 2))
-- size = math.pow(size, Lerp(delta, 1, 2))
-- cam.Start3D()
local col = i.Color or Color(255, 225, 200)
-- local col = Color(255, 225, 200)
render.SetMaterial(head)
render.DrawSprite(i.Pos, size, size, col)
render.SetMaterial(tracer)
render.DrawBeam(i.Pos, i.Pos - i.Vel:GetNormalized() * math.min(i.Vel:Length() * 0.5, 2048), size * 0.75, 0, 1, col)
-- cam.End3D()
end
cam.End3D()
end
hook.Add("PreDrawEffects", "ARC9_DrawPhysBullets", ARC9.DrawPhysBullets)
hook.Add("PostCleanupMap", "ARC9_CleanPhysBullets", function()
ARC9.PhysBullets = {}
end)

View File

@ -0,0 +1,3 @@
function ARC9:EnableTrueNames()
return GetConVar("arc9_truenames"):GetBool()
end

View File

@ -0,0 +1,30 @@
AddCSLuaFile()
ARC9 = {}
ARC9.Overrun = false
local searchdir = "arc9/"
for _, v in pairs(file.Find(searchdir .. "common/*", "LUA")) do
include(searchdir .. "common/" .. v)
AddCSLuaFile(searchdir .. "common/" .. v)
end
for _, v in pairs(file.Find(searchdir .. "shared/*", "LUA")) do
include(searchdir .. "shared/" .. v)
AddCSLuaFile(searchdir .. "shared/" .. v)
end
for _, v in pairs(file.Find(searchdir .. "client/*", "LUA")) do
AddCSLuaFile(searchdir .. "client/" .. v)
if CLIENT then
include(searchdir .. "client/" .. v)
end
end
if SERVER or game.SinglePlayer() then
for _, v in pairs(file.Find(searchdir .. "server/*", "LUA")) do
include(searchdir .. "server/" .. v)
end
end

View File

@ -0,0 +1,38 @@
function EFFECT:Init(data)
local wpn = data:GetEntity()
if !IsValid(wpn) then self:Remove() return end
local muzzle = wpn:GetValue("MuzzleEffect")
local att = data:GetAttachment() or 1
local wm = false
if (LocalPlayer():ShouldDrawLocalPlayer() or wpn.Owner != LocalPlayer()) then
wm = true
att = 1
end
local parent = wpn
if !wm then
parent = LocalPlayer():GetViewModel()
end
parent = wpn:GetMuzzleDevice(wm)
-- if !IsValid(parent) then return end
if muzzle then
ParticleEffectAttach(muzzle, PATTACH_POINT_FOLLOW, parent, att)
end
end
function EFFECT:Think()
return false
end
function EFFECT:Render()
return false
end

View File

@ -0,0 +1,178 @@
EFFECT.Type = 1
EFFECT.Pitch = 100
EFFECT.Model = "models/shells/shell_57.mdl"
EFFECT.AlreadyPlayedSound = false
EFFECT.ShellTime = 0.5
EFFECT.SpawnTime = 0
EFFECT.TypeSettings = {
[1] = {
Model = "models/weapons/shell.mdl",
Sounds = {
"player/pl_shell1.wav",
"player/pl_shell2.wav",
"player/pl_shell3.wav",
}
},
[2] = {
Model = "models/weapons/rifleshell.mdl",
Sounds = {
"player/pl_shell1.wav",
"player/pl_shell2.wav",
"player/pl_shell3.wav",
},
Scale = 0.5
},
[3] = {
Model = "models/weapons/shotgun_shell.mdl",
Sounds = {
"weapons/fx/tink/shotgun_shell1.wav",
"weapons/fx/tink/shotgun_shell2.wav",
"weapons/fx/tink/shotgun_shell3.wav",
}
},
}
function EFFECT:Init(data)
local att = data:GetAttachment()
local ent = data:GetEntity()
local type = data:GetFlags()
self.Type = type or self.Type
local typetbl = self.TypeSettings[self.Type]
if !IsValid(ent) then self:Remove() return end
if !IsValid(ent:GetOwner()) then self:Remove() return end
if LocalPlayer():ShouldDrawLocalPlayer() or ent:GetOwner() != LocalPlayer() then
mdl = ent
att = 2
else
mdl = LocalPlayer():GetViewModel()
end
if !IsValid(ent) then self:Remove() return end
if !mdl or !IsValid(mdl) then self:Remove() return end
if !mdl:GetAttachment(att) then self:Remove() return end
if !typetbl then return end
local origin, ang = mdl:GetAttachment(att).Pos, mdl:GetAttachment(att).Ang
-- ang:RotateAroundAxis(ang:Up(), -90)
-- ang:RotateAroundAxis(ang:Right(), (ent.ShellRotateAngle or Angle(0, 0, 0))[1])
-- ang:RotateAroundAxis(ang:Up(), (ent.ShellRotateAngle or Angle(0, 0, 0))[2])
-- ang:RotateAroundAxis(ang:Forward(), (ent.ShellRotateAngle or Angle(0, 0, 0))[3])
local dir = ang:Forward()
ang:RotateAroundAxis(ang:Forward(), 90)
ang:RotateAroundAxis(ang:Up(), 180)
self:SetPos(origin)
self:SetModel(typetbl.Model)
self:DrawShadow(true)
self:SetAngles(ang)
self:SetModelScale(typetbl.Scale or 1)
-- if !LocalPlayer():ShouldDrawLocalPlayer() and ent:GetOwner() == LocalPlayer() then
-- self:SetNoDraw(true)
-- end
-- table.insert(ent.EjectedShells, self)
self.Sounds = typetbl.Sounds
local s = typetbl.Scale or 1
local pb_vert = 2 * s
local pb_hor = 0.25 * s
local mag = 150
self:PhysicsInitBox(Vector(-pb_vert,-pb_hor,-pb_hor), Vector(pb_vert,pb_hor,pb_hor))
self:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
local phys = self:GetPhysicsObject()
local plyvel = Vector(0, 0, 0)
if IsValid(ent.Owner) then
plyvel = ent.Owner:GetAbsVelocity()
end
phys:Wake()
phys:SetDamping(0, 0)
phys:SetMass(1)
phys:SetMaterial("gmod_silent")
phys:SetVelocity((dir * mag * math.Rand(1, 2)) + plyvel)
phys:AddAngleVelocity(VectorRand() * 100)
phys:AddAngleVelocity(ang:Up() * 2500 * math.Rand(0.75, 1.25))
-- local emitter = ParticleEmitter(origin)
-- for i = 1, 3 do
-- local particle = emitter:Add("particles/smokey", origin + (dir * 2))
-- if (particle) then
-- particle:SetVelocity(VectorRand() * 10 + (dir * i * math.Rand(48, 64)) + plyvel)
-- particle:SetLifeTime(0)
-- particle:SetDieTime(math.Rand(0.05, 0.15))
-- particle:SetStartAlpha(math.Rand(40, 60))
-- particle:SetEndAlpha(0)
-- particle:SetStartSize(0)
-- particle:SetEndSize(math.Rand(18, 24))
-- particle:SetRoll(math.rad(math.Rand(0, 360)))
-- particle:SetRollDelta(math.Rand(-1, 1))
-- particle:SetLighting(true)
-- particle:SetAirResistance(96)
-- particle:SetGravity(Vector(-7, 3, 20))
-- particle:SetColor(150, 150, 150)
-- end
-- end
self.SpawnTime = CurTime()
end
function EFFECT:PhysicsCollide()
if self.AlreadyPlayedSound then return end
sound.Play(self.Sounds[math.random(#self.Sounds)], self:GetPos(), 65, 100, 1)
self.AlreadyPlayedSound = true
end
function EFFECT:Think()
if self:GetVelocity():Length() > 0 then self.SpawnTime = CurTime() end
if (self.SpawnTime + self.ShellTime) <= CurTime() then
if !IsValid(self) then return end
self:SetRenderFX( kRenderFxFadeFast )
if (self.SpawnTime + self.ShellTime + 0.25) <= CurTime() then
if !IsValid(self:GetPhysicsObject()) then return end
self:GetPhysicsObject():EnableMotion(false)
if (self.SpawnTime + self.ShellTime + 0.5) <= CurTime() then
self:Remove()
return
end
end
end
return true
end
function EFFECT:Render()
if !IsValid(self) then return end
self:DrawModel()
end
function EFFECT:DrawTranslucent()
self:DrawModel()
end

View File

@ -0,0 +1,127 @@
SWEP.MultiSightTable = {
-- {
-- Pos = Vector(0, 0, 0),
-- Ang = Angle(0, 0, 0)
-- }
}
function SWEP:BuildMultiSight()
self.MultiSightTable = {}
local keepbaseirons = true
for i, slottbl in ipairs(self:GetSubSlotList()) do
if !slottbl.Installed then continue end
local atttbl = ARC9.GetAttTable(slottbl.Installed)
if atttbl.Sights then
for _, sight in pairs(atttbl.Sights) do
local s = self:GenerateAutoSight(sight, slottbl)
s.atttbl = atttbl
s.ExtraSightDistance = slottbl.ExtraSightDistance or 0
table.insert(self.MultiSightTable, s)
end
if !slottbl.KeepBaseIrons and !atttbl.KeepBaseIrons then
keepbaseirons = false
end
end
end
if keepbaseirons then
local tbl = {}
table.Add(tbl, self.BaseSights)
table.Add(self.MultiSightTable, self.BaseSights)
self.MultiSightTable = tbl
end
if self.MultiSightIndex > #self.MultiSightTable then
self.MultiSightIndex = 1
end
end
function SWEP:GenerateAutoSight(sight, slottbl)
local pos, ang = self:GetAttPos(slottbl, false, true)
pos = pos - (ang:Right() * sight.Pos.x)
pos = pos - (ang:Forward() * sight.Pos.y)
pos = pos - (ang:Up() * sight.Pos.z)
ang:RotateAroundAxis(ang:Right(), sight.Ang.p)
ang:RotateAroundAxis(ang:Up(), sight.Ang.y)
ang:RotateAroundAxis(ang:Forward(), sight.Ang.r)
debugoverlay.Axis(pos, ang, 16, 1, true)
local s_pos = Vector(0, 0, 0)
local s_ang = Angle(0, 0, 0)
local up, forward, right = s_ang:Up(), s_ang:Forward(), s_ang:Right()
s_pos = s_pos + (right * pos.x)
s_pos = s_pos + (forward * pos.y)
s_pos = s_pos + (up * -pos.z)
return {
Pos = s_pos,
Ang = Angle(0, 0, 0),
Magnification = sight.Magnification or 1,
-- ExtraAng = ang
}
end
SWEP.MultiSightIndex = 1
function SWEP:GetSight()
if GetConVar("developer"):GetBool() then
self:BuildMultiSight()
end
return self.MultiSightTable[self.MultiSightIndex] or self.IronSights
end
function SWEP:GetSightPositions()
local s = self:GetSight()
return s.Pos, s.Ang
end
function SWEP:GetExtraSightPositions()
local s = self:GetSight()
local se = s.ExtraPos or Vector(0, 0, 0)
se.y = se.y - (s.ExtraSightDistance or 0)
return se, s.ExtraAng or Angle(0, 0, 0)
end
function SWEP:SwitchMultiSight()
local old_msi = self.MultiSightIndex
self.MultiSightIndex = self.MultiSightIndex + 1
if self.MultiSightIndex > #self.MultiSightTable then
self.MultiSightIndex = 1
end
if self.MultiSightIndex != old_msi then
// eh put some code in here
end
end
function SWEP:GetMagnification()
local sight = self:GetSight()
return sight.Magnification or 1
end
function SWEP:AdjustMouseSensitivity()
if self:GetSightAmount() <= 0 then return end
local mag = self:GetMagnification()
local fov = GetConVar("fov_desired"):GetFloat()
local sight = self:GetSight()
if sight.atttbl and sight.atttbl.RTScope then
mag = mag + (fov / sight.atttbl.RTScopeFOV)
end
if mag > 0 then
return 1 / mag
end
end

View File

@ -0,0 +1,29 @@
SWEP.SmoothedMagnification = 1
function SWEP:CalcView(ply, pos, ang, fov)
local rec = (self:GetLastRecoilTime() + self:GetProcessedValue("RecoilResetTime")) - CurTime()
rec = rec * 10
rec = math.Clamp(rec, 0, 1)
rec = rec * self:GetProcessedValue("RecoilKick")
if rec > 0 then
ang.r = ang.r + (math.sin(CurTime() * 70.151) * rec * 0.25)
end
local mag = 1
if self:GetSightAmount() > 0 then
mag = Lerp(self:GetSightAmount(), 1, self:GetMagnification())
end
local diff = math.abs(self.SmoothedMagnification - mag)
self.SmoothedMagnification = math.Approach(self.SmoothedMagnification, mag, FrameTime() * diff * 10)
fov = fov / self.SmoothedMagnification
return pos, ang, fov
end

View File

@ -0,0 +1,504 @@
local function multlinetext(text, maxw, font)
local content = {}
local tline = ""
local x = 0
surface.SetFont(font)
local newlined = string.Split(text, "\n")
for _, line in pairs(newlined) do
local words = string.Split(line, " ")
for _, word in pairs(words) do
local tx = surface.GetTextSize(word)
if x + tx >= maxw then
table.insert(content, tline)
tline = ""
x = surface.GetTextSize(word)
end
tline = tline .. word .. " "
x = x + surface.GetTextSize(word .. " ")
end
table.insert(content, tline)
tline = ""
x = 0
end
return content
end
local function PaintScrollBar(panel, w, h)
local ss = ScreenScale(2)
local s = ss * 2
draw.RoundedBox(ss * 1, (w - s) / 2, 0, s, h, col_fg)
end
// span: panel that hosts the rotating text
// txt: the text to draw
// x: where to start the crop
// y: where to start the crp
// tx, ty: where to draw the text
// maxw: maximum width
// only: don't advance text
local function DrawTextRot(span, txt, x, y, tx, ty, maxw, only)
local tw, th = surface.GetTextSize(txt)
span.TextRot = span.TextRot or {}
if tw > maxw then
local realx, realy = span:LocalToScreen(x, y)
render.SetScissorRect(realx, realy, realx + maxw, realy + (th * 2), true)
span.TextRot[txt] = span.TextRot[txt] or 0
if !only then
span.StartTextRot = span.StartTextRot or CurTime()
span.TextRotState = span.TextRotState or 0 -- 0: start, 1: moving, 2: end
if span.TextRotState == 0 then
span.TextRot[txt] = 0
if span.StartTextRot < CurTime() - 2 then
span.TextRotState = 1
end
elseif span.TextRotState == 1 then
span.TextRot[txt] = span.TextRot[txt] + (FrameTime() * ScreenScale(16))
if span.TextRot[txt] >= (tw - maxw) + ScreenScale(8) then
span.StartTextRot = CurTime()
span.TextRotState = 2
end
elseif span.TextRotState == 2 then
if span.StartTextRot < CurTime() - 2 then
span.TextRotState = 3
span.StartTextRot = CurTime()
end
elseif span.TextRotState == 3 then
span.TextRot[txt] = span.TextRot[txt] - (FrameTime() * ScreenScale(16))
if span.TextRot[txt] <= 0 then
span.StartTextRot = CurTime()
span.TextRotState = 0
end
end
end
surface.SetTextPos(tx - span.TextRot[txt], ty)
surface.DrawText(txt)
render.SetScissorRect(0, 0, 0, 0, false)
else
surface.SetTextPos(tx, ty)
surface.DrawText(txt)
end
end
SWEP.CustomizeHUD = nil
SWEP.CustomizeBoxes = nil
local mat_circle = Material("ARC9/circle.png", "mips smooth")
local col_hi = Color(255, 237, 193)
local col_lo = Color(255, 255, 255)
function SWEP:RefreshCustomizeMenu()
if !self.CustomizeHUD then return end
self:CreateCustomizeBoxes(self.CustomizeHUD)
if self.CustomizeSelectAddr then
self:CreateCustomizeSelectMenu(self.CustomizeHUD, self:LocateSlotFromAddress(self.CustomizeSelectAddr))
end
end
function SWEP:CreateCustomizeBoxes(panel)
for _, i in pairs(self.CustomizeBoxes or {}) do
i:Remove()
end
self.CustomizeBoxes = {}
for _, i in pairs(self:GetSubSlotList()) do
local cbox = vgui.Create("DPanel", panel)
cbox.slottbl = i
cbox:SetSize(ScreenScale(32), ScreenScale(40))
cbox:SetPos(0, 0)
cbox.Paint = function(self2, w, h)
local apos, aang = self:GetAttPos(self2.slottbl, false)
apos = apos + (aang:Up() * 3.5)
local col1 = col_hi
if self:GetSlotBlocked(self2.slottbl) and !self2.slottbl.Installed then
col1 = Color(255, 100, 100)
end
cam.Start3D(nil, nil, self.ViewModelFOV)
local screenpos = apos:ToScreen()
cam.End3D()
local sx = screenpos.x
local sy = screenpos.y
sx = sx - (cbox:GetWide() * 0.5)
sy = sy - (cbox:GetTall() * 0.9)
sx = math.Clamp(sx, 0, ScrW() - ScreenScale(32))
sy = math.Clamp(sy, 0, ScrH() - ScreenScale(40))
self2:SetPos(sx, sy)
surface.SetDrawColor(col1)
surface.SetMaterial(mat_circle)
local s = ScreenScale(8)
surface.DrawTexturedRect((w - s) / 2, h - s, s, s)
end
cbox:Paint(0, 0)
csquare = vgui.Create("DPanel", cbox)
csquare.slottbl = i
csquare:SetSize(ScreenScale(32), ScreenScale(32))
csquare:SetPos(0, 0)
csquare.OnMousePressed = function(self2, kc)
if kc == MOUSE_LEFT then
if !self:GetSlotBlocked(self2.slottbl) or self2.slottbl.Installed then
if self:CreateCustomizeSelectMenu(panel, self2.slottbl) then
self.CustomizeSelectAddr = self2.slottbl.Address
self:CreateCustomizeBoxes(panel)
end
end
elseif kc == MOUSE_RIGHT then
if self:Detach(self2.slottbl.Address) then
self.CustomizeSelectAddr = self2.slottbl.Address
self:CreateCustomizeBoxes(panel)
self:CreateCustomizeSelectMenu(panel)
end
end
end
csquare.Paint = function(self2, w, h)
local col1 = Color(0, 0, 0, 150)
local col2 = col_hi
local col3 = Color(255, 255, 255)
if self2:IsHovered() then
col1 = Color(100, 100, 100, 150)
col2 = Color(0, 0, 0, 255)
col3 = Color(50, 50, 50)
end
if self:GetSlotBlocked(self2.slottbl) and !self2.slottbl.Installed then
col1 = Color(50, 0, 0, 150)
col2 = Color(255, 100, 100)
col3 = Color(200, 0, 0)
end
surface.SetDrawColor(col1)
surface.DrawRect(0, 0, w, h)
if self2.slottbl.Installed then
local atttbl = ARC9.GetAttTable(self2.slottbl.Installed)
surface.SetDrawColor(col3)
surface.SetMaterial(atttbl.Icon)
surface.DrawTexturedRect(0, 0, w, h)
end
local txt = self2.slottbl.PrintName or ""
if self2.slottbl.Installed then
local atttbl = ARC9.GetAttTable(self2.slottbl.Installed)
txt = atttbl.CompactName or atttbl.PrintName or atttbl.ShortName
end
surface.SetTextColor(col2)
surface.SetFont("ARC9_6")
-- local tw = surface.GetTextSize(txt)
-- surface.SetTextPos(0, 0)
-- surface.DrawText(txt)
DrawTextRot(self2, txt, 0, ScreenScale(32 - 6 - 1), ScreenScale(2), ScreenScale(32 - 6 - 1), ScreenScale(32))
surface.SetDrawColor(col2)
local outlines = ScreenScale(0.25)
for j = 0, math.ceil(outlines) do
surface.DrawOutlinedRect(j, j, w - (2 * j), h - (2 * j))
end
end
table.insert(self.CustomizeBoxes, cbox)
end
end
SWEP.CustomizeSelectAddr = nil
SWEP.CustomizeSelectMenu = nil
function SWEP:CreateCustomizeSelectMenu(panel, slottbl)
if self.CustomizeSelectMenu then
self.CustomizeSelectMenu:Remove()
self.CustomizeSelectMenu = nil
end
if !slottbl then return end
local bg = vgui.Create("DPanel", panel)
bg:SetSize(ScreenScale(96), ScrH() - ScreenScale(32))
bg:SetPos(ScreenScale(16), ScreenScale(16))
bg.Paint = function(self2, w, h)
local col1 = Color(0, 0, 0, 150)
local col2 = col_hi
surface.SetDrawColor(col1)
surface.DrawRect(0, ScreenScale(18), w, h)
surface.SetDrawColor(col2)
surface.DrawLine(0, ScreenScale(18), w, ScreenScale(18))
surface.SetTextColor(col_hi)
surface.SetTextPos(ScreenScale(4), 0)
surface.SetFont("ARC9_16")
DrawTextRot(self2, slottbl.PrintName or "Attachment", 0, 0, ScreenScale(4), 0, ScreenScale(96), false)
end
-- Menu for attachments
attmenu = vgui.Create("DScrollPanel", bg)
attmenu:SetPos(0, ScreenScale(18))
attmenu:SetSize(ScreenScale(96), ScrH() - ScreenScale(32 + 18))
-- attmenu.Paint = function(self2, w, h)
-- draw.RoundedBox(2, 0, 0, w, h, col_fg)
-- end
local scroll_2 = attmenu:GetVBar()
-- scroll_2.AlreadySet = false
-- scroll_2.Paint = function(self2, w, h)
-- if !self2.AlreadySet then
-- self2:SetScroll(self.Inv_Scroll[self.Inv_SelectedSlot or 0] or 0)
-- self2.AlreadySet = true
-- end
-- local scroll = self2:GetScroll()
-- self.Inv_Scroll[self.Inv_SelectedSlot or 0] = scroll
-- end
scroll_2.btnUp.Paint = function(span, w, h)
end
scroll_2.btnDown.Paint = function(span, w, h)
end
scroll_2.btnGrip.Paint = PaintScrollBar
local slot = slottbl
if !slot then return end
local atts = ARC9.GetAttsForCats(slottbl.Category or "")
table.sort(atts, function(a, b)
a = a or ""
b = b or ""
if a == "" or b == "" then return true end
local atttbl_a = ARC9.GetAttTable(a)
local atttbl_b = ARC9.GetAttTable(b)
local order_a = 0
local order_b = 0
order_a = atttbl_a.SortOrder or order_a
order_b = atttbl_b.SortOrder or order_b
if order_a == order_b then
return (atttbl_a.PrintName or "") < (atttbl_b.PrintName or "")
end
return order_a < order_b
end)
for i, att in pairs(atts) do
local atttbl = ARC9.GetAttTable(att)
local attbtn = vgui.Create("DScrollPanel", attmenu)
attbtn:SetSize(ScreenScale(96), ScreenScale(12))
attbtn:Dock(TOP)
attbtn.att = att
attbtn.slottbl = slottbl
attbtn.address = slottbl.Address
attbtn.OnMousePressed = function(self2, kc)
if kc == MOUSE_LEFT then
self:Attach(self2.slottbl.Address, self2.att)
self.CustomizeSelectAddr = self2.slottbl.Address
self:RefreshCustomizeMenu()
elseif kc == MOUSE_RIGHT then
self:Detach(self2.slottbl.Address)
self.CustomizeSelectAddr = self2.slottbl.Address
self:RefreshCustomizeMenu()
end
end
attbtn.Paint = function(self2, w, h)
surface.SetDrawColor(col_hi)
surface.DrawLine(0, h-1, w, h-1)
local attached = self2.slottbl.Installed == self2.att
local col1 = Color(0, 0, 0, 150)
local col2 = col_hi
if self2:IsHovered() or attached then
col1 = col_hi
col2 = Color(0, 0, 0, 255)
end
if self2:IsHovered() and attached then
col1 = col_lo
col2 = Color(0, 0, 0, 255)
end
local canattach = self:CanAttach(self2.slottbl.Address, self2.att, self2.slottbl.slottbl)
if !canattach then
col1 = Color(50, 0, 0, 150)
col2 = Color(255, 100, 100)
col3 = Color(200, 0, 0)
end
surface.SetDrawColor(col1)
surface.DrawRect(0, 0, w, h)
local icon = atttbl.Icon
surface.SetDrawColor(col2)
surface.SetMaterial(icon)
surface.DrawTexturedRect(0, 0, ScreenScale(12), ScreenScale(12))
surface.SetTextColor(col2)
surface.SetTextPos(ScreenScale(13), 0)
surface.SetFont("ARC9_12")
DrawTextRot(self2, atttbl.CompactName or atttbl.PrintName or atttbl.ShortName, ScreenScale(12), 0, ScreenScale(13), 0, ScreenScale(96 - 12), false)
end
end
self.CustomizeSelectMenu = bg
end
SWEP.MenuRotation = Angle(0, 0, 0)
SWEP.MenuPan = Vector(0, 0, 0)
SWEP.MenuRotating = false
SWEP.MenuZooming = false
SWEP.LastMouseX = 0
SWEP.LastMouseY = 0
function SWEP:CreateCustomizeHUD()
self:RemoveCustomizeHUD()
self.MenuRotation = Angle(0, 0, 0)
self.MenuPan = Vector(0, 0, 0)
self.MenuRotating = false
self.MenuZooming = false
gui.EnableScreenClicker(true)
local bg = vgui.Create("DPanel")
self.CustomizeHUD = bg
local scrw = ScrW()
local scrh = ScrH()
local airgap = ScreenScale(8)
local smallgap = ScreenScale(4)
bg:SetPos(0, 0)
bg:SetSize(ScrW(), ScrH())
bg.OnRemove = function(self2)
if !IsValid(self) then return end
-- self:SavePreset()
end
bg.OnMousePressed = function(self2, kc)
if kc == MOUSE_LEFT then
self.MenuRotating = true
self.LastMousePos = Vec
self.LastMouseX, self.LastMouseY = input.GetCursorPos()
elseif kc == MOUSE_RIGHT then
self.MenuZooming = true
self.LastMousePos = Vec
self.LastMouseX, self.LastMouseY = input.GetCursorPos()
end
end
bg.OnMouseWheeled = function(self2, sd)
self.MenuPan = self.MenuPan + Vector(0, 0, sd)
end
bg.Paint = function(self2, w, h)
if !IsValid(self) then
self:Remove()
gui.EnableScreenClicker(false)
end
local name_txt = self:GetValue("PrintName")
surface.SetFont("ARC9_16")
local name_w = surface.GetTextSize(name_txt)
surface.SetTextPos(w - name_w - ScreenScale(14), airgap)
surface.SetTextColor(0, 0, 0)
surface.DrawText(name_txt)
if self.MenuRotating or self.MenuZooming then
if !input.IsMouseDown(MOUSE_LEFT) then
self.MenuRotating = false
end
if !input.IsMouseDown(MOUSE_RIGHT) then
self.MenuZooming = false
end
local mousex, mousey = input.GetCursorPos()
local dx = mousex - self.LastMouseX
local dy = mousey - self.LastMouseY
dx = dx * 200 / ScrW()
dy = dy * 200 / ScrW()
if self.MenuRotating then
self.MenuRotation = self.MenuRotation + Angle(dx, dy, 0)
end
if self.MenuZooming then
self.MenuPan = self.MenuPan + Vector(dx, dy, 0)
end
self.MenuRotation:Normalize()
self.LastMouseX, self.LastMouseY = input.GetCursorPos()
end
end
timer.Simple(0, function()
self:RefreshCustomizeMenu()
end)
end
function SWEP:RemoveCustomizeHUD()
if self.CustomizeHUD then
self.CustomizeHUD:Remove()
gui.EnableScreenClicker(false)
self.CustomizeHUD = nil
end
end
function SWEP:DrawCustomizeHUD()
local customize = self:GetCustomize()
if customize and !self.CustomizeHUD then
self:CreateCustomizeHUD()
elseif !customize and self.CustomizeHUD then
self:RemoveCustomizeHUD()
end
lastcustomize = self:GetCustomize()
end

View File

@ -0,0 +1,82 @@
function SWEP:PreDrawViewModel()
self:DoRHIK()
if self:GetCustomize() then
DrawBokehDOF( 10, 1, 0.1 )
cam.Start2D()
surface.SetDrawColor(0, 0, 0, 150)
surface.DrawRect(0, 0, ScrW(), ScrH())
cam.End2D()
end
self:DoBodygroups(false)
self:GetVM():SetPoseParameter("sights", self:GetSightAmount())
if GetConVar("ARC9_benchgun"):GetBool() then
cam.Start3D()
end
cam.IgnoreZ(true)
end
function SWEP:PostDrawViewModel()
cam.IgnoreZ(false)
if GetConVar("ARC9_benchgun"):GetBool() then
cam.End3D()
end
end
function SWEP:ViewModelDrawn()
self:DrawCustomModel(false)
-- self:DrawLasers()
end
function SWEP:DrawCustomModel(wm)
if !wm and !IsValid(self:GetOwner()) then return end
if !wm and self:GetOwner():IsNPC() then return end
local mdl = self.VModel
if wm then
mdl = self.WModel
end
if !mdl then
self:SetupModel(wm)
mdl = self.VModel
if wm then
mdl = self.WModel
end
end
for _, model in pairs(mdl) do
local slottbl = model.slottbl
-- local atttbl = model.atttbl
local apos, aang = self:GetAttPos(slottbl, wm)
model:SetPos(apos)
model:SetAngles(aang)
model:SetRenderOrigin(apos)
model:SetRenderAngles(aang)
if !wm and model.atttbl.HoloSight then
self:DoHolosight(model, model.atttbl)
end
if !wm and model.atttbl.RTScope then
self:DoRTScope(model, model.atttbl)
end
if !model.NoDraw then
model:DrawModel()
end
end
if !wm then
self:DrawFlashlightsVM()
end
end

View File

@ -0,0 +1,90 @@
function SWEP:DoHolosight(mdl, atttbl)
if ARC9.OverDraw then return end
if self:GetOwner() != LocalPlayer() then return end
local ref = 64
render.UpdateScreenEffectTexture()
render.ClearStencil()
render.SetStencilEnable(true)
render.SetStencilCompareFunction(STENCIL_ALWAYS)
render.SetStencilPassOperation(STENCIL_REPLACE)
render.SetStencilFailOperation(STENCIL_KEEP)
render.SetStencilZFailOperation(STENCIL_KEEP)
render.SetStencilWriteMask(255)
render.SetStencilTestMask(255)
render.SetBlend(0)
render.SetStencilReferenceValue(ref)
mdl:DrawModel()
render.SetBlend(1)
render.SetStencilPassOperation(STENCIL_KEEP)
render.SetStencilCompareFunction(STENCIL_EQUAL)
-- cam.Start2D()
-- surface.SetDrawColor(255, 255, 255)
-- surface.DrawRect(0, 0, ScrW(), ScrH())
-- render.SetColorMaterial()
-- render.DrawScreenQuad()
local img = atttbl.HoloSightReticle
if img then
local pos = self:GetOwner():EyePos()
pos = pos + mdl:GetAngles():Forward() * 9000
-- cam.Start3D()
local s = atttbl.HoloSightSize
render.SetMaterial(img)
local up = mdl:GetAngles():Up()
local right = mdl:GetAngles():Right()
local v1 = pos + (up * s / 2) - (right * s / 2)
local v2 = pos + (up * s / 2) + (right * s / 2)
local v3 = pos - (up * s / 2) + (right * s / 2)
local v4 = pos - (up * s / 2) - (right * s / 2)
-- render.DrawQuadEasy(pos, -mdl:GetAngles():Forward(), s, s, atttbl.HoloSightColor or Color(255, 255, 255))
render.DrawQuad(v1, v2, v3, v4, atttbl.HoloSightColor or Color(255, 255, 255))
-- cam.End3D()
-- local toscreen = pos:ToScreen()
-- local x = toscreen.x
-- local y = toscreen.y
-- local ss = ScreenScale(32)
-- local sx = x - (ss / 2)
-- local sy = y - (ss / 2)
-- local shakey = math.min(cross * 35, 3)
-- sx = sx + math.Round(math.Rand(-shakey, shakey))
-- sy = sy + math.Round(math.Rand(-shakey, shakey))
-- surface.SetMaterial(img)
-- surface.SetDrawColor(255, 255, 255, 255)
-- surface.DrawTexturedRect(sx, sy, ss, ss)
-- surface.SetDrawColor(0, 0, 0)
-- surface.DrawRect(0, 0, w, sy)
-- surface.DrawRect(0, sy + ss, w, h - sy)
-- surface.DrawRect(0, 0, sx, h)
-- surface.DrawRect(sx + ss, 0, w - sx, h)
end
-- cam.End2D()
render.SetStencilEnable(false)
end

View File

@ -0,0 +1,42 @@
function SWEP:ShouldDrawCrosshair()
-- return false
end
function SWEP:DoDrawCrosshair(x, y)
return true
end
function SWEP:GetBinding(bind)
local t_bind = input.LookupBinding(bind)
if !t_bind then
t_bind = "BIND " .. bind .. "!"
end
return string.upper(t_bind)
end
function SWEP:DrawHUD()
self:DrawCustomizeHUD()
end
SWEP.Mat_Select = nil
function SWEP:DrawWeaponSelection(x, y, w, h, a)
-- if !self.Mat_Select then
-- self.Mat_Select = Material("entities/" .. self:GetClass() .. ".png")
-- end
-- surface.SetDrawColor(255, 255, 255, 255)
-- surface.SetMaterial(self.Mat_Select)
-- if w > h then
-- y = y - ((w - h) / 2)
-- end
-- surface.DrawTexturedRect(x, y, w, w)
end
function SWEP:RangeUnitize(range)
return tostring(math.Round(range)) .. " HU"
end

View File

@ -0,0 +1,144 @@
SWEP.Flashlights = {} -- tracks projectedlights
-- {{att = int, light = ProjectedTexture}}
function SWEP:GetHasFlashlights()
for i, k in pairs(self:GetAttachmentList()) do
local atttbl = ARC9.GetAttTable(k)
if atttbl.Flashlight then return true end
end
return false
end
function SWEP:CreateFlashlightsVM()
self:KillFlashlights()
self.Flashlights = {}
local total_lights = 0
for _, k in pairs(self:GetSubSlotList()) do
if !k.Installed then continue end
local atttbl = ARC9.GetAttTable(k.Installed)
if atttbl.Flashlight then
local newlight = {
slottbl = k,
light = ProjectedTexture(),
col = Color(255, 255, 255),
br = 3,
}
total_lights = total_lights + 1
local l = newlight.light
if !IsValid(l) then continue end
table.insert(self.Flashlights, newlight)
l:SetFOV(atttbl.FlashlightFOV or 50)
l:SetFarZ(atttbl.FlashlightDistance or 1024)
l:SetNearZ(4)
l:SetQuadraticAttenuation(100)
l:SetColor(atttbl.FlashlightColor or Color(255, 255, 255))
l:SetTexture(atttbl.FlashlightMaterial or "effects/flashlight001")
l:SetBrightness(3)
l:SetEnableShadows(true)
l:Update()
local g_light = {
Weapon = self,
ProjectedTexture = l
}
table.insert(ARC9.FlashlightPile, g_light)
end
end
if total_lights > 2 then -- you are a madman
for i, k in pairs(self.Flashlights) do
if k.light:IsValid() then k.light:SetEnableShadows(false) end
end
end
end
function SWEP:KillFlashlights()
self:KillFlashlightsVM()
-- self:KillFlashlightsWM()
end
function SWEP:KillFlashlightsVM()
if !self.Flashlights then return end
for i, k in pairs(self.Flashlights) do
if k.light and k.light:IsValid() then
k.light:Remove()
end
end
self.Flashlights = nil
end
function SWEP:DrawFlashlightsVM()
if !self.Flashlights then
self:CreateFlashlightsVM()
end
for i, k in pairs(self.Flashlights) do
local model = (k.slottbl or {}).VModel
if !model then continue end
local pos, ang
if !model then
pos = self:GetOwner():EyePos()
ang = self:GetOwner():EyeAngles()
else
pos = model:GetPos()
ang = model:GetAngles()
end
local tr = util.TraceLine({
start = self:GetOwner():EyePos(),
endpos = self:GetOwner():EyePos() - -ang:Forward() * 128,
mask = MASK_OPAQUE,
filter = LocalPlayer(),
})
if tr.Fraction < 1 then -- We need to push the flashlight back
local tr2 = util.TraceLine({
start = self:GetOwner():EyePos(),
endpos = self:GetOwner():EyePos() + -ang:Forward() * 128,
mask = MASK_OPAQUE,
filter = LocalPlayer(),
})
-- push it as back as the area behind us allows
pos = pos + -ang:Forward() * 128 * math.min(1 - tr.Fraction, tr2.Fraction)
end
-- ang:RotateAroundAxis(ang:Up(), 90)
k.light:SetPos(pos)
k.light:SetAngles(ang)
k.light:Update()
-- local col = k.col
-- local dl = DynamicLight(self:EntIndex())
-- if dl then
-- dl.pos = pos
-- dl.r = col.r
-- dl.g = col.g
-- dl.b = col.b
-- dl.brightness = k.br or 2
-- -- print(z / maxz)
-- dl.Decay = 1000 / 0.1
-- dl.dietime = CurTime() + 0.1
-- dl.size = (k.br or 2) * 64
-- end
end
end

View File

@ -0,0 +1,196 @@
function SWEP:GetAttPos(slottbl, wm, idle)
idle = idle or false
local parentmdl = self:GetVM()
if wm then
if slottbl.WMBase then
parentmdl = self:GetOwner()
else
parentmdl = self.WModel[1]
end
end
if idle then
parentmdl = ClientsideModel(self.ViewModel)
parentmdl:SetPos(Vector(0, 0, 0))
parentmdl:SetAngles(Angle(0, 0, 0))
parentmdl:SetNoDraw(true)
local anim = self:TranslateSequence("idle")
local seq = parentmdl:LookupSequence(anim)
parentmdl:ResetSequence(seq)
parentmdl:SetPoseParameter("sights", 1)
parentmdl:SetupBones()
end
local bone = slottbl.Bone
local atttbl = {}
if slottbl.WMBase then
bone = "ValveBiped.Bip01_R_Hand"
end
if slottbl.Installed then
atttbl = ARC9.GetAttTable(slottbl.Installed)
end
local offset_pos = slottbl.Pos or Vector(0, 0, 0)
local offset_ang = slottbl.Ang or Angle(0, 0, 0)
local boneindex = parentmdl:LookupBone(bone)
if !boneindex then return Vector(0, 0, 0), Angle(0, 0, 0) end
local bonemat = parentmdl:GetBoneMatrix(boneindex)
if bonemat then
bpos = bonemat:GetTranslation()
bang = bonemat:GetAngles()
end
local apos, aang
apos = bpos + bang:Forward() * offset_pos.x
apos = apos + bang:Right() * offset_pos.y
apos = apos + bang:Up() * offset_pos.z
aang = Angle()
aang:Set(bang)
aang:RotateAroundAxis(aang:Right(), offset_ang.p)
aang:RotateAroundAxis(aang:Up(), offset_ang.y)
aang:RotateAroundAxis(aang:Forward(), offset_ang.r)
local moffset = (atttbl.ModelOffset or Vector(0, 0, 0)) * (slottbl.Scale or 1)
apos = apos + aang:Forward() * moffset.x
apos = apos + aang:Right() * moffset.y
apos = apos + aang:Up() * moffset.z
if idle then
SafeRemoveEntity(parentmdl)
end
return apos, aang
end
function SWEP:CreateAttachmentModel(wm, atttbl, slottbl)
local model = atttbl.Model
if wm and atttbl.WorldModel then
model = atttbl.WorldModel
end
local csmodel = ClientsideModel(model)
if !IsValid(csmodel) then return end
csmodel:SetNoDraw(true)
csmodel.atttbl = atttbl
csmodel.slottbl = slottbl
local scale = Matrix()
local vec = Vector(1, 1, 1) * (atttbl.Scale or 1)
vec = vec * (slottbl.Scale or 1)
scale:Scale(vec)
csmodel:EnableMatrix("RenderMultiply", scale)
local tbl = {
Model = csmodel,
Weapon = self
}
table.insert(ARC9.CSModelPile, tbl)
if wm then
table.insert(self.WModel, csmodel)
else
table.insert(self.VModel, csmodel)
end
return csmodel
end
function SWEP:SetupModel(wm)
self:KillModel()
if !wm and !IsValid(self:GetOwner()) then return end
if !wm then
self.VModel = {}
else
self.WModel = {}
local csmodel = ClientsideModel(self.MirrorModel or self.ViewModel)
if !IsValid(csmodel) then return end
csmodel:SetNoDraw(true)
csmodel.atttbl = {}
csmodel.slottbl = {
WMBase = true,
Pos = self.WorldModelOffset.Pos,
Ang = self.WorldModelOffset.Ang
}
local scale = Matrix()
local vec = Vector(1, 1, 1) * (self.WorldModelOffset.Scale or 1)
scale:Scale(vec)
csmodel:EnableMatrix("RenderMultiply", scale)
local tbl = {
Model = csmodel,
Weapon = self
}
table.insert(ARC9.CSModelPile, tbl)
table.insert(self.WModel, 1, csmodel)
end
self:DoBodygroups(wm)
if !wm and self:GetOwner() != LocalPlayer() then return end
for _, slottbl in pairs(self:GetSubSlotList()) do
if !slottbl.Installed then continue end
local atttbl = ARC9.GetAttTable(slottbl.Installed)
if !atttbl.Model then continue end
local csmodel = self:CreateAttachmentModel(wm, atttbl, slottbl)
if atttbl.MuzzleDevice then
local slmodel = self:CreateAttachmentModel(wm, atttbl, slottbl)
slmodel.IsMuzzleDevice = true
slmodel.NoDraw = true
end
if wm then
slottbl.WModel = csmodel
else
slottbl.VModel = csmodel
end
end
if !wm then
self:CreateFlashlightsVM()
end
end
SWEP.VModel = nil
SWEP.WModel = nil
function SWEP:KillModel()
for _, model in pairs(self.VModel or {}) do
SafeRemoveEntity(model)
end
for _, model in pairs(self.WModel or {}) do
SafeRemoveEntity(model)
end
self.VModel = nil
self.WModel = nil
end

View File

@ -0,0 +1,109 @@
local rtmat = GetRenderTarget("ARC9_pipscope", 512, 512, false)
local rtsize = 512
function SWEP:DoRT(fov)
if ARC9.OverDraw then return end
local rt = {
x = 0,
y = 0,
w = rtsize,
h = rtsize,
angles = self:GetOwner():EyeAngles(),
origin = self:GetOwner():GetShootPos(),
drawviewmodel = false,
fov = fov,
}
render.PushRenderTarget(rtmat, 0, 0, rtsize, rtsize)
ARC9.OverDraw = true
render.RenderView(rt)
ARC9.OverDraw = false
render.PopRenderTarget()
end
local rtsurf = Material("effects/ARC9_rt")
local shadow = Material("ARC9/shadow.png", "smooth")
function SWEP:DoRTScope(model, atttbl)
local pos = model:GetPos()
local ang = model:GetAngles()
pos = pos + ang:Forward() * 12000
local screenpos = pos:ToScreen()
local sh_x = (screenpos.x - (ScrW() / 2)) + (rtsize / 2)
local sh_y = (screenpos.y - (ScrH() / 2)) + (rtsize / 2)
local sh_s = math.floor(rtsize * 1)
sh_x = sh_x - (sh_s / 2)
sh_y = sh_y - (sh_s / 2)
render.PushRenderTarget(rtmat)
cam.Start2D()
surface.SetDrawColor(255, 255, 255)
surface.SetMaterial(atttbl.RTScopeReticle)
surface.DrawTexturedRect(0, 0, rtsize, rtsize)
surface.SetDrawColor(0, 0, 0)
surface.SetMaterial(shadow)
surface.DrawTexturedRect(sh_x, sh_y, sh_s, sh_s)
if !screenpos.visible then
surface.DrawRect(0, 0, rtsize, rtsize)
else
surface.DrawRect(sh_x - rtsize, sh_y - rtsize, rtsize * 4, rtsize)
surface.DrawRect(sh_x - rtsize, sh_y - rtsize, rtsize, rtsize * 4)
surface.DrawRect(sh_x + sh_s, sh_y - rtsize, rtsize, rtsize * 4)
surface.DrawRect(sh_x - rtsize, sh_y + sh_s, rtsize * 4, rtsize)
end
surface.SetDrawColor(0, 0, 0, 255 * (1 - self:GetSightAmount()))
surface.DrawRect(0, 0, rtsize, rtsize)
cam.End2D()
render.PopRenderTarget()
rtsurf:SetTexture("$basetexture", rtmat)
model:SetSubMaterial()
model:SetSubMaterial(atttbl.RTScopeSubmatIndex, "effects/ARC9_rt")
end
function SWEP:DoCheapScope(fov)
local scrw = ScrW()
local scrh = ScrH()
scrw = scrw
scrh = scrh * 9 / 16
local s = (self:GetOwner():GetFOV() / self:GetMagnification() / fov) * 1.40
local scrx = (ScrW() - scrw * s) / 2
local scry = (ScrH() - scrh * s) / 2
scrx = scrx + 8
scry = scry + 8
ARC9:DrawPhysBullets()
render.UpdateScreenEffectTexture()
local screen = render.GetScreenEffectTexture()
render.PushRenderTarget(rtmat, 0, 0, rtsize, rtsize)
-- cam.Start2D()
render.DrawTextureToScreenRect(screen, scrx, scry, scrw * s, scrh * s)
-- render.DrawTextureToScreenRect(ITexture tex, number x, number y, number width, number height)
-- cam.End2D()
render.PopRenderTarget()
end

View File

@ -0,0 +1,47 @@
function SWEP:DoRHIK()
local vm = self:GetOwner():GetViewModel()
local delta = self:Curve(self.CustomizeDelta)
for _, bone in ipairs(ARC9.LHIKBones) do
local vmbone = vm:LookupBone(bone)
if !vmbone then continue end -- Happens when spectating someone prolly
local vmtransform = vm:GetBoneMatrix(vmbone)
if !vmtransform then continue end -- something very bad has happened
local vm_pos = vmtransform:GetTranslation()
local vm_ang = vmtransform:GetAngles()
local newtransform = Matrix()
newtransform:SetTranslation(LerpVector(delta, vm_pos, vm_pos - (EyeAngles():Up() * 128) - (EyeAngles():Forward() * 128)))
newtransform:SetAngles(vm_ang)
vm:SetBoneMatrix(vmbone, newtransform)
end
for _, bone in ipairs(ARC9.RHIKBones) do
local vmbone = vm:LookupBone(bone)
if !vmbone then continue end -- Happens when spectating someone prolly
local vmtransform = vm:GetBoneMatrix(vmbone)
if !vmtransform then continue end -- something very bad has happened
local vm_pos = vmtransform:GetTranslation()
local vm_ang = vmtransform:GetAngles()
local newtransform = Matrix()
newtransform:SetTranslation(LerpVector(delta, vm_pos, vm_pos - (EyeAngles():Up() * 128) - (EyeAngles():Forward() * 128)))
newtransform:SetAngles(vm_ang)
vm:SetBoneMatrix(vmbone, newtransform)
end
end

View File

@ -0,0 +1,150 @@
SWEP.ViewModelVelocityPos = Vector(0, 0, 0)
SWEP.ViewModelVelocityAng = Angle(0, 0, 0)
SWEP.ViewModelPos = Vector(0, 0, 0)
SWEP.ViewModelAng = Angle(0, 0, 0)
SWEP.SwayCT = 0
function SWEP:GetViewModelSway(pos, ang)
-- local d = Lerp(self:GetSightAmount(), 1, 0.02)
-- local v = 1
-- local steprate = 1
-- d = d * 0.25
-- pos = pos + (ang:Up() * (math.sin(self.SwayCT * 0.311 * v) + math.cos(self.SwayCT * 0.44 * v)) * math.sin(CurTime() * 0.8) * d)
-- pos = pos + (ang:Right() * (math.sin(self.SwayCT * 0.324 * v) + math.cos(self.SwayCT * 0.214 * v)) * math.sin(CurTime() * 0.76) * d)
-- if IsFirstTimePredicted() then
-- self.SwayCT = self.SwayCT + (FrameTime() * steprate)
-- end
return pos, ang
end
SWEP.ViewModelLastEyeAng = Angle(0, 0, 0)
SWEP.ViewModelSwayInertia = Angle(0, 0, 0)
function SWEP:GetViewModelInertia(pos, ang)
local d = 1 - self:GetSightAmount()
local diff = self:GetOwner():EyeAngles() - self.ViewModelLastEyeAng
diff = diff / 4
diff.p = math.Clamp(diff.p, -1, 1)
diff.y = math.Clamp(diff.y, -1, 1)
local vsi = self.ViewModelSwayInertia
vsi.p = math.ApproachAngle(vsi.p, diff.p, vsi.p / 10 * FrameTime() / 0.5)
vsi.y = math.ApproachAngle(vsi.y, diff.y, vsi.y / 10 * FrameTime() / 0.5)
self.ViewModelLastEyeAng = self:GetOwner():EyeAngles()
ang:RotateAroundAxis(ang:Up(), vsi.y * 12 * d)
ang:RotateAroundAxis(ang:Right(), -vsi.p * 12 * d)
-- pos = pos - (ang:Up() * vsi.p * 0.5 * d)
-- pos = pos - (ang:Right() * vsi.y * 0.5 * d)
return pos, ang
end
function SWEP:GetViewModelSmooth(pos, ang)
return pos, ang
end
SWEP.ViewModelBobVelocity = 0
SWEP.ViewModelNotOnGround = 0
SWEP.BobCT = 0
function SWEP:GetViewModelBob(pos, ang)
local step = 10
local mag = 1
local v = self:GetOwner():GetVelocity():Length()
v = math.Clamp(v, 0, 350)
self.ViewModelBobVelocity = math.Approach(self.ViewModelBobVelocity, v, FrameTime() * 10000)
local d = math.Clamp(self.ViewModelBobVelocity / 350, 0, 1)
if self:GetOwner():OnGround() then
self.ViewModelNotOnGround = math.Approach(self.ViewModelNotOnGround, 0, FrameTime() / 1)
else
self.ViewModelNotOnGround = math.Approach(self.ViewModelNotOnGround, 1, FrameTime() / 1)
end
d = d * Lerp(self:GetSightAmount(), 1, 0.1)
mag = d * 2
step = 10
ang:RotateAroundAxis(ang:Forward(), math.sin(self.BobCT * step * 0.5) * ((math.sin(CurTime() * 6.151) * 0.2) + 1) * 4.5 * d)
ang:RotateAroundAxis(ang:Right(), math.sin(self.BobCT * step * 0.12) * ((math.sin(CurTime() * 1.521) * 0.2) + 1) * 2.11 * d)
pos = pos - (ang:Up() * math.sin(self.BobCT * step) * 0.07 * ((math.sin(CurTime() * 3.515) * 0.2) + 1) * mag)
pos = pos + (ang:Forward() * math.sin(self.BobCT * step * 0.3) * 0.11 * ((math.sin(CurTime() * 1.615) * 0.2) + 1) * mag)
pos = pos + (ang:Right() * (math.sin(self.BobCT * step * 0.15) + (math.cos(self.BobCT * step * 0.3332))) * 0.16 * mag)
local steprate = Lerp(d, 1, 2.5)
steprate = Lerp(self.ViewModelNotOnGround, steprate, 0.25)
if IsFirstTimePredicted() or game.SinglePlayer() then
self.BobCT = self.BobCT + (FrameTime() * steprate)
end
return pos, ang
end
SWEP.LastViewModelVerticalVelocity = 0
-- SWEP.ViewModelLanded = 0
-- SWEP.ViewModelLanding = 0
function SWEP:GetMidAirBob(pos, ang)
local v = -self:GetOwner():GetVelocity().z / 200
v = math.Clamp(v, -1, 1)
-- if v == 0 and self.LastViewModelVerticalVelocity != 0 then
-- self.ViewModelLanding = self.LastViewModelVerticalVelocity
-- self.ViewModelLanded = 1
-- end
-- if self.ViewModelLanded > 0 then
-- self.ViewModelLanded = math.Approach(self.ViewModelLanded, 0, FrameTime() / 0.25)
v = Lerp(5 * FrameTime(), self.LastViewModelVerticalVelocity, v)
-- end
self.LastViewModelVerticalVelocity = v
local d = self.ViewModelNotOnGround
d = d * Lerp(self:GetSightAmount(), 1, 0.1)
ang:RotateAroundAxis(ang:Right(), -v * d * 8 * math.sin(CurTime() * 0.15))
return pos, ang
end
SWEP.ViewModelInertiaX = 0
SWEP.ViewModelInertiaY = 0
function SWEP:GetViewModelLeftRight(pos, ang)
local v = self:GetOwner():GetVelocity()
local d = Lerp(self:GetSightDelta(), 1, 0)
v, _ = WorldToLocal(v, Angle(0, 0, 0), Vector(0, 0, 0), self:GetOwner():EyeAngles())
local vx = math.Clamp(v.x / 200, -1, 1)
local vy = math.Clamp(v.y / 200, -1, 1)
self.ViewModelInertiaX = math.Approach(self.ViewModelInertiaX, vx, math.abs(vx) * FrameTime() / 0.1)
self.ViewModelInertiaY = math.Approach(self.ViewModelInertiaY, vy, math.abs(vy) * FrameTime() / 0.1)
pos = pos + (ang:Right() * -self.ViewModelInertiaX * 0.65 * d)
pos = pos + (ang:Forward() * self.ViewModelInertiaY * 0.5 * d)
return pos, ang
end

View File

@ -0,0 +1,152 @@
SWEP.CustomizeDelta = 0
SWEP.ViewModelPos = Vector(0, 0, 0)
SWEP.ViewModelAng = Angle(0, 0, 0)
function SWEP:GetViewModelPosition(pos, ang)
if GetConVar("ARC9_benchgun"):GetBool() then
return Vector(0, 0, 0), Angle(0, 0, 0)
end
-- pos = Vector(0, 0, 0)
-- ang = Angle(0, 0, 0)
local oldang = Angle(0, 0, 0)
local up, forward, right = oldang:Up(), oldang:Forward(), oldang:Right()
oldang:Set(ang)
local cor_val = 0.75
local offsetpos = Vector(0, 0, 0)
local offsetang = Angle(0, 0, 0)
local extra_offsetpos = Vector(0, 0, 0)
local extra_offsetang = Angle(0, 0, 0)
-- print(extra_offsetang)
offsetpos:Set(self:GetProcessedValue("ActivePos"))
offsetang:Set(self:GetProcessedValue("ActiveAng"))
local sightdelta = self:Curve(self:GetSightDelta())
-- cor_val = Lerp(sightdelta, cor_val, 1)
if sightdelta > 0 then
local sightpos, sightang = self:GetSightPositions()
-- local sightpos = self.SightPos
-- local sightang = self.SightAng
offsetpos = LerpVector(sightdelta, offsetpos, sightpos)
offsetang = LerpAngle(sightdelta, offsetang, sightang)
end
local eepos, eeang = self:GetExtraSightPositions()
-- local eepos, eeang = Vector(0, 0, 0), Angle(0, 0, 0)
local im = self:GetProcessedValue("SightMidPoint")
local midpoint = sightdelta * math.cos(sightdelta * (math.pi / 2))
local joffset = (im and im.Pos or Vector(0, 0, 0)) * midpoint
local jaffset = (im and im.Ang or Angle(0, 0, 0)) * midpoint
extra_offsetpos = LerpVector(sightdelta, extra_offsetpos, -eepos + joffset)
extra_offsetang = LerpAngle(sightdelta, extra_offsetang, -eeang + jaffset)
extra_offsetang.y = extra_offsetang.y - (self:GetFreeSwayAngles().p * cor_val)
extra_offsetang.p = extra_offsetang.p + (self:GetFreeSwayAngles().y * cor_val)
extra_offsetang.y = extra_offsetang.y - (self:GetFreeAimOffset().p * cor_val)
extra_offsetang.p = extra_offsetang.p + (self:GetFreeAimOffset().y * cor_val)
if game.SinglePlayer() or IsFirstTimePredicted() then
if self:GetCustomize() then
self.CustomizeDelta = math.Approach(self.CustomizeDelta, 1, FrameTime() * 1 / 0.15)
else
self.CustomizeDelta = math.Approach(self.CustomizeDelta, 0, FrameTime() * 1 / 0.15)
end
end
local curvedcustomizedelta = self:Curve(self.CustomizeDelta)
-- local sprintdelta = self:Curve(self:GetSprintDelta())
local sprintdelta = self:Curve(self:GetSprintDelta())
if sprintdelta > 0 then
offsetpos = LerpVector(sprintdelta, offsetpos, self:GetProcessedValue("SprintPos") or self:GetProcessedValue("HolsterPos"))
offsetang = LerpAngle(sprintdelta, offsetang, self:GetProcessedValue("SprintAng") or self:GetProcessedValue("HolsterAng"))
extra_offsetang = LerpAngle(sprintdelta, extra_offsetang, Angle(0, 0, 0))
end
local sim = self:GetProcessedValue("SprintMidPoint")
local spr_midpoint = sprintdelta * math.cos(sprintdelta * (math.pi / 2))
local spr_joffset = (sim and sim.Pos or Vector(0, 0, 0)) * spr_midpoint
local spr_jaffset = (sim and sim.Ang or Angle(0, 0, 0)) * spr_midpoint
extra_offsetpos = extra_offsetpos + spr_joffset
extra_offsetang = extra_offsetang + spr_jaffset
self.BobScale = 0
self.SwayScale = Lerp(sightdelta, 1, 0.1)
if curvedcustomizedelta > 0 then
offsetpos = LerpVector(curvedcustomizedelta, offsetpos, self:GetProcessedValue("CustomizePos"))
offsetang = LerpAngle(curvedcustomizedelta, offsetang, self:GetProcessedValue("CustomizeAng"))
extra_offsetpos = LerpVector(curvedcustomizedelta, extra_offsetpos, Vector(0, 0, 0))
extra_offsetang = LerpAngle(curvedcustomizedelta, extra_offsetang, Angle(0, 0, 0))
extra_offsetang.p = self.MenuRotation.p
extra_offsetang.y = self.MenuRotation.y
-- extra_offsetpos = extra_offsetpos + (Vector(-24, 0, 0) * (math.cos(math.rad(self.MenuRotation.p)) - 1) / -2)
-- extra_offsetpos = extra_offsetpos + (Vector(0, 0, -24) * (math.cos(math.rad(self.MenuRotation.r)) - 1) / -2)
extra_offsetpos = extra_offsetpos + Vector(0.5, 0, 0) * self.MenuPan.x
extra_offsetpos = extra_offsetpos + Vector(0, 0, -0.5) * self.MenuPan.y
extra_offsetpos = extra_offsetpos + Vector(0, -1.5, 0) * self.MenuPan.z
extra_offsetpos = LerpVector(1 - curvedcustomizedelta, extra_offsetpos, Vector(0, 0, 0))
end
if game.SinglePlayer() or IsFirstTimePredicted() then
self.ViewModelPos = LerpVector(0.8, offsetpos, self.ViewModelPos)
self.ViewModelAng = LerpAngle(0.8, offsetang, self.ViewModelAng)
end
offsetpos = self.ViewModelPos
offsetang = self.ViewModelAng
self.ViewModelAng:Normalize()
pos = pos + (ang:Right() * offsetpos.x)
pos = pos + (ang:Forward() * offsetpos.y)
pos = pos + (ang:Up() * offsetpos.z)
ang:RotateAroundAxis(oldang:Up(), offsetang.p)
ang:RotateAroundAxis(oldang:Right(), offsetang.y)
ang:RotateAroundAxis(oldang:Forward(), offsetang.r)
pos = pos + (oldang:Right() * extra_offsetpos[1])
pos = pos + (oldang:Forward() * extra_offsetpos[2])
pos = pos + (oldang:Up() * extra_offsetpos[3])
ang:RotateAroundAxis(oldang:Up(), extra_offsetang[1])
ang:RotateAroundAxis(oldang:Right(), extra_offsetang[2])
ang:RotateAroundAxis(oldang:Forward(), extra_offsetang[3])
pos, ang = self:GetViewModelBob(pos, ang)
pos, ang = self:GetMidAirBob(pos, ang)
-- pos, ang = self:GetViewModelLeftRight(pos, ang)
pos, ang = self:GetViewModelInertia(pos, ang)
pos, ang = self:GetViewModelSway(pos, ang)
pos, ang = self:GetViewModelSmooth(pos, ang)
self.LastViewModelPos = pos
self.LastViewModelAng = ang
return pos, ang
end

View File

@ -0,0 +1,3 @@
function SWEP:DrawWorldModel()
self:DrawCustomModel(true)
end

View File

@ -0,0 +1,72 @@
function SWEP:PlayAnimation(anim, mult, lock, doidle)
mult = mult or 1
lock = lock or false
anim = self:TranslateAnimation(anim)
doidle = doidle or true
mult = self:RunHook("Hook_TranslateAnimSpeed", {mult = mult, anim = anim}).Mult or mult
if !self:HasAnimation(anim) then return end
local vm = self:GetVM()
if !IsValid(vm) then return end
local animation = self:GetAnimationEntry(anim)
local source = self:RandomChoice(animation.Source)
if animation.RareSource then
if util.SharedRandom("ARC9_raresource", 0, 1) <= (animation.RareSourceChance or 0.01) then
source = self:RandomChoice(animation.RareSource)
end
end
local seq = vm:LookupSequence(source)
if seq == 0 then return end
local time = animation.Time or vm:SequenceDuration(seq)
mult = mult * (animation.Mult or 1)
time = time * mult
vm:SendViewModelMatchingSequence(seq)
vm:SetPlaybackRate(1 / mult)
if animation.EventTable then
self:PlaySoundTable(animation.EventTable, 1 / mult)
end
if lock then
self:SetAnimLockTime(CurTime() + time)
else
self:SetAnimLockTime(CurTime())
end
if doidle then
self:SetNextIdle(CurTime() + time)
else
self:SetNextIdle(math.huge)
end
return time
end
function SWEP:IdleAtEndOfAnimation()
local vm = self:GetVM()
local cyc = vm:GetCycle()
local duration = vm:SequenceDuration()
local rate = vm:GetPlaybackRate()
local time = (1 - cyc) * (duration / rate)
self:SetNextIdle(CurTime() + time)
end
function SWEP:Idle()
if self:GetPrimedAttack() then return end
self:PlayAnimation("idle")
end

View File

@ -0,0 +1,52 @@
function SWEP:TranslateAnimation(seq)
seq = self:RunHook("Hook_TranslateAnimation", seq) or seq
if self:GetSightAmount() > 0 and self:HasAnimation(seq .. "_iron") then
seq = seq .. "_iron"
end
if (self:Clip1() == 0 or self:GetEmptyReload()) and self:HasAnimation(seq .. "_empty") then
seq = seq .. "_empty"
end
if istable(seq) then
seq["BaseClass"] = nil
seq = seq[math.Round(util.SharedRandom("ARC9_animtr", 1, #seq))]
end
return seq
end
function SWEP:HasAnimation(seq)
-- seq = self:TranslateSequence(seq)
if self.Animations[seq] then
return true
end
local vm = self:GetVM()
seq = vm:LookupSequence(seq)
return seq != -1
end
function SWEP:GetSequenceTime(seq)
local vm = self:GetVM()
seq = vm:LookupSequence(seq)
return vm:SequenceDuration(seq)
end
function SWEP:GetAnimationEntry(seq)
if self:HasAnimation(seq) then
if self.Animations[seq] then
return self.Animations[seq]
else
return {
Source = seq,
Time = self:GetSequenceTime(seq)
}
end
else
return nil
end
end

View File

@ -0,0 +1,196 @@
function SWEP:Attach(addr, att, silent)
if !self:CanAttach(addr, att) then return false end
local slottbl = self:LocateSlotFromAddress(addr)
slottbl.Installed = att
slottbl.ToggleNum = 1
self:PostModify()
return true
end
function SWEP:Detach(addr, silent)
if !self:CanDetach(addr) then return false end
local slottbl = self:LocateSlotFromAddress(addr)
slottbl.Installed = nil
self:PostModify()
return true
end
function SWEP:PostModify()
self:InvalidateCache()
if CLIENT then
self:SendWeapon()
self:SetupModel(true)
self:SetupModel(false)
end
end
function SWEP:ToggleCustomize(on)
if on == self:GetCustomize() then return end
self:SetCustomize(on)
self:SetShouldHoldType()
if !self:GetCustomize() then
self:Inspect(true)
end
end
function SWEP:GetAttBlocked(atttbl)
local eles = self:GetElements()
if atttbl.ExcludeElements then
for _, group in ipairs(atttbl.ExcludeElements) do
if !istable(group) then
group = {group}
end
local ok = false
for _, ele in ipairs(group) do
if !eles[ele] then ok = true break end
end
if !ok then return true end
end
end
if atttbl.RequireElements then
for _, group in ipairs(atttbl.ExcludeElements) do
if !istable(group) then
group = {group}
end
local ok = true
for _, ele in ipairs(group) do
if !eles[ele] then ok = false break end
end
if ok then return true end
end
return true
end
return false
end
function SWEP:GetSlotBlocked(slottbl)
local eles = self:GetElements()
if slottbl.ExcludeElements then
for _, group in ipairs(slottbl.ExcludeElements) do
if !istable(group) then
group = {group}
end
local ok = false
for _, ele in ipairs(group) do
if !eles[ele] then ok = true break end
end
if !ok then return true end
end
end
local totalcount = self:CountAttachments()
if totalcount >= ARC9.GetMaxAtts() then return true end
if slottbl.RequireElements then
for _, group in ipairs(slottbl.RequireElements) do
if !istable(group) then
group = {group}
end
local ok = true
for _, ele in ipairs(group) do
if !eles[ele] then ok = false break end
end
if ok then return true end
end
return true
end
return false
end
function SWEP:CanAttach(addr, att, slottbl)
slottbl = slottbl or self:LocateSlotFromAddress(addr)
if self:RunHook("Hook_BlockAttachment", {att = att, slottbl = slottbl}) == false then return false end
if self:GetSlotBlocked(slottbl) then return false end
local cat = slottbl.Category
if !istable(cat) then
cat = {cat}
end
local atttbl = ARC9.GetAttTable(att)
if atttbl.Max then
local count = self:CountAttachments(att)
if slottbl.Installed then
local installed_atttbl = ARC9.GetAttTable(slottbl.Installed)
if slottbl.Installed == installed_atttbl.InvAtt then
count = count - 1
end
end
if count >= atttbl.Max then return false end
end
if self:GetAttBlocked(atttbl) then return false end
local attcat = atttbl.Category
if !istable(attcat) then
attcat = {attcat}
end
for _, c in pairs(attcat) do
if table.HasValue(cat, c) then
return true
end
end
return false
end
function SWEP:CanDetach(addr)
local slottbl = self:LocateSlotFromAddress(addr)
if slottbl and slottbl.Integral then return false end
return true
end
function SWEP:CountAttachments(countatt)
local qty = 0
for _, att in ipairs(self:GetAttachmentList()) do
if !countatt then
qty = qty + 1
else
if countatt == att then
qty = qty + 1
end
end
end
return qty
end

View File

@ -0,0 +1,52 @@
function SWEP:DoBodygroups(wm)
if !wm and !IsValid(self:GetOwner()) then return end
if !wm and self:GetOwner():IsNPC() then return end
local dbg = self:GetValue("DefaultBodygroups")
local mdl
if wm then
mdl = self:GetWM()
else
mdl = self:GetVM()
end
if !IsValid(mdl) then return end
mdl:SetBodyGroups(dbg or "")
local eles = self:GetElements()
for i, k in pairs(eles) do
local ele = self.AttachmentElements[i]
if !ele then continue end
for _, j in pairs(ele.Bodygroups or {}) do
mdl:SetBodygroup(j[1], j[2])
end
end
local bbg = self:GetValue("BulletBodygroups")
if bbg then
local amt = self:Clip1()
if self:GetReloading() then
amt = self:GetLoadedRounds()
end
for c, bgs in pairs(bbg) do
if !isnumber(c) then continue end
if amt < c then
mdl:SetBodygroup(bgs[1], bgs[2])
break
end
end
end
end
function SWEP:GetElements()
return {}
end

View File

@ -0,0 +1,2 @@
function SWEP:DoBoneMods()
end

View File

@ -0,0 +1,84 @@
function SWEP:DoEffects()
if !IsFirstTimePredicted() then return end
local muzz_qca = self:GetQCAMuzzle()
local data = EffectData()
data:SetEntity(self)
data:SetAttachment(muzz_qca)
util.Effect( "ARC9_muzzleeffect", data )
end
function SWEP:GetQCAMuzzle()
return self:GetProcessedValue("MuzzleEffectQCA")
end
function SWEP:GetQCAEject()
return self:GetProcessedValue("CaseEffectQCA")
end
SWEP.EjectedShells = {}
function SWEP:DoEject()
if !IsFirstTimePredicted() then return end
local eject_qca = self:GetQCAEject()
local data = EffectData()
data:SetEntity(self)
data:SetAttachment(eject_qca)
util.Effect("ARC9_shelleffect", data)
end
function SWEP:GetTracerOrigin()
local ow = self:GetOwner()
local wm = !ow:GetViewModel():IsValid() or ow:ShouldDrawLocalPlayer()
local att = self:GetQCAMuzzle()
local muzz = self
if !wm then
muzz = ow:GetViewModel()
end
if muzz and muzz:IsValid() then
local posang = muzz:GetAttachment(att)
if !posang then return muzz:GetPos() end
local pos = posang.Pos
return pos
end
end
function SWEP:GetMuzzleDevice(wm)
local model = self.VModel
local muzz = self:GetVM()
if wm then
model = self.WModel
muzz = self:GetWM()
end
-- if model then
-- for i, k in pairs(model) do
-- if k.IsMuzzleDevice then
-- return k
-- end
-- end
-- end
return muzz
end
function SWEP:DrawEjectedShells()
local newshells = {}
for i, k in pairs(self.EjectedShells) do
if !k:IsValid() then continue end
k:DrawModel()
table.insert(newshells, k)
end
self.EjectedShells = newshells
end

View File

@ -0,0 +1,41 @@
SWEP.ElementsCache = {}
function SWEP:GetElements()
if self.ElementsCache then return self.ElementsCache end
local eles = {}
for _, slottbl in pairs(self:GetSubSlotList()) do
if slottbl.Installed then
table.Add(eles, slottbl.InstalledElements or {})
local atttbl = ARC9.GetAttTable(slottbl.Installed)
table.Add(eles, atttbl.ActivateElements or {})
local cat = atttbl.Category
if !istable(cat) then
cat = {cat}
end
table.Add(eles, cat)
table.insert(eles, slottbl.Installed)
else
table.Add(eles, slottbl.UnInstalledElements or {})
end
end
table.insert(eles, self.DefaultElements or {})
if !ARC9.Overrun then
ARC9.Overrun = true
table.insert(eles, self:GetCurrentFiremodeTable().ActivateElements or {})
ARC9.Overrun = false
end
local eles2 = {}
for _, ele in ipairs(eles) do
eles2[ele] = true
end
self.ElementsCache = eles2
return eles2
end

View File

@ -0,0 +1,53 @@
function SWEP:SwitchFiremode()
if #self:GetValue("Firemodes") == 0 then return end
local fm = self:GetFiremode()
self:PlayAnimation("firemode" .. tostring(fm))
fm = fm + 1
if fm > #self:GetValue("Firemodes") then
fm = 1
end
self:EmitSound(self:RandomChoice(self:GetProcessedValue("FiremodeSound")), 75, 100, 1, CHAN_ITEM)
self:SetFiremode(fm)
self:InvalidateCache()
end
function SWEP:GetCurrentFiremode()
mode = self:GetValue("Firemodes")[self:GetFiremode()].Mode
mode = self:RunHook("Hook_TranslateMode") or mode
return mode
end
function SWEP:GetCurrentFiremodeTable()
local fm = self:GetFiremode()
if fm > #self:GetValue("Firemodes") then
fm = 1
end
return self:GetValue("Firemodes")[fm]
end
function SWEP:ToggleSafety(onoff)
onoff = onoff or !self:GetSafe()
self:SetSafe(onoff)
end
function SWEP:ThinkFiremodes()
if self:GetOwner():KeyPressed(IN_ATTACK2) and self:GetOwner():KeyDown(IN_USE) then
self:ToggleSafety()
end
if self:GetOwner():KeyPressed(IN_RELOAD) and self:GetOwner():KeyDown(IN_USE) then
self:SwitchFiremode()
end
end

View File

@ -0,0 +1,58 @@
SWEP.ClientFreeAimAng = Angle(0, 0, 0)
function SWEP:ThinkFreeAim()
local diff = self:GetOwner():EyeAngles() - self:GetLastAimAngle()
local freeaimang = Angle(self:GetFreeAimAngle())
local max = self:GetProcessedValue("FreeAimRadius")
diff.p = math.NormalizeAngle(diff.p)
diff.y = math.NormalizeAngle(diff.y)
freeaimang.p = math.Clamp(math.NormalizeAngle(freeaimang.p) + math.NormalizeAngle(diff.p), -max, max)
freeaimang.y = math.Clamp(math.NormalizeAngle(freeaimang.y) + math.NormalizeAngle(diff.y), -max, max)
local ang2d = math.atan2(freeaimang.p, freeaimang.y)
local mag2d = math.sqrt(math.pow(freeaimang.p, 2) + math.pow(freeaimang.y, 2))
mag2d = math.min(mag2d, max)
freeaimang.p = mag2d * math.sin(ang2d)
freeaimang.y = mag2d * math.cos(ang2d)
self:SetFreeAimAngle(freeaimang)
if CLIENT then
self.ClientFreeAimAng = freeaimang
end
self:SetLastAimAngle(self:GetOwner():EyeAngles())
end
function SWEP:GetFreeAimOffset()
if !GetConVar("arc9_freeaim"):GetBool() then return Angle(0, 0, 0) end
if CLIENT then
return self.ClientFreeAimAng
else
return self:GetFreeAimAngle()
end
end
function SWEP:GetFreeSwayAmount()
local sway = self:GetProcessedValue("Sway")
return sway
end
function SWEP:GetFreeSwayAngles()
if !GetConVar("arc9_freeaim"):GetBool() then return Angle(0, 0, 0) end
local swayamt = self:GetFreeSwayAmount()
local swayspeed = 1
local ang = Angle(math.sin(CurTime() * 0.6 * swayspeed) + (math.cos(CurTime() * 2) * 0.5), math.sin(CurTime() * 0.4 * swayspeed) + (math.cos(CurTime() * 1.6) * 0.5), 0)
ang = ang * swayamt
return ang
end

View File

@ -0,0 +1,179 @@
function SWEP:DoDeployAnimation()
if !self:GetReady() and self:HasAnimation("ready") then
self:PlayAnimation("ready", self:GetProcessedValue("DeployTime", 1), true, true)
self:SetReady(true)
else
self:PlayAnimation("draw", self:GetProcessedValue("DeployTime", 1), true, true)
end
end
function SWEP:Deploy()
if self:GetOwner():IsNPC() then
return
end
self:InvalidateCache()
self:SetBaseSettings()
self:SetNextPrimaryFire(0)
self:SetNextSecondaryFire(0)
self:SetAnimLockTime(0)
self:SetLastMeleeTime(0)
self:SetRecoilAmount(0)
self:SetPrimedAttack(false)
self:SetReloading(false)
self:SetBlindFire(false)
self:SetBlindFireLeft(false)
self:SetFreeAimAngle(Angle(0, 0, 0))
self:SetLastAimAngle(Angle(0, 0, 0))
self:DoDeployAnimation()
self:SetBurstCount(0)
self:SetSightAmount(0)
self:SetLoadedRounds(self:Clip1())
self:SetCustomize(false)
self:SetTriggerDown(self:GetOwner():KeyDown(IN_ATTACK))
self:GetOwner():DoAnimationEvent(self:GetValue("AnimDraw"))
if SERVER then
if !self.GaveDefaultAmmo then
self:GiveDefaultAmmo()
self.GaveDefaultAmmo = true
end
-- self:NetworkWeapon()
self:SetTimer(0.25, function()
self:SendWeapon()
end)
end
self:SetShouldHoldType()
return true
end
function SWEP:GiveDefaultAmmo()
self:SetClip1(self:GetValue("ClipSize"))
self:GetOwner():GiveAmmo(self:GetValue("ClipSize") * 2, self:GetValue("Ammo"))
end
function SWEP:Holster()
if self:GetOwner():IsNPC() then
return
end
self:KillTimers()
self:GetOwner():SetFOV(0, 0.1)
if self:GetReloading() then
self:SetReady(false)
end
-- if CLIENT then
-- self:RemoveCustomizeHUD()
-- end
-- if CLIENT then
-- RunConsoleCommand("pp_bokeh", "0")
-- end
return true
end
function SWEP:Initialize()
self:SetShouldHoldType()
if self:GetOwner():IsNPC() then
self:NPC_Initialize()
return
end
self:SetBaseSettings()
self:SetLastMeleeTime(0)
self:SetNthShot(0)
self:BuildAttachmentAddresses()
self:InitTimers()
self:ClientInitialize()
end
function SWEP:ClientInitialize()
if game.SinglePlayer() then self:CallOnClient("ClientInitialize") end
if SERVER then return end
self:BuildAttachmentAddresses()
self:SetBaseSettings()
self:InitTimers()
end
function SWEP:SetBaseSettings()
if game.SinglePlayer() and SERVER then
self:CallOnClient("SetBaseSettings")
end
self.Primary.Automatic = true
self.Primary.ClipSize = self:GetValue("ClipSize")
self.Primary.Ammo = self:GetValue("Ammo")
self.Primary.DefaultClip = self.Primary.ClipSize
if SERVER then
if self:GetCapacity() > 0 and self:Clip1() > self:GetCapacity() then
self:GetOwner():GiveAmmo(self:Clip1() - self:GetCapacity(), self:GetValue("Ammo"))
self:SetClip1(self:GetCapacity())
end
end
end
function SWEP:SetShouldHoldType()
if self:GetOwner():IsNPC() then
self:SetHoldType(self:GetValue("HoldTypeNPC") or self:GetValue("HoldType"))
return
end
if self:GetSightAmount() > 0 then
if self:GetValue("HoldTypeSights") then
self:SetHoldType(self:GetValue("HoldTypeSights"))
return
end
end
if self:GetSafe() then
if self:GetValue("HoldTypeHolstered") then
self:SetHoldType(self:GetValue("HoldTypeHolstered"))
return
end
end
if self:GetIsSprinting() or self:GetSafe() then
if self:GetValue("HoldTypeSprint") then
self:SetHoldType(self:GetValue("HoldTypeSprint"))
return
end
end
if self:GetCustomize() then
if self:GetValue("HoldTypeCustomize") then
self:SetHoldType(self:GetValue("HoldTypeCustomize"))
return
end
end
self:SetHoldType(self:GetValue("HoldType"))
end

View File

@ -0,0 +1,5 @@
function SWEP:Inspect(force)
if !force and self:StillWaiting() then return end
self:PlayAnimation("inspect", 1, true)
end

View File

@ -0,0 +1,108 @@
function SWEP:SendWeapon(rec)
net.Start("ARC9_networkweapon")
net.WriteEntity(self)
for i, k in pairs(self.Attachments or {}) do
self:SendAttachmentTree(self.Attachments[i])
end
if SERVER then
if rec then
// send to just this one person
net.Send(rec)
else
net.Broadcast()
end
else
net.SendToServer()
end
end
function SWEP:SendAttachmentTree(tree)
if tree and tree.Installed then
local atttbl = ARC9.GetAttTable(tree.Installed)
local id = atttbl.ID
net.WriteUInt(id, ARC9.Attachments_Bits)
tree.SubAttachments = tree.SubAttachments or {}
if atttbl.ToggleStats then
net.WriteUInt((atttbl.ToggleNum or 1) - 1, 8)
end
if atttbl.Attachments then
for i, k in pairs(atttbl.Attachments) do
self:SendAttachmentTree(tree.SubAttachments[i])
end
end
else
net.WriteUInt(0, ARC9.Attachments_Bits)
end
end
function SWEP:CountAttsInTree(tree)
local flattree = self:AttTreeToList(tree)
local count = {}
for _, i in ipairs(flattree) do
if i.Installed then
local att = i.Installed
count[att] = (count[att] or 0) + 1
end
end
return count
end
function SWEP:ReceiveWeapon()
local tbl = {}
for i, k in pairs(self.Attachments or {}) do
tbl[i] = self:ReceiveAttachmentTree()
end
if SERVER then
if !self:ValidateInventoryForNewTree(tbl) then
return
end
end
self:BuildSubAttachments(tbl)
if CLIENT then
self:SetupModel(true)
self:SetupModel(false)
self:RefreshCustomizeMenu()
else
self:SendWeapon()
self:InvalidateCache()
end
end
function SWEP:ReceiveAttachmentTree()
local id = net.ReadUInt(ARC9.Attachments_Bits)
local att = ARC9.Attachments_Index[id]
local tree = {
Installed = att,
SubAttachments = {}
}
if !att then return tree end
local atttbl = ARC9.GetAttTable(att)
if atttbl.ToggleStats then
tree.ToggleNum = net.ReadUInt(8) + 1
end
if atttbl.Attachments then
for i, k in pairs(atttbl.Attachments) do
tree.SubAttachments[i] = self:ReceiveAttachmentTree()
end
end
return tree
end

View File

@ -0,0 +1,166 @@
local function IsPenetrating(ptr, ptrent)
if ptrent:IsWorld() then
return !ptr.StartSolid or ptr.AllSolid
elseif IsValid(ptrent) then
local mins, maxs = ptrent:WorldSpaceAABB()
local wsc = ptrent:WorldSpaceCenter()
-- Expand the bounding box by a bit to account for hitboxes outside it
-- This is more consistent but less accurate
mins = mins + (mins - wsc) * 0.25
maxs = maxs + (maxs - wsc) * 0.25
local withinbounding = ptr.HitPos:WithinAABox(mins, maxs)
if GetConVar("developer"):GetBool() then
debugoverlay.Cross(ptr.HitPos, withinbounding and 2 or 6, 5, withinbounding and Color(255, 255, 0) or Color(128, 255, 0), true)
end
if withinbounding then return true end
end
return false
end
function SWEP:Penetrate(tr, range, penleft, alreadypenned)
if !GetConVar("ARC9_penetration"):GetBool() then return end
local hitpos, startpos = tr.HitPos, tr.StartPos
local dir = (hitpos - startpos):GetNormalized()
if tr.HitSky then return end
if penleft <= 0 then return end
alreadypenned = alreadypenned or {}
local skip = false
local trent = tr.Entity
local penmult = ARC9.PenTable[tr.MatType] or 1
local pentracelen = math.max(penleft * penmult / 8, 1)
local curr_ent = trent
if self:GetRicochetChance(tr) > math.random(0, 100) then
local degree = tr.HitNormal:Dot((tr.StartPos - tr.HitPos):GetNormalized())
if degree == 0 or degree == 1 then return end
-- sound.Play(ArcCW.RicochetSounds[math.random(#ArcCW.RicochetSounds)], tr.HitPos)
if (tr.Normal:Length() == 0) then return end
-- ACT3_ShootPBullet(tr.HitPos, ((2 * degree * tr.HitNormal) + tr.Normal) * (vel * math.Rand(0.25, 0.75)), owner, inflictor, bulletid, false, 1, penleft, dist)
-- return
dir = (2 * degree * tr.HitNormal) + tr.Normal
ang = dir:Angle()
ang = ang + (AngleRand() * (1 - degree) * 15 / 360)
dir = ang:Forward()
local d = math.Rand(0.25, 0.95)
penleft = penleft * d
skip = true
end
if !tr.HitWorld then penmult = penmult * 0.5 end
if trent.mmRHAe then penmult = trent.mmRHAe end
penmult = penmult * math.Rand(0.9, 1.1) * math.Rand(0.9, 1.1)
local endpos = hitpos
local td = {}
td.start = endpos
td.endpos = endpos + (dir * pentracelen)
td.mask = MASK_SHOT
local ptr = util.TraceLine(td)
local ptrent = ptr.Entity
while !skip and penleft > 0 and IsPenetrating(ptr, ptrent) and ptr.Fraction < 1 and ptrent == curr_ent do
penleft = penleft - (pentracelen * penmult)
td.start = endpos
td.endpos = endpos + (dir * pentracelen)
td.mask = MASK_SHOT
ptr = util.TraceLine(td)
if GetConVar("developer"):GetBool() then
local pdeltap = penleft / self:GetValue("Penetration")
local colorlr = Lerp(pdeltap, 0, 255)
debugoverlay.Line(endpos, endpos + (dir * pentracelen), 10, Color(255, colorlr, colorlr), true)
end
endpos = endpos + (dir * pentracelen)
range = range + pentracelen
end
if penleft > 0 then
if (dir:Length() == 0) then return end
if GetConVar("ARC9_bullet_physics"):GetBool() then
ARC9:ShootPhysBullet(self, endpos, dir * self:GetProcessedValue("PhysBulletMuzzleVelocity"), {
Penleft = penleft,
Travelled = range,
Damaged = alreadypenned
})
else
self:GetOwner():FireBullets({
Damage = self:GetValue("Damage_Max"),
Force = 4,
Tracer = 0,
Num = 1,
Dir = dir,
Src = endpos,
Callback = function(att, btr, dmg)
range = range + (btr.HitPos - btr.StartPos):Length()
self:AfterShotFunction(btr, dmg, range, penleft, alreadypenned)
if GetConVar("developer"):GetBool() then
if SERVER then
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 0, 0), false)
else
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 255, 255), false)
end
end
end
})
end
self:GetOwner():FireBullets({
Damage = 0,
Force = 0,
Tracer = 0,
Num = 1,
Distance = 8,
Dir = -dir,
Src = endpos,
})
end
end
function SWEP:GetRicochetChance(tr)
if !GetConVar("ARC9_ricochet"):GetBool() then return 0 end
local degree = tr.HitNormal:Dot((tr.StartPos - tr.HitPos):GetNormalized())
degree = 90 - math.deg(math.acos(degree))
local ricmult = ARC9.PenTable[tr.MatType] or 1
-- 0 at 1
-- 100 at 0
if degree > self:GetProcessedValue("RicochetAngleMax") then return 0 end
local c = self:GetProcessedValue("RicochetChance")
c = c * ricmult
-- c = c * GetConVar("arccw_ricochet_mult"):GetFloat()
-- c = 100
c = c * 100
return math.Clamp(c, 0, 100)
end

View File

@ -0,0 +1,91 @@
function SWEP:ThinkRecoil()
if (self:GetLastRecoilTime() + self:GetProcessedValue("RecoilResetTime")) < CurTime() then
local rec = self:GetRecoilAmount()
rec = rec - (FrameTime() * self:GetProcessedValue("RecoilDissipationRate"))
self:SetRecoilAmount(math.max(rec, 0))
end
self:SetRecoilUp(self:GetRecoilUp() - (FrameTime() * self:GetRecoilUp() * self:GetProcessedValue("RecoilDissipationRate")))
self:SetRecoilSide(self:GetRecoilSide() - (FrameTime() * self:GetRecoilSide() * self:GetProcessedValue("RecoilDissipationRate")))
end
function SWEP:ApplyRecoil()
local rec = self:GetRecoilAmount()
local rps = 1
rec = rec + rps
local delay = 60 / self:GetProcessedValue("RPM")
local recoilup = 1
local recoilside = 0
local seed = self:GetProcessedValue("RecoilSeed") or self:GetClass()
local shot = math.floor(self:GetRecoilAmount())
if isstring(seed) then
local numseed = 0
for _, i in ipairs(string.ToTable(seed)) do
numseed = numseed + string.byte(i)
end
numseed = numseed % 16777216
seed = numseed
end
seed = seed + shot
if self:GetProcessedValue("RecoilLookupTable") then
local recoilpattern = self:PatternWithRunOff(self:GetProcessedValue("RecoilLookupTable"), self:GetProcessedValue("RecoilLookupTableOverrun") or self:GetProcessedValue("RecoilLookupTable"), shot)
recoilup = recoilpattern.y or 1
recoilside = recoilpattern.x or 0
else
math.randomseed(seed)
recoilup = math.random(-1.5, 0.5)
recoilside = math.random(-1.25, 0.75)
local randomrecoilup = util.SharedRandom("arc9_recoil_up_r", -1, 1)
local randomrecoilside = util.SharedRandom("arc9_recoil_side_r", -1, 1)
recoilup = recoilup * self:GetProcessedValue("RecoilUp")
recoilside = recoilside * self:GetProcessedValue("RecoilSide")
randomrecoilup = randomrecoilup * self:GetProcessedValue("RecoilRandomUp")
randomrecoilside = randomrecoilside * self:GetProcessedValue("RecoilRandomSide")
recoilup = recoilup + randomrecoilup
recoilside = recoilside + randomrecoilside
recoilup = recoilup * self:GetProcessedValue("Recoil")
recoilside = recoilside * self:GetProcessedValue("Recoil")
end
self:SetRecoilUp(recoilup)
self:SetRecoilSide(recoilside)
-- self:SetRecoilDirection(-90)
self:SetRecoilAmount(rec)
self:SetLastRecoilTime(CurTime() + (delay * 2))
-- local vis_kick = self:GetProcessedValue("RecoilKick")
-- local vis_shake = 0
-- vis_kick = vis_kick * rps
-- vis_shake = vis_kick * rps
-- local vis_kick_v = vis_kick * 0.5
-- local vis_kick_h = vis_kick * util.SharedRandom("ARC9_vis_kick_h", -1, 1)
-- vis_shake = vis_shake * util.SharedRandom("ARC9_vis_kick_shake", -1, 1)
-- self:GetOwner():SetViewPunchAngles(Angle(vis_kick_v, vis_kick_h, vis_shake))
-- self:GetOwner():SetFOV(self:GetOwner():GetFOV() * 0.99, 0)
-- self:GetOwner():SetFOV(0, 60 / (self:GetProcessedValue("RPM")))
end

View File

@ -0,0 +1,170 @@
function SWEP:Reload()
if self:GetOwner():IsNPC() then
self:NPC_Reload()
return
end
if !self:GetOwner():KeyPressed(IN_RELOAD) then
return
end
if self:GetOwner():KeyDown(IN_USE) then
-- firemode switch
return
end
if self:GetOwner():KeyDown(IN_WALK) then
self:Inspect()
return
end
if self:StillWaiting() then return end
if self:GetCapacity() <= 0 then return end
if self:Clip1() >= self:GetCapacity() then return end
if self:Ammo1() <= 0 then return end
-- self:ScopeToggle(0)
-- self:ToggleCustomize(false)
if self:Clip1() == 0 then
self:SetEmptyReload(true)
else
self:SetEmptyReload(false)
end
local anim = "reload"
if self:GetShouldShotgunReload() then
anim = "reload_start"
end
local t = self:PlayAnimation(anim, self:GetProcessedValue("ReloadTime"), true, true)
self:GetOwner():DoAnimationEvent(self:GetProcessedValue("AnimReload"))
if !self:GetShouldShotgunReload() then
local minprogress = self:GetAnimationEntry(anim).MinProgress or 1
self:SetTimer(t * minprogress, function()
self:SetLoadedRounds(math.min(self:GetValue("ClipSize"), self:Clip1() + self:Ammo1()))
end)
end
if SERVER then
self:DropMagazine()
end
self:SetLoadedRounds(self:Clip1())
self:SetReloading(true)
self:SetEndReload(false)
-- self:SetTimer(t * 0.9, function()
-- if !IsValid(self) then return end
-- self:SetEndReload(false)
-- self:EndReload()
-- end)
self:SetReloadFinishTime(CurTime() + (t * 0.95))
end
function SWEP:DropMagazine()
-- if !IsFirstTimePredicted() and !game.SinglePlayer() then return end
if self:GetProcessedValue("DropMagazineModel") then
for i = 1, self:GetProcessedValue("DropMagazineAmount") do
local mag = ents.Create("ARC9_droppedmag")
if mag then
mag:SetPos(self:GetOwner():EyePos() - (self:GetOwner():EyeAngles():Up() * 8))
mag:SetAngles(self:GetOwner():EyeAngles())
mag.Model = self:GetProcessedValue("DropMagazineModel")
mag.ImpactType = self:GetProcessedValue("DropMagazineImpact")
mag:SetOwner(self:GetOwner())
mag:Spawn()
local phys = mag:GetPhysicsObject()
if IsValid(phys) then
phys:AddAngleVelocity(Vector(math.Rand(-300, 300), math.Rand(-300, 300), math.Rand(-300, 300)))
end
end
end
end
end
function SWEP:GetCapacity()
return self:GetValue("ClipSize") + self:GetValue("ChamberSize")
end
function SWEP:RestoreClip(amt)
local reserve = self:Clip1() + self:Ammo1()
local lastclip1 = self:Clip1()
self:SetClip1(math.min(math.min(self:Clip1() + amt, self:GetCapacity()), reserve))
reserve = reserve - self:GetCapacity()
self:GetOwner():SetAmmo(reserve, self.Primary.Ammo)
return self:Clip1() - lastclip1
end
function SWEP:GetShouldShotgunReload()
if self:GetProcessedValue("HybridReload") then
if self:Clip1() >= self:GetProcessedValue("ChamberSize") then
return true
else
return false
end
end
return self:GetProcessedValue("ShotgunReload")
end
function SWEP:EndReload()
if self:GetShouldShotgunReload() then
if self:Clip1() >= self:GetCapacity() or self:Ammo1() == 0 or self:GetEndReload() then
// finish
self:PlayAnimation("reload_finish", self:GetProcessedValue("ReloadTime", 1), true, true)
self:SetReloading(false)
self:SetNthShot(0)
self:SetNthReload(self:GetNthReload() + 1)
else
local t = self:PlayAnimation("reload_insert", self:GetProcessedValue("ReloadTime", 1), true)
local res = math.min(math.min(3, self:GetCapacity() - self:Clip1()), self:Ammo1())
self:SetLoadedRounds(res)
for i = 1, res do
self:SetTimer(t * 0.95 * ((i - 1) / 3), function()
self:RestoreClip(1)
end)
end
self:SetReloadFinishTime(CurTime() + (t * 0.95 * (res / 3)))
-- self:SetTimer(t * 0.95 * (res / 3), function()
-- if !IsValid(self) then return end
-- self:EndReload()
-- end)
end
else
self:RestoreClip(self:GetValue("ClipSize"))
self:SetReloading(false)
self:SetNthShot(0)
self:SetNthReload(self:GetNthReload() + 1)
self:SetEmptyReload(false)
end
end
function SWEP:ThinkReload()
if self:GetReloading() and self:GetReloadFinishTime() < CurTime() then
self:EndReload()
end
end

View File

@ -0,0 +1,64 @@
function SWEP:GetSightDelta()
return self:GetSightAmount()
end
function SWEP:EnterSights()
if self:GetSprintAmount() > 0 then return end
if !self:GetProcessedValue("HasSights") then return end
self:SetInSights(true)
self:EmitSound(self:RandomChoice(self:GetProcessedValue("EnterSightsSound")), 100, 75)
if CLIENT then
self:BuildMultiSight()
elseif game.SinglePlayer() then
self:CallOnClient("BuildMultiSight")
end
end
function SWEP:ExitSights()
self:SetInSights(false)
self:EmitSound(self:RandomChoice(self:GetProcessedValue("ExitSightsSound")), 100, 75)
end
SWEP.LastPressedETime = 0
function SWEP:ThinkSights()
if self:GetSafe() then return end
local sighted = self:GetInSights()
local amt = self:GetSightAmount()
if sighted then
amt = math.Approach(amt, 1, FrameTime() / self:GetProcessedValue("AimDownSightsTime"))
else
amt = math.Approach(amt, 0, FrameTime() / self:GetProcessedValue("AimDownSightsTime"))
end
self:GetVM():SetPoseParameter("sights", amt)
self:SetSightAmount(amt)
if sighted and !self:GetOwner():KeyDown(IN_ATTACK2) then
self:ExitSights()
elseif !sighted and self:GetOwner():KeyDown(IN_ATTACK2) then
if self:GetOwner():KeyDown(IN_USE) then
return
end
self:EnterSights()
end
if self:GetOwner():KeyPressed(IN_USE) then
if CurTime() - self.LastPressedETime < 0.33 then
if game.SinglePlayer() then
self:CallOnClient("SwitchMultiSight")
elseif CLIENT then
self:SwitchMultiSight()
end
else
self.LastPressedETime = CurTime()
end
end
end

View File

@ -0,0 +1,299 @@
local cancelmults = {
[HITGROUP_HEAD] = 2,
[HITGROUP_CHEST] = 1,
[HITGROUP_STOMACH] = 1,
[HITGROUP_LEFTARM] = 0.25,
[HITGROUP_RIGHTARM] = 0.25,
[HITGROUP_LEFTLEG] = 0.25,
[HITGROUP_RIGHTLEG] = 0.25,
[HITGROUP_GEAR] = 0.25
}
function SWEP:StillWaiting()
if self:GetNextPrimaryFire() > CurTime() then return true end
if self:GetNextSecondaryFire() > CurTime() then return true end
if self:GetAnimLockTime() > CurTime() then return true end
if self:GetPrimedAttack() then return true end
return false
end
function SWEP:SprintLock()
if self:GetSprintAmount() > 0 then return true end
if self:GetIsSprinting() then return true end
return false
end
function SWEP:PrimaryAttack()
if self:GetOwner():IsNPC() then
return
end
if self:GetReloading() then
self:SetEndReload(true)
end
if self:StillWaiting() then return end
if self:GetCustomize() then return end
-- if self:GetProcessedValue("CanQuickNade") then
-- if self:GetOwner():KeyDown(IN_USE) then
-- self:PrimeGrenade()
-- self:SetBurstCount(0)
-- return
-- end
-- end
-- if self:GetProcessedValue("Melee") then
-- if self:GetOwner():KeyDown(IN_USE) then
-- self:Melee()
-- return
-- end
-- end
if self:GetNeedTriggerPress() then return end
if self:GetSafe() then
self:ToggleSafety(false)
self:SetNeedTriggerPress(true)
return
end
if self:GetCurrentFiremode() > 0 and self:GetBurstCount() >= self:GetCurrentFiremode() then return end
if self:Clip1() < self:GetProcessedValue("AmmoPerShot") then
self:PlayAnimation("dryfire")
self:EmitSound(self:RandomChoice(self:GetProcessedValue("DryFireSound")), 75, 100, 1, CHAN_BODY)
self:SetBurstCount(0)
self:SetNeedTriggerPress(true)
return
end
self:SetBaseSettings()
if self:SprintLock() then return end
if self:RunHook("HookP_BlockFire") then return end
if IsFirstTimePredicted() then
self:TakePrimaryAmmo(self:GetProcessedValue("AmmoPerShot"))
end
local idle = true
self:PlayAnimation("fire", 1, false, idle)
local ejectdelay = self:GetProcessedValue("EjectDelay")
if ejectdelay == 0 then
self:DoEject()
else
self:SetTimer(ejectdelay, function()
self:DoEject()
end)
end
self:GetOwner():DoAnimationEvent(self:GetProcessedValue("AnimShoot"))
local pvar = self:GetProcessedValue("ShootPitchVariation")
local pvrand = util.SharedRandom("ARC9_sshoot", -pvar, pvar)
self:EmitSound(self:RandomChoice(self:GetProcessedValue("ShootSound")) or "", self:GetProcessedValue("ShootVolume"), self:GetProcessedValue("ShootPitch") + pvrand, 1, CHAN_WEAPON)
self:EmitSound(self:RandomChoice(self:GetProcessedValue("DistantShootSound")) or "", 149, self:GetProcessedValue("ShootPitch") + pvrand, 1, CHAN_WEAPON + 1)
local delay = 60 / self:GetProcessedValue("RPM")
local curatt = self:GetNextPrimaryFire()
local diff = CurTime() - curatt
if diff > engine.TickInterval() or diff < 0 then
curatt = CurTime()
end
self:SetNextPrimaryFire(curatt + delay)
self:SetNthShot(self:GetNthShot() + 1)
self:DoEffects()
local spread = self:GetProcessedValue("Spread")
local dir = self:GetShootDir()
if self:GetProcessedValue("ShootEnt") then
self:ShootRocket()
else
if IsFirstTimePredicted() then
if GetConVar("ARC9_bullet_physics"):GetBool() then
for i = 1, self:GetProcessedValue("Num") do
dir = self:GetOwner():EyeAngles() + (spread * AngleRand() / 3.6)
ARC9:ShootPhysBullet(self, self:GetOwner():GetShootPos(), dir:Forward() * self:GetProcessedValue("PhysBulletMuzzleVelocity"))
end
else
self:GetOwner():LagCompensation(true)
local tr = self:GetProcessedValue("TracerNum")
self:GetOwner():FireBullets({
Damage = self:GetProcessedValue("Damage_Max"),
Force = 8,
Tracer = tr,
Num = self:GetProcessedValue("Num"),
Dir = dir:Forward(),
Src = self:GetOwner():GetShootPos(),
Spread = Vector(spread, spread, spread),
IgnoreEntity = self:GetOwner():GetVehicle(),
Callback = function(att, btr, dmg)
local range = (btr.HitPos - btr.StartPos):Length()
self:AfterShotFunction(btr, dmg, range, self:GetProcessedValue("Penetration"), {})
if GetConVar("developer"):GetBool() then
if SERVER then
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 0, 0), false)
else
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 255, 255), false)
end
end
end
})
self:GetOwner():LagCompensation(false)
end
end
end
self:ApplyRecoil()
self:SetBurstCount(self:GetBurstCount() + 1)
if self:GetCurrentFiremode() == 1 or self:Clip1() == 0 then
self:SetNeedTriggerPress(true)
end
end
function SWEP:AfterShotFunction(tr, dmg, range, penleft, alreadypenned)
if !IsFirstTimePredicted() and !game.SinglePlayer() then return end
local dmgv = self:GetDamageAtRange(range)
self:RunHook("Hook_BulletImpact", {
tr = tr,
dmg = dmg,
range = range,
penleft = penleft,
alreadypenned = alreadypenned
})
local bodydamage = self:GetProcessedValue("BodyDamageMults")
local dmgbodymult = 1
if bodydamage[tr.HitGroup] then
dmgbodymult = dmgbodymult * bodydamage[tr.HitGroup]
end
if GetConVar("ARC9_bodydamagecancel"):GetBool() and cancelmults[tr.HitGroup] then
-- if cancelmults[tr.HitGroup] then
dmgbodymult = dmgbodymult / cancelmults[tr.HitGroup]
end
dmgv = dmgv * dmgbodymult
local pendelta = penleft / self:GetProcessedValue("Penetration")
pendelta = math.Clamp(pendelta, 0.1, 1)
dmgv = dmgv * pendelta
if self:GetOwner():IsNPC() and !GetConVar("ARC9_npc_equality"):GetBool() then
dmgv = dmgv * 0.25
end
dmg:SetDamage(dmgv)
if tr.Entity and alreadypenned[tr.Entity] then
dmg:SetDamage(0)
elseif tr.Entity then
alreadypenned[tr.Entity] = true
end
self:Penetrate(tr, range, penleft, alreadypenned)
end
function SWEP:GetDamageAtRange(range)
local d = 1
local r_min = self:GetProcessedValue("RangeMin")
local r_max = self:GetProcessedValue("RangeMax")
if range <= r_min then
d = 0
elseif range >= r_max then
d = 1
else
d = (range - r_min) / (r_max - r_min)
end
local dmgv = Lerp(d, self:GetProcessedValue("DamageMax"), self:GetProcessedValue("DamageMin"))
dmgv = self:GetProcessedValue("Damage", dmgv)
dmgv = math.ceil(dmgv)
return dmgv
end
function SWEP:GetShootDir()
local dir = self:GetOwner():EyeAngles()
dir = dir + self:GetFreeAimOffset()
dir = dir + self:GetFreeSwayAngles()
return dir
end
function SWEP:ShootRocket()
if CLIENT then return end
local src = self:GetMuzzleOrigin()
local dir = self:GetShootDir()
local num = self:GetProcessedValue("Num")
local ent = self:GetProcessedValue("ShootEnt")
local spread
if self:GetOwner():IsNPC() then
-- ang = self:GetOwner():GetAimVector():Angle()
spread = self:GetNPCSpread()
else
spread = self:GetSpread()
end
for i = 1, num do
local dispersion = Angle(math.Rand(-1, 1), math.Rand(-1, 1), 0)
dispersion = dispersion * spread * 36
local rocket = ents.Create(ent)
if !IsValid(rocket) then return end
rocket:SetPos(src)
rocket:SetOwner(self:GetOwner())
rocket:SetAngles(dir + dispersion)
rocket:Spawn()
local phys = rocket:GetPhysicsObject()
if phys:IsValid() then
phys:ApplyForceCenter((dir + dispersion):Forward() * self:GetProcessedValue("ShootEntForce"))
end
end
end
function SWEP:FireAnimationEvent( pos, ang, event, options )
return true
end

View File

@ -0,0 +1,60 @@
function SWEP:GetSprintToFireTime()
return self:GetProcessedValue("SprintToFireTime")
end
function SWEP:GetIsSprinting()
local owner = self:GetOwner()
if !self:GetOwner():IsValid() or self:GetOwner():IsNPC() then
return false
end
local curspeed = owner:GetVelocity():Length()
if !owner:KeyDown(IN_FORWARD) and !owner:KeyDown(IN_BACK) and !owner:KeyDown(IN_MOVELEFT) and !owner:KeyDown(IN_MOVERIGHT) then return false end
if !owner:KeyDown(IN_SPEED) then return false end
if curspeed <= 0 then return false end
if !owner:OnGround() then return false end
return true
end
function SWEP:GetSprintDelta()
return self:GetSprintAmount()
end
function SWEP:EnterSprint()
self:SetShouldHoldType()
end
function SWEP:ExitSprint()
self:SetShouldHoldType()
end
SWEP.LastWasSprinting = false
function SWEP:ThinkSprint()
local sprinting = self:GetIsSprinting() or self:GetSafe()
if self:GetSightAmount() >= 1 then
sprinting = false
end
local amt = self:GetSprintAmount()
if self.LastWasSprinting and !sprinting then
self:ExitSprint()
elseif !self.LastWasSprinting and sprinting then
self:EnterSprint()
end
self.LastWasSprinting = sprinting
if sprinting then
amt = math.Approach(amt, 1, FrameTime() / self:GetSprintToFireTime())
else
amt = math.Approach(amt, 0, FrameTime() / self:GetSprintToFireTime())
end
self:SetSprintAmount(amt)
end

View File

@ -0,0 +1,240 @@
SWEP.StatCache = {}
SWEP.HookCache = {}
SWEP.AffectorsCache = nil
SWEP.ExcludeFromRawStats = {
["PrintName"] = true,
}
function SWEP:InvalidateCache()
self.StatCache = {}
self.HookCache = {}
self.AffectorsCache = nil
self.ElementsCache = nil
self:SetBaseSettings()
end
function SWEP:RunHook(val, data)
if self.HookCache[val] then
for _, chook in pairs(self.HookCache[val]) do
local d = chook(self, data)
if d != nil then
data = d
end
end
return data
end
self.HookCache[val] = {}
for _, tbl in pairs(self:GetAllAffectors()) do
if tbl[val] then
table.insert(self.HookCache[val], tbl[val])
if !pcall(function()
local d = tbl[val](self, data)
if d != nil then
data = d
end
end) then
print("!!! ARC9 ERROR - \"" .. (tbl["PrintName"] or "Unknown") .. "\" TRIED TO RUN INVALID HOOK ON " .. val .. "!")
end
end
end
return data
end
function SWEP:GetFinalAttTableFromAddress(address)
return self:GetFinalAttTable(self:LocateSlotFromAddress(address))
end
function SWEP:GetFinalAttTable(slot)
if !slot.Installed then return {} end
local atttbl = ARC9.GetAttTable(slot.Installed)
if atttbl.ToggleStats then
local tbl = table.Copy(atttbl)
local toggletbl = atttbl.ToggleStats[slot.ToggleNum or 1]
table.Add(tbl, toggletbl)
return tbl
else
return atttbl
end
end
function SWEP:GetAllAffectors()
if self.AffectorsCache then return self.AffectorsCache end
local aff = {}
table.insert(aff, self:GetTable())
for _, slot in pairs(self:GetSubSlotList()) do
local atttbl = self:GetFinalAttTable(slot)
if atttbl then
table.insert(aff, atttbl)
end
end
if !ARC9.Overrun then
ARC9.Overrun = true
table.insert(aff, self:GetCurrentFiremodeTable())
ARC9.Overrun = false
end
self.AffectorsCache = aff
return aff
end
function SWEP:GetProcessedValue(val, base)
local stat = self:GetValue(val, base)
if self:GetValue("Silencer") then
stat = self:GetValue(val, stat, "Silenced")
end
if !self:GetOwner():OnGround() then
stat = self:GetValue(val, stat, "MidAir")
end
if self:GetOwner():Crouching() and self:GetOwner():OnGround() then
stat = self:GetValue(val, stat, "Crouch")
end
if self:GetBurstCount() == 0 then
stat = self:GetValue(val, stat, "FirstShot")
stat = self:GetValue(val, stat, "First")
end
if self:Clip1() == 0 then
stat = self:GetValue(val, stat, "Empty")
stat = self:GetValue(val, stat, "LastShot")
stat = self:GetValue(val, stat, "Last")
end
if self:GetNthShot() % 2 == 0 then
stat = self:GetValue(val, stat, "EvenShot")
else
stat = self:GetValue(val, stat, "OddShot")
end
if self:GetNthReload() % 2 == 0 then
stat = self:GetValue(val, stat, "EvenReload")
else
stat = self:GetValue(val, stat, "OddReload")
end
if isnumber(stat) then
stat = Lerp(self:GetSightAmount(), self:GetValue(val, stat, "HipFire"), self:GetValue(val, stat, "Sights"))
else
if self:GetSightAmount() >= 1 then
stat = self:GetValue(val, stat, "Sights")
else
stat = self:GetValue(val, stat, "HipFire")
end
end
if self:GetNextPrimaryFire() + 0.1 > CurTime() then
local pft = CurTime() - self:GetNextPrimaryFire() + 0.1
local d = pft / 0.1
d = math.Clamp(d, 0, 1)
if isnumber(stat) then
stat = Lerp(d, stat, self:GetValue(val, stat, "Shooting"))
else
if d > 0 then
stat = self:GetValue(val, stat, "Shooting")
end
end
end
stat = self:GetValue(val, stat, "Recoil", self:GetRecoilAmount())
local spd = math.min(self:GetOwner():GetAbsVelocity():Length(), 250)
spd = spd / 250
if isnumber(stat) then
stat = Lerp(spd, stat, self:GetValue(val, stat, "Move"))
else
if spd > 0 then
stat = self:GetValue(val, stat, "Move")
end
end
return stat
end
function SWEP:GetValue(val, base, condition, amount)
condition = condition or ""
amount = amount or 1
local stat = base or self:GetTable()[val]
if self.StatCache[tostring(base) .. val .. condition] then
stat = self.StatCache[tostring(base) .. val .. condition]
stat = self:RunHook(val .. "Hook" .. condition, stat) or stat
return stat
end
local priority = 0
if !self.ExcludeFromRawStats[val] then
for _, tbl in pairs(self:GetAllAffectors()) do
local att_priority = tbl[val .. condition .. "_Priority"] or 1
if tbl[val .. condition] != nil and att_priority >= priority then
stat = tbl[val .. condition]
priority = att_priority
end
end
end
for _, tbl in pairs(self:GetAllAffectors()) do
local att_priority = tbl[val .. "Override" .. condition .. "Priority"] or 1
if tbl[val .. "Override" .. condition] != nil and att_priority >= priority then
stat = tbl[val .. "Override" .. condition]
priority = att_priority
end
end
if isnumber(stat) then
for _, tbl in pairs(self:GetAllAffectors()) do
if tbl[val .. "Add" .. condition] != nil then
if !pcall(function() stat = stat + (tbl[val .. "Add" .. condition] * amount) end) then
print("!!! ARC9 ERROR - \"" .. (tbl["PrintName"] or "Unknown") .. "\" TRIED TO ADD INVALID VALUE: (" .. tbl[val .. "Add" .. condition] .. ") TO " .. val .. "!")
end
end
end
for _, tbl in pairs(self:GetAllAffectors()) do
if tbl[val .. "Mult" .. condition] != nil then
if !pcall(function() stat = stat * math.pow(tbl[val .. "Mult" .. condition], amount) end) then
print("!!! ARC9 ERROR - \"" .. (tbl["PrintName"] or "Unknown") .. "\" TRIED TO MULTIPLY INVALID VALUE: (" .. tbl[val .. "Add" .. condition] .. ") TO " .. val .. "!")
end
end
end
end
self.StatCache[tostring(base) .. val .. condition] = stat
stat = self:RunHook(val .. "Hook" .. condition, stat) or stat
return stat
end

View File

@ -0,0 +1,126 @@
SWEP.AttachmentAddresses = {}
function SWEP:LocateSlotFromAddress(address)
return self.AttachmentAddresses[address]
end
function SWEP:BuildAttachmentAddresses()
self.AttachmentAddresses = {}
for c, i in pairs(self:GetSubSlotList()) do
i.Address = c
self.AttachmentAddresses[c] = i
end
end
function SWEP:AttTreeToList(tree)
if !istable(tree) then return {} end
local atts = {}
atts = {tree}
if tree.SubAttachments then
for _, sub in pairs(tree.SubAttachments) do
table.Add(atts, self:AttTreeToList(sub))
end
end
return atts
end
function SWEP:GetSubSlotList()
local atts = {}
for _, i in pairs(self.Attachments or {}) do
table.Add(atts, self:AttTreeToList(i))
end
return atts
end
function SWEP:GetAttachmentList()
local atts = {}
for _, i in pairs(self:GetSubSlotList()) do
if i.Installed then
table.insert(atts, i.Installed)
end
end
return atts
end
function SWEP:BuildSubAttachmentTree(tbl, parenttbl)
if !tbl.Installed then return {} end
local atttbl = ARC9.GetAttTable(tbl.Installed)
local subatts = {}
if atttbl then
if atttbl.Attachments then
subatts = table.Copy(atttbl.Attachments)
for i, k in ipairs(tbl.SubAttachments) do
subatts[i].Bone = parenttbl.Bone
local pos, _ = LocalToWorld(subatts[i].Pos or Vector(0, 0, 0), subatts[i].Ang or Angle(0, 0, 0), parenttbl.Pos, parenttbl.Ang)
subatts[i].Pos = pos
subatts[i].Ang = subatts[i].Ang + parenttbl.Ang
subatts[i].Ang:Normalize()
subatts[i].ExtraSightDistance = parenttbl.ExtraSightDistance
subatts[i].Installed = tbl.SubAttachments[i].Installed
subatts[i].SubAttachments = self:BuildSubAttachmentTree(k, subatts[i])
end
end
end
return subatts
end
function SWEP:BuildSubAttachments(tbl)
for i, k in pairs(self.Attachments) do
k.SubAttachments = {}
end
for i, k in pairs(tbl) do
self.Attachments[i].Installed = k.Installed
if !k.Installed then continue end
local atttbl = ARC9.GetAttTable(k.Installed)
if atttbl then
if atttbl.Attachments then
self.Attachments[i].SubAttachments = self:BuildSubAttachmentTree(k, self.Attachments[i])
end
end
end
self:BuildAttachmentAddresses()
end
function SWEP:ValidateInventoryForNewTree(tree)
local count = self:CountAttsInTree(tree)
local currcount = self:CountAttsInTree(self.Attachments)
for att, attc in pairs(count) do
local atttbl = ARC9.GetAttTable(att)
if atttbl.Free then continue end
if (currcount[att] or 0) + ARC9:PlayerGetAtts(self:GetOwner(), att) > count[att] then
continue
end
return false
end
return true
end
function SWEP:PruneAttachments()
end

View File

@ -0,0 +1,43 @@
function SWEP:Think()
local owner = self:GetOwner()
if owner:KeyReleased(IN_ATTACK) then
self:SetNeedTriggerPress(false)
if !self:GetProcessedValue("RunawayBurst") then
self:SetBurstCount(0)
end
if self:GetCurrentFiremode() < 0 and !self:GetProcessedValue("RunawayBurst") and self:GetBurstCount() > 0 then
self:SetNextPrimaryFire(CurTime() + self:GetProcessedValue("PostBurstDelay"))
end
end
if self:GetProcessedValue("RunawayBurst") then
if self:GetBurstCount() >= self:GetCurrentFiremode() and self:GetCurrentFiremode() > 0 then
self:SetBurstCount(0)
self:SetNextPrimaryFire(CurTime() + self:GetProcessedValue("PostBurstDelay"))
if !self:GetProcessedValue("AutoBurst") then
self:SetNeedTriggerPress(true)
end
elseif self:GetBurstCount() > 0 and self:GetBurstCount() < self:GetCurrentFiremode() then
self:PrimaryAttack()
end
end
self:ThinkRecoil()
self:ThinkSprint()
self:ThinkReload()
self:ThinkSights()
self:ThinkFiremodes()
self:ThinkFreeAim()
self:ProcessTimers()
if self:GetNextIdle() < CurTime() then
self:Idle()
end
end

View File

@ -0,0 +1,80 @@
local tick = 0
// Avoid using this system - it breaks prediction.
function SWEP:InitTimers()
self.ActiveTimers = {} -- { { time, id, func } }
end
function SWEP:SetTimer(time, callback, id)
if !IsFirstTimePredicted() then return end
table.insert(self.ActiveTimers, { time + CurTime(), id or "", callback })
end
function SWEP:TimerExists(id)
for _, v in pairs(self.ActiveTimers) do
if v[2] == id then return true end
end
return false
end
function SWEP:KillTimer(id)
local keeptimers = {}
for _, v in pairs(self.ActiveTimers) do
if v[2] != id then table.insert(keeptimers, v) end
end
self.ActiveTimers = keeptimers
end
function SWEP:KillTimers()
self.ActiveTimers = {}
end
function SWEP:ProcessTimers()
local keeptimers = {}
local UCT = CurTime()
if CLIENT and UCT == tick then return end
if !self.ActiveTimers then self:InitTimers() end
for _, v in pairs(self.ActiveTimers) do
if v[1] <= UCT then v[3]() end
end
for _, v in pairs(self.ActiveTimers) do
if v[1] > UCT then table.insert(keeptimers, v) end
end
self.ActiveTimers = keeptimers
end
function SWEP:PlaySoundTable(soundtable, mult)
--if CLIENT and game.SinglePlayer() then return end
local owner = self:GetOwner()
start = start or 0
mult = 1 / (mult or 1)
for _, v in pairs(soundtable) do
local ttime
if v.t then
ttime = v.t * mult
else
continue
end
if ttime < 0 then continue end
if !(IsValid(self) and IsValid(owner)) then continue end
local playtime = ttime
self:SetTimer(playtime, function()
self:EmitSound(self:RandomChoice(v.s or ""), v.v or 75, v.p or 100, 1, v.c or CHAN_AUTO)
end)
end
end

View File

@ -0,0 +1,40 @@
function SWEP:SanityCheck()
if !IsValid(self) then return false end
if !IsValid(self:GetOwner()) then return false end
if !IsValid(self:GetVM()) then return false end
end
function SWEP:GetWM()
return self.WModel[1]
end
function SWEP:GetVM()
return self:GetOwner():GetViewModel()
end
function SWEP:Curve(x)
return 0.5 * math.cos((x + 1) * math.pi) + 0.5
end
function SWEP:IsAnimLocked()
return self:GetAnimLockTime() > CurTime()
end
function SWEP:RandomChoice(choice)
if istable(choice) then
choice = table.Random(choice)
end
return choice
end
function SWEP:PatternWithRunOff(pattern, runoff, num)
if num < #pattern then
return pattern[num]
else
num = num - #pattern
num = num % #runoff
return runoff[num + 1]
end
end

View File

@ -0,0 +1,785 @@
AddCSLuaFile()
SWEP.Spawnable = false
SWEP.Category = "ARC-9"
SWEP.AdminOnly = false
-- Any string can be replaced with a localized string by using # in front of it.
-- Get the appropriate localized string for the weapon with #WEAPON_NAME_VARIABLE.
-- For example, #A2_BASE_PRINTNAME.
-- Otherwise, it'll just use the direct string.
-- If a localized string is unavailable in your language, English will be used. Otherwise, the default language will be used.
SWEP.PrintName = "ARC9 Base"
SWEP.TrueName = nil
-- PrintName could be a game's fictional name for a gun, while TrueName is its real name.
-- You could also have it be a generic name, like "Assault Rifle" vs. "AK-47".
-- TrueName should be something that improves the cross-compatibility of weapon naming.
SWEP.Class = ARC9.CLASS_OTHER -- Refer to ARC9/common/sh_enums.lua.
SWEP.Trivia = {} -- Optional. Any stats you like can be added.
-- SWEP.Trivia = {
-- Manufacturer = "Arctic Armament International",
-- Calibre = "9x21mm Jager",
-- Mechanism = "Roller-Delayed Blowback",
-- Country = "UK-Australia-China",
-- Year = 2021
-- }
SWEP.Description = [[]]
-- Multi-line strings are possible with the double square brackets.]]
SWEP.UseHands = true -- Same as weapon_base
SWEP.ViewModel = ""
SWEP.WorldModel = ""
SWEP.MirrorVMWM = false -- Use this to use the viewmodel as a worldmodel.
-- Highly recommended to save effort!
SWEP.WorldModelMirror = nil -- Use this to set a lower-quality version of the viewmodel, with the same bone structure, as a worldmodel, to take advantage of MirrorVMWM without having to use the viewmodel.
SWEP.WorldModelOffset = nil
-- SWEP.WorldModelOffset = {
-- pos = Vector(0, 0, 0),
-- ang = Angle(0, 0, 0),
-- scale = 1
-- }
-------------------------- SAVING
SWEP.SaveBase = nil -- set to a weapon class to make this weapon share saves with it.
-------------------------- DEFAULT ELEMENTS
-- Using MirrorVMWM will use viewmodel parameters for the world model.
SWEP.DefaultBodygroups = {}
-- {
-- {
-- ind = 0,
-- bg = 0,
-- }
-- }
SWEP.DefaultWMBodygroups = {}
SWEP.DefaultSkin = 0
SWEP.DefaultWMSkin = 0
-------------------------- DAMAGE PROFILE
SWEP.DamageMax = 20 -- Damage done at point blank range
SWEP.DamageMin = 15 -- Damage done at maximum range
SWEP.DamageRand = 0 -- Damage varies randomly per shot by this fraction. 0.1 = +- 10% damage per shot.
SWEP.RangeMin = 0 -- How far bullets retain their maximum damage for.
SWEP.RangeMax = 5000 -- In Hammer units, how far bullets can travel before dealing DamageMin.
SWEP.Num = 1 -- Number of bullets to shoot
-- Bear in mind: Damage is divided by Num
SWEP.Penetration = 5 -- Units of wood that can be penetrated by this gun.
SWEP.RicochetAngleMax = 45 // maximum angle at which a ricochet can occur. Between 1 and 90. Angle of 0 is impossible but would theoretically always ricochet.
SWEP.RicochetChance = 1 // if the angle is right, what is the chance that a ricochet can occur?
SWEP.DamageType = DMG_BULLET -- The damage type of the gun.
-- DMG_BLAST will create explosive effects and create AOE damage.
-- DMG_BURN will ignite the target.
SWEP.ArmorPiercing = 0 -- Between 0-1. A proportion of damage that is done as direct damage, ignoring protection.
SWEP.BodyDamageMults = {
[HITGROUP_HEAD] = 1.25,
[HITGROUP_CHEST] = 1,
[HITGROUP_LEFTARM] = 0.9,
[HITGROUP_RIGHTARM] = 0.9,
}
-- Set the multiplier for each part of the body.
-- If a limb is not set the damage multiplier will default to 1
-- That means gmod's stupid default limb mults will **NOT** apply
-- {
-- [HITGROUP_HEAD] = 1.25,
-- [HITGROUP_CHEST] = 1,
-- [HITGROUP_LEFTARM] = 0.9,
-- [HITGROUP_RIGHTARM] = 0.9,
-- }
-------------------------- ENTITY LAUNCHING
SWEP.ShootEntity = nil -- Set to an entity to launch it out of this weapon.
SWEP.EntityMuzzleVelocity = 10000
SWEP.ShootEntityData = {} -- Extra data that can be given to a projectile. Sets SENT.WeaponDataLink with this table.
-------------------------- PHYS BULLET BALLISTICS
-- These settings override the player's physical bullet options.
SWEP.AlwaysPhysBullet = false
SWEP.NeverPhysBullet = false
SWEP.PhysBulletMuzzleVelocity = 150000 -- Physical bullet muzzle velocity in Hammer Units/second. 1 HU ~= 1 inch.
SWEP.PhysBulletDrag = 1 -- Drag multiplier
SWEP.PhysBulletGravity = 1 -- Gravity multiplier
SWEP.PhysBulletDontInheritPlayerVelocity = false -- Set to true to disable "Browning Effect"
SWEP.FancyBullets = false -- set to true to allow for multicolor mags and crap
-- Each bullet runs HookP_ModifyBullet, within which modifications can be made
-------------------------- TRACERS
SWEP.TracerNum = 1 -- Tracer every X
SWEP.TracerFinalMag = 0 -- The last X bullets in a magazine are all tracers
SWEP.TracerEffect = "ARC9_tracer" -- The effect to use for hitscan tracers
SWEP.TracerColor = Color(255, 255, 255) -- Color of tracers. Only works if tracer effect supports it. For physical bullets, this is compressed down to 9-bit color.
-------------------------- MAGAZINE
SWEP.Ammo = "pistol" -- What ammo type this gun uses.
SWEP.ChamberSize = 1 -- The amount of rounds this gun can chamber.
SWEP.ClipSize = 25 -- Self-explanatory.
SWEP.SupplyLimit = 5 -- Amount of magazines of ammo this gun can take from an ArcCW-A2 supply crate.
SWEP.SecondarySupplyLimit = 2 -- Amount of reserve UBGL magazines you can take.
SWEP.ForceDefaultClip = nil -- Set to force a default amount of ammo this gun can have. Otherwise, this is controlled by console variables.
SWEP.AmmoPerShot = 1
SWEP.InfiniteAmmo = false -- Weapon reloads for free
SWEP.BottomlessClip = false -- Weapon never has to reload
SWEP.ShotgunReload = false -- Weapon reloads like shotgun. Uses insert_1, insert_2, etc animations instead.
SWEP.HybridReload = false -- Enable on top of Shotgun Reload. If the weapon is completely empty, use the normal reload animation.
-- Use SWEP.Hook_TranslateAnimation in order to do custom animation stuff.
SWEP.ManualActionChamber = 1 -- How many shots we go between needing to cycle again.
SWEP.ManualAction = false -- Pump/bolt action. Play the "cycle" animation after firing, when the trigger is released.
SWEP.ManualActionNoLastCycle = false -- Do not cycle on the last shot.
SWEP.ReloadInSights = false -- This weapon can aim down sights while reloading.
SWEP.CanFireUnderwater = false -- This weapon can shoot while underwater.
SWEP.Disposable = false -- When all ammo is expended, this gun will remove itself from the inventory.
SWEP.AutoReload = false -- When the gun is drawn, it will automatically reload.
SWEP.TriggerDelay = 0 -- Set to > 0 to play the "trigger" animation before shooting. Delay time is based on this value.
SWEP.DropMagazineModel = nil -- Set to a string or table to drop this magazine when reloading.
SWEP.DropMagazineSounds = {} -- Table of sounds a dropped magazine should play.
SWEP.DropMagazineAmount = 1 -- Amount of mags to drop.
-------------------------- FIREMODES
SWEP.RPM = 750
// Works different to ArcCW
// -1: Automatic
// 0: Safe. Don't use this for safety.
// 1: Semi.
// 2: Two-round burst.
// 3: Three-round burst.
// n: n-round burst.
SWEP.Firemodes = {
{
Mode = 1,
// add other attachment modifiers
}
}
SWEP.AutoBurst = false -- Hold fire to keep firing bursts
SWEP.PostBurstDelay = 0
SWEP.RunAwayBurst = false -- Burst will keep firing until all of the burst has been expended.
SWEP.NonResetBurst = false -- Annoying behaviour where you have to shoot ALL THREE BULLETS of a burst before it resets. Supposedly realistic for the M16.
SWEP.Akimbo = false
-- Use this hook to modify features of a firemode.
-- SWEP.HookP_ModifyFiremode = function(self, firemode) return firemode end
-------------------------- RECOIL
SWEP.RecoilSeed = nil -- Leave blank to use weapon class name as recoil seed.
-- Should be a number.
SWEP.RecoilLookupTable = nil -- Use to set specific values for predictible recoil. If it runs out, it'll just use Recoil Seed.
-- SWEP.RecoilLookupTable = {
-- {
-- x = -1,
-- y = 1
-- }
-- }
SWEP.RecoilLookupTableOverrun = nil -- Repeatedly take values from this table if we run out in the main table
-- General recoil multiplier
SWEP.Recoil = 1
-- These multipliers affect the predictible recoil by making the pattern taller, shorter, wider, or thinner.
SWEP.RecoilUp = 1 -- Multiplier for vertical recoil
SWEP.RecoilSide = 1 -- Multiplier for vertical recoil
-- These values determine how much extra movement is applied to the recoil entirely randomly, like in a circle.
-- This type of recoil CANNOT be predicted.
SWEP.RecoilRandomUp = 0.1
SWEP.RecoilRandomSide = 0.1
SWEP.RecoilDissipationRate = 10 -- How much recoil dissipates per second.
SWEP.RecoilResetTime = 0.1 -- How long the gun must go before the recoil pattern starts to reset.
SWEP.RecoilAutoControl = 1 -- Multiplier for automatic recoil control.
-------------------------- VISUAL RECOIL
SWEP.UseVisualRecoil = true
SWEP.VisualRecoilUp = 0.15 -- Vertical tilt for visual recoil.
SWEP.VisualRecoilSide = 0.1 -- Horizontal tilt for visual recoil.
SWEP.VisualRecoilRoll = 1 -- Roll tilt for visual recoil.
SWEP.VisualRecoilCenter = Vector(0, 2, 2) -- The "axis" of visual recoil. Where your hand is.
SWEP.VisualRecoilPunch = 1 -- How far back visual recoil moves the gun.
SWEP.VisualRecoilMultSights = 0.25 -- Visual recoil multiplier while in sights.
SWEP.RecoilKick = 1 -- Camera recoil
-------------------------- SPREAD
SWEP.Spread = 0
SWEP.UsePelletSpread = false -- Multiple bullets fired at once clump up, like for a shotgun. Spread affects which direction they get fired, not their spread relative to one another.
SWEP.PelletSpread = 0.2
SWEP.PelletSpreadPattern = {} -- Use to give shotguns custom spread patterns. If Pellet Spread is off, each pellet will be subject to spread. Otherwise, the entire pattern shifts, and each pellet is randomly offset by pellet spread amount.
-- SWEP.PelletSpreadPattern = {
-- {
-- x = -1,
-- y = -1
-- },
-- {
-- x = -1,
-- y = 1
-- }
-- }
SWEP.PelletSpreadPatternOverrun = nil
-- {Angle(1, 1, 0), Angle(1, 0, 0) ..}
-- list of how far each pellet should veer
-- if only one pellet then it'll use the first index
-- if two then the first two
-- in case of overrun pellets will start looping, preferably with the second one, so use that for the loopables
SWEP.SpreadAddMove = 0 -- Applied when speed is equal to walking speed.
SWEP.SpreadAddMidAir = 0 -- Applied when not touching the ground.
SWEP.SpreadAddHipFire = 0 -- Applied when not sighted.
SWEP.SpreadAddSighted = 0 -- Applied when sighted. Can be negative.
SWEP.SpreadAddBlindFire = 0 -- Applied when blind firing.
SWEP.SpreadAddCrouch = 0 -- Applied when crouching.
SWEP.SpreadAddRecoil = 0 -- Applied per unit of recoil.
-------------------------- HANDLING
SWEP.FreeAimRadius = 10 -- In degrees, how much this gun can free aim in hip fire.
SWEP.Sway = 1 -- How much the gun sways.
SWEP.FreeAimMultSights = 0.25
SWEP.SwayMultSights = 0.5
SWEP.AimDownSightsTime = 0.25 -- How long it takes to go from hip fire to aiming down sights.
SWEP.SprintToFireTime = 0.25 -- How long it takes to go from sprinting to being able to fire.
SWEP.ReloadTimeMult = 1
SWEP.DeployTimeMult = 1
SWEP.CycleTimeMult = 1
SWEP.FixTimeMult = 1
SWEP.OverheatTimeMult = 1
SWEP.ShootWhileSprint = false
SWEP.SpeedMult = 1
SWEP.SpeedMultSights = 0.75
SWEP.SpeedMultShooting = 1
SWEP.SpeedMultMelee = 0.75
SWEP.SpeedMultCrouch = 1
SWEP.SpeedMultBlindFire = 1
-------------------------- MALFUNCTIONS
SWEP.Overheat = false -- Weapon will jam when it overheats, playing the "overheat" animation.
SWEP.HeatCapacity = 50 -- rounds that can be fired non-stop before the gun jams, playing the "fix" animation
SWEP.HeatDissipation = 10 -- rounds' worth of heat lost per second
SWEP.HeatLockout = true -- overheating means you cannot fire until heat has been fully depleted
SWEP.HeatDelayTime = 0.5 -- Amount of time that passes before heat begins to dissipate.
SWEP.HeatFix = false -- when the "overheat" animation is played, all heat is restored.
-- If Malfunction is enabled, the gun has a random chance to be jammed
-- after the gun is jammed, it won't fire unless reload is pressed, which plays the "fix" animation
-- if no "fix" or "cycle" animations exist, the weapon will reload instead
-- When the trigger is pressed, the gun will try to play the "jamfire" animation. Otherwise, it will try "dryfire". Otherwise, it will do nothing.
SWEP.Malfunction = false
SWEP.MalfunctionJam = true -- After a malfunction happens, the gun will dryfire until reload is pressed. If unset, instead plays animation right after.
SWEP.MalfunctionTakeRound = true -- When malfunctioning, a bullet is consumed.
SWEP.MalfunctionWait = 0.25 -- The amount of time to wait before playing malfunction animation (or can reload)
SWEP.MalfunctionMeanShotsToFail = 1000 -- The mean number of shots between malfunctions, will be autocalculated if nil
-------------------------- HOOKS
-- SWEP.Hook_Draw = function(self, vm) end # Called when the weapon is drawn. Call functions here to modify the viewmodel, such as drawing RT screens onto the gun.
-- SWEP.Hook_HUDPaint = function(self) end
-- SWEP.HookP_ModifyFiremode = function(self, firemode) return firemode end
-- SWEP.HookP_ModifyBullet = function(self, bullet) return end # bullet = phys bullet table, modify in place, does not accept return
-- SWEP.HookP_BlockFire = function(self) return block end # return true to block firing
-------------------------- BLIND FIRE
SWEP.CanBlindFire = true -- This weapon is capable of blind firing.
SWEP.BlindFireOffset = Vector(0, 0, 16) -- The amount by which to offset the blind fire muzzle.
SWEP.BlindFirePos = Vector(1, -2, 0)
SWEP.BlindFireAng = Angle(0, 15, 0)
SWEP.BlindFireBoneMods = {
["ValveBiped.Bip01_R_UpperArm"] = {
ang = Angle(25, -50, 0),
pos = Vector(0, 0, 0)
},
["ValveBiped.Bip01_R_Hand"] = {
ang = Angle(-50, 0, 0),
pos = Vector(0, 0, 0)
}
}
-------------------------- NPC
SWEP.NotForNPCs = false -- Won't be given to NPCs.
SWEP.NPCWeight = 100 -- How likely it is for an NPC to get this weapon as opposed to other weapons.
-------------------------- BIPOD
SWEP.Bipod = false -- This weapon comes with a bipod.
-- When bipod is deployed, the gun does not experience recoil.
SWEP.BipodSpreadPenalty = 0
SWEP.BipodSpreadMult = 0.25
-------------------------- SOUNDS
SWEP.ShootVolume = 125
SWEP.ShootPitch = 100
SWEP.ShootPitchVariation = 0.05
SWEP.FirstShootSound = nil
SWEP.ShootSound = ""
SWEP.FirstShootSoundSilenced = nil
SWEP.ShootSoundSilenced = ""
SWEP.FirstShootSoundIndoor = nil
SWEP.ShootSoundIndoor = nil
SWEP.FirstShootSoundSilencedIndoor = nil
SWEP.ShootSoundSilencedIndoor = nil
SWEP.ShootSoundLooping = nil
SWEP.ShootSoundLoopingSilenced = nil
SWEP.Silencer = false -- Silencer installed or not?
SWEP.DistantShootSound = nil
SWEP.DryFireSound = ""
SWEP.FiremodeSound = ""
SWEP.EnterSightsSound = ""
SWEP.ExitSightsSound = ""
SWEP.EnterBipodSound = ""
SWEP.ExitBipodSound = ""
SWEP.SelectUBGLSound = ""
SWEP.ExitUBGLSound = ""
SWEP.MalfunctionSound = ""
-------------------------- EFFECTS
SWEP.NoFlash = false -- Disable light flash
SWEP.MuzzleParticle = "" -- Used for some muzzle effects.
SWEP.MuzzleEffect = nil
SWEP.FastMuzzleEffect = nil
SWEP.ImpactEffect = nil
SWEP.ImpactDecal = nil
SWEP.ShellEffect = nil -- Override the ARC9 shell eject effect for your own.
SWEP.ShellModel = "models/shells/shell_556.mdl"
SWEP.ShellMaterial = nil
SWEP.EjectDelay = 0
SWEP.ShellScale = Vector(1, 1, 1)
SWEP.ShellPhysBox = Vector(0.5, 0.5, 2)
SWEP.ShellPitch = 100 -- for shell sounds
SWEP.ShellSounds = ArcCW.ShellSoundsTable
SWEP.ShellCorrectPos = Vector(0, 0, 0)
SWEP.ShellCorrectAng = Angle(0, 0, 0)
SWEP.ShellTime = 0.5 -- Extra time these shells stay on the ground for.
SWEP.MuzzleEffectQCA = 1 -- QC Attachment that controls muzzle effect.
SWEP.CaseEffectQCA = 2 -- QC Attachment for shell ejection.
SWEP.CamQCA = nil -- QC Attachment for camera movement.
SWEP.ProceduralViewQCA = nil -- QC Attachment for procedural camera movement. Use if you don't have a camera. Usually the muzzle.
-------------------------- VISUALS
SWEP.BulletBones = { -- the bone that represents bullets in gun/mag
-- [0] = "bulletchamber",
-- [1] = "bullet1"
}
SWEP.CaseBones = {}
-- Unlike BulletBones, these bones are determined by the missing bullet amount when reloading
SWEP.StripperClipBones = {}
-- The same as the bone versions but works via bodygroups.
-- Bodygroups work the same as in attachmentelements.
-- [0] = {ind = 0, bg = 1}
SWEP.BulletBGs = {}
SWEP.CaseBGs = {}
SWEP.StripperClipBGs = {}
SWEP.PoseParameters = {} -- Poseparameters to manage. ["parameter"] = starting value.
-- Use animations to switch between different pose parameters.
-- When an animation is being played that switches between pose parameters, those parameters are all set to 0 for the animation.
-- There are also different default pose parameters:
-- firemode (Changes based on Fire Mode. Don't use this if you have animated firemode switching.)
-- sights (Changes based on sight delta)
-- sprint (Changes based on sprint delta)
-- empty (Changes based on whether a bullet is loaded)
-- ammo (Changes based on the ammo in the clip)
-------------------------- POSITIONS
SWEP.IronSights = {
Pos = Vector(0, 0, 0),
Ang = Angle(0, 0, 0),
Magnification = 1,
AssociatedSlot = 0, -- Attachment slot to associate the sights with. Causes RT scopes to render.
CrosshairInSights = false,
}
SWEP.SightMidpoint = { -- Where the gun should be at the middle of it's irons
Pos = Vector(0, 15, -4),
Ang = Angle(0, 0, -45),
}
SWEP.HasSights = true
-- Alternative "resting" position
SWEP.ActivePos = Vector(0, 0, 0)
SWEP.ActiveAng = Angle(0, 0, 0)
-- Position when sprinting or holstered
SWEP.HolsterPos = Vector(0.532, -6, 0)
SWEP.HolsterAng = Angle(-4.633, 36.881, 0)
-- Overrides HolsterPos/Ang but only for sprinting
SWEP.SprintPos = nil
SWEP.SprintAng = nil
SWEP.SprintMidPoint = {
pos = Vector(4, 10, 2),
ang = Angle(0, -10, -45)
}
-- Position for customizing
SWEP.CustomizeAng = Angle(90, 0, 0)
SWEP.CustomizePos = Vector(20, 32, 4)
SWEP.InBipodPos = Vector(-8, 0, -4)
SWEP.InBipodMult = Vector(2, 1, 1)
-------------------------- HoldTypeS
SWEP.HoldType = "shotgun"
SWEP.HoldTypeSprint = "passive"
SWEP.HoldTypeHolstered = nil
SWEP.HoldTypeSights = "smg"
SWEP.HoldTypeCustomize = "slam"
SWEP.HoldTypeBlindfire = "pistol"
SWEP.HoldTypeNPC = nil
SWEP.AnimShoot = ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2
SWEP.AnimReload = ACT_HL2MP_GESTURE_RELOAD_AR2
SWEP.AnimDraw = ACT_HL2MP_GESTURE_RANGE_ATTACK_KNIFE
-------------------------- TTT
-- No free attachments when this gun is purchased
SWEP.TTTNoAttachmentsOnBuy = false
-- Automatically spawn in TTT
SWEP.TTTAutospawn = true
-- Specifically replace a certain weapon in TTT
SWEP.TTTWeaponType = nil
-- The chance this weapon will spawn in TTT
SWEP.TTTWeight = 100
-- Use a different ammo type in TTT
SWEP.TTTAmmoType = nil
-------------------------- ATTACHMENTS
SWEP.AttachmentBodygroups = {
-- ["name"] = {
-- VM = {
-- {
-- ind = 1,
-- bg = 1
-- }
-- },
-- WM = {
-- {
-- ind = 1,
-- bg = 1
-- }
-- },
-- }
}
-- Activate attachment elements by default.
SWEP.DefaultElements = {}
-- Allows for the attachment of models and stuff, like SCK.
-- You can use SCK to get the positions and angles for this.
-- Not recommended to be used.
SWEP.AttachmentElements = {
-- ["name"] = {
-- VM = {
-- model = "",
-- color = Color(255, 255, 255),
-- skin = 0,
-- bodygroups = "",
-- scale = Vector(1, 1 ,1),
-- bone = "",
-- pos = Vector(0, 0, 0),
-- ang = Angle(0, 0, 0),
-- },
-- WM = {
-- model = "",
-- color = Color(255, 255, 255),
-- skin = 0,
-- bodygroups = "",
-- scale = Vector(1, 1 ,1),
-- bone = "",
-- pos = Vector(0, 0, 0),
-- ang = Angle(0, 0, 0),
-- }
-- }
}
-- Use to override attachment table entry data.
SWEP.AttachmentSlotMods = {
-- ["name"] = {
-- [1] = {
-- }
-- }
}
-- Adjust the stats of specific attachments when applied to this gun
SWEP.AttachmentTableOverrides = {
-- ["att_name"] = {
-- Mult_Recoil = 1
-- }
}
-- Specifically refuse to allow certain attachments to be attached
SWEP.RejectAttachments = {
-- ["att_name"] = true
}
-- The big one
SWEP.Attachments = {
-- [1] = {
-- PrintName = "",
-- DefaultIcon = Material(""),
-- InstalledElements = {""}, // list of elements to activate when something is installed here
-- UnInstalledElements = {""},
-- RequireElements = {}, // {{a and b}, or {c and d and e}, or f}
-- // list of "strings" or {"lists", "of", "strings"}.
-- // one of these must all be enabled for this to be valid.
-- ExcludeElements = {},
-- // same but for exclusion.
-- Integral = false, // cannot be removed
-- Category = "", // single or {"list", "of", "values"}
-- Bone = "",
-- Pos = Vector(0, 0, 0),
-- Ang = Angle(0, 0, 0),
-- KeepBaseIrons = false,
-- ExtraSightDistance = 0,
-- Installed = nil,
-- MergeSlots = {},
-- SubAttachments = {
-- {
-- Installed = nil,
-- SubAttachments = {}
-- },
-- {
-- Installed = nil,
-- SubAttachments = {}
-- }
-- }
-- }
-- }
}
-- Not necessary; if your sequences are named the same as animations, they will be used automatically.
SWEP.Animations = {
-- ["idle"] = {
-- Source = "idle",
-- Mult = 1.1,
-- },
-- ["draw"] = {
-- Source = {"deploy", "deploy2"}, -- QC sequence source, can be {"table", "of", "strings"} or "string"
-- RareSource = "magicdeploy", -- Has a small chance to play instead of normal source
-- RareSourceChance = 0.01, -- chance that rare source will play
-- Time = 0.5, -- overrides the duration of the sequence
-- Mult = 1, -- multiplies time
-- LHIK = false,
-- LHIKTimeline = {
-- {
-- t = 0.1,
-- ik = 0
-- },
-- {
-- t = 0.9,
-- ik = 1
-- }
-- },
-- RHIK = false,
-- RHIKTimeline = {
-- {
-- t = 0.1,
-- ik = 0
-- },
-- {
-- t = 0.9,
-- ik = 1
-- }
-- },
-- EventTable = {
-- {
-- t = 1, -- in seconds
-- s = "", -- sound to play
-- chan = CHAN_ITEM, -- sound channel
-- e = "", -- effect to emit
-- att = nil, -- on attachment point X
-- mag = 100, -- with magnitude whatever this is
-- ind = 0, -- change bodygroup
-- bg = 0,
-- }
-- },
-- PoseParamChanges = { -- pose parameters to change after this animation is done.
-- ["selector"] = 1 -- an application might be to change firemodes.
-- }, -- relevant pose parameters will be set to default values while the animation is playing, so make sure you take that into consideration for animating.
-- MagSwapTime = 0.5, -- in seconds, how long before the new magazine replaces the old one.
-- MinProgress = 0, -- seconds that must pass before the reload is considered done
-- }
}
SWEP.Primary.Automatic = true
SWEP.Primary.DefaultClip = -1
SWEP.Secondary.ClipSize = -1
SWEP.Secondary.DefaultClip = -1
SWEP.Secondary.Automatic = false
SWEP.Secondary.Ammo = "none"
SWEP.DrawCrosshair = true
SWEP.ARC9 = true
local searchdir = "weapons/arc9_base"
local function autoinclude(dir)
local files, dirs = file.Find(searchdir .. "/*.lua", "LUA")
for _, filename in pairs(files) do
if filename == "shared.lua" then continue end
local luatype = string.sub(filename, 1, 2)
if luatype == "sv" then
if SERVER then
include(dir .. "/" .. filename)
end
elseif luatype == "cl" then
AddCSLuaFile(dir .. "/" .. filename)
if CLIENT then
include(dir .. "/" .. filename)
end
else
AddCSLuaFile(dir .. "/" .. filename)
include(dir .. "/" .. filename)
end
end
for _, path in pairs(dirs) do
autoinclude(dir .. "/" .. path)
end
end
autoinclude(searchdir)
function SWEP:SetupDataTables()
self:NetworkVar("Float", 0, "RecoilAmount")
self:NetworkVar("Float", 1, "AnimLockTime")
self:NetworkVar("Float", 2, "NextIdle")
self:NetworkVar("Float", 3, "LastRecoilTime")
self:NetworkVar("Float", 4, "RecoilUp")
self:NetworkVar("Float", 5, "RecoilSide")
self:NetworkVar("Float", 6, "SprintAmount")
self:NetworkVar("Float", 7, "LastMeleeTime")
self:NetworkVar("Float", 8, "PrimedAttackTime")
self:NetworkVar("Float", 9, "StartPrimedAttackTime")
self:NetworkVar("Float", 10, "ReloadFinishTime")
self:NetworkVar("Float", 11, "SightAmount")
self:NetworkVar("Float", 12, "HeatAmount")
self:NetworkVar("Float", 13, "BlindFireAmount")
self:NetworkVar("Int", 0, "BurstCount")
self:NetworkVar("Int", 1, "NthShot")
self:NetworkVar("Int", 2, "LoadedRounds")
self:NetworkVar("Int", 3, "Firemode")
self:NetworkVar("Int", 4, "NthReload")
self:NetworkVar("Bool", 0, "Customize")
self:NetworkVar("Bool", 1, "Reloading")
self:NetworkVar("Bool", 2, "EndReload")
self:NetworkVar("Bool", 3, "Safe")
self:NetworkVar("Bool", 4, "Jammed")
self:NetworkVar("Bool", 5, "Ready")
self:NetworkVar("Bool", 6, "TriggerDown")
self:NetworkVar("Bool", 7, "NeedTriggerPress")
self:NetworkVar("Bool", 8, "UBGL")
self:NetworkVar("Bool", 9, "EmptyReload")
self:NetworkVar("Bool", 10, "InSights")
self:NetworkVar("Bool", 11, "PrimedAttack")
self:NetworkVar("Bool", 12, "BlindFire")
self:NetworkVar("Bool", 13, "BlindFireLeft")
self:NetworkVar("Angle", 0, "FreeAimAngle")
self:NetworkVar("Angle", 1, "LastAimAngle")
self:SetFiremode(1)
self:SetNthReload(0)
self:SetNthShot(0)
end
function SWEP:SecondaryAttack()
end