class a_MonsterOptimizer extends Actor
  config(HideMut);

var PlayerController PC;
var float NumSequences;
var int NewTicks, CurrentZone; 			//for tick comparing
var bool Opt_Launched,		//optimizer started
            LowGore,			//Client Low Gore == KillingFloor.ini, parameter UseLowGore
            Cooldown;			//Flag for ZoneInfo zed search
var int NumZeds,				//Num spawned Zeds
        NumberOfZonesChecked; 	//Number of checked zones
var array<KFMonster>      cachedZeds;	//cache zeds for culling and other purposes
var array<KFMonster>      deadZeds;	//cache zeds for ragdoll management
var array<ZoneInfo>		ZoneInfos;
var ZoneInfo Zone;

//=========================Configz
var config array<string> KFMonsterGibClassNames;	//Gibs aka severed arms
var config array<string> KFMonsterGibletsClassNames;	//giblets - small parts that spawn when arm, leg or etc. has been lost
var config array<string> KFMonsterGibEmitters;			//5000 tons of bloods and particles...

var config array<string> KFMonsterClassNames_Standard,		//Default monster classes
                         KFMonsterClassNames_Circus,		//Circus
                         KFMonsterClassNames_Halloween,		//Halloween
                         KFMonsterClassNames_XMas;			//Xmas
var config array<string> CustomZeds;			//Scrn zeds
var config array<string> NiceZeds;				//NiceZeds
var config array<string> LagEmitters;			//Firebug shit

var config array<string> TWIGoreBackup, TWIGibEmitters;

//==================
//===============================Structures for optimizer paramas
struct EmitLightControl
{
  var bool UseFireTweaks;		//Use or not fire tweaks
  var float LightRadius;		//Light radius for fire emitters
  var ELightType LightType;		//Light tyoe
  var byte LightBrightness;
  var byte LightHue;			//Color
  var byte LightSaturation;
  var bool bDynamicLight;		//Should be dynamic?
  var bool bSpecialLit;		//Don't lit dynamic actors
  var bool UseAdditionalTweaks;	//Use additional tweaks flashlight and etc.
  var bool FixDoshLighting;		//Attempt to fix dosh mesh lighting
};

struct OptParams			//for ambient sounds
{
  var string WholeMapSearchMethod;		//Use zoneinfo.zoneactors search or use DynamicActors search
  var bool ConsNetSpeed; 		//Constant net speed
  var bool bMonsterSpotted;

  var float ZedCullDistance;		//Cull distance for zeds
  var float Timer;
  var int Ticks; 					//every goddamn tick optimizer will detect decapped zeds
  var int nozed_NetSpeed;			//NetSpeed without zeds
  var int new_NetSpeed; 			//NetSpeed with zeds
  var bool UseUnlit; 				//if unlit zed will appear in full brightness
  var byte AmbientGlow;			//if unlit this will regulate zed's brightness
  var bool bSeasonedOff; 			//disable season zeds //replace seasoned(super laggy) zeds with default. Fuk u twi for 70 bones skeletons and MSM_Smooth_Only shit.
                                      //Read dat fucking manual first
  var bool bLagOff;
  var ESoundOcclusion SoundOcclusion;		//Sound occlusion == what will block sounds(bsp, static meshes, none and etc)
  var float SoundRadius;			//Ambient Sound radius
  //=====================Seasoned zeds shit
  var bool bStandard;
  var bool bCircus;
  var bool bHillBilly;
  var bool bXmas;
  //=====================Seasoned zeds shit

  //================================Gore related tweaks
  var string GoreLevel;				//my gore level preset for twi classes
  //Gibs
  var bool bNoRagdolls;		//Destroy Ragdolls them
  var float RagdollLifeSpan;
  var bool bAdvancedGibs;
  var bool bUnlitGibs;
  var bool bHideGibs;
  var float GibsLifespan;
  var bool GibUseDeferRendering;
  var int GibMaxLights;
  var bool GibUseCollisionStaticMesh;
  var bool GibAcceptProjectors;
  var bool GibStaticLighting;

  //Giblets
  var bool bAdvancedGiblets;
  var bool bUnlitGiblets;
  var bool bHideGiblets;
  var float GibletsLifespan;
  var bool GibletsUseDeferRendering;
  var int GibletsMaxLights;
  var bool GibletsUseCollisionStaticMesh;
  var bool GibletsAcceptProjectors;
  var bool GibletsStaticLighting;

  //===============================Gore related tweaks end
  var bool UserSettingsToEmitters;
  var int ZedSearchRadius;
};

struct EMB		//Emitter backups. /////////Stub//////Should rewrite dis
{				//contains variables from Particle emitter class. Used to backup and edit particle emitter classes
  var string ClassName;		//Particle emitter name
  var bool bHidden;
  var bool bUnlit;
  var bool Disabled;			//Disable Emitter
  var float InitialParticlesPerSecond;	//plz make it 500000000000000000000
  var bool UseCollision;			//Use collision for emitter particles
  var bool AcceptsProjectors;			//Accept projectors or don't
  var float LifeSpan;
};

struct EMP_backups
{
  var string ClassName;
  var bool bHidden;
  var bool bUnlit;
  var float LifeSpan;		//lifespan applies to emitter class
  var array<EMB> CEMP;		//particle emitters array
};

struct GorePresets	//Gore presets for my emitters
{
  var string PresetName;
  var string PresetDescription;
  var array<EMP_backups> EmittersToLoad;
};

//===============================Structures for optimizer paramas
var OptParams CurrentOptParams;		//Current optimizer parameters
var EmitLightControl LC;			//Firebug and lighting shit also dosh meshlighting fix
var EMP_backups EMP_backSingle;		//for backuping emitters properties, single emitter
var array<EMP_backups> EMP_back;	//for backuping emitters properties, emitters array
var EMB User_EMPS;		//User settings for all emitters
var array<GorePresets> Gore_Presets;		//Gore_Presets. mb should add custom?
var GorePresets Gore_Preset;				//Single Gore Preset for using in structure arrays

var array<class <Emitter> >  BackupGore, LowGoreE, OptGore, NoGore;

function startOptimize()
{
  local int i;

  PC = Level.GetLocalPlayerController();

  if(CurrentOptParams.WholeMapSearchMethod ~= "ZoneActors")	//Zone infos stuff only dependent to ZoneActors search method
  {
    CurrentZone = 0;
    ForEach AllActors(class'ZoneInfo', Zone)
    {
      //PC.ClientMessage(PC.GameReplicationInfo.PRIArray.length);
      ZoneInfos[i] = Zone;
      i++;
    }

    for(i=0;i<ZoneInfos.length;i++)
    {
      if(ZoneInfos[i] == none
            || Instr(CAPS(string(ZoneInfos[i])), "SKY") != -1
            || Instr(CAPS(string(ZoneInfos[i])), "ROCKING") != -1
            || Instr(CAPS(string(ZoneInfos[i])), "WARP") != -1
            || Instr(CAPS(string(ZoneInfos[i])), "LEVEL") != -1)
            ZoneInfos.Remove(i,1);
            //PC.ClientMessage(ZoneInfos[i]);
    }

    if(ZoneInfos.length == 0)
    {
      CurrentOptParams.WholeMapSearchMethod = "AllActors";
      PC.ClientMEssage("No zones. Using All actors method");
    }
  }

  if(CurrentOptParams.bSeasonedOff)
    RemoveSeasonedZeds();
  else
    RestoreSeasonedZeds();

  LowGore = class'GameInfo'.static.UseLowGore(); 	//Client Low Gore == KillingFloor.ini, parameter UseLowGore
  OptimizeGore();		//Optimize gibs and gilets
  if(LC.UseFireTweaks)
    FirebugDisabler();	//Disable firebug shit

  if (LC.UseAdditionalTweaks)
  {
    //Additional tweaks
    class'SingleAttachment'.default.LightType = LT_Steady;		//Why dafuq you put LT_Pulse on flashlight.
    class'ZEDMKIIPrimaryProjectileImpact'.default.LightType = LT_Steady;
    class'ZEDMKIISecondaryProjectileExplosion'.default.LightType = LT_Steady;
    class'ZEDProjectileImpact'.default.LightType = LT_Steady;
  }

  if(LC.FixDoshLighting)									//Dosh tweak
    class'KFMod.CashPickup'.default.bUnlit = true; 			//Remove crazy mesh lighting dosh shit;

  if(!Opt_Launched)
  {
    if(CurrentOptParams.bLagOff)	//Don't start timer if using default class pollution
      PolluteDefaults(false);
    else
    {
      SetTimer(CurrentOptParams.Timer, true);
      Opt_Launched = true;
      if(!LowGore)
        Disable('Tick');
      else
        Enable('Tick');
    }
    return;
  }
  else
  {
    stopOptimize();
    Opt_Launched = true;
    if(!CurrentOptParams.bLagOff)			//Don't start timer if using non-laggy optimization
      SetTimer(CurrentOptParams.Timer, true);
    if(!LowGore)
      Disable('Tick');
    else
      Enable('Tick');
  }
}

function stopOptimize()
{
  local int i;

  SetTimer(0.00, false);
  Disable('Tick');
  ZoneInfos.length = 0;

  Opt_Launched = false;
  if(cachedZeds.Length != 0)			//Restoring defaults on cached zeds
  {
    for(i=0; i<cachedZeds.Length; i++)
    {
      if(cachedZeds[i]==none)
      {
        cachedZeds.Remove(i,1);
        continue;
      }
      cachedZeds[i].cullDistance = 0;
      cachedZeds[i].bUnlit = false;
      cachedZeds[i].AmbientGlow = 0;
      cachedZeds[i].SoundOcclusion = Occlusion_Default;
      cachedZeds[i].SoundRadius = 300;
    }
    cachedZeds.Length = 0;
  }
  if(deadZeds.Length != 0)			//Restoring defaults on dead zeds
  {
    for(i=0; i<deadZeds.Length; i++)
    {
      if(deadZeds[i]==none)
      {
        deadZeds.Remove(i,1);
        continue;
      }
      deadZeds[i].LifeSpan = 8.5;
    }
    deadZeds.length = 0;
  }

  if(CurrentOptParams.bLagOff)
  {
    PolluteDefaults(true);		//Default actor values
  }
  if(CurrentOptParams.bSeasonedOff)
    RestoreSeasonedZeds();

  GibAndGibletsTweaks(true);
}

function Timer()
{
  NumSequences = NumSequences + 1;

  cacheZeds();			//Get zeds on client and cache them to array
  //PC.ClientMEssage("Zeds on map: " @ cachedZeds.length);

  if(CurrentOptParams.new_NetSpeed > 0 && CurrentOptParams.nozed_NetSpeed > 0) //Change my netspeed
    NetSpeedChanger();

  if(cachedZeds.length > 0)	//use cooling only if there are zeds and client "saw" them.
  {
    //Apply our shit
    CullZedsOnMap();	//for wave based maps use KFGameReplicationInfo(myKFPC.GameReplicationInfo).MaxMonsters
  }
  if(deadZeds.length > 0)
  {
    ControlRagdolls();	//for ragdoll controlling
  }
}

simulated function Tick(float Delta)
{
  if(CurrentOptParams.Ticks == 0)
    return;
  NewTicks++;
  if (NewTicks == CurrentOptParams.Ticks)
  {
    DetectHeadless();
  }
}

//Cache Spawned Zeds
simulated function cacheZeds()
{
  local KFMonster KFM;
  local int i;

  if (KFGameReplicationInfo(PC.GameReplicationInfo).TimeToNextWave > 0)		//Don't work on trader time
  {
    cachedZeds.length = 0;				//And destroy cached on previous wave zeds if they exist
    return;
  }

  if (PC.AcknowledgedPawn == none && CurrentOptParams.WholeMapSearchMethod != "ZoneActors")	//Do not work if we dont have pawn however zoneinfo search will work
    return;																						//Incase of spectating with my stuff turned on

  switch (CurrentOptParams.WholeMapSearchMethod)
  {
    case "AllActors":
      ForEach PC.AcknowledgedPawn.AllActors(class'KFMonster', KFM)
      {
        if(KFM.Physics == PHYS_KarmaRagdoll)		// cache this to dead zeds
        {
          deadZeds[deadZeds.length] = KFM;
        }
        else				// cache this to alive zeds
        {
          if(KFM.CullDistance != CurrentOptParams.ZedCullDistance)		//if we having culldistance property on a Monster => it is already been cached
          {
            cachedZeds[cachedZeds.length] = KFM;
          }
        }
      }
      break;
    case "ZoneActors":
      if(!CoolDown && PC.PlayerReplicationInfo.PlayerZone != none)		//Wait for cooldown
      {
        for(i=0; i<ZoneInfos.Length; i++)
        {
          if(ZoneInfos[i] == PC.PlayerReplicationInfo.PlayerZone);		//Let's start with player zone then check other zones
            CurrentZone = i;	//Save index
        }
        CoolDown = true;
      }
      ForEach ZoneInfos[CurrentZone].ZoneActors(class'KFMonster', KFM)
      {
                        if (KFM.Physics == PHYS_KarmaRagdoll)		//cache this to dead zeds
                        {
                        deadZeds[deadZeds.length] = KFM;
                        }
                        else				//cache this to alive zeds
                        {
                            if (KFM.CullDistance != CurrentOptParams.ZedCullDistance)		//if we having culldistance set on a Monster => it is already been cached
                            {
                            cachedZeds[cachedZeds.length] = KFM;
                            //PC.ClientMEssage("Was cached" @ CachedZeds.Length @ "zeds");
                            }
                        }
                NumberOfZonesChecked++;
                }
        //PC.ClientMessage(cachedZeds.length @ deadZeds.length);
        CurrentZone++;
        if (NumberOfZonesChecked == ZoneInfos.length - 1)		//back to checking player zone
        {
        Cooldown = false;
        NumberOfZonesChecked = 0;
        }
        if (CurrentZone >= ZoneInfos.length)
        CurrentZone = 0;

    break;

    case "DynamicActors":

        forEach PC.AcknowledgedPawn.DynamicActors (class'KFMonster', KFM)
        {
                if (KFM.Physics == PHYS_KarmaRagdoll)		//cache this to dead zeds
                {
                deadZeds[deadZeds.length] = KFM;
                }
                else				//cache this to alive zeds
                {
                    if (KFM.CullDistance != CurrentOptParams.ZedCullDistance)		//if we having culldistance set on a Monster => it is already been cached
                    {
                    cachedZeds[cachedZeds.length] = KFM;
                    }
                }
        }
    break;

    case "RadiusActors":
        foreach PC.AcknowledgedPawn.CollidingActors(class'KFMonster', KFM, CurrentOptParams.ZedSearchRadius)
        {
                if (KFM.Physics == PHYS_KarmaRagdoll)		//cache this to dead zeds
                {
                deadZeds[deadZeds.length] = KFM;
                }
                else				//cache this to alive zeds
                {
                    if (KFM.CullDistance != CurrentOptParams.ZedCullDistance)		//if we having culldistance set on a Monster => it is already been cached
                    {
                    cachedZeds[cachedZeds.length] = KFM;
                    }
                }
        }
    break;
    }
}

simulated function CullZedsOnMap()		//Apply our tweaks to cached zeds
{
  local int i;
  local KFMonster KFM;

  for(i = 0; i < cachedZeds.length; i++)
  {
    if(cachedZeds[i] == none || cachedZeds[i].Health <= 0)		//Remove killed zeds
    {
      cachedZeds.Remove(i,1);
      continue;
    }
    KFM = cachedZeds[i];
    if(KFM == none)			//none check
      continue;
    if(CurrentOptParams.bMonsterSpotted) //&& int(NumSequences%2)>0)
    {
      MonsterSpotted(KFM); 		//REMOVE IT
    }

    if(KFM.cullDistance != CurrentOptParams.ZedCullDistance)
    {
      KFM.cullDistance = CurrentOptParams.ZedCullDistance;
      KFM.bUnlit = CurrentOptParams.UseUnlit;
      KFM.AmbientGlow = CurrentOptParams.AmbientGlow;
      KFM.SoundOcclusion = CurrentOptParams.SoundOcclusion;
      KFM.SoundRadius = CurrentOptParams.SoundRadius;
    }
  }
}

simulated function MonsterSpotted(KFMOnster KFMO)	//Remove IT
{
  local string KFMonsterST;

  if(KFMO == none)
    return;

  KFMonsterST = string(KFMO);

  if(Instr(caps(KFMonsterST), "STALKER") != -1 && !KFMO.bSpotted)
  {
    KFMO.Skins[0] = Finalblend'KFX.StalkerGlow';
    KFMO.Skins[1] = Finalblend'KFX.StalkerGlow';
    KFMO.bUnlit = true;
  }

  if(Instr(caps(KFMonsterST), "GHOST") != -1 && !KFMO.bSpotted)
  {
    KFMO.Skins[0] = Finalblend'KFX.StalkerGlow';
    KFMO.Skins[1] = Finalblend'KFX.StalkerGlow';
    KFMO.bUnlit = true;
  }

  if(Instr(caps(KFMonsterST), "BOSS") != -1 || Instr(caps(KFMonsterST), "PAT") != -1
    || Instr(caps(KFMonsterST), "HARD") != -1)
  {
    if(KFMO.bCloaked == true )
    {
      KFMO.Skins[0] = Finalblend'KFX.StalkerGlow';
      KFMO.Skins[1] = Finalblend'KFX.StalkerGlow';
      KFMO.bUnlit = true;
    }
    if(KFMO.bHidden == true)		//Remove hard pat bullshit
      KFMO.bHidden = false;
  }
}

simulated function NetSpeedChanger() //Netspeed changer cuz lazy me
{
    if (CurrentOptParams.ConsNetSpeed)	//Change netspeed
    {
    PC.SetNetSpeed(CurrentOptParams.New_NetSpeed); //constant netspeed
    return;
    }

    if (KFGameReplicationInfo(PC.GameReplicationInfo).TimeToNextWave > 0 || !KFGameReplicationInfo(PC.GameReplicationInfo).bWaveInProgress)
    {
        PC.SetNetSpeed(CurrentOptParams.nozed_NetSpeed); // same as PC.ConsoleCommand("netspeed x"), but faster
    }
    else if (KFGameReplicationInfo(PC.GameReplicationInfo).bWaveInProgress
    || KFGameReplicationInfo(PC.GameReplicationInfo).MaxMonsters > 0)
    {
        PC.SetNetSpeed(CurrentOptParams.New_NetSpeed); // same as PC.ConsoleCommand("netspeed x"), but faster
    }
}

simulated function ControlRagdolls()
{
  local int i;

  for (i=0; i < deadZeds.length; i++)
  {
    if (deadZeds[i]==none)			//Avoid log-spam
    {
      deadZeds.Remove(i,1);
      continue;
    }
    //PC.ClientMessage(deadZeds[i].name @ deadZeds[i].lifespan @ deadZeds[i].bCollideWorld);

    if (CurrentOptParams.bNoRagdolls)		//Destroy ragdolls
    {
      if (deadZeds[i].Destroy())
        deadZeds.Remove(i,1);
      else
      {
        deadZeds[i].bDestroyNextTick = true;
        deadZeds.Remove(i,1);
      }
    }

    else if(!deadZeds[i].bCollideWorld) //set lifespan only once
    {									//vanilla dead zeds don't have bCollideWorld
      deadZeds[i].Lifespan = CurrentOptParams.RagdollLifeSpan;
      deadZeds[i].bCollideWorld = true;			//Bodies just fall through floor without that
    }
  }

  if (DeadZeds.length > Level.MaxRagdolls)		//destroying our ragdolls with lifespan cuz sometimes engine ragdoll destroying bugging out
  {
    for (i=0; i < DeadZeds.length - Level.MaxRagdolls; i++)
      if (DeadZeds[DeadZeds.length-1].Destroy())		//Destroying last ragdolls in our array
        DeadZeds.Remove(DeadZeds.length-1,1);		//Removing none references
  }
}

//================================Firebug shit

simulated function FirebugDisabler()		//Some tweaks to burn emitters
{
  local class<Emitter> E;
  local int i;

  if (LowGore)
  {
    //Some tweaks to Alt burn emitter
    class'KFAltMonsterFlame'.default.Emitters[0] = class'HideMut.KFBurnEffect_FPS'.default.Emitters[0];
    //light
    class'KFAltMonsterFlame'.default.LightRadius = LC.LightRadius;
    class'KFAltMonsterFlame'.default.LightType = LC.LightType;
    class'KFAltMonsterFlame'.default.LightBrightness = LC.LightBrightness;
    class'KFAltMonsterFlame'.default.LightHue = LC.LightHue;
    class'KFAltMonsterFlame'.default.LightSaturation = LC.LightSaturation;
    class'KFAltMonsterFlame'.default.bDynamicLight = LC.bDynamicLight;
    class'KFAltMonsterFlame'.default.bSpecialLit = LC.bSpecialLit;
  }

  if (LagEmitters.length == 0)
    return;

  //Lighting tweaks (from pulse to steady light cuz of fps drops when using multiple pulse lights)
  for (i=0;i<LagEmitters.length;i++)
  {
    E = GetEmitterClassFromString(LagEmitters[i]);
    if (E==none)
      continue;

    if (LC.LightRadius >= 0)
      E.default.LightRadius = LC.LightRadius;
    E.default.LightType = LC.LightType;
    if (LC.LightBrightness >= 0)
      E.default.LightBrightness = LC.LightBrightness;
    if (LC.LightHue >= 0)
      E.default.LightHue = LC.LightHue;
    if (LC.LightSaturation >= 0)
      E.default.LightSaturation = LC.LightSaturation;
    E.default.bDynamicLight = LC.bDynamicLight;
    E.default.bSpecialLit = LC.bSpecialLit;
  }

  //Some tweaks to Burn emitter
  class'KFMonsterFlame'.default.LightRadius = LC.LightRadius;
  class'KFMonsterFlame'.default.LightType = LC.LightType;
  class'KFMonsterFlame'.default.LightBrightness = LC.LightBrightness;
  class'KFMonsterFlame'.default.LightHue = LC.LightHue;
  class'KFMonsterFlame'.default.LightSaturation = LC.LightSaturation;
  class'KFMonsterFlame'.default.bDynamicLight = LC.bDynamicLight;
  class'KFMonsterFlame'.default.bSpecialLit = LC.bSpecialLit;
}

//=======================================================for preloading assets hax ofc
simulated function LoadZedsAssets(byte Asset)
{
  local KFMonster KFM;
  local int i;
  /*
  KFMonsterClassNames_Standard,		//Default monster classes
                          KFMonsterClassNames_Circus,		//Circus
                          KFMonsterClassNames_Halloween,		//Halloween
                          KFMonsterClassNames_XMas;
  */
  switch (Asset)
  {
    case 0:		//Standard
      for (i=0; i<KFMonsterClassNames_Standard.length; i++)
      {
        LoadVanillaMonster(KFMonsterClassNames_Standard[i]);
      }
      break;
    case 1:		//Circus
      for (i=0; i<KFMonsterClassNames_Circus.length; i++)
      {
        LoadVanillaMonster(KFMonsterClassNames_Circus[i]);
      }
      break;

    case 2:		//Hillbilly

            for (i=0; i<KFMonsterClassNames_Halloween.length; i++)
            {
                LoadVanillaMonster(KFMonsterClassNames_Halloween[i]);
            }
    break;

    case 3:		//XMAS
            for (i=0; i<KFMonsterClassNames_XMas.length; i++)
            {
                LoadVanillaMonster(KFMonsterClassNames_XMas[i]);
            }
    break;

    case 4:		//Custom
            for (i=0; i<CustomZeds.length; i++)
            {
                LoadVanillaMonster(CustomZeds[i]);
            }
            break;

    }

  // Kill locally spawned zeds hehehe
  forEach DynamicActors (class'KFMonster', KFM)
    KFM.Destroy();
}

simulated function LoadVanillaMonster(string ClassName)			//TWI summon function
{
  local class<actor> NewClass;
  local vector SpawnLoc;

  NewClass = class<actor>( DynamicLoadObject( ClassName, class'Class', true ) );

  if( NewClass!=none )
  {
    if ( PC != none )
    {
      SpawnLoc = PC.Location;
    }
    else
      return;
    PC.Spawn( NewClass,,,SpawnLoc);
  }
}

//=======================================================for preloading assets hax ofc end
//Simple function that copy All particle emitter inline set from source to target
//return -1 if error
//If Target has more emitters that source, emitters will be nopped if NoneFlag or left intact
simulated function int LoadAndPolluteGoreEmitters(string sourceName, string targetName, optional bool NoneFlag)
{
  local class<Emitter> Source;
  local class<Emitter> Target;
  local int k;

  Source = class<Emitter>(DynamicLoadObject(sourceName, class'class', true));
  Target = class<Emitter>(DynamicLoadObject(targetName, class'class', true));

  if(Source==none || Target == none )
    return -1;

  for(k=0; k < Target.default.Emitters.Length; k++)
  {
    if(Source.default.Emitters[k] == none && NoneFlag)		//if NoneFlag overwrite particle emitters with nones
      Target.default.Emitters[k] = none;
    else if(Source.default.Emitters[k] == none && !NoneFlag)	//stop on first none reference
      break;

    Target.default.Emitters[k] = Source.default.Emitters[k];
  }
  return 1;
}

//Load emitter defaults from backup classes
simulated function LoadGoreEmitters(string targetName, byte EmitterNumber)
{
  local int i;
  local Class<Emitter>  Target;
  local array<class <Emitter> > SourceArray;

  if(BackupGore.length == 0 && EmitterNumber==0)
  {
    BackupGore[0] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[0], class'class', true));
    BackupGore[1] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[1], class'class', true));
    BackupGore[2] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[2], class'class', true));
    BackupGore[3] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[3], class'class', true));
  }
  if(OptGore.length == 0 && EmitterNumber==1)
  {
    OptGore[0] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[4], class'class', true));
    OptGore[1] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[5], class'class', true));
    OptGore[2] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[6], class'class', true));
    OptGore[3] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[7], class'class', true));
  }
  if(LowGoreE.length == 0 && EmitterNumber==2)
  {
    LowGoreE[0] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[8], class'class', true));
    LowGoreE[1] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[9], class'class', true));
    LowGoreE[2] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[10], class'class', true));
    LowGoreE[3] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[11], class'class', true));
  }
  if(NoGore.length == 0 && EmitterNumber==3)
  {
    NoGore[0] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[12], class'class', true));
    NoGore[1] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[13], class'class', true));
    NoGore[2] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[14], class'class', true));
    NoGore[3] = class<Emitter>(DynamicLoadObject(TWIGoreBackup[15], class'class', true));
  }

  Target = class<Emitter>(DynamicLoadObject(targetName, class'class', true));
  if(Target == none )
    return;

  switch (EmitterNumber)
  {
    case 0:
      SourceArray = BackupGore;
      break;
    case 1:
      SourceArray = OptGore;
      break;
    case 2:
      SourceArray = LowGoreE;
      break;
    case 3:
      SourceArray = NoGore;
      break;
  }

  for(i=0;i<SourceArray.length;i++)
  {
    if(Instr(Target, Mid(SourceArray[i],Instr(SourceArray[i], "_")+1)) != -1)
    {
      LoadAndPolluteGoreEmitters(string(SourceArray[i]), string(target));
      target.default.bHidden = SourceArray[i].default.bHidden;
      target.default.bUnlit = SourceArray[i].default.bUnlit;
      target.default.LifeSpan = SourceArray[i].default.LifeSpan;
      target.default.Style = SourceArray[i].default.Style;
      target.default.bDirectional = SourceArray[i].default.bDirectional;
      break;
    }
    else
      continue;
  }

  BackupGore.length = 0;		//unload our classes
  OptGore.length = 0;
  LowGoreE.length = 0;
  NoGore.length = 0;
}

//Restore emitters from backup
simulated function RestoreGoreEmitterFromBackups()
{
  local int i, k;
  local Class<Emitter>  CLEmitter;

  if (EMP_back.length==0)
    return;

  for (i=0;i<EMP_back.length;i++)
  {
    CLEmitter = class<Emitter>(DynamicLoadObject(EMP_back[i].ClassName, class'class', true));
    if(CLEmitter==none)
      continue;

    for (k=0; k < CLEmitter.default.Emitters.Length; k++)
    {
      CLEmitter.default.Emitters[k].Disabled = EMP_back[i].CEMP[k].Disabled;
      CLEmitter.default.Emitters[k].UseCollision = EMP_back[i].CEMP[k].UseCollision;
      CLEmitter.default.Emitters[k].AcceptsProjectors = EMP_back[i].CEMP[k].AcceptsProjectors;
    }
  }
}

//Reset flag indicates that emitter should reset to default parameters
simulated function AnotherGoreEmitterLoad(string EmitterToLoad, EMB EmitterPrefs)
{
  /*
  var EMP_backups EMP_backSingle;		//for backuping emitters properties, single emitter
  var array<EMP_backups> EMP_back;	//for backuping emitters properties, emitters array
  */
  local Class<Emitter>  CLEmitter;
  local int k;

  CLEmitter = class<Emitter>(DynamicLoadObject(EmitterToLoad, class'class', true));
  if(CLEmitter==none)
    return;

  // backuping our emitters
  EMP_backSingle.ClassName = string(CLEmitter);
  EMP_backSingle.bHidden = CLEmitter.default.bHidden;
  EMP_backSingle.bUnlit = CLEmitter.default.bUnlit;
  EMP_backSingle.Lifespan = CLEmitter.default.LifeSpan;

  for(k=0; k < CLEmitter.default.Emitters.Length; k++)		//Backuping particle emitters preferences
  {
    EMP_backSingle.CEMP[k].ClassName = string(CLEmitter.default.Emitters[k]);
    EMP_backSingle.CEMP[k].Disabled = CLEmitter.default.Emitters[k].Disabled;
    EMP_backSingle.CEMP[k].UseCollision = CLEmitter.default.Emitters[k].UseCollision;
    EMP_backSingle.CEMP[k].AcceptsProjectors = CLEmitter.default.Emitters[k].AcceptsProjectors;
  }

  if(EMP_back.length !=0)
  {
    EMP_back.length = EMP_back.length + 1;
    EMP_back[EMP_back.length] = EMP_backSingle;
  }

  else
    EMP_back[EMP_back.length] = EMP_backSingle;

  // Apply our tweaks
  CLEmitter.default.bHidden = EmitterPrefs.bHidden;
  CLEmitter.default.bUnlit = EmitterPrefs.bUnlit;
  CLEmitter.default.LifeSpan = EmitterPrefs.Lifespan;

  for(k=0; k < CLEmitter.default.Emitters.Length; k++)
  {
    CLEmitter.default.Emitters[k].UseCollision = EmitterPrefs.UseCollision;
    ClEmitter.default.Emitters[k].Disabled = EmitterPrefs.Disabled;		//Disable all particle emitters
    ClEmitter.default.Emitters[k].AcceptsProjectors = EmitterPrefs.AcceptsProjectors;
  }
}
//Rewrite this and make this shit dynamic
//=====================================================================
simulated function AddStandardGorePresets()
{
  local int i,k;
  local class<Emitter> ClEmitter;

  Gore_Preset.PresetName = "OPTIMIZED_TWI";
  Gore_Preset.PresetDescription = "Optimized TWI shit...";
  Gore_Preset.EmittersToLoad.Insert(0, KFMonsterGibEmitters.length);

  for (i=0; i< KFMonsterGibEmitters.length; i++)
  {
    Gore_Preset.EmittersToLoad[i].ClassName = KFMonsterGibEmitters[i];
    Gore_Preset.EmittersToLoad[i].bHidden = false;
    Gore_Preset.EmittersToLoad[i].bUnlit = true;
    Gore_Preset.EmittersToLoad[i].LifeSpan = 5;

    CLEmitter = class<Emitter>(DynamicLoadObject(KFMonsterGibEmitters[i], class'class', true));
    // if (CLEmitter == none)
    // PC.ClientMessage("OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" @ KFMonsterGibEmitters[i]);
    Gore_Preset.EmittersToLoad[i].CEMP.Insert(0, CLEmitter.default.Emitters.Length);
    for (k=0; k < CLEmitter.default.Emitters.Length; k++)		//Editing particle emitters
    {
      Gore_Preset.EmittersToLoad[i].CEMP[k].ClassName = string(CLEmitter.default.Emitters[k]);
      Gore_Preset.EmittersToLoad[i].CEMP[k].Disabled = false;
      if (int(CLEmitter.default.Emitters[k].InitialParticlesPerSecond / CLEmitter.default.Emitters[k].MaxParticles) > 350)			//not fucking 0.00000001
        Gore_Preset.EmittersToLoad[i].CEMP[k].InitialParticlesPerSecond = float(CLEmitter.default.Emitters[k].MaxParticles * 200);		//0.01 ms to spawn
      else
        Gore_Preset.EmittersToLoad[i].CEMP[k].InitialParticlesPerSecond = CLEmitter.default.Emitters[k].InitialParticlesPerSecond;		//else let's save old value

      Gore_Preset.EmittersToLoad[i].CEMP[k].UseCollision = CLEmitter.default.Emitters[k].UseCollision;
      Gore_Preset.EmittersToLoad[i].CEMP[k].AcceptsProjectors = false;
    }
  }
  Gore_Presets[0] = Gore_Preset;

  Gore_Preset.PresetName = "NO_GORE";
  Gore_Preset.PresetDescription = "No gore at all. Disables all emitters.";

  Gore_Preset.EmittersToLoad.Insert(0, KFMonsterGibEmitters.length);
  for (i=0; i< KFMonsterGibEmitters.length; i++)
  {
    Gore_Preset.EmittersToLoad[i].ClassName = KFMonsterGibEmitters[i];
    Gore_Preset.EmittersToLoad[i].bHidden = true;
    Gore_Preset.EmittersToLoad[i].bUnlit = true;
    Gore_Preset.EmittersToLoad[i].LifeSpan = 0.5;

    CLEmitter = class<Emitter>(DynamicLoadObject(KFMonsterGibEmitters[i], class'class', true));
    Gore_Preset.EmittersToLoad[i].CEMP.Insert(0, CLEmitter.default.Emitters.Length);
    for (k=0; k < CLEmitter.default.Emitters.Length; k++)		//Editing particle emitters
    {
      Gore_Preset.EmittersToLoad[i].CEMP[k].ClassName = string(CLEmitter.default.Emitters[k]);
      Gore_Preset.EmittersToLoad[i].CEMP[k].Disabled = true;
      Gore_Preset.EmittersToLoad[i].CEMP[k].UseCollision = false;
      Gore_Preset.EmittersToLoad[i].CEMP[k].AcceptsProjectors = false;
    }
  }
  Gore_Presets[1] = Gore_Preset;

  Gore_Preset.PresetName = "LOW_GORE";
  Gore_Preset.PresetDescription = "Low gore.";

  Gore_Preset.EmittersToLoad.Insert(0, KFMonsterGibEmitters.length);
  for (i=0; i< KFMonsterGibEmitters.length; i++)
  {
    Gore_Preset.EmittersToLoad[i].ClassName = KFMonsterGibEmitters[i];
    Gore_Preset.EmittersToLoad[i].bHidden = false;
    Gore_Preset.EmittersToLoad[i].bUnlit = true;
    Gore_Preset.EmittersToLoad[i].LifeSpan = 5;

    CLEmitter = class<Emitter>(DynamicLoadObject(KFMonsterGibEmitters[i], class'class', true));
    Gore_Preset.EmittersToLoad[i].CEMP.Insert(0, CLEmitter.default.Emitters.Length);
    for (k=0; k < CLEmitter.default.Emitters.Length; k++)		//Editing particle emitters
    {
      Gore_Preset.EmittersToLoad[i].CEMP[k].ClassName = string(CLEmitter.default.Emitters[k]);
      Gore_Preset.EmittersToLoad[i].CEMP[k].Disabled = false;

      if (int(CLEmitter.default.Emitters[k].InitialParticlesPerSecond / CLEmitter.default.Emitters[k].MaxParticles) > 350)			//not fucking 0.00000001
        Gore_Preset.EmittersToLoad[i].CEMP[k].InitialParticlesPerSecond = float(CLEmitter.default.Emitters[k].MaxParticles * 200);		//0.01 ms to spawn
      else
        Gore_Preset.EmittersToLoad[i].CEMP[k].InitialParticlesPerSecond = CLEmitter.default.Emitters[k].InitialParticlesPerSecond;		//else let's save old value

      Gore_Preset.EmittersToLoad[i].CEMP[k].UseCollision = false;
      Gore_Preset.EmittersToLoad[i].CEMP[k].AcceptsProjectors = false;
    }
  }
    Gore_Presets[2] = Gore_Preset;
    Gore_Preset.PresetName = "TWI";
    Gore_Preset.PresetDescription = "Just TWI shit...";

    Gore_Preset.EmittersToLoad.Insert(0, KFMonsterGibEmitters.length);
    for (i=0; i< KFMonsterGibEmitters.length; i++)
    {
      CLEmitter = class<Emitter>(DynamicLoadObject(KFMonsterGibEmitters[i], class'class', true));
      Gore_Preset.EmittersToLoad[i].ClassName = KFMonsterGibEmitters[i];
      Gore_Preset.EmittersToLoad[i].bHidden = false;
      Gore_Preset.EmittersToLoad[i].bUnlit = false;
      Gore_Preset.EmittersToLoad[i].LifeSpan = CLEmitter.default.Lifespan;

      Gore_Preset.EmittersToLoad[i].CEMP.Insert(0, CLEmitter.default.Emitters.Length);
      for (k=0; k < CLEmitter.default.Emitters.Length; k++)		//Editing particle emitters
      {
        Gore_Preset.EmittersToLoad[i].CEMP[k].ClassName = string(CLEmitter.default.Emitters[k]);
        Gore_Preset.EmittersToLoad[i].CEMP[k].Disabled = false;
        Gore_Preset.EmittersToLoad[i].CEMP[k].InitialParticlesPerSecond = CLEmitter.default.Emitters[k].InitialParticlesPerSecond;		//else let's save old value

        Gore_Preset.EmittersToLoad[i].CEMP[k].UseCollision = CLEmitter.default.Emitters[k].UseCollision;
        Gore_Preset.EmittersToLoad[i].CEMP[k].AcceptsProjectors = false;
      }
    }
  Gore_Presets[3] = Gore_Preset;
}


simulated function ApplyStandardGorePresets(string SetPresetName)
{
local int i,k, l;
local class<Emitter> ClEmitter;
    for (l=0; l < Gore_Presets.length; l++)
    {
        if (Gore_Presets[l].PresetName == SetPresetName)
        {
        Gore_Preset = Gore_Presets[l];

            for (i=0; i< KFMonsterGibEmitters.length; i++)
            {
                if (Gore_Preset.EmittersToLoad[i].ClassName == KFMonsterGibEmitters[i])
                {

                CLEmitter = class<Emitter>(DynamicLoadObject(KFMonsterGibEmitters[i], class'class', true));

                CLEmitter.default.bHidden = Gore_Preset.EmittersToLoad[i].bHidden;
                CLEmitter.default.bUnlit = Gore_Preset.EmittersToLoad[i].bUnlit;
                CLEmitter.default.LifeSpan = Gore_Preset.EmittersToLoad[i].LifeSpan;
                //PC.ClientMessage("Hidden is" @ CLEmitter.default.bHidden @ "Unlit is" @ CLEmitter.default.bUnlit @ "Lifespan is" @ CLEmitter.default.LifeSpan);

                    for (k=0; k < CLEmitter.default.Emitters.Length; k++)		//Editing particle emitters
                    {
                        CLEmitter.default.Emitters[k].Disabled = Gore_Preset.EmittersToLoad[i].CEMP[k].Disabled;
                        //PC.ClientMessage("Disabled is" @ Gore_Preset.EmittersToLoad[i].CEMP[k].Disabled @ "on" @ CLEmitter.default.Emitters[k]);
                        CLEmitter.default.Emitters[k].InitialParticlesPerSecond = Gore_Preset.EmittersToLoad[i].CEMP[k].InitialParticlesPerSecond;
                        //PC.ClientMessage("Initial particles are" @ Gore_Preset.EmittersToLoad[i].CEMP[k].InitialParticlesPerSecond);
                        CLEmitter.default.Emitters[k].UseCollision = Gore_Preset.EmittersToLoad[i].CEMP[k].UseCollision;
                        //PC.ClientMessage(Gore_Preset.EmittersToLoad[i].CEMP[k].UseCollision);
                        CLEmitter.default.Emitters[k].AcceptsProjectors = Gore_Preset.EmittersToLoad[i].CEMP[k].AcceptsProjectors;
                        //PC.ClientMessage(Gore_Preset.EmittersToLoad[i].CEMP[k].AcceptsProjectors);
                    }
                break;
                }
            }
        break;
        }
        else
        continue;
    }
}

//=====================================================================
//Some gore-related emitter tweaks
simulated function OptimizeGore()
{
  //local int i;
  GibAndGibletsTweaks(false);
  /*
  if(CurrentOptParams.UserSettingsToEmitters)		//load user settings
  {
    for (i=0; i < KFMonsterGibEmitters.length; i++)
    {
      AnotherGoreEmitterLoad(KFMonsterGibEmitters[i], User_EMPS);		//Load user settings
    }
  }
  */
  if(Gore_Presets.length == 0)
    AddStandardGorePresets();

  ApplyStandardGorePresets(CurrentOptParams.GoreLevel);		//Apply our gore parameters
  switch(CurrentOptParams.GoreLevel)
  {
    case "OPTIMIZED_TWI":
      LoadGoreEmitters(TWIGibEmitters[0], 1);
      LoadGoreEmitters(TWIGibEmitters[1], 1);
      LoadGoreEmitters(TWIGibEmitters[2], 1);
      LoadGoreEmitters(TWIGibEmitters[3], 1);
      break;
    case "TWI":
      LoadGoreEmitters(TWIGibEmitters[0], 0);
      LoadGoreEmitters(TWIGibEmitters[1], 0);
      LoadGoreEmitters(TWIGibEmitters[2], 0);
      LoadGoreEmitters(TWIGibEmitters[3], 0);
      break;
    case "NO_GORE":		//No gore preset
      LoadGoreEmitters(TWIGibEmitters[0], 3);
      LoadGoreEmitters(TWIGibEmitters[1], 3);
      LoadGoreEmitters(TWIGibEmitters[2], 3);
      LoadGoreEmitters(TWIGibEmitters[3], 3);
      break;
    case "LOW_GORE":
      LoadGoreEmitters(TWIGibEmitters[0], 2);
      LoadGoreEmitters(TWIGibEmitters[1], 2);
      LoadGoreEmitters(TWIGibEmitters[2], 2);
      LoadGoreEmitters(TWIGibEmitters[3], 2);
      break;
    case "GORY!":
      break;
    case "BLOOD FOR THE BLOOD GOD!":
      break;
  }
}

// Tweaks for gibz and giblets
// Pollute gib classes, flag reset to default parameters
simulated function PolluteGibClasses(string GibGlass,optional bool Reset)
{
  local class<KFGib> KFGib;

  KFGib = GetGibletsClassFromString(GibGlass);
  if (KFGib==none)
    return;

  if (!Reset)
  {
    KFGib.default.bHidden = CurrentOptParams.bHideGibs;
    KFGib.default.bUnlit = CurrentOptParams.bUnlitGibs;
    KFGib.default.LifeSpan= CurrentOptParams.GibsLifespan;
    if (CurrentOptParams.GibUseDeferRendering)
    {
      KFGib.default.Style = STY_Additive;			//Enable defer rendering
      KFGib.default.bDeferRendering = true;
    }
    KFGib.default.MaxLights = CurrentOptParams.GibMaxLights;
    KFGib.default.bUseCollisionStaticMesh= CurrentOptParams.GibUseCollisionStaticMesh;

    KFGib.default.bAcceptsProjectors = CurrentOptParams.GibAcceptProjectors;
    KFGib.default.bStaticLighting = CurrentOptParams.GibStaticLighting;
  }
  else
  {
    KFGib.default.bHidden = false;
    KFGib.default.bUnlit = false;
    KFGib.default.LifeSpan= 6.0;
    if (CurrentOptParams.GibUseDeferRendering)
    {
      KFGib.default.Style = STY_Normal;
    }
    KFGib.default.MaxLights = 4;
    KFGib.default.bUseCollisionStaticMesh= true;

    KFGib.default.bAcceptsProjectors = true;
    KFGib.default.bStaticLighting = false;
  }
}

simulated function PolluteGibletsClasses(string GibletGlass,optional bool Reset)
{
  local class<SeveredAppendage> KFSevered;

  KFSevered = GetGibClassFromString(GibletGlass);
  if (KFSevered==none)
    return;

  if (!Reset)
  {
    KFSevered.default.bHidden = CurrentOptParams.bHideGiblets;
    KFSevered.default.bUnlit = CurrentOptParams.bUnlitGiblets;
    KFSevered.default.LifeSpan = CurrentOptParams.GibletsLifespan;
    if (CurrentOptParams.GibletsUseDeferRendering)
    {
      KFSevered.default.Style = STY_Additive;			//Enable defer rendering
      KFSevered.default.bDeferRendering = true;
    }
    KFSevered.default.MaxLights = CurrentOptParams.GibletsMaxLights;
    KFSevered.default.bUseCollisionStaticMesh= CurrentOptParams.GibletsUseCollisionStaticMesh;

    KFSevered.default.bAcceptsProjectors = CurrentOptParams.GibletsAcceptProjectors;
    KFSevered.default.bStaticLighting = CurrentOptParams.GibletsStaticLighting;
  }

  else
  {
    KFSevered.default.bHidden = false;
    KFSevered.default.bUnlit = false;
    KFSevered.default.LifeSpan = 8.0;
    if (CurrentOptParams.GibletsUseDeferRendering)
    {
      KFSevered.default.Style = STY_Normal;			//Disable defer rendering
      KFSevered.default.bDeferRendering = true;
    }
    KFSevered.default.MaxLights = 4;
    KFSevered.default.bUseCollisionStaticMesh= false;

    KFSevered.default.bAcceptsProjectors = true;
    KFSevered.default.bStaticLighting = false;
  }
}

simulated function GibAndGibletsTweaks(bool Reset)
{
  local int i;

  if (CurrentOptParams.bAdvancedGiblets)
  {
    for (i=0; i<KFMonsterGibletsClassNames.length; i++)
    {
      PolluteGibletsClasses(KFMonsterGibletsClassNames[i], Reset);
    }
  }

  if (CurrentOptParams.bAdvancedGibs)
  {
    for (i=0; i<KFMonsterGibClassNames.length; i++)
    {
      PolluteGibClasses(KFMonsterGibClassNames[i], Reset);
    }
  }
}

//Load and pollute default monster classes
simulated function LoadMonsters(string MonsterClassToLoad, optional bool Reset)
{
  local class<KFMonster> KFM;

  if(!Reset)
  {
    KFM = GetMonsterClassFromString(MonsterClassToLoad);
    if(KFM==none)
      return;

    KFM.default.cullDistance = CurrentOptParams.ZedCullDistance;
    KFM.default.bUnlit = CurrentOptParams.useUnlit;
    KFM.default.AmbientGlow =CurrentOptParams.AmbientGlow;
    KFM.default.SoundOcclusion = CurrentOptParams.SoundOcclusion;
    KFM.default.SoundRadius = CurrentOptParams.SoundRadius;
  }

  else
  {
    KFM.default.cullDistance = 0;
    KFM.default.bUnlit = false;
    KFM.default.AmbientGlow = 0;
    KFM.default.SoundOcclusion = Occlusion_Default;
    KFM.default.SoundRadius = 300;
  }
}

