Zet0rz / nzombies

A GM13 Nazi Zombies style (WIP) gamemode
GNU General Public License v3.0
73 stars 79 forks source link

collision groups #337

Open jaxjaxk opened 7 years ago

jaxjaxk commented 7 years ago

a while back i did away with the ShouldCollide hook for several reasons and replaced it with collision groups. main one being that players could enter the zombie spawn and trust me, they do that a whole lot.

so i thought it was about time i refined my method a bit. the only issue i had with doing this was that zombies would not collide with each other but it was only bad at the barriers. it was apparently not refined enough to make it into any versions so i have decided to fix this.

now i do not have the answer yet. I'm brute forcing it in an odd method but here is what i have so far ( mostly making this so that i don't lose the paper and info)

so far the collision groups that WILL collide with things of the same group are 0,5,6,7,8,9,11,15,16,17,18,21. for my method to work the zombies MUST be in one of these groups. this also means that we have preference to have humans in a group where they will not collide with each other so they must be in either group 1,2,3,4,10,12,13,14,19,20.

so now that we have that narrowed down we need a group that humans collide with but zombies do not. BUT humans and zombies must collide! we have that and we are golden!

more data later, got stuff todo today.

jaxjaxk commented 7 years ago

so the bad news is i could not find the perfect groups. the good news is i altered the baracade file to imporve the colission hook enough to prevent players from gettin through.

`AddCSLuaFile( )

ENT.Type = "anim"

ENT.PrintName = "breakable_entry" ENT.Author = "Alig96" ENT.Contact = "Don't" ENT.Purpose = "" ENT.Instructions = ""

ENT.NZOnlyVisibleInCreative = true

-- models/props_interiors/elevatorshaft_door01a.mdl -- models/props_debris/wood_board02a.mdl function ENT:Initialize() --self:SetCollisionGroup( COLLISION_GROUP_NONE ) self:SetModel("models/props_c17/fence01b.mdl") self:SetMoveType( MOVETYPE_NONE ) self:SetSolid( SOLID_VPHYSICS ) self.open = false --self:SetHealth(0) self:SetCustomCollisionCheck(true) self.NextPlank = CurTime() self.CollisionResetTime = CurTime() self.Planks = {}

if SERVER then
    self:ResetPlanks(true)
end

end

function ENT:SetupDataTables()

self:NetworkVar( "Int", 0, "NumPlanks" )
self:NetworkVar( "Bool", 0, "HasPlanks" )
self:NetworkVar( "Bool", 1, "TriggerJumps" )

end

function ENT:AddPlank(nosound) if !self:GetHasPlanks() then return end self:SpawnPlank() self:SetNumPlanks( (self:GetNumPlanks() or 0) + 1 ) if !nosound then self:EmitSound("nz/effects/board_slam_0"..math.random(0,5)..".wav") end --self:SetCollisionGroup( COLLISION_GROUP_NONE ) end

function ENT:RemovePlank()

local plank = table.Random(self.Planks)

if !IsValid(plank) and plank != nil then -- Not valid but not nil (NULL)
    table.RemoveByValue(self.Planks, plank) -- Remove it from the table
    self:RemovePlank() -- and try again
end

if IsValid(plank) then
    -- Drop off
    plank:SetParent(nil)
    plank:PhysicsInit(SOLID_VPHYSICS)
    local entphys = plank:GetPhysicsObject()
    if entphys:IsValid() then
         entphys:EnableGravity(true)
         entphys:Wake()
    end
    plank:SetCollisionGroup( COLLISION_GROUP_DEBRIS )
    -- Remove
    timer.Simple(2, function() if IsValid(plank) then plank:Remove() end
        if self:GetNumPlanks() <=0 then
            --self:SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS )
        end
    end)
end

table.RemoveByValue(self.Planks, plank)
self:SetNumPlanks( self:GetNumPlanks() - 1 )

end

function ENT:ResetPlanks(nosoundoverride) for i=1, table.Count(self.Planks) do self:RemovePlank() end self.Planks = {} self:SetNumPlanks(0) if self:GetHasPlanks() then for i=1, GetConVar("nz_difficulty_barricade_planks_max"):GetInt() do self:AddPlank(!nosoundoverride) end end end

function ENT:Use( activator, caller ) if CurTime() > self.NextPlank then if self:GetHasPlanks() and self:GetNumPlanks() < GetConVar("nz_difficulty_barricade_planks_max"):GetInt() then self:AddPlank() activator:GivePoints(10) activator:EmitSound("nz/effects/repair_ching.wav") self.NextPlank = CurTime() + 1 end end end

function ENT:SpawnPlank() -- Spawn local angs = {-60,-70,60,70} local plank = ents.Create("breakable_entry_plank") local min = self:GetTriggerJumps() and 0 or -45 plank:SetPos( self:GetPos()+Vector(0,0, math.random( min, 45 )) ) plank:SetAngles( Angle(0,self:GetAngles().y, table.Random(angs)) ) plank:Spawn() plank:SetParent(self) plank:SetCollisionGroup( COLLISION_GROUP_DEBRIS ) table.insert(self.Planks, plank) end

function ENT:Touch(ent) --if self:GetTriggerJumps() and self:GetNumPlanks() == 0 then --if ent.TriggerBarricadeJump then ent:TriggerBarricadeJump(self, self:GetTouchTrace().HitNormal) end --end end

hook.Add("ShouldCollide", "zCollisionHook", function(ent1, ent2)

if IsValid(ent1) and ent1:GetClass() == "breakable_entry" and nzConfig.ValidEnemies[ent2:GetClass()] and !ent1:GetTriggerJumps() and ent1:GetNumPlanks() == 0 then
    --if !IsValid(ent1.CollisionResetTime) then ent1.CollisionResetTime = CurTime() end 
    if ent1.CollisionResetTime < CurTime() then
        local nearplys = ents.FindInSphere( ent1:GetPos(), 50 )
        ent1.open = true
        for _,v in ipairs(nearplys) do
            if v:IsPlayer() then
                ent1.open = false
            end
        end
        if ent1.open then
        ent1:SetSolid(SOLID_NONE)

            timer.Simple(.1,function()

            ent1:SetSolid(SOLID_VPHYSICS)
            end)
        end
    end
    ent1.CollisionResetTime = CurTime() + 0.1
end

if IsValid(ent2) and ent2:GetClass() == "breakable_entry" and nzConfig.ValidEnemies[ent1:GetClass()] and !ent2:GetTriggerJumps() and ent2:GetNumPlanks() == 0 then
    --if !IsValid(ent2.CollisionResetTime) then ent2.CollisionResetTime = CurTime() end
    if ent2.CollisionResetTime < CurTime() then
        local nearplys = ents.FindInSphere( ent2:GetPos(), 50 )
        ent2.open = true
        for _,v in ipairs(nearplys) do
            if v:IsPlayer() then
                ent2.open = false
            end
        end 
        if ent2.open then

            ent2:SetSolid(SOLID_NONE)
            timer.Simple(.1,function()              
            ent2:SetSolid(SOLID_VPHYSICS)
            end)
        end
    end
    ent2.CollisionResetTime = CurTime() + 0.1
end

end)

if CLIENT then function ENT:Draw() if ConVarExists("nz_creative_preview") and !GetConVar("nz_creative_preview"):GetBool() and nzRound:InState( ROUND_CREATE ) then self:DrawModel() end end else function ENT:Think() --[[if self.CollisionResetTime and self.CollisionResetTime < CurTime() then self:SetSolid(SOLID_VPHYSICS) self.CollisionResetTime = nil end]] end end`

i also added in this to make sure players and zombies collide right. ( just in case, might not be needed)

`hook.Add("ShouldCollide", "huCollisionHook", function(ent1, ent2) --print("hook")

if ent1:IsPlayer() and ent2:IsPlayer() then return false end

if nzConfig.ValidEnemies[ent1:GetClass()] and nzConfig.ValidEnemies[ent2:GetClass()] then return true end if ent1:IsPlayer() and IsValid(ent2) and ent2:GetClass() == "breakable_entry" then return true end if ent2:IsPlayer() and IsValid(ent1) and ent1:GetClass() == "breakable_entry" then return true end

end)`

i know shouldcollide can cause crashes so i would not recommend the change until i test it out on my server a bit.

Zet0rz commented 7 years ago

I see where you're going with that, but if you simply want to close them again if a player nears, use the already-available ShouldCollide hook to check for players rather than an ents.FindInSphere. You could like that set a variable like self.PlayerNear = CurTime() + 0.2 any time ShouldCollide is called with a player as the other entity, and perform a check on this variable when the zombie is the entity.

Also ShouldCollide doesn't crash the game if it doesn't have any heavy calculations (like your ents.FindInSphere), rather it can crash the physics engine, however it only ever does this if the return value between two entities was changed without calling ent:CollisionRulesChanged(). It really isn't a problem in its current state (other than players getting through).

jaxjaxk commented 7 years ago

@Zet0rz i hear you but the problem with your idea is that using self.PlayerNear is by the time should collide is called it is too late. the reason i added the findinsphere is because we need them caught before players run into it.

for this reason i found the best solution. `function ENT:Touch(ent) --if self:GetTriggerJumps() and self:GetNumPlanks() == 0 then --if ent.TriggerBarricadeJump then ent:TriggerBarricadeJump(self, self:GetTouchTrace().HitNormal) end --end --ent:ShouldCollide(self) if IsValid(self) and nzConfig.ValidEnemies[ent:GetClass()] and self:GetNumPlanks() == 0 then --if !IsValid(ent1.CollisionResetTime) then ent1.CollisionResetTime = CurTime() end

        local nearplys = ents.FindInSphere( self:GetPos(), 50 )
        self.open = true
        for _,v in ipairs(nearplys) do
            if v:IsPlayer() then
                self.open = false
            end
        end
        if self.open then
        --print("open")
        self:SetSolid(SOLID_NONE)

            timer.Simple(.1,function()

            self:SetSolid(SOLID_VPHYSICS)
            end)
        end

end

end`

this removes the need for the should collide hook all together. worst case here is the zombie gets stuck in the while while the player is close.

Zet0rz commented 7 years ago

ShouldCollide is called before two entities collide to poll whether they should. It's called about 1.5 full meters away from the entity's collision hull

jaxjaxk commented 7 years ago

@Zet0rz then why have we never been able to get it to work? return true they collide, return false they don't.

Zet0rz commented 7 years ago

Because ShouldCollide returning false towards zombies will still not let them through as their navigation seems to think it is still solid. You can use ShouldCollide to detect when they are going to collide, but then use SetSolid instead of returning false, and that's how we've been doing. Just saying if you wanted to detect players, then you can simple re-use the ShouldCollide instead of an ents.FindInSphere and then use CurTime delays to prevent it from going solid within this time.

It want to experiment more with ShouldCollide at some point. Maybe the navigation would be able to pass through if you return false on the locomotives as well as the nextbot?

jaxjaxk commented 7 years ago

@Zet0rz that first part is just not true. the zombies still try to go through the entity. the fact is that nextbots do not pickup entities in their code to navigate so to them the entity does not even exist. only reason they stop to hit the planks is because you coded them to do that. So if you are correct that means them not going through is a fault in the zombie code and not collisions.

Zet0rz commented 7 years ago

Nextbots navigate using navmeshes, yes, but they do still have collision detection and will try to walk around stuff if it's in the same Navmesh square. You can see this if you enable developer mode and draw the zombies pathing, you will see two green boxes around the feet, these will turn red when they collide with stuff and the bot will try to walk away from that. If ShouldCollide returned false towards a zombie, sure it wouldn't be solid, but these boxes would still go red and the bot would refuse to walk through.

Feel free to test it yourself. Just make a barricade with no planks and change the ShouldCollide hook to just return false (all the time, nothing else at all) and try in creative and see if it will walk through.