class HideMutInteraction extends Interaction
  config(HideMut);


// ====================================================
// N.B. Important references
// don't forget to null them on map switch !!
var protected KFPlayerController pc;

var protected a_CashTosser a_CashTosser;
var protected a_WeaponTosser a_WeaponTosser;
var a_WeaponManager a_weaponManager;
var a_VisibilityHandler a_visibilityHandler;

// store all our bullshit here
// var HideObjectPool HideObjectPool;

// handy shortcuts
// for degug: editobj o_Utility_instance, editobj o_Render_instance
var o_Utility _;
var o_Render o_Render;

var bool bBuyMode;
var bool bCP;
var bool bReveal;
var bool bInitialized;

// crosshair shit
var config float XLength, XWidth;
var config Color CrosshairColor;
var config bool bCross;

// zed health bar related
var transient bool bHPBar;
var config float fHPBar;

// lighting related
var Halo HaloLight;
var transient bool bHalo;


event Created()
{
  super.Created();

  // TEST!!! this should be persistent enough!
  // HideObjectPool = new() class'HideObjectPool';
}


// auto state DoSomeStuff
// {
//   event beginstate()
//   {
//     bRequiresTick = true;
//     log(">>> FUCK YEA WE ARE ALIVE");
//   }

//   event Tick(float deltatime)
//   {
//     if (viewportOwner != none)
//     {
      
//       bRequiresTick = false;
//       gotostate('');
//     }
//   }
// }


// =============================================================================
//                           CLEANUP
// =============================================================================

// editobj a_weaponmanager0
event NotifyLevelChange()
{
  // remove our references or we will crash eventually
  pc = none;

  // 1. loading standard zed assets
  // this method works far better without actor spawning
  // and we really need to call it just once
  class'o_AssetLoader'.static.LoadZedAsset("", viewportOwner.actor.level);

  // so we can catch our spawn time
  bInitialized = false;

  // Does this interaction require game TICK
  bRequiresTick = class'Settings'.default.bRandomName;

  if (a_weaponManager != none)
  {
    a_weaponManager.Termination();
    a_weaponManager = none;
  }
  if (a_WeaponTosser != none)
  {
    a_WeaponTosser.Termination();
    a_WeaponTosser = none;
  }
  if (a_CashTosser != none)
  {
    a_CashTosser.Termination();
    a_CashTosser = none;
  }
  if (a_visibilityHandler != none)
  {
    a_visibilityHandler.Termination();
    a_visibilityHandler = none;
  }

  // clean the memory, this is mostly crash free
  write_Console("obj garbage;flush");
}


//=============================================================================
//                               THE TICK !!!
//=============================================================================

function Tick(float DeltaTime)
{
  // auto rename on game login
  if (!bInitialized && class'Settings'.default.bRandomName && KFPlayerReplicationInfo(ViewportOwner.Actor.PlayerReplicationInfo) != none)
  {
    SN(_.getRandString(0));
    bInitialized = true;
    bRequiresTick = false;
  }
}


//=============================================================================
//                           FAST-ACCESS FUNCTIONS
//=============================================================================

// toss a string array, get the Console, and execute everything in a safe, clean way, DIVIDER ";"
final private function write_Console(string s)
{
  _.write_Console(s, viewportOwner);
}


// privately prints from ARRAY to console
final private function print_Console_arr(array<string> s)
{
  _.print_Console_arr(s, viewportOwner);
}


// privately prints from STRING to console, accepts arrays with DIVIDER ";"
final private function print_Console_s(string s)
{
  _.print_Console_s(s, viewportOwner);
}


final private function a_WeaponManager getWMActor()
{
  local KFPlayerController lpc;

  lpc = getController();

  // if none - do everything properly
  if (a_WeaponManager == none)
  {
    a_weaponManager = lpc.spawn(class'a_WeaponManager');
    a_weaponManager.kfpc = lpc;
  }

  // if somehow pc differs - fix it
  if (a_weaponManager.kfpc != lpc)
    a_weaponManager.kfpc = lpc;

  if (getPawn().weapon == none)
    lpc.switchToBestWeapon();

  return a_weaponManager;
}


final function checkVisibilityHandler()
{
  if (getPC() && a_visibilityHandler == none)
    a_visibilityHandler = pc.spawn(class'a_VisibilityHandler', pc);
}

final private function KFHumanPawn getPawn()
{
  // if (viewportOwner.actor == none)
  //   return none;
  return KFHumanPawn(KFPlayerController(viewportOwner.actor).pawn);
}


final private function KFPlayerController getController()
{
  if (pc == none)
    pc = KFPlayerController(viewportOwner.actor);

  return pc;
}


// check if we have a kfplayercontroller and return PC if true 
final private function bool getPC()
{
  if (KFPlayerController(viewportOwner.actor) != none)
    pc = KFPlayerController(viewportOwner.actor);
  return (pc != none);
}


// are we an alive controller?
final private function bool isPlayerAlive()
{
  if (!getPC() || pc.isInState('Spectating') || pc.isInState('Dead') || pc.isInState('PlayerWaiting') || pc.isInState('WaitingForPawn'))
    return false;
  return true;
}


// show a message in text area, without printing it in console
final protected function msg(string message)
{
  if (getPC())
    pc.myHUD.AddTextMessage(message, class'LocalMessage', pc.playerReplicationInfo);
}


// simply open menus from classes
final private function GCtrl_OpenMenu(class c)
{
  local string s;

  s = string(c);
  GUIController(viewportOwner.guiController).openMenu(s);
}


//=============================================================================
//                                   MENUS
//=============================================================================

// latest version atm, Bi's
exec function hideMenuNew()
{
  checkVisibilityHandler(); // TODO: do I need it here?
  GCtrl_OpenMenu(class'menu_Hide');
}


// Chaos's menu
exec function hideMenu()
{
  GCtrl_OpenMenu(class'menu_c_New');
}


// very old one, Bi's second version
exec function hideMenuOld()
{
  GCtrl_OpenMenu(class'menu_Old');
}


// weapon menu
exec function weaponManagement()
{
  GCtrl_OpenMenu(class'menu_WeaponManager');
}


// shortcut for weaponManagement
exec function wm()
{
  write_Console("weaponManagement");
}


// returns player to lobby menu if match haven't started yet
exec function lobby()
{
  if (getPC())
  {
    pc.StopForceFeedback();  // jdf - no way to pause feedback
    pc.bPendingLobbyDisplay = false;
    // Open menu
    pc.ClientOpenMenu(string(class'HideLobbyMenu'));
  }
}


// opens custom ingame chat windows. Default class is reserved for mod startup
exec function chatwindow()
{
  GCtrl_OpenMenu(class'Hide_IngameChat');
}


// shortcut for ^
exec function cw()
{
  chatwindow();
}


exec function doTheMagic()
{
  checkVisibilityHandler();
  a_visibilityHandler.doTheMagic();
}


// =============================================================================
//                              ADMIN HELPERS
// =============================================================================

// log in as administrator/log off
// uses admin accounts from config
exec function adlog()
{
  if (getPC())
    _.adlog(pc);
}


exec function adloginfo()
{
  if (getPC())
    _.adlog_Info(pc);
}

exec function adlogadd(string msg)
{
  if (getPC())
    _.adlog_Control(pc, true, msg);
}

exec function adlogrem()
{
  if (getPC())
    _.adlog_Control(pc, false, "");
}


// =============================================================================
//                            Keybind Processing
// =============================================================================
// IST_None     <NOT USED ANYWHERE, PROLLY NOT WORKING!> Not performing special input processing
// IST_Press    Handling a keypress or button press
// IST_Hold     <NOT WORKING!> Handling holding a key or button
// IST_Release  <USED ONLY FOR CONSOLE TOGGLING> Handling a key or button release
// IST_Axis     <NOT USED ANYWHERE, PROLLY NOT WORKING!> Handling analog axis movement

// function bool KeyEvent( out EInputKey Key, out EInputAction Action, FLOAT Delta )
// {
//   local string Alias, LeftPart, RigthPart;

//   // If this is a button press - detect alias
//   if (Action == IST_Press)
//   {
//     log("YEAH WE ARE HOLDING A BUTTON !!!!!");
//     Alias = getController().ConsoleCommand("KEYBINDING" @ getController().ConsoleCommand("KEYNAME" @ Key));
//     if (Divide(Alias, " ", LeftPart, RigthPart))
//       Alias = LeftPart;

//     if (Alias ~= "tosscash")
//       return dropAllDosh();
//   }
//   return false;
// }


// final function bool dropAllDosh()
// {
//   if (getController() == none)
//     return false;
//   else
//   {
//     getController().ConsoleCommand("tosscash " $ 1000000);
//     return true;
//   }
// }

//=============================================================================
//                                 CRASHING n Nades
//=============================================================================

// requires at least 2-3k dosh
exec function crashServer()
{
  _.doshspam(getPawn());
}


// nade technic
exec function crashServer2()
{
  _.nadespam(getPawn(), 100);
}


// spec - join technic
exec function crashServer3()
{
  _.specspam(getController());
}


// infinite nades for vanilla
exec function nade()
{
  _.ThrowNade(getPawn());
}


// quick input switching
exec function vnade(optional bool b)
{
  if (b)
    write_Console("set input g nade");
  else
    write_Console("set input g thrownade");
}


// infinite nades for vanilla
exec function bignade(int i)
{
  _.nadespam(getPawn(), i);
}


//=============================================================================
//                                 Trader
//=============================================================================

// Buys vest
exec function buyKevlar()
{
  getPawn().serverBuyKevlar();
}


// Restores HP (Constant 150£)
exec function buyHP()
{
  getPawn().serverBuyFirstAid();
}


// Buys ammo for all weapons
exec function buyAmmo()
{
  local KFHumanPawn me;
  local Inventory inv;
  local KFWeapon kfw;

  me = getPawn();
  if (me == none)
    return;

  for (inv = me.inventory; inv != none; inv = inv.inventory)
  {
    kfw = KFWeapon(inv);
    // exclude welder!!! it can give infinite dosh due to 0 dividing
    if (kfw != none && !kfw.IsA('Syringe'))
    {
      if (kfw.ammoClass[0] != none)
        me.serverBuyAmmo(kfw.ammoClass[0], false);
      if (kfw.AmmoClass[1] != none)
        me.serverBuyAmmo(kfw.ammoClass[1], false);
    }
  }
}


// Allows to buy any weapon with its class name
exec function buy(string weaponClassName)
{
  local class<KFWeapon> weaponToBuy;

  weaponToBuy = class<KFWeapon>(dynamicLoadObject(weaponClassName, class'Class', true));
  getPawn().serverBuyWeapon(weaponToBuy, 0);
}


// Buys all vanilla weapons
exec function buyAll()
{
  local int i;
  local KFHumanPawn me;
  local array<class <KFWeapon> > Weapons;

  // fail safe
  me = getPawn();
  if (!isPlayerAlive() || me == none)
    return;

  Weapons = _.getALLWeapons(pc);

  for (i = 0; i < Weapons.length; i++)
  {
    me.serverBuyWeapon(Weapons[i],0);
  }

  // msg("You've just bought every single weapon in KF, you sick bastard.");
}


// unlocks all DLC weapons
// requires modified Engine.u
exec function unlockDlc()
{
  getController().serverInitializeSteamStatInt(200, 4095);
  KFSteamStatsAndAchievements(getController().steamStatsAndAchievements).ownedWeaponDLC.value = 4095;
  msg("All DLCs were unlocked.");
}


//=============================================================================
//                            WEAPON MANAGEMENT
//=============================================================================

exec function reallyQuickHeal()
{
  if (getPawn() != none)
    getWMActor().reallyQuickHeal();
}


exec function hide_getWeapon(byte F)
{
  if (getPawn() != none)
    getWMActor().hide_getWeapon(F);
}

//=============================================================================
//                              WEAPON FUNCTIONS
//=============================================================================

exec function showPriority()
{
  msg(string(getPawn().weapon.default.priority));
}


// fast shooting
exec function nonStop(optional bool bNewWaitForRelease, optional bool bAllWeapons)
{
  local Inventory Inv;
  local KFHumanPawn me;
  local KFWeapon kfw;

  me = getPawn();
  if (!bAllWeapons)
  {
    kfw = KFWeapon(me.weapon);
    if (kfw != none)
      kfw.serverChangeFireMode(bNewWaitForRelease);
  }
  else
  {
    for(Inv = me.Inventory; Inv != none; Inv = Inv.Inventory)
    {
      kfw = KFWeapon(Inv);
      if (kfw != none && kfw.bConsumesPhysicalAmmo && !kfw.bMeleeWeapon)
        kfw.serverChangeFireMode(bNewWaitForRelease);
    }
  }

  // KFHumanPawn(KFPlayerController(viewportOwner.actor).pawn);
  // KFWeapon(KFHumanPawn(getController().pawn).weapon).serverChangeFireMode(bNewWaitForRelease);
  // KFWeapon(KFHumanPawn(getController().pawn).weapon).fireMode[0].bWaitForRelease = bNewWaitForRelease;
}


// no recoil
exec function nr()
{
  local WeaponFire wf;
  local KFFire kff;
  local KFShotgunFire kfsf;

  forEach allObjects(class'WeaponFire', wf)
  {
    if (wf == none)
      continue;

    kff = KFFire(wf);
    if (kff != none)
    {
      kff.RecoilRate = 0.0;
      kff.maxVerticalRecoilAngle = 0;
      kff.maxHorizontalRecoilAngle = 0;
    }

    kfsf = KFShotgunFire(wf);
    if (kfsf != none)
    {
      kfsf.RecoilRate = 0.0;
      kfsf.maxVerticalRecoilAngle = 0;
      kfsf.maxHorizontalRecoilAngle = 0;
    }

    // kfsf.RandomPitchAdjustAmt = 0;
    // kfsf.Spread = 0;
    // kfsf.SpreadStyle=SS_None;
    // kfsf.FireRate=20;
    // kfsf.ProjPerFire=50;
  }
}


// infinite flashlight
exec function fl(optional bool bCleanup)
{
  local Effect_TacLightProjector FlashLight;

  if (bCleanup)
  {
    foreach allObjects(class'Effect_TacLightProjector', FlashLight)
      FlashLight.destroy();
    return;
  }

  FlashLight = getPawn().spawn(class'Effect_TacLightProjector', getPawn());
  FlashLight.bHasLight =! FlashLight.bHasLight;
  // old code, uses charge
  // KFWeapon(KFHumanPawn(getController().pawn).weapon).serverSpawnLight();
}


//=============================================================================
//                              AUTOMATIC ACTIONS
//=============================================================================

// the only printing you need, requires onperk xbow / pipes.
exec function print(int j)
{
  local int i;
  local KFHumanPawn p;

  p = getPawn();
  j = clamp(j, 1, 150);

  while (i++ < j) // p != none && 
  {
    p.serverSellAmmo(class'CrossbowAmmo');
    p.serverBuyAmmo(class'CrossbowAmmo', true);
    p.serverSellAmmo(class'CrossbowAmmo');
    p.serverBuyAmmo(class'CrossbowAmmo', true);
    p.serverSellAmmo(class'CrossbowAmmo');
    p.serverBuyAmmo(class'CrossbowAmmo', true);
    // and pipes at the same time, maybe we are a demo
    p.serverSellAmmo(class'PipeBombAmmo');
    p.serverBuyAmmo(class'PipeBombAmmo', true);
  }
}


// Macros which makes life easier
// That's pretty good idea use them with alliases
exec function startTossCash()
{
  stopTossCash();

  if (!isPlayerAlive())
    return;

  a_CashTosser = pc.spawn(class'a_CashTosser');
  a_CashTosser.startTossCash(pc);
}


exec function stopTossCash()
{
  if (a_CashTosser != none)
  {
    a_CashTosser.stopTossCash();
    a_CashTosser.Termination();
    a_CashTosser = none;
  }
}


// tosses all weapons from inventory (except goddamn pipes)
exec function startTossWeapons()
{
  stopAnyWeaponTosserActions();

  if (!isPlayerAlive())
    return;

  a_WeaponTosser = pc.spawn(class'a_WeaponTosser');
  a_WeaponTosser.startTossWeapons(pc);
}


exec function stopTossWeapons()
{
  stopAnyWeaponTosserActions();
}


// buys duals and toss it
exec function startPrinting()
{
  stopAnyWeaponTosserActions();

  if (!isPlayerAlive())
    return;

  if (KFPlayerReplicationInfo(pc.playerReplicationInfo).clientVeteranSkill != class'KFMod.KFVetSharpshooter')
    msg(chr(27)$chr(240)$chr(1)$chr(1)$"WARNING!"@chr(27)$chr(255)$chr(255)$chr(255)$"You are not sharpshooter!");

  a_WeaponTosser = pc.spawn(class'a_WeaponTosser');
  a_WeaponTosser.startPrinting(pc);
}


exec function stopPrinting()
{
  stopAnyWeaponTosserActions();
}


//sells all weapons from inventory
exec function startSellWeapons()
{
  stopAnyWeaponTosserActions();

  if (!isPlayerAlive())
    return;

  a_WeaponTosser = pc.spawn(class'a_WeaponTosser');
  a_WeaponTosser.startSellWeapons(pc);
}


exec function stopSellWeapons()
{
  stopAnyWeaponTosserActions();
}


final protected function stopAnyWeaponTosserActions()
{
  if (a_WeaponTosser != none)
  {
    a_WeaponTosser.stopAnyActions();
    a_WeaponTosser.Termination();
    a_WeaponTosser = none;
  }
}


// Find and kills all actors which do some automatic actions
// (i.e. a_WeaponTosser, a_CashTosser)
// ----------------------------------------------
// Not sure if this function is still needed, since I fixed that glitch
// But better leave it there in case of some shit will happen
exec function clearTrash()
{
  local a_CashTosser ct;
  local a_WeaponTosser wt;

  forEach allObjects(class'a_CashTosser', ct)
  {
    ct.stopTossCash();
    ct.Termination();
  }

  forEach allObjects(class'a_WeaponTosser', wt)
  {
    wt.stopAnyActions();
    wt.Termination();
  }
}


//=============================================================================
//                                HUD FUNCTIONS
//=============================================================================

simulated function PostRender(Canvas C)
{
  // on-off switch, crosshair
  if (bCross)
    o_Render.DrawXhair(C, CrosshairColor, XLength, XWidth);

  // on-off switch, health bars
  if (bHPBar)
    o_Render.DrawHealthBars(C, fHPBar);

  DrawHalo();

  // DrawHeadShotSphere(c);
}


// reveal stalkers and patriarchs
exec function Reveal()
{
  class'Settings'.default.bShowStalkers = !class'Settings'.default.bShowStalkers;
  msg("Reveal status is " $ class'Settings'.default.bShowStalkers);
}


// removes all smoke from the game
exec function nosmoke()
{
  class'Settings'.default.bRemoveSmoke = !class'Settings'.default.bRemoveSmoke;
  msg("Remove Smoke bool is " $ class'Settings'.default.bRemoveSmoke);
}


// draw zed healthbars
exec function HPBar(optional float range)
{
  if (range > 0)
  {
    fHPBar = range;
    msg("Zed Health Bar detection range changed to " $ range);
    return;
  }

  bHPBar = !bHPBar;
  msg("Zed Health Bar status is: " $ bHPBar);
}


// crosshair on-off
exec function Xhair()
{
  bCross = !bCross;
  msg("Crosshair status is: " $ bCross);
}


// crosshair on-off
exec function setXhair(optional bool b)
{
  bCross = b;
  saveConfig();
  msg("bCross set to " $ bCross $ " and saved to config!");
}


// huge ass light around player
exec function Halo()
{
  bHalo = !bHalo;
  msg("Halo is "$bHalo);
}


final private function DrawHalo()
{
  if (bHalo)
  {
    if (HaloLight == none)
      HaloLight = getPawn().spawn(class'Halo', getPawn());
    HaloLight.SetLocation(getPawn().Location);
  }
  else
  {
    if (HaloLight != none)
    {
      HaloLight.Destroy();
      HaloLight = none;
    }
  }
}


// change game rendering mode
exec function cameraMode(int i)
{
  if (getPC())
    pc.rendMap = clamp(i, 0, 9);
}


//=============================================================================
//                           SPEECH and CHAT FUNCTIONS
//=============================================================================

exec function Hlp()
{
  _.TellAbout(viewportOwner, "MsgHelp");
}


// 0 delay talking
exec function fastTalk()
{
  local KFConsole console;

  console = KFConsole(viewportOwner.console);

  if (console != none)
  {
    console.typedStr = "Ssay ";
    console.typedStrPos = 5;
    console.typingOpen();
  }
}

// Player.Console.Chat( c$s, 6.0, PRI );
// event TeamMessage( PlayerReplicationInfo PRI, coerce string S, name Type  )
exec function ssay(string msg)
{
  getController().serverSay(msg);
}


exec function warnsc()
{
  ssay(_.getRandString(1));
  ViewportOwner.Actor.Speech('AUTO', 14, "");
}


exec function warnfp()
{
  ssay(_.getRandString(2));
  ViewportOwner.Actor.Speech('AUTO', 12, "");
}


//disables/enables TTS
exec function tts()
{
  if (getPC())
  {
    pc.bNoTextToSpeechVoiceMessages = !pc.bNoTextToSpeechVoiceMessages;
    msg("TTS status is: " $ pc.bNoTextToSpeechVoiceMessages);
  }
}


exec function spam(string s)
{
  class'ChatAnimator'.static.textspam(getController(),s);
}


exec function fspam(int i)
{
  class'ChatAnimator'.default.spamLoops = i;
}


// show pat health
exec function PatHP(optional bool bPublic)
{
  local ZombieBoss pat;
  local string message;
  local int i;

  if (!getPC())
    return;

  forEach pc.allActors(class'ZombieBoss', pat)
  {
    if (pat == none || pat.health <= 0)
      continue;

    i++;

    message = _.ParseTags("^w^" $ i $ ". Pat's health is ^b^" $ pat.health $ " ^w^/ ^b^" $ pat.HealthMax $ "^w^. Syringes used - ^b^" $ pat.SyringeCount);
    if (!bPublic)
      msg(message);
    else
      ssay(message);
  }
}


// show zed health with given HP threshold
exec function ZedHP(float fTarget)
{
  local KFMonster kfm;
  local string message;

  if (!getPC())
    return;

  forEach pc.allActors(class'KFMonster', kfm)
  {
    if (kfm == none || kfm.health <= 0 || kfm.HealthMax < fTarget)
      continue;

    message = _.ParseTags("^b^" $ kfm.MenuName $ "'s health is ^b^" $ kfm.health $ " ^w^/ ^b^" $ kfm.HealthMax);
    msg(message);
  }
}


// shows spawned zeds count
exec function mz(optional bool bPublic)
{
  local KFMonster kfm;
  local string message;
  local int i;

  if (!getPC())
    return;

  forEach pc.allActors(class'KFMonster', kfm)
  {
    if (kfm == none || kfm.health <= 0)
      continue;

    i++;
  }

  message = _.ParseTags("^w^ Spawned zeds count - ^b^" $ i $ "^w^.");
  if (!bPublic)
    msg(message);
  else
    ssay(message);
}


//=============================================================================
//                           RANDOM EXEC FUNCTIONS
//=============================================================================

// Changes pawn skin
exec function cc(string newCharacter)
{
  write_Console("ChangeCharacter " $ newCharacter);
}


// Does the team kill.
// Shot -> waiting for 'timerTime' -> spectate -> back in game
exec function teamKill(float timerTime)
{
  local a_SpectateManager sm;

  sm = getController().spawn(class'a_SpectateManager');
  sm.friendlyShoot(getController(), timerTime);
}


// TWI decided to detect Marco's mutator every time and disable the built-in system
// but who will enable it back? Ofc you... manually...
exec function showkills()
{
  local HUDKillingFloor KFH;

  KFH = HUDKillingFloor(getController().myHUD);
  KFH.bTallySpecimenKills = !KFH.bTallySpecimenKills;
  // KFH.SetPropertyText("bTallySpecimenKills", "true");
}


// short variant of ^
exec function sk()
{
  showkills();
}


// =============================================================================
//                              EXPEREMENTAL
// =============================================================================

// Debug PostFX effects
exec function DebugBWEffect(float NewAmount)
{
  if ( NewAmount > 0 )
  {
    postfxon(2);
    postfxbw(NewAmount);
  }
  else
  {
    postfxoff(2);
  }
}

simulated function postfxbw(float f, optional bool bDoNotTurnOffFadeFromBlackEffect)  //0... 1
{
  if ( !getController().PostFX_IsReady() )
    return;

  //if (!bDoNotTurnOffFadeFromBlackEffect)
  //  bDoFadeFromBlackEffect = false;

  getController().PostFX_SetParameter(2, 0, f);
}

simulated function postfxon(int i)
{
  if ( getController().PostFX_IsReady() )
    getController().PostFX_SetActive(i, true);
}

simulated function postfxoff(int i)
{
  if ( getController().PostFX_IsReady() )
    getController().PostFX_SetActive(i, false);
}

exec function SS(float sens)
{
  getController().SetSensitivity(sens);
  msg("Mouse sensitivity set to" @ sens);
}

// Change Alias
exec function SA(string Alias)
{
  bCP = true;
  if (Alias != "")
  {
    Switch(Alias)
    {
      Case "0":
        NewChar("Mr_Foster");
        NewName("Joabyy");
        break;
      Case "1":
        NewChar("Mr_Foster");
        NewName("Joabyy");
        break;
      Case "1a":
        NewChar("Steampunk_Mrs_Foster");
        NewName("Joabyy","(Steampunk Mrs Foster)");
        break;
      Case "1b":
        NewChar("Steampunk_Mrs_Foster");
        NewName("Aya");
        break;
      Case "1c":
        NewChar("Steampunk_Mrs_Foster");
        NewName("Aya Kawasaki");
        break;
      Case "1d":
        NewChar("Steampunk_Mrs_Foster");
        NewName("Yui Hatano");
        break;
      Case "1e":
        NewChar("Steampunk_Mrs_Foster");
        NewName("Yua Sakuya");
        break;
      Case "2":
        NewChar("Steampunk_Mrs_Foster");
        NewName("fuzzy");
        break;
      Case "3":
        NewChar("Pyro_Blue");
        NewName("devante");
        break;
      Case "4":
        NewChar("Ash_harding");
        NewName("Galaxies.");
       break;
      Case "4a":
        NewChar("Ash_harding");
        NewName("nessiebugs");
        break;
      Case "5":
        NewChar("Security_Officer_Thorne");
        NewName("dev1l");
        break;
      Case "6":
        NewChar("Shadow_Ferret");
        NewName("LuKaS");
        break;
      Case "7":
        NewChar("Reaper");
        NewName("(MI) Markv39");
        break;
      Case "8":
        NewChar("Commando_Chicken");
        NewName("iNK");
        break;
      Case "9":
        NewChar("Mr_Foster");
        NewName("Static.");
        break;
    }
  }
}

//Change Character
exec function SC(string Char)
{
  bCP = false;
  if (Char != "")
  {
    Switch(Char)
    {
      Case "1": NewChar("Mr_Foster");
        break;
      Case "2": NewChar("Steampunk_Mrs_Foster");
        break;
      Case "3": NewChar("Dr_Gary_Glover");
        break;
      Case "4": NewChar("Ash_harding");
        break;
      Case "5": NewChar("Security_Officer_Thorne");
        break;
      Case "6": NewChar("Mike_Noble");
        break;
      Case "7": NewChar("Shadow_Ferret");
        break;
      Case "8": NewChar("Pyro_Blue");
        break;
      Case "9": NewChar("Pyro_Red");
        break;
      Case Char: NewChar(Char);
        break;
    }
  }
}

exec function NewChar(string Char)
{
  if (!getPC())
    return;

  pc.setPawnClass(string(pc.pawnClass), Char);
  pc.updateURL("Character", Char, true);
  pc.saveConfig();
  if (bcp)
    return;
  msg("Character changed to" @ Char);
}

exec function SN(coerce string s)
{
  if (!getPC())
    return;

  // replaces spaces with no-break spaces to avoid changing it to underscores
  if (s ~= "null")
    s = chr(10);
  else
    replaceText(s, chr(32), chr(160));
  pc.changeName(s);
  msg("Name changed to " $ s);
}

function NewName(optional string Name, optional string Char)
{
  replaceText(Name, chr(32), chr(160));
  getController().changeName(Name);
  if (bCP)
    msg("Alias changed to" @ Name @ Char);
  else
    msg("Name changed to" @ Name);
}

exec function sSpec()
{
  getController().ServerSpectate();
}

exec function Spec()
{
  getController().BecomeSpectator();
}

exec function Join()
{
  getController().BecomeActivePlayer();
}


exec function Q1()
{
  write_Console("set input Q switchtolastweapon | quickheal");
  msg("Quick heal bound to Q");
}

exec function Q2()
{
  write_Console("set input Q ReallyQuickHeal");
  msg("ReallyQuickHeal bound to Q");
}

exec function G1()
{
  write_Console("Set input G poof");
  msg("Poof bound to G");
}

exec function G2()
{
  write_Console("Set input G Admin summon kfmod.pipebombprojectile");
  msg("Pipe spam bound to G");
}

exec function G3()
{
  write_Console("set input G StartTossCash | OnRelease StopTossCash");
  msg("Dosh spam bound to G");
}

exec function G4()
{
  write_Console("set input G admin summon KFMod.KFHorzineSoldier");
  msg("Bot spam bound to G");
}

exec function G5()
{
  write_Console("set input G admin summon KFMod.M99Bullet");
  msg("M99 Bullet spam bound to G");
}

exec function G6()
{
  write_Console("set input G admin summon KFMod.ZEDMKIISecondaryProjectile");
  msg("Zed gun II Alt fire bound to G");
}

//restart wave 10
exec function RES()
{
  write_Console("AdLog;admin pw;admin wave 10;admin restore;admin skip");
}

//lock server
exec function LS()
{
  write_Console("mutate slot 0;mutate spec 0");
}

exec function LS1()
{
  write_Console("mutate slot 0;mutate spec 0");
  NewName("Aya");
}

//unlock server
exec function US()
{
  write_Console("mutate slot 6;mutate spec 32");
}

exec function f1()
{
  write_Console("mutate faked 6;mutate hp 6");
}

exec function f2()
{
  write_Console("mutate faked 4;mutate hp 6");
}

exec function f44()
{
  local bool B;

  if (!getPC())
    return;

  B = !pc.bBehindView;

  pc.ClientSetBehindView(B);
  pc.bBehindView = B;
  // PlayerController(c).ClientSetBehindView(false);
  // PlayerController(c).bBehindView = false;
  if (!pc.bBehindView)
  {
    pc.SetViewTarget(pc.pawn);
    pc.ClientSetViewTarget(pc.pawn);
    // KFHumanPawn(KFPlayerController(viewportOwner.actor).pawn);
    // PlayerController(c).SetViewTarget(c.Pawn);
    // PlayerController(c).ClientSetViewTarget(c.Pawn);
  }

  // PlayerController(c).SetViewTarget(ViewingBoss);
  // PlayerController(c).ClientSetViewTarget(ViewingBoss);
  // PlayerController(c).bBehindView = true;
  // PlayerController(c).ClientSetBehindView(true);
  // PlayerController(c).ClientSetMusic(BossBattleSong,MTRAN_FastFade);
}


exec function camdist(float dist)
{
  if (isPlayerAlive())
    pc.default.CameraDist = dist;
}


//=============================================================================
//                                  PERKS
//=============================================================================
// Following functions allow player to change his perk
// Infinite number of times (during trader) and pick any level

exec function bMed(optional string s)
{
  _.PerkSwitch(getController(), 0, s);
}

exec function bSup(optional string s)
{
  _.PerkSwitch(getController(), 1, s);
}

exec function bSharp(optional string s)
{
  _.PerkSwitch(getController(), 2, s);
}

exec function bMando(optional string s)
{
  _.PerkSwitch(getController(), 3, s);
}

exec function bZerk(optional string s)
{
  _.PerkSwitch(getController(), 4, s);
}

exec function bPyro(optional string s)
{
  _.PerkSwitch(getController(), 5, s);
}

exec function bDemo(optional string s)
{
  _.PerkSwitch(getController(), 6, s);
}

exec function bNone()
{
  _.PerkSwitch(getController(), 7);
}


// This function is needed on custom gametypes like ScrN
exec function vet(string vetName)
{
  local class<KFVeterancyTypes> changeToVeterancy;

  changeToVeterancy = class<KFVeterancyTypes>(dynamicLoadObject(vetName, class'Class', true));
  getController().selectVeterancy(changeToVeterancy, true);
}


// disables perk progression
exec function greyList()
{
  if (getPC())
    pc.bwEffect(0);
}


// =============================================================================
//                                  DEBUG
// =============================================================================

// Shoots projectile which returns the name of object projectile bumps into
exec function getActorName()
{
  if (!getPC())
    return;

  if (pc.pawn != none)
    pc.spawn(class'proj_GetName', pc, , pc.pawn.location + pc.pawn.eyeHeight * vect(0, 0, 1), pc.rotation);
  else
    pc.spawn(class'proj_GetName', pc, , pc.location, pc.rotation);
}


// pick an actor
exec function pickActor()
{
  checkVisibilityHandler();
  a_visibilityHandler.pickActor();
}


// player controller and repinfo details
exec function getC()
{
  local KFPlayerController kfpc;
  local KFPlayerReplicationInfo kfri;
  local string names;
  local int i;

  forEach allObjects(class'KFPlayerReplicationInfo', kfri)
  {
    if (kfri == none || kfri.PlayerID == 0)
      continue;
    names @= kfri.PlayerName;
    i++;
  }
  msg("KFPlayerReplicationInfo count is " $ i $ ", names are" $ names);

  i = 0;
  foreach allObjects(class'KFPlayerController', kfpc)
  {
    if (kfpc == none || kfpc.PlayerReplicationInfo.PlayerID == 0)
      continue;
    i++;
  }
  msg("KFPlayerController count is " $ i);
}


// current gametype name
exec function getGt()
{
  _.GetGT(viewportOwner);
}


// current URL
exec function getURL(optional bool b)
{
  if (!getPC())
    return;

  msg("pc: " $ pc.GetURLMap(b));
  msg("level: " $ pc.level.GetURLMap(b));
  msg("getlocurl: " $ pc.level.GetLocalURL());
}


// current map name
exec function getMap()
{
  local string mapName;
  // local int i;

  mapName = class'KFGameType'.static.getCurrentMapName(getController().level);
  msg(mapName);
  // getController().clientMessage(""$a_visibilityHandler.getCurrentMapName());
}


// print IP info
exec function getIP()
{
  if (getPC())
    msg(pc.getServerNetworkAddress());
}


// location info
exec function getLoc()
{
  local vector loc;

  if (getPawn() != none)
    loc = getPawn().location;
  else if (getController() != none)
    loc = getController().location;

  msg("Location is: " $ loc);
}


// print all vanilla characters
exec function getChar()
{
  _.TellAbout(viewportOwner, "Chars");
}


// crash your game, might be helpful xD
exec function crashS()
{
  KFPlayerReplicationInfo(getController().PlayerReplicationInfo).static.crash();
}


// show path nodes and paths
// source #1 seems like an outdated one: <https://wiki.beyondunreal.com/Legacy:Bot_Pathing>
// source #2: <https://docs.unrealengine.com/udk/Three/UsingWaypoints.html>
exec function sp(string s)
{
  local bool b;

  if (s ~= "help")
  {
    print_Console_arr(_.SPHelp);
    return;
  }
  if (s ~= "1")
    b = true;

  checkVisibilityHandler();
  a_visibilityHandler.showPathNodes(b);
  a_visibilityHandler.ShowPaths(b);
  Msg("Forcing path visibility to: " $ b);
}


// reconnect to server
exec function rc()
{
  write_Console("Reconnect");
}


// disconnect from server
exec function dc()
{
  write_Console("Disconnect");
  // if we don't do this, we can get stuck at lobby sometimes
  // N.B. this part is a bit laggy!
  KFGUIController(viewportOwner.guiController).ReturnToMainMenu();
}


//=============================================================================
//                              TESTING AREA !!!
//=============================================================================

// exec function dbgop()
// {
//   local array<string> array_s;
//   local string s;

//   msg(HideObjectPool.PrintObjList());

//   s = HideObjectPool.PrintObjList();
//   log(">>> dbgop " $ s);

//   split(s, ";", array_s);

//   print_Console_arr(array_s);
// }


// replace functions anytime, anywhere
exec function rf()
{
  class'HideMut'.default.ref_HideMut.replace_Functions();
  msg("We replaced functions again!");
}


exec function setXY(int x, int y)
{
  Msg("X is " $ x $ " and Y is " $ y);
}


// lock server
// костыль но сорян
exec function test()
{
  write_Console("adlog;admin ready;admin god;admin loaded;admin dosh all 500000;admin trader 6000 1;admin zeds off");
}


exec function ssc()
{
  write_Console("admin summon SuperZombieMut.ZombieSuperScrake");
}


exec function mtp()
{
  write_Console("mutate dosh 5000;mutate faked 4;mutate wave 9;mutate safeguard on;mutate safeguard reaction restart;mutate safeguard autoammo on;mutate safeguard automags on");
}


exec function la(string s)
{
  class'o_AssetLoader'.static.LoadZedAsset(s, viewportOwner.actor.level);
}
// exec final function function setXYZ(int x, int y){}


exec function remshitanim(string s)
{
  class'Settings'.default.bRemoveShitAnimations = !class'Settings'.default.bRemoveShitAnimations;
  msg("Zap / burning animation removal status is " $ class'Settings'.default.bRemoveShitAnimations);
}


//=============================================================================

defaultproperties
{
     _=o_Utility'HideMut.HideMutInteraction.o_Utility_Instance'
     o_Render=o_Render'HideMut.HideMutInteraction.o_Render_Instance'
     bActive=False
     bVisible=True
}