//Pollute default zed classes and detect gametype
simulated function PolluteDefaults(bool Reset)
{
  local int i;

  for(i=0; i<KFMonsterClassNames_Standard.length; i++)
  {
    if (CurrentOptParams.bStandard)			//Loading standard classes
    {
      LoadMonsters(KFMonsterClassNames_Standard[i], Reset);
    }
    else if (CurrentOptParams.bCircus)			//Loading circus classes
    {
      LoadMonsters(KFMonsterClassNames_Circus[i], Reset);
    }
    else if (CurrentOptParams.bXmas)			//Loading xmas classes
    {
      LoadMonsters(KFMonsterClassNames_XMas[i], Reset);
    }
    else if (CurrentOptParams.bHillBilly)			//Loading standard classes
    {
      LoadMonsters(KFMonsterClassNames_Halloween[i], Reset);
    }
  }

  if(Instr(CAPS(Level.GRI.GameClass), "SCRN")!=-1)			//Scrn gametype custom zeds
  {
    for (i=0; i<CustomZeds.length; i++)
    {
      LoadMonsters(CustomZeds[i], Reset);
    }
  }

  if(Instr(CAPS(Level.GRI.GameClass), "NICE")!=-1)			//NicePack.NiceGameType
  {
    for(i=0; i<NiceZeds.length; i++)
    {
      LoadMonsters(NiceZeds[i], Reset);
    }
  }
}


//=======================================Seasoned zeds remover
simulated function RemoveSeasonedZeds()
{
  /*
  CurrentOptParams.bStandard
  */
  if (CurrentOptParams.bCircus)
    ReplaceZedsDefaults(KFMonsterClassNames_Circus, KFMonsterClassNames_Standard);
  if (CurrentOptParams.bXmas)
    ReplaceZedsDefaults(KFMonsterClassNames_Halloween, KFMonsterClassNames_Standard);
  if (CurrentOptParams.bHillBilly)
    ReplaceZedsDefaults(KFMonsterClassNames_XMas, KFMonsterClassNames_Standard);
}

//===================================Replaces zed properties sets with KFMReplace class properties sets
simulated function ReplaceZedsDefaults(array<string> KFMonsterClassNames, array<string> KFMReplace)
{
  local int i,k;
  local class<KFMonster> KFM, ReplaceKFM;
  local string MeshName;

  //OFC some bullshit first...
  /*Shaders. Need to re-do this...
        class'ZombieClot_HALLOWEEN'.default.ClotSkins[0] = shader'Chaos_WeaponPack_T.Clot_standard_shader';
        class'ZombieClot_HALLOWEEN'.default.ClotSkins[1] = shader'Chaos_WeaponPack_T.Clot_standard_shader';
        class'ZombieClot_HALLOWEEN'.default.ClotSkins[2] = shader'Chaos_WeaponPack_T.Clot_standard_shader';
        class'ZombieClot_HALLOWEEN'.default.ClotSkins[3] = shader'Chaos_WeaponPack_T.Clot_standard_shader';
        class'ZombieClot_HALLOWEEN'.default.ClotSkins[4] = shader'Chaos_WeaponPack_T.Clot_standard_shader';
        class'ZombieClot_HALLOWEEN'.default.ClotSkins[5] = shader'Chaos_WeaponPack_T.Clot_standard_shader';
  */
  if (KFMonsterClassNames.length != KFMReplace.length)
    return;

  class'ZombieGoreFast_CIRCUS'.default.bHeadGibbed=false;		//go fuck your mouth with your circus shit

  for (i=0; i<KFMonsterClassNames.length; i++)
  {
    KFM = GetMonsterClassFromString(KFMonsterClassNames[i]);	//Get monster class to pollute
    ReplaceKFM = GetMonsterClassFromString(KFMReplace[i]);		//load class replacer

    if (KFM == none || ReplaceKFM == none)		// if none - continue
      continue;

    MeshName = string(ReplaceKFM.default.Mesh);			//save mesh name
    KFM.static.UpdateDefaultMesh(SkeletalMesh(DynamicLoadObject(MeshName, Class'SkeletalMesh', true)));	//replace default skeletal mesh

    for(k=0;k<KFM.default.Skins.Length;k++)					//Reloading skins
    {
      KFM.default.Skins[k] = ReplaceKFM.default.Skins[k];
    }

    KFM.default.DetachedArmClass = ReplaceKFM.default.DetachedArmClass;				//Reloading gore-related shit
    KFM.default.DetachedLegClass = ReplaceKFM.default.DetachedLegClass;
    KFM.default.DetachedHeadClass = ReplaceKFM.default.DetachedHeadClass;
    KFM.default.HitSound[0] = ReplaceKFM.default.HitSound[0];			//Static arrays...
    KFM.default.ChallengeSound[0] = ReplaceKFM.default.ChallengeSound[0];		//...
    KFM.default.ChallengeSound[1] = ReplaceKFM.default.ChallengeSound[1];
    KFM.default.ChallengeSound[2] = ReplaceKFM.default.ChallengeSound[2];
    KFM.default.ChallengeSound[3] = ReplaceKFM.default.ChallengeSound[3];
    KFM.default.AmbientSound = ReplaceKFM.default.AmbientSound;			//Replacing ambient sounds
    KFM.default.MoanVoice = ReplaceKFM.default.MoanVoice;			//Replacing moan voice aka idle moaning sounds
    KFM.default.DeathSound[0]=ReplaceKFM.default.DeathSound[0];			//...
    KFM.default.DeathSound[1]=ReplaceKFM.default.DeathSound[1];
    KFM.default.JumpSound=ReplaceKFM.default.JumpSound;
    KFM.default.MeleeAttackHitSound=ReplaceKFM.default.MeleeAttackHitSound;
  }
}

simulated function RestoreSeasonedZeds(){}

simulated function DetectHeadless() //Detect headless zeds for LowGore decaps
{
  local int i;
  local KFMonster KFM;

  NewTicks=0;
  if(!LowGore)
    return;

  for(i = 0; i < cachedZeds.length; i++)
  {
    KFM = cachedZeds[i];
    if(KFM == none)
      continue;
    if((KFM.bDecapitated && KFM.Health <= 0) || KFM.bDecapitated )
    {
      KFM.NeckRot.Yaw = 0;				//CuteDecapFX undone
      KFM.NeckRot.Roll = 0;
      KFM.NeckRot.Pitch = 0;
      KFM.SetBoneRotation('neck', KFM.NeckRot);
      KFM.SpecialHideHead();			//Using decap function for melee weapons cuz no fps drops.
    }
  }
}

simulated event Destroyed()
{
  super.Destroyed();
}

//==============String to class conversion
simulated function class<KFGib> GetGibletsClassFromString(string String)
{
  //for conversion from string to class reference
  return class<KFGib>(DynamicLoadObject(String, class'class', true));
}

simulated function class<SeveredAppendage> GetGibClassFromString(string String)
{
  //for conversion from string to class reference
  return class<SeveredAppendage>(DynamicLoadObject(String, class'class', true));
}

simulated function class<KFMonster> GetMonsterClassFromString(string String)
{
  //for conversion from string to class reference
  return class<KFMonster>(DynamicLoadObject(String, class'class', true));
}

//==============String to class conversion
simulated function class<Emitter> GetEmitterClassFromString(string String)
{
  //for conversion from string to class reference
  return class<Emitter>(DynamicLoadObject(String, class'class', true));
}

defaultproperties
{
  bHidden=true
  bUnlit=true
}