NPCs now have a getting up animation (if I have to try and work on it again I'm going to kill someone), various new configurations and optimizations
This commit is contained in:
parent
259a3da4e1
commit
278a0a9d7d
6 changed files with 218 additions and 16 deletions
|
|
@ -14,8 +14,9 @@ This addon is based off of Kazarei's Euphoria, which in turn is a fork of Fedhor
|
||||||
|
|
||||||
## Known issues
|
## 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.
|
- 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.
|
||||||
- NPCs currently instantly get up, with no animation. I plan on remedying this in the future.
|
- 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
|
## Compatibility
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,12 @@ local function PopulateRagdollSBXToolMenu(pnl)
|
||||||
|
|
||||||
pnl:NumSlider("Wound grab time", "zcnpci_woundgrab_time", 0, 10, 3)
|
pnl:NumSlider("Wound grab time", "zcnpci_woundgrab_time", 0, 10, 3)
|
||||||
pnl:ControlHelp("How long the ragdoll should hold its wound.")
|
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: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.")
|
pnl:ControlHelp("The minimum amount of time the ragdoll should be down before trying to get back up.")
|
||||||
|
|
|
||||||
|
|
@ -128,9 +128,11 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
|
||||||
ragdoll.citizentype = ent:GetInternalVariable("citizentype")
|
ragdoll.citizentype = ent:GetInternalVariable("citizentype")
|
||||||
|
|
||||||
timer.Simple(0, function()
|
timer.Simple(0, function()
|
||||||
|
if !IsValid(ragdoll) then return end
|
||||||
|
|
||||||
ragdoll.organism.alive = true
|
ragdoll.organism.alive = true
|
||||||
|
|
||||||
if !IsValid(ragdoll) then return end
|
ragdoll:SetRenderMode(RENDERMODE_NORMAL)
|
||||||
|
|
||||||
zcnpci.StartModule(ragdoll, "stumble_legs", phys_bone, lpos)
|
zcnpci.StartModule(ragdoll, "stumble_legs", phys_bone, lpos)
|
||||||
last_dmgpos[ent] = nil
|
last_dmgpos[ent] = nil
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,31 @@ local twitchable_bone_names = {
|
||||||
"ValveBiped.Bip01_L_Foot"
|
"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 Copies of Functions for Optimization
|
||||||
local math_Clamp = math.Clamp
|
local math_Clamp = math.Clamp
|
||||||
local math_Rand = math.Rand
|
local math_Rand = math.Rand
|
||||||
|
|
@ -71,6 +96,10 @@ local cv_anim_roll_playback_rate = CreateConVar("zcnpci_falling_anim_roll_playba
|
||||||
-- Down time
|
-- Down time
|
||||||
local minimum_down_time = CreateConVar("zcnpci_down_time", "5", {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
|
Tug function
|
||||||
Applies a random impulse to one of the leg bones.
|
Applies a random impulse to one of the leg bones.
|
||||||
|
|
@ -184,6 +213,7 @@ function MODULE:Init()
|
||||||
|
|
||||||
if ent:Disposition(self.dummy_entity) == D_HT then
|
if ent:Disposition(self.dummy_entity) == D_HT then
|
||||||
ent:AddEntityRelationship(self.bullseye, D_HT, -1)
|
ent:AddEntityRelationship(self.bullseye, D_HT, -1)
|
||||||
|
|
||||||
print("HATE. HATE. HATE")
|
print("HATE. HATE. HATE")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -210,12 +240,23 @@ end
|
||||||
---------------------------------------------------------------------------]]
|
---------------------------------------------------------------------------]]
|
||||||
|
|
||||||
function MODULE:Think()
|
function MODULE:Think()
|
||||||
if (CurTime() - self.LastThink) < 1 then return end
|
--if (CurTime() - self.LastThink) < 1 then return end
|
||||||
|
print("thinking")
|
||||||
|
|
||||||
|
local target = self:GetTarget()
|
||||||
if !IsValid(target) then return end
|
if !IsValid(target) then return end
|
||||||
|
|
||||||
self.LastThink = CurTime()
|
self.LastThink = CurTime()
|
||||||
|
|
||||||
local phys = target:GetPhysicsObject()
|
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
|
if !IsValid(phys) or !phys:IsAsleep() then return end
|
||||||
|
|
||||||
|
|
@ -263,15 +304,82 @@ end
|
||||||
Physics Simulation Hook (FIXED)
|
Physics Simulation Hook (FIXED)
|
||||||
---------------------------------------------------------------------------]]
|
---------------------------------------------------------------------------]]
|
||||||
function MODULE:PhysicsSimulate(phys, dt)
|
function MODULE:PhysicsSimulate(phys, dt)
|
||||||
if self.StopProcessing then return false end
|
local cur_time = CurTime()
|
||||||
|
local target = self:GetTarget()
|
||||||
|
if not IsValid(target) then self:Remove(); self.bullseye:Remove(); return false end
|
||||||
|
|
||||||
|
if target.FakeUp then
|
||||||
|
print("fucking it")
|
||||||
|
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)
|
||||||
|
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)
|
--print((CurTime() - self.LastPhysProcess) < 0.05)
|
||||||
--if (CurTime() - self.LastPhysProcess) < (1 /) then return false end())
|
--if (CurTime() - self.LastPhysProcess) < (1 /) then return false end())
|
||||||
|
|
||||||
phys:Wake()
|
phys:Wake()
|
||||||
|
|
||||||
local cur_time = CurTime()
|
|
||||||
local target = self:GetTarget()
|
|
||||||
if not IsValid(target) then self:Remove(); self.bullseye:Remove(); return false end
|
|
||||||
|
|
||||||
self.bullseye:SetPos(target:GetPos())
|
self.bullseye:SetPos(target:GetPos())
|
||||||
--self.bullseye:SetAngles(target:EyeAngles())
|
--self.bullseye:SetAngles(target:EyeAngles())
|
||||||
|
|
@ -310,7 +418,7 @@ function MODULE:PhysicsSimulate(phys, dt)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if (CurTime() - self.LastFakeUpCheck) >= 1.0 then
|
if ((CurTime() - self.LastFakeUpCheck) >= 1.0) and (can_unfake:GetBool()) then
|
||||||
self.LastFakeUpCheck = CurTime()
|
self.LastFakeUpCheck = CurTime()
|
||||||
|
|
||||||
-- This basically checks if the NPC will be in collision with anything when it spawns.
|
-- This basically checks if the NPC will be in collision with anything when it spawns.
|
||||||
|
|
@ -351,6 +459,9 @@ function MODULE:PhysicsSimulate(phys, dt)
|
||||||
|
|
||||||
ent:Spawn()
|
ent:Spawn()
|
||||||
|
|
||||||
|
ent:SetNotSolid(true)
|
||||||
|
ent:SetNPCState(NPC_STATE_NONE)
|
||||||
|
|
||||||
timer.Simple(0, function()
|
timer.Simple(0, function()
|
||||||
hg.organism.Add(ent)
|
hg.organism.Add(ent)
|
||||||
table.Merge(ent.organism, target.organism)
|
table.Merge(ent.organism, target.organism)
|
||||||
|
|
@ -361,6 +472,8 @@ function MODULE:PhysicsSimulate(phys, dt)
|
||||||
ent.organism.alive = true
|
ent.organism.alive = true
|
||||||
ent.organism.owner = ent
|
ent.organism.owner = ent
|
||||||
|
|
||||||
|
target.organism = nil
|
||||||
|
|
||||||
ent:CallOnRemove("organism", hg.organism.Remove, ent)
|
ent:CallOnRemove("organism", hg.organism.Remove, ent)
|
||||||
hg.send_bareinfo(ent.organism)
|
hg.send_bareinfo(ent.organism)
|
||||||
|
|
||||||
|
|
@ -369,13 +482,88 @@ function MODULE:PhysicsSimulate(phys, dt)
|
||||||
for i = 0, target:GetNumBodyGroups() - 1 do
|
for i = 0, target:GetNumBodyGroups() - 1 do
|
||||||
ent:SetBodygroup(i, target:GetBodygroup(i))
|
ent:SetBodygroup(i, target:GetBodygroup(i))
|
||||||
end
|
end
|
||||||
|
|
||||||
target:Remove()
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
self.StopProcessing = true
|
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()
|
||||||
|
ent:SetNotSolid(false)
|
||||||
|
ent:SetNPCState(NPC_STATE_IDLE)
|
||||||
|
|
||||||
|
ent:SetRenderMode(RENDERMODE_NORMAL)
|
||||||
|
|
||||||
|
target:Remove()
|
||||||
|
|
||||||
|
self:Remove()
|
||||||
|
end)
|
||||||
|
--[[]
|
||||||
|
local parent = self.FakeParent
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
for i,v in pairs(self.ModelBoneList) do
|
||||||
|
print(v)
|
||||||
|
|
||||||
|
print("I'm here")
|
||||||
|
|
||||||
|
local bone = target:TranslateBoneToPhysBone(target:LookupBone(v))
|
||||||
|
local object = target:GetPhysicsObjectNum(bone)
|
||||||
|
if !IsValid(object) then i = i + 1; continue end
|
||||||
|
|
||||||
|
print("I'm getting to object")
|
||||||
|
|
||||||
|
local parent_bone = parent:TranslateBoneToPhysBone(parent:LookupBone(v))
|
||||||
|
if parent_bone == nil then i = i + 1; continue end
|
||||||
|
|
||||||
|
print("I'm getting to parent object")
|
||||||
|
|
||||||
|
object:Wake()
|
||||||
|
|
||||||
|
local shadow_data = {
|
||||||
|
secondstoarrive = 0.01,
|
||||||
|
pos = LerpVector(0.1, object:GetPos(), parent:GetBonePosition(parent_bone)),
|
||||||
|
angle = LerpAngle(0.1, object:GetAngles(), parent:GetBoneMatrix(parent_bone):GetAngles()),
|
||||||
|
maxspeed = 5000,
|
||||||
|
maxangular = 5000,
|
||||||
|
maxspeeddamp = 2000,
|
||||||
|
maxangularspeeddamp = 2000,
|
||||||
|
}
|
||||||
|
|
||||||
|
object:ComputeShadowControl(shadow_data)
|
||||||
|
|
||||||
|
--object:SetPos(LerpVector(0.2, object:GetPos(), parent:GetBonePosition(parent_bone)))
|
||||||
|
--object:SetAngles(LerpAngle(0.2, object:GetAngles(), parent:GetBoneMatrix(parent_bone):GetAngles()))
|
||||||
|
|
||||||
|
--object:EnableMotion(false)
|
||||||
|
|
||||||
|
--object:SetVelocity(Vector())
|
||||||
|
object:EnableGravity(false)
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
end]]
|
||||||
|
|
||||||
self:Remove()
|
|
||||||
self.bullseye:Remove()
|
self.bullseye:Remove()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
@ -451,7 +639,7 @@ function MODULE:OnRemove()
|
||||||
local timer_name = "Fedhoria_FallingLegs_Twitch_" .. self:EntIndex()
|
local timer_name = "Fedhoria_FallingLegs_Twitch_" .. self:EntIndex()
|
||||||
timer_Remove(timer_name)
|
timer_Remove(timer_name)
|
||||||
|
|
||||||
if self.bullseye then self.bullseye:Remove() end
|
if IsValid(self.bullseye) then self.bullseye:Remove() end
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[-------------------------------------------------------------------------
|
--[[-------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ function MODULE:Init()
|
||||||
if seq then self:ResetSequence(seq) end
|
if seq then self:ResetSequence(seq) end
|
||||||
|
|
||||||
local target = self:GetTarget()
|
local target = self:GetTarget()
|
||||||
|
|
||||||
self:SetPlaybackRate(1)
|
self:SetPlaybackRate(1)
|
||||||
self.LastCollideTime = 0
|
self.LastCollideTime = 0
|
||||||
self.LastGroundCollideTime = 0
|
self.LastGroundCollideTime = 0
|
||||||
|
|
@ -170,7 +170,10 @@ end
|
||||||
function MODULE:PhysicsSimulate(phys, dt)
|
function MODULE:PhysicsSimulate(phys, dt)
|
||||||
local cur_time = CurTime()
|
local cur_time = CurTime()
|
||||||
local target = self:GetTarget()
|
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
|
if cur_time < self.AnimationRollEndTime then
|
||||||
|
|
|
||||||
|
|
@ -279,6 +279,8 @@ local trace = {output={}}
|
||||||
local tr = trace.output
|
local tr = trace.output
|
||||||
|
|
||||||
function MODULE:PhysicsSimulate(phys, dt)
|
function MODULE:PhysicsSimulate(phys, dt)
|
||||||
|
if self.FakeUp then return end
|
||||||
|
|
||||||
local phys_bone = phys:GetID()
|
local phys_bone = phys:GetID()
|
||||||
|
|
||||||
local target = self:GetTarget()
|
local target = self:GetTarget()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue