1. -- Global variables for SERVER context
  2. local lastPainSoundTime = {}
  3. local lastFreeFallSoundTime = {}
  4.  
  5. -- Rapid hit tracking for shotgun knockdown system
  6. local playerHitTracking = {}
  7. local RAPID_HIT_WINDOW = 0.5 -- Time window in seconds to track rapid hits
  8.  
  9. if SERVER then
  10.  
  11. -- Function to clean up old hit data to prevent memory leaks
  12. local function CleanupOldHitData()
  13. local currentTime = CurTime()
  14. for steamID, hitData in pairs(playerHitTracking) do
  15. -- Remove hits older than the tracking window
  16. for i = #hitData.hits, 1, -1 do
  17. if currentTime - hitData.hits[i] > RAPID_HIT_WINDOW then
  18. table.remove(hitData.hits, i)
  19. end
  20. end
  21.  
  22. -- Remove player entry if no recent hits
  23. if #hitData.hits == 0 then
  24. playerHitTracking[steamID] = nil
  25. end
  26. end
  27. end
  28.  
  29. -- Clean up old hit data every 2 seconds
  30. timer.Create("RADS_CleanupHitData", 2, 0, CleanupOldHitData)
  31.  
  32. -- vFire integration variables
  33. local vFireSupported = false
  34.  
  35. -- Check for vFire support
  36. timer.Simple(0.5, function()
  37. if vFireInstalled then
  38. vFireSupported = true
  39. if GetConVar("developer"):GetInt() > 0 then
  40. print("[RADS] vFire integration enabled in ragdoll system")
  41. end
  42. end
  43.  
  44.  
  45. if SERVER then
  46. -- Automatic shock ragdoll system
  47. hook.Add("Think", "RADS_ShockRagdollCheck", function()
  48. if not GetConVar("rads_shock_enable"):GetBool() then return end
  49.  
  50. local ragdollThreshold = GetConVar("rads_shock_ragdoll_threshold"):GetFloat()
  51.  
  52. for _, ply in ipairs(player.GetAll()) do
  53. if IsValid(ply) and ply:Alive() and not ply:GetNWBool("radsfa", false) then
  54. local shock = ply:GetNWFloat("RADS_Shock", 0)
  55.  
  56. if shock >= ragdollThreshold then
  57. -- Force ragdoll due to shock
  58. if not ply.lastShockRagdollTime or CurTime() - ply.lastShockRagdollTime > 2 then
  59. rads(ply, false) -- Not manual ragdoll
  60. ply.lastShockRagdollTime = CurTime()
  61. end
  62. end
  63. end
  64. end
  65. end)
  66.  
  67. -- Store shock responsiveness factor for ragdolls
  68. hook.Add("Think", "RADS_ShockRagdollSlowMovement", function()
  69. if not GetConVar("rads_shock_enable"):GetBool() then return end
  70.  
  71. for _, rag in ipairs(ents.FindByClass("prop_ragdoll")) do
  72. if IsValid(rag) and rag.isShockRagdoll then
  73. local owner = rag:GetNWEntity("owner")
  74. if IsValid(owner) and owner:IsPlayer() then
  75. local shock = owner:GetNWFloat("RADS_Shock", 0)
  76.  
  77. -- Calculate control responsiveness factor based on shock level
  78. -- Higher shock = slower/less responsive controls (not physics)
  79. local responsivenessFactor = math.Clamp(1 - (shock / 150), 0.2, 1) -- Max 80% control slowdown
  80.  
  81. -- Store the responsiveness factor on the ragdoll for use in control systems
  82. rag.shockResponsiveness = responsivenessFactor
  83.  
  84. -- Update shock ragdoll status
  85. local ragdollThreshold = GetConVar("rads_shock_ragdoll_threshold"):GetFloat()
  86. if shock < ragdollThreshold then
  87. rag.isShockRagdoll = false
  88. rag.shockLevel = nil
  89. rag.shockResponsiveness = 1.0 -- Reset to normal responsiveness
  90. end
  91. else
  92. -- Clean up if owner is invalid
  93. rag.isShockRagdoll = false
  94. rag.shockLevel = nil
  95. rag.shockResponsiveness = 1.0
  96. end
  97. end
  98. end
  99. end)
  100. end
  101. end)
  102.  
  103. -- Transfer vFire from player to ragdoll
  104. function RADS_TransferVFire(ply, rag)
  105. if not vFireInstalled or not IsValid(ply) or not IsValid(rag) then return end
  106.  
  107. -- Check if player is on fire
  108. if ply:IsOnFire() then
  109. -- Get fires on player
  110. local fires = vFireGetFires(ply)
  111. if fires and #fires > 0 then
  112. -- Extinguish player
  113. ply:Extinguish()
  114.  
  115. -- Create fires on ragdoll
  116. local fireCount = math.min(#fires, 8) -- Limit fire count
  117. CreateVFireEntFires(rag, fireCount)
  118.  
  119. -- Set fire owner for kill tracking
  120. timer.Simple(0.1, function()
  121. if IsValid(rag) then
  122. local ragFires = vFireGetFires(rag)
  123. if ragFires then
  124. for _, fire in pairs(ragFires) do
  125. if IsValid(fire) and fire.SetOwner then
  126. fire:SetOwner(ply.LastAttacker or ply)
  127. end
  128. end
  129. end
  130. end
  131. end)
  132. end
  133. end
  134. end
  135.  
  136. -- Transfer vFire from ragdoll back to player
  137. function RADS_TransferVFireToPlayer(rag, ply)
  138. if not vFireInstalled or not IsValid(rag) or not IsValid(ply) then return end
  139.  
  140. -- Check if ragdoll is on fire
  141. if rag:IsOnFire() then
  142. -- Get fires on ragdoll
  143. local fires = vFireGetFires(rag)
  144. if fires and #fires > 0 then
  145. -- Store fire owner for tracking
  146. local fireOwner = nil
  147. if fires[1] and IsValid(fires[1]) and fires[1].GetOwner then
  148. fireOwner = fires[1]:GetOwner()
  149. end
  150.  
  151. -- Extinguish ragdoll
  152. rag:Extinguish()
  153.  
  154. -- Ignite player
  155. local fireCount = math.min(#fires, 6)
  156. CreateVFireEntFires(ply, fireCount)
  157.  
  158. -- Restore fire owner
  159. if IsValid(fireOwner) then
  160. timer.Simple(0.1, function()
  161. if IsValid(ply) then
  162. local playerFires = vFireGetFires(ply)
  163. if playerFires then
  164. for _, fire in pairs(playerFires) do
  165. if IsValid(fire) and fire.SetOwner then
  166. fire:SetOwner(fireOwner)
  167. end
  168. end
  169. end
  170. end
  171. end)
  172. end
  173. end
  174. end
  175. end
  176.  
  177. -- Gore System: Head Explosion Function
  178. -- Old RADS_TriggerHeadExplosion function removed - using newer version with proper positioning
  179.  
  180. -- Gore System: Blood Explosion Effect
  181. function RADS_CreateBloodExplosion(pos, target)
  182. if not IsValid(target) then return end
  183.  
  184. -- Create multiple blood spurts in different directions
  185. for i = 1, 12 do
  186. local bloodDir = VectorRand():GetNormalized()
  187. bloodDir.z = math.abs(bloodDir.z) * 0.5 -- Bias upward
  188.  
  189. local effectdata = EffectData()
  190. effectdata:SetOrigin(pos + VectorRand() * 2)
  191. effectdata:SetNormal(bloodDir)
  192. effectdata:SetMagnitude(math.random(50, 100))
  193. effectdata:SetScale(math.random(1, 3))
  194. util.Effect("BloodImpact", effectdata)
  195. end
  196.  
  197. -- Create blood decals on nearby surfaces
  198. for i = 1, 8 do
  199. local traceDir = VectorRand():GetNormalized()
  200. local trace = util.TraceLine({
  201. start = pos,
  202. endpos = pos + traceDir * 200,
  203. filter = target
  204. })
  205.  
  206. if trace.Hit then
  207. util.Decal("Blood", trace.HitPos + trace.HitNormal, trace.HitPos - trace.HitNormal)
  208. end
  209. end
  210. end
  211.  
  212. -- Gore System: Continuous Blood Stream
  213. function RADS_StartBloodStream(target, pos)
  214. if not IsValid(target) then return end
  215.  
  216. local bloodDuration = GetConVar("rads_gore_blood_duration"):GetFloat()
  217. local timerName = "RADS_BloodStream_" .. target:EntIndex()
  218.  
  219. -- Remove existing timer if any
  220. if timer.Exists(timerName) then
  221. timer.Remove(timerName)
  222. end
  223.  
  224. -- Start blood stream timer
  225. timer.Create(timerName, 0.1, bloodDuration * 10, function()
  226. if not IsValid(target) or not target.hasGoreExplosion then
  227. timer.Remove(timerName)
  228. return
  229. end
  230.  
  231. -- Get current neck position
  232. local currentPos = target:GetBonePosition(target:LookupBone("ValveBiped.Bip01_Neck1") or 0)
  233. if currentPos == Vector(0,0,0) then
  234. currentPos = pos
  235. end
  236.  
  237. -- Create blood drip effect
  238. local effectdata = EffectData()
  239. effectdata:SetOrigin(currentPos)
  240. effectdata:SetNormal(Vector(0, 0, -1))
  241. effectdata:SetMagnitude(math.random(10, 30))
  242. effectdata:SetScale(1)
  243. util.Effect("BloodImpact", effectdata)
  244.  
  245. -- Occasionally create blood decals below
  246. if math.random(1, 3) == 1 then
  247. local trace = util.TraceLine({
  248. start = currentPos,
  249. endpos = currentPos + Vector(0, 0, -100),
  250. filter = target
  251. })
  252.  
  253. if trace.Hit then
  254. util.Decal("Blood", trace.HitPos + trace.HitNormal, trace.HitPos - trace.HitNormal)
  255. end
  256. end
  257. end)
  258. end
  259.  
  260. function RADS.PainSound(ply)
  261. if not IsValid(ply) then return end
  262. local model = ply:GetModel()
  263. if not model or type(model) ~= "string" then return end
  264.  
  265. local g = "man"
  266. if string.find(model, "alyx.mdl") or string.find(model, "mossman.mdl") or string.find(model, "mossman_arctic.mdl") or string.find(model, "p2_chell.mdl") or string.find(model, "female_") then g = "woman" end
  267. if string.find(model, "combine") or string.find(model, "police.mdl") then g = "combine" end
  268. if GetConVar("rads_painsounds"):GetBool() then
  269. local curTime = CurTime()
  270. local cooldown = 0.3
  271. if g == "man" then
  272. if not lastPainSoundTime[ply] or curTime - lastPainSoundTime[ply] >= cooldown then
  273. local rndmsnd = math.random(1, #RADS.MalePain)
  274. local randomSound = RADS.MalePain[rndmsnd]
  275. ply:EmitSound(randomSound)
  276. lastPainSoundTime[ply] = curTime
  277. end
  278. elseif g == "woman" then
  279. if not lastPainSoundTime[ply] or curTime - lastPainSoundTime[ply] >= cooldown then
  280. local rndmsnd = math.random(1, #RADS.FemalePain)
  281. local randomSound = RADS.FemalePain[rndmsnd]
  282. ply:EmitSound(randomSound)
  283. lastPainSoundTime[ply] = curTime
  284. end
  285. elseif g == "combine" then
  286. if not lastPainSoundTime[ply] or curTime - lastPainSoundTime[ply] >= cooldown then
  287. local rndmsnd = math.random(1, #RADS.CombinePain)
  288. local randomSound = RADS.CombinePain[rndmsnd]
  289. ply:EmitSound(randomSound)
  290. lastPainSoundTime[ply] = curTime
  291. end
  292. end
  293. end
  294. end
  295.  
  296. function RADS.FreeFall(ply)
  297. if not IsValid(ply) then return end
  298. local model = ply:GetModel()
  299. if not model or type(model) ~= "string" then return end
  300.  
  301. local g = "man"
  302. if string.find(model, "alyx.mdl") or string.find(model, "mossman.mdl") or string.find(model, "female_") then g = "woman" end
  303. if string.find(model, "combine") or string.find(model, "police.mdl") then g = "combine" end
  304. if GetConVar("rads_painsounds"):GetBool() then
  305. local curTime = CurTime()
  306. local cooldown = 0.3
  307. if g == "man" then
  308. if not lastFreeFallSoundTime[ply] or curTime - lastFreeFallSoundTime[ply] >= cooldown then
  309. local rndmsnd = math.random(1, #RADS.MaleFall)
  310. local randomSound = RADS.MaleFall[rndmsnd]
  311. ply:EmitSound(randomSound)
  312. lastFreeFallSoundTime[ply] = curTime
  313. end
  314. elseif g == "woman" then
  315. if not lastFreeFallSoundTime[ply] or curTime - lastFreeFallSoundTime[ply] >= cooldown then
  316. local rndmsnd = math.random(1, #RADS.FemaleFall)
  317. local randomSound = RADS.FemaleFall[rndmsnd]
  318. ply:EmitSound(randomSound)
  319. lastFreeFallSoundTime[ply] = curTime
  320. end
  321. elseif g == "combine" then
  322. if not lastFreeFallSoundTime[ply] or curTime - lastFreeFallSoundTime[ply] >= cooldown then
  323. local rndmsnd = math.random(1, #RADS.CombineFall)
  324. local randomSound = RADS.CombineFall[rndmsnd]
  325. ply:EmitSound(randomSound)
  326. lastFreeFallSoundTime[ply] = curTime
  327. end
  328. end
  329. end
  330. end
  331.  
  332. _P = FindMetaTable("Player")
  333. _ENT = FindMetaTable("Entity")
  334. function _P:IsRag()
  335. return self:GetNWBool("radsfa")
  336. end
  337.  
  338. function _P:GetRads()
  339. return self:GetNWEntity("player_ragdoll")
  340. end
  341.  
  342. function RADS.IsTTT()
  343. if engine.ActiveGamemode() == "terrortown" then return true end
  344. return false
  345. end
  346.  
  347. function _ENT:GetOwnerrr()
  348. if self:GetNWEntity("owner") ~= nil then return self:GetNWEntity("owner") end
  349. return nil
  350. end
  351.  
  352. function _ENT:IsRads()
  353. if self:GetNWEntity("owner") ~= nil then return true end
  354. return false
  355. end
  356.  
  357. function RADS.IsJmodAct()
  358. return type(JMod) == "table"
  359. end
  360.  
  361. hook.Add("PhysgunDrop", "RADS.Drop", function(ply, ent)
  362. if ply:IsSuperAdmin() and ent:IsRagdoll() and ent:IsRads() then ent.physgunned = false end
  363. if ply:IsSuperAdmin() and ent:IsPlayer() then ent.physgunned = false end
  364. end)
  365.  
  366. hook.Add("PhysgunPickup", "RADS.Pickup", function(ply, ent)
  367. if ply:IsSuperAdmin() and ent:IsPlayer() and not ent.fake then
  368. rads(ent)
  369. ent.physgunned = true
  370. return false
  371. end
  372.  
  373. if ent:IsRagdoll() and ent:IsRads() then ent.physgunned = true end
  374. end)
  375.  
  376. hook.Add("CanPlayerSuicide", "RADS.SuicideAllow", function(ply)
  377. if not GetConVar("rads_enablekill"):GetBool() and ply.Otrub then
  378. ply:ChatPrint("No easy way out.")
  379. return false
  380. end
  381. return true
  382. end)
  383.  
  384. local CurTime = CurTime
  385. local time
  386. local player_GetAll = player.GetAll
  387. local tbl
  388. hook.Add("PlayerSpawn", "RADS.SpawnReset", function(ply)
  389. ply:SetParent(nil)
  390. while not ply:IsInWorld() and not timer.Exists("respawntimer" .. ply:EntIndex()) do
  391. ply:Spawn()
  392. end
  393.  
  394. if timer.Exists("respawntimer" .. ply:EntIndex()) then return end
  395. if timer.Exists("radstimer" .. ply:EntIndex()) then timer.Remove("radstimer" .. ply:EntIndex()) end
  396.  
  397. -- Always remove calcview on spawn to ensure clean state
  398. net.Start('REMOVECALC')
  399. net.Send(ply)
  400.  
  401. -- Clear death first-person flag
  402. ply:SetNWBool('rads_dead_firstperson', false)
  403. local exr = ply:GetNWEntity("player_ragdoll")
  404. if IsValid(exr) then
  405. ply:SetNWBool('radsfa', false)
  406. exr:RemoveEFlags(EFL_KEEP_ON_RECREATE_ENTITIES)
  407. ply:SetNWEntity('deadbody', exr)
  408. exr:SetNWEntity('deadbodyowner', ply)
  409. ply:SetNWEntity("player_ragdoll", nil)
  410. exr:SetNWEntity("RagdollController", nil)
  411. exr:SetNWEntity("owner", nil)
  412. end
  413.  
  414. ply.fake = false
  415. ply.physgunned = false
  416. ply.brokenspine = false
  417. ply.gettingUp = false
  418. ply.upValue = 0
  419. ply.lastGetUpAttempt = 0
  420. ply.lastGetUpTime = nil -- Reset the get up timer on spawn
  421.  
  422. -- NEW: Reset lung oxygen system states on spawn
  423. ply.lungOxygenActive = false
  424. ply.lungStaminaDrain = false
  425. if IsValid(ply.wheezeEntity) then
  426. ply.wheezeEntity:Remove()
  427. ply.wheezeEntity = nil
  428. end
  429.  
  430.  
  431. end)
  432.  
  433. util.AddNetworkString("ragscale")
  434. util.AddNetworkString("ragplayercolor")
  435. util.AddNetworkString("showiconleft")
  436. util.AddNetworkString("hideiconleft")
  437. util.AddNetworkString("showiconright")
  438. util.AddNetworkString("hideiconright")
  439.  
  440. -- Improved color transfer function
  441. function _ENT:BetterSetPlayerColor(col)
  442. if not (col or self) then return end
  443. timer.Simple(
  444. .1,
  445. function()
  446. if not IsValid(self) then return end
  447. net.Start("ragplayercolor")
  448. net.WriteEntity(self)
  449. net.WriteVector(col)
  450. net.Broadcast()
  451. end
  452. )
  453. end
  454. hook.Add("Think", "RADS.PlayerThink", function(ply)
  455. tbl = player_GetAll()
  456. time = CurTime()
  457. for i = 1, #tbl do
  458. hook.Run("Player Think", tbl[i], time)
  459. end
  460. end)
  461.  
  462.  
  463.  
  464. -- NEW: Hook to ensure wheeze sounds follow ragdolls
  465. hook.Add("Think", "RADS_UpdateWheezePosition", function()
  466. for _, ply in ipairs(player.GetAll()) do
  467. if IsValid(ply) and ply:IsPlayer() and IsValid(ply.wheezeEntity) then
  468. local targetPos
  469.  
  470. -- If player is ragdolled, follow the ragdoll
  471. if ply:GetNWBool("radsfa") then
  472. local ragdoll = ply:GetNWEntity("player_ragdoll")
  473. if IsValid(ragdoll) then
  474. -- Try to get head position from ragdoll
  475. local headBone = ragdoll:LookupBone("ValveBiped.Bip01_Head1")
  476. if headBone then
  477. targetPos = ragdoll:GetBonePosition(headBone)
  478. else
  479. targetPos = ragdoll:GetPos() + Vector(0, 0, 64)
  480. end
  481. else
  482. targetPos = ply:GetPos() + Vector(0, 0, 64)
  483. end
  484. else
  485. -- Follow the player
  486. local headBone = ply:LookupBone("ValveBiped.Bip01_Head1")
  487. if headBone then
  488. targetPos = ply:GetBonePosition(headBone)
  489. else
  490. targetPos = ply:GetPos() + Vector(0, 0, 64)
  491. end
  492. end
  493.  
  494. -- Update wheeze entity position
  495. if targetPos then
  496. ply.wheezeEntity:SetPos(targetPos)
  497. end
  498. end
  499. end
  500. end)
  501.  
  502. -- NEW: Clean up wheeze entities when player disconnects
  503. hook.Add("PlayerDisconnected", "RADS_CleanupWheezeOnDisconnect", function(ply)
  504. if IsValid(ply) and IsValid(ply.wheezeEntity) then
  505. ply.wheezeEntity:Remove()
  506. ply.wheezeEntity = nil
  507. end
  508. end)
  509.  
  510. util.AddNetworkString('REMOVECALC')
  511. util.AddNetworkString("ADDCALC")
  512. fallChanceTable = {
  513. [HITGROUP_HEAD] = 0.45, -- Reduced from 0.75 to 0.45 (45% chance)
  514. [HITGROUP_CHEST] = 0.35, -- Reduced from 0.65 to 0.35 (35% chance)
  515. [HITGROUP_STOMACH] = 0.40, -- Reduced from 0.50 to 0.40 (40% chance)
  516. [HITGROUP_LEFTARM] = 0.15, -- Reduced from 0.25 to 0.15 (15% chance)
  517. [HITGROUP_RIGHTARM] = 0.15, -- Reduced from 0.25 to 0.15 (15% chance)
  518. [HITGROUP_LEFTLEG] = 0.25, -- Reduced from 0.35 to 0.25 (25% chance)
  519. [HITGROUP_RIGHTLEG] = 0.25, -- Reduced from 0.35 to 0.25 (25% chance)
  520. [HITGROUP_GENERIC] = 0.55 -- NEW: Pelvis hitgroup - high fall chance due to balance importance
  521. }
  522.  
  523. -- Improved shouldFall function that works with damage and body part
  524. function shouldFall(bodyPart, damage, damageType, ply)
  525. local baseChance = fallChanceTable[bodyPart] or 0.20 -- Default 20% for unknown body parts
  526.  
  527. -- NEW: Special pelvis handling - check for existing pelvis damage
  528. if bodyPart == HITGROUP_GENERIC and IsValid(ply) then
  529. -- If pelvis is broken, extremely high fall chance
  530. if ply.brokenpelvis then
  531. baseChance = 0.95 -- 95% chance if pelvis is already broken
  532. elseif ply.Organs and ply.Organs['pelvis'] then
  533. -- Increase fall chance based on pelvis damage level
  534. local pelvisHealth = ply.Organs['pelvis']
  535. local pelvisDamagePercent = 1 - (pelvisHealth / 25) -- 25 is max pelvis health
  536. baseChance = baseChance + (pelvisDamagePercent * 0.3) -- Up to 30% additional fall chance
  537. end
  538. end
  539.  
  540. -- General pelvis damage modifier for all body parts
  541. local pelvisModifier = 1.0
  542. if IsValid(ply) and ply.brokenpelvis then
  543. pelvisModifier = 1.4 -- 40% higher fall chance for any damage when pelvis is broken
  544. elseif IsValid(ply) and ply.Organs and ply.Organs['pelvis'] then
  545. local pelvisHealth = ply.Organs['pelvis']
  546. if pelvisHealth < 15 then -- Severely damaged pelvis
  547. pelvisModifier = 1.2 -- 20% higher fall chance
  548. elseif pelvisHealth < 20 then -- Moderately damaged pelvis
  549. pelvisModifier = 1.1 -- 10% higher fall chance
  550. end
  551. end
  552.  
  553. -- Damage scaling: higher damage increases chance
  554. local damageMultiplier = 1.0
  555. if damage then
  556. if damage >= 75 then
  557. damageMultiplier = 1.8 -- Very high damage
  558. elseif damage >= 50 then
  559. damageMultiplier = 1.5 -- High damage
  560. elseif damage >= 35 then
  561. damageMultiplier = 1.2 -- Medium damage
  562. elseif damage >= 25 then
  563. damageMultiplier = 1.0 -- Normal damage
  564. else
  565. damageMultiplier = 0.7 -- Low damage
  566. end
  567. end
  568.  
  569. -- Damage type modifiers
  570. local typeMultiplier = 1.0
  571. if damageType then
  572. if bit.band(damageType, DMG_BLAST) ~= 0 then
  573. typeMultiplier = 1.4 -- Explosions more likely to knock down
  574. elseif bit.band(damageType, DMG_BUCKSHOT) ~= 0 then
  575. typeMultiplier = 1.2 -- Shotguns more likely
  576. elseif bit.band(damageType, DMG_CLUB) ~= 0 then
  577. typeMultiplier = 0.8 -- Club damage less likely
  578. end
  579. end
  580.  
  581. local finalChance = baseChance * damageMultiplier * typeMultiplier * pelvisModifier
  582. finalChance = math.min(finalChance, 0.95) -- Cap at 95%
  583.  
  584. return math.random() < finalChance
  585. end
  586.  
  587. concommand.Add("rads_viewrag", function(ply, cmd, args)
  588. if not ply:IsSuperAdmin() then return end
  589. local tr = ply:GetEyeTrace()
  590. if not IsValid(tr.Entity) or not tr.Entity:IsPlayer() then return end
  591. local tarpp = tr.Entity
  592. rads(tarpp)
  593. end)
  594.  
  595. -- Console command to remove handcuffs from ragdolls
  596. concommand.Add("rads_remove_handcuffs", function(ply, cmd, args)
  597. if not IsValid(ply) then return end
  598.  
  599. local tr = util.TraceLine({
  600. start = ply:GetShootPos(),
  601. endpos = ply:GetShootPos() + ply:GetAimVector() * 80,
  602. filter = ply
  603. })
  604.  
  605. if IsValid(tr.Entity) and tr.Entity:IsRagdoll() then
  606. local ragdollOwner = tr.Entity:GetNWEntity("owner")
  607.  
  608. -- Check if it's a handcuffed player ragdoll
  609. if IsValid(ragdollOwner) and ragdollOwner:IsPlayer() and tr.Entity:GetNWBool("RADS_Handcuffed", false) then
  610. -- Remove handcuff status
  611. tr.Entity:SetNWBool("RADS_Handcuffed", false)
  612. ragdollOwner:SetNWBool("RADS_Handcuffed", false)
  613.  
  614. -- Remove handcuff model
  615. if IsValid(tr.Entity.HandcuffModel) then
  616. tr.Entity.HandcuffModel:Remove()
  617. tr.Entity.HandcuffModel = nil
  618. end
  619.  
  620. -- Play sound
  621. tr.Entity:EmitSound("physics/metal/metal_solid_impact_soft1.wav", 60, 100)
  622.  
  623. -- Notify players
  624. ply:PrintMessage(HUD_PRINTCENTER, "Removed handcuffs from " .. ragdollOwner:Nick() .. ".")
  625. ragdollOwner:PrintMessage(HUD_PRINTCENTER, ply:Nick() .. " removed your handcuffs!")
  626.  
  627. -- Create handcuffs item on ground
  628. local handcuffsEnt = ents.Create("ent_jack_hmcd_handcuffs")
  629. handcuffsEnt:SetPos(tr.Entity:GetPos() + Vector(0, 0, 10))
  630. handcuffsEnt:SetAngles(Angle(0, math.random(0, 360), 0))
  631. handcuffsEnt:Spawn()
  632. handcuffsEnt:Activate()
  633.  
  634. -- Add some velocity to the dropped handcuffs
  635. local phys = handcuffsEnt:GetPhysicsObject()
  636. if IsValid(phys) then
  637. phys:SetVelocity(Vector(math.random(-50, 50), math.random(-50, 50), math.random(20, 50)))
  638. end
  639. else
  640. ply:PrintMessage(HUD_PRINTCENTER, "No handcuffed ragdoll in range.")
  641. end
  642. else
  643. ply:PrintMessage(HUD_PRINTCENTER, "No ragdoll in range.")
  644. end
  645. end)
  646.  
  647. -- Removed damage interruption hook as it was causing issues with getting up
  648. -- hook.Add("EntityTakeDamage", "RADS.RagdollDamageInterrupt", function(target, dmginfo)
  649. -- local owner = nil
  650. -- if IsValid(target) and target:IsRagdoll() then
  651. -- owner = target:GetNWEntity("owner") or target:GetNWEntity('deadbodyowner')
  652. -- elseif IsValid(target) and target:IsPlayer() then
  653. -- owner = target
  654. -- end
  655. --
  656. -- if IsValid(owner) and owner:IsPlayer() and owner:GetNWBool("radsfa") then
  657. -- owner.takingDamage = true
  658. -- end
  659. -- end)
  660. end
  661.  
  662. local CustomWeight = {
  663. ["models/player/police_fem.mdl"] = 50,
  664. ["models/player/police.mdl"] = 60,
  665. ["models/player/combine_soldier.mdl"] = 70,
  666. ["models/player/combine_super_soldier.mdl"] = 80,
  667. ["models/player/combine_soldier_prisonguard.mdl"] = 70,
  668. ['models/player/charple.mdl'] = 5
  669. }
  670.  
  671. if SERVER then
  672. util.AddNetworkString("SendSavedPlayerWeaponsToActivator")
  673. util.AddNetworkString("SavedPlayerWeapons")
  674. -- ConVars removed - values are now hardcoded
  675. -- rads_waketime = 2.5, rads_upspeed = 400, rads_maxupspeed = 400
  676. CreateConVar("rads_namedisplay_server", "1", {FCVAR_ARCHIVE, "Enable server-side name display support"})
  677. function SendSavedPlayerWeapons(ply)
  678. net.Start("SavedPlayerWeapons")
  679. net.WriteTable(ply.Info.Weapons3)
  680. net.Send(ply)
  681. end
  682.  
  683. savedPlayerState = {}
  684. function RADS_EzArmorSaveInfo(ply)
  685. local steamID = ply:SteamID()
  686. savedPlayerState[steamID] = {
  687. EZarmor = {},
  688. EZhealth = ply.EZhealth or nil,
  689. EZirradiated = ply.EZirradiated or nil,
  690. o2 = ply.o2 or nil,
  691. EZbleeding = ply.EZbleeding or nil,
  692. EZvirus = ply.EZvirus or nil,
  693. -- NEW: Save lung damage oxygen system states
  694. lungOxygenActive = ply.lungOxygenActive or false,
  695. lungStaminaDrain = ply.lungStaminaDrain or false,
  696. wheezeEntity = ply.wheezeEntity or nil
  697. }
  698.  
  699. if ply.EZarmor then
  700. savedPlayerState[steamID].EZarmor = {
  701. items = ply.EZarmor.items or nil,
  702. speedFrac = ply.EZarmor.speedFrac or nil,
  703. effects = ply.EZarmor.effects or nil,
  704. mskmat = ply.EZarmor.mskmat or nil,
  705. sndlop = ply.EZarmor.sndlop or nil,
  706. suited = ply.EZarmor.suited or nil,
  707. bodygroups = ply.EZarmor.bodygroups or nil,
  708. totalWeight = ply.EZarmor.totalWeight or nil
  709. }
  710. end
  711. end
  712.  
  713. function RADS_RestoreEzArmor(ply) -- SendSavedPlayerWeapons(ply)
  714. local steamID = ply:SteamID()
  715. if savedPlayerState[steamID] then
  716. local state = savedPlayerState[steamID]
  717. if RADS.IsJmodAct() and ply.EZarmor then
  718. ply.EZarmor = {
  719. items = state.EZarmor.items,
  720. speedFrac = state.EZarmor.speedFrac,
  721. effects = state.EZarmor.effects,
  722. mskmat = state.EZarmor.mskmat,
  723. sndlop = state.EZarmor.sndlop,
  724. suited = state.EZarmor.suited,
  725. bodygroups = state.EZarmor.bodygroups,
  726. totalWeight = state.EZarmor.totalWeight
  727. }
  728.  
  729. ply.EZhealth = state.EZhealth
  730. ply.EZirradiated = state.EZirradiated
  731. ply.o2 = state.o2
  732. ply.EZbleeding = state.EZbleeding
  733. ply.EZvirus = state.EZvirus
  734.  
  735. -- NEW: Restore lung damage oxygen system states
  736. ply.lungOxygenActive = state.lungOxygenActive
  737. ply.lungStaminaDrain = state.lungStaminaDrain
  738.  
  739. -- Clean up any existing wheeze entity before restoring
  740. if IsValid(ply.wheezeEntity) then
  741. ply.wheezeEntity:Remove()
  742. ply.wheezeEntity = nil
  743. end
  744. end
  745.  
  746. savedPlayerState[steamID] = nil
  747. end
  748. end
  749.  
  750. function RADS_SavePlyInfo(ply)
  751. ply.Info = {}
  752. local info = ply.Info
  753. info.HasSuit = ply:IsSuitEquipped()
  754. info.SuitPower = ply:GetSuitPower()
  755. info.Ammo = ply:GetAmmo()
  756. info.ActiveWeapon = IsValid(ply:GetActiveWeapon()) and ply:GetActiveWeapon():GetClass() or nil
  757. info.runspeed = ply:GetRunSpeed()
  758. info.walkspeed = ply:GetWalkSpeed()
  759. info.ActiveWeapon2 = ply:GetActiveWeapon()
  760. GetFakeWeapon(ply)
  761. info.Angles = ply:GetAngles()
  762. info.Weapons = {}
  763. for i, wep in pairs(ply:GetWeapons()) do
  764. info.Weapons[wep:GetClass()] = {
  765. Clip1 = wep:Clip1(),
  766. Clip2 = wep:Clip2(),
  767. AmmoType = wep:GetPrimaryAmmoType()
  768. }
  769.  
  770. -- RADS Grappling Hook Compatibility: Save grappling hook state
  771. if wep:GetClass() == "wep_jack_hmcd_grapl" and wep.SaveGrapplingState then
  772. wep:SaveGrapplingState()
  773. end
  774.  
  775. -- IED Compatibility: Save IED rigged state for proper restoration
  776. if wep:GetClass() == "wep_jack_hmcd_ied" and wep.GetRigged then
  777. info.Weapons[wep:GetClass()].IEDRigged = wep:GetRigged()
  778. end
  779. end
  780.  
  781. info.Weapons2 = {}
  782. for i, wep in ipairs(ply:GetWeapons()) do
  783. info.Weapons2[i - 1] = wep:GetClass()
  784. end
  785.  
  786. info.Weapons3 = {}
  787. for i, wep in ipairs(ply:GetWeapons()) do
  788. end
  789.  
  790. SendSavedPlayerWeapons(ply) -- info.Weapons3[wep:GetClass()] = wep:GetPrintName()
  791. info.eyeviewcvar = ply:GetInfoNum('eyeview_enabled', 1)
  792. info.AllAmmo = {}
  793. local i
  794. for ammo, amt in pairs(ply:GetAmmo()) do
  795. i = i or 0
  796. i = i + 1
  797. info.AllAmmo[ammo] = {i, amt}
  798. end
  799. return info
  800. end
  801.  
  802. function RADS_ReturnPlyInfo(ply)
  803. ClearFakeWeapon(ply)
  804. ply:SetSuppressPickupNotices(true)
  805. local info = ply.Info
  806. if not info then return end
  807.  
  808. -- CRITICAL FIX: Enhanced weapon duplication prevention for guns1 compatibility
  809. if ply.gettingUpFromRagdoll then
  810. -- Store the flag on ALL existing weapons before stripping
  811. for _, weapon in pairs(ply:GetWeapons()) do
  812. if IsValid(weapon) then
  813. weapon.ownerGettingUpFromRagdoll = true
  814. -- Special handling for guns1 weapons
  815. if string.find(weapon:GetClass(), "wep_jack_") or string.find(weapon:GetClass(), "wep_mann_") or string.find(weapon:GetClass(), "wep_viz_") then
  816. weapon.preventDuplication = true
  817. end
  818. end
  819. end
  820.  
  821. -- Clean up any existing dropped weapon entities from guns1 that might cause duplication
  822. -- Only remove dropped weapon boxes, not planted explosives
  823. for _, ent in pairs(ents.FindByClass("prop_physics")) do
  824. if IsValid(ent) and ent.IEDAttacker == ply then
  825. -- Check if this is a dropped weapon box (not a planted explosive)
  826. -- Planted explosives should have ExplodeIED function, dropped weapons don't
  827. if not ent.ExplodeIED then
  828. ent:Remove()
  829. end
  830. end
  831. end
  832. end
  833.  
  834. ply:StripWeapons()
  835. ply:StripAmmo()
  836. ply.slots = {}
  837.  
  838. -- Enhanced weapon restoration with duplication prevention
  839. for name, wepinfo in pairs(info.Weapons or {}) do
  840. local weapon = ply:Give(name, true)
  841. if IsValid(weapon) then
  842. -- Set gettingUpFromRagdoll flag on newly given weapons
  843. if ply.gettingUpFromRagdoll then
  844. weapon.ownerGettingUpFromRagdoll = true
  845. weapon.preventDuplication = true
  846. end
  847.  
  848. -- Restore clip data
  849. if wepinfo.Clip1 ~= nil and wepinfo.Clip2 ~= nil then
  850. weapon:SetClip1(wepinfo.Clip1)
  851. weapon:SetClip2(wepinfo.Clip2)
  852. end
  853.  
  854. -- RADS Grappling Hook Compatibility: Restore grappling hook state
  855. if weapon:GetClass() == "wep_jack_hmcd_grapl" and weapon.RestoreGrapplingState then
  856. timer.Simple(0.1, function()
  857. if IsValid(weapon) and IsValid(ply) then
  858. weapon:RestoreGrapplingState()
  859. end
  860. end)
  861. end
  862.  
  863. -- IED Compatibility: Restore IED state if needed
  864. if weapon:GetClass() == "wep_jack_hmcd_ied" and wepinfo.IEDRigged then
  865. timer.Simple(0.05, function()
  866. if IsValid(weapon) and IsValid(ply) then
  867. weapon:SetRigged(wepinfo.IEDRigged)
  868.  
  869. -- Reconnect to planted explosive if it exists
  870. for _, ent in pairs(ents.FindByClass("prop_physics")) do
  871. if IsValid(ent) and ent.IEDAttacker == ply and ent.ExplodeIED then
  872. weapon.Explosive = ent
  873. break
  874. end
  875. end
  876. end
  877. end)
  878. end
  879. end
  880. end
  881.  
  882. for ammo, amt in pairs(info.Ammo or {}) do
  883. ply:GiveAmmo(amt, ammo)
  884. end
  885.  
  886. if info.ActiveWeapon then ply:SelectWeapon(info.ActiveWeapon) end
  887. if info.HasSuit then
  888. ply:EquipSuit()
  889. ply:SetSuitPower(info.SuitPower or 0)
  890. else
  891. ply:RemoveSuit()
  892. end
  893.  
  894. ply:SetRunSpeed(info.runspeed)
  895. ply:SetWalkSpeed(info.walkspeed)
  896. ply:SetHealth(info.Hp or 0)
  897. ply:SetArmor(info.Armor or 0)
  898. ply:SetEyeAngles(info.Angles)
  899. info.Weapons3 = nil
  900.  
  901. -- CRITICAL: Clear gettingUpFromRagdoll flag after restoration to prevent lingering effects
  902. if ply.gettingUpFromRagdoll then
  903. timer.Simple(0.2, function()
  904. if IsValid(ply) then
  905. ply.gettingUpFromRagdoll = nil
  906. -- Clear flags from all weapons as well
  907. for _, weapon in pairs(ply:GetWeapons()) do
  908. if IsValid(weapon) then
  909. weapon.ownerGettingUpFromRagdoll = nil
  910. weapon.preventDuplication = nil
  911. end
  912. end
  913. end
  914. end)
  915. end
  916. end
  917.  
  918. function GetFakeWeapon(ply)
  919. ply.curweapon = ply.Info.ActiveWeapon
  920. end
  921.  
  922. function ClearFakeWeapon(ply)
  923. if ply.FakeShooting then
  924. if _G.DespawnWeapon then
  925. _G.DespawnWeapon(ply)
  926. else
  927. if GetConVar("developer"):GetInt() > 0 then
  928. print("[RADS] ERROR: DespawnWeapon function not available globally at line 470!")
  929. end
  930. end
  931. end
  932. end
  933.  
  934. function RADS_RagBones(ent) -- util.AddNetworkString('RetrieveWeaponFromRagdoll') -- net.Receive("RetrieveWeaponFromRagdoll", function(len, ply) -- weaponClass = net.ReadTable() -- local tr = ply:GetEyeTrace() -- if IsValid(tr.Entity) and tr.Entity:GetClass() == "prop_ragdoll" then -- local owner = tr.Entity:GetNWEntity("owner") -- local deadbodyowner = tr.Entity:GetNWEntity('deadbodyowner') -- if IsValid(owner) and owner:IsPlayer() or IsValid(deadbodyowner) and deadbodyowner:IsPlayer() then -- if owner.Info and owner.Info.Weapons3 then -- owner.Info.Weapons3[weaponClass] = nil -- ply:Give(weaponClass) -- else -- ply:Give(weaponClass) -- end -- end -- end -- end)
  935. local rag = ent:GetNWEntity("player_ragdoll")
  936. local ragdollBones = rag:GetPhysicsObjectCount()
  937. local vel = ent:GetVelocity() / 1
  938. for i = 0, rag:GetPhysicsObjectCount() - 1 do
  939. local physobj = rag:GetPhysicsObjectNum(i)
  940. local ragbonename = rag:GetBoneName(rag:TranslatePhysBoneToBone(i))
  941. local bone = ent:LookupBone(ragbonename)
  942. if bone then
  943. local bonemat = ent:GetBoneMatrix(bone)
  944. if bonemat then
  945. local bonepos = bonemat:GetTranslation()
  946. local boneang = bonemat:GetAngles()
  947. physobj:SetPos(bonepos, true)
  948. physobj:SetAngles(boneang)
  949. if ent:Alive() then vel = vel end
  950. if not ent:Alive() then vel = vel / 2 end
  951. physobj:AddVelocity(vel)
  952. end
  953. end
  954. end
  955. end
  956.  
  957. -- RADS_ValidPos function removed due to being buggy
  958. -- function RADS_ValidPos(originalPos, ply)
  959. -- This function is removed due to being buggy
  960. -- end
  961.  
  962. -- Function to remove all armors from a ragdoll
  963. local function RemoveRag(rag)
  964. if not IsValid(rag) then return end
  965. if rag.armors then
  966. for id, ent in pairs(rag.armors) do
  967. if IsValid(ent) then
  968. ent.override = true
  969. ent:Remove()
  970. end
  971. end
  972. end
  973.  
  974. -- Clean up gore system when ragdoll is removed
  975. if rag.hasGoreExplosion then
  976. -- Remove gore stump
  977. if IsValid(rag.goreStump) then
  978. rag.goreStump:Remove()
  979. rag.goreStump = nil
  980. end
  981.  
  982. -- Stop blood stream timer
  983. local timerName = "RADS_BloodStream_" .. rag:EntIndex()
  984. if timer.Exists(timerName) then
  985. timer.Remove(timerName)
  986. end
  987.  
  988. rag.hasGoreExplosion = nil
  989. end
  990. end
  991.  
  992. hook.Add("RADS_Ready", "RADS.CustomApi", function(rag) if GetConVar("developer"):GetInt() > 0 then print("") end end)
  993.  
  994. -- RADS Grappling Hook Compatibility Hook
  995. hook.Add("RADS_Ready", "RADS.GrapplingHookCompat", function(rag)
  996. local ply = rag:GetNWEntity("owner")
  997. if not IsValid(ply) then return end
  998.  
  999. -- Handle grappling hook physics interactions with ragdoll
  1000. if ply.GrapplingHookData and ply.GrapplingHookData.pos then
  1001. timer.Simple(0.1, function()
  1002. if IsValid(rag) and IsValid(ply) then
  1003. -- Find the grappling hook entity
  1004. local hookEnt = nil
  1005. for _, ent in pairs(ents.FindByClass("ent_jack_hmcd_grapl")) do
  1006. if ent.Owner == ply then
  1007. hookEnt = ent
  1008. break
  1009. end
  1010. end
  1011.  
  1012. if IsValid(hookEnt) then
  1013. -- Get the pelvis bone and physics object for proper attachment
  1014. local pelvisBone = rag:LookupBone("ValveBiped.Bip01_Pelvis")
  1015. local pelvisPhysBone = rag:TranslateBoneToPhysBone(pelvisBone)
  1016. local pelvisPhys = rag:GetPhysicsObjectNum(pelvisPhysBone)
  1017.  
  1018. if IsValid(pelvisPhys) then
  1019. -- Create a rope constraint between hook and ragdoll pelvis (invisible)
  1020. local ropeConstraint = constraint.Rope(
  1021. hookEnt, rag,
  1022. 0, pelvisPhysBone,
  1023. Vector(0,0,0), Vector(0,0,0),
  1024. ply.GrapplingHookData.ropeLength or 1000,
  1025. 0, 0, 0, "cable/rope", false
  1026. )
  1027.  
  1028. -- Store constraint for cleanup
  1029. rag.GrapplingRopeConstraint = ropeConstraint
  1030.  
  1031. -- Apply grappling forces to pelvis physics object
  1032. local hookPos = hookEnt:GetPos()
  1033. local pelvisPos = pelvisPhys:GetPos()
  1034. local dist = hookPos:Distance(pelvisPos)
  1035. local desiredDist = ply.GrapplingHookData.ropeLength or 1000
  1036.  
  1037. if dist > desiredDist then
  1038. local dir = (hookPos - pelvisPos):GetNormalized()
  1039. local force = dir * math.min((dist - desiredDist) * 100, 3000)
  1040. pelvisPhys:ApplyForceCenter(force)
  1041.  
  1042. -- Apply counter-force to hook for realistic physics
  1043. hookEnt:GetPhysicsObject():ApplyForceCenter(-dir * force * 0.3)
  1044. end
  1045.  
  1046. -- Create a think timer for continuous grappling physics
  1047. timer.Create("GrapplingPhysics_" .. rag:EntIndex(), 0.1, 0, function()
  1048. if not IsValid(rag) or not IsValid(hookEnt) or not IsValid(pelvisPhys) then
  1049. timer.Remove("GrapplingPhysics_" .. rag:EntIndex())
  1050. return
  1051. end
  1052.  
  1053. local hookPos = hookEnt:GetPos()
  1054. local pelvisPos = pelvisPhys:GetPos()
  1055. local dist = hookPos:Distance(pelvisPos)
  1056. local desiredDist = ply.GrapplingHookData.ropeLength or 1000
  1057.  
  1058. if dist > desiredDist then
  1059. local dir = (hookPos - pelvisPos):GetNormalized()
  1060. local force = dir * math.min((dist - desiredDist) * 80, 2500)
  1061. pelvisPhys:ApplyForceCenter(force)
  1062.  
  1063. -- Apply counter-force to hook
  1064. hookEnt:GetPhysicsObject():ApplyForceCenter(-dir * force * 0.2)
  1065. end
  1066. end)
  1067. end
  1068. end
  1069. end
  1070. end)
  1071. end
  1072. end)
  1073.  
  1074. -- RADS Grappling Hook Cleanup Hooks
  1075. hook.Add("PlayerDisconnected", "RADS.GrapplingHookCleanup", function(ply)
  1076. if not IsValid(ply) then return end
  1077.  
  1078. -- Clean up grappling physics timer
  1079. local rag = ply:GetNWEntity("player_ragdoll")
  1080. if IsValid(rag) then
  1081. timer.Remove("GrapplingPhysics_" .. rag:EntIndex())
  1082.  
  1083. -- Clean up rope constraint
  1084. if rag.GrapplingRopeConstraint and IsValid(rag.GrapplingRopeConstraint) then
  1085. rag.GrapplingRopeConstraint:Remove()
  1086. rag.GrapplingRopeConstraint = nil
  1087. end
  1088. end
  1089.  
  1090. -- Clean up any grappling hooks owned by disconnecting player
  1091. for _, ent in pairs(ents.FindByClass("ent_jack_hmcd_grapl")) do
  1092. if ent.Owner == ply then
  1093. ent:Remove()
  1094. end
  1095. end
  1096.  
  1097. -- Clean up stored grappling hook data
  1098. ply.GrapplingHookState = nil
  1099. ply.GrapplingHookData = nil
  1100. end)
  1101.  
  1102. hook.Add("PlayerDeath", "RADS.GrapplingHookCleanup", function(ply)
  1103. if not IsValid(ply) then return end
  1104.  
  1105. -- Clean up grappling physics timer
  1106. local rag = ply:GetNWEntity("player_ragdoll")
  1107. if IsValid(rag) then
  1108. timer.Remove("GrapplingPhysics_" .. rag:EntIndex())
  1109.  
  1110. -- Clean up rope constraint
  1111. if rag.GrapplingRopeConstraint and IsValid(rag.GrapplingRopeConstraint) then
  1112. rag.GrapplingRopeConstraint:Remove()
  1113. rag.GrapplingRopeConstraint = nil
  1114. end
  1115. end
  1116.  
  1117. -- Clean up grappling hooks on death
  1118. for _, ent in pairs(ents.FindByClass("ent_jack_hmcd_grapl")) do
  1119. if ent.Owner == ply then
  1120. ent:Remove()
  1121. end
  1122. end
  1123.  
  1124. -- Clean up grappling hook weapon state
  1125. local grapplingWeapon = ply:GetWeapon("wep_jack_hmcd_grapl")
  1126. if IsValid(grapplingWeapon) and grapplingWeapon.CleanupGrapplingHook then
  1127. grapplingWeapon:CleanupGrapplingHook()
  1128. end
  1129.  
  1130. -- Clean up stored data
  1131. ply.GrapplingHookState = nil
  1132. ply.GrapplingHookData = nil
  1133. end)
  1134. function rads(ply, isManual)
  1135. if not GetConVar("rads_status"):GetBool() then
  1136. if GetConVar("developer"):GetInt() > 0 then
  1137. print("Script is disabled. Not executing functionality.")
  1138. end
  1139. return
  1140. end
  1141.  
  1142. if not IsValid(ply) or not ply:IsPlayer() or not ply:Alive() then
  1143. if GetConVar("developer"):GetInt() > 0 then
  1144. print("[RADS] Failed validation - IsValid: " .. tostring(IsValid(ply)) .. ", IsPlayer: " .. tostring(ply:IsPlayer()) .. ", Alive: " .. tostring(ply:Alive()))
  1145. end
  1146. return
  1147. end
  1148. if timer.Exists("radstimer" .. ply:EntIndex()) then
  1149. if GetConVar("developer"):GetInt() > 0 then
  1150. print("[RADS] Timer exists for player: " .. ply:Nick())
  1151. end
  1152. return
  1153. end
  1154. if ply:GetNWBool("gh.Ghosted") then
  1155. if GetConVar("developer"):GetInt() > 0 then
  1156. print("[RADS] Player is ghosted: " .. ply:Nick())
  1157. end
  1158. return
  1159. end
  1160.  
  1161. if GetConVar("developer"):GetInt() > 0 then
  1162. print("[RADS] Attempting to ragdoll player: " .. ply:Nick() .. ", Manual: " .. tostring(isManual))
  1163. end
  1164.  
  1165. -- Only apply 2-second protection for manual ragdolling
  1166. if isManual and ply.lastGetUpTime and CurTime() - ply.lastGetUpTime < 1 then
  1167. ply:ChatPrint("You need to wait a moment before you can ragdoll again.")
  1168. return
  1169. end
  1170.  
  1171. local rag = ply:GetNWEntity("player_ragdoll")
  1172. if IsValid(rag) then
  1173. if ply.brokenspine then
  1174. ply:ChatPrint("You are Paralyzed.")
  1175. return
  1176. end
  1177.  
  1178. -- Prevent getting up from shock ragdoll
  1179. local shock = ply:GetNWFloat("RADS_Shock", 0)
  1180. local ragdollThreshold = GetConVar("rads_shock_ragdoll_threshold"):GetFloat()
  1181. if shock >= ragdollThreshold and rag.isShockRagdoll then
  1182. return
  1183. end
  1184.  
  1185. ply:SetNWBool("radsfa", false)
  1186. local health = ply:Health()
  1187. ragpos = rag:GetPos()
  1188. respawnmodel = ply:GetModel()
  1189. spawnpos = rag:GetPos() -- Direct position without room checking
  1190.  
  1191. -- Transfer vFire from ragdoll back to player
  1192. RADS_TransferVFireToPlayer(rag, ply)
  1193.  
  1194. -- Mark player as getting up from ragdoll to preserve adrenaline
  1195. ply.gettingUpFromRagdoll = true
  1196.  
  1197. -- Set the get up time to prevent immediate re-ragdolling
  1198. ply.lastGetUpTime = CurTime()
  1199.  
  1200. -- Restore normal NPC targeting when getting up from ragdoll
  1201. ply:SetNoTarget(false)
  1202.  
  1203. ply:Spawn()
  1204. ply:SetPos(spawnpos) -- RADS_PostRag(ply)
  1205.  
  1206. -- Clean up bullseye entity when ragdoll is removed
  1207. if IsValid(rag.bullseye) then
  1208. print("[BULLSEYE DEBUG] Cleaning up bullseye entity for player: " .. ply:Name())
  1209. rag.bullseye:Remove()
  1210. rag.bullseye = nil
  1211. end
  1212.  
  1213. rag:Remove()
  1214. if table.HasValue(BleedingEntities, rag) then table.insert(BleedingEntities, ply) end
  1215. ply.fake = false
  1216. ply:SetModel(respawnmodel)
  1217. ply.resetinv = true
  1218. hook.Run("RADSLoadout", ply)
  1219. ply.resetinv = false
  1220. ply:SetParent(nil)
  1221. ply:SetNoDraw(false)
  1222. ply:SetMoveType(MOVETYPE_WALK)
  1223. ply:SetCollisionGroup(COLLISION_GROUP_PLAYER)
  1224. ply:DrawViewModel(true)
  1225. ply:DrawWorldModel(true)
  1226. ply:SetSuppressPickupNotices(false)
  1227. ply:SetShouldPlayPickupSound(true)
  1228. ply.FakeShooting = false
  1229. ply:SetNWEntity("player_ragdoll", nil)
  1230. ply:SetViewEntity(ply)
  1231. ply:SetHealth(health)
  1232. net.Start("REMOVECALC")
  1233. net.Send(ply)
  1234. if IsValid(rag.target) then rag.target:Remove() end
  1235. timer.Remove("respawntimer" .. ply:EntIndex())
  1236.  
  1237. -- Clean up grappling physics timer when getting up
  1238. timer.Remove("GrapplingPhysics_" .. rag:EntIndex())
  1239.  
  1240. -- Clean up rope constraint
  1241. if rag.GrapplingRopeConstraint and IsValid(rag.GrapplingRopeConstraint) then
  1242. rag.GrapplingRopeConstraint:Remove()
  1243. rag.GrapplingRopeConstraint = nil
  1244. end
  1245.  
  1246. -- Clear the flag after a short delay
  1247. timer.Simple(0.2, function()
  1248. if IsValid(ply) then
  1249. ply.gettingUpFromRagdoll = nil
  1250. end
  1251. end)
  1252. else
  1253. local veh
  1254. if ply:InVehicle() then
  1255. veh = ply:GetVehicle()
  1256. ply:ExitVehicle()
  1257. end
  1258.  
  1259. ply:SetNoDraw(true)
  1260. timer.Create("respawntimer" .. ply:EntIndex(), 99999, 1, function() end)
  1261. ply.fake = true
  1262. if ply.IsBleeding or (ply.BloodLosing or 0) > 0 then
  1263. rag.IsBleeding = true
  1264. rag.bloodNext = CurTime()
  1265. rag.Blood = ply.Blood
  1266. RADS_Bleed(rag)
  1267. end
  1268.  
  1269. RADS_SavePlyInfo(ply)
  1270. RADS_EzArmorSaveInfo(ply)
  1271. if not ply:IsInWorld() then return end
  1272. net.Start("ADDCALC")
  1273. net.Send(ply)
  1274. local rag = ents.Create("prop_ragdoll")
  1275. rag:SetModel(ply:GetModel())
  1276. rag:SetSkin(ply:GetSkin())
  1277. for k, v in pairs(ply:GetBodyGroups()) do
  1278. rag:SetBodygroup(v.id, ply:GetBodygroup(v.id))
  1279. end
  1280.  
  1281. -- Get player color and transfer it
  1282. local playerColor = ply:GetPlayerColor()
  1283. if playerColor then
  1284. rag:BetterSetPlayerColor(playerColor)
  1285. else
  1286. -- Simple fallback
  1287. rag:BetterSetPlayerColor(Vector(1, 1, 1))
  1288. end
  1289.  
  1290. rag:SetAngles(ply:GetAngles())
  1291. rag:Spawn()
  1292.  
  1293. timer.Simple(0, function()
  1294. if IsValid(ply) and IsValid(rag) then
  1295. ply:SetNWBool("radsfa", true) -- RADS_PreRag(ply)
  1296. ply:SetParent(rag)
  1297. ply:SetMoveType(MOVETYPE_NONE)
  1298. ply:SetCollisionGroup(COLLISION_GROUP_IN_VEHICLE)
  1299. end
  1300. end)
  1301.  
  1302. rag:Activate()
  1303. local wep = ply:GetActiveWeapon()
  1304. if IsValid(wep) and table.HasValue(Guns, wep:GetClass()) then
  1305. if _G.SpawnWeapon then
  1306. _G.SpawnWeapon(ply)
  1307. ply.FakeShooting = true
  1308. else
  1309. if GetConVar("developer"):GetInt() > 0 then
  1310. print("[RADS] ERROR: SpawnWeapon function not available globally!")
  1311. end
  1312. end
  1313. end
  1314.  
  1315. rag:SetCollisionGroup(COLLISION_GROUP_WEAPON)
  1316. ply:SetNWEntity("player_ragdoll", rag)
  1317. rag:SetNWEntity("owner", ply)
  1318. rag:SetPos(ply:GetPos())
  1319. RADS_RagBones(ply)
  1320. rag:Activate()
  1321.  
  1322. -- Transfer vFire to ragdoll with proper timing
  1323. timer.Simple(0.1, function()
  1324. if IsValid(ply) and IsValid(rag) then
  1325. RADS_TransferVFire(ply, rag)
  1326. end
  1327. end)
  1328. -- Supersus feature removed - convar no longer exists
  1329. --[[
  1330. if GetConVar("rads_supersus"):GetBool() then
  1331. local light = ents.Create("light_dynamic")
  1332. light:SetPos(rag:GetPos() + Vector(0, 0, 20))
  1333. light:SetKeyValue("brightness", "5")
  1334. light:SetKeyValue("distance", "200")
  1335. light:SetKeyValue("style", "0")
  1336. light:Spawn()
  1337. light:Activate()
  1338. light:Fire("TurnOn", "", 0)
  1339. light:SetParent(rag)
  1340. end
  1341. --]]
  1342.  
  1343. local rpos = rag:GetPos()
  1344. timer.Simple(0, function()
  1345. if RADS.IsJmodAct() then
  1346. local armors = {}
  1347. for id, info in pairs(ply.EZarmor.items) do
  1348. local ent = CreateArmor(rag, info)
  1349. ent.armorID = id
  1350. ent.ragdoll = rag
  1351. ent.Owner = ply
  1352. armors[id] = ent
  1353. ent:CallOnRemove("Fake", function()
  1354. if ent.override then return end
  1355. rag.armors[ent.armorID] = nil
  1356. JMod.RemoveArmorByID(ply, ent.armorID, true)
  1357. end)
  1358. end
  1359.  
  1360. rag.armors = armors
  1361. rag:CallOnRemove("ArmorCleanup", function(ragdoll)
  1362. if IsValid(ragdoll) then
  1363. RemoveRag(ragdoll)
  1364. end
  1365. end)
  1366. end
  1367. end)
  1368.  
  1369. if IsValid(rag:GetPhysicsObject()) then rag:GetPhysicsObject():SetMass(CustomWeight[rag:GetModel()] or 20) end
  1370. rag:AddEFlags(EFL_KEEP_ON_RECREATE_ENTITIES)
  1371. ply:SetActiveWeapon(nil)
  1372. ply:DropObject()
  1373. ply:SetPos(rag:GetPos())
  1374. rag.pulse = ply.pulse or 0
  1375. RADS_RagBones(ply)
  1376.  
  1377. -- Transfer player velocity to ragdoll (FIX FOR EXCESSIVE INERTIA)
  1378.  
  1379. -- Mark as shock ragdoll if applicable
  1380. local shock = ply:GetNWFloat("RADS_Shock", 0)
  1381. local ragdollThreshold = GetConVar("rads_shock_ragdoll_threshold"):GetFloat()
  1382. if shock >= ragdollThreshold then
  1383. rag.isShockRagdoll = true
  1384. rag.shockLevel = shock
  1385. end
  1386. local playerVel = ply:GetVelocity()
  1387. if playerVel:Length() > 0 then
  1388. -- Apply velocity to main physics object with reduced multiplier to prevent excessive speed
  1389. local velocityMultiplier = 0.85 -- Reduce from default 1.0 to prevent speed doubling/tripling
  1390.  
  1391. -- GOALKEEPER DIVING MECHANICS
  1392. -- Check if player was jumping (vertical velocity > 100) and holding A or D
  1393. local isJumping = playerVel.z > 100
  1394. local holdingLeft = ply:KeyDown(IN_MOVELEFT)
  1395. local holdingRight = ply:KeyDown(IN_MOVERIGHT)
  1396.  
  1397. if isJumping and (holdingLeft or holdingRight) then
  1398. -- Player is diving like a goalkeeper
  1399. local diveForce = 130 -- Reduced horizontal dive force to prevent excessive flinging
  1400. local diveDirection = Vector(0, 0, 0)
  1401. local angularVelocity = Vector(0, 0, 0)
  1402.  
  1403. -- Use player's right vector for proper lateral diving
  1404. local playerRight = ply:GetRight()
  1405. local playerForward = ply:GetForward()
  1406.  
  1407. if holdingLeft then
  1408. diveDirection = playerRight * -diveForce -- Dive left (negative right)
  1409. -- Angular velocity to lean left (roll around forward axis) - increased for more dramatic lean
  1410. angularVelocity = playerForward * -25 -- Negative roll for left lean
  1411. elseif holdingRight then
  1412. diveDirection = playerRight * diveForce -- Dive right (positive right)
  1413. -- Angular velocity to lean right (roll around forward axis) - increased for more dramatic lean
  1414. angularVelocity = playerForward * 25 -- Positive roll for right lean
  1415. end
  1416.  
  1417. -- Apply diving velocity with original player velocity
  1418. local finalVelocity = (playerVel * velocityMultiplier) + diveDirection
  1419. rag:GetPhysicsObject():SetVelocity(finalVelocity)
  1420.  
  1421. -- Mark ragdoll as diving to prevent rolling until landing
  1422. rag.isDiving = true
  1423. rag.diveStartTime = CurTime()
  1424.  
  1425. -- Set timer to return to normal rolling after landing (3 seconds max)
  1426. timer.Simple(3, function()
  1427. if IsValid(rag) then
  1428. rag.isDiving = false
  1429. end
  1430. end)
  1431.  
  1432. -- Apply diving velocity and angular velocity to all physics objects for realistic goalkeeper dive
  1433. for i = 0, rag:GetPhysicsObjectCount() - 1 do
  1434. local physObj = rag:GetPhysicsObjectNum(i)
  1435. if IsValid(physObj) then
  1436. physObj:SetVelocity(finalVelocity)
  1437. -- Add angular velocity to make the ragdoll lean/rotate during dive
  1438. physObj:AddAngleVelocity(angularVelocity)
  1439. end
  1440. end
  1441. else
  1442. -- Normal ragdoll velocity transfer
  1443. rag:GetPhysicsObject():SetVelocity(playerVel * velocityMultiplier)
  1444.  
  1445. -- Also apply to other physics objects for more realistic momentum transfer
  1446. for i = 0, rag:GetPhysicsObjectCount() - 1 do
  1447. local physObj = rag:GetPhysicsObjectNum(i)
  1448. if IsValid(physObj) then
  1449. physObj:SetVelocity(playerVel * velocityMultiplier)
  1450. end
  1451. end
  1452. end
  1453. end
  1454.  
  1455. hook.Run("RADS_Ready", rag)
  1456.  
  1457. -- Create npc_bullseye for ragdolled player
  1458. if IsValid(rag) and IsValid(ply) then
  1459. local bullseye = ents.Create("npc_bullseye")
  1460. if IsValid(bullseye) then
  1461. -- Position bullseye slightly outside ragdoll body for NPC visibility
  1462. local ragPos = rag:GetPos()
  1463. local ragAngles = rag:GetAngles()
  1464. local offset = ragAngles:Forward() * 10 + Vector(0, 0, 15) -- 10 units forward, 15 units up
  1465. bullseye:SetPos(ragPos + offset)
  1466. bullseye:SetAngles(ragAngles)
  1467. bullseye:Spawn()
  1468. bullseye:Activate()
  1469.  
  1470. -- Make bullseye bigger
  1471. bullseye:SetModelScale(2.0, 0)
  1472.  
  1473. -- Make bullseye nodraw and remove physics
  1474. bullseye:SetNoDraw(true)
  1475. bullseye:SetSolid(SOLID_NONE)
  1476. bullseye:SetMoveType(MOVETYPE_NONE)
  1477. bullseye:SetCollisionGroup(COLLISION_GROUP_IN_VEHICLE)
  1478.  
  1479. -- Parent bullseye to ragdoll
  1480. bullseye:SetParent(rag)
  1481.  
  1482. -- Store references
  1483. rag:SetNWEntity("bullseye", bullseye)
  1484. bullseye:SetNWEntity("owner", ply)
  1485. bullseye:SetNWEntity("ragdoll", rag)
  1486.  
  1487. -- Set NPC relationships based on player's relationships
  1488. timer.Simple(0.1, function()
  1489. if IsValid(bullseye) and IsValid(ply) then
  1490. for _, npc in pairs(ents.FindByClass("npc_*")) do
  1491. if IsValid(npc) then
  1492. local playerDisposition = D_HT -- Default to hate disposition
  1493. if npc.Disposition then
  1494. playerDisposition = npc:Disposition(ply)
  1495. print("[RADS BULLSEYE] NPC " .. npc:GetClass() .. " disposition to player: " .. playerDisposition)
  1496. else
  1497. print("[RADS BULLSEYE] NPC " .. npc:GetClass() .. " has no Disposition method, using default: " .. playerDisposition)
  1498. end
  1499. npc:AddEntityRelationship(bullseye, playerDisposition, 99)
  1500. end
  1501. end
  1502. print("[RADS BULLSEYE] Created bullseye for player: " .. ply:Nick() .. " at position: " .. tostring(bullseye:GetPos()))
  1503. end
  1504. end)
  1505.  
  1506. -- Make NPCs ignore the ragdolled player and target the bullseye instead
  1507. ply:SetNoTarget(true)
  1508. end
  1509. end
  1510. end
  1511.  
  1512. if IsValid(veh) then rag:GetPhysicsObject():SetVelocity(veh:GetPhysicsObject():GetVelocity() * 5) end
  1513. end
  1514.  
  1515. function CC(ply, message)
  1516. if not IsValid(ply) then return end
  1517. local curTime = CurTime()
  1518. if ply.lastChatTime == nil or curTime - ply.lastChatTime >= 3 then
  1519. ply.lastChatTime = curTime
  1520. ply:ChatPrint(message)
  1521. end
  1522. end
  1523.  
  1524. hook.Add("PlayerFootstep", "RADS.BrokenBones", function(ply, pos, foot, sound, volume, filter)
  1525. if ply.LeftLeg <= 0.6 or ply.RightLeg <= 0.6 then
  1526. if ply:IsSprinting() then
  1527. ply.pain = ply.pain + 35
  1528. end
  1529. end
  1530. end)
  1531.  
  1532. hook.Add("Player Think", "RADS.SYNCPOS", function(ply)
  1533. local qw = ply:GetNWEntity('player_ragdoll')
  1534. if IsValid(qw) and ply:IsRag() then
  1535. local qe = qw:GetAttachment(qw:LookupAttachment("eyes")).Pos
  1536. ply:SetPos(qe)
  1537. end
  1538. end)
  1539.  
  1540. util.AddNetworkString("CheckPulseAndLink")
  1541. net.Receive("CheckPulseAndLink", function(len, ply)
  1542. local ragdoll = net.ReadEntity()
  1543. if IsValid(ragdoll) and ragdoll:IsRagdoll() then
  1544. -- Check if this is a dead body first
  1545. local deadOwner = ragdoll:GetNWEntity('deadbodyowner')
  1546. local livingOwner = ragdoll:GetNWEntity('owner')
  1547.  
  1548. -- Enhanced debug info
  1549. local debugMsg = "[STATUS DEBUG] "
  1550. if IsValid(deadOwner) then
  1551. debugMsg = debugMsg .. "DeadOwner: " .. deadOwner:Nick() .. " (HP: " .. deadOwner:Health() .. ") "
  1552. end
  1553. if IsValid(livingOwner) then
  1554. debugMsg = debugMsg .. "LivingOwner: " .. livingOwner:Nick() .. " (HP: " .. livingOwner:Health() .. ") "
  1555. end
  1556.  
  1557. if GetConVar("developer"):GetInt() > 0 then
  1558. print(debugMsg)
  1559. end
  1560. ply:ChatPrint(debugMsg)
  1561.  
  1562. if IsValid(deadOwner) and deadOwner:IsPlayer() then
  1563. -- This is a dead body - always show no vitals
  1564. ply:ChatPrint("No Pulse")
  1565. ply:ChatPrint("Not Breathing")
  1566. ply:ChatPrint("No Reaction")
  1567. return
  1568. end
  1569.  
  1570. if IsValid(livingOwner) and livingOwner:IsPlayer() then
  1571. local owner = livingOwner
  1572. -- FIXED: Get pulse from networked value or ragdoll
  1573. local pulse = owner:GetNWInt("PlayerPulse", owner.pulse or 70)
  1574. local hp = owner:Health()
  1575. local isDead = hp <= 0
  1576. -- FIXED: Check networked Otrub value instead of direct property
  1577. local isUnconscious = owner:GetNWBool("Otrub", false)
  1578.  
  1579. -- Enhanced debug info to chat and console
  1580. local statusDebug = "[LIVING] HP: " .. hp .. ", Pulse: " .. pulse .. ", Otrub: " .. tostring(isUnconscious) .. " (networked)"
  1581. if GetConVar("developer"):GetInt() > 0 then
  1582. print(statusDebug)
  1583. end
  1584. ply:ChatPrint(statusDebug)
  1585.  
  1586. -- If player is dead, show no vitals
  1587. if isDead then
  1588. ply:ChatPrint("No Pulse")
  1589. ply:ChatPrint("Not Breathing")
  1590. ply:ChatPrint("No Reaction")
  1591. return
  1592. end
  1593.  
  1594. -- Get organ health (default to healthy values if not set)
  1595. local leftLungHealth = (owner.Organs and owner.Organs['left_lung']) or 5
  1596. local rightLungHealth = (owner.Organs and owner.Organs['right_lung']) or 5
  1597. local heartHealth = (owner.Organs and owner.Organs['heart']) or 9
  1598.  
  1599. local organDebug = "[ORGANS] Heart: " .. heartHealth .. ", Left Lung: " .. leftLungHealth .. ", Right Lung: " .. rightLungHealth
  1600. if GetConVar("developer"):GetInt() > 0 then
  1601. print(organDebug)
  1602. end
  1603. ply:ChatPrint(organDebug)
  1604.  
  1605. -- Pulse status (based on heart health and pulse value)
  1606. if heartHealth <= 0 or pulse <= 0 then
  1607. ply:ChatPrint("No Pulse")
  1608. elseif pulse >= 130 then
  1609. ply:ChatPrint("Has High Pulse")
  1610. elseif pulse >= 60 then
  1611. ply:ChatPrint("Has Normal Pulse")
  1612. elseif pulse >= 30 and pulse < 60 then
  1613. ply:ChatPrint("Has Low Pulse")
  1614. else
  1615. ply:ChatPrint("No Pulse")
  1616. end
  1617.  
  1618. -- Breathing status (based on lung health)
  1619. if leftLungHealth <= 0 and rightLungHealth <= 0 then
  1620. ply:ChatPrint("Not Breathing")
  1621. else
  1622. ply:ChatPrint("Breathing")
  1623. end
  1624.  
  1625. -- Consciousness status (based on Otrub variable) - FIXED
  1626. local consciousnessDebug = "[CONSCIOUSNESS] Otrub value: " .. tostring(owner.Otrub) .. " (" .. type(owner.Otrub) .. "), isUnconscious: " .. tostring(isUnconscious)
  1627. if GetConVar("developer"):GetInt() > 0 then
  1628. print(consciousnessDebug)
  1629. end
  1630. ply:ChatPrint(consciousnessDebug)
  1631.  
  1632. -- FIXED: Use the isUnconscious variable we already calculated
  1633. if isUnconscious then
  1634. ply:ChatPrint("No Reaction")
  1635. else
  1636. ply:ChatPrint("Has Reaction")
  1637. end
  1638. else
  1639. -- No valid owner found - treat as dead body
  1640. ply:ChatPrint("[NO OWNER] No Pulse")
  1641. ply:ChatPrint("[NO OWNER] Not Breathing")
  1642. ply:ChatPrint("[NO OWNER] No Reaction")
  1643. end
  1644. end
  1645. end)
  1646.  
  1647. util.AddNetworkString("rads.bloodcheck")
  1648. net.Receive("rads.bloodcheck", function(len, ply)
  1649. local r = net.ReadEntity()
  1650. if IsValid(r) and r:IsRagdoll() then
  1651. net.Start("rads.bloodcheck")
  1652. local i = r.Blood or 0
  1653. net.WriteInt(i, 14)
  1654. net.Send(ply)
  1655. end
  1656. end)
  1657.  
  1658. function _P:PickupEnt()
  1659. local ply = self
  1660. local rag = ply:GetNWEntity("player_ragdoll")
  1661. local phys = rag:GetPhysicsObjectNum(7)
  1662. local offset = phys:GetAngles():Right() * 5
  1663. local traceinfo = {
  1664. start = phys:GetPos(),
  1665. endpos = phys:GetPos() + offset,
  1666. filter = rag,
  1667. output = trace,
  1668. }
  1669.  
  1670. local trace = util.TraceLine(traceinfo)
  1671. if trace.Entity == Entity(0) or trace.Entity == NULL or not trace.Entity.canpickup then return end
  1672. if trace.Entity:GetClass() == "wep" then
  1673. ply:Give(trace.Entity.curweapon, true):SetClip1(trace.Entity.Clip)
  1674. ply.wep.Clip = trace.Entity.Clip
  1675. trace.Entity:Remove()
  1676. end
  1677. end
  1678.  
  1679. function _P:DropWeapon1(wep)
  1680. local ply = self
  1681. wep = wep or ply:GetActiveWeapon()
  1682. if not IsValid(wep) then return end
  1683. ply:DropWeapon(wep)
  1684. wep.Spawned = true
  1685. ply:SetActiveWeapon(nil)
  1686. end
  1687.  
  1688. hook.Add("PlayerSay", "dropweaponhuy", function(ply, text)
  1689. if string.lower(text) == "#drop" or string.lower(text) == "*drop" or string.lower(text) == "!drop" then
  1690. if not ply.fake then
  1691. ply:DropWeapon1()
  1692. return ""
  1693. else
  1694. if IsValid(ply.wep) then
  1695. if IsValid(ply.WepCons) then
  1696. ply.WepCons:Remove()
  1697. ply.WepCons = nil
  1698. end
  1699.  
  1700. if IsValid(ply.WepCons2) then
  1701. ply.WepCons2:Remove()
  1702. ply.WepCons2 = nil
  1703. end
  1704.  
  1705. ply.wep.canpickup = true
  1706. ply.wep:SetOwner()
  1707. ply.wep.curweapon = ply.curweapon
  1708. -- Preserve weapon data instead of deleting it
  1709. if ply.Info.Weapons[ply.Info.ActiveWeapon] then
  1710. ply.Info.Weapons[ply.Info.ActiveWeapon].Clip1 = ply.wep.Clip
  1711. -- Don't delete the weapon data, just mark it as not active
  1712. ply.Info.Weapons[ply.Info.ActiveWeapon].IsActive = false
  1713. end
  1714. ply:StripWeapon(ply.Info.ActiveWeapon)
  1715. ply.wep = nil
  1716. ply.Info.ActiveWeapon = nil
  1717. ply.Info.ActiveWeapon2 = nil
  1718. ply:SetActiveWeapon(nil)
  1719. ply.FakeShooting = false
  1720. else
  1721. ply:PickupEnt()
  1722. end
  1723. return ""
  1724. end
  1725. end
  1726. end)
  1727.  
  1728. hook.Add("Think", "radsshoot", function()
  1729. for i, ply in pairs(player.GetAll()) do
  1730. if ply:Alive() then
  1731. if IsValid(ply:GetNWEntity("player_ragdoll")) and ply.FakeShooting then
  1732. if _G.SpawnWeapon then
  1733. _G.SpawnWeapon(ply)
  1734. else
  1735. if GetConVar("developer"):GetInt() > 0 then
  1736. print("[RADS] ERROR: SpawnWeapon function not available globally at line 1772!")
  1737. end
  1738. end
  1739. else
  1740. if IsValid(ply.wep) then
  1741. if _G.DespawnWeapon then
  1742. _G.DespawnWeapon(ply)
  1743. else
  1744. if GetConVar("developer"):GetInt() > 0 then
  1745. print("[RADS] ERROR: DespawnWeapon function not available globally!")
  1746. end
  1747. end
  1748. end
  1749. end
  1750. end
  1751. end
  1752. end)
  1753.  
  1754. function _P:PickupEnt()
  1755. local ply = self
  1756. local rag = ply:GetNWEntity("player_ragdoll")
  1757. local phys = rag:GetPhysicsObjectNum(7)
  1758. local offset = phys:GetAngles():Right() * 5
  1759. local traceinfo = {
  1760. start = phys:GetPos(),
  1761. endpos = phys:GetPos() + offset,
  1762. filter = rag,
  1763. output = trace,
  1764. }
  1765.  
  1766. local trace = util.TraceLine(traceinfo)
  1767. if trace.Entity == Entity(0) or trace.Entity == NULL or not trace.Entity.canpickup then return end
  1768. if trace.Entity:GetClass() == "wep" then
  1769. ply:Give(trace.Entity.curweapon, true):SetClip1(trace.Entity.Clip)
  1770. ply.wep.Clip = trace.Entity.Clip
  1771. trace.Entity:Remove()
  1772. end
  1773. end
  1774.  
  1775. util.AddNetworkString("Unload")
  1776. net.Receive("Unload", function(len, ply)
  1777. local wep = net.ReadEntity()
  1778. local oldclip = wep:Clip1()
  1779. local ammo = wep:GetPrimaryAmmoType()
  1780. wep:EmitSound("snd_jack_hmcd_ammotake.wav")
  1781. wep:SetClip1(0)
  1782. ply:GiveAmmo(oldclip, ammo)
  1783. end)
  1784.  
  1785. -- Network strings for inventory system
  1786. util.AddNetworkString("RequestRagdollInventory")
  1787. util.AddNetworkString("SendRagdollInventory")
  1788. util.AddNetworkString("TakeWeaponFromRagdoll")
  1789. util.AddNetworkString("TakeAllWeaponsFromRagdoll")
  1790.  
  1791. -- Handle inventory request
  1792. net.Receive("RequestRagdollInventory", function(len, ply)
  1793. local ragdoll = net.ReadEntity()
  1794. if not IsValid(ragdoll) or not ragdoll:IsRagdoll() then return end
  1795.  
  1796. local owner = ragdoll:GetNWEntity("owner") or ragdoll:GetNWEntity('deadbodyowner')
  1797. if not IsValid(owner) or not owner:IsPlayer() then return end
  1798.  
  1799. -- Check distance
  1800. local distance = ply:GetPos():Distance(ragdoll:GetPos())
  1801. if distance > 100 then return end
  1802.  
  1803. -- Get weapons from owner's saved info
  1804. local weaponsData = {}
  1805. if owner.Info and owner.Info.Weapons then
  1806. for weaponClass, weaponInfo in pairs(owner.Info.Weapons) do
  1807. local weaponTable = weapons.Get(weaponClass)
  1808. weaponsData[weaponClass] = {
  1809. name = weaponTable and weaponTable.PrintName or weaponClass,
  1810. clip1 = weaponInfo.Clip1 or 0,
  1811. clip2 = weaponInfo.Clip2 or 0,
  1812. ammoType = weaponInfo.AmmoType or -1
  1813. }
  1814. end
  1815. end
  1816.  
  1817. -- Send inventory to client
  1818. net.Start("SendRagdollInventory")
  1819. net.WriteTable(weaponsData)
  1820. net.Send(ply)
  1821. end)
  1822.  
  1823. -- Handle taking single weapon
  1824. net.Receive("TakeWeaponFromRagdoll", function(len, ply)
  1825. local ragdoll = net.ReadEntity()
  1826. local weaponClass = net.ReadString()
  1827.  
  1828. if not IsValid(ragdoll) or not ragdoll:IsRagdoll() then return end
  1829.  
  1830. local owner = ragdoll:GetNWEntity("owner") or ragdoll:GetNWEntity('deadbodyowner')
  1831. if not IsValid(owner) or not owner:IsPlayer() then return end
  1832.  
  1833. -- Check distance
  1834. local distance = ply:GetPos():Distance(ragdoll:GetPos())
  1835. if distance > 100 then return end
  1836.  
  1837. -- Check if weapon exists in ragdoll's inventory
  1838. if owner.Info and owner.Info.Weapons and owner.Info.Weapons[weaponClass] then
  1839. local weaponInfo = owner.Info.Weapons[weaponClass]
  1840.  
  1841. -- Give weapon to player
  1842. local weapon = ply:Give(weaponClass, true)
  1843. if IsValid(weapon) then
  1844. weapon:SetClip1(weaponInfo.Clip1 or 0)
  1845. weapon:SetClip2(weaponInfo.Clip2 or 0)
  1846.  
  1847. -- Remove from ragdoll's inventory
  1848. owner.Info.Weapons[weaponClass] = nil
  1849.  
  1850. ply:ChatPrint("Taken: " .. (weapons.Get(weaponClass) and weapons.Get(weaponClass).PrintName or weaponClass))
  1851. end
  1852. end
  1853. end)
  1854.  
  1855. -- Handle taking all weapons
  1856. net.Receive("TakeAllWeaponsFromRagdoll", function(len, ply)
  1857. local ragdoll = net.ReadEntity()
  1858.  
  1859. if not IsValid(ragdoll) or not ragdoll:IsRagdoll() then return end
  1860.  
  1861. local owner = ragdoll:GetNWEntity("owner") or ragdoll:GetNWEntity('deadbodyowner')
  1862. if not IsValid(owner) or not owner:IsPlayer() then return end
  1863.  
  1864. -- Check distance
  1865. local distance = ply:GetPos():Distance(ragdoll:GetPos())
  1866. if distance > 100 then return end
  1867.  
  1868. local takenCount = 0
  1869.  
  1870. -- Give all weapons to player
  1871. if owner.Info and owner.Info.Weapons then
  1872. for weaponClass, weaponInfo in pairs(owner.Info.Weapons) do
  1873. local weapon = ply:Give(weaponClass, true)
  1874. if IsValid(weapon) then
  1875. weapon:SetClip1(weaponInfo.Clip1 or 0)
  1876. weapon:SetClip2(weaponInfo.Clip2 or 0)
  1877. takenCount = takenCount + 1
  1878. end
  1879. end
  1880.  
  1881. -- Clear ragdoll's inventory
  1882. owner.Info.Weapons = {}
  1883.  
  1884. if takenCount > 0 then
  1885. ply:ChatPrint("Taken " .. takenCount .. " weapons from " .. owner:Name())
  1886. else
  1887. ply:ChatPrint("No weapons to take")
  1888. end
  1889. end
  1890. end)
  1891.  
  1892. hook.Add("KeyPress", "Shooting", function(ply, key)
  1893. if not ply:Alive() then return end
  1894. -- if key == IN_RELOAD then Reload(ply.wep) end -- Removed undefined Reload call
  1895.  
  1896. -- Jump pain for broken/dislocated bones
  1897. if key == IN_JUMP then
  1898. local hasLegInjury = false
  1899. local painAmount = 0
  1900.  
  1901. -- Check for broken legs
  1902. if ply:GetNWBool("RADS_LeftLegBroken") or ply:GetNWBool("RADS_RightLegBroken") then
  1903. hasLegInjury = true
  1904. painAmount = painAmount + math.random(15, 25) -- High pain for broken bones
  1905. end
  1906.  
  1907. -- Check for dislocated legs
  1908. if ply:GetNWBool("RADS_LeftLegDislocated") or ply:GetNWBool("RADS_RightLegDislocated") then
  1909. hasLegInjury = true
  1910. painAmount = painAmount + math.random(8, 15) -- Moderate pain for dislocated bones
  1911. end
  1912.  
  1913. -- Check for other broken bones that would affect jumping
  1914. if ply:GetNWBool("RADS_LeftArmBroken") or ply:GetNWBool("RADS_RightArmBroken") then
  1915. painAmount = painAmount + math.random(3, 8) -- Minor pain for arm injuries
  1916. end
  1917.  
  1918. if ply.brokenspine or ply.brokenupperspine then
  1919. painAmount = painAmount + math.random(20, 35) -- Severe pain for spine injuries
  1920. end
  1921.  
  1922. -- Apply realistic pain system for jumping with injuries
  1923. if hasLegInjury or painAmount > 0 then
  1924. local damageIntensity = painAmount
  1925.  
  1926. -- Check for severe bone injury pain (instant pain threshold)
  1927. if painAmount >= 20 or (ply.brokenspine or ply.brokenupperspine) then
  1928. -- Severe injuries: instant pain + pain debt
  1929. local instantPain = painAmount * 0.5 -- 50% instant for severe jumping pain
  1930. local painDebt = painAmount * 0.5 -- 50% debt
  1931.  
  1932. ply.pain = (ply.pain or 0) + instantPain
  1933. ply.painDebt = (ply.painDebt or 0) + painDebt
  1934. ply.lastDamageTime = CurTime()
  1935. ply.damageIntensity = (ply.damageIntensity or 0) + damageIntensity
  1936.  
  1937. ply:ChatPrint("Jumping with your injuries causes excruciating pain!")
  1938. elseif painAmount >= 8 then
  1939. -- Moderate injuries: mostly pain debt
  1940. local instantPain = painAmount * 0.2 -- 20% instant
  1941. local painDebt = painAmount * 0.8 -- 80% debt
  1942.  
  1943. ply.pain = (ply.pain or 0) + instantPain
  1944. ply.painDebt = (ply.painDebt or 0) + painDebt
  1945. ply.lastDamageTime = CurTime()
  1946. ply.damageIntensity = (ply.damageIntensity or 0) + damageIntensity
  1947.  
  1948. ply:ChatPrint("Jumping with your injuries hurts badly.")
  1949. else
  1950. -- Minor injuries: almost all pain debt
  1951. local instantPain = painAmount * 0.1 -- 10% instant
  1952. local painDebt = painAmount * 0.9 -- 90% debt
  1953.  
  1954. ply.pain = (ply.pain or 0) + instantPain
  1955. ply.painDebt = (ply.painDebt or 0) + painDebt
  1956. ply.lastDamageTime = CurTime()
  1957. ply.damageIntensity = (ply.damageIntensity or 0) + damageIntensity
  1958.  
  1959. ply:ChatPrint("Jumping with your injuries causes some pain.")
  1960. end
  1961. end
  1962. end
  1963. end)
  1964.  
  1965. local lbt = 0 -- util.AddNetworkString('RequestRagdollInfo') -- util.AddNetworkString('SendPlayerWeaponsAndAmmo') -- util.AddNetworkString('RequestRagdollInfo') -- net.Receive("RequestRagdollInfo", function(len, ply) -- local ragdoll = net.ReadEntity() -- if IsValid(ragdoll) and ragdoll:GetClass() == "prop_ragdoll" then -- owner = ragdoll:GetNWEntity("owner") -- deadbodyowner = ragdoll:GetNWEntity('deadbodyowner') -- if IsValid(owner) and owner:IsPlayer() or IsValid(deadbodyowner) and deadbodyowner:IsPlayer() then -- local weaponsData = owner.Info and owner.Info.Weapons3 or {} -- local ammoData = owner.Info and owner.Info.AllAmmo or {} -- if not weaponsData or not AmmoData then -- local weaponsData = deadbodyowner.Info and deadbodyowner.Info.AllAmmo or {} -- end -- net.Start("SendPlayerWeaponsAndAmmo") -- net.WriteTable({weapons = weaponsData, ammo = ammoData}) -- net.Send(ply) -- end -- end -- end
  1966. util.AddNetworkString("RADS.CHATSAY")
  1967. hook.Add('Think', "RADS.CHAT", function()
  1968. if CurTime() - lbt >= 600 then
  1969. net.Start('RADS.CHATSAY')
  1970. net.Broadcast()
  1971. lbt = CurTime()
  1972. end
  1973. end)
  1974.  
  1975. hook.Add("PlayerUse", "nouse", function(ply, ent) if ply.fake then return false end end)
  1976. function deathrem(victim)
  1977. local rag = victim:GetNWEntity("player_ragdoll")
  1978.  
  1979. -- If player dies while in ragdoll state, set to spectator mode to prevent entity appearance
  1980. if victim.fake then
  1981. victim:Spectate(OBS_MODE_ROAMING)
  1982. victim:SetMoveType(MOVETYPE_OBSERVER)
  1983. end
  1984.  
  1985. net.Start('ADDCALC')
  1986. net.Send(victim)
  1987. timer.Remove('respawntimer' .. victim:EntIndex())
  1988. if victim.IsBleeding or (victim.BloodLosing or 0) > 0 then
  1989. rag.IsBleeding = true
  1990. rag.bloodNext = CurTime()
  1991. rag.Blood = victim.Blood
  1992. RADS_Bleed(rag)
  1993. end
  1994.  
  1995. if IsValid(rag.ZacConsLH) then
  1996. rag.ZacConsLH:Remove()
  1997. rag.ZacConsLH = nil
  1998. end
  1999.  
  2000. if IsValid(rag.ZacConsRH) then
  2001. rag.ZacConsRH:Remove()
  2002. rag.ZacConsRH = nil
  2003. end
  2004.  
  2005. if not IsValid(rag) and not RADS.IsTTT() then
  2006. victim:SetNWBool("radsfa", false)
  2007. rag = ents.Create("prop_ragdoll")
  2008. rag:SetModel(victim:GetModel())
  2009. rag:SetPos(victim:GetPos())
  2010. rag:SetAngles(victim:GetAngles())
  2011. rag:Spawn()
  2012. rag:Activate()
  2013. rag:SetSkin(victim:GetSkin())
  2014. for key, value in pairs(victim:GetBodyGroups()) do
  2015. rag:SetBodygroup(value.id, victim:GetBodygroup(value.id))
  2016. end
  2017.  
  2018. if victim.IsBleeding or (victim.BloodLosing or 0) > 0 then
  2019. rag.IsBleeding = true
  2020. rag.bloodNext = CurTime()
  2021. rag.Blood = victim.Blood
  2022. RADS_Bleed(rag)
  2023. end
  2024.  
  2025. -- Get player color and transfer it
  2026. local playerColor = victim:GetPlayerColor()
  2027. if playerColor then
  2028. rag:BetterSetPlayerColor(playerColor)
  2029. else
  2030. -- Simple fallback
  2031. rag:BetterSetPlayerColor(Vector(1, 1, 1))
  2032. end
  2033.  
  2034. victim:SetNWEntity("player_ragdoll", rag)
  2035. rag:SetNWEntity("owner", victim)
  2036. RADS_RagBones(victim)
  2037. if IsValid(rag:GetPhysicsObject()) then
  2038. local CustomWeightt = CustomWeight[rag:GetModel()]
  2039. rag:GetPhysicsObject():SetMass(30)
  2040. end
  2041.  
  2042. victim:SetPos(rag:GetPos())
  2043. if RADS.IsJmodAct() then
  2044. local armors = {}
  2045. for id, info in pairs(victim.EZarmor.items) do
  2046. local ent = CreateArmor(rag, info)
  2047. ent.armorID = id
  2048. ent.ragdoll = rag
  2049. ent.Owner = victim
  2050. armors[id] = ent
  2051. ent:CallOnRemove("Fake", Remove, victim)
  2052. end
  2053.  
  2054. rag.armors = armors
  2055. rag:CallOnRemove("Armors", RemoveRag)
  2056. end
  2057.  
  2058. victim:SetParent(nil)
  2059. if not victim:IsBot() then
  2060. local steamID = victim:SteamID()
  2061. if victim.Info then
  2062. victim.Info.Hp = nil
  2063. victim.Info.Armor = nil
  2064. end
  2065.  
  2066. if victim.Info then
  2067. -- Don't clear weapons on death - preserve inventory for looting
  2068. -- victim.Info.Weapons2 = {}
  2069. -- victim.Info.Weapons = {}
  2070. -- victim.Info.AllAmmo = {}
  2071. end
  2072.  
  2073. savedPlayerState[steamID] = nil
  2074. if victim:HasGodMode() then victim:GodDisable() end
  2075. end
  2076. end
  2077. -- This is the correct place to apply rigor mortis, after 'rag' is guaranteed to be the active ragdoll.
  2078. if victim.rigorMortis and IsValid(rag) then
  2079. ApplyRigorMortis(rag)
  2080. victim.rigorMortis = nil -- Clear the flag after applying
  2081. end
  2082. end
  2083.  
  2084. hook.Add("DoPlayerDeath", "RADS.DeathH3", function(ply, att, dmg) deathrem(ply) end)
  2085. hook.Add("PlayerDeath", "RADS.DeathH1", function(v, i, a)
  2086. v:SetParent(nil)
  2087. v:SetNWBool('brokenspine', false)
  2088. v.pulse = 0
  2089. end)
  2090.  
  2091. hook.Add("PlayerDeath", "RADS.DeathH2", function(victim, inflictor, attacker)
  2092. local rag = victim:GetNWEntity('player_ragdoll')
  2093. victim:SetParent(nil)
  2094. victim:SetNWBool('brokenspine', false)
  2095.  
  2096. -- Check if death first-person is enabled, if not remove calcview
  2097. if not GetConVar('rads_death_firstperson'):GetBool() then
  2098. if GetConVar('rads_spectatorfix'):GetBool() then
  2099. net.Start('REMOVECALC')
  2100. net.Send(victim)
  2101. end
  2102. else
  2103. -- Mark player as dead for calcview system
  2104. victim:SetNWBool('rads_dead_firstperson', true)
  2105. end
  2106.  
  2107. victim.pulse = 0
  2108.  
  2109. -- Clean up delayed bone pain timers to prevent pain after death
  2110. local entIndex = victim:EntIndex()
  2111. local limbNames = {"head", "neck", "chest", "stomach", "leftarm", "rightarm", "leftleg", "rightleg"}
  2112. for _, limb in ipairs(limbNames) do
  2113. timer.Remove("RADS_DelayedBonePain_" .. entIndex .. "_" .. limb)
  2114. timer.Remove("RADS_DelayedBonePain_" .. entIndex .. "_" .. limb .. "_dislocated")
  2115. timer.Remove("RADS_DelayedBonePain_" .. entIndex .. "_" .. limb .. "_escalation")
  2116. end
  2117.  
  2118. -- Clean up gore system on death
  2119. if victim.hasGoreExplosion then
  2120. victim.hasGoreExplosion = nil
  2121. end
  2122.  
  2123. if IsValid(rag) and rag.hasGoreExplosion then
  2124. -- Stop blood stream timer
  2125. local timerName = "RADS_BloodStream_" .. rag:EntIndex()
  2126. if timer.Exists(timerName) then
  2127. timer.Remove(timerName)
  2128. end
  2129. end
  2130.  
  2131. -- Clean up fear system on death
  2132. if victim.fearLevel then
  2133. victim.fearLevel = 0
  2134. end
  2135. if victim.fearDecayTimer then
  2136. timer.Remove(victim.fearDecayTimer)
  2137. victim.fearDecayTimer = nil
  2138. end
  2139. end)
  2140.  
  2141. concommand.Add("fake", function(ply, cmd, args)
  2142. if ply:GetMoveType() == MOVETYPE_OBSERVER then return end
  2143. if ply.fake then -- If already in ragdoll, try to get up
  2144. if ply:IsRag() then if ply:GetRads().physgunned then return nil end end
  2145. if timer.Exists("radstimer" .. ply:EntIndex()) then return nil end
  2146. if timer.Exists("StunTime" .. ply:EntIndex()) then return nil end
  2147. if timer.Exists("Epilepsy" .. ply:EntIndex()) then return nil end
  2148. if ply.brokenspine or ply.brokenupperspine then return nil end
  2149. if ply.Blood < GetConVar("rads_bloodlimit"):GetInt() then return end
  2150. if IsValid(ply:GetNWEntity("player_ragdoll")) and ply:GetNWEntity("player_ragdoll"):GetVelocity():Length() > 300 then return nil end
  2151.  
  2152. if table.Count(constraint.FindConstraints(ply:GetNWEntity("player_ragdoll"), 'Rope')) > 0 then
  2153. ply:ChatPrint("You\'re tied up, Space to struggle.")
  2154. return nil
  2155. end
  2156.  
  2157. -- Check if player is handcuffed
  2158. if ply:GetNWBool("RADS_Handcuffed", false) or (IsValid(ply:GetNWEntity("player_ragdoll")) and ply:GetNWEntity("player_ragdoll"):GetNWBool("RADS_Handcuffed", false)) then
  2159. ply:ChatPrint("You are handcuffed and cannot get up.")
  2160. return nil
  2161. end
  2162.  
  2163. -- Check for conditions that prevent getting up
  2164. if ply.Otrub then
  2165. return nil
  2166. end
  2167.  
  2168. if ply.concussionActive then
  2169. ply:ChatPrint("You are too disoriented from the concussion to get up.")
  2170. return nil
  2171. end
  2172.  
  2173. -- Check for extreme shock preventing get up
  2174. local shockLevel = ply:GetNWFloat("RADS_Shock", 0)
  2175. if shockLevel >= 75 then
  2176. ply:ChatPrint("You are in too much shock to even attempt getting up.")
  2177. return nil
  2178. end
  2179.  
  2180. -- Check if player is fully submerged in water (prevent getting up)
  2181. local ragdoll = ply:GetNWEntity("player_ragdoll")
  2182. if IsValid(ragdoll) then
  2183. local headBone = ragdoll:LookupBone("ValveBiped.Bip01_Head1")
  2184. if headBone then
  2185. local headPos = ragdoll:GetBonePosition(headBone)
  2186. local waterLevel = util.PointContents(headPos)
  2187. local isUnderwater = bit.band(waterLevel, CONTENTS_WATER) ~= 0
  2188.  
  2189. if isUnderwater then
  2190. ply:ChatPrint("You cannot get up while fully submerged in water.")
  2191. return nil
  2192. end
  2193. end
  2194. end
  2195.  
  2196. -- Check if instant get up is enabled
  2197. if GetConVar("rads_instant_getup"):GetBool() then
  2198. -- Instant get up - bypass the get up process but respect all blocking conditions
  2199. rads(ply)
  2200. return
  2201. end
  2202.  
  2203. ply.gettingUp = true
  2204. ply.lastGetUpAttempt = CurTime()
  2205. ply:ChatPrint("Attempting to get up...")
  2206. else -- If not in ragdoll, ragdoll the player
  2207. rads(ply, true) -- Pass true to indicate this is manual
  2208. if not RADS.IsTTT() and not ply.fake then timer.Create("radstimer" .. ply:EntIndex(), 1.5, 1, function() end) end
  2209. end
  2210. end)
  2211.  
  2212. hook.Add("PlayerDisconnected", "removeallwhenleave", function(ply)
  2213. local steamID = ply:SteamID()
  2214. savedPlayerState[steamID] = nil
  2215. if ply.Info then
  2216. ply.Info.Hp = nil
  2217. ply.Info.Armor = nil
  2218. ply.Info = nil
  2219. end
  2220.  
  2221. if savedPlayerState[steamID] then savedPlayerState[steamID] = nil end
  2222. local exr = ply:GetNWEntity("player_ragdoll")
  2223. if IsValid(exr) then
  2224. -- Clean up bullseye entity when player disconnects
  2225. if IsValid(exr.bullseye) then
  2226. print("[BULLSEYE DEBUG] Cleaning up bullseye entity for disconnected player: " .. ply:Name())
  2227. exr.bullseye:Remove()
  2228. exr.bullseye = nil
  2229. end
  2230.  
  2231. -- Clean up gore system when player disconnects
  2232. if exr.hasGoreExplosion then
  2233. -- Remove gore stump
  2234. if IsValid(exr.goreStump) then
  2235. exr.goreStump:Remove()
  2236. exr.goreStump = nil
  2237. end
  2238.  
  2239. -- Stop blood stream timer
  2240. local timerName = "RADS_BloodStream_" .. exr:EntIndex()
  2241. if timer.Exists(timerName) then
  2242. timer.Remove(timerName)
  2243. end
  2244.  
  2245. exr.hasGoreExplosion = nil
  2246. end
  2247.  
  2248. ply:SetNWEntity("player_ragdoll", nil)
  2249. exr:SetNWEntity('owner', nil)
  2250. exr:SetNWEntity("RagdollController", nil)
  2251. end
  2252.  
  2253. -- Clean up player gore state
  2254. if ply.hasGoreExplosion then
  2255. ply.hasGoreExplosion = nil
  2256. end
  2257. end)
  2258.  
  2259. hook.Add("OnPlayerHitGround", "GovnoJopa", function(ply, a, b, speed)
  2260. if speed > 200 then
  2261. local tr = {}
  2262. tr.start = ply:GetPos()
  2263. tr.endpos = ply:GetPos() - Vector(0, 0, 10)
  2264. tr.mins = ply:OBBMins()
  2265. tr.maxs = ply:OBBMaxs()
  2266. tr.filter = ply
  2267. local traceResult = util.TraceHull(tr)
  2268. if traceResult.Entity:IsPlayer() and not traceResult.Entity.fake then rads(traceResult.Entity) end
  2269. end
  2270. end)
  2271.  
  2272.  
  2273.  
  2274. hook.Add("Think", "RemoveRagdoll", function()
  2275. for _, ply in ipairs(player.GetAll()) do -- end
  2276. local ragdoll_entity = ply:GetRagdollEntity()
  2277. if IsValid(ragdoll_entity) then ragdoll_entity:Remove() end
  2278. end
  2279. end)
  2280.  
  2281. function propknocked(ply)
  2282. if timer.Exists("propknocked" .. ply:UserID()) then
  2283. return true
  2284. else
  2285. timer.Create("propknocked" .. ply:UserID(), 1, 1, function() end)
  2286. return false
  2287. end
  2288. end
  2289.  
  2290. hook.Add("EntityTakeDamage", "fallfromclub", function(target, dmginfo)
  2291. local random = math.Rand(1, 2)
  2292. if target:IsPlayer() and GetConVar('rads_fallonclub'):GetBool() and dmginfo:IsDamageType(DMG_CLUB) and not target.fake then
  2293. -- Make club damage more reasonable - only 10% chance instead of guaranteed
  2294. if GetConVar('rads_randomfallfromclub'):GetBool() then
  2295. if random > 1.9 then rads(target) end
  2296. else
  2297. -- Instead of always ragdolling, add damage threshold and chance
  2298. if dmginfo:GetDamage() >= 25 and math.random() < 0.1 then -- 10% chance for significant club damage
  2299. rads(target)
  2300. end
  2301. end
  2302. end
  2303. end)
  2304.  
  2305. hook.Add("EntityTakeDamage", "fallondamage", function(target, dmginfo)
  2306. if target:IsPlayer() then -- ply.pain = ply.pain + 5
  2307. if GetConVar('rads_fallchance'):GetBool() and not target.fake then
  2308. -- Add damage threshold to prevent ragdolling from minor damage
  2309. local damage = dmginfo:GetDamage()
  2310. if damage >= 15 then -- Increased threshold from 10 to 15
  2311. local damagePosition = dmginfo:GetDamagePosition()
  2312. local bodyPart = target:LastHitGroup(damagePosition)
  2313.  
  2314. -- Initialize pain and consciousness if not set
  2315. target.pain = target.pain or 0
  2316.  
  2317. -- Check for knockdown protection (1 second after getting up)
  2318. if target.lastGetUpTime and CurTime() - target.lastGetUpTime < 1.0 then
  2319. return -- Player has knockdown protection
  2320. end
  2321.  
  2322. -- Use the improved shouldFall function for base calculation
  2323. local shouldRagdoll = shouldFall(bodyPart, damage, dmginfo:GetDamageType(), target)
  2324.  
  2325. if not shouldRagdoll then
  2326. -- Additional chance modifiers for special conditions
  2327. local extraChance = 0
  2328.  
  2329. -- Pain-based modifier: Higher pain increases knockdown chance
  2330. if target.pain and target.pain > 100 then
  2331. extraChance = extraChance + 0.20 -- 20% extra for very high pain
  2332. elseif target.pain and target.pain > 50 then
  2333. extraChance = extraChance + 0.10 -- 10% extra for high pain
  2334. end
  2335.  
  2336. -- Consciousness-based modifier: Unconscious players more likely to be knocked down
  2337. if target.Otrub then -- Player is unconscious
  2338. extraChance = extraChance + 0.30 -- 30% extra when unconscious
  2339. end
  2340.  
  2341. -- Rapid hit tracking for shotguns (preserved from original system)
  2342. if dmginfo:IsDamageType(DMG_BUCKSHOT) then
  2343. local steamID = target:SteamID()
  2344. local currentTime = CurTime()
  2345.  
  2346. -- Initialize hit tracking for this player if not exists
  2347. if not playerHitTracking[steamID] then
  2348. playerHitTracking[steamID] = {
  2349. hits = {},
  2350. lastCleanup = currentTime
  2351. }
  2352. end
  2353.  
  2354. local hitData = playerHitTracking[steamID]
  2355.  
  2356. -- Add current hit
  2357. table.insert(hitData.hits, currentTime)
  2358.  
  2359. -- Count hits within the rapid hit window
  2360. local recentHits = 0
  2361. for _, hitTime in ipairs(hitData.hits) do
  2362. if currentTime - hitTime <= RAPID_HIT_WINDOW then
  2363. recentHits = recentHits + 1
  2364. end
  2365. end
  2366.  
  2367. -- Apply rapid hit bonus based on hit count
  2368. if recentHits >= 4 then
  2369. extraChance = extraChance + 0.40 -- 40% extra for 4+ rapid hits
  2370. elseif recentHits >= 3 then
  2371. extraChance = extraChance + 0.25 -- 25% extra for 3+ rapid hits
  2372. elseif recentHits >= 2 then
  2373. extraChance = extraChance + 0.15 -- 15% extra for 2+ rapid hits
  2374. end
  2375. end
  2376.  
  2377. -- Apply extra chance if any modifiers are present
  2378. if extraChance > 0 then
  2379. shouldRagdoll = math.random() < extraChance
  2380. end
  2381. end
  2382.  
  2383. -- Execute ragdoll if shouldRagdoll is true
  2384. if shouldRagdoll then
  2385. rads(target)
  2386. end
  2387. end
  2388. end
  2389. end
  2390. end)
  2391.  
  2392. hook.Add("EntityTakeDamage", "stundmg", function(target, dmginfo)
  2393. if IsValid(target) and not target.fake then -- target.pain = target.pain + 7
  2394. if IsValid(dmginfo:GetAttacker()) then
  2395. local attacker = dmginfo:GetAttacker()
  2396. local inflictor = dmginfo:GetInflictor()
  2397. if IsValid(attacker) and IsValid(inflictor) then if attacker:IsPlayer() and attacker:GetActiveWeapon() and attacker:GetActiveWeapon():IsValid() then if attacker:GetActiveWeapon():GetClass() == "weapon_stunstick" then Stun(target) end end end
  2398. end
  2399. end
  2400. end)
  2401.  
  2402. -- Removed duplicate damage interruption hook
  2403. -- hook.Add("EntityTakeDamage", "RADS.RagdollDamageInterrupt", function(target, dmginfo)
  2404. -- local owner = nil
  2405. -- if IsValid(target) and target:IsRagdoll() then
  2406. -- owner = target:GetNWEntity("owner") or target:GetNWEntity('deadbodyowner')
  2407. -- elseif IsValid(target) and target:IsPlayer() then
  2408. -- owner = target
  2409. -- end
  2410. --
  2411. -- if IsValid(owner) and owner:IsPlayer() and owner:GetNWBool("radsfa") then
  2412. -- owner.takingDamage = true
  2413. -- end
  2414. -- end)
  2415.  
  2416. function Seizure(ent) -- target.pain = target.pain + 20
  2417. if ent:IsRagdoll() then
  2418. local seizuret = math.random(1, 6)
  2419. local iterrr = seizuret * 10
  2420. RagdollOwner(ent):ChatPrint("Cramps")
  2421. timer.Create("seizuret_" .. ent:EntIndex(), seizuret, 1, function() end)
  2422. timer.Create("seizuret_" .. ent:EntIndex(), 0.1, iterrr, function() ent:GetPhysicsObjectNum(1):SetVelocity(ent:GetPhysicsObjectNum(1):GetVelocity() + Vector(math.random(-45, 45), math.random(-45, 45), 0)) end)
  2423. end
  2424. end
  2425.  
  2426. function Stun(Entity)
  2427. if Entity:IsPlayer() then
  2428. rads(Entity)
  2429. local stuntime = math.random(1, 15)
  2430. local iter = stuntime * 10
  2431. timer.Create("StunTime" .. Entity:EntIndex(), stuntime, 1, function() end)
  2432. local radsrag = Entity:GetNWEntity("player_ragdoll")
  2433. if not IsValid(radsrag) then return end
  2434. timer.Create("StunEffect" .. Entity:EntIndex(), 0.1, iter, function()
  2435. local rand = math.random(1, 2)
  2436. if rand == 2 then end
  2437. radsrag:GetPhysicsObjectNum(1):SetVelocity(radsrag:GetPhysicsObjectNum(1):GetVelocity() + Vector(math.random(-85, 85), math.random(-85, 85), 0)) -- Entity:Say('#drop')
  2438. radsrag:EmitSound("ambient/energy/spark2.wav")
  2439. end)
  2440. end
  2441. end
  2442.  
  2443. function RADS_Epilepsy(Entity)
  2444. if Entity:IsPlayer() then
  2445. local rag = Entity:GetNWEntity('player_ragdoll')
  2446. if IsValid(rag) then return end
  2447. rads(Entity)
  2448. local mineptime, maxeptime = GetConVar('rads_mineptime'):GetInt(), GetConVar('rads_maxeptime'):GetInt()
  2449. local eptime = math.random(mineptime, maxeptime)
  2450. local iterr = eptime * 10
  2451. timer.Create("Epilepsy" .. Entity:EntIndex(), eptime, 1, function() end)
  2452. local radsrag = Entity:GetNWEntity("player_ragdoll")
  2453. if not IsValid(radsrag) then return end
  2454. timer.Create("EpilepsyM" .. Entity:EntIndex(), 0.1, iterr, function()
  2455. local rand = math.random(1, 2)
  2456. if rand == 2 then end
  2457. radsrag:GetPhysicsObjectNum(1):SetVelocity(radsrag:GetPhysicsObjectNum(1):GetVelocity() + Vector(math.random(-125, 125), math.random(-125, 125), 0)) -- Entity:Say('#drop')
  2458. end)
  2459.  
  2460. radsrag:EmitSound("lol.wav")
  2461. end
  2462. end
  2463.  
  2464. concommand.Add('rads_epil', function(ply) RADS_Epilepsy(ply) end)
  2465. function RADS_RagdollCollision(ragdoll, collisionData)
  2466. if GetConVar('rads_doorbreach'):GetBool() then
  2467. local collidedEntity = collisionData.HitEntity
  2468. if IsValid(collidedEntity) and (collidedEntity:GetClass() == "prop_door_rotating" or collidedEntity:GetClass() == "func_door") then
  2469. local ragdollVelocity = collisionData.OurOldVelocity:Length()
  2470. if ragdollVelocity >= 450 then BreachDoor(ragdoll, collidedEntity, collisionData) end
  2471. end
  2472. end
  2473. end
  2474.  
  2475. hook.Add("PlayerInitialSpawn", "rads-knocked-callback", function(ply)
  2476. ply:AddCallback("PhysicsCollide", function(phys, data) hook.Run("Player Collide", ply, data.HitEntity, data) end)
  2477. net.Start("RADS.CHATSAY")
  2478. net.Send(ply)
  2479. end)
  2480.  
  2481. hook.Add("Player Collide", "rads-knocked", function(ply, hitEnt, data)
  2482. if GetConVar('rads_propknock'):GetBool() and not ply:HasGodMode() and data.Speed > 8000 or not GetConVar('rads_propknock'):GetBool() and not ply:HasGodMode() and data.Speed >= math.max(5000, 8000 / hitEnt:GetPhysicsObject():GetMass() * 20) and not ply.fake and hitEnt:IsPlayerHolding() and hitEnt:GetVelocity():Length() > 1500 then
  2483. timer.Simple(0, function()
  2484. if not IsValid(ply) or ply.fake then return end
  2485. if hook.Run("Should Fake Collide", ply, hitEnt, data) == false then return end
  2486. rads(ply)
  2487. RADS.PainSound(ply)
  2488. end)
  2489. end
  2490. end)
  2491.  
  2492. -- Enhanced Player Collide hook based on reference addon
  2493. hook.Add("Player Collide", "homigrad-fake", function(ply, hitEnt, data)
  2494. if not ply:HasGodMode() and not ply.fake then
  2495. local speed = data.Speed
  2496. local threshold = 6000
  2497.  
  2498. -- Calculate speed threshold based on entity mass if it has physics
  2499. -- Use much higher base values and ensure minimum threshold
  2500. if IsValid(hitEnt) and IsValid(hitEnt:GetPhysicsObject()) then
  2501. threshold = math.max(5000, 8000 / hitEnt:GetPhysicsObject():GetMass() * 20)
  2502. end
  2503.  
  2504. -- Additional check: only ragdoll if the entity is moving fast or player is moving very fast
  2505. local entityVelocity = IsValid(hitEnt) and hitEnt:GetVelocity():Length() or 0
  2506. local shouldRagdoll = speed > threshold and (entityVelocity > 200 or speed > threshold * 1.5)
  2507.  
  2508. if shouldRagdoll then
  2509. timer.Simple(0, function()
  2510. if IsValid(ply) and not ply.fake then
  2511. rads(ply)
  2512. end
  2513. end)
  2514. end
  2515. end
  2516. end)
  2517.  
  2518. util.AddNetworkString('nodraw_helmet')
  2519. function CreateArmor(ragdoll, info)
  2520. local item = JMod.ArmorTable[info.name]
  2521. if not item then return end
  2522. local Index = ragdoll:LookupBone(item.bon)
  2523. if not Index then return end
  2524. local Pos, Ang = (ply or ragdoll):GetBonePosition(Index)
  2525. if not Pos then return end
  2526. local ent = ents.Create(item.ent)
  2527. local Right, Forward, Up = Ang:Right(), Ang:Forward(), Ang:Up()
  2528. Pos = Pos + Right * item.pos.x + Forward * item.pos.y + Up * item.pos.z
  2529. Ang:RotateAroundAxis(Right, item.ang.p)
  2530. Ang:RotateAroundAxis(Up, item.ang.y)
  2531. Ang:RotateAroundAxis(Forward, item.ang.r)
  2532. ent.IsArmor = true
  2533. ent:SetPos(Pos)
  2534. ent:SetAngles(Ang)
  2535. local color = info.col
  2536. ent:SetColor(Color(color.r, color.g, color.b, color.a))
  2537. ent:Spawn() -- timer.Simple(.1,function()
  2538. ent:SetCollisionGroup(COLLISION_GROUP_IN_VEHICLE) -- ent:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
  2539. if IsValid(ent:GetPhysicsObject()) then
  2540. ent:GetPhysicsObject():SetMaterial("Armorflesh")
  2541. ent:GetPhysicsObject():SetMass(1)
  2542. ent:GetPhysicsObject():EnableCollisions(false)
  2543. end
  2544.  
  2545. timer.Simple(0.1, function()
  2546. local ply = RagdollOwner(ragdoll) -- end)
  2547. if item.bon == "ValveBiped.Bip01_Head1" and ply and IsValid(ply) and ply:IsPlayer() then
  2548. net.Start("nodraw_helmet")
  2549. net.WriteEntity(ent)
  2550. net.Send(ply)
  2551. end
  2552. end)
  2553.  
  2554. constraint.Weld(ent, ragdoll, 0, ragdoll:TranslateBoneToPhysBone(Index), 0, true, false)
  2555. ragdoll:DeleteOnRemove(ent)
  2556. return ent
  2557. end
  2558.  
  2559. local function Remove(self, ply)
  2560. if self.override then return end
  2561. self.ragdoll.armors[self.armorID] = nil
  2562. JMod.RemoveArmorByID(ply, self.armorID, true)
  2563. end
  2564.  
  2565. -- RemoveRag function moved earlier in the file
  2566.  
  2567. hook.Add("OnEntityCreated", "ragdoor", function(ent) if IsValid(ent) and ent:IsRagdoll() and GetConVar('rads_doorbreach'):GetBool() then ent:AddCallback("PhysicsCollide", function(ragdoll, collisionData) RADS_RagdollCollision(ragdoll, collisionData) end) end end)
  2568.  
  2569. -- Ragdoll tackling system based on reference addon
  2570. hook.Add('Think', 'RADS_RagdollTackleCheck', function()
  2571. for _, ragdoll in pairs(ents.FindByClass('prop_ragdoll')) do
  2572. if IsValid(ragdoll) then
  2573. local velocity = ragdoll:GetVelocity():Length()
  2574.  
  2575. if velocity > 200 then
  2576. ragdoll:SetCollisionGroup(COLLISION_GROUP_NONE)
  2577.  
  2578. -- Trace ahead of the ragdoll to detect potential player collisions
  2579. local traceData = {
  2580. start = ragdoll:GetPos(),
  2581. endpos = ragdoll:GetPos() + ragdoll:GetVelocity():GetNormalized() * 40,
  2582. filter = ragdoll
  2583. }
  2584. local trace = util.TraceLine(traceData)
  2585.  
  2586. if IsValid(trace.Entity) and trace.Entity:IsPlayer() then
  2587. local ragdollOwner = ragdoll:GetNWEntity("owner")
  2588. if IsValid(ragdollOwner) and ragdollOwner ~= trace.Entity and not trace.Entity.fake then
  2589. -- Trigger tackling on the hit player
  2590. rads(trace.Entity)
  2591. end
  2592. end
  2593. else
  2594. ragdoll:SetCollisionGroup(COLLISION_GROUP_WEAPON)
  2595. end
  2596. end
  2597. end
  2598. end)
  2599. function BreachDoor(ragdoll, door, collisionData)
  2600. if GetConVar('rads_doorbreach'):GetBool() then
  2601. local ragdollVelocity = collisionData.OurOldVelocity:GetNormalized()
  2602. local breachedDoor = ents.Create("prop_physics")
  2603. breachedDoor:SetModel(door:GetModel())
  2604. breachedDoor:SetSkin(door:GetSkin())
  2605. for key, value in pairs(door:GetBodyGroups()) do
  2606. breachedDoor:SetBodygroup(value.id, door:GetBodygroup(value.id))
  2607. end
  2608.  
  2609. breachedDoor:SetPos(door:GetPos())
  2610. breachedDoor:SetAngles(door:GetAngles())
  2611. door:Remove()
  2612. breachedDoor:Spawn()
  2613. local phys = breachedDoor:GetPhysicsObject()
  2614. if IsValid(phys) then
  2615. local force = ragdollVelocity * phys:GetMass()
  2616. phys:ApplyForceCenter(force, collisionData.HitPos)
  2617. end
  2618. end
  2619. end
  2620.  
  2621. concommand.Add("rads_god", function(ply, cmd, args)
  2622. if IsValid(ply) and ply:IsSuperAdmin() then
  2623. if ply:HasGodMode() then
  2624. ply:GodDisable()
  2625. ply:PrintMessage(HUD_PRINTTALK, "God disabled.")
  2626. else
  2627. ply:GodEnable()
  2628. ply:EmitSound("restains.wav", 150, 200, 1, CHAN_ITEM)
  2629. ply:PrintMessage(HUD_PRINTTALK, "God enabled.")
  2630. end
  2631. end
  2632. end)
  2633.  
  2634.  
  2635.  
  2636. -- Initialize cumulative damage tracking tables
  2637. if not RADS_CumulativeDamage then
  2638. RADS_CumulativeDamage = {}
  2639. end
  2640.  
  2641. -- Function to clean up old damage entries
  2642. local function RADS_CleanupOldDamage()
  2643. local currentTime = CurTime()
  2644. for targetID, data in pairs(RADS_CumulativeDamage) do
  2645. if currentTime - data.lastHit > 0.5 then
  2646. RADS_CumulativeDamage[targetID] = nil
  2647. end
  2648. end
  2649. end
  2650.  
  2651. -- Gore System: Head Explosion on Extreme Damage
  2652. hook.Add("EntityTakeDamage", "RADS_GoreSystem", function(target, dmginfo)
  2653. -- Check if gore system is enabled
  2654. if not GetConVar('rads_gore_enable'):GetBool() then return end
  2655.  
  2656. local damage = dmginfo:GetDamage()
  2657. local baseDamageThreshold = GetConVar('rads_gore_head_damage_threshold'):GetFloat()
  2658.  
  2659. -- Apply damage type multipliers
  2660. local damageThreshold = baseDamageThreshold
  2661. local damageType = dmginfo:GetDamageType()
  2662.  
  2663. -- Explicitly exclude fall and crush damage from triggering head explosions
  2664. if bit.band(damageType, DMG_FALL) > 0 or bit.band(damageType, DMG_CRUSH) > 0 or dmginfo:IsFallDamage() then
  2665. return -- Fall and crush damage should not cause head explosions
  2666. end
  2667.  
  2668. local isBuckshot = bit.band(damageType, DMG_BUCKSHOT) > 0
  2669.  
  2670. if bit.band(damageType, DMG_CLUB) > 0 then
  2671. damageThreshold = baseDamageThreshold * 2 -- DMG_CLUB needs 2x damage
  2672. elseif bit.band(damageType, DMG_BULLET) > 0 then
  2673. damageThreshold = baseDamageThreshold * 1.0 -- DMG_BULLET uses base threshold
  2674. elseif isBuckshot then
  2675. damageThreshold = baseDamageThreshold * 1.0 -- DMG_BUCKSHOT uses base threshold
  2676. else
  2677. -- Other damage types don't trigger head explosions (including DMG_BLAST)
  2678. return
  2679. end
  2680.  
  2681. -- Handle cumulative damage for buckshot and bullet (shotgun pellets and bullets)
  2682. local finalDamage = damage
  2683. local isBulletOrBuckshot = bit.band(damageType, DMG_BULLET) > 0 or isBuckshot
  2684. if isBulletOrBuckshot then
  2685. -- Clean up old damage entries periodically
  2686. RADS_CleanupOldDamage()
  2687.  
  2688. local targetID = target:EntIndex()
  2689. local currentTime = CurTime()
  2690.  
  2691. -- Initialize or update cumulative damage for this target
  2692. if not RADS_CumulativeDamage[targetID] then
  2693. RADS_CumulativeDamage[targetID] = {
  2694. totalDamage = 0,
  2695. lastHit = currentTime
  2696. }
  2697. end
  2698.  
  2699. local damageData = RADS_CumulativeDamage[targetID]
  2700.  
  2701. -- Check if this hit is within the 0.5 second window
  2702. if currentTime - damageData.lastHit <= 0.5 then
  2703. -- Add to cumulative damage
  2704. damageData.totalDamage = damageData.totalDamage + damage
  2705. damageData.lastHit = currentTime
  2706. finalDamage = damageData.totalDamage
  2707. else
  2708. -- Reset cumulative damage (new damage window)
  2709. damageData.totalDamage = damage
  2710. damageData.lastHit = currentTime
  2711. finalDamage = damage
  2712. end
  2713. end
  2714.  
  2715. -- Check if target is valid and damage is above threshold
  2716. if not IsValid(target) or finalDamage < damageThreshold then return end
  2717.  
  2718. local isPlayer = target:IsPlayer()
  2719. local isRagdoll = target:IsRagdoll()
  2720.  
  2721. -- Completely disable head explosions for ragdolls
  2722. if isRagdoll then return end
  2723.  
  2724. -- Only process players (ragdolls are now excluded)
  2725. if not isPlayer then return end
  2726.  
  2727. -- Check if head already exploded to prevent multiple explosions
  2728. if target.headExploded then return end
  2729.  
  2730. -- For players, check if damage is to the head
  2731. local damagePosition = dmginfo:GetDamagePosition()
  2732. local bodyPart = target:LastHitGroup(damagePosition)
  2733.  
  2734. if bodyPart == HITGROUP_HEAD and not target.fake then
  2735. -- Mark head as exploded to prevent multiple explosions
  2736. target.headExploded = true
  2737.  
  2738. -- Trigger head explosion
  2739. RADS_TriggerHeadExplosion(target, dmginfo)
  2740.  
  2741. -- Force instant death
  2742. target:Kill()
  2743. target:SetHealth(0)
  2744. end
  2745. end)
  2746.  
  2747. hook.Add("EntityTakeDamage", "falldamage", function(target, dmginfo)
  2748. if GetConVar('rads_ragonfall'):GetBool() then
  2749. if target:IsPlayer() and dmginfo:IsFallDamage() and not target.fake then
  2750. -- Increased threshold from 8 to 25 for more realistic ragdolling
  2751. if dmginfo:GetDamage() > 25 then
  2752. target:EmitSound("NPC_Barnacle.BreakNeck", 511, 200, 1, CHAN_ITEM)
  2753. rads(target)
  2754.  
  2755. -- NEW: Trigger adrenaline based on fall damage severity
  2756. if SERVER then
  2757. local fallDamage = dmginfo:GetDamage()
  2758. local adrenalineGain = 0
  2759.  
  2760. -- More reasonable thresholds
  2761. if fallDamage >= 50 then
  2762. adrenalineGain = 35 -- Severe fall (was 50)
  2763. RADS_TriggerSevereFallEffects(target, fallDamage)
  2764. -- Trigger internal bleeding for extreme fall damage
  2765. RADS_TriggerInternalBleeding(target, "fall", fallDamage)
  2766. elseif fallDamage >= 40 then
  2767. adrenalineGain = 15 -- Medium fall damage (was 15)
  2768. RADS_TriggerLightFallEffects(target, fallDamage)
  2769. elseif fallDamage >= 35 then
  2770. adrenalineGain = 25 -- High fall damage (was 30)
  2771. RADS_TriggerModerateFallEffects(target, fallDamage)
  2772. -- Trigger internal bleeding for severe fall damage
  2773. RADS_TriggerInternalBleeding(target, "fall", fallDamage)
  2774. elseif fallDamage >= 25 then
  2775. adrenalineGain = 10 -- Light fall damage (was 8)
  2776. end
  2777.  
  2778. if adrenalineGain > 0 then
  2779. UpdateAdrenaline(target, adrenalineGain)
  2780. if GetConVar("developer"):GetInt() > 0 then
  2781. print("[FALL DAMAGE] Triggered adrenaline for " .. target:Name() .. " with gain: " .. adrenalineGain)
  2782. end
  2783. end
  2784. end
  2785. end
  2786. end
  2787. end
  2788. end)
  2789.  
  2790. -- NEW: Fall damage effect functions
  2791. if SERVER then
  2792. -- Network strings for fall effects
  2793.  
  2794.  
  2795. function RADS_TriggerSevereFallEffects(ply, damage)
  2796. if not IsValid(ply) or not ply:IsPlayer() then return end
  2797.  
  2798. -- Add significant pain
  2799. ply.pain = (ply.pain or 0) + math.min(damage * 2, 80)
  2800.  
  2801. -- Severe speed reduction for 15-25 seconds
  2802. local duration = math.Rand(15, 25)
  2803. ply.fallSpeedDebuff = true
  2804. ply:SetWalkSpeed(20) -- Very slow
  2805. ply:SetRunSpeed(35)
  2806.  
  2807. -- Timer to restore speed
  2808. timer.Create("RADS_FallSpeedRestore_" .. ply:UserID(), duration, 1, function()
  2809. if IsValid(ply) and ply:IsPlayer() then
  2810. ply.fallSpeedDebuff = false
  2811. -- Restore normal speed (check if other debuffs are active)
  2812. if not (ply:GetNWBool("RADS_LeftLegDislocated") or ply:GetNWBool("RADS_RightLegDislocated") or
  2813. ply:GetNWBool("RADS_LeftLegBroken") or ply:GetNWBool("RADS_RightLegBroken")) then
  2814. ply:SetWalkSpeed(160)
  2815. ply:SetRunSpeed(250)
  2816. end
  2817. end
  2818. end)
  2819. end
  2820.  
  2821. function RADS_TriggerModerateFallEffects(ply, damage)
  2822. if not IsValid(ply) or not ply:IsPlayer() then return end
  2823.  
  2824. -- Add moderate pain
  2825. ply.pain = (ply.pain or 0) + math.min(damage * 1, 50)
  2826.  
  2827. -- Moderate speed reduction for 8-15 seconds
  2828. local duration = math.Rand(8, 15)
  2829. ply.fallSpeedDebuff = true
  2830. ply:SetWalkSpeed(40)
  2831. ply:SetRunSpeed(70)
  2832.  
  2833. -- Timer to restore speed
  2834. timer.Create("RADS_FallSpeedRestore_" .. ply:UserID(), duration, 1, function()
  2835. if IsValid(ply) and ply:IsPlayer() then
  2836. ply.fallSpeedDebuff = false
  2837. if not (ply:GetNWBool("RADS_LeftLegDislocated") or ply:GetNWBool("RADS_RightLegDislocated") or
  2838. ply:GetNWBool("RADS_LeftLegBroken") or ply:GetNWBool("RADS_RightLegBroken")) then
  2839. ply:SetWalkSpeed(160)
  2840. ply:SetRunSpeed(250)
  2841. end
  2842. end
  2843. end)
  2844. end
  2845.  
  2846. function RADS_TriggerLightFallEffects(ply, damage)
  2847. if not IsValid(ply) or not ply:IsPlayer() then return end
  2848.  
  2849. -- Add light pain
  2850. ply.pain = (ply.pain or 0) + math.min(damage * 0.6, 30)
  2851.  
  2852. -- Light speed reduction for 5-10 seconds
  2853. local duration = math.Rand(5, 10)
  2854. ply.fallSpeedDebuff = true
  2855. ply:SetWalkSpeed(80)
  2856. ply:SetRunSpeed(120)
  2857.  
  2858. -- Timer to restore speed
  2859. timer.Create("RADS_FallSpeedRestore_" .. ply:UserID(), duration, 1, function()
  2860. if IsValid(ply) and ply:IsPlayer() then
  2861. ply.fallSpeedDebuff = false
  2862. if not (ply:GetNWBool("RADS_LeftLegDislocated") or ply:GetNWBool("RADS_RightLegDislocated") or
  2863. ply:GetNWBool("RADS_LeftLegBroken") or ply:GetNWBool("RADS_RightLegBroken")) then
  2864. ply:SetWalkSpeed(160)
  2865. ply:SetRunSpeed(250)
  2866. end
  2867. end
  2868. end)
  2869. end
  2870.  
  2871. -- Gore System: Head Explosion Function
  2872. function RADS_TriggerHeadExplosion(target, dmginfo)
  2873. if not IsValid(target) then return end
  2874.  
  2875. local isPlayer = target:IsPlayer()
  2876.  
  2877. -- COMPLETELY DISABLE HEAD EXPLOSIONS FOR RAGDOLLS
  2878. if not isPlayer then
  2879. return -- Block all ragdoll head explosions
  2880. end
  2881.  
  2882. local ragdoll = nil
  2883.  
  2884. -- Ensure instant death for players
  2885. if isPlayer then
  2886. -- Destroy brain organ to cause instant death
  2887. target.Organs = target.Organs or {
  2888. ["brain"] = 1,
  2889. ["heart"] = 1,
  2890. ["lungs"] = 1,
  2891. ["liver"] = 1,
  2892. ["stomach"] = 1,
  2893. ["intestines"] = 1,
  2894. ["spine"] = 1
  2895. }
  2896. target.Organs["brain"] = 0
  2897.  
  2898. -- Set consciousness to 0 for instant unconsciousness
  2899. if target.consciousness then
  2900. target.consciousness = 0
  2901. target:SetNWInt("PlayerConsciousness", 0)
  2902. target.Otrub = true
  2903. target:SetNWBool("Otrub", true)
  2904. target.consciousnessBasedUnconscious = false
  2905. end
  2906.  
  2907. -- Force instant death
  2908. target:Kill()
  2909. target:SetHealth(0)
  2910. end
  2911.  
  2912. -- Get or create ragdoll
  2913. if isPlayer then
  2914. -- If player, get their ragdoll (create one if needed)
  2915. ragdoll = target:GetNWEntity('player_ragdoll')
  2916. if not IsValid(ragdoll) then
  2917. -- Player doesn't have ragdoll yet, create one
  2918. rads(target)
  2919. timer.Simple(0.1, function()
  2920. if IsValid(target) then
  2921. ragdoll = target:GetNWEntity('player_ragdoll')
  2922. if IsValid(ragdoll) then
  2923. RADS_ProcessHeadExplosion(ragdoll, dmginfo)
  2924. end
  2925. end
  2926. end)
  2927. return
  2928. end
  2929. else
  2930. -- Target is already a ragdoll
  2931. ragdoll = target
  2932. end
  2933.  
  2934. if IsValid(ragdoll) then
  2935. RADS_ProcessHeadExplosion(ragdoll, dmginfo)
  2936. end
  2937. end
  2938.  
  2939. function RADS_ProcessHeadExplosion(ragdoll, dmginfo)
  2940. if not IsValid(ragdoll) then return end
  2941.  
  2942. -- Prevent multiple explosions on same ragdoll
  2943. if ragdoll.headExploded then return end
  2944. ragdoll.headExploded = true
  2945.  
  2946. -- Find head bone
  2947. local headBone = ragdoll:LookupBone("ValveBiped.Bip01_Head1")
  2948. if not headBone then return end
  2949.  
  2950. local headPos, headAng = ragdoll:GetBonePosition(headBone)
  2951. if not headPos then return end
  2952.  
  2953. -- Mark head as exploded for gore system (prevents conflicts with other head hiding code)
  2954. ragdoll.goreHeadExploded = true
  2955.  
  2956. -- Hide the head bone by scaling it to 0
  2957. ragdoll:ManipulateBoneScale(headBone, Vector(0, 0, 0))
  2958.  
  2959. -- Find neck bone for stump placement
  2960. local neckBone = ragdoll:LookupBone("ValveBiped.Bip01_Neck1")
  2961. local stumpPos = headPos
  2962. local stumpAng = headAng
  2963.  
  2964. -- ADJUSTABLE STUMP POSITIONING VARIABLES (modify these to adjust stump placement)
  2965. local STUMP_OFFSET_FORWARD = 7.45 -- Forward/backward offset from neck bone
  2966. local STUMP_OFFSET_RIGHT = 3 -- Left/right offset from neck bone
  2967. local STUMP_OFFSET_UP = -0.7 -- Up/down offset from neck bone (positive = up)
  2968. local STUMP_ANGLE_PITCH = -253 -- Pitch angle adjustment (positive = nose down)
  2969. local STUMP_ANGLE_YAW = 0 -- Yaw angle adjustment (positive = turn right)
  2970. local STUMP_ANGLE_ROLL = 75 -- Roll angle adjustment (positive = roll right)
  2971.  
  2972. -- Always use neck bone for positioning if available, with improved fallback
  2973. if neckBone then
  2974. stumpPos, stumpAng = ragdoll:GetBonePosition(neckBone)
  2975.  
  2976. -- Apply adjustable positioning offsets
  2977. local forward = stumpAng:Forward() * STUMP_OFFSET_FORWARD
  2978. local right = stumpAng:Right() * STUMP_OFFSET_RIGHT
  2979. local up = stumpAng:Up() * STUMP_OFFSET_UP
  2980.  
  2981. stumpPos = stumpPos + forward + right + up
  2982.  
  2983. -- Apply adjustable angle offsets
  2984. stumpAng = stumpAng + Angle(STUMP_ANGLE_PITCH, STUMP_ANGLE_YAW, STUMP_ANGLE_ROLL)
  2985. else
  2986. -- Fallback: Use head position but adjust for better stump placement
  2987. -- Move the stump slightly down from head position to simulate neck cut
  2988. stumpPos = headPos + Vector(0, 0, -8) -- Move down 8 units from head
  2989.  
  2990. -- Try to get a more appropriate angle for the stump
  2991. -- Use the ragdoll's root bone angle as reference if available
  2992. local spineBone = ragdoll:LookupBone("ValveBiped.Bip01_Spine4") or ragdoll:LookupBone("ValveBiped.Bip01_Spine3") or ragdoll:LookupBone("ValveBiped.Bip01_Spine2")
  2993. if spineBone then
  2994. local _, spineAng = ragdoll:GetBonePosition(spineBone)
  2995. if spineAng then
  2996. stumpAng = spineAng + Angle(STUMP_ANGLE_PITCH, STUMP_ANGLE_YAW, STUMP_ANGLE_ROLL)
  2997. end
  2998. else
  2999. -- Last resort: use head angle with adjustments
  3000. stumpAng = headAng + Angle(STUMP_ANGLE_PITCH, STUMP_ANGLE_YAW, STUMP_ANGLE_ROLL)
  3001. end
  3002. end
  3003.  
  3004. -- Create gore stump model
  3005. local stump = ents.Create("prop_physics")
  3006. if IsValid(stump) then
  3007. stump:SetModel("models/mosi/fnv/props/character/headcap.mdl")
  3008. stump:SetPos(stumpPos)
  3009. stump:SetAngles(stumpAng)
  3010. stump:Spawn()
  3011.  
  3012. -- Make stump non-collidable and attach to ragdoll
  3013. stump:SetCollisionGroup(COLLISION_GROUP_IN_VEHICLE)
  3014. if IsValid(stump:GetPhysicsObject()) then
  3015. stump:GetPhysicsObject():SetMass(1)
  3016. stump:GetPhysicsObject():EnableCollisions(false)
  3017. end
  3018.  
  3019. -- Weld stump to neck bone
  3020. if neckBone then
  3021. local neckPhysBone = ragdoll:TranslateBoneToPhysBone(neckBone)
  3022. if neckPhysBone >= 0 then
  3023. constraint.Weld(stump, ragdoll, 0, neckPhysBone, 0, true, false)
  3024. end
  3025. end
  3026.  
  3027. -- Clean up stump when ragdoll is removed
  3028. ragdoll:DeleteOnRemove(stump)
  3029.  
  3030. -- Store stump reference for blood effects
  3031. ragdoll.goreStump = stump
  3032. end
  3033.  
  3034. -- Play explosion sound with random pitch
  3035. local pitch = math.random(95, 115)
  3036. ragdoll:EmitSound("head_explodie_01.mp3", 75, pitch, 1, CHAN_AUTO)
  3037.  
  3038. -- Create initial blood explosion effect
  3039. RADS_CreateBloodExplosion(ragdoll, headPos)
  3040.  
  3041. -- Start continuous blood stream from stump
  3042. RADS_StartBloodStream(ragdoll, stumpPos)
  3043. end
  3044.  
  3045. function RADS_CreateBloodExplosion(ragdoll, position)
  3046. if not IsValid(ragdoll) or not position then return end
  3047.  
  3048. -- Create dramatic arterial blood spurts in all directions
  3049. for i = 1, 15 do
  3050. local direction = VectorRand():GetNormalized()
  3051. direction.z = math.abs(direction.z) * 0.8 -- Bias upward for arterial spurting
  3052.  
  3053. -- Create high-pressure arterial blood spurts
  3054. local effectdata = EffectData()
  3055. effectdata:SetOrigin(position + VectorRand() * 3)
  3056. effectdata:SetNormal(direction)
  3057. effectdata:SetMagnitude(math.random(80, 150)) -- Increased magnitude for arterial spurting
  3058. effectdata:SetScale(math.random(3, 6)) -- Larger scale for dramatic effect
  3059. util.Effect("BloodImpact", effectdata)
  3060.  
  3061. -- Add secondary smaller spurts for realism
  3062. if math.random(1, 3) == 1 then
  3063. local secondaryDir = (direction + VectorRand() * 0.3):GetNormalized()
  3064. local secondaryEffect = EffectData()
  3065. secondaryEffect:SetOrigin(position + direction * math.random(10, 25))
  3066. secondaryEffect:SetNormal(secondaryDir)
  3067. secondaryEffect:SetMagnitude(math.random(40, 80))
  3068. secondaryEffect:SetScale(math.random(1, 3))
  3069. util.Effect("BloodImpact", secondaryEffect)
  3070. end
  3071. end
  3072.  
  3073. -- Create extensive blood decals around the explosion
  3074. for i = 1, 12 do
  3075. local traceDir = VectorRand():GetNormalized()
  3076. local trace = util.TraceLine({
  3077. start = position,
  3078. endpos = position + traceDir * 200, -- Increased range for arterial spurting
  3079. filter = ragdoll
  3080. })
  3081.  
  3082. if trace.Hit then
  3083. util.Decal("Blood", trace.HitPos + trace.HitNormal, trace.HitPos - trace.HitNormal)
  3084.  
  3085. -- Add additional blood splatters nearby
  3086. for j = 1, 3 do
  3087. local nearbyPos = trace.HitPos + VectorRand() * 15
  3088. local nearbyTrace = util.TraceLine({
  3089. start = nearbyPos + Vector(0, 0, 10),
  3090. endpos = nearbyPos - Vector(0, 0, 10),
  3091. filter = ragdoll
  3092. })
  3093. if nearbyTrace.Hit then
  3094. util.Decal("Blood", nearbyTrace.HitPos + nearbyTrace.HitNormal, nearbyTrace.HitPos - nearbyTrace.HitNormal)
  3095. end
  3096. end
  3097. end
  3098. end
  3099. end
  3100.  
  3101. function RADS_StartBloodStream(ragdoll, stumpPos)
  3102. if not IsValid(ragdoll) or not stumpPos then return end
  3103.  
  3104. local bloodDuration = GetConVar('rads_gore_blood_duration'):GetFloat()
  3105. local timerName = "RADS_BloodStream_" .. ragdoll:EntIndex()
  3106.  
  3107. -- Create blood stream effect every 0.5 seconds
  3108. timer.Create(timerName, 0.5, bloodDuration * 2, function()
  3109. if not IsValid(ragdoll) then
  3110. timer.Remove(timerName)
  3111. return
  3112. end
  3113.  
  3114. -- Update stump position if it exists
  3115. local currentStumpPos = stumpPos
  3116. if IsValid(ragdoll.goreStump) then
  3117. currentStumpPos = ragdoll.goreStump:GetPos()
  3118. end
  3119.  
  3120. -- Create downward blood stream
  3121. local effectdata = EffectData()
  3122. effectdata:SetOrigin(currentStumpPos)
  3123. effectdata:SetNormal(Vector(0, 0, -1))
  3124. effectdata:SetMagnitude(30)
  3125. effectdata:SetScale(1.5)
  3126. util.Effect("BloodImpact", effectdata)
  3127.  
  3128. -- Create blood decal on ground
  3129. local trace = util.TraceLine({
  3130. start = currentStumpPos,
  3131. endpos = currentStumpPos + Vector(0, 0, -200),
  3132. filter = ragdoll
  3133. })
  3134.  
  3135. if trace.Hit then
  3136. util.Decal("Blood", trace.HitPos + trace.HitNormal, trace.HitPos - trace.HitNormal)
  3137. end
  3138. end)
  3139.  
  3140. -- Clean up timer when ragdoll is removed
  3141. ragdoll:CallOnRemove("CleanupBloodStream", function()
  3142. timer.Remove(timerName)
  3143. end)
  3144. end
  3145.  
  3146. -- Cleanup cumulative damage tracking when ragdoll is removed
  3147. if IsValid(ragdoll) then
  3148. ragdoll:CallOnRemove("CleanupCumulativeDamage", function()
  3149. local targetID = ragdoll:EntIndex()
  3150. if RADS_CumulativeDamage and RADS_CumulativeDamage[targetID] then
  3151. RADS_CumulativeDamage[targetID] = nil
  3152. end
  3153. end)
  3154. end
  3155. end
  3156.  
  3157. -- Cleanup cumulative damage tracking when player disconnects
  3158. hook.Add("PlayerDisconnected", "RADS_CleanupCumulativeDamage", function(ply)
  3159. if not RADS_CumulativeDamage then return end
  3160.  
  3161. local playerID = ply:EntIndex()
  3162. if RADS_CumulativeDamage[playerID] then
  3163. RADS_CumulativeDamage[playerID] = nil
  3164. end
  3165.  
  3166. -- Also cleanup any ragdoll associated with this player
  3167. local ragdoll = ply:GetNWEntity('player_ragdoll')
  3168. if IsValid(ragdoll) then
  3169. local ragdollID = ragdoll:EntIndex()
  3170. if RADS_CumulativeDamage[ragdollID] then
  3171. RADS_CumulativeDamage[ragdollID] = nil
  3172. end
  3173. end
  3174. end)
  3175.  
  3176. -- Blast damage ragdoll system with knockback
  3177. hook.Add("EntityTakeDamage", "RADS_BlastRagdoll", function(target, dmginfo)
  3178. if IsValid(target) and target:IsPlayer() and not target.fake and dmginfo:IsDamageType(DMG_BLAST) then
  3179. -- Check if player is already ragdolled to prevent double ragdolling
  3180. local existingRag = target:GetNWEntity('player_ragdoll')
  3181. if IsValid(existingRag) then
  3182. -- Player is already ragdolled, just apply knockback to existing ragdoll
  3183. local forceMagnitude = math.min(dmginfo:GetDamage() * 15, 35000)
  3184. local explosionPosition = dmginfo:GetDamagePosition()
  3185.  
  3186. for i = 0, existingRag:GetPhysicsObjectCount() - 1 do
  3187. local physobj = existingRag:GetPhysicsObjectNum(i)
  3188. if IsValid(physobj) then
  3189. local bonePosition = physobj:GetPos()
  3190. local forceDirection = (bonePosition - explosionPosition):GetNormalized()
  3191. -- Add some upward force for more realistic blast effect
  3192. forceDirection = forceDirection + Vector(0, 0, 0.6)
  3193. forceDirection:Normalize()
  3194. physobj:ApplyForceOffset(forceDirection * forceMagnitude, bonePosition)
  3195. end
  3196. end
  3197. return
  3198. end
  3199.  
  3200. -- Player is not ragdolled, ragdoll them first
  3201. rads(target)
  3202.  
  3203. -- Store damage info before timer since dmginfo becomes invalid
  3204. local damage = dmginfo:GetDamage()
  3205. local explosionPos = dmginfo:GetDamagePosition()
  3206.  
  3207. -- Apply knockback after a short delay to ensure ragdoll is created
  3208. timer.Simple(0.1, function()
  3209. if IsValid(target) then
  3210. local rag = target:GetNWEntity('player_ragdoll')
  3211. if IsValid(rag) then
  3212. local forceMagnitude = math.min(damage * 30, 2000)
  3213.  
  3214. for i = 0, rag:GetPhysicsObjectCount() - 1 do
  3215. local physobj = rag:GetPhysicsObjectNum(i)
  3216. if IsValid(physobj) then
  3217. local bonePosition = physobj:GetPos()
  3218. local forceDirection = (bonePosition - explosionPos):GetNormalized()
  3219. -- Add some upward force for more realistic blast effect
  3220. forceDirection = forceDirection + Vector(0, 0, 0.3)
  3221. forceDirection:Normalize()
  3222. physobj:ApplyForceOffset(forceDirection * forceMagnitude, bonePosition)
  3223. end
  3224. end
  3225. end
  3226. end
  3227. end)
  3228. end
  3229. end)
  3230.  
  3231. -- Club and slash damage knockback system (only applies to existing ragdolls)
  3232. -- Directional Club/Slash Knockback System - Applies force only to specific hit body parts
  3233. hook.Add("EntityTakeDamage", "RADS_ClubSlashKnockback", function(target, dmginfo)
  3234. if IsValid(target) and target:IsPlayer() and not target.fake and (dmginfo:IsDamageType(DMG_CLUB) or dmginfo:IsDamageType(DMG_SLASH)) then
  3235. -- Prevent multiple knockback applications with cooldown
  3236. target.lastKnockbackTime = target.lastKnockbackTime or 0
  3237. if CurTime() - target.lastKnockbackTime < 0.5 then
  3238. return -- Ignore if knockback was applied recently
  3239. end
  3240. target.lastKnockbackTime = CurTime()
  3241.  
  3242. -- Store damage info before timer since dmginfo becomes invalid
  3243. local hitGroup = target:LastHitGroup() or HITGROUP_GENERIC
  3244. local damage = dmginfo:GetDamage()
  3245. local attacker = dmginfo:GetAttacker()
  3246. local damagePos = dmginfo:GetDamagePosition()
  3247.  
  3248. -- Calculate precise directional force from attacker to hit location
  3249. local forceDirection = Vector(0, 0, 0)
  3250. if IsValid(attacker) and attacker:IsPlayer() then
  3251. -- Get attacker's aim direction for more realistic knockback
  3252. local attackerEyes = attacker:EyePos()
  3253. local targetHitPos = target:GetBonePosition(target:LookupBone("ValveBiped.Bip01_Spine2") or 0)
  3254.  
  3255. -- Adjust target position based on hit group for accuracy
  3256. if hitGroup == HITGROUP_HEAD then
  3257. targetHitPos = target:GetBonePosition(target:LookupBone("ValveBiped.Bip01_Head1") or 0)
  3258. elseif hitGroup == HITGROUP_LEFTARM then
  3259. targetHitPos = target:GetBonePosition(target:LookupBone("ValveBiped.Bip01_L_UpperArm") or 0)
  3260. elseif hitGroup == HITGROUP_RIGHTARM then
  3261. targetHitPos = target:GetBonePosition(target:LookupBone("ValveBiped.Bip01_R_UpperArm") or 0)
  3262. elseif hitGroup == HITGROUP_LEFTLEG then
  3263. targetHitPos = target:GetBonePosition(target:LookupBone("ValveBiped.Bip01_L_Thigh") or 0)
  3264. elseif hitGroup == HITGROUP_RIGHTLEG then
  3265. targetHitPos = target:GetBonePosition(target:LookupBone("ValveBiped.Bip01_R_Thigh") or 0)
  3266. end
  3267.  
  3268. forceDirection = (targetHitPos - attackerEyes):GetNormalized()
  3269. elseif damagePos and damagePos ~= Vector(0,0,0) then
  3270. forceDirection = (target:GetPos() - damagePos):GetNormalized()
  3271. else
  3272. -- Fallback to attacker's forward direction
  3273. if IsValid(attacker) then
  3274. forceDirection = attacker:GetAngles():Forward()
  3275. else
  3276. forceDirection = target:GetAngles():Forward()
  3277. end
  3278. end
  3279.  
  3280. -- Store knockback data for later application
  3281. target.pendingKnockback = {
  3282. hitGroup = hitGroup,
  3283. damage = damage,
  3284. forceDirection = forceDirection,
  3285. attacker = attacker,
  3286. timestamp = CurTime()
  3287. }
  3288.  
  3289. -- Apply knockback with delay to ensure ragdoll is fully created
  3290. timer.Simple(0.15, function()
  3291. if IsValid(target) and target.pendingKnockback and (CurTime() - target.pendingKnockback.timestamp) < 2.5 then
  3292. local rag = target:GetNWEntity('player_ragdoll')
  3293. if IsValid(rag) then
  3294. local knockbackData = target.pendingKnockback
  3295.  
  3296. -- Calculate force magnitude based on damage (further reduced to prevent neck breaking)
  3297. local baseForceMagnitude = math.min(knockbackData.damage * 120, 10000)
  3298.  
  3299. -- Apply force only to the specific hit body part for realistic effect
  3300. local targetBoneName = ""
  3301. local forceMultiplier = 1.0
  3302. local upwardComponent = 0.2 -- Reduced upward force
  3303.  
  3304. if knockbackData.hitGroup == HITGROUP_HEAD then
  3305. targetBoneName = "ValveBiped.Bip01_Head1"
  3306. forceMultiplier = 1.4 -- Further reduced from 2.5 to make neck breaking harder
  3307. upwardComponent = 0.3 -- Further reduced upward snap
  3308. elseif knockbackData.hitGroup == HITGROUP_CHEST then
  3309. targetBoneName = "ValveBiped.Bip01_Spine2"
  3310. forceMultiplier = 2.0 -- Reduced
  3311. upwardComponent = 0.3
  3312. elseif knockbackData.hitGroup == HITGROUP_STOMACH then
  3313. targetBoneName = "ValveBiped.Bip01_Spine1"
  3314. forceMultiplier = 1.8 -- Reduced
  3315. upwardComponent = 0.2
  3316. elseif knockbackData.hitGroup == HITGROUP_LEFTARM then
  3317. targetBoneName = "ValveBiped.Bip01_L_UpperArm"
  3318. forceMultiplier = 2.2 -- Reduced from 4.0
  3319. upwardComponent = 0.3
  3320. elseif knockbackData.hitGroup == HITGROUP_RIGHTARM then
  3321. targetBoneName = "ValveBiped.Bip01_R_UpperArm"
  3322. forceMultiplier = 2.2 -- Reduced from 4.0
  3323. upwardComponent = 0.3
  3324. elseif knockbackData.hitGroup == HITGROUP_LEFTLEG then
  3325. targetBoneName = "ValveBiped.Bip01_L_Thigh"
  3326. forceMultiplier = 2.0 -- Reduced
  3327. upwardComponent = 0.4
  3328. elseif knockbackData.hitGroup == HITGROUP_RIGHTLEG then
  3329. targetBoneName = "ValveBiped.Bip01_R_Thigh"
  3330. forceMultiplier = 2.0 -- Reduced
  3331. upwardComponent = 0.4
  3332. else
  3333. -- Generic hit - apply to torso
  3334. targetBoneName = "ValveBiped.Bip01_Spine2"
  3335. forceMultiplier = 1.5 -- Reduced
  3336. upwardComponent = 0.2
  3337. end
  3338.  
  3339. -- Find and apply force to the specific bone
  3340. local boneIndex = rag:LookupBone(targetBoneName)
  3341. if boneIndex then
  3342. local physBone = rag:TranslateBoneToPhysBone(boneIndex)
  3343. if physBone >= 0 then
  3344. local physobj = rag:GetPhysicsObjectNum(physBone)
  3345. if IsValid(physobj) then
  3346. -- Validate and fix force direction to prevent zero force
  3347. local finalForce = knockbackData.forceDirection
  3348.  
  3349. -- Check if force direction is invalid (zero vector or very small)
  3350. if not finalForce or finalForce:Length() < 0.1 then
  3351. -- Fallback: use attacker's forward direction or random horizontal direction
  3352. if IsValid(knockbackData.attacker) then
  3353. finalForce = knockbackData.attacker:GetAngles():Forward()
  3354. else
  3355. finalForce = Vector(math.random(-1, 1), math.random(-1, 1), 0):GetNormalized()
  3356. end
  3357. if GetConVar("developer"):GetInt() > 0 then
  3358. print("[DIRECTIONAL KNOCKBACK] Fixed invalid force direction for " .. target:Name())
  3359. end
  3360. end
  3361.  
  3362. -- Add upward component
  3363. finalForce = finalForce + Vector(0, 0, upwardComponent)
  3364. finalForce:Normalize()
  3365.  
  3366. local forceMagnitude = baseForceMagnitude * forceMultiplier
  3367.  
  3368. -- Ensure minimum force magnitude
  3369. if forceMagnitude < 500 then
  3370. forceMagnitude = 7500
  3371. if GetConVar("developer"):GetInt() > 0 then
  3372. print("[DIRECTIONAL KNOCKBACK] Applied minimum force to " .. target:Name())
  3373. end
  3374. end
  3375.  
  3376. -- Apply force using ApplyForceCenter for more controlled physics
  3377. physobj:ApplyForceCenter(finalForce * forceMagnitude)
  3378.  
  3379. -- For head hits, add even more minimal rotational effect to prevent neck breaking
  3380. if knockbackData.hitGroup == HITGROUP_HEAD then
  3381. timer.Simple(0.05, function()
  3382. if IsValid(physobj) then
  3383. -- Add extremely gentle rotational force
  3384. local rotForce = knockbackData.forceDirection:Cross(Vector(0, 0, 1)) * forceMagnitude * 0.05
  3385. physobj:ApplyForceCenter(rotForce)
  3386. end
  3387. end)
  3388. end
  3389.  
  3390. -- Debug print
  3391. if GetConVar("developer"):GetInt() > 0 then
  3392. print("[DIRECTIONAL KNOCKBACK] Applied " .. math.Round(forceMagnitude) .. " force to " .. target:Name() .. "'s " .. targetBoneName .. " (hitgroup: " .. knockbackData.hitGroup .. ")")
  3393. end
  3394. end
  3395. end
  3396. end
  3397.  
  3398. -- Clear the pending knockback
  3399. target.pendingKnockback = nil
  3400. else
  3401. -- Player didn't ragdoll, clear pending knockback
  3402. target.pendingKnockback = nil
  3403. end
  3404. end
  3405. end)
  3406. end
  3407. end)
  3408.  
  3409.  
  3410.  
  3411. hook.Add("PlayerDeathSound", "DeFlatline", function()
  3412. return true
  3413. end)
  3414.  
  3415. local noise = Sound("death.wav")
  3416. hook.Add("PlayerDeath", "NewSound", function(vic, unused1, unused2) vic:EmitSound(noise) end)
  3417. hook.Add("PlayerTick", "CheckPlayerSpeed", function(ply, mv)
  3418. if GetConVar('rads_fallonspeedlimit'):GetBool() and not ply.fake then
  3419. local speed = ply:GetVelocity():Length()
  3420. local rag = ply:GetNWEntity('player_ragdoll')
  3421. if not IsValid(rag) and ply:GetMoveType() ~= MOVETYPE_NOCLIP and not ply:HasGodMode() and ply:GetMoveType() ~= MOVETYPE_OBSERVER then
  3422. -- Use ConVar for velocity threshold
  3423. local velocityThreshold = GetConVar("rads_fallonspeedlimit_threshold"):GetInt()
  3424. if speed >= velocityThreshold then
  3425. rads(ply)
  3426. RADS.FreeFall(ply)
  3427. end
  3428. end
  3429. end
  3430. end)
  3431.  
  3432. -- Optimized ragdoll velocity adrenaline system with performance scaling
  3433. local lastRagdollVelocityCheck = 0
  3434. local ragdollVelocityInterval = 0.5
  3435.  
  3436. hook.Add("Think", "RADS_RagdollVelocityAdrenaline", function()
  3437. if not SERVER then return end
  3438.  
  3439. local cheapEffectsCvar = GetConVar("rads_cheapeffects")
  3440. local cheapEffects = cheapEffectsCvar and cheapEffectsCvar:GetInt() or 0
  3441.  
  3442. -- Performance scaling based on cheapeffects
  3443. if cheapEffects >= 2 then
  3444. ragdollVelocityInterval = 2.0 -- Very slow updates for max performance
  3445. elseif cheapEffects >= 1 then
  3446. ragdollVelocityInterval = 1.0 -- Moderate updates
  3447. else
  3448. ragdollVelocityInterval = 0.5 -- Normal updates
  3449. end
  3450.  
  3451. if CurTime() - lastRagdollVelocityCheck < ragdollVelocityInterval then return end
  3452. lastRagdollVelocityCheck = CurTime()
  3453.  
  3454. -- Skip entirely if cheapeffects is 2
  3455. if cheapEffects >= 2 then return end
  3456.  
  3457. for _, ply in pairs(player.GetAll()) do
  3458. if IsValid(ply) and ply:Alive() then
  3459. local rag = ply:GetNWEntity('player_ragdoll')
  3460.  
  3461. -- Check if non-ragdolled player is fully submerged and auto-ragdoll them
  3462. if not IsValid(rag) then
  3463. local headPos = ply:GetPos() + Vector(0, 0, 64) -- Approximate head position
  3464. local waterLevel = util.PointContents(headPos)
  3465. local isUnderwater = bit.band(waterLevel, CONTENTS_WATER) ~= 0
  3466.  
  3467. if isUnderwater then
  3468. -- Force ragdoll the player when fully submerged
  3469. rads(ply, false) -- Auto-ragdoll when submerged
  3470. end
  3471. return -- Skip drowning logic for non-ragdolled players
  3472. end
  3473.  
  3474. if IsValid(rag) then
  3475. -- Initialize cooldown tracking
  3476. ply.ragdollAdrenalineCooldown = ply.ragdollAdrenalineCooldown or 0
  3477.  
  3478. -- Check if cooldown has passed (15 seconds)
  3479. if CurTime() >= ply.ragdollAdrenalineCooldown then
  3480. -- Get main physics object (torso)
  3481. local mainPhys = rag:GetPhysicsObjectNum(1)
  3482. if IsValid(mainPhys) then
  3483. local velocity = mainPhys:GetVelocity():Length()
  3484.  
  3485. -- Trigger adrenaline based on velocity thresholds
  3486. if velocity >= 1100 then
  3487. UpdateAdrenaline(ply, 35) -- Extreme velocity
  3488. ply.ragdollAdrenalineCooldown = CurTime() + 15
  3489. if cheapEffects == 0 then
  3490. if GetConVar("developer"):GetInt() > 0 then
  3491. print("[ADRENALINE DEBUG] " .. ply:Name() .. " ragdoll extreme velocity: " .. math.Round(velocity) .. " units/s")
  3492. end
  3493. end
  3494. elseif velocity >= 850 then
  3495. UpdateAdrenaline(ply, 25) -- High velocity
  3496. ply.ragdollAdrenalineCooldown = CurTime() + 15
  3497. if cheapEffects == 0 then
  3498. if GetConVar("developer"):GetInt() > 0 then
  3499. print("[ADRENALINE DEBUG] " .. ply:Name() .. " ragdoll high velocity: " .. math.Round(velocity) .. " units/s")
  3500. end
  3501. end
  3502. elseif velocity >= 650 then
  3503. UpdateAdrenaline(ply, 15) -- Medium velocity
  3504. ply.ragdollAdrenalineCooldown = CurTime() + 15
  3505. if cheapEffects == 0 then
  3506. if GetConVar("developer"):GetInt() > 0 then
  3507. print("[ADRENALINE DEBUG] " .. ply:Name() .. " ragdoll medium velocity: " .. math.Round(velocity) .. " units/s")
  3508. end
  3509. end
  3510. end
  3511. end
  3512. end
  3513. end
  3514. end
  3515. end
  3516. end)
  3517.  
  3518. -- Drowning System for Ragdolls
  3519. local drowningPlayers = {}
  3520. local lastDrowningCheck = 0
  3521. local drowningCheckInterval = 0.5
  3522. local swimmingPlayers = {} -- Track swimming state
  3523. local lastSplashTime = {} -- Track splash sound cooldown
  3524. local lastSwimTime = {} -- Track individual swimming cooldown (0.5 seconds)
  3525.  
  3526. hook.Add("Think", "RADS_DrowningSystem", function()
  3527. if not SERVER then return end
  3528.  
  3529. if CurTime() - lastDrowningCheck < drowningCheckInterval then return end
  3530. lastDrowningCheck = CurTime()
  3531.  
  3532. for _, ply in pairs(player.GetAll()) do
  3533. if IsValid(ply) and ply:Alive() then
  3534. local rag = ply:GetNWEntity('player_ragdoll')
  3535. if IsValid(rag) then
  3536. -- Initialize drowning data
  3537. if not drowningPlayers[ply] then
  3538. drowningPlayers[ply] = {
  3539. submergedTime = 0,
  3540. isDrowning = false,
  3541. drowningStartTime = 0,
  3542. soundPlaying = false
  3543. }
  3544. end
  3545.  
  3546. local drowningData = drowningPlayers[ply]
  3547.  
  3548. -- Check if ragdoll head is underwater
  3549. local head = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Head1")))
  3550. if IsValid(head) then
  3551. local headPos = head:GetPos()
  3552. local waterLevel = util.PointContents(headPos)
  3553. local isUnderwater = bit.band(waterLevel, CONTENTS_WATER) ~= 0
  3554.  
  3555. if isUnderwater then
  3556. drowningData.submergedTime = drowningData.submergedTime + drowningCheckInterval
  3557.  
  3558. -- Start drowning after 20 seconds underwater
  3559. if drowningData.submergedTime >= GetConVar("rads_drowning_threshold_time"):GetFloat() and not drowningData.isDrowning then
  3560. drowningData.isDrowning = true
  3561. drowningData.drowningStartTime = CurTime()
  3562. -- Set player drowning state for damage.lua
  3563. ply.isDrowning = true
  3564. ply.drowningStartTime = CurTime()
  3565.  
  3566. -- Start drowning sound with continuous looping (clientside only)
  3567. if not drowningData.soundPlaying then
  3568. if SERVER then
  3569. net.Start("PlayDrowningSound")
  3570. net.Send(ply)
  3571. end
  3572. drowningData.soundPlaying = true
  3573.  
  3574. -- Create looping timer for drowning sound (loop every 10 seconds)
  3575. timer.Create("DrowningLoop_" .. ply:EntIndex(), 10, 0, function()
  3576. if IsValid(ply) and drowningData and drowningData.soundPlaying and drowningData.isDrowning and ply:Alive() then
  3577. if SERVER then
  3578. net.Start("PlayDrowningSound")
  3579. net.Send(ply)
  3580. end
  3581. else
  3582. timer.Remove("DrowningLoop_" .. ply:EntIndex())
  3583. end
  3584. end)
  3585. end
  3586. end
  3587.  
  3588. -- Apply continuous sinking physics with gravity-like force
  3589. -- But reduce sinking significantly when player is actively swimming
  3590. local sinkForce = GetConVar("rads_drowning_sink_force"):GetFloat()
  3591. local isSwimming = swimmingPlayers[ply] and (CurTime() - swimmingPlayers[ply]) < 0.5
  3592. local sinkMultiplier = isSwimming and 0.05 or 1.0 -- Reduce sinking by 95% when swimming
  3593.  
  3594. for i = 0, rag:GetPhysicsObjectCount() - 1 do
  3595. local phys = rag:GetPhysicsObjectNum(i)
  3596. if IsValid(phys) then
  3597. local pos = phys:GetPos()
  3598. local waterCheck = util.PointContents(pos)
  3599. if bit.band(waterCheck, CONTENTS_WATER) ~= 0 then
  3600. local mass = phys:GetMass()
  3601. -- Apply continuous downward force proportional to mass (like gravity) - reduced intensity
  3602. local gravityForce = Vector(0, 0, -sinkForce * mass * 0.003 * sinkMultiplier) -- Reduced from 0.005 to 0.003 (40% additional reduction for even slower sinking)
  3603. phys:ApplyForceCenter(gravityForce)
  3604. -- Set buoyancy based on swimming state
  3605. phys:SetBuoyancyRatio(isSwimming and 0.8 or 0)
  3606. -- Add underwater drag for more realistic movement
  3607. local velocity = phys:GetVelocity()
  3608. local drag = velocity * -0.2
  3609. phys:ApplyForceCenter(drag)
  3610. end
  3611. end
  3612. end
  3613. else
  3614. -- Reset drowning when head is above water
  3615. if drowningData.isDrowning or drowningData.submergedTime > 0 then
  3616. -- Stop looping sound immediately (clientside)
  3617. if drowningData.soundPlaying then
  3618. timer.Remove("DrowningLoop_" .. ply:EntIndex())
  3619. timer.Remove("DrowningFadeOut_" .. ply:EntIndex())
  3620.  
  3621. if SERVER then
  3622. net.Start("StopDrowningSound")
  3623. net.Send(ply)
  3624. end
  3625. drowningData.soundPlaying = false
  3626. end
  3627.  
  3628. drowningData.submergedTime = 0
  3629. drowningData.isDrowning = false
  3630. drowningData.drowningStartTime = 0
  3631. -- Reset player drowning state for damage.lua
  3632. ply.isDrowning = false
  3633. ply.drowningStartTime = nil
  3634. end
  3635. end
  3636.  
  3637. -- Check for death after 1 minute of drowning
  3638. if drowningData.isDrowning then
  3639. local drowningTime = CurTime() - drowningData.drowningStartTime
  3640. if drowningTime >= GetConVar("rads_drowning_death_time"):GetFloat() then
  3641. -- Kill player after drowning time limit
  3642. ply:Kill()
  3643. -- Clean up drowning data
  3644. drowningPlayers[ply] = nil
  3645. timer.Remove("DrowningLoop_" .. ply:EntIndex())
  3646. timer.Remove("DrowningFadeOut_" .. ply:EntIndex())
  3647. if SERVER then
  3648. net.Start("StopDrowningSound")
  3649. net.Send(ply)
  3650. end
  3651. end
  3652. end
  3653. end
  3654. end
  3655. else
  3656. -- Clean up drowning data for dead/invalid players
  3657. if drowningPlayers[ply] then
  3658. if drowningPlayers[ply].soundPlaying then
  3659. if SERVER then
  3660. net.Start("StopDrowningSound")
  3661. net.Send(ply)
  3662. end
  3663. end
  3664. drowningPlayers[ply] = nil
  3665. timer.Remove("DrowningLoop_" .. ply:EntIndex())
  3666. timer.Remove("DrowningFadeOut_" .. ply:EntIndex())
  3667. end
  3668. end
  3669. end
  3670. end)
  3671.  
  3672. -- Swimming Controls for Ragdolls (Individual Arm Control)
  3673. hook.Add("PlayerButtonDown", "RADS_SwimmingControls", function(ply, button)
  3674. if not IsValid(ply) or not ply:Alive() then return end
  3675.  
  3676. local rag = ply:GetNWEntity('player_ragdoll')
  3677. if not IsValid(rag) then return end
  3678.  
  3679. -- Check if ragdoll is in water first
  3680. local head = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Head1")))
  3681. if not IsValid(head) then return end
  3682.  
  3683. local headPos = head:GetPos()
  3684. local waterLevel = util.PointContents(headPos)
  3685. local isUnderwater = bit.band(waterLevel, CONTENTS_WATER) ~= 0
  3686.  
  3687. if not isUnderwater then return end
  3688.  
  3689. -- Check swimming cooldown (0.5 seconds between strokes)
  3690. if lastSwimTime[ply] and (CurTime() - lastSwimTime[ply]) < 0.5 then
  3691. return -- Prevent swimming spam
  3692. end
  3693.  
  3694. local swimForce = GetConVar("rads_drowning_swim_force"):GetFloat()
  3695.  
  3696. -- Update swimming cooldown
  3697. lastSwimTime[ply] = CurTime()
  3698.  
  3699. -- Track swimming state for sinking reduction
  3700. swimmingPlayers[ply] = CurTime()
  3701.  
  3702. -- Play splash sound with cooldown
  3703. if not lastSplashTime[ply] or (CurTime() - lastSplashTime[ply]) > 1.0 then
  3704. ply:EmitSound("physics/water/water_impact_soft" .. math.random(1,3) .. ".wav", 60, math.random(90, 110), 0.7)
  3705. lastSplashTime[ply] = CurTime()
  3706. end
  3707.  
  3708. -- LMB - Left Arm Swimming
  3709. if button == MOUSE_LEFT then
  3710. local leftArmBone = rag:LookupBone("ValveBiped.Bip01_L_UpperArm")
  3711. if leftArmBone then
  3712. local leftArmPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(leftArmBone))
  3713. if IsValid(leftArmPhys) then
  3714. -- Get player's view direction for more intuitive swimming
  3715. local viewAngles = ply:EyeAngles()
  3716. local armDirection = viewAngles:Forward()
  3717.  
  3718. -- Add slight left bias for left arm swimming
  3719. local leftBias = viewAngles:Right() * -0.3
  3720. armDirection = (armDirection + leftBias):GetNormalized()
  3721.  
  3722. -- Apply stronger force to multiple body parts for better swimming
  3723. local torso = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine")))
  3724. local pelvis = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Pelvis")))
  3725. local chest = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine2")))
  3726.  
  3727. -- Enhanced force multiplier for more dynamic swimming
  3728. local forceMultiplier = 3.2 -- Reduced from 4.0 to 3.2 for more balanced swimming strokes
  3729. -- Add upward component to help surface above water
  3730. local upwardForce = Vector(0, 0, swimForce * 2.8) -- Reduced from 3.5 to 2.8 for more balanced upward movement
  3731.  
  3732. if IsValid(torso) then
  3733. torso:ApplyForceCenter(armDirection * swimForce * forceMultiplier + upwardForce)
  3734. -- Add angular velocity for more natural swimming motion
  3735. local torqueForce = viewAngles:Right() * swimForce * 0.3
  3736. torso:ApplyTorqueCenter(torqueForce)
  3737. end
  3738. if IsValid(pelvis) then
  3739. pelvis:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.8 + upwardForce * 0.7)
  3740. -- Add slight rotational force to pelvis
  3741. local pelvisTorque = viewAngles:Up() * swimForce * 0.2
  3742. pelvis:ApplyTorqueCenter(pelvisTorque)
  3743. end
  3744. if IsValid(chest) then
  3745. chest:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.6 + upwardForce * 0.5)
  3746. -- Add chest rotation for swimming stroke
  3747. local chestTorque = viewAngles:Right() * swimForce * -0.2
  3748. chest:ApplyTorqueCenter(chestTorque)
  3749. end
  3750.  
  3751. -- Add leg paddling for left arm swimming
  3752. local leftThighBone = rag:LookupBone("ValveBiped.Bip01_L_Thigh")
  3753. local rightThighBone = rag:LookupBone("ValveBiped.Bip01_R_Thigh")
  3754. local leftCalfBone = rag:LookupBone("ValveBiped.Bip01_L_Calf")
  3755. local rightCalfBone = rag:LookupBone("ValveBiped.Bip01_R_Calf")
  3756.  
  3757. if leftThighBone then
  3758. local leftThighPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(leftThighBone))
  3759. if IsValid(leftThighPhys) then
  3760. leftThighPhys:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.4 + upwardForce * 0.3)
  3761. end
  3762. end
  3763. if rightThighBone then
  3764. local rightThighPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rightThighBone))
  3765. if IsValid(rightThighPhys) then
  3766. rightThighPhys:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.4 + upwardForce * 0.3)
  3767. end
  3768. end
  3769. if leftCalfBone then
  3770. local leftCalfPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(leftCalfBone))
  3771. if IsValid(leftCalfPhys) then
  3772. leftCalfPhys:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.3 + upwardForce * 0.2)
  3773. end
  3774. end
  3775. if rightCalfBone then
  3776. local rightCalfPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rightCalfBone))
  3777. if IsValid(rightCalfPhys) then
  3778. rightCalfPhys:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.3 + upwardForce * 0.2)
  3779. end
  3780. end
  3781. end
  3782. end
  3783. end
  3784.  
  3785. -- RMB - Right Arm Swimming
  3786. if button == MOUSE_RIGHT then
  3787. local rightArmBone = rag:LookupBone("ValveBiped.Bip01_R_UpperArm")
  3788. if rightArmBone then
  3789. local rightArmPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rightArmBone))
  3790. if IsValid(rightArmPhys) then
  3791. -- Get player's view direction for more intuitive swimming
  3792. local viewAngles = ply:EyeAngles()
  3793. local armDirection = viewAngles:Forward()
  3794.  
  3795. -- Add slight right bias for right arm swimming
  3796. local rightBias = viewAngles:Right() * 0.3
  3797. armDirection = (armDirection + rightBias):GetNormalized()
  3798.  
  3799. -- Apply stronger force to multiple body parts for better swimming
  3800. local torso = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine")))
  3801. local pelvis = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Pelvis")))
  3802. local chest = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine2")))
  3803.  
  3804. -- Enhanced force multiplier for more dynamic swimming
  3805. local forceMultiplier = 3.2 -- Reduced from 4.0 to 3.2 for more balanced swimming strokes
  3806. -- Add upward component to help surface above water
  3807. local upwardForce = Vector(0, 0, swimForce * 2.8) -- Reduced from 3.5 to 2.8 for more balanced upward movement
  3808.  
  3809. if IsValid(torso) then
  3810. torso:ApplyForceCenter(armDirection * swimForce * forceMultiplier + upwardForce)
  3811. -- Add angular velocity for more natural swimming motion
  3812. local torqueForce = viewAngles:Right() * swimForce * -0.3 -- Opposite direction for right arm
  3813. torso:ApplyTorqueCenter(torqueForce)
  3814. end
  3815. if IsValid(pelvis) then
  3816. pelvis:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.8 + upwardForce * 0.7)
  3817. -- Add slight rotational force to pelvis
  3818. local pelvisTorque = viewAngles:Up() * swimForce * -0.2 -- Opposite direction for right arm
  3819. pelvis:ApplyTorqueCenter(pelvisTorque)
  3820. end
  3821. if IsValid(chest) then
  3822. chest:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.6 + upwardForce * 0.5)
  3823. -- Add chest rotation for swimming stroke
  3824. local chestTorque = viewAngles:Right() * swimForce * 0.2 -- Opposite direction for right arm
  3825. chest:ApplyTorqueCenter(chestTorque)
  3826. end
  3827.  
  3828. -- Add leg paddling for right arm swimming
  3829. local leftThighBone = rag:LookupBone("ValveBiped.Bip01_L_Thigh")
  3830. local rightThighBone = rag:LookupBone("ValveBiped.Bip01_R_Thigh")
  3831. local leftCalfBone = rag:LookupBone("ValveBiped.Bip01_L_Calf")
  3832. local rightCalfBone = rag:LookupBone("ValveBiped.Bip01_R_Calf")
  3833.  
  3834. if leftThighBone then
  3835. local leftThighPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(leftThighBone))
  3836. if IsValid(leftThighPhys) then
  3837. leftThighPhys:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.4 + upwardForce * 0.3)
  3838. end
  3839. end
  3840. if rightThighBone then
  3841. local rightThighPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rightThighBone))
  3842. if IsValid(rightThighPhys) then
  3843. rightThighPhys:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.4 + upwardForce * 0.3)
  3844. end
  3845. end
  3846. if leftCalfBone then
  3847. local leftCalfPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(leftCalfBone))
  3848. if IsValid(leftCalfPhys) then
  3849. leftCalfPhys:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.3 + upwardForce * 0.2)
  3850. end
  3851. end
  3852. if rightCalfBone then
  3853. local rightCalfPhys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rightCalfBone))
  3854. if IsValid(rightCalfPhys) then
  3855. rightCalfPhys:ApplyForceCenter(armDirection * swimForce * forceMultiplier * 0.3 + upwardForce * 0.2)
  3856. end
  3857. end
  3858. end
  3859. end
  3860. end
  3861. end)
  3862.  
  3863. -- Death state cleanup for drowning system
  3864. hook.Add("PlayerDeath", "RADS_DrowningDeathCleanup", function(victim, inflictor, attacker)
  3865. if not IsValid(victim) then return end
  3866.  
  3867. -- Clean up drowning data and sounds on death
  3868. if drowningPlayers[victim] then
  3869. if drowningPlayers[victim].soundPlaying then
  3870. victim:StopSound("drowning.ogg")
  3871. end
  3872. drowningPlayers[victim] = nil
  3873. end
  3874.  
  3875. -- Clean up swimming state tracking
  3876. swimmingPlayers[victim] = nil
  3877. lastSplashTime[victim] = nil
  3878. lastSwimTime[victim] = nil
  3879.  
  3880. -- Remove all drowning-related timers
  3881. timer.Remove("DrowningLoop_" .. victim:EntIndex())
  3882. timer.Remove("DrowningFadeOut_" .. victim:EntIndex())
  3883.  
  3884. -- Reset drowning state variables
  3885. victim.isDrowning = false
  3886. victim.drowningStartTime = nil
  3887. end)
  3888.  
  3889. -- Respawn cleanup for drowning system
  3890. hook.Add("PlayerSpawn", "RADS_DrowningRespawnCleanup", function(ply)
  3891. if not IsValid(ply) then return end
  3892.  
  3893. -- Ensure clean state on respawn
  3894. if drowningPlayers[ply] then
  3895. drowningPlayers[ply] = nil
  3896. end
  3897.  
  3898. -- Clean up swimming state tracking
  3899. swimmingPlayers[ply] = nil
  3900. lastSplashTime[ply] = nil
  3901. lastSwimTime[ply] = nil
  3902.  
  3903. -- Remove any lingering timers
  3904. timer.Remove("DrowningLoop_" .. ply:EntIndex())
  3905. timer.Remove("DrowningFadeOut_" .. ply:EntIndex())
  3906.  
  3907. -- Stop any drowning sounds
  3908. ply:StopSound("drowning.ogg")
  3909.  
  3910. -- Reset drowning state variables
  3911. ply.isDrowning = false
  3912. ply.drowningStartTime = nil
  3913. end)
  3914.  
  3915. hook.Add("RADSLoadout", "RADSLuaLoad", function(ply)
  3916. if not ply.resetinv and not GetConVar('rads_loadoutusinglua'):GetBool() then
  3917. return
  3918. else
  3919. RADS_ReturnPlyInfo(ply)
  3920. RADS_RestoreEzArmor(ply)
  3921. end
  3922.  
  3923. if RADS.IsTTT() then
  3924. ply:Give('weapon_zm_improvised')
  3925. ply:Give('weapon_zm_carry')
  3926. ply:Give('weapon_ttt_unarmed')
  3927. end
  3928. end)
  3929.  
  3930. hook.Add("PreCleanupMap", "getupnoobis", function()
  3931. for i, v in pairs(player.GetAll()) do
  3932. if v.brokenspine then v:Kill() end
  3933. if v.fake then rads(v) end
  3934. end
  3935. end)
  3936.  
  3937. util.AddNetworkString("ebal_chellele")
  3938. hook.Add("PlayerSwitchWeapon", "fakewep", function(ply, oldwep, newwep)
  3939. rag = ply:GetNWEntity('player_ragdoll')
  3940. if IsValid(rag) then
  3941. if ply.fake then
  3942. if IsValid(ply.Info.ActiveWeapon2) and IsValid(ply.wep) and ply.wep.Clip ~= nil and ply.wep.Amt ~= nil and ply.wep.AmmoType ~= nil then
  3943. ply.Info.ActiveWeapon2:SetClip1(ply.wep.Clip or 0)
  3944. ply:SetAmmo(ply.wep.Amt or 0, ply.wep.AmmoType or 0)
  3945. end
  3946.  
  3947. if table.HasValue(Guns, newwep:GetClass()) then
  3948. if IsValid(ply.wep) then
  3949. if _G.DespawnWeapon then
  3950. _G.DespawnWeapon(ply)
  3951. else
  3952. if GetConVar("developer"):GetInt() > 0 then
  3953. print("[RADS] ERROR: DespawnWeapon function not available globally at line 1770!")
  3954. end
  3955. end
  3956. end
  3957. ply:SetActiveWeapon(newwep)
  3958. ply.Info.ActiveWeapon = newwep
  3959. ply.curweapon = newwep:GetClass()
  3960. RADS_SavePlyInfo(ply)
  3961. ply:SetActiveWeapon(nil)
  3962. if _G.SpawnWeapon then
  3963. _G.SpawnWeapon(ply)
  3964. else
  3965. if GetConVar("developer"):GetInt() > 0 then
  3966. print("[RADS] ERROR: SpawnWeapon function not available globally at line 1776!")
  3967. end
  3968. end
  3969. ply.FakeShooting = true
  3970. else
  3971. if IsValid(ply.wep) then
  3972. if _G.DespawnWeapon then
  3973. _G.DespawnWeapon(ply)
  3974. else
  3975. if GetConVar("developer"):GetInt() > 0 then
  3976. print("[RADS] ERROR: DespawnWeapon function not available globally at line 1783!")
  3977. end
  3978. end
  3979. end
  3980. ply:SetActiveWeapon(nil)
  3981. ply.curweapon = nil
  3982. ply.FakeShooting = false
  3983. end
  3984.  
  3985. net.Start("ebal_chellele")
  3986. net.WriteEntity(ply)
  3987. net.WriteString(ply.curweapon or "")
  3988. net.Broadcast()
  3989. return true
  3990. end
  3991. end
  3992. end)
  3993.  
  3994. hook.Add("Player Think", "ragmovement", function(ply, time)
  3995. if not ply:Alive() then return end
  3996. local rag = ply:GetNWEntity('player_ragdoll')
  3997. if not IsValid(rag) or not ply:Alive() then return end
  3998. local walkTime = 1 -- rag:SetFlexWeight(5,0)
  3999. local eyeangs = ply:EyeAngles()
  4000. local head = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Head1")))
  4001.  
  4002. -- Rolling functionality for ragdolls
  4003. -- Roll left (A key) - but not while diving
  4004. if ply:KeyDown(IN_MOVELEFT) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not (rag.isDiving or false) and not ply.brokenupperspine then
  4005. local torso = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine")))
  4006. local pelvis = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Pelvis")))
  4007.  
  4008. if IsValid(torso) and IsValid(pelvis) then
  4009. -- Get the ragdoll's up vector to determine which way is "up"
  4010. local ragUp = torso:GetAngles():Up()
  4011. -- Only roll if the ragdoll is somewhat upright (not already on its side)
  4012. local upDot = ragUp:Dot(Vector(0,0,1))
  4013.  
  4014. -- Get world-aligned roll direction (left is -Y in Source)
  4015. local rollDirection = Vector(0, -1, 0)
  4016.  
  4017. -- Reduced force and torque values
  4018. local rollForce = 40
  4019. local rollTorque = 80
  4020.  
  4021. -- Apply downward force on the "high" side to initiate roll
  4022. local sideOffset = torso:GetPos() + Vector(0, -20, 0) -- Left side
  4023. torso:ApplyForceOffset(Vector(0, 0, -rollForce*2), sideOffset)
  4024.  
  4025. -- Apply gentle torque for rotation assistance
  4026. torso:ApplyTorqueCenter(Vector(rollTorque, 0, 0))
  4027. pelvis:ApplyTorqueCenter(Vector(rollTorque, 0, 0))
  4028.  
  4029. -- Apply main rolling force to the body center
  4030. torso:ApplyForceCenter(rollDirection * rollForce)
  4031. pelvis:ApplyForceCenter(rollDirection * rollForce)
  4032. end
  4033. end
  4034.  
  4035. -- Roll right (D key) - but not while diving
  4036. if ply:KeyDown(IN_MOVERIGHT) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not (rag.isDiving or false) and not ply.brokenupperspine then
  4037. local torso = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine")))
  4038. local pelvis = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Pelvis")))
  4039.  
  4040. if IsValid(torso) and IsValid(pelvis) then
  4041. -- Get the ragdoll's up vector to determine which way is "up"
  4042. local ragUp = torso:GetAngles():Up()
  4043. -- Only roll if the ragdoll is somewhat upright (not already on its side)
  4044. local upDot = ragUp:Dot(Vector(0,0,1))
  4045.  
  4046. -- Get world-aligned roll direction (right is +Y in Source)
  4047. local rollDirection = Vector(0, 1, 0)
  4048.  
  4049. -- Reduced force and torque values
  4050. local rollForce = 40
  4051. local rollTorque = 80
  4052.  
  4053. -- Apply downward force on the "high" side to initiate roll
  4054. local sideOffset = torso:GetPos() + Vector(0, 20, 0) -- Right side
  4055. torso:ApplyForceOffset(Vector(0, 0, -rollForce*2), sideOffset)
  4056.  
  4057. -- Apply gentle torque for rotation assistance
  4058. torso:ApplyTorqueCenter(Vector(-rollTorque, 0, 0))
  4059. pelvis:ApplyTorqueCenter(Vector(-rollTorque, 0, 0))
  4060.  
  4061. -- Apply main rolling force to the body center
  4062. torso:ApplyForceCenter(rollDirection * rollForce)
  4063. pelvis:ApplyForceCenter(rollDirection * rollForce)
  4064. end
  4065. end
  4066.  
  4067. if ply:KeyDown(IN_ATTACK) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then
  4068. local pos = ply:EyePos()
  4069. pos[3] = head:GetPos()[3]
  4070. if not ply.FakeShooting then
  4071. local phys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_L_Hand")))
  4072. local ang = ply:EyeAngles()
  4073. ang:RotateAroundAxis(eyeangs:Forward(), 90)
  4074. ang:RotateAroundAxis(eyeangs:Right(), 75)
  4075. -- Apply shock responsiveness factor
  4076. local responsivenessFactor = rag.responsivenessFactor or 1
  4077. local shadowparams = {
  4078. secondstoarrive = 0.4 / responsivenessFactor,
  4079. pos = head:GetPos() + eyeangs:Forward() * 50 + eyeangs:Right() * -5,
  4080. angle = ang,
  4081. maxangular = 670 * responsivenessFactor,
  4082. maxangulardamp = 600,
  4083. maxspeeddamp = 50,
  4084. maxspeed = 500 * responsivenessFactor,
  4085. teleportdistance = 0,
  4086. deltatime = 0.01,
  4087. }
  4088.  
  4089. phys:Wake()
  4090. phys:ComputeShadowControl(shadowparams)
  4091. end
  4092. end
  4093.  
  4094. -- Check if weapon is automatic using the Automatic table
  4095. if ply.curweapon and Automatic and Automatic[ply.curweapon] then
  4096. -- Debug: Print automatic weapon detection
  4097. if ply.curweapon == "wep_mann_hmcd_akm" then
  4098. if GetConVar("developer"):GetInt() > 0 then
  4099. print("[RADS DEBUG] AKM detected as AUTOMATIC, using KeyDown for continuous fire")
  4100. end
  4101. end
  4102. if ply:KeyDown(IN_ATTACK) then if ply.FakeShooting then
  4103. if _G.FireShot then
  4104. _G.FireShot(ply.wep)
  4105. else
  4106. if GetConVar("developer"):GetInt() > 0 then
  4107. print("[RADS] ERROR: FireShot function not available globally at line 1898!")
  4108. end
  4109. end
  4110. end end
  4111. else
  4112. -- Debug: Print semi-automatic weapon detection
  4113. if ply.curweapon == "wep_mann_hmcd_akm" then
  4114. if GetConVar("developer"):GetInt() > 0 then
  4115. print("[RADS DEBUG] AKM detected as SEMI-AUTO, using KeyPressed for single shots")
  4116. print("[RADS DEBUG] Automatic table value for AKM:", Automatic and Automatic["wep_mann_hmcd_akm"] or "nil")
  4117. end
  4118. end
  4119. if ply:KeyPressed(IN_ATTACK) then if ply.FakeShooting then
  4120. if _G.FireShot then
  4121. _G.FireShot(ply.wep)
  4122. else
  4123. if GetConVar("developer"):GetInt() > 0 then
  4124. print("[RADS] ERROR: FireShot function not available globally at line 1900!")
  4125. end
  4126. end
  4127. end end
  4128. end
  4129.  
  4130. if ply:KeyDown(IN_JUMP) and (table.Count(constraint.FindConstraints(ply:GetNWEntity("player_ragdoll"), 'Rope')) > 0 or ((rag.IsWeld or 0) > 0)) and (ply.lastuntietry or 0) < CurTime() and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then -- if(ply:KeyDown(IN_MOVERIGHT)) and !timer.Exists("StunTime"..ply:EntIndex()) and !timer.Exists("Epilepsy"..ply:EntIndex()) and not ply.Otrub then -- local pos = ply:EyePos() -- pos[3] = head:GetPos()[3] -- local phys = rag:GetPhysicsObjectNum( rag:TranslateBoneToPhysBone(rag:LookupBone( "ValveBiped.Bip01_R_Calf" )) ) -- local ang=ply:EyeAngles() -- ang:RotateAroundAxis(eyeangs:Forward(),90) -- ang:RotateAroundAxis(eyeangs:Right(),80) -- local shadowparams = { -- secondstoarrive=0.4, -- pos=head:GetPos()+eyeangs:Forward()*55+eyeangs:Right()*2, -- angle=ang, -- maxangular=670, -- maxangulardamp=600, -- maxspeeddamp=50, -- maxspeed=500, -- teleportdistance=0, -- deltatime=0.01, -- } -- phys:Wake() -- phys:ComputeShadowControl(shadowparams) -- end -- if(ply:KeyDown(IN_MOVELEFT)) and !timer.Exists("StunTime"..ply:EntIndex()) and !timer.Exists("Epilepsy"..ply:EntIndex()) and not ply.Otrub then -- local pos = ply:EyePos() -- pos[3] = head:GetPos()[3] -- local phys = rag:GetPhysicsObjectNum( rag:TranslateBoneToPhysBone(rag:LookupBone( "ValveBiped.Bip01_L_Calf" )) ) -- local ang=ply:EyeAngles() -- ang:RotateAroundAxis(eyeangs:Forward(),90) -- ang:RotateAroundAxis(eyeangs:Right(),80) -- local shadowparams = { -- secondstoarrive=0.4, -- pos=head:GetPos()+eyeangs:Forward()*55+eyeangs:Right()*2, -- angle=ang, -- maxangular=670, -- maxangulardamp=600, -- maxspeeddamp=50, -- maxspeed=500, -- teleportdistance=0, -- deltatime=0.01, -- } -- phys:Wake() -- phys:ComputeShadowControl(shadowparams) -- end
  4131. ply.lastuntietry = CurTime() + 1
  4132. rag.IsWeld = math.max((rag.IsWeld or 0) - 0.1, 0)
  4133. local RopeCount = table.Count(constraint.FindConstraints(ply:GetNWEntity("player_ragdoll"), 'Rope'))
  4134. Ropes = constraint.FindConstraints(ply:GetNWEntity("player_ragdoll"), 'Rope')
  4135. Try = math.random(1, 10 * RopeCount)
  4136. local phys = rag:GetPhysicsObjectNum(1)
  4137. local speed = 200
  4138. -- Apply shock responsiveness factor
  4139. local responsivenessFactor = rag.responsivenessFactor or 1
  4140. local shadowparams = {
  4141. secondstoarrive = 0.05 / responsivenessFactor,
  4142. pos = phys:GetPos() + phys:GetAngles():Forward() * 20,
  4143. angle = phys:GetAngles(),
  4144. maxangulardamp = 30,
  4145. maxspeeddamp = 30,
  4146. maxangular = 90 * responsivenessFactor,
  4147. maxspeed = speed * responsivenessFactor,
  4148. teleportdistance = 0,
  4149. deltatime = 0.01,
  4150. }
  4151.  
  4152. phys:Wake()
  4153. phys:ComputeShadowControl(shadowparams)
  4154. if Try > (7 * RopeCount) or ((rag.IsWeld or 0) > 0) then
  4155. if RopeCount > 1 or (rag.IsWeld or 0 > 0) then
  4156. if RopeCount > 1 then ply:ChatPrint("Left: " .. RopeCount - 1) end
  4157. if (rag.IsWeld or 0) > 0 then ply:ChatPrint("All that's left is to knock off the nails: " .. tostring(math.ceil(rag.IsWeld))) end
  4158. else
  4159. ply:ChatPrint("You've come untied")
  4160. end
  4161.  
  4162. Ropes[1].Constraint:Remove()
  4163. rag:EmitSound("restains.wav", 90, 50, 0.5, CHAN_AUTO)
  4164. end
  4165. end
  4166.  
  4167. if ply:KeyDown(IN_USE) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then
  4168. -- Mark that player is intentionally raising upper body to prevent neck breaking
  4169. ply.raisingUpperBody = true
  4170.  
  4171. local phys = head
  4172. local angs = ply:EyeAngles()
  4173. angs:RotateAroundAxis(angs:Forward(), 90)
  4174. -- Apply shock responsiveness factor
  4175. local responsivenessFactor = rag.responsivenessFactor or 1
  4176.  
  4177. -- Check if ragdoll is airborne or has high velocity
  4178. local ragVelocity = rag:GetVelocity():Length()
  4179. local isAirborne = ragVelocity > 100 -- Threshold for high velocity/airborne
  4180.  
  4181. local shadowparams
  4182. if isAirborne then
  4183. -- Use current settings for mid-air/high velocity
  4184. shadowparams = {
  4185. secondstoarrive = 0.45 / responsivenessFactor, -- Increased from 0.5 for smoother movement
  4186. pos = head:GetPos() + Vector(0, 0, 20 / math.Clamp(rag:GetVelocity():Length() / 300, 1, 12)),
  4187. angle = angs,
  4188. maxangulardamp = 20, -- Increased from 10 for more damping
  4189. maxspeeddamp = 20, -- Increased from 10 for more damping
  4190. maxangular = 360 * responsivenessFactor, -- Reduced from 370 to prevent neck stress
  4191. maxspeed = 39 * responsivenessFactor, -- Reduced from 40 for gentler movement
  4192. teleportdistance = 0,
  4193. deltatime = deltatime,
  4194. }
  4195. else
  4196. -- Use new settings for grounded/low velocity
  4197. shadowparams = {
  4198. secondstoarrive = 0.15 / responsivenessFactor, -- Increased from 0.5 for smoother movement
  4199. pos = head:GetPos() + Vector(0, 0, 20 / math.Clamp(rag:GetVelocity():Length() / 300, 1, 12)),
  4200. angle = angs,
  4201. maxangulardamp = 35, -- Increased from 10 for more damping
  4202. maxspeeddamp = 25, -- Increased from 10 for more damping
  4203. maxangular = 450 * responsivenessFactor, -- Reduced from 370 to prevent neck stress
  4204. maxspeed = 45 * responsivenessFactor, -- Reduced from 40 for gentler movement
  4205. teleportdistance = 0,
  4206. deltatime = deltatime,
  4207. }
  4208. end
  4209.  
  4210. head:Wake()
  4211. head:ComputeShadowControl(shadowparams)
  4212. else
  4213. -- Clear the flag when not holding E
  4214. if ply.raisingUpperBody then
  4215. ply.raisingUpperBody = false
  4216. end
  4217. end
  4218.  
  4219. if ply:KeyDown(IN_ATTACK2) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then
  4220. local physa = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_R_Hand")))
  4221. local phys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_L_Hand"))) --rhand
  4222. local ang = ply:EyeAngles()
  4223. ang:RotateAroundAxis(eyeangs:Forward(), 90)
  4224. ang:RotateAroundAxis(eyeangs:Right(), 75)
  4225. local pos = ply:EyePos()
  4226. pos[3] = head:GetPos()[3]
  4227. -- Apply shock responsiveness factor
  4228. local responsivenessFactor = rag.responsivenessFactor or 1
  4229. local shadowparams = {
  4230. secondstoarrive = 0.4 / responsivenessFactor,
  4231. pos = head:GetPos() + eyeangs:Forward() * 50 + eyeangs:Right() * 15,
  4232. angle = ang,
  4233. maxangular = 670 * responsivenessFactor,
  4234. maxangulardamp = 600,
  4235. maxspeeddamp = 50,
  4236. maxspeed = 500 * responsivenessFactor,
  4237. teleportdistance = 0,
  4238. deltatime = 0.01,
  4239. }
  4240.  
  4241. physa:Wake()
  4242. if not ply.suiciding or TwoHandedOrNo[ply.curweapon] then
  4243. if TwoHandedOrNo[ply.curweapon] and IsValid(ply.wep) then
  4244. local ang = ply:EyeAngles()
  4245. ang:RotateAroundAxis(ang:Forward(), 90)
  4246. ang:RotateAroundAxis(ang:Up(), 20)
  4247. ang:RotateAroundAxis(ang:Right(), 10)
  4248. shadowparams.angle = ang
  4249. local wepPhys = ply.wep:GetPhysicsObject()
  4250. if IsValid(wepPhys) then
  4251. wepPhys:ComputeShadowControl(shadowparams)
  4252. end
  4253. shadowparams.pos = shadowparams.pos
  4254. phys:ComputeShadowControl(shadowparams)
  4255. shadowparams.pos = shadowparams.pos + eyeangs:Forward() * -50 + eyeangs:Right() * -15
  4256. physa:ComputeShadowControl(shadowparams)
  4257. elseif IsValid(ply.wep) and IsValid(ply.wep:GetPhysicsObject()) then
  4258. -- One-handed weapon (pistol) aiming with counter-force to prevent excessive movement
  4259. ang:RotateAroundAxis(ply:EyeAngles():Forward(), 90)
  4260. ang:RotateAroundAxis(ply:EyeAngles():Up(), 110)
  4261. ang:RotateAroundAxis(eyeangs:Right(), -30)
  4262. shadowparams.angle = ang
  4263. shadowparams.pos = shadowparams.pos + eyeangs:Right() * -15
  4264.  
  4265. -- Apply stronger constraint for pistols to prevent going beyond hand
  4266. local pistolShadowParams = {
  4267. secondstoarrive = 0.2 / responsivenessFactor, -- Faster response
  4268. pos = physa:GetPos() + eyeangs:Forward() * 8 + eyeangs:Right() * -5, -- Closer to hand
  4269. angle = ang,
  4270. maxangular = 400 * responsivenessFactor, -- Reduced angular movement
  4271. maxangulardamp = 800, -- Higher damping
  4272. maxspeeddamp = 100, -- Higher speed damping
  4273. maxspeed = 200 * responsivenessFactor, -- Reduced max speed
  4274. teleportdistance = 0,
  4275. deltatime = 0.01,
  4276. }
  4277.  
  4278. local wepPhys = ply.wep:GetPhysicsObject()
  4279. if IsValid(wepPhys) then
  4280. wepPhys:ComputeShadowControl(pistolShadowParams)
  4281. end
  4282. physa:ComputeShadowControl(shadowparams)
  4283. else
  4284. physa:ComputeShadowControl(shadowparams)
  4285. end
  4286. else
  4287. if ply.FakeShooting and IsValid(ply.wep) then
  4288. shadowparams.maxspeed = 500
  4289. shadowparams.maxangular = 500
  4290. shadowparams.pos = head:GetPos() - ply.wep:GetAngles():Forward() * 12
  4291. local wepPhys = ply.wep:GetPhysicsObject()
  4292. if IsValid(wepPhys) then
  4293. shadowparams.angle = wepPhys:GetAngles()
  4294. wepPhys:ComputeShadowControl(shadowparams)
  4295. end
  4296. physa:ComputeShadowControl(shadowparams)
  4297. end
  4298. end
  4299. end
  4300.  
  4301. if ply:KeyDown(IN_SPEED) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then
  4302. local bone = rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_L_Hand"))
  4303. local phys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_L_Hand")))
  4304. if not IsValid(rag.ZacConsLH) and (not rag.ZacNextGrLH or rag.ZacNextGrLH <= CurTime()) then -- if !TwoHandedOrNo[ply.curweapon] then -- local shadowparams = { -- secondstoarrive=0.5, -- pos=head:GetPos(), -- angle=angs, -- maxangulardamp=10, -- maxspeeddamp=10, -- maxangular=370, -- maxspeed=1120, -- teleportdistance=0, -- deltatime=deltatime, -- } -- phys:Wake() -- phys:ComputeShadowControl(shadowparams)
  4305. rag.ZacNextGrLH = CurTime() + 0.1
  4306. for i = 1, 3 do
  4307. local offset = phys:GetAngles():Up() * -5
  4308. if i == 2 then offset = phys:GetAngles():Right() * 5 end
  4309. if i == 3 then offset = phys:GetAngles():Right() * -5 end
  4310. local traceinfo = {
  4311. start = phys:GetPos(),
  4312. endpos = phys:GetPos() + offset,
  4313. filter = rag,
  4314. output = trace,
  4315. }
  4316.  
  4317. local trace = util.TraceLine(traceinfo)
  4318. if trace.Hit and not trace.HitSky then
  4319. local cons = constraint.Weld(rag, trace.Entity, bone, trace.PhysicsBone, GetConVar('rads_lefthandlimit'):GetInt(), false, false)
  4320. if IsValid(cons) then
  4321. rag.ZacConsLH = cons
  4322. local pos = rag.ZacConsLH:GetPos() -- rag:EmitSound("physics/rubber/rubber_tire_strain3.wav", 100, 100, 1) -- if IsValid(rag.ZacConsLH) then
  4323. net.Start("CapturePositionLH")
  4324. net.WriteVector(pos)
  4325. net.Send(ply)
  4326.  
  4327. -- NEW: Hand grappling finger manipulation
  4328. local gripAngle = Angle(-25, 0, 0)
  4329. for i = 0, 4 do -- 5 fingers
  4330. for j = 1, 3 do -- 3 joints per finger
  4331. local fingerBone = "ValveBiped.Bip01_L_Finger" .. i .. j
  4332. if rag:LookupBone(fingerBone) then
  4333. rag:ManipulateBoneAngles(rag:LookupBone(fingerBone), gripAngle)
  4334. end
  4335. end
  4336. end
  4337.  
  4338. -- Send grappling icon message
  4339. net.Start("showiconleft")
  4340. net.Send(ply)
  4341.  
  4342. if trace.Entity:IsPlayer() and GetConVar('rads_fallwhengrabbed'):GetBool() then -- end
  4343. rads(trace.Entity)
  4344. end
  4345. end
  4346.  
  4347. break
  4348. end
  4349. end
  4350. end
  4351. else
  4352. if IsValid(rag.ZacConsLH) then
  4353. rag.ZacConsLH:Remove()
  4354. rag.ZacConsLH = nil
  4355.  
  4356. -- NEW: Reset finger angles when releasing grip
  4357. local zeroAng = Angle(0, 0, 0)
  4358. for i = 0, 4 do -- 5 fingers
  4359. for j = 1, 3 do -- 3 joints per finger
  4360. local fingerBone = "ValveBiped.Bip01_L_Finger" .. i .. j
  4361. if rag:LookupBone(fingerBone) then
  4362. rag:ManipulateBoneAngles(rag:LookupBone(fingerBone), zeroAng)
  4363. end
  4364. end
  4365. end
  4366.  
  4367. -- Hide grappling icon
  4368. net.Start("hideiconleft")
  4369. net.Send(ply)
  4370. end
  4371. end
  4372.  
  4373. if ply:KeyDown(IN_WALK) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then -- end
  4374. local bone = rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_R_Hand"))
  4375. local phys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_R_Hand")))
  4376. if not IsValid(rag.ZacConsRH) and (not rag.ZacNextGrRH or rag.ZacNextGrRH <= CurTime()) then
  4377. rag.ZacNextGrRH = CurTime() + 0.1
  4378. for i = 1, 3 do
  4379. local offset = phys:GetAngles():Up() * 5
  4380. if i == 2 then offset = phys:GetAngles():Right() * 5 end
  4381. if i == 3 then offset = phys:GetAngles():Right() * -5 end
  4382. local traceinfo = {
  4383. start = phys:GetPos(),
  4384. endpos = phys:GetPos() + offset,
  4385. filter = rag,
  4386. output = trace,
  4387. }
  4388.  
  4389. local trace = util.TraceLine(traceinfo)
  4390. if trace.Hit and not trace.HitSky then
  4391. local cons = constraint.Weld(rag, trace.Entity, bone, trace.PhysicsBone, GetConVar('rads_righthandlimit'):GetInt(), false, false)
  4392. if IsValid(cons) then
  4393. rag.ZacConsRH = cons
  4394. local pos = rag.ZacConsRH:GetPos() -- rag:EmitSound("physics/rubber/rubber_tire_strain3.wav", 100, 100, 1) -- if IsValid(rag.ZacConsRH) then
  4395. net.Start("CapturePositionRH")
  4396. net.WriteVector(pos)
  4397. net.Send(ply)
  4398.  
  4399. -- NEW: Hand grappling finger manipulation for right hand
  4400. local gripAngle = Angle(25, 0, 0) -- Different angle for right hand to prevent "snapped" appearance
  4401. for i = 0, 4 do -- 5 fingers
  4402. for j = 1, 3 do -- 3 joints per finger
  4403. local fingerBone = "ValveBiped.Bip01_R_Finger" .. i .. j
  4404. if rag:LookupBone(fingerBone) then
  4405. rag:ManipulateBoneAngles(rag:LookupBone(fingerBone), gripAngle)
  4406. end
  4407. end
  4408. end
  4409.  
  4410. -- Send grappling icon message
  4411. net.Start("showiconright")
  4412. net.Send(ply)
  4413.  
  4414. if trace.Entity:IsPlayer() and GetConVar('rads_fallwhengrabbed'):GetBool() then -- end
  4415. rads(trace.Entity)
  4416. end
  4417. end
  4418.  
  4419. break
  4420. end
  4421. end
  4422. end
  4423. else
  4424. if IsValid(rag.ZacConsRH) then
  4425. rag.ZacConsRH:Remove()
  4426. rag.ZacConsRH = nil
  4427.  
  4428. -- NEW: Reset finger angles when releasing grip
  4429. local zeroAng = Angle(0, 0, 0)
  4430. for i = 0, 4 do -- 5 fingers
  4431. for j = 1, 3 do -- 3 joints per finger
  4432. local fingerBone = "ValveBiped.Bip01_R_Finger" .. i .. j
  4433. if rag:LookupBone(fingerBone) then
  4434. rag:ManipulateBoneAngles(rag:LookupBone(fingerBone), zeroAng)
  4435. end
  4436. end
  4437. end
  4438.  
  4439. -- Hide grappling icon
  4440. net.Start("hideiconright")
  4441. net.Send(ply)
  4442. end
  4443. end
  4444.  
  4445. if (ply:KeyDown(IN_FORWARD) and IsValid(rag.ZacConsLH)) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then
  4446. local phys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine")))
  4447. local lh = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_L_Hand")))
  4448. local angs = ply:EyeAngles()
  4449. angs:RotateAroundAxis(angs:Forward(), 90)
  4450. angs:RotateAroundAxis(angs:Up(), 90)
  4451. local speed = GetConVar('rads_pullupspeed'):GetInt()
  4452. if rag.ZacConsLH.Ent2:GetVelocity():LengthSqr() < 1000 then
  4453. -- Apply shock responsiveness factor
  4454. local responsivenessFactor = rag.responsivenessFactor or 1
  4455. local shadowparams = {
  4456. secondstoarrive = 0.5 / responsivenessFactor,
  4457. pos = lh:GetPos(),
  4458. angle = phys:GetAngles(),
  4459. maxangulardamp = 10,
  4460. maxspeeddamp = 10,
  4461. maxangular = 50 * responsivenessFactor,
  4462. maxspeed = speed * responsivenessFactor,
  4463. teleportdistance = 0,
  4464. deltatime = deltatime,
  4465. }
  4466.  
  4467. phys:Wake()
  4468. phys:ComputeShadowControl(shadowparams)
  4469. end
  4470. end
  4471.  
  4472. if (ply:KeyDown(IN_FORWARD) and IsValid(rag.ZacConsRH)) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then
  4473. local phys = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine")))
  4474. local rh = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_R_Hand")))
  4475. local angs = ply:EyeAngles()
  4476. angs:RotateAroundAxis(angs:Forward(), 90)
  4477. angs:RotateAroundAxis(angs:Up(), 90)
  4478. local speed = GetConVar('rads_pullupspeed'):GetInt()
  4479. if rag.ZacConsRH.Ent2:GetVelocity():LengthSqr() < 1000 then
  4480. -- Apply shock responsiveness factor
  4481. local responsivenessFactor = rag.responsivenessFactor or 1
  4482. local shadowparams = {
  4483. secondstoarrive = 0.5 / responsivenessFactor,
  4484. pos = rh:GetPos(),
  4485. angle = phys:GetAngles(),
  4486. maxangulardamp = 10,
  4487. maxspeeddamp = 10,
  4488. maxangular = 50 * responsivenessFactor,
  4489. maxspeed = speed * responsivenessFactor,
  4490. teleportdistance = 0,
  4491. deltatime = deltatime,
  4492. }
  4493.  
  4494. phys:Wake()
  4495. phys:ComputeShadowControl(shadowparams)
  4496. end
  4497. end
  4498.  
  4499. if (ply:KeyDown(IN_BACK) and IsValid(rag.ZacConsLH)) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then
  4500. local phys = rag:GetPhysicsObjectNum(1)
  4501. local chst = rag:GetPhysicsObjectNum(0)
  4502. local angs = ply:EyeAngles()
  4503. angs:RotateAroundAxis(angs:Forward(), 90)
  4504. angs:RotateAroundAxis(angs:Up(), 90)
  4505. local speed = 30
  4506. if rag.ZacConsLH.Ent2:GetVelocity():LengthSqr() < 1000 then
  4507. -- Apply shock responsiveness factor
  4508. local responsivenessFactor = rag.responsivenessFactor or 1
  4509. local shadowparams = {
  4510. secondstoarrive = 0.5 / responsivenessFactor,
  4511. pos = chst:GetPos(),
  4512. angle = phys:GetAngles(),
  4513. maxangulardamp = 10,
  4514. maxspeeddamp = 10,
  4515. maxangular = 50 * responsivenessFactor,
  4516. maxspeed = speed * responsivenessFactor,
  4517. teleportdistance = 0,
  4518. deltatime = deltatime,
  4519. }
  4520.  
  4521. phys:Wake()
  4522. phys:ComputeShadowControl(shadowparams)
  4523. end
  4524. end
  4525.  
  4526. if (ply:KeyDown(IN_BACK) and IsValid(rag.ZacConsRH)) and not timer.Exists("StunTime" .. ply:EntIndex()) and not timer.Exists("Epilepsy" .. ply:EntIndex()) and not ply.Otrub and not ply.brokenupperspine then
  4527. local phys = rag:GetPhysicsObjectNum(1)
  4528. local chst = rag:GetPhysicsObjectNum(0)
  4529. local angs = ply:EyeAngles()
  4530. angs:RotateAroundAxis(angs:Forward(), 90)
  4531. angs:RotateAroundAxis(angs:Up(), 90)
  4532. local speed = 30
  4533. if rag.ZacConsRH.Ent2:GetVelocity():LengthSqr() < 1000 then
  4534. -- Apply shock responsiveness factor
  4535. local responsivenessFactor = rag.responsivenessFactor or 1
  4536. local shadowparams = {
  4537. secondstoarrive = 0.5 / responsivenessFactor,
  4538. pos = chst:GetPos(),
  4539. angle = phys:GetAngles(),
  4540. maxangulardamp = 10,
  4541. maxspeeddamp = 10,
  4542. maxangular = 50 * responsivenessFactor,
  4543. maxspeed = speed * responsivenessFactor,
  4544. teleportdistance = 0,
  4545. deltatime = deltatime,
  4546. }
  4547.  
  4548. phys:Wake()
  4549. phys:ComputeShadowControl(shadowparams)
  4550. end
  4551. end
  4552.  
  4553. -- Getting up logic
  4554. local head = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Head1")))
  4555. if head and IsValid(head) then
  4556. -- REMOVED: Leg restriction code - players can now get up regardless of leg condition
  4557. local ragdollVelocity = rag:GetVelocity():Length()
  4558. local MaxUp = 400 -- Hardcoded from rads_maxupspeed
  4559. local UpSpeed = 400 -- Hardcoded from rads_upspeed
  4560. local wakeTime = 2.5 -- Hardcoded from rads_waketime
  4561.  
  4562. -- Prevent get up if traveling at high speed (500-600 units/second)
  4563. if ragdollVelocity >= 500 and ply.gettingUp then
  4564. ply.gettingUp = false
  4565. ply.upValue = 0
  4566. ply.lastGetUpAttempt = CurTime()
  4567. ply:ChatPrint("You're moving too fast to get up safely!")
  4568. return
  4569. end
  4570.  
  4571.  
  4572.  
  4573. -- Height-based automatic get up for airborne ragdolls that are already getting up
  4574. if ply.gettingUp then
  4575. -- Check if ragdoll is airborne by tracing downward
  4576. local traceData = {
  4577. start = rag:GetPos(),
  4578. endpos = rag:GetPos() + Vector(0, 0, -200), -- Trace 200 units down
  4579. filter = rag
  4580. }
  4581. local trace = util.TraceLine(traceData)
  4582. local heightAboveGround = rag:GetPos().z - trace.HitPos.z
  4583.  
  4584. -- If ragdoll is high enough above ground (120+ units) and already getting up, auto-complete get up
  4585. if heightAboveGround >= 120 then
  4586. rads(ply)
  4587. ply.gettingUp = false
  4588. ply.upValue = 0
  4589. ply.lastGetUpAttempt = CurTime()
  4590. return -- Exit early since we completed the get up
  4591. end
  4592. end
  4593.  
  4594. -- Improved interruption logic - less sensitive to damage
  4595. if ragdollVelocity > 300 then -- Increased threshold from 200 to 300
  4596. ply.gettingUp = false
  4597. ply.upValue = 0
  4598. ply.lastGetUpAttempt = CurTime()
  4599. -- Remove takingDamage interruption entirely
  4600. -- REMOVED: Automatic get up triggering based on time and velocity
  4601. -- elseif not ply.gettingUp and CurTime() - ply.lastGetUpAttempt >= wakeTime and not ply.brokenspine and not ply.Otrub then
  4602. -- -- Only start getting up if ragdoll is relatively still and enough time has passed
  4603. -- if ragdollVelocity < 60 then -- Reduced threshold from 80 to 60 for faster response
  4604. -- ply.gettingUp = true
  4605. -- ply.upValue = 0
  4606. -- end
  4607. end
  4608.  
  4609. -- KEEP: Manual get up physics (triggered by rads_ragdolize command)
  4610. if ply.gettingUp then
  4611. -- Check for failure conditions during get up process
  4612. local shouldFailGetUp = false
  4613. local failureReason = ""
  4614.  
  4615. -- Check if player becomes unconscious during get up
  4616. if ply.Otrub then
  4617. shouldFailGetUp = true
  4618. failureReason = "You lost consciousness while trying to get up."
  4619. end
  4620.  
  4621. -- Check if player gets concussion during get up
  4622. if ply.concussionActive then
  4623. shouldFailGetUp = true
  4624. failureReason = "The concussion makes it impossible to get up."
  4625. end
  4626.  
  4627. -- If failure conditions are met, interrupt the get up process
  4628. if shouldFailGetUp then
  4629. ply.gettingUp = false
  4630. ply.upValue = 0
  4631. ply.lastGetUpAttempt = CurTime()
  4632. ply:ChatPrint(failureReason)
  4633. return -- Exit early to prevent further get up physics
  4634. end
  4635.  
  4636. if ply.upValue < MaxUp then
  4637. ply.upValue = math.Approach(ply.upValue, MaxUp, FrameTime() * UpSpeed)
  4638. -- Enhanced getting up physics - stronger and more coordinated
  4639. local spine = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine")))
  4640. local pelvis = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Pelvis")))
  4641. local chest = rag:GetPhysicsObjectNum(rag:TranslateBoneToPhysBone(rag:LookupBone("ValveBiped.Bip01_Spine2")))
  4642.  
  4643. -- Apply stronger force to multiple body parts for better body raising
  4644. if IsValid(head) then
  4645. head:ApplyForceCenter(Vector(0, 0, 1) * ply.upValue * 0.5) -- Increased from 0.4 to 0.5
  4646. end
  4647. if IsValid(chest) then
  4648. chest:ApplyForceCenter(Vector(0, 0, 1) * ply.upValue * 0.6) -- New chest force for better torso lifting
  4649. end
  4650. if IsValid(spine) then
  4651. spine:ApplyForceCenter(Vector(0, 0, 1) * ply.upValue * 0.5) -- Increased from 0.4 to 0.5
  4652. end
  4653. if IsValid(pelvis) then
  4654. pelvis:ApplyForceCenter(Vector(0, 0, 1) * ply.upValue * 0.3) -- Increased from 0.2 to 0.3
  4655. end
  4656.  
  4657. -- Add slight forward momentum to help with getting up motion
  4658. local forward = rag:GetForward()
  4659. if IsValid(pelvis) then
  4660. pelvis:ApplyForceCenter(forward * ply.upValue * 0.1)
  4661. end
  4662. else
  4663. -- Ragdoll is fully up, restore player
  4664. rads(ply)
  4665. ply.gettingUp = false
  4666. ply.upValue = 0
  4667. ply.lastGetUpAttempt = CurTime()
  4668. end
  4669. end
  4670. end
  4671. end)
  4672.  
  4673. util.AddNetworkString('CapturePositionRH')
  4674. util.AddNetworkString('CapturePositionLH')
  4675. hook.Add("Player Think", "Pulse", function(ply, curTime)
  4676. if not ply.pulse and ply:Alive() then ply.pulse = 70 end
  4677. local rag = ply:GetNWEntity('player_ragdoll')
  4678. local lastPulseChange = ply.lastPulseChange or 0
  4679. if curTime - lastPulseChange >= 2 then
  4680. local change = math.random(-7, 7)
  4681. if ply.pulse >= 130 then
  4682. ply.pulse = math.max(ply.pulse - math.random(3, 5), 0)
  4683. ply:SetNWBool("radshighpulse", true)
  4684. elseif ply.pulse <= 50 and ply:Alive() then
  4685. ply.pulse = math.min(ply.pulse + math.random(10, 20), 100)
  4686. ply:SetNWBool("radshighpulse", false)
  4687. end
  4688.  
  4689. if not ply:Alive() then ply.pulse = 0 end
  4690. if ply.pulse >= 55 and ply.pulse <= 129 then ply:SetNWBool("radshighpulse", false) end
  4691. ply.pulse = ply.pulse + change
  4692. rag.pulse = ply.pulse
  4693. -- FIXED: Network the pulse value to clients
  4694. ply:SetNWInt("PlayerPulse", ply.pulse)
  4695. ply.lastPulseChange = curTime
  4696. end
  4697. end)
  4698.  
  4699. -- Utility function to apply heavy fall effects (sound, organ damage, pain)
  4700. local function RADS_HeavyFallEffect(ent, impactPos)
  4701. if not IsValid(ent) then return end
  4702. -- Play random heavy fall sound
  4703. local sounds = {"fallheavy1.wav", "fallheavy2.wav", "fallheavy3.wav", "fallheavy4.wav"}
  4704. local snd = sounds[math.random(1, #sounds)]
  4705. ent:EmitSound(snd, 80, 100, 1, CHAN_AUTO)
  4706.  
  4707. -- Get the player (for ragdoll, get owner)
  4708. local ply = ent
  4709. if ent:IsRagdoll() and ent:GetNWEntity("owner") and ent:GetNWEntity("owner"):IsPlayer() then
  4710. ply = ent:GetNWEntity("owner")
  4711. end
  4712. if not IsValid(ply) or not ply:IsPlayer() then return end
  4713. if not ply.Organs then return end
  4714.  
  4715. -- Damage all inner organs except brain and arteries, but including skull
  4716. local ignore_organs = {
  4717. brain = true,
  4718. artery = true,
  4719. radial_artery_l = true,
  4720. radial_artery_r = true,
  4721. femoral_artery_l = true,
  4722. femoral_artery_r = true,
  4723. popliteal_artery_l = true,
  4724. popliteal_artery_r = true
  4725. }
  4726. for organ, _ in pairs(ply.Organs) do
  4727. if not ignore_organs[organ] then
  4728. -- Damage: set to 0 or reduce by a large amount (here, set to 0 for dramatic effect)
  4729. if organ == "skull" then
  4730. ply.Organs[organ] = math.max(ply.Organs[organ] - 15, 0) -- skull: not instant break
  4731. else
  4732. ply.Organs[organ] = math.max(ply.Organs[organ] - 999, 0)
  4733. end
  4734. end
  4735. end
  4736. -- Add moderate pain (reduced from 800)
  4737. ply.pain = (ply.pain or 0) + 160
  4738. end
  4739.  
  4740. -- Utility function for extreme fall: smash all organs, kill, loud sound
  4741. local function RADS_DeathFallEffect(ent, impactPos)
  4742. if not IsValid(ent) then return end
  4743. -- Play random heavy fall sound, louder
  4744. local sounds = {"fallheavy1.wav", "fallheavy2.wav", "fallheavy3.wav", "fallheavy4.wav"}
  4745. local snd = sounds[math.random(1, #sounds)]
  4746. ent:EmitSound(snd, 120, 100, 1, CHAN_AUTO)
  4747.  
  4748. -- Get the player (for ragdoll, get owner)
  4749. local ply = ent
  4750. if ent:IsRagdoll() and ent:GetNWEntity("owner") and ent:GetNWEntity("owner"):IsPlayer() then
  4751. ply = ent:GetNWEntity("owner")
  4752. end
  4753. if not IsValid(ply) or not ply:IsPlayer() then return end
  4754. if not ply.Organs then return end
  4755.  
  4756. -- Smash all organs
  4757. for organ, _ in pairs(ply.Organs) do
  4758. ply.Organs[organ] = 0
  4759. end
  4760. ply.pain = (ply.pain or 0) + 400
  4761. -- Kill the player if possible
  4762. if ply:Alive() then ply:Kill() end
  4763. end
  4764.  
  4765. -- Extend OnPlayerHitGround for heavy and death fall logic
  4766. hook.Add("OnPlayerHitGround", "RADS_HeavyFallImpact", function(ply, a, b, speed)
  4767. -- Increased thresholds for more realistic fall damage
  4768. local velocityThreshold = GetConVar("rads_fallonspeedlimit_threshold"):GetInt()
  4769. if speed >= velocityThreshold then -- Use ConVar for death fall threshold
  4770. local impactPos = ply:GetPos()
  4771. RADS_DeathFallEffect(ply, impactPos)
  4772. elseif speed >= 800 then -- Increased from 500 for heavy fall
  4773. local impactPos = ply:GetPos()
  4774. RADS_HeavyFallEffect(ply, impactPos)
  4775. end
  4776.  
  4777. -- Velocity-based pain for broken/dislocated legs on ground impact
  4778. if speed >= 200 then -- Only apply pain for significant impacts
  4779. local hasLegInjury = false
  4780. local painMultiplier = 1.0
  4781.  
  4782. -- Check for broken legs (higher pain multiplier)
  4783. if ply:GetNWBool("RADS_LeftLegBroken") or ply:GetNWBool("RADS_RightLegBroken") then
  4784. hasLegInjury = true
  4785. painMultiplier = painMultiplier + 2.0 -- 3x pain for broken legs
  4786. end
  4787.  
  4788. -- Check for dislocated legs (moderate pain multiplier)
  4789. if ply:GetNWBool("RADS_LeftLegDislocated") or ply:GetNWBool("RADS_RightLegDislocated") then
  4790. hasLegInjury = true
  4791. painMultiplier = painMultiplier + 1.0 -- 2x pain for dislocated legs
  4792. end
  4793.  
  4794. -- Apply velocity-based pain if leg injuries are present
  4795. if hasLegInjury then
  4796. -- Calculate pain based on impact velocity
  4797. -- Speed ranges from 200 (minimum) to 1200+ (maximum)
  4798. local velocityFactor = math.Clamp((speed - 200) / 1000, 0, 1) -- Normalize to 0-1 range
  4799. local basePain = 5 + (velocityFactor * 25) -- 5-30 base pain
  4800. local finalPain = basePain * painMultiplier
  4801.  
  4802. -- Apply realistic pain system for impact on injured legs
  4803. local damageIntensity = finalPain
  4804.  
  4805. -- Check for severe impact pain (instant pain threshold)
  4806. if finalPain >= 40 or speed >= 800 then
  4807. -- Severe impact: instant pain + pain debt
  4808. local instantPain = finalPain * 0.6 -- 60% instant for severe impact
  4809. local painDebt = finalPain * 0.4 -- 40% debt
  4810.  
  4811. ply.pain = (ply.pain or 0) + instantPain
  4812. ply.painDebt = (ply.painDebt or 0) + painDebt
  4813. ply.lastDamageTime = CurTime()
  4814. ply.damageIntensity = (ply.damageIntensity or 0) + damageIntensity
  4815.  
  4816. ply:ChatPrint("The impact on your injured legs causes agonizing pain!")
  4817. elseif finalPain >= 20 then
  4818. -- Moderate impact: balanced pain
  4819. local instantPain = finalPain * 0.3 -- 30% instant
  4820. local painDebt = finalPain * 0.7 -- 70% debt
  4821.  
  4822. ply.pain = (ply.pain or 0) + instantPain
  4823. ply.painDebt = (ply.painDebt or 0) + painDebt
  4824. ply.lastDamageTime = CurTime()
  4825. ply.damageIntensity = (ply.damageIntensity or 0) + damageIntensity
  4826.  
  4827. ply:ChatPrint("Landing hard on your injured legs hurts terribly.")
  4828. elseif finalPain >= 10 then
  4829. -- Minor impact: mostly pain debt
  4830. local instantPain = finalPain * 0.15 -- 15% instant
  4831. local painDebt = finalPain * 0.85 -- 85% debt
  4832.  
  4833. ply.pain = (ply.pain or 0) + instantPain
  4834. ply.painDebt = (ply.painDebt or 0) + painDebt
  4835. ply.lastDamageTime = CurTime()
  4836. ply.damageIntensity = (ply.damageIntensity or 0) + damageIntensity
  4837.  
  4838. ply:ChatPrint("The impact aggravates your leg injuries.")
  4839. else
  4840. -- Light impact: almost all pain debt
  4841. local instantPain = finalPain * 0.1 -- 10% instant
  4842. local painDebt = finalPain * 0.9 -- 90% debt
  4843.  
  4844. ply.pain = (ply.pain or 0) + instantPain
  4845. ply.painDebt = (ply.painDebt or 0) + painDebt
  4846. ply.lastDamageTime = CurTime()
  4847. ply.damageIntensity = (ply.damageIntensity or 0) + damageIntensity
  4848.  
  4849. ply:ChatPrint("Your injured legs ache from the impact.")
  4850. end
  4851. end
  4852. end
  4853. end)
  4854.  
  4855. -- Add PhysicsCollide callback for ragdolls to detect heavy and death ground impacts
  4856. hook.Add("OnEntityCreated", "RADS_RagdollHeavyFall", function(ent)
  4857. if not ent:IsRagdoll() then return end
  4858. -- Only add once
  4859. if ent._radsHeavyFallPhysicsCollide then return end
  4860. ent._radsHeavyFallPhysicsCollide = true
  4861. ent:AddCallback("PhysicsCollide", function(ragdoll, data)
  4862. -- Check for diving landing (any ground impact while diving)
  4863. if ragdoll.isDiving and data.HitNormal.z > 0.5 then
  4864. -- End diving state when ragdoll hits ground
  4865. ragdoll.isDiving = false
  4866. end
  4867.  
  4868. -- Only care about ground impacts (normal.z > 0.7)
  4869. local now = CurTime()
  4870. -- Increased thresholds for ragdoll impacts
  4871. if data.Speed >= 1300 and data.HitNormal.z > 0.7 then -- Increased from 950
  4872. if not ragdoll._lastDeathFallTime or now - ragdoll._lastDeathFallTime > 1 then
  4873. ragdoll._lastDeathFallTime = now
  4874. RADS_DeathFallEffect(ragdoll, data.HitPos)
  4875. end
  4876. elseif data.Speed >= 750 and data.HitNormal.z > 0.7 then -- Increased from 450
  4877. if not ragdoll._lastHeavyFallTime or now - ragdoll._lastHeavyFallTime > 1 then
  4878. ragdoll._lastHeavyFallTime = now
  4879. RADS_HeavyFallEffect(ragdoll, data.HitPos)
  4880. end
  4881. end
  4882. end)
  4883. end)
  4884.  
  4885. -- Utility: Apply realistic body relaxation (gradual rigor mortis release)
  4886. function ApplyRigorMortis(rag)
  4887. if not IsValid(rag) then return end
  4888.  
  4889. -- Initial pose: arms and legs extended, head tilted back (mimicking immediate post-mortem state)
  4890. local poseBones = {
  4891. {"ValveBiped.Bip01_L_UpperArm", Angle(0, 0, -80)},
  4892. {"ValveBiped.Bip01_R_UpperArm", Angle(0, 0, 80)},
  4893. {"ValveBiped.Bip01_L_Forearm", Angle(0, 0, -40)},
  4894. {"ValveBiped.Bip01_R_Forearm", Angle(0, 0, 40)},
  4895. {"ValveBiped.Bip01_L_Thigh", Angle(0, 0, -30)},
  4896. {"ValveBiped.Bip01_R_Thigh", Angle(0, 0, 30)},
  4897. {"ValveBiped.Bip01_L_Calf", Angle(0, 0, 0)},
  4898. {"ValveBiped.Bip01_R_Calf", Angle(0, 0, 0)},
  4899. {"ValveBiped.Bip01_Head1", Angle(-60, 0, 0)}
  4900. }
  4901.  
  4902. -- Apply initial pose
  4903. for _, v in ipairs(poseBones) do
  4904. local bone = rag:LookupBone(v[1])
  4905. if bone then
  4906. rag:ManipulateBoneAngles(bone, v[2])
  4907. end
  4908. end
  4909.  
  4910. -- Create strong initial welds (rigor mortis)
  4911. local pelvis = rag:LookupBone("ValveBiped.Bip01_Pelvis")
  4912. rag._rigorWelds = {}
  4913. rag._rigorStrength = {} -- Store strength values separately
  4914. rag._rigorStartTime = CurTime()
  4915.  
  4916. local function createGradualWeld(boneName, initialStrength)
  4917. local bone = rag:LookupBone(boneName)
  4918. if bone and pelvis then
  4919. local phys1 = rag:TranslateBoneToPhysBone(bone)
  4920. local phys2 = rag:TranslateBoneToPhysBone(pelvis)
  4921. if phys1 and phys2 and phys1 ~= phys2 then
  4922. -- Create weld with initial strength
  4923. local cons = constraint.Weld(rag, rag, phys1, phys2, initialStrength, true, false)
  4924. if cons then
  4925. table.insert(rag._rigorWelds, cons)
  4926. rag._rigorStrength[cons] = initialStrength -- Store strength separately
  4927. end
  4928. end
  4929. end
  4930. end
  4931.  
  4932. -- Create initial welds (much reduced strength for less stiffness)
  4933. createGradualWeld("ValveBiped.Bip01_L_UpperArm", 2500)
  4934. createGradualWeld("ValveBiped.Bip01_R_UpperArm", 2500)
  4935. createGradualWeld("ValveBiped.Bip01_L_Thigh", 2500)
  4936. createGradualWeld("ValveBiped.Bip01_R_Thigh", 2500)
  4937.  
  4938. -- Less stiff rigor mortis: shorter stiff period, faster relaxation
  4939. local stiffDuration = math.Rand(1.5, 3) -- Reduced initial stiffness period
  4940. local relaxDuration = math.Rand(4, 8) -- Faster relaxation period
  4941. local totalDuration = stiffDuration + relaxDuration
  4942.  
  4943. -- Keep initial strong welds for stiff period, then relax quickly
  4944. timer.Simple(stiffDuration, function()
  4945. if not IsValid(rag) then return end
  4946.  
  4947. -- Quick relaxation steps over the relaxDuration
  4948. local relaxationSteps = 8
  4949. local stepInterval = relaxDuration / relaxationSteps
  4950.  
  4951. for step = 1, relaxationSteps do
  4952. timer.Simple(step * stepInterval, function()
  4953. if not IsValid(rag) then return end
  4954.  
  4955. -- Rapidly reduce weld strength
  4956. if rag._rigorWelds and rag._rigorStrength then
  4957. for _, cons in ipairs(rag._rigorWelds) do
  4958. if IsValid(cons) and rag._rigorStrength[cons] then
  4959. -- Sharp drop in strength for realistic relaxation
  4960. local progress = step / relaxationSteps
  4961. local initialStrength = rag._rigorStrength[cons]
  4962. local remainingStrength = initialStrength * math.pow(1 - progress, 2) -- Exponential decay
  4963.  
  4964. -- Update weld strength with faster decay
  4965. cons:SetTable({
  4966. forcelimit = math.max(50, remainingStrength),
  4967. torquelimit = math.max(25, remainingStrength * 0.3)
  4968. })
  4969. end
  4970. end
  4971. end
  4972.  
  4973. -- Faster bone relaxation with natural variation
  4974. local boneProgress = math.pow(step / relaxationSteps, 1.5) -- Accelerated return
  4975. for _, v in ipairs(poseBones) do
  4976. local bone = rag:LookupBone(v[1])
  4977. if bone then
  4978. local targetAngle = Angle(
  4979. math.Rand(-5, 5), -- Natural variation
  4980. math.Rand(-5, 5),
  4981. math.Rand(-5, 5)
  4982. )
  4983. local currentAngle = rag:GetManipulateBoneAngles(bone)
  4984. local relaxedAngle = LerpAngle(boneProgress, currentAngle, targetAngle)
  4985. rag:ManipulateBoneAngles(bone, relaxedAngle)
  4986. end
  4987. end
  4988. end)
  4989. end
  4990. end)
  4991.  
  4992. -- Final cleanup after relaxation is complete
  4993. timer.Simple(totalDuration + 0.5, function()
  4994. if not IsValid(rag) then return end
  4995.  
  4996. -- Remove all remaining welds
  4997. if rag._rigorWelds then
  4998. for _, cons in ipairs(rag._rigorWelds) do
  4999. if IsValid(cons) then cons:Remove() end
  5000. end
  5001. rag._rigorWelds = nil
  5002. end
  5003.  
  5004. if rag._rigorStrength then
  5005. rag._rigorStrength = nil
  5006. end
  5007.  
  5008. -- Ensure final relaxed pose with natural variation
  5009. for _, v in ipairs(poseBones) do
  5010. local bone = rag:LookupBone(v[1])
  5011. if bone then
  5012. rag:ManipulateBoneAngles(bone, Angle(
  5013. math.Rand(-2, 2),
  5014. math.Rand(-2, 2),
  5015. math.Rand(-2, 2)
  5016. ))
  5017. end
  5018. end
  5019. end)
  5020. end
  5021. end
  5022.  
  5023. if CLIENT then
  5024. surface.CreateFont("ScFont", {
  5025. font = "Coolvetica",
  5026. size = 24,
  5027. weight = 1100,
  5028. outline = false
  5029. })
  5030.  
  5031. lastView = nil
  5032. local organData = {}
  5033.  
  5034. -- Enhanced debug display system
  5035. surface.CreateFont("RADS_DebugTitle", {
  5036. font = "Coolvetica",
  5037. size = 18,
  5038. weight = 600,
  5039. outline = true
  5040. })
  5041.  
  5042. surface.CreateFont("RADS_DebugText", {
  5043. font = "Coolvetica",
  5044. size = 14,
  5045. weight = 500,
  5046. outline = true
  5047. })
  5048.  
  5049. surface.CreateFont("RADS_DebugValue", {
  5050. font = "Coolvetica",
  5051. size = 13,
  5052. weight = 400,
  5053. outline = true
  5054. })
  5055.  
  5056. local function GetHealthColor(percentage)
  5057. if percentage >= 75 then return Color(46, 204, 113) end -- Green
  5058. if percentage >= 50 then return Color(241, 196, 15) end -- Yellow
  5059. if percentage >= 25 then return Color(230, 126, 34) end -- Orange
  5060. return Color(231, 76, 60) -- Red
  5061. end
  5062.  
  5063. local function DrawEnhancedDebugDisplay()
  5064. local ply = LocalPlayer()
  5065. if not IsValid(ply) then return end
  5066.  
  5067. local scrW, scrH = ScrW(), ScrH()
  5068. local panelW, panelH = 280, 380
  5069. local x, y = scrW - panelW - 20, 20
  5070.  
  5071. -- Semi-transparent background
  5072. draw.RoundedBox(8, x, y, panelW, panelH, Color(0, 0, 0, 180))
  5073. draw.RoundedBox(8, x + 2, y + 2, panelW - 4, panelH - 4, Color(20, 20, 20, 120))
  5074.  
  5075. -- Title
  5076. draw.SimpleText("TRAUMA DEBUG", "RADS_DebugTitle", x + panelW/2, y + 15, Color(255, 255, 255), TEXT_ALIGN_CENTER)
  5077.  
  5078. local yOffset = 45
  5079. local lineHeight = 18
  5080.  
  5081. -- Player Health
  5082. local health = ply:Health()
  5083. local healthColor = GetHealthColor(health)
  5084. draw.SimpleText("Health:", "RADS_DebugText", x + 15, y + yOffset, Color(200, 200, 200))
  5085. draw.SimpleText(health .. "/100", "RADS_DebugValue", x + panelW - 15, y + yOffset, healthColor, TEXT_ALIGN_RIGHT)
  5086. yOffset = yOffset + lineHeight
  5087.  
  5088. -- Blood Level (using RADS_ClientData or networked variable as fallback)
  5089. local blood = (RADS_ClientData and RADS_ClientData.blood) or ply:GetNWInt("PlayerBlood", 5000)
  5090. local bloodPercentage = math.Clamp((blood / 5000) * 100, 0, 100)
  5091. local bloodColor = GetHealthColor(bloodPercentage)
  5092. draw.SimpleText("Blood:", "RADS_DebugText", x + 15, y + yOffset, Color(200, 200, 200))
  5093. draw.SimpleText(blood .. "ml", "RADS_DebugValue", x + panelW - 15, y + yOffset, bloodColor, TEXT_ALIGN_RIGHT)
  5094. yOffset = yOffset + lineHeight
  5095.  
  5096. -- Pain Level (using RADS_ClientData)
  5097. local pain = (RADS_ClientData and RADS_ClientData.pain) or 0
  5098. local painLimitConVar = GetConVar("rads_painlimit")
  5099. local painLimit = painLimitConVar and painLimitConVar:GetInt() or 190
  5100. local painColor = pain > 150 and Color(231, 76, 60) or (pain > 100 and Color(230, 126, 34) or Color(46, 204, 113))
  5101. draw.SimpleText("Pain:", "RADS_DebugText", x + 15, y + yOffset, Color(200, 200, 200))
  5102. draw.SimpleText(pain .. "/" .. painLimit, "RADS_DebugValue", x + panelW - 15, y + yOffset, painColor, TEXT_ALIGN_RIGHT)
  5103. yOffset = yOffset + lineHeight
  5104.  
  5105. -- Shock Level (using correct networked variable)
  5106. local shock = ply:GetNWFloat("RADS_Shock", 0)
  5107. local shockColor = shock > 60 and Color(231, 76, 60) or (shock > 30 and Color(230, 126, 34) or Color(46, 204, 113))
  5108. draw.SimpleText("Shock:", "RADS_DebugText", x + 15, y + yOffset, Color(200, 200, 200))
  5109. draw.SimpleText(math.floor(shock) .. "/100", "RADS_DebugValue", x + panelW - 15, y + yOffset, shockColor, TEXT_ALIGN_RIGHT)
  5110. yOffset = yOffset + lineHeight
  5111.  
  5112. -- Pulse Rate (using correct networked variable)
  5113. local pulse = ply:GetNWInt("PlayerPulse", 70)
  5114. local pulseColor = (pulse > 120 or pulse < 50) and Color(231, 76, 60) or Color(46, 204, 113)
  5115. draw.SimpleText("Pulse:", "RADS_DebugText", x + 15, y + yOffset, Color(200, 200, 200))
  5116. draw.SimpleText(pulse .. " BPM", "RADS_DebugValue", x + panelW - 15, y + yOffset, pulseColor, TEXT_ALIGN_RIGHT)
  5117. yOffset = yOffset + lineHeight
  5118.  
  5119. -- Consciousness Status (using correct networked variable)
  5120. local isUnconscious = ply:GetNWBool("Otrub", false)
  5121. local consciousnessColor = isUnconscious and Color(231, 76, 60) or Color(46, 204, 113)
  5122. draw.SimpleText("Consciousness:", "RADS_DebugText", x + 15, y + yOffset, Color(200, 200, 200))
  5123. draw.SimpleText(isUnconscious and "Unconscious" or "Conscious", "RADS_DebugValue", x + panelW - 15, y + yOffset, consciousnessColor, TEXT_ALIGN_RIGHT)
  5124. yOffset = yOffset + lineHeight
  5125.  
  5126. -- Breathing Status (calculated from lung health)
  5127. local leftLungHealth = (ply.Organs and ply.Organs['left_lung']) or 5
  5128. local rightLungHealth = (ply.Organs and ply.Organs['right_lung']) or 5
  5129. local isBreathing = leftLungHealth > 0 or rightLungHealth > 0
  5130. local breathColor = isBreathing and Color(46, 204, 113) or Color(231, 76, 60)
  5131. draw.SimpleText("Breathing:", "RADS_DebugText", x + 15, y + yOffset, Color(200, 200, 200))
  5132. draw.SimpleText(isBreathing and "Normal" or "Not Breathing", "RADS_DebugValue", x + panelW - 15, y + yOffset, breathColor, TEXT_ALIGN_RIGHT)
  5133. yOffset = yOffset + lineHeight + 10
  5134.  
  5135. -- Organ Health Section
  5136. draw.SimpleText("ORGAN HEALTH", "RADS_DebugText", x + 15, y + yOffset, Color(255, 255, 255))
  5137. yOffset = yOffset + lineHeight + 5
  5138.  
  5139. -- Organ maximum health values
  5140. local maxHealth = {
  5141. ["liver"] = 15,
  5142. ["stomach"] = 15,
  5143. ["intestines"] = 30,
  5144. ["heart"] = 9,
  5145. ["left_lung"] = 5,
  5146. ["right_lung"] = 5
  5147. }
  5148.  
  5149. -- Display organ data if available
  5150. local organsToDisplay = (RADS_ClientData and RADS_ClientData.organs) or (organData and organData.organs)
  5151. if organsToDisplay and next(organsToDisplay) ~= nil then
  5152. for organ, health in pairs(organsToDisplay) do
  5153. local maxHP = maxHealth[organ] or 10
  5154. local organPercentage = math.Clamp((health / maxHP) * 100, 0, 100)
  5155. local organColor = GetHealthColor(organPercentage)
  5156. local organName = string.gsub(organ, "_", " ")
  5157. organName = string.upper(string.sub(organName, 1, 1)) .. string.sub(organName, 2)
  5158.  
  5159. draw.SimpleText(organName .. ":", "RADS_DebugValue", x + 25, y + yOffset, Color(180, 180, 180))
  5160. draw.SimpleText(math.floor(organPercentage) .. "%", "RADS_DebugValue", x + panelW - 15, y + yOffset, organColor, TEXT_ALIGN_RIGHT)
  5161. yOffset = yOffset + 15
  5162. end
  5163. else
  5164. draw.SimpleText("No organ data available", "RADS_DebugValue", x + 25, y + yOffset, Color(150, 150, 150))
  5165. end
  5166. end
  5167.  
  5168. net.Receive("info_org", function()
  5169. organData = net.ReadTable()
  5170. end)
  5171.  
  5172. -- Timer to request organism_info data periodically
  5173. timer.Create("RADS_RequestOrganismInfo", 1, 0, function()
  5174. local drawOrgConVar = GetConVar('rads_draworg')
  5175. if drawOrgConVar and drawOrgConVar:GetBool() and IsValid(LocalPlayer()) then
  5176. net.Start("request_organism_info")
  5177. net.SendToServer()
  5178. end
  5179. end)
  5180.  
  5181. hook.Add("HUDPaint", "DrawEnhancedDebugDisplay", function()
  5182. local drawOrgConVar = GetConVar('rads_draworg')
  5183. if drawOrgConVar and drawOrgConVar:GetBool() then
  5184. DrawEnhancedDebugDisplay()
  5185. end
  5186. end)
  5187. CreateClientConVar("rads_draworg", "0", {FCVAR_ARCHIVE, ""})
  5188. CreateClientConVar("rads_thirdperson", "0", {FCVAR_ARCHIVE, "Thirdperson"})
  5189. -- Server-side admin-only viewmode command
  5190. if SERVER then
  5191. CreateConVar("rads_viewmode", "1", {FCVAR_ARCHIVE, FCVAR_NOTIFY}, "0 means view from eyes but with movement,1 means from eyes without movement - Admin only")
  5192.  
  5193. concommand.Add("rads_viewmode", function(ply, cmd, args)
  5194. if not IsValid(ply) or not ply:IsAdmin() then
  5195. if IsValid(ply) then
  5196. ply:ChatPrint("[RADS] Only admins can change the view mode for all players.")
  5197. end
  5198. return
  5199. end
  5200.  
  5201. if not args[1] then
  5202. ply:ChatPrint("[RADS] Usage: rads_viewmode <0|1>")
  5203. return
  5204. end
  5205.  
  5206. local newMode = tonumber(args[1])
  5207. if newMode ~= 0 and newMode ~= 1 then
  5208. ply:ChatPrint("[RADS] Invalid mode. Use 0 or 1.")
  5209. return
  5210. end
  5211.  
  5212. GetConVar("rads_viewmode"):SetInt(newMode)
  5213.  
  5214. local modeText = newMode == 0 and "free camera movement" or "locked to ragdoll eyes"
  5215. for _, p in pairs(player.GetAll()) do
  5216. p:ChatPrint("[RADS] Admin " .. ply:Name() .. " changed view mode to: " .. modeText)
  5217. end
  5218. end)
  5219. else
  5220. -- Client-side: Get viewmode from server convar
  5221. function GetViewMode()
  5222. local serverCvar = GetConVar("rads_viewmode")
  5223. return serverCvar and serverCvar:GetInt() or 1
  5224. end
  5225. end
  5226.  
  5227. CreateClientConVar("rads_mouthscale", "6", {FCVAR_ARCHIVE, "Mouth Scale while speaking"})
  5228. CreateClientConVar("rads_viewfov", "100", {FCVAR_ARCHIVE, "Fov in ragdoll."})
  5229. CreateClientConVar("rads_disablelerp", "0", {FCVAR_ARCHIVE, ""})
  5230. CreateClientConVar("rads_drawmotd", "1", {FCVAR_ARCHIVE, "Draw motd"})
  5231.  
  5232. -- Key binding for handcuff removal
  5233. hook.Add("PlayerButtonDown", "RADS_HandcuffRemoval", function(ply, button)
  5234. if button == KEY_H and input.IsKeyDown(KEY_LALT) then
  5235. RunConsoleCommand("rads_remove_handcuffs")
  5236. end
  5237. end)
  5238.  
  5239.  
  5240. -- Simple ragdoll color sync from server
  5241. net.Receive(
  5242. "ragplayercolor",
  5243. function()
  5244. local ent = net.ReadEntity()
  5245. local col = net.ReadVector()
  5246. if IsValid(ent) and isvector(col) then
  5247. function ent:GetPlayerColor()
  5248. return col
  5249. end
  5250. end
  5251. end
  5252. )
  5253.  
  5254. local helmEnt
  5255. net.Receive("nodraw_helmet", function() helmEnt = net.ReadEntity() end)
  5256. if IsValid(helmEnt) then
  5257. helmEnt:SetNoDraw(true)
  5258. helmEnt:SetColor(Color(0, 0, 0, 0))
  5259. helmEnt:SetRenderMode(RENDERMODE_TRANSCOLOR)
  5260. end
  5261.  
  5262. hook.Add("Think", "mouthanim", function()
  5263. for i, ply in pairs(player.GetAll()) do
  5264. local ent = IsValid(ply:GetNWEntity("player_ragdoll")) and ply:GetNWEntity("player_ragdoll") or ply
  5265. local flexes = {ent:GetFlexIDByName("jaw_drop"), ent:GetFlexIDByName("left_part"), ent:GetFlexIDByName("right_part"), ent:GetFlexIDByName("left_mouth_drop"), ent:GetFlexIDByName("right_mouth_drop")}
  5266. local volume = ply:VoiceVolume()
  5267. local weight = math.Clamp(volume * 75, 0, 1.5) or 0 -- Further reduced from 150 to 75, and max from 3 to 1.5
  5268. if ply:IsSpeaking() then
  5269. for k, v in pairs(flexes) do
  5270. ent:SetFlexWeight(v, weight)
  5271. end
  5272. end
  5273. end
  5274. end)
  5275. local oldFakeOrigin = Vector(0, 0, 0)
  5276. local oldFakeAng = Angle(0, 0, 0)
  5277. local oldOrigin = Vector(0, 0, 0)
  5278. local oldAng = Angle(0, 0, 0)
  5279. local lerping = 1
  5280. local MyLerp = 0
  5281. function HomigradCam(ply, vec, ang, fov, znear, zfar)
  5282. local eye = ply:GetAttachment(ply:LookupAttachment("eyes"))
  5283. local org = eye.Pos
  5284. local ang1 = LerpAngle(0, ply:EyeAngles(), eye.Ang)
  5285. local org1 = eye.Pos + eye.Ang:Up() * 2 + eye.Ang:Forward() * 2.5
  5286. if ply:GetNWBool("radsfa") == true and IsValid(ply:GetNWEntity("player_ragdoll")) then
  5287. local attach = ply:GetNWEntity("player_ragdoll"):GetAttachment(1)
  5288. local headBoneIndex = ply:GetNWEntity("player_ragdoll"):LookupBone("ValveBiped.Bip01_Head1")
  5289.  
  5290. -- Only hide head if not already exploded by gore system
  5291. if not ply:GetNWEntity("player_ragdoll").goreHeadExploded then
  5292. ply:GetNWEntity("player_ragdoll"):ManipulateBoneScale(headBoneIndex, Vector(0, 0, 0))
  5293. end
  5294. lerping = Lerp(3 * FrameTime(), lerping, 0)
  5295. local view = {
  5296. origin = LerpVector(lerping, attach.Pos, oldOrigin),
  5297. angles = LerpAngle(lerping, LerpAngle(0.35, ang1, attach.Ang), oldAng),
  5298. fov = fov,
  5299. drawviewer = true
  5300. }
  5301.  
  5302. oldFakeOrigin = view.origin
  5303. oldFakeAng = view.angles
  5304. return view
  5305. end
  5306.  
  5307. if ply:InVehicle() == true then
  5308. -- org = eye.Pos + eye.Ang:Forward() * 0.8
  5309. ang = eye.Ang
  5310. MyLerp = 1
  5311. ply:ManipulateBoneScale(ply:LookupBone("ValveBiped.Bip01_Head1"), vector_origin)
  5312. anglerp = LerpAngle(MyLerp, ang1, ang)
  5313. else
  5314. -- Restore head visibility when not in vehicle (unless head exploded)
  5315. local headBone = ply:LookupBone("ValveBiped.Bip01_Head1")
  5316. if headBone and not ply.headExploded then
  5317. ply:ManipulateBoneScale(headBone, Vector(1, 1, 1))
  5318. end
  5319. anglerp = LerpAngle(MyLerp / 2, ang1, sightAng or Angle(0, 0, 0))
  5320. end
  5321.  
  5322. lerping = Lerp(3 * FrameTime(), lerping, 1)
  5323. local view = {
  5324. origin = LerpVector(lerping, oldFakeOrigin, LerpVector(MyLerp, org1, org)),
  5325. angles = LerpAngle(lerping, oldFakeAng, LerpAngle(0.01, anglerp, ang1)),
  5326. fov = fov,
  5327. drawviewer = true,
  5328. -- znear = 0.2
  5329. }
  5330.  
  5331. oldOrigin = view.origin
  5332. oldAng = view.angles
  5333. return view
  5334. end
  5335.  
  5336. function RadsMM(ply, origin, angles, fov)
  5337. local rag = ply:GetNWEntity("player_ragdoll")
  5338. if IsValid(rag) then
  5339. local att = rag:GetAttachment(rag:LookupAttachment("eyes"))
  5340. if att then
  5341. local view = {}
  5342. local v = angles
  5343. if lastView == nil then
  5344. lastView = {
  5345. origin = att.Pos,
  5346. angles = att.Ang,
  5347. fov = fov
  5348. }
  5349. end
  5350.  
  5351. local lerpedAngles = LerpAngle(0.8, lastView.angles, att.Ang)
  5352. local lerpedang = LerpAngle(0.8, lastView.angles, v)
  5353.  
  5354. if GetViewMode() == 1 then
  5355. -- Mode 1: Camera locked to ragdoll eyes (original behavior)
  5356. view.origin = att.Pos
  5357. view.angles = lerpedAngles
  5358. else
  5359. -- Mode 0: Camera at ragdoll eyes but allows mouse movement
  5360. if GetConVar("rads_disablelerp"):GetInt() == 1 then
  5361. view.origin = att.Pos
  5362. view.angles = Angle(lerpedang.p, lerpedang.y, 0)
  5363. else
  5364. view.origin = att.Pos
  5365. view.angles = lerpedang
  5366. end
  5367. end
  5368.  
  5369. view.znear = 1
  5370. view.fov = fov
  5371. view.drawviewer = true
  5372. lastView = {
  5373. origin = view.origin,
  5374. angles = view.angles,
  5375. fov = fov
  5376. }
  5377. return view
  5378. end
  5379. end
  5380. end
  5381.  
  5382. net.Receive("REMOVECALC", function(ply) hook.Remove('CalcView', 'govnishe') end)
  5383. net.Receive('ADDCALC', function(ply)
  5384. -- hook.Add("CalcView", "govnishe", RadsMM)
  5385. -- hook.Add("CalcView", "govnishe", HomigradCam)
  5386. end)
  5387.  
  5388. hook.Add("CalcView", "RADS.ForceFirstPerson", function(ply, origin, angles, fov)
  5389. -- Handle ragdoll first-person view
  5390. if ply:GetNWBool("radsfa") == true and IsValid(ply:GetNWEntity("player_ragdoll")) then
  5391. local rag = ply:GetNWEntity("player_ragdoll")
  5392. local attachIndex = rag:LookupAttachment("eyes")
  5393. local att = attachIndex and rag:GetAttachment(attachIndex)
  5394. local camPos = att and att.Pos or rag:GetPos()
  5395. -- Clamp camera distance to ragdoll origin (prevents flying away)
  5396. if (camPos - rag:GetPos()):Length() > 50 then
  5397. camPos = rag:GetPos()
  5398. end
  5399.  
  5400. local finalAngles
  5401. if GetViewMode() == 0 then
  5402. -- Free camera movement - use player's current view angles
  5403. finalAngles = angles
  5404. else
  5405. -- Locked camera - use ragdoll's eye angles (default behavior)
  5406. finalAngles = att and att.Ang or rag:GetAngles()
  5407. end
  5408.  
  5409. -- Apply tinnitus screenshake for ragdolls
  5410. if (tinnitusRagdollActive and CurTime() < tinnitusRagdollEndTime) or tinnitusFadeOutActive then
  5411. local currentShakeStrength
  5412.  
  5413. if tinnitusFadeOutActive then
  5414. -- During fade-out, use fade multiplier to gradually reduce shake to 0
  5415. local elapsed = CurTime() - tinnitusFadeOutStartTime
  5416. local progress = math.Clamp(elapsed / tinnitusFadeOutDuration, 0, 1)
  5417. local fadeMultiplier = 1 - progress
  5418. currentShakeStrength = tinnitusRagdollShakeStrength * fadeMultiplier
  5419.  
  5420. -- Check if fade-out is complete
  5421. if progress >= 1 then
  5422. tinnitusFadeOutActive = false
  5423. tinnitusRagdollActive = false
  5424. end
  5425. else
  5426. -- During normal tinnitus, gradually reduce shake strength over the duration
  5427. local elapsed = CurTime() - tinnitusRagdollStartTime
  5428. local totalDuration = tinnitusRagdollEndTime - tinnitusRagdollStartTime
  5429. local progress = math.Clamp(elapsed / totalDuration, 0, 1)
  5430. currentShakeStrength = tinnitusRagdollShakeStrength * (1 - progress * 0.7) -- Reduce to 30% by the end
  5431.  
  5432. -- Check if duration ended, start fade-out
  5433. if CurTime() >= tinnitusRagdollEndTime then
  5434. tinnitusFadeOutActive = true
  5435. tinnitusFadeOutStartTime = CurTime()
  5436. end
  5437. end
  5438.  
  5439. -- Apply continuous shake with varying frequency (same as damage.lua)
  5440. local t = CurTime()
  5441. local tinnitusShake = Angle(
  5442. math.sin(t * 8) * currentShakeStrength,
  5443. math.cos(t * 8 * 0.8) * currentShakeStrength,
  5444. math.sin(t * 8 * 0.6) * currentShakeStrength * 0.5
  5445. )
  5446. finalAngles = finalAngles + tinnitusShake
  5447. end
  5448.  
  5449. return {
  5450. origin = camPos,
  5451. angles = finalAngles,
  5452. fov = GetConVar("rads_ragdoll_fov"):GetFloat(),
  5453. drawviewer = true
  5454. }
  5455. end
  5456.  
  5457. -- Handle death first-person view
  5458. local deathFirstPersonCvar = GetConVar("rads_death_firstperson")
  5459. if deathFirstPersonCvar and deathFirstPersonCvar:GetBool() and ply:GetNWBool("rads_dead_firstperson") and not ply:Alive() then
  5460. -- Keep the camera at the death position in first-person
  5461. return {
  5462. origin = origin,
  5463. angles = angles,
  5464. fov = fov,
  5465. drawviewer = false
  5466. }
  5467. end
  5468.  
  5469. return nil
  5470. end)
  5471.  
  5472. scrw, scrh = ScrW(), ScrH()
  5473. hook.Add("RenderScreenspaceEffects", "RADS.FFAFAPFPAP", function()
  5474. local ply = LocalPlayer()
  5475. local rag = ply:GetNWBool('radsfa')
  5476. local pulsehigh = ply:GetNWBool('radshighpulse')
  5477. local thirdPersonCvar = GetConVar("rads_thirdperson")
  5478. if rag and thirdPersonCvar and not thirdPersonCvar:GetBool() then end
  5479. if pulsehigh then
  5480. end
  5481. end)
  5482.  
  5483. local grtodown = Material("vgui/gradient-u")
  5484. local grtoup = Material("vgui/gradient-d")
  5485. local grtoright = Material("vgui/gradient-l")
  5486. local grtoleft = Material("vgui/gradient-r")
  5487. pain, painlosing, impulse = 0, 0, 0
  5488. net.Receive("info_pain", function()
  5489. pain = net.ReadFloat()
  5490. painlosing = net.ReadFloat()
  5491. end)
  5492.  
  5493. -- Tinnitus screenshake variables for ragdolls
  5494. local tinnitusRagdollActive = false
  5495. local tinnitusRagdollEndTime = 0
  5496. local tinnitusRagdollStartTime = 0
  5497. local tinnitusRagdollShakeStrength = 0
  5498. local tinnitusFadeOutActive = false
  5499. local tinnitusFadeOutStartTime = 0
  5500. local tinnitusFadeOutDuration = 3 -- Same as damage.lua
  5501.  
  5502. -- Network receiver for tinnitus screenshake state
  5503. net.Receive("RADS_TinnitusScreenshake", function()
  5504. local isActive = net.ReadBool()
  5505. local duration = net.ReadFloat()
  5506. local shakeStrength = net.ReadFloat()
  5507.  
  5508. if isActive then
  5509. -- Start tinnitus screenshake for ragdoll
  5510. tinnitusRagdollActive = true
  5511. tinnitusRagdollStartTime = CurTime()
  5512. tinnitusRagdollEndTime = CurTime() + duration
  5513. tinnitusRagdollShakeStrength = shakeStrength
  5514. tinnitusFadeOutActive = false
  5515. else
  5516. -- Stop tinnitus screenshake for ragdoll
  5517. tinnitusRagdollActive = false
  5518. tinnitusRagdollEndTime = 0
  5519. tinnitusRagdollStartTime = 0
  5520. tinnitusRagdollShakeStrength = 0
  5521. tinnitusFadeOutActive = false
  5522. end
  5523. end)
  5524.  
  5525. local ScrW, ScrH = ScrW, ScrH
  5526. local math_Clamp = math.Clamp
  5527. local k = 0
  5528. local k4 = 0
  5529. local time = 0
  5530.  
  5531. local icons = {}
  5532. net.Receive("CapturePositionLH", function()
  5533. local posLH = net.ReadVector()
  5534. table.insert(icons, {
  5535. pos = posLH,
  5536. time = CurTime()
  5537. })
  5538. end)
  5539.  
  5540. net.Receive("CapturePositionRH", function()
  5541. local posRH = net.ReadVector()
  5542. table.insert(icons, {
  5543. pos = posRH,
  5544. time = CurTime()
  5545. })
  5546. end)
  5547.  
  5548. hook.Add("HUDPaint", "DrawIcons", function()
  5549. if posLH ~= nil or posRH ~= nil then
  5550. for i, icon in ipairs(icons) do
  5551. surface.SetDrawColor(255, 255, 255)
  5552. surface.SetMaterial(Material("vgui/gmod_hand"))
  5553. surface.DrawTexturedRect(icon.pos.x, icon.pos.y, iconWidth, iconHeight)
  5554. if CurTime() - icon.time >= 5 then table.remove(icons, i) end
  5555. end
  5556. end
  5557. end)
  5558.  
  5559.  
  5560.  
  5561. local addmat_r = Material("CA/add_r")
  5562. local addmat_g = Material("CA/add_g")
  5563. local addmat_b = Material("CA/add_b")
  5564. local vgbm = Material("vgui/black")
  5565. local function DrawCA(rx, gx, bx, ry, gy, by)
  5566. render.UpdateScreenEffectTexture()
  5567. addmat_r:SetTexture("$basetexture", render.GetScreenEffectTexture())
  5568. addmat_g:SetTexture("$basetexture", render.GetScreenEffectTexture())
  5569. addmat_b:SetTexture("$basetexture", render.GetScreenEffectTexture())
  5570. render.SetMaterial(vgbm)
  5571. render.DrawScreenQuad()
  5572. render.SetMaterial(addmat_r)
  5573. render.DrawScreenQuadEx(-rx / 2, -ry / 2, ScrW() + rx, ScrH() + ry)
  5574. render.SetMaterial(addmat_g)
  5575. render.DrawScreenQuadEx(-gx / 2, -gy / 2, ScrW() + gx, ScrH() + gy)
  5576. render.SetMaterial(addmat_b)
  5577. render.DrawScreenQuadEx(-bx / 2, -by / 2, ScrW() + bx, ScrH() + by)
  5578. end
  5579.  
  5580. net.Receive("info_impulse", function() impulse = net.ReadFloat() * 50 end)
  5581. local k3 = 0
  5582. hook.Add("RenderScreenspaceEffects", "renderimpulse", function()
  5583. local cheapEffectsCvar = GetConVar("rads_cheapeffects")
  5584. local cheapEffects = cheapEffectsCvar and cheapEffectsCvar:GetInt() or 0
  5585.  
  5586. -- Skip chromatic aberration entirely if cheapeffects >= 2
  5587. if cheapEffects >= 2 then return end
  5588.  
  5589. k3 = math.Clamp(Lerp(0.01, k3, impulse), 0, 50)
  5590.  
  5591. -- Reduce intensity based on cheapeffects level
  5592. local intensity = cheapEffects >= 1 and 0.5 or 1.0
  5593. DrawCA(4 * k3 * intensity, 2 * k3 * intensity, 0, 2 * k3 * intensity, 1 * k3 * intensity, 0)
  5594. end)
  5595.  
  5596. net.Receive('RADS.CHATSAY', function()
  5597. chat.AddText(Color(255, 0, 221), " ") -- end
  5598. end)
  5599.  
  5600. -- REMOVED: Consciousness network receiver - migrated to damage.lua
  5601.  
  5602. -- Vision enhancement variables
  5603. local visionSharpness = 0
  5604. local accumulationBlur = 0
  5605. local smoothShakeStrength = 0
  5606. local smoothShakeSeed = math.random(1000)
  5607. local lastShakeTime = 0
  5608. local shakeDecay = 2.0
  5609.  
  5610. -- Accumulation blur variables (reduced intensity)
  5611. local blurAccumulation = 0
  5612. local maxBlurAccumulation = 0.3 -- Reduced from 0.8 to 0.3
  5613. local blurDecayRate = 0.03 -- Increased decay rate for faster recovery
  5614.  
  5615. -- Add vision enhancement CalcView hook with FOV control (separate from ragdoll camera)
  5616. hook.Add("CalcView", "RADS_VisionEnhancement", function(ply, pos, angles, fov)
  5617. -- Only apply to alive players, not ragdolled ones
  5618. if not ply:Alive() or ply:GetNWBool("radsfa") then return end
  5619.  
  5620. local shake = Angle(0, 0, 0)
  5621.  
  5622. -- Smooth screenshake based on adrenaline/stress
  5623. if smoothShakeStrength > 0.1 then
  5624. local t = CurTime() + smoothShakeSeed
  5625. -- Reduced frequency multipliers for smoother shake
  5626. shake = Angle(
  5627. math.sin(t * 0.8) * smoothShakeStrength * 0.5, -- Reduced from 2.5 to 0.8
  5628. math.cos(t * 0.6) * smoothShakeStrength * 0.3, -- Reduced from 2.2 to 0.6
  5629. math.sin(t * 0.4) * smoothShakeStrength * 0.15 -- Reduced from 1.8 to 0.4
  5630. )
  5631.  
  5632. -- Smoother decay with interpolation
  5633. smoothShakeStrength = Lerp(FrameTime() * shakeDecay * 0.5, smoothShakeStrength, 0) -- Added 0.5 multiplier for gentler decay
  5634. end
  5635.  
  5636. -- Trigger shake on damage or high stress (with reduced sensitivity)
  5637. local pulse = ply:GetNWInt("PlayerPulse", 70)
  5638. if pulse > 120 and CurTime() - lastShakeTime > 1.0 then -- Increased cooldown from 0.5 to 1.0
  5639. smoothShakeStrength = math.min(smoothShakeStrength + (pulse - 120) * 0.01, 1.0) -- Reduced from 0.02 to 0.01 and max from 2.0 to 1.0
  5640. lastShakeTime = CurTime()
  5641. end
  5642.  
  5643. -- Check if player is using a scoped weapon and is aiming
  5644. local weapon = ply:GetActiveWeapon()
  5645. local useScopedFOV = false
  5646. local scopedFOV = nil
  5647.  
  5648. if IsValid(weapon) and weapon.Scoped and weapon.ScopeFoV then
  5649. -- Check if weapon is currently aiming (using the GetAiming method)
  5650. if weapon.GetAiming and weapon:GetAiming() > 99 then
  5651. useScopedFOV = true
  5652. scopedFOV = weapon.ScopeFoV
  5653. end
  5654. end
  5655.  
  5656. -- Apply custom FOV for standing players, unless using a scoped weapon
  5657. local playerFovCvar = GetConVar("rads_player_fov")
  5658. local customFOV = useScopedFOV and scopedFOV or (playerFovCvar and playerFovCvar:GetFloat() or 90)
  5659.  
  5660. -- Always return a view table to ensure FOV is applied
  5661. return {
  5662. origin = pos,
  5663. angles = angles + shake,
  5664. fov = customFOV
  5665. }
  5666. end)
  5667.  
  5668. -- Optimized vision sharpening and accumulation blur effects with performance scaling
  5669. local lastVisionEffectUpdate = 0
  5670. local visionEffectInterval = 0.016 -- ~60fps base
  5671.  
  5672. hook.Add("RenderScreenspaceEffects", "RADS_VisionEffects", function()
  5673. local ply = LocalPlayer()
  5674. if not IsValid(ply) or not ply:Alive() then return end
  5675.  
  5676. -- Don't apply effects if ragdolled (to avoid interfering with ragdoll camera)
  5677. if ply:GetNWBool("radsfa") then return end
  5678.  
  5679. local cheapEffectsCvar = GetConVar("rads_cheapeffects")
  5680. local cheapEffects = cheapEffectsCvar and cheapEffectsCvar:GetInt() or 0
  5681.  
  5682. -- Skip all vision effects if cheapeffects >= 2
  5683. if cheapEffects >= 2 then return end
  5684.  
  5685. -- Throttle updates based on cheapeffects level
  5686. if cheapEffects >= 1 then
  5687. visionEffectInterval = 0.033 -- ~30fps for moderate performance
  5688. if CurTime() - lastVisionEffectUpdate < visionEffectInterval then return end
  5689. end
  5690. lastVisionEffectUpdate = CurTime()
  5691.  
  5692. local pulse = ply:GetNWInt("PlayerPulse", 70)
  5693. local adrenalineLevel = ply.adrenaline or 0
  5694.  
  5695. -- Vision sharpening based on adrenaline (reduced for cheapeffects)
  5696. if adrenalineLevel > 20 then
  5697. local targetSharpness = math.Clamp(adrenalineLevel / 100, 0, cheapEffects >= 1 and 0.4 or 0.8)
  5698. visionSharpness = Lerp(FrameTime() * 3, visionSharpness, targetSharpness)
  5699. else
  5700. visionSharpness = Lerp(FrameTime() * 2, visionSharpness, 0)
  5701. end
  5702.  
  5703. -- Apply vision sharpening (reduced intensity for cheapeffects)
  5704. if visionSharpness > 0.1 and cheapEffects < 1 then
  5705. local intensity = cheapEffects >= 1 and 0.5 or 1.0
  5706. local sharpenTab = {
  5707. ["$pp_colour_addr"] = 0,
  5708. ["$pp_colour_addg"] = 0,
  5709. ["$pp_colour_addb"] = 0,
  5710. ["$pp_colour_brightness"] = visionSharpness * 0.1 * intensity,
  5711. ["$pp_colour_contrast"] = 1 + (visionSharpness * 0.3 * intensity),
  5712. ["$pp_colour_colour"] = 1 + (visionSharpness * 0.2 * intensity),
  5713. ["$pp_colour_mulr"] = 1,
  5714. ["$pp_colour_mulg"] = 1,
  5715. ["$pp_colour_mulb"] = 1
  5716. }
  5717. DrawColorModify(sharpenTab)
  5718. end
  5719.  
  5720. -- Skip blur effects entirely if cheapeffects >= 1
  5721. if cheapEffects >= 1 then return end
  5722.  
  5723. -- Accumulation blur based on stress/fatigue (only for full quality)
  5724. local stressFactor = 0
  5725. if pulse > 100 then
  5726. stressFactor = math.Clamp((pulse - 100) / 50, 0, 1)
  5727. end
  5728.  
  5729. if ply.pain and ply.pain > 50 then
  5730. stressFactor = stressFactor + math.Clamp(ply.pain / 200, 0, 0.5)
  5731. end
  5732.  
  5733. -- Accumulate blur over time when stressed (reduced accumulation rate)
  5734. if stressFactor > 0.2 then
  5735. blurAccumulation = math.min(blurAccumulation + (stressFactor * FrameTime() * 0.15), maxBlurAccumulation)
  5736. else
  5737. blurAccumulation = math.max(blurAccumulation - (FrameTime() * blurDecayRate), 0)
  5738. end
  5739.  
  5740. -- Apply accumulation blur (reduced intensity)
  5741. if blurAccumulation > 0.05 then
  5742. DrawMotionBlur(0.05, blurAccumulation * 0.6, 0.005)
  5743. end
  5744. end)
  5745.  
  5746. -- Trigger enhanced shake on damage
  5747. hook.Add("EntityTakeDamage", "RADS_VisionShakeOnDamage", function(target, dmginfo)
  5748. if target == LocalPlayer() and dmginfo:GetDamage() > 5 then
  5749. local shakeAmount = math.Clamp(dmginfo:GetDamage() / 20, 0.5, 3.0)
  5750. smoothShakeStrength = math.min(smoothShakeStrength + shakeAmount, 4.0)
  5751. lastShakeTime = CurTime()
  5752. end
  5753. end)
  5754.  
  5755. -- Reset pulse-related visual effects on player death
  5756. hook.Add("PostPlayerDeath", "RADS_ClearPulseEffects", function()
  5757. -- Reset vision enhancement variables
  5758. visionSharpness = 0
  5759. accumulationBlur = 0
  5760. smoothShakeStrength = 0
  5761. smoothShakeSeed = math.random(1000)
  5762. lastShakeTime = 0
  5763.  
  5764. -- Reset accumulation blur variables
  5765. blurAccumulation = 0
  5766.  
  5767. -- Reset impulse effects
  5768. impulse = 0
  5769. k3 = 0
  5770.  
  5771. -- Reset pain effects
  5772. pain = 0
  5773. painlosing = 0
  5774.  
  5775. -- NOTE: Consciousness effects are now handled in damage.lua
  5776. end)
  5777.  
  5778. -- Reset pulse-related visual effects on player spawn
  5779. hook.Add("PlayerSpawn", "RADS_ResetPulseEffects", function(ply)
  5780. if ply == LocalPlayer() then
  5781. -- Reset vision enhancement variables
  5782. visionSharpness = 0
  5783. accumulationBlur = 0
  5784. smoothShakeStrength = 0
  5785. smoothShakeSeed = math.random(1000)
  5786. lastShakeTime = 0
  5787.  
  5788. -- Reset accumulation blur variables
  5789. blurAccumulation = 0
  5790.  
  5791. -- Reset impulse effects
  5792. impulse = 0
  5793. k3 = 0
  5794.  
  5795. -- Reset pain effects
  5796. pain = 0
  5797. painlosing = 0
  5798.  
  5799. -- NOTE: Consciousness effects are now handled in damage.lua
  5800. end
  5801. end)
  5802.  
  5803. -- Bullseye damage redirection hook
  5804. hook.Add("EntityTakeDamage", "RADS_BullseyeDamageRedirect", function(target, dmginfo)
  5805. if IsValid(target) and target:GetClass() == "npc_bullseye" then
  5806. local owner = target:GetNWEntity("owner")
  5807. local ragdoll = target:GetNWEntity("ragdoll")
  5808.  
  5809. if IsValid(owner) and IsValid(ragdoll) then
  5810. -- Check if player is dead - if so, don't redirect damage
  5811. if owner:Health() <= 0 then
  5812. print("[RADS BULLSEYE] Player " .. owner:Nick() .. " is dead, blocking damage redirection")
  5813. -- Remove bullseye since player is dead
  5814. if IsValid(target) then
  5815. print("[RADS BULLSEYE] Removing bullseye for dead player: " .. owner:Nick())
  5816. target:Remove()
  5817. if IsValid(ragdoll) then
  5818. ragdoll.bullseye = nil
  5819. end
  5820. end
  5821. return true -- Block damage to bullseye
  5822. end
  5823.  
  5824. print("[RADS BULLSEYE] Redirecting damage from bullseye to player: " .. owner:Nick() .. " (Damage: " .. dmginfo:GetDamage() .. ")")
  5825.  
  5826. -- Create new damage info for the player
  5827. local newDmgInfo = DamageInfo()
  5828. newDmgInfo:SetDamage(dmginfo:GetDamage())
  5829. newDmgInfo:SetAttacker(dmginfo:GetAttacker())
  5830. newDmgInfo:SetInflictor(dmginfo:GetInflictor())
  5831. newDmgInfo:SetDamageType(dmginfo:GetDamageType())
  5832. newDmgInfo:SetDamagePosition(dmginfo:GetDamagePosition())
  5833. newDmgInfo:SetDamageForce(dmginfo:GetDamageForce())
  5834. newDmgInfo:SetReportedPosition(dmginfo:GetReportedPosition())
  5835.  
  5836. -- Apply damage to the player
  5837. owner:TakeDamageInfo(newDmgInfo)
  5838.  
  5839. -- Prevent the bullseye from taking damage
  5840. return true
  5841. else
  5842. print("[RADS BULLSEYE] Warning: Bullseye damaged but owner or ragdoll is invalid")
  5843. end
  5844. end
  5845. end)
  5846.  
  5847. -- Death check timer for bullseye cleanup
  5848. timer.Create("RADS_BullseyeDeathCheck", 1, 0, function()
  5849. for _, ragdoll in pairs(ents.FindByClass("prop_ragdoll")) do
  5850. if IsValid(ragdoll) and IsValid(ragdoll.bullseye) then
  5851. local owner = ragdoll:GetNWEntity("owner")
  5852. if IsValid(owner) and owner:Health() <= 0 then
  5853. print("[RADS BULLSEYE] Death check: Removing bullseye for dead player: " .. owner:Nick())
  5854. ragdoll.bullseye:Remove()
  5855. ragdoll.bullseye = nil
  5856. -- Set all NPCs to neutral towards this bullseye (cleanup)
  5857. for _, npc in pairs(ents.FindByClass("npc_*")) do
  5858. if IsValid(npc) and npc.AddEntityRelationship then
  5859. npc:AddEntityRelationship(ragdoll.bullseye, D_NU, 99)
  5860. end
  5861. end
  5862. end
  5863. end
  5864. end
  5865. end)
  5866.  
  5867. -- Clientside drowning sound handling
  5868. if CLIENT then
  5869. local drowningSound = nil
  5870.  
  5871. net.Receive("PlayDrowningSound", function()
  5872. if drowningSound then
  5873. drowningSound:Stop()
  5874. end
  5875. drowningSound = CreateSound(LocalPlayer(), "drowning.ogg")
  5876. if drowningSound then
  5877. drowningSound:SetSoundLevel(75)
  5878. drowningSound:ChangeVolume(GetConVar("rads_drowning_sound_volume"):GetFloat())
  5879. drowningSound:Play()
  5880. end
  5881. end)
  5882.  
  5883. net.Receive("StopDrowningSound", function()
  5884. if drowningSound then
  5885. drowningSound:Stop()
  5886. drowningSound = nil
  5887. end
  5888. end)
  5889.  
  5890. -- Clean up sound on player death/disconnect
  5891. hook.Add("PlayerDisconnected", "RADS_CleanupDrowningSound", function(ply)
  5892. if ply == LocalPlayer() and drowningSound then
  5893. drowningSound:Stop()
  5894. drowningSound = nil
  5895. end
  5896. end)
  5897.  
  5898. hook.Add("PostPlayerDeath", "RADS_CleanupDrowningSound", function(ply)
  5899. if ply == LocalPlayer() and drowningSound then
  5900. drowningSound:Stop()
  5901. drowningSound = nil
  5902. end
  5903. end)
  5904. end
  5905.  
  5906. end