Compare commits
5 commits
d9415d880f
...
72c4515402
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72c4515402 | ||
|
|
5af8fdc14a | ||
|
|
786e997351 | ||
|
|
278a0a9d7d | ||
|
|
259a3da4e1 |
8 changed files with 384 additions and 93 deletions
35
README.md
35
README.md
|
|
@ -4,6 +4,8 @@ Adds better NPC integration for the Garry's Mod addon Z-City. While Z-City has a
|
|||
|
||||
- NPCs can be ragdolled like players can, and will wiggle around on the ground. They can get back up, too!
|
||||
- It takes all elements of Z-City's health system into account, such as unconsciousness and pain.
|
||||
- NPCs are responsive to damage from many sources -- bullets, fire, melee (especially kicks!) and more.
|
||||
- NPCs can also see and target downed NPCs, and will prioritize standing NPCs over them when needed.
|
||||
- Various configurable settings are included, allowing you to tweak the mod to your liking.
|
||||
|
||||
This mod is built to perform reasonably well, thus it includes optimizations, configurable performance settings, and an automatic corpse remover.
|
||||
|
|
@ -12,9 +14,9 @@ This addon is based off of Kazarei's Euphoria, which in turn is a fork of Fedhor
|
|||
|
||||
## Known issues
|
||||
|
||||
- Bandages, tourniquets, etc. that are on the ragdoll or NPC body will not be there when they get back up. Don't know how to fix this unfortunately.
|
||||
- NPCs currently instantly get up, with no animation. I plan on remedying this in the future.
|
||||
- Enemy NPCs will not target living downed NPCs like how they do players. This will be fixed.
|
||||
- Bandages, tourniquets, etc. that are on the ragdoll or NPC body will not be there when they get back up. Don't know how to fix this right now, though if anyone experienced with Z-City's codebase would know how to I would appreciate knowing.
|
||||
- The NPC getting up animation is quite ridiculous and a little ugly. Honestly, getting it to work that much was a struggle in itself and I'm terrified of trying to do anything more than that. If anyone else would like to do that, however, feel free to create a discussion and I can send you the source code link.
|
||||
- Occasionally the game thinks that all damage is bullet damage? God knows why. I'll figure it out eventually.
|
||||
|
||||
## Compatibility
|
||||
|
||||
|
|
@ -28,10 +30,27 @@ It is, however, not compatbile with:
|
|||
- Most mods that modify the behavior of death ragdolls (Reagdoll, Artagdoll, especially Fedhoria.)
|
||||
- Mods that automatically remove death ragdolls (it will delete all ragdolled NPCs, even if they're still alive! Try the built-in automatic corpse removal instead.)
|
||||
|
||||
## Future plans
|
||||
## Todo list
|
||||
|
||||
No guarantees any of this will become real.
|
||||
Urgent shit:
|
||||
|
||||
- NPCs will back out of combat when injured.
|
||||
- Friendly NPCs can heal each other.
|
||||
- NPCs can heal themselves.
|
||||
- Large amounts of damage (such as shotguns) fling NPC ragdolls to ridiculous lengths, sometimes severely injuring the player. While this is absolutely fucking hilarious, it also isn't the best experience.
|
||||
|
||||
Stuff to do:
|
||||
|
||||
- Facial expressions
|
||||
- Actual specific reactions to being on fire / falling / lots of pain
|
||||
- Wound holding
|
||||
- NPCs should not stumble when dead
|
||||
- Generally improve stumbling
|
||||
|
||||
Stuff to fix possibly:
|
||||
|
||||
- NPC targeting works, but works really weirdly (sometimes it just. keeps targeting corpses when it shouldn't.) Why? Who knows!!!!
|
||||
- Look into improving the unfake animation? Really scared to try and touch it again but it would be worthwhile
|
||||
|
||||
Post release ideas:
|
||||
|
||||
- Localization support for other languages
|
||||
- ReaSFX integration would probably be nice. I mean there's so many SFX packs to choose from, so I don't even have to pack it in
|
||||
- To be entirely honest Fedhoria isn't the best addon out there for euphoria physics. Maybe rebase to another addon (or alternatively build a new one?)
|
||||
|
|
@ -37,6 +37,12 @@ local function PopulateRagdollSBXToolMenu(pnl)
|
|||
|
||||
pnl:NumSlider("Wound grab time", "zcnpci_woundgrab_time", 0, 10, 3)
|
||||
pnl:ControlHelp("How long the ragdoll should hold its wound.")
|
||||
|
||||
pnl:CheckBox("Allow getting up", "zcnpci_unfake_enabled")
|
||||
pnl:ControlHelp("If enabled, NPCs will get back up if able.")
|
||||
|
||||
pnl:NumSlider("Get up time", "zcnpci_unfake_time", 0, 5, 3)
|
||||
pnl:ControlHelp("How long the getting up animation lasts. Setting to 0 will disable the getting up animation.")
|
||||
|
||||
pnl:NumSlider("Minimum down time", "zcnpci_down_time", 0, 20, 3)
|
||||
pnl:ControlHelp("The minimum amount of time the ragdoll should be down before trying to get back up.")
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ end
|
|||
function ENT:Initialize()
|
||||
if !self.Module then return end
|
||||
|
||||
self:SetModel(self.Module.Model)
|
||||
if self.Module.Model then self:SetModel(self.Module.Model) end
|
||||
|
||||
for key, value in pairs(self.Module) do
|
||||
if !self[key] then
|
||||
|
|
|
|||
99
lua/entities/npc_ragdoll_target.lua
Normal file
99
lua/entities/npc_ragdoll_target.lua
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "ai"
|
||||
ENT.Base = "base_ai"
|
||||
ENT.AutomaticFrameAdvance = true
|
||||
|
||||
local debug_show_ragdoll_targets = CreateConVar("zcnpci_debug_show_ragdoll_targets", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||
|
||||
local function UpdateRelationship(ent, me)
|
||||
if !IsValid(ent) or !ent:IsNPC() then return end
|
||||
|
||||
if ent:Disposition(me.dummy_entity) == D_HT then
|
||||
ent:AddEntityRelationship(me, D_HT, -1)
|
||||
end
|
||||
end
|
||||
|
||||
local function RemoveRelationship(ent, me)
|
||||
if !IsValid(ent) or !ent:IsNPC() then return end
|
||||
|
||||
if ent:Disposition(me.dummy_entity) == D_HT then
|
||||
ent:AddEntityRelationship(me, D_NU, -1)
|
||||
ent:ClearEnemyMemory(me)
|
||||
|
||||
if ent:GetEnemy() == me then
|
||||
ent:SetEnemy(nil)
|
||||
ent:ClearSchedule()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
if SERVER then
|
||||
self:SetModel("models/maxofs2d/hover_basic.mdl")
|
||||
self:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
|
||||
self:SetHullType(HULL_TINY_CENTERED)
|
||||
self:SetNoDraw(!debug_show_ragdoll_targets:GetBool())
|
||||
self.dummy_entity = ents.Create(self.ragdoll_to_follow.class_in_previous_life)
|
||||
self.last_stop_targeting = nil
|
||||
|
||||
hook.Add("OnEntityCreated", "zcnpci-"..self:GetCreationID(), function(new_entity)
|
||||
if !IsValid(new_entity) or !new_entity:IsNPC() then return end
|
||||
|
||||
if !self.last_stop_targeting then
|
||||
UpdateRelationship(new_entity, self)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
if SERVER then
|
||||
local ragdoll = self.ragdoll_to_follow
|
||||
if !IsValid(ragdoll) then return end
|
||||
|
||||
self:SetPos(ragdoll:GetPos())
|
||||
|
||||
self:SetNoDraw(!debug_show_ragdoll_targets:GetBool())
|
||||
|
||||
if !ragdoll.organism then return true end
|
||||
|
||||
local should_stop_targeting = (
|
||||
(ragdoll.organism.consciousness <= 0.4) or
|
||||
(ragdoll.organism.critical)
|
||||
)
|
||||
|
||||
if should_stop_targeting != self.last_stop_targeting then
|
||||
self.last_stop_targeting = should_stop_targeting
|
||||
|
||||
if should_stop_targeting then
|
||||
self:SetColor(Color(255, 0, 0))
|
||||
for i,ent in ipairs(ents.GetAll()) do
|
||||
RemoveRelationship(ent, self)
|
||||
end
|
||||
else
|
||||
self:SetColor(Color(0, 255, 0))
|
||||
for i,ent in ipairs(ents.GetAll()) do
|
||||
UpdateRelationship(ent, self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self:NextThink(CurTime())
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if SERVER then
|
||||
for i,ent in ipairs(ents.GetAll()) do
|
||||
RemoveRelationship(ent, self)
|
||||
end
|
||||
|
||||
self.dummy_entity:Remove()
|
||||
|
||||
hook.Remove("OnEntityCreated", "zcnpci-"..self:GetCreationID())
|
||||
end
|
||||
end
|
||||
|
|
@ -19,7 +19,9 @@ local treat_near_death_as_dead = CreateConVar("zcnpci_treat_near_death_as_dead"
|
|||
local allow_extended_base_npcs = CreateConVar("zcnpci_allow_extended_base_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||
local allow_modded_npcs = CreateConVar("zcnpci_allow_modded_npcs", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||
local auto_enable_humanoid_npcs = CreateConVar("zcnpci_auto_enable_humanoid_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||
local modded_npc_whitelist = CreateConVar("zcnpci_modded_npc_whitelist", "nb_example", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||
local modded_npc_whitelist = CreateConVar("zcnpci_modded_npc_whitelist", "npc_example_class", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||
|
||||
local debug_ragdoll_all = CreateConVar("zcnpci_debug_ragdoll_all", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||
|
||||
-- The way I have to set up the npc whitelist client-side makes it so the host can't configure it without console commands, so we have to do this shit
|
||||
local set_modded_npc_whitelist = concommand.Add("zcnpci_set_modded_npc_whitelist", function(ply, cmd, args)
|
||||
|
|
@ -30,8 +32,6 @@ local set_modded_npc_whitelist = concommand.Add("zcnpci_set_modded_npc_whitelis
|
|||
modded_npc_whitelist:SetString(args[1])
|
||||
end)
|
||||
|
||||
local debug_ragdoll_all = CreateConVar("zcnpci_debug_ragdoll_all", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||
|
||||
local last_dmgpos = {}
|
||||
local last_hitgroup = {}
|
||||
local last_attacker = {}
|
||||
|
|
@ -121,6 +121,11 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
|
|||
ragdoll:GetPhysicsObject():SetVelocity(ent.npcfakeknockback)
|
||||
end
|
||||
|
||||
local velocity = ragdoll:GetPhysicsObject():GetVelocity()
|
||||
|
||||
print(velocity)
|
||||
print(max_clamp)
|
||||
|
||||
-- For some reason, citizen types such as rebels, medics, refugees, etc. use a keyvalue.
|
||||
-- That keyvalue does not transfer over to the ragdoll, despite it looking like the NPC,
|
||||
-- and you have to manually save it to the ragdoll to be able to make it work.
|
||||
|
|
@ -128,9 +133,11 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
|
|||
ragdoll.citizentype = ent:GetInternalVariable("citizentype")
|
||||
|
||||
timer.Simple(0, function()
|
||||
if !IsValid(ragdoll) then return end
|
||||
|
||||
ragdoll.organism.alive = true
|
||||
|
||||
if !IsValid(ragdoll) then return end
|
||||
ragdoll:SetRenderMode(RENDERMODE_NORMAL)
|
||||
|
||||
zcnpci.StartModule(ragdoll, "stumble_legs", phys_bone, lpos)
|
||||
last_dmgpos[ent] = nil
|
||||
|
|
@ -139,36 +146,44 @@ end)
|
|||
|
||||
hook.Add("HomigradDamage", "zcnpci", function(ent, dmginfo)
|
||||
if !enabled:GetBool() or !IsValid(dmginfo) then return end
|
||||
|
||||
if !ent:IsNPC() then
|
||||
if !ent.organism or !ent.StartDie then return end
|
||||
|
||||
if dmginfo:IsDamageType(DMG_BULLET + DMG_BUCKSHOT + DMG_BLAST + DMG_CLUB + DMG_SLASH + DMG_GENERIC) then
|
||||
-- Reset ragdoll get up timer -- don't want someone getting up mid-curbstomp
|
||||
ent.StartDie = CurTime()
|
||||
print("I'M HIT FOR "..dmginfo:GetDamage())
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
last_dmgpos[ent] = dmginfo:GetDamage()
|
||||
last_dmgpos[ent] = dmginfo:GetDamagePosition()
|
||||
last_attacker[ent] = dmginfo:GetAttacker()
|
||||
|
||||
if dmginfo:IsDamageType(DMG_BULLET + DMG_BUCKSHOT + DMG_BLAST) then
|
||||
if dmginfo:GetDamage() > 3 then
|
||||
table.insert(npcs_to_fake, ent)
|
||||
|
||||
local attacker_angle = dmginfo:GetAttacker():EyeAngles()
|
||||
|
||||
local normal = attacker_angle:Forward(normal)
|
||||
|
||||
ent.npcfakeknockback = normal * dmginfo:GetDamage() * 0
|
||||
end
|
||||
elseif dmginfo:IsDamageType(DMG_CLUB + DMG_SLASH) then
|
||||
local fists = dmginfo:GetAttacker():GetWeapon("weapon_hands_sh")
|
||||
if !IsValid(fists) then fists = dmginfo:GetAttacker():GetWeapon("weapon_hg_coolhands") end
|
||||
local attacker = dmginfo:GetAttacker()
|
||||
if !IsValid(attacker) then return end
|
||||
|
||||
local fists = attacker:HasWeapon("weapon_hands_sh")
|
||||
if !fists then fists = dmginfo:GetAttacker():HasWeapon("weapon_hg_coolhands") end
|
||||
|
||||
local attacker = dmginfo:GetAttacker()
|
||||
|
||||
print(fists.InLegKick)
|
||||
|
||||
-- Kicks should knock NPCs down
|
||||
if IsValid(dmginfo:GetInflictor()) and IsValid(fists) and attacker.InLegKick and ((attacker.InLegKick + 0.1) > CurTime()) then
|
||||
if IsValid(dmginfo:GetInflictor()) and fists and attacker.InLegKick and ((attacker.InLegKick + 0.1) > CurTime()) then
|
||||
table.insert(npcs_to_fake, ent)
|
||||
print("ADD TO LIST!!!")
|
||||
|
||||
local attacker_angle = dmginfo:GetAttacker():EyeAngles()
|
||||
|
||||
|
|
@ -287,8 +302,6 @@ hook.Add("Tick", "zcnpci", function()
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
PrintTable(npcs_to_fake)
|
||||
|
||||
-- NPC faking is in a seperate area because we want NPCs to ragdoll as soon as they can
|
||||
for i,ent in pairs(npcs_to_fake) do
|
||||
|
|
@ -304,13 +317,6 @@ hook.Add("Tick", "zcnpci", function()
|
|||
end
|
||||
end)
|
||||
|
||||
--[[hook.Add("OnNPCKilled", "Fedhoria", function(ent, attacker, inflictor)
|
||||
if (!enabled:GetBool() or !npcs:GetBool()) then return end
|
||||
if (!ent:IsNPC() or dmginfo:GetDamage() < ent:Health()) then return end
|
||||
|
||||
last_dmgpos[ent] = dmginfo:GetDamagePosition()
|
||||
end)]]
|
||||
|
||||
hook.Add("OnEntityCreated", "zcnpci", function(ent)
|
||||
if !IsValid(ent) then return end
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,31 @@ local twitchable_bone_names = {
|
|||
"ValveBiped.Bip01_L_Foot"
|
||||
}
|
||||
|
||||
local fakeup_bone_names = {
|
||||
"ValveBiped.Bip01_Pelvis",
|
||||
--[[]"ValveBiped.Bip01_Spine2",]]
|
||||
"ValveBiped.Bip01_Head1",
|
||||
--[[]"ValveBiped.Bip01_R_Thigh",
|
||||
"ValveBiped.Bip01_L_Thigh",
|
||||
"ValveBiped.Bip01_L_Calf",
|
||||
"ValveBiped.Bip01_R_Calf",
|
||||
"ValveBiped.Bip01_R_Foot",
|
||||
"ValveBiped.Bip01_L_Foot",
|
||||
--[["ValveBiped.Bip01_R_Upperarm",
|
||||
--[["ValveBiped.Bip01_L_Upperarm",
|
||||
"ValveBiped.Bip01_R_Forearm",
|
||||
"ValveBiped.Bip01_L_Forearm",
|
||||
"ValveBiped.Bip01_R_Hand",
|
||||
"ValveBiped.Bip01_L_Hand",]]
|
||||
}
|
||||
|
||||
local fakeup_bone_down_names = {
|
||||
"ValveBiped.Bip01_R_Forearm",
|
||||
"ValveBiped.Bip01_L_Forearm",
|
||||
"ValveBiped.Bip01_R_Thigh",
|
||||
"ValveBiped.Bip01_L_Thigh",
|
||||
}
|
||||
|
||||
-- Local Copies of Functions for Optimization
|
||||
local math_Clamp = math.Clamp
|
||||
local math_Rand = math.Rand
|
||||
|
|
@ -69,7 +94,11 @@ local cv_anim_roll_impact_threshold = CreateConVar("zcnpci_falling_anim_roll_imp
|
|||
local cv_anim_roll_playback_rate = CreateConVar("zcnpci_falling_anim_roll_playback_rate", "3.0", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Скорость воспроизведения анимации")
|
||||
|
||||
-- Down time
|
||||
local minimum_down_time = CreateConVar("zcnpci_down_time", "3", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Down time!!!!!!!!!")
|
||||
local minimum_down_time = CreateConVar("zcnpci_down_time", "5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Down time!!!!!!!!!")
|
||||
|
||||
-- Unfaking
|
||||
local can_unfake = CreateConVar("zcnpci_unfake_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Whether an NPC can unfake")
|
||||
local unfake_time = CreateConVar("zcnpci_unfake_time", "1.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Time it takes for an NPC to unfake")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Tug function
|
||||
|
|
@ -165,7 +194,13 @@ function MODULE:Init()
|
|||
self.AnimationRollEndTime = 0
|
||||
self.StopProcessing = false
|
||||
self.LastThink = CurTime()
|
||||
self.LastPhysProcess = CurTime()
|
||||
self.LastFakeUpCheck = CurTime()
|
||||
|
||||
self.bullseye = ents.Create("npc_ragdoll_target")
|
||||
self.bullseye:SetPos(target:GetPos())
|
||||
self.bullseye.ragdoll_to_follow = target
|
||||
self.bullseye:Spawn()
|
||||
self.bullseye:Activate()
|
||||
|
||||
-- Add damping to all bones.
|
||||
local phys = self:GetPhysicsObject()
|
||||
|
|
@ -189,12 +224,21 @@ end
|
|||
---------------------------------------------------------------------------]]
|
||||
|
||||
function MODULE:Think()
|
||||
if (CurTime() - self.LastThink) < 1 then return end
|
||||
--if (CurTime() - self.LastThink) < 1 then return end
|
||||
local target = self:GetTarget()
|
||||
if !IsValid(target) then return end
|
||||
|
||||
self.LastThink = CurTime()
|
||||
|
||||
local phys = target:GetPhysicsObject()
|
||||
|
||||
if target.FakeUp then
|
||||
print("fucking it")
|
||||
local parent = self.FakeParent
|
||||
if !IsValid(parent) then return end
|
||||
|
||||
--parent:FrameAdvance(FrameTime())
|
||||
end
|
||||
|
||||
if !IsValid(phys) or !phys:IsAsleep() then return end
|
||||
|
||||
|
|
@ -242,15 +286,84 @@ end
|
|||
Physics Simulation Hook (FIXED)
|
||||
---------------------------------------------------------------------------]]
|
||||
function MODULE:PhysicsSimulate(phys, dt)
|
||||
if self.StopProcessing then return false end
|
||||
--print((CurTime() - self.LastPhysProcess) < 0.05)
|
||||
--if (CurTime() - self.LastPhysProcess) < (1 /) then return false end
|
||||
|
||||
self.LastPhysProcess = CurTime()
|
||||
|
||||
local cur_time = CurTime()
|
||||
local target = self:GetTarget()
|
||||
if not IsValid(target) then self:Remove(); return false end
|
||||
if not IsValid(target) then self:Remove(); self.bullseye:Remove(); return false end
|
||||
|
||||
if target.FakeUp then
|
||||
local parent = self.FakeParent
|
||||
|
||||
if !IsValid(parent) then
|
||||
self:Remove()
|
||||
return false
|
||||
end
|
||||
|
||||
if !self.ModelBoneList then
|
||||
self.ModelBoneList = {}
|
||||
|
||||
local i = 0
|
||||
|
||||
while i < target:GetBoneCount() do
|
||||
table.insert(self.ModelBoneList, target:GetBoneName(i))
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
local animation_progress = (CurTime() - self.FakeUpStart) / (self.FakeUpEnd - self.FakeUpStart)
|
||||
|
||||
--[[util.TraceLine({
|
||||
start = parent:GetPos() + Vector(0, 128, 0),
|
||||
endpos = parent:GetPos(),
|
||||
collisiongroup = COLLISION_GROUP_NPC
|
||||
})]]
|
||||
|
||||
for i,v in pairs(fakeup_bone_down_names) do
|
||||
local object = target:GetPhysicsObjectNum(target:TranslateBoneToPhysBone(target:LookupBone(v)))
|
||||
|
||||
object:SetMass(0.5)
|
||||
end
|
||||
|
||||
for i,v in pairs(fakeup_bone_names) do
|
||||
local object = target:GetPhysicsObjectNum(target:TranslateBoneToPhysBone(target:LookupBone(v)))
|
||||
local parent_bone = parent:LookupBone(v)
|
||||
|
||||
if parent_bone == -1 then continue end
|
||||
|
||||
local parent_bone_matrix = parent:GetBoneMatrix(parent_bone)
|
||||
if !parent_bone_matrix then continue end
|
||||
|
||||
local parent_bone_pos, parent_bone_angle = parent_bone_matrix:GetTranslation(), parent_bone_matrix:GetAngles()
|
||||
parent_bone_angle.y = parent_bone_angle.y + 90
|
||||
|
||||
local shadow_data = {
|
||||
secondstoarrive = 0.01,
|
||||
pos = LerpVector(animation_progress, object:GetPos(), parent_bone_pos),
|
||||
angle = LerpAngle(animation_progress, object:GetAngles(), parent_bone_angle),
|
||||
maxspeed = 400,
|
||||
maxangular = 2000,
|
||||
maxspeeddamp = 60,
|
||||
maxangularspeeddamp = 600,
|
||||
}
|
||||
|
||||
-- Can't set position inside physics tick, crashes the game instantly.
|
||||
-- Instead, send a shadow for it to follow (I think? I don't know GMod is kinda funky)
|
||||
object:ComputeShadowControl(shadow_data)
|
||||
|
||||
object:Wake()
|
||||
object:EnableGravity(false)
|
||||
object:Wake()
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
--print((CurTime() - self.LastPhysProcess) < 0.05)
|
||||
--if (CurTime() - self.LastPhysProcess) < (1 /) then return false end())
|
||||
|
||||
phys:Wake()
|
||||
|
||||
--self.bullseye:SetAngles(target:EyeAngles())
|
||||
|
||||
target.is_npc_corpse = true
|
||||
|
||||
|
|
@ -272,71 +385,117 @@ function MODULE:PhysicsSimulate(phys, dt)
|
|||
|
||||
if !target.organism then
|
||||
self:Remove()
|
||||
self.bullseye:Remove()
|
||||
return false -- Cut the bullshit
|
||||
end
|
||||
|
||||
if (!target.organism.alive) then
|
||||
-- If the NPC is dead, they probably aren't coming back; don't bother bringing them back to life
|
||||
self:Remove()
|
||||
self.bullseye:Remove()
|
||||
return false -- Cut the bullshit
|
||||
elseif (target.organism.consciousness <= 0.5) or ((target.organism.lleg >= 0.85) and (target.organism.rleg >= 0.85)) then
|
||||
target.StartDie = cur_time
|
||||
return false
|
||||
end
|
||||
|
||||
-- Getting up
|
||||
-- Don't need to check for consciousness and the like because we've done it already above
|
||||
if (
|
||||
(minimum_down_timer >= 1.0) and
|
||||
(target.class_in_previous_life != nil) and
|
||||
!(target.organism.llegamputated or target.organism.rlegamputated or target.organism.larmamputated or target.organism.rarmamputated) and
|
||||
(target.organism.pain <= 80) and
|
||||
(target.organism.consciousness > 0.65) and
|
||||
!target:IsOnFire()
|
||||
) then
|
||||
local ent = ents.Create(target.class_in_previous_life)
|
||||
ent:SetPos(target:GetPos())
|
||||
ent:SetModel(target:GetModel())
|
||||
ent:SetMaterial(target:GetMaterial())
|
||||
if ((CurTime() - self.LastFakeUpCheck) >= 1.0) and (can_unfake:GetBool()) then
|
||||
self.LastFakeUpCheck = CurTime()
|
||||
|
||||
ent:SetSkin(target:GetSkin())
|
||||
-- This basically checks if the NPC will be in collision with anything when it spawns.
|
||||
-- Side note: the mins and maxs values are slightly larger than the default npc_citizen
|
||||
-- hitboxes, mostly because I assume that most humanoid NPCs will be around that size.
|
||||
local tracehull = util.TraceHull({
|
||||
start = target:GetPos(),
|
||||
endpos = target:GetPos(),
|
||||
mins = Vector(-20, -20, 0),
|
||||
maxs = Vector(20, 20, 84),
|
||||
mask = MASK_NPCSOLID,
|
||||
filter = { target }
|
||||
})
|
||||
|
||||
if target.citizentype then
|
||||
ent:SetKeyValue("citizentype", target.citizentype)
|
||||
end
|
||||
|
||||
for i = 0, target:GetNumBodyGroups() - 1 do
|
||||
ent:SetBodygroup(i, target:GetBodygroup(i))
|
||||
end
|
||||
|
||||
ent:Spawn()
|
||||
|
||||
timer.Simple(0, function()
|
||||
hg.organism.Add(ent)
|
||||
table.Merge(ent.organism, target.organism)
|
||||
|
||||
ent.tourniquets = table.Copy(target.tourniquets)
|
||||
ent.bandaged_limbs = table.Copy(target.bandaged_limbs)
|
||||
|
||||
ent.organism.alive = true
|
||||
ent.organism.owner = ent
|
||||
|
||||
ent:CallOnRemove("organism", hg.organism.Remove, ent)
|
||||
hg.send_bareinfo(ent.organism)
|
||||
if (
|
||||
(!tracehull.Hit) and
|
||||
(minimum_down_timer >= 1.0) and
|
||||
(target.class_in_previous_life != nil) and
|
||||
!(target.organism.llegamputated or target.organism.rlegamputated or target.organism.larmamputated or target.organism.rarmamputated) and
|
||||
(target.organism.pain <= 80) and
|
||||
(target.organism.consciousness > 0.65) and
|
||||
!target:IsOnFire()
|
||||
) then
|
||||
local ent = ents.Create(target.class_in_previous_life)
|
||||
ent:SetPos(target:GetPos())
|
||||
ent:SetModel(target:GetModel())
|
||||
ent:SetMaterial(target:GetMaterial())
|
||||
|
||||
ent:SetSkin(target:GetSkin())
|
||||
|
||||
if target.citizentype then
|
||||
ent:SetKeyValue("citizentype", target.citizentype)
|
||||
end
|
||||
|
||||
for i = 0, target:GetNumBodyGroups() - 1 do
|
||||
ent:SetBodygroup(i, target:GetBodygroup(i))
|
||||
end
|
||||
|
||||
target:Remove()
|
||||
end)
|
||||
ent:Spawn()
|
||||
|
||||
self.StopProcessing = true
|
||||
ent:SetNotSolid(true)
|
||||
ent:SetNPCState(NPC_STATE_NONE)
|
||||
|
||||
self:Remove()
|
||||
return false
|
||||
timer.Simple(0, function()
|
||||
hg.organism.Add(ent)
|
||||
table.Merge(ent.organism, target.organism)
|
||||
|
||||
ent.tourniquets = table.Copy(target.tourniquets)
|
||||
ent.bandaged_limbs = table.Copy(target.bandaged_limbs)
|
||||
|
||||
ent.organism.alive = true
|
||||
ent.organism.owner = ent
|
||||
|
||||
target.organism = nil
|
||||
|
||||
ent:CallOnRemove("organism", hg.organism.Remove, ent)
|
||||
hg.send_bareinfo(ent.organism)
|
||||
|
||||
ent:SetSkin(target:GetSkin())
|
||||
|
||||
for i = 0, target:GetNumBodyGroups() - 1 do
|
||||
ent:SetBodygroup(i, target:GetBodygroup(i))
|
||||
end
|
||||
end)
|
||||
|
||||
target.FakeUp = true
|
||||
self.FakeParent = ent
|
||||
self.FakeUpTime = unfake_time:GetFloat()
|
||||
self.FakeUpEnd = CurTime() + self.FakeUpTime
|
||||
self.FakeUpStart = CurTime()
|
||||
|
||||
ent:SetAngles(Angle(0, math.random(-180, 180), 0))
|
||||
|
||||
local phys = target:GetPhysicsObject()
|
||||
phys:EnableGravity(false)
|
||||
target:SetNotSolid(true)
|
||||
target:SetMoveType(MOVETYPE_NONE)
|
||||
|
||||
ent:SetRenderMode(RENDERMODE_NONE)
|
||||
|
||||
timer.Simple(self.FakeUpTime, function()
|
||||
if !IsValid(ent) then return end
|
||||
|
||||
ent:SetNotSolid(false)
|
||||
ent:SetNPCState(NPC_STATE_IDLE)
|
||||
|
||||
ent:SetRenderMode(RENDERMODE_NORMAL)
|
||||
|
||||
target:Remove()
|
||||
|
||||
self:Remove()
|
||||
end)
|
||||
|
||||
self.bullseye:Remove()
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local phys_bone_id = phys:GetID()
|
||||
|
|
@ -404,6 +563,8 @@ end
|
|||
function MODULE:OnRemove()
|
||||
local timer_name = "Fedhoria_FallingLegs_Twitch_" .. self:EntIndex()
|
||||
timer_Remove(timer_name)
|
||||
|
||||
if IsValid(self.bullseye) then self.bullseye:Remove() end
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ function MODULE:Init()
|
|||
if seq then self:ResetSequence(seq) end
|
||||
|
||||
local target = self:GetTarget()
|
||||
|
||||
|
||||
self:SetPlaybackRate(1)
|
||||
self.LastCollideTime = 0
|
||||
self.LastGroundCollideTime = 0
|
||||
|
|
@ -170,7 +170,10 @@ end
|
|||
function MODULE:PhysicsSimulate(phys, dt)
|
||||
local cur_time = CurTime()
|
||||
local target = self:GetTarget()
|
||||
if not IsValid(target) then self:Remove(); return false end -- если цели нет, то идем нахуй
|
||||
if !IsValid(target) then self:Remove(); return false end -- если цели нет, то идем нахуй
|
||||
if !target.organism then self:Remove(); return false end
|
||||
|
||||
if target.FakeUp then self.AnimationRollEndTime = 0; return false end
|
||||
|
||||
-- проверка на физику
|
||||
if cur_time < self.AnimationRollEndTime then
|
||||
|
|
|
|||
|
|
@ -279,6 +279,8 @@ local trace = {output={}}
|
|||
local tr = trace.output
|
||||
|
||||
function MODULE:PhysicsSimulate(phys, dt)
|
||||
if self.FakeUp then return end
|
||||
|
||||
local phys_bone = phys:GetID()
|
||||
|
||||
local target = self:GetTarget()
|
||||
|
|
@ -291,6 +293,11 @@ function MODULE:PhysicsSimulate(phys, dt)
|
|||
|
||||
local st = stumble_time:GetFloat()
|
||||
|
||||
-- End stumbling if player is dead
|
||||
if !target.organism or !target.organism.alive then
|
||||
st = 0
|
||||
end
|
||||
|
||||
if (st <= 0) then
|
||||
self:Remove()
|
||||
return false
|
||||
|
|
@ -298,16 +305,6 @@ function MODULE:PhysicsSimulate(phys, dt)
|
|||
|
||||
local f = 1 - (CurTime() - self.Created) / st
|
||||
|
||||
--RagMod Reworked support
|
||||
if ragmod and ragmod:IsRagmodRagdoll(target) then
|
||||
local owner = target:GetOwningPlayer()
|
||||
if !IsValid(owner) or !owner:Alive() then
|
||||
f = 0
|
||||
else
|
||||
f = 1
|
||||
end
|
||||
end
|
||||
|
||||
if (f <= 0) then
|
||||
self:Remove()
|
||||
return false
|
||||
|
|
|
|||
Loading…
Reference in a new issue