Advertisement
Guest User

Untitled

a guest
Jul 19th, 2018
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.63 KB | None | 0 0
  1.  
  2. include("weaponry_shd.lua") -- inits WEPS tbl
  3.  
  4. ---- Weapon system, pickup limits, etc
  5.  
  6. local IsEquipment = WEPS.IsEquipment
  7.  
  8. -- Prevent players from picking up multiple weapons of the same type etc
  9. function GM:PlayerCanPickupWeapon(ply, wep)
  10. if not IsValid(wep) or not IsValid(ply) then return end
  11. if ply:IsSpec() then return false end
  12.  
  13. -- Disallow picking up for ammo
  14. if ply:HasWeapon(wep:GetClass()) then
  15. return false
  16. elseif not ply:CanCarryWeapon(wep) then
  17. return false
  18. elseif IsEquipment(wep) and wep.IsDropped and (not ply:KeyDown(IN_USE)) then
  19. return false
  20. end
  21.  
  22. local tr = util.TraceEntity({start=wep:GetPos(), endpos=ply:GetShootPos(), mask=MASK_SOLID}, wep)
  23. if tr.Fraction == 1.0 or tr.Entity == ply then
  24. wep:SetPos(ply:GetShootPos())
  25. end
  26.  
  27. return true
  28. end
  29.  
  30. -- Cache role -> default-weapons table
  31. local loadout_weapons = nil
  32. local function GetLoadoutWeapons(r)
  33. if not loadout_weapons then
  34. local tbl = {
  35. [ROLE_INNOCENT] = {},
  36. [ROLE_TRAITOR] = {},
  37. [ROLE_DETECTIVE]= {}
  38. };
  39.  
  40. for k, w in pairs(weapons.GetList()) do
  41. if w and type(w.InLoadoutFor) == "table" then
  42. for _, wrole in pairs(w.InLoadoutFor) do
  43. table.insert(tbl[wrole], WEPS.GetClass(w))
  44. end
  45. end
  46. end
  47.  
  48. loadout_weapons = tbl
  49. end
  50.  
  51. return loadout_weapons[r]
  52. end
  53.  
  54. -- Give player loadout weapons he should have for his role that he does not have
  55. -- yet
  56. local function GiveLoadoutWeapons(ply)
  57. local r = GetRoundState() == ROUND_PREP and ROLE_INNOCENT or ply:GetRole()
  58. local weps = GetLoadoutWeapons(r)
  59. if not weps then return end
  60.  
  61. for _, cls in pairs(weps) do
  62. if not ply:HasWeapon(cls) and ply:CanCarryType(WEPS.TypeForWeapon(cls)) then
  63. ply:Give(cls)
  64. end
  65. end
  66. end
  67.  
  68. local function HasLoadoutWeapons(ply)
  69. if ply:IsSpec() then return true end
  70.  
  71. local r = GetRoundState() == ROUND_PREP and ROLE_INNOCENT or ply:GetRole()
  72. local weps = GetLoadoutWeapons(r)
  73. if not weps then return true end
  74.  
  75.  
  76. for _, cls in pairs(weps) do
  77. if not ply:HasWeapon(cls) and ply:CanCarryType(WEPS.TypeForWeapon(cls)) then
  78. return false
  79. end
  80. end
  81.  
  82. return true
  83. end
  84.  
  85. -- Give loadout items.
  86. local function GiveLoadoutItems(ply)
  87. local items = EquipmentItems[ply:GetRole()]
  88. if items then
  89. for _, item in pairs(items) do
  90. if item.loadout and item.id then
  91. ply:GiveEquipmentItem(item.id)
  92. end
  93. end
  94. end
  95. end
  96.  
  97. -- Quick hack to limit hats to models that fit them well
  98. local Hattables = { "phoenix.mdl", "arctic.mdl", "Group01", "monk.mdl" }
  99. local function CanWearHat(ply)
  100. local path = string.Explode("/", ply:GetModel())
  101. if #path == 1 then path = string.Explode("\\", path) end
  102.  
  103. return table.HasValue(Hattables, path[3])
  104. end
  105.  
  106. CreateConVar("ttt_detective_hats", "0")
  107. -- Just hats right now
  108. local function GiveLoadoutSpecial(ply)
  109. if ply:IsActiveDetective() and GetConVar("ttt_detective_hats"):GetBool() and CanWearHat(ply) then
  110.  
  111. if not IsValid(ply.hat) then
  112. local hat = ents.Create("ttt_hat_deerstalker")
  113. if not IsValid(hat) then return end
  114.  
  115. hat:SetPos(ply:GetPos() + Vector(0,0,70))
  116. hat:SetAngles(ply:GetAngles())
  117.  
  118. hat:SetParent(ply)
  119.  
  120. ply.hat = hat
  121.  
  122. hat:Spawn()
  123. end
  124. else
  125. SafeRemoveEntity(ply.hat)
  126.  
  127. ply.hat = nil
  128. end
  129. end
  130.  
  131. -- Sometimes, in cramped map locations, giving players weapons fails. A timer
  132. -- calling this function is used to get them the weapons anyway as soon as
  133. -- possible.
  134. local function LateLoadout(id)
  135. local ply = player.GetByID(id)
  136. if not IsValid(ply) then
  137. timer.Remove("lateloadout" .. id)
  138. return
  139. end
  140.  
  141. if not HasLoadoutWeapons(ply) then
  142. GiveLoadoutWeapons(ply)
  143.  
  144. if HasLoadoutWeapons(ply) then
  145. timer.Remove("lateloadout" .. id)
  146. end
  147. end
  148. end
  149.  
  150. -- Note that this is called both when a player spawns and when a round starts
  151. function GM:PlayerLoadout( ply )
  152. if IsValid(ply) and (not ply:IsSpec()) then
  153. -- clear out equipment flags
  154. ply:ResetEquipment()
  155.  
  156. -- give default items
  157. GiveLoadoutItems(ply)
  158.  
  159. -- hand out weaponry
  160. GiveLoadoutWeapons(ply)
  161.  
  162. GiveLoadoutSpecial(ply)
  163.  
  164. if not HasLoadoutWeapons(ply) then
  165. MsgN("Could not spawn all loadout weapons for " .. ply:Nick() .. ", will retry.")
  166. timer.Create("lateloadout" .. ply:EntIndex(), 1, 0,
  167. function() LateLoadout(ply:EntIndex()) end)
  168. end
  169. end
  170. end
  171.  
  172. function GM:UpdatePlayerLoadouts()
  173. for _, ply in ipairs(player.GetAll()) do
  174. hook.Call("PlayerLoadout", GAMEMODE, ply)
  175. end
  176. end
  177.  
  178. ---- Weapon switching
  179. local function ForceWeaponSwitch(ply, cmd, args)
  180. if not ply:IsPlayer() or not args[1] then return end
  181. -- Turns out even SelectWeapon refuses to switch to empty guns, gah.
  182. -- Worked around it by giving every weapon a single Clip2 round.
  183. -- Works because no weapon uses those.
  184. local wepname = args[1]
  185. local wep = ply:GetWeapon(wepname)
  186. if IsValid(wep) then
  187. -- Weapons apparently not guaranteed to have this
  188. if wep.SetClip2 then
  189. wep:SetClip2(1)
  190. end
  191. ply:SelectWeapon(wepname)
  192. end
  193. end
  194. concommand.Add("wepswitch", ForceWeaponSwitch)
  195.  
  196. ---- Weapon dropping
  197.  
  198. function WEPS.DropNotifiedWeapon(ply, wep, death_drop)
  199. if IsValid(ply) and IsValid(wep) then
  200. -- Hack to tell the weapon it's about to be dropped and should do what it
  201. -- must right now
  202. if wep.PreDrop then
  203. wep:PreDrop(death_drop)
  204. end
  205.  
  206. -- PreDrop might destroy weapon
  207. if not IsValid(wep) then return end
  208.  
  209. -- Tag this weapon as dropped, so that if it's a special weapon we do not
  210. -- auto-pickup when nearby.
  211. wep.IsDropped = true
  212.  
  213. ply:DropWeapon(wep)
  214.  
  215. wep:PhysWake()
  216.  
  217. -- After dropping a weapon, always switch to holstered, so that traitors
  218. -- will never accidentally pull out a traitor weapon
  219. ply:SelectWeapon("weapon_ttt_unarmed")
  220. end
  221. end
  222.  
  223. local function DropActiveWeapon(ply)
  224. if not IsValid(ply) then return end
  225.  
  226. local wep = ply:GetActiveWeapon()
  227.  
  228. if not IsValid(wep) then return end
  229.  
  230. if wep.AllowDrop == false then
  231. return
  232. end
  233.  
  234. local tr = util.QuickTrace(ply:GetShootPos(), ply:GetAimVector() * 32, ply)
  235.  
  236. if tr.HitWorld then
  237. LANG.Msg(ply, "drop_no_room")
  238. return
  239. end
  240.  
  241. ply:AnimPerformGesture(ACT_ITEM_PLACE)
  242.  
  243. WEPS.DropNotifiedWeapon(ply, wep)
  244. end
  245. concommand.Add("ttt_dropweapon", DropActiveWeapon)
  246.  
  247. local function DropActiveAmmo(ply)
  248. if not IsValid(ply) then return end
  249.  
  250. local wep = ply:GetActiveWeapon()
  251. if not IsValid(wep) then return end
  252.  
  253. if not wep.AmmoEnt then return end
  254.  
  255. local amt = wep:Clip1()
  256. if amt < 1 or amt <= (wep.Primary.ClipSize * 0.25) then
  257. LANG.Msg(ply, "drop_no_ammo")
  258. return
  259. end
  260.  
  261. local pos, ang = ply:GetShootPos(), ply:EyeAngles()
  262. local dir = (ang:Forward() * 32) + (ang:Right() * 6) + (ang:Up() * -5)
  263.  
  264. local tr = util.QuickTrace(pos, dir, ply)
  265. if tr.HitWorld then return end
  266.  
  267. wep:SetClip1(0)
  268.  
  269. ply:AnimPerformGesture(ACT_ITEM_GIVE)
  270.  
  271. local box = ents.Create(wep.AmmoEnt)
  272. if not IsValid(box) then return end
  273.  
  274. box:SetPos(pos + dir)
  275. box:SetOwner(ply)
  276. box:Spawn()
  277.  
  278. box:PhysWake()
  279.  
  280. local phys = box:GetPhysicsObject()
  281. if IsValid(phys) then
  282. phys:ApplyForceCenter(ang:Forward() * 1000)
  283. phys:ApplyForceOffset(VectorRand(), vector_origin)
  284. end
  285.  
  286. box.AmmoAmount = amt
  287.  
  288. timer.Simple(2, function()
  289. if IsValid(box) then
  290. box:SetOwner(nil)
  291. end
  292. end)
  293. end
  294. concommand.Add("ttt_dropammo", DropActiveAmmo)
  295.  
  296.  
  297. -- Give a weapon to a player. If the initial attempt fails due to heisenbugs in
  298. -- the map, keep trying until the player has moved to a better spot where it
  299. -- does work.
  300. local function GiveEquipmentWeapon(sid, cls)
  301. -- Referring to players by SteamID because a player may disconnect while his
  302. -- unique timer still runs, in which case we want to be able to stop it. For
  303. -- that we need its name, and hence his SteamID.
  304. local ply = player.GetBySteamID(sid)
  305. local tmr = "give_equipment" .. sid
  306.  
  307. if (not IsValid(ply)) or (not ply:IsActiveSpecial()) then
  308. timer.Remove(tmr)
  309. return
  310. end
  311.  
  312. -- giving attempt, will fail if we're in a crazy spot in the map or perhaps
  313. -- other glitchy cases
  314. local w = ply:Give(cls)
  315.  
  316. if (not IsValid(w)) or (not ply:HasWeapon(cls)) then
  317. if not timer.Exists(tmr) then
  318. timer.Create(tmr, 1, 0, function() GiveEquipmentWeapon(sid, cls) end)
  319. end
  320.  
  321. -- we will be retrying
  322. else
  323. -- can stop retrying, if we were
  324. timer.Remove(tmr)
  325.  
  326. if w.WasBought then
  327. -- some weapons give extra ammo after being bought, etc
  328. w:WasBought(ply)
  329. end
  330. end
  331. end
  332.  
  333. local function HasPendingOrder(ply)
  334. return timer.Exists("give_equipment" .. tostring(ply:SteamID()))
  335. end
  336.  
  337. function GM:TTTCanOrderEquipment(ply, id, is_item)
  338. --- return true to allow buying of an equipment item, false to disallow
  339. return true
  340. end
  341.  
  342. -- Equipment buying
  343. local function OrderEquipment(ply, cmd, args)
  344. if not IsValid(ply) or #args != 1 then return end
  345.  
  346. if not (ply:IsActiveTraitor() or ply:IsActiveDetective()) then return end
  347.  
  348. -- no credits, can't happen when buying through menu as button will be off
  349. if ply:GetCredits() < 1 then return end
  350.  
  351. -- it's an item if the arg is an id instead of an ent name
  352. local id = args[1]
  353. local is_item = tonumber(id)
  354.  
  355. if not hook.Run("TTTCanOrderEquipment", ply, id, is_item) then return end
  356.  
  357. -- we use weapons.GetStored to save time on an unnecessary copy, we will not
  358. -- be modifying it
  359. local swep_table = (not is_item) and weapons.GetStored(id) or nil
  360.  
  361. -- some weapons can only be bought once per player per round, this used to be
  362. -- defined in a table here, but is now in the SWEP's table
  363. if swep_table and swep_table.LimitedStock and ply:HasBought(id) then
  364. LANG.Msg(ply, "buy_no_stock")
  365. return
  366. end
  367.  
  368. local received = false
  369.  
  370. if is_item then
  371. id = tonumber(id)
  372.  
  373. -- item whitelist check
  374. local allowed = GetEquipmentItem(ply:GetRole(), id)
  375.  
  376. if not allowed then
  377. print(ply, "tried to buy item not buyable for his class:", id)
  378. return
  379. end
  380.  
  381. -- ownership check and finalise
  382. if id and EQUIP_NONE < id then
  383. if not ply:HasEquipmentItem(id) then
  384. ply:GiveEquipmentItem(id)
  385. received = true
  386. end
  387. end
  388. elseif swep_table then
  389. -- weapon whitelist check
  390. if not table.HasValue(swep_table.CanBuy, ply:GetRole()) then
  391. print(ply, "tried to buy weapon his role is not permitted to buy")
  392. return
  393. end
  394.  
  395. -- if we have a pending order because we are in a confined space, don't
  396. -- start a new one
  397. if HasPendingOrder(ply) then
  398. LANG.Msg(ply, "buy_pending")
  399. return
  400. end
  401.  
  402. -- no longer restricted to only WEAPON_EQUIP weapons, just anything that
  403. -- is whitelisted and carryable
  404. if ply:CanCarryWeapon(swep_table) then
  405. GiveEquipmentWeapon(ply:SteamID(), id)
  406.  
  407. received = true
  408. end
  409. end
  410.  
  411. if received then
  412. ply:SubtractCredits(1)
  413. LANG.Msg(ply, "buy_received")
  414.  
  415. ply:AddBought(id)
  416.  
  417. timer.Simple(0.5,
  418. function()
  419. if not IsValid(ply) then return end
  420. net.Start("TTT_BoughtItem")
  421. net.WriteBit(is_item)
  422. if is_item then
  423. net.WriteUInt(id, 16)
  424. else
  425. net.WriteString(id)
  426. end
  427. net.Send(ply)
  428. end)
  429.  
  430. hook.Call("TTTOrderedEquipment", GAMEMODE, ply, id, is_item)
  431. end
  432. end
  433. concommand.Add("ttt_order_equipment", OrderEquipment)
  434.  
  435. function GM:TTTToggleDisguiser(ply, state)
  436. -- Can be used to prevent players from using this button.
  437. -- return true to prevent it.
  438. end
  439.  
  440. local function SetDisguise(ply, cmd, args)
  441. if not IsValid(ply) or not ply:IsActiveTraitor() then return end
  442.  
  443. if ply:HasEquipmentItem(EQUIP_DISGUISE) then
  444. local state = #args == 1 and tobool(args[1])
  445. if hook.Run("TTTToggleDisguiser", ply, state) then return end
  446.  
  447. ply:SetNWBool("disguised", state)
  448. LANG.Msg(ply, state and "disg_turned_on" or "disg_turned_off")
  449. end
  450. end
  451. concommand.Add("ttt_set_disguise", SetDisguise)
  452.  
  453. local function CheatCredits(ply)
  454. if IsValid(ply) then
  455. ply:AddCredits(10)
  456. end
  457. end
  458. concommand.Add("ttt_cheat_credits", CheatCredits, nil, nil, FCVAR_CHEAT)
  459.  
  460. local function TransferCredits(ply, cmd, args)
  461. if (not IsValid(ply)) or (not ply:IsActiveSpecial()) then return end
  462. if #args != 2 then return end
  463.  
  464. local sid = tostring(args[1])
  465. local credits = tonumber(args[2])
  466. if sid and credits then
  467. local target = player.GetBySteamID(sid)
  468. if (not IsValid(target)) or (not target:IsActiveSpecial()) or (target:GetRole() ~= ply:GetRole()) or (target == ply) then
  469. LANG.Msg(ply, "xfer_no_recip")
  470. return
  471. end
  472.  
  473. if ply:GetCredits() < credits then
  474. LANG.Msg(ply, "xfer_no_credits")
  475. return
  476. end
  477.  
  478. credits = math.Clamp(credits, 0, ply:GetCredits())
  479. if credits == 0 then return end
  480.  
  481. ply:SubtractCredits(credits)
  482. target:AddCredits(credits)
  483.  
  484. LANG.Msg(ply, "xfer_success", {player=target:Nick()})
  485. LANG.Msg(target, "xfer_received", {player = ply:Nick(), num = credits})
  486. end
  487. end
  488. concommand.Add("ttt_transfer_credits", TransferCredits)
  489.  
  490. -- Protect against non-TTT weapons that may break the HUD
  491. function GM:WeaponEquip(wep)
  492. if IsValid(wep) then
  493. -- only remove if they lack critical stuff
  494. if not wep.Kind then
  495. wep:Remove()
  496. ErrorNoHalt("Equipped weapon " .. wep:GetClass() .. " is not compatible with TTT\n")
  497. end
  498. end
  499. end
  500.  
  501. -- non-cheat developer commands can reveal precaching the first time equipment
  502. -- is bought, so trigger it at the start of a round instead
  503. function WEPS.ForcePrecache()
  504. for k, w in ipairs(weapons.GetList()) do
  505. if w.WorldModel then
  506. util.PrecacheModel(w.WorldModel)
  507. end
  508. if w.ViewModel then
  509. util.PrecacheModel(w.ViewModel)
  510. end
  511. end
  512. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement