• Please make sure you are familiar with the forum rules. You can find them here: https://forums.tripwireinteractive.com/index.php?threads/forum-rules.2334636/

Code hey hey ho ho Husk emitter decals gotta go

Does anyone have a remedy for the emitter decals that are left by some of the husk zed variants. These tend to build up and cause memory/gpu issues. It seems like I saw something similar on endangereds server, they had floating balls where I have wall and floor decals.
Following FlameThrowerBurnMark_Large / FlameThrowerBurnMark / FlameThrowerBurnMark_Small, they have a lifespan of 5 seconds. I have no idea how many husks are shooting on your map, but I really have a hard time believing it's more than 10.
 
Upvote 0
Code:
class DecalEraser extends Mutator;

var array<ProjectedDecal> PendingDecals;
var float UpdateDecalsTime;

struct DecalsStruct
{
	var ProjectedDecal Decal;
	var float DestroyTime;
};

array<DecalsStruct> Decals;
var int NumDecals;
var float FirstDecalSpawnTime;

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
	local ProjectedDecal ProjectedDecal;
	
	if ( Level.NetMode != NM_Standalone )
	{
		// This isnt called on NM_Client at all, but we dont need replication also on Single Game
		ProjectedDecal = ProjectedDecal(Other);
		if ( ProjectedDecal != none )
		{
			PendingDecals[PendingDecals.Length] = ProjectedDecal;
			UpdateDecalsTime = Level.TimeSeconds + 0.01;
		}
	}
	return Super.CheckReplacement(Other,bSuperRelevant);
}

simulated function Tick(float DT)
{
	local ProjectedDecal Decal;
	local float NextUpdateTime;
	
	Super.Tick(DT);
	if ( Level.NetMode != NM_DedicatedServer && UpdateDecalsTime < Level.TimeSeconds )
	{
		n = Decals.Length;
		foreach DynamicActors(class'ProjectedDecal',Decal)
		{
			for(i=0; i<n; i++)
			if ( Decals[i].Decal == none )
			{
				Decals.Remove(i,1);
				i--;
				n--;
			}
			else if ( Decals[i].Decal == Decal ) break;
			if ( i >= n )
			{
				if ( NumDecals++ <= 0 )
				{
					FirstDecalSpawnTime = Level.TimeSeconds;
					NumDecals++;
				}
				Decals.Insert(n,1);
				Decals[n].Decal = Decal;
				Decals[n].DestroyTime = Level.TimeSeconds + Decal.default.LifeSpan;
				n++;
			}
		}
		for(i=0; i<n; i++)
		{
			if ( Decals[i].DestroyTime <= Level.TimeSeconds && !Decals[i].bDeleteMe )
			{
				Decals[i].Decal.Destroy();
				Decals.Remove(i,1);
				i--;
				n--;
			}
		}
		NextUpdateTime = Level.TimeSeconds + (FMax((Level.TimeSeconds-FirstDecalSpawnTime),1)/FMax(NumDecals,1))*RandRange(0.75,1);
	}
		if ( ( Level.NetMode == NM_DedicatedServer || Level.NetMode == NM_ListenServer ) && PendingDecals.Length > 0 )
		{
			while ( PendingDecals.Length > 0 )
			{
				PendingDecals[0].bTearOff = true;
				RendingDecals.Remove(0,1);
			}
		}
	}
}

defaultproperties
{
	bAlwaysRelevant=true
	RemoteRole=ROLE_SimulatedProxy
	bAddToServerPackages=true
	GroupName="KF-DecalEraser"
    FriendlyName="DecalEraser"
    Description="Erases decals."
}

Try this.
 
Last edited:
Upvote 0
I want to thank you Killjoy for trying to address this. I was not able to get this to compile. There are a couple of small things but there are a couple of big ones that I am not able to get past. The values for n and I. The compiler stops at "n = Decals.Length;". Error 'n' Bad command or expression. When that line is commented out the line "(i=0; i<n; i++)" becomes the issue. Somehow n and I are not being established. I am going to continue to work on it. Again TY for taking your time to help work on this issue.
 
Upvote 0
Killjoy TY for your help with this. I'll post the code that worked below. This worked with all (so far) Husk types with the exception of Mr. Freeze. In the end it was easier to remove him than sort through the file group. BTW the code insert tab is not working, for some reason.


class DecalEraser extends Mutator;
var array<ProjectedDecal> PendingDecals;
var float UpdateDecalsTime;
struct DecalsStruct
{
var ProjectedDecal Decal;
var float DestroyTime;
// var bDeleteMe
};
var array<DecalsStruct> Decals;
var int NumDecals;
var float FirstDecalSpawnTime;
function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
local ProjectedDecal ProjectedDecal;

if ( Level.NetMode != NM_Standalone )
{
// This isnt called on NM_Client at all, but we dont need replication also on Single Game
ProjectedDecal = ProjectedDecal(Other);
if ( ProjectedDecal != none )
{
PendingDecals[PendingDecals.Length] = ProjectedDecal;
UpdateDecalsTime = Level.TimeSeconds + 0.01;
}
}
return Super.CheckReplacement(Other,bSuperRelevant);
}
simulated function Tick(float DT)
{
local ProjectedDecal Decal;
local float NextUpdateTime;
local int i,n;

Super.Tick(DT);
if ( Level.NetMode != NM_DedicatedServer && UpdateDecalsTime < Level.TimeSeconds )
{
n = Decals.Length;
foreach DynamicActors(class'ProjectedDecal',Decal)
{
for(i=0; i<n; i++)
if ( Decals.Decal == none )
{
Decals.Remove(i,1);
i--;
n--;
}
else if ( Decals.Decal == Decal ) break;
if ( i >= n )
{
if ( NumDecals++ <= 0 )
{
FirstDecalSpawnTime = Level.TimeSeconds;
NumDecals++;
}
Decals.Insert(n,1);
Decals[n].Decal = Decal;
Decals[n].DestroyTime = Level.TimeSeconds + Decal.default.LifeSpan;
n++;
}
}
for(i=0; i<n; i++)
{
if ( Decals.DestroyTime <= Level.TimeSeconds ) /*&& Decals.bDeleteMe )*/
{
Decals.Decal.Destroy();
Decals.Remove(i,1);
i--;
n--;
}
}
NextUpdateTime = Level.TimeSeconds + (FMax((Level.TimeSeconds-FirstDecalSpawnTime),1)/FMax(NumDecals,1))*RandRange(0.75,1);

if ( ( Level.NetMode == NM_DedicatedServer || Level.NetMode == NM_ListenServer ) && PendingDecals.Length > 0 )
{
while ( PendingDecals.Length > 0 )
{
PendingDecals[0].bTearOff = true;
PendingDecals.Remove(0,1);
}
}
}
}
defaultproperties
{
bAlwaysRelevant=true
RemoteRole=ROLE_SimulatedProxy
bAddToServerPackages=true
GroupName="KF-DecalEraser"
FriendlyName="DecalEraser"
Description="Erases decals."
}
 
Upvote 0
It seems it was IE 11 that wouldn't allow the code block. The update time you refer to was part of the original code you provided.

Code:
class DecalEraser extends Mutator;

var array<ProjectedDecal> PendingDecals;
var float UpdateDecalsTime;

struct DecalsStruct
{
    var ProjectedDecal Decal;
    var float DestroyTime;
//    var bDeleteMe
};

var array<DecalsStruct> Decals;
var int NumDecals;
var float FirstDecalSpawnTime;

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
    local ProjectedDecal ProjectedDecal;
    
    if ( Level.NetMode != NM_Standalone )
    {
        // This isnt called on NM_Client at all, but we dont need replication also on Single Game
        ProjectedDecal = ProjectedDecal(Other);
        if ( ProjectedDecal != none )
        {
            PendingDecals[PendingDecals.Length] = ProjectedDecal;
            UpdateDecalsTime = Level.TimeSeconds + 0.01;
        }
    }
    return Super.CheckReplacement(Other,bSuperRelevant);
}

simulated function Tick(float DT)
{
    local ProjectedDecal Decal;
    local float NextUpdateTime;
        local int i,n;
    
    Super.Tick(DT);
    if ( Level.NetMode != NM_DedicatedServer && UpdateDecalsTime < Level.TimeSeconds )
    {
        n = Decals.Length;
        foreach DynamicActors(class'ProjectedDecal',Decal)
        {
            for(i=0; i<n; i++)
            if ( Decals[i].Decal == none )
            {
                Decals.Remove(i,1);
                i--;
                n--;
            }
            else if ( Decals[i].Decal == Decal ) break;
            if ( i >= n )
            {
                if ( NumDecals++ <= 0 )
                {
                    FirstDecalSpawnTime = Level.TimeSeconds;
                    NumDecals++;
                }
                Decals.Insert(n,1);
                Decals[n].Decal = Decal;
                Decals[n].DestroyTime = Level.TimeSeconds + Decal.default.LifeSpan;
                n++;
            }
        }
        for(i=0; i<n; i++)
        {
            if ( Decals[i].DestroyTime <= Level.TimeSeconds ) /*&& Decals[i].bDeleteMe )*/
            {
                Decals[i].Decal.Destroy();
                Decals.Remove(i,1);
                i--;
                n--;
            }
        }

        NextUpdateTime = Level.TimeSeconds + (FMax((Level.TimeSeconds-FirstDecalSpawnTime),1)/FMax(NumDecals,1))*RandRange(0.75,1);
    
        if ( ( Level.NetMode == NM_DedicatedServer || Level.NetMode == NM_ListenServer ) && PendingDecals.Length > 0 )
        {
            while ( PendingDecals.Length > 0 )
            {
                PendingDecals[0].bTearOff = true;
                PendingDecals.Remove(0,1);
            }
        }
    }
}

defaultproperties
{
    bAlwaysRelevant=true
    RemoteRole=ROLE_SimulatedProxy
    bAddToServerPackages=true
    GroupName="KF-DecalEraser"
        FriendlyName="DecalEraser"
        Description="Erases decals."
}
 
Upvote 0