Advertisement
Purfect

Untitled

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