// custom HUD supporting KFO
class HideHUD extends HUD_StoryMode;


#exec obj load file="KFMapEndTextures.utx"
#exec obj load file="2K4Menus.utx"

#exec TEXTURE IMPORT FILE="Textures\AllyHealthBackdrop_D.tga" NAME="AllyHealthBackdrop_D" GROUP="Extra" MIPS=0 MASKED=1
#exec TEXTURE IMPORT FILE="Textures\AllyHealthBar_D.tga" NAME="AllyHealthBar_D" GROUP="Extra" MIPS=0 MASKED=1

// =============================================================================
// defined in HUD
// var() PlayerController PlayerOwner;
// var() Pawn PawnOwner;
// var() PlayerReplicationInfo PawnOwnerPRI;
// var() Console PlayerConsole;

// defined in HUDKillingFloor
// var     KFPlayerReplicationInfo KFPRI;
// var     KFGameReplicationInfo   KFGRI;

// message struct
struct MsgStruct
{
  var string Msg;
  var int count;
  var float time;
};
var transient array<MsgStruct> MsgArray;


var transient float OldDilation,CurrentBW,DesiredBW,NextLevelTimer,LevelProgressBar,VisualProgressBar;
var bool bUseBloom,bUseMotionBlur,bDisplayingProgress;
var transient bool bFadeBW;


// Miscellaneous arrays.
// Used for kill icons.
struct KillIconSetting
{
  var string ZedName;
  var int ZedPriority;
  var string IconTexture;
};
var KillIconSetting KillIconList[128];

struct ZedCounter
{
  var string ZedName;
  var int ZedPriority;
  var int NumKills;
  var Material ZedIcon;
};
// Used to store kill data.
var array<ZedCounter> ZedsKilled;


struct ScoreSprite
{
  var string ScoreText; // Drawn text
  var float X, Y; // 2D location
  var float SpeedX, SpeedY; // 2D speed
  var float UpTime; // How long this sprite has been around for.
  var float FadeTime; // How long this sprite will last.
};
var array<ScoreSprite> ScoreList; // Holds a list of current score popups.


var string HealthBarPerkList[16];
const MAXPLAYERS=16;
var Font TinyFont;

// Materials for the health bars.
var Material AllyMaterial, AllyMaterialBackdrop;
// Used to create the effect when an ally has low health.
var float BlinkCounter;
var float BlinkCounterDir; 


simulated function DrawHudPassA(Canvas C)
{
  super.DrawHudPassA(C);

  DrawAllyData(C);
}


final private function string GetHealthBarPerk(int Index)
{
  return HealthBarPerkList[Index];
}


final private function bool InOrder(PlayerReplicationInfo P1, PlayerReplicationInfo P2)
{
  if (P1.bOnlySpectator )
  {
    if (P2.bOnlySpectator)
      return true;
    else
      return false;
  }
  else if (P2.bOnlySpectator)
    return true;

  if (P1.Score < P2.Score)
    return false;

  if (P1.Score == P2.Score)
  {
    if (P1.Deaths > P2.Deaths)
      return false;
    if ( (P1.Deaths == P2.Deaths) && (PlayerController(P2.Owner) != None) && (Viewport(PlayerController(P2.Owner).Player) != None) )
      return false;
  }

  return true;
}


final private function SortPRIArray()
{
  local int i,j;
  local PlayerReplicationInfo tmp;

  for (i = 0; i < KFGRI.PRIArray.Length-1; i++)
  {
    for (j = i + 1; j < KFGRI.PRIArray.Length; j++)
    {
      if (!InOrder(KFGRI.PRIArray[i], KFGRI.PRIArray[j]))
      {
        tmp = KFGRI.PRIArray[i];
        KFGRI.PRIArray[i] = KFGRI.PRIArray[j];
        KFGRI.PRIArray[j] = tmp;
      }
    }
  }
}


// KFGRI - Game Replication Info
// KFPRI - Player Replication Info
final private function function DrawAllyData(Canvas C)
{
  local KFPlayerReplicationInfo PRI;
  local array<KFPlayerReplicationInfo> TeamPRIArray;

  local float AllyBarHeight, AllyBarLength;
  local float AllyBarScaleX, AllyBarScaleY;
  local float TempX, TempY;

  local int i, PlayerCount;
  local int BlinkColour;
  local int BackdropBlinkColour;
  local float BlinkRate;

  local string PlayerName;
  local float YL, XL;

  // If we're missing either of these or are showing the scoreboard, leave the function.
  if (KFGRI == None || KFPRI == None || bShowScoreBoard || GetHealthBarPerk(0) == "None")
    return;

  // If the list is empty, all perks will display this.
  if(GetHealthBarPerk(0) != "")
  {
    for(i = 0; i < 16; i++) 
    {
      // Reached an end point without breaking out of the for loop, leave the function.
      if(GetHealthBarPerk(i) == "")
        return;

      // The class matches 
      if(KFPRI.ClientVeteranSkill == class<KFVeterancyTypes>(DynamicLoadObject(GetHealthBarPerk(i), Class'Class')))
        break;

      // We looped through all of the possible perks, leave the function.
      if(i == 15)
        return;
    }    
  }

  for(i = 0; i < KFGRI.PRIArray.Length; i++)
  {
    PRI = KFPlayerReplicationInfo(KFGRI.PRIArray[i]);

    if(!PRI.bOnlySpectator)
    {
      // Do not include this player - or any dead players.
      if(PRI == KFPRI || PRI.PlayerHealth <= 0)
        continue;

      PlayerCount++;
      TeamPRIArray[TeamPRIArray.Length] = PRI;
    }
  }

  if(PlayerCount == 0)
    return;

  SortPRIArray();

  // Leave in this max players clamp.
  PlayerCount = Min(PlayerCount, MAXPLAYERS);

  // All bars together should be equal to 1/4 of the height.
  AllyBarHeight = (C.SizeY * 0.15) / 8.f;
  AllyBarLength = AllyBarHeight * 10.f;

  // Get scales.
  AllyBarScaleX = AllyBarLength/AllyMaterial.MaterialUSize();
  AllyBarScaleY = AllyBarHeight/AllyMaterial.MaterialVSize();

  TempY = C.SizeY * 0.5 - (AllyBarHeight * PlayerCount * 0.5f);
  TempX = -(C.SizeX * 0.005);

  if(TinyFont == None)
    TinyFont = Font(DynamicLoadObject("Engine.DefaultFont", Class'Font'));
  C.Font = TinyFont;

  for(i = 0; i < PlayerCount ; i++)
  {
    if(TeamPRIArray[i] == None)
      return;

    if(TeamPRIArray[i].PlayerHealth > 50)
      BlinkColour = 255;
    else
    {
      //  50: 255
      //  0: 0
      BlinkColour = int((float(Clamp(TeamPRIArray[i].PlayerHealth, 0, 50)) / 50.f) * 255.f);

      if(TeamPRIArray[i].PlayerHealth < 25)
      {
        BlinkRate = BlinkCounter * 2.f;

        if(BlinkRate > 1.f)
          BlinkRate -= 1.f;
      }
      else
        BlinkRate = BlinkCounter;

      BlinkColour = int(FClamp(float(BlinkColour) + (255.f - float(BlinkColour)) * BlinkRate,0.f,255.f));
    }

    BackdropBlinkColour = int(FClamp((float(BlinkColour)/255.f) * 64.f,0.f,255.f));

    C.SetDrawColor(64, BackdropBlinkColour, BackdropBlinkColour, (KFHUDAlpha*0.85f));

    C.SetPos(TempX, TempY);
    C.DrawTileScaled(AllyMaterialBackdrop,AllyBarScaleX,AllyBarScaleY);

    C.SetDrawColor(255, BlinkColour, BlinkColour, (KFHUDAlpha*0.5f));

    C.SetPos(TempX - (AllyBarLength * 0.95f * float(100 - Clamp(TeamPRIArray[i].PlayerHealth, 0, 100)) / 100.f), TempY);
    C.DrawTileScaled(AllyMaterial,AllyBarScaleX,AllyBarScaleY);

    C.SetDrawColor(0, 0, 0, KFHUDAlpha);

    PlayerName = TeamPRIArray[i].PlayerName;

    if(Len(PlayerName) > 16)
      PlayerName = Left(PlayerName,16);

    C.Strlen(PlayerName, XL, YL);

    C.SetPos(TempX + AllyBarLength * 0.95f - XL, TempY + AllyBarHeight * 0.5f - YL * 0.5f);
    C.DrawText(PlayerName);

    TempY -= AllyBarHeight;
  }
}


// =============================================================================
//                                Zed Kill Counter
// =============================================================================

// SkeletonHUD
function UpdateKillMessage(Object OptionalObject, PlayerReplicationInfo RelatedPRI_1)
{
  local class<Monster> MClass;

  // If the RelatedPRI doesn't exist, then it's the current player.
  // If the RelatedPRI does exist, make sure it's equal to the player.
  if (OptionalObject != none && (RelatedPRI_1 == none || KFPlayerReplicationInfo(RelatedPRI_1) == KFPRI))
  {
    MClass = class<Monster>(OptionalObject);

    ProcessNewKill(MClass.default.MenuName);
    // ProcessNewScore(MClass.Default.ScoringValue);
  }

  super(HUDKillingFloor).UpdateKillMessage(OptionalObject, RelatedPRI_1);
}


simulated function ProcessNewKill(String EnemyName)
{
  local ZedCounter NewZedEntry;
  local int i, NewIndex; // j

  for(i = 0; i < ZedsKilled.Length; i++)
  {
    if(ZedsKilled[i].ZedName == EnemyName)
    {
      ZedsKilled[i].NumKills++;
      SortZedArray();
      return;
    }

    // If this new zed is an event version, handle this accordingly.
    // else if(bCompactZedNames &&
    //     (InStr(EnemyName, ZedsKilled[i].ZedName) > -1 || InStr(ZedsKilled[i].ZedName, EnemyName) > -1))
    // {
    //   // Set the zed name to the smaller of the two.
    //   if(Len(EnemyName) < Len(ZedsKilled[i].ZedName))
    //   {
    //     for(j = i + 1; j < ZedsKilled.Length; j++)
    //     {
    //       if(InStr(ZedsKilled[j].ZedName, EnemyName) > - 1)
    //       {
    //         ZedsKilled[i].NumKills += ZedsKilled[j].NumKills;
    //         ZedsKilled.Remove(j,1);
    //       }
    //     }

    //     ZedsKilled[i].ZedName = EnemyName;
    //     ZedsKilled[i].ZedIcon = Texture(DynamicLoadObject(GetZedTexture(EnemyName), Class'Texture', true));    
    //   }

    //   ZedsKilled[i].NumKills++;
    //   SortZedArray();
    //   return;      
    // }
  }

  NewZedEntry.ZedName = EnemyName;
  NewZedEntry.NumKills = 1;
  NewZedEntry.ZedPriority = GetZedPriority(EnemyName);

  NewIndex = ZedsKilled.Length;
  ZedsKilled[NewIndex] = NewZedEntry;

  // if(GetZedTexture(EnemyName) == "")
  //   ZedsKilled[NewIndex].ZedIcon = PerkStarIcons[0];
  // else
    ZedsKilled[NewIndex].ZedIcon = Texture(DynamicLoadObject(GetZedTexture(EnemyName), Class'Texture', true));

  SortZedArray();
}


simulated function int GetZedPriority(string MonsterName)
{
  local int i;

  for(i = 0; i < 128; i++)
  {
    if(KillIconList[i].ZedName == "")
      return 0;

    if(MonsterName == KillIconList[i].ZedName)
      return KillIconList[i].ZedPriority;

    // if(bCompactZedNames
    //   && InStr(MonsterName, KillIconList[i].ZedName) > -1
    //   && Len(MonsterName) > Len(KillIconList[i].ZedName))
    // {
    //     return KillIconList[i].ZedPriority;
    // }
  }

  return 0;  
}


// Pulls the zed texture reference at a specific index from the replicated array.
simulated function string GetZedTexture(string MonsterName)
{
  local int i;

  for(i = 0; i < 128; i++)
  {
    if(KillIconList[i].ZedName == "")
      break;

    if(MonsterName == KillIconList[i].ZedName)
      return KillIconList[i].IconTexture;

      
    // if(bCompactZedNames
    // && InStr(MonsterName, KillIconList[i].ZedName) > -1
    // && Len(MonsterName) > Len(KillIconList[i].ZedName))
    // {
    //     return KillIconList[i].IconTexture;
    // }
  }

  return "";
}


// Sorts Zeds by weight and then name.
simulated function SortZedArray()
{
  local int i,j;
  local ZedCounter SwitchZed;

  for (i = 0; i < ZedsKilled.Length - 1; i++)
  {
    for (j = i + 1; j < ZedsKilled.Length; j++)
    {
      if (ZedsKilled[i].ZedPriority > ZedsKilled[j].ZedPriority)
      {
        SwitchZed = ZedsKilled[i];
        ZedsKilled[i] = ZedsKilled[j];
        ZedsKilled[j] = SwitchZed;
      }

      else if (ZedsKilled[i].ZedPriority == ZedsKilled[j].ZedPriority)
      {
        if (Caps(ZedsKilled[i].ZedName) < Caps(ZedsKilled[j].ZedName))
        {
          SwitchZed = ZedsKilled[i];
          ZedsKilled[i] = ZedsKilled[j];
          ZedsKilled[j] = SwitchZed;      
        }
      }
    }
  }
}


// =============================================================================
//                                INVENTORY
// =============================================================================

// ServerPerks.SRHUDKillingFloor
simulated function PostBeginPlay()
{
  local Font MyFont;

  Super(HudBase).PostBeginPlay();
  SetHUDAlpha();

  foreach DynamicActors(class'KFSPLevelInfo', KFLevelRule)
    break;

  Hint_45_Time = 9999999;

  MyFont = LoadWaitingFont(0);
  MyFont = LoadWaitingFont(1);

  // NOTE!!! SHUT ME LATER!!!
  bUseBloom = bool(ConsoleCommand("get ini:Engine.Engine.ViewportManager Bloom"));
  bUseMotionBlur = class'KFHumanPawn'.Default.bUseBlurEffect;

  CacheDialogueActors();
  CacheObjectives();
}


// Only do this if objective mode is enabled.
// ServerPerks.SRHUDKillingFloor
simulated function DrawStoryHUDInfo(Canvas C)
{
  if (KF_StoryGRI(Level.GRI) != none)
    super.DrawStoryHUDInfo(C);
}


// Only do this if objective mode is enabled.
// ServerPerks.SRHUDKillingFloor
simulated function DrawKFHUDTextElements(Canvas Canvas)
{
  if (KF_StoryGRI(Level.GRI) == none)
    super(HudKillingFloor).DrawKFHUDTextElements(Canvas);
}


// ServerPerks.SRHUDKillingFloor
simulated function DrawEndGameHUD(Canvas C, bool bVictory)
{
  local float Scalar;
  local Shader M;

  if (SGRI != none)
  {
    super.DrawEndGameHUD(C, bVictory);
    return;
  }
  C.DrawColor.A = 255;
  C.DrawColor.R = 255;
  C.DrawColor.G = 255;
  C.DrawColor.B = 255;
  Scalar = FClamp(C.ClipY, 320, 1024);
  C.CurX = C.ClipX / 2 - Scalar / 2;
  C.CurY = C.ClipY / 2 - Scalar / 2;
  C.Style = ERenderStyle.STY_Alpha;

  if (bVictory)
    M = Shader'KFMapEndTextures.VictoryShader';
  else
    M = Shader'KFMapEndTextures.DefeatShader';

  C.DrawTile(M, Scalar, Scalar, 0, 0, 1024, 1024);

  if (bShowScoreBoard && ScoreBoard != none)
    ScoreBoard.DrawScoreboard(C);
}


// fix accessed none spam, add scrolling through entire list
// ServerPerks.SRHUDKillingFloor
function DrawInventory(Canvas C)
{
  local Inventory CurInv;
  local int i, Categorized[5], Num[5], X, Y, TempX, TempY, TempWidth, TempHeight, TempBorder, StartY;

  if (PawnOwner == none)
  {
    return;
  }

  if (bInventoryFadingIn)
  {
    if (Level.TimeSeconds < InventoryFadeStartTime + InventoryFadeTime)
    {
      C.SetDrawColor(255, 255, 255, byte(((Level.TimeSeconds - InventoryFadeStartTime) / InventoryFadeTime) * 255.0));
    }
    else
    {
      bInventoryFadingIn = false;
      C.SetDrawColor(255, 255, 255, 255);
    }
  }
  else if (bInventoryFadingOut)
  {
    if (Level.TimeSeconds < InventoryFadeStartTime + InventoryFadeTime)
    {
      C.SetDrawColor(255, 255, 255, byte((1.0 - ((Level.TimeSeconds - InventoryFadeStartTime) / InventoryFadeTime)) * 255.0));
    }
    else
    {
      bInventoryFadingOut = false;
      return;
    }
  }
  else
  {
    C.SetDrawColor(255, 255, 255, 255);
  }

  TempX = InventoryX * C.ClipX;
  TempY = InventoryY * C.ClipY;
  TempWidth = InventoryBoxWidth * C.ClipX;
  TempHeight = InventoryBoxHeight * C.ClipX;
  TempBorder = BorderSize * C.ClipX;
  C.Font = GetFontSizeIndex(C, -3);
  SelectedInventoryCategory = -1;

  // First count the weapons
  for (CurInv = PawnOwner.Inventory; CurInv != none; CurInv = CurInv.Inventory)
  {
    // Don't allow non-categorized or Grenades
    if (CurInv.InventoryGroup > 0 && CurInv.InventoryGroup <= ArrayCount(Categorized) && KFWeapon(CurInv) != None)
    {
      if (CurInv == SelectedInventory) // Make sure index is in sync (could have desynced)
      {
        SelectedInventoryCategory = CurInv.InventoryGroup-1;
        SelectedInventoryIndex = Categorized[SelectedInventoryCategory];
      }
      ++Categorized[CurInv.InventoryGroup-1];
    }
  }
  
  // Check if current selected weapon goes off screen.
  if (SelectedInventoryCategory!=-1 && (TempY+Categorized[SelectedInventoryCategory]*TempHeight)>=C.ClipY )
  {
    // Adjust offset based on current selected weapon.
    Y = SelectedInventoryIndex*TempHeight;
    X = (C.ClipY-TempY)/2;
    if( Y>X )
      StartY = X-Y;
  }
  
  // Now draw weapons.
  for ( CurInv = PawnOwner.Inventory; CurInv != none; CurInv = CurInv.Inventory )
  {
    // Don't allow non-categorized or Grenades
    if ( CurInv.InventoryGroup>0 && CurInv.InventoryGroup<=ArrayCount(Categorized) && KFWeapon(CurInv)!=None )
    {
      i = CurInv.InventoryGroup - 1;
      X = TempX+(TempWidth*i);
      Y = TempY+(Num[i]*TempHeight);
      if( i==SelectedInventoryCategory )
        Y+=StartY;

      // Draw this item's Background
      C.SetPos(X, Y);
      if ( CurInv==SelectedInventory )
        C.DrawTileStretched(SelectedInventoryBackgroundTexture, TempWidth, TempHeight);
      else C.DrawTileStretched(InventoryBackgroundTexture, TempWidth, TempHeight);

      // Draw the Weapon's Icon over the Background
      C.SetPos(X + TempBorder, Y + TempBorder);
      DrawSelectionIcon(C,CurInv==SelectedInventory,KFWeapon(CurInv),TempWidth - (2.0 * TempBorder),TempHeight - (2.0 * TempBorder));
      ++Num[i];
    }
  }

  // Draw empty categories boxes.
  for ( i=0; i<ArrayCount(Categorized); i++ )
  {
    if ( Categorized[i]==0 )
    {
      C.SetPos(TempX+(TempWidth*i), TempY);
      C.DrawTileStretched(InventoryBackgroundTexture, TempWidth, TempHeight * 0.25);
    }
  }
}


// weapon name in icon
// ServerPerks.SRHUDKillingFloor
final function DrawSelectionIcon(Canvas C, bool bSelected, KFWeapon I, float Width, float Height)
{
  local float RX,RY;
  local Material M;

  if (bSelected)
    M = I.SelectedHudImage;
  else
    M = I.HudImage;

  if (M != None)
  {
    RX = C.CurX;
    RY = C.CurY;
    C.DrawTile(M, Width, Height, 0, 0, 256, 192);
    if( !bSelected )
      return;
    C.CurX = RX;
    C.CurY = RY;
  }

  if( !bSelected )
  {
    C.DrawColor.G = 128;
    C.DrawColor.B = 128;
  }
  RX = C.ClipX;
  RY = C.ClipY;
  C.OrgX = C.CurX;
  C.CurX = 0;
  C.ClipX = Width;
  C.ClipY = C.CurY+Height;
  C.DrawText(I.ItemName,false);
  C.OrgX = 0;
  C.ClipX = RX;
  C.ClipY = RY;
  if( !bSelected )
  {
    C.DrawColor.G = 255;
    C.DrawColor.B = 255;
  }
}


// Muuuuch better weapon scrolling
// ServerPerks.SRHUDKillingFloor
function PrevWeapon()
{
  local Inventory CurInv;
  local int Categorized[5], Num;
//  local byte Tries;

  if ( PawnOwner==none || PawnOwner.Inventory==None || !ShowInventory() )
    return;

//  while( ++Tries<3 )
//  {
    SelectedInventoryCategory = -1;
    SelectedInventoryIndex = 0;

    // First pass, gather weapon counts.
    for ( CurInv = PawnOwner.Inventory; CurInv != none; CurInv = CurInv.Inventory )
    {
      // Don't allow non-categorized or Grenades
      if ( CurInv.InventoryGroup>0 && CurInv.InventoryGroup<=ArrayCount(Categorized) && KFWeapon(CurInv)!=None )
      {
        if ( CurInv==SelectedInventory )
        {
          SelectedInventoryCategory = CurInv.InventoryGroup - 1;
          SelectedInventoryIndex = Categorized[SelectedInventoryCategory];
        }
        ++Num;
        ++Categorized[CurInv.InventoryGroup - 1];
      }
    }

    if( Num<=1 )
      return; // Prevent runaway loop.

    // Now check for suitable prev index.
    // Find next available category.
    if( SelectedInventoryIndex==0 )
    {
      while( true )
      {
        if( --SelectedInventoryCategory<0 )
          SelectedInventoryCategory = ArrayCount(Categorized)-1;

        if( Categorized[SelectedInventoryCategory]>0 )
          break;
      }
      SelectedInventoryIndex = Categorized[SelectedInventoryCategory]-1;
    }
    else --SelectedInventoryIndex; // Simply go to previous index.

    // Second pass, find our desired item.
    Num = 0;
    for ( CurInv = PawnOwner.Inventory; CurInv != none; CurInv = CurInv.Inventory )
    {
      if ( CurInv.InventoryGroup==(SelectedInventoryCategory+1) && KFWeapon(CurInv)!=None && (Num++)==SelectedInventoryIndex )
      {
        SelectedInventory = CurInv;
        break;
      }
    }
//    if( PawnOwner.Weapon!=SelectedInventory ) // Make sure not reselecting current weapon.
//      break;
//  }
}


// Muuuuch better weapon scrolling
// ServerPerks.SRHUDKillingFloor
function NextWeapon()
{
  local Inventory CurInv;
  local int Categorized[5], Num;
//  local byte Tries;

  if ( PawnOwner==none || PawnOwner.Inventory==None || !ShowInventory() )
    return;

//  while( ++Tries<3 )
//  {
    SelectedInventoryCategory = -1;
    SelectedInventoryIndex = 0;

    // First pass, gather weapon counts.
    for ( CurInv = PawnOwner.Inventory; CurInv != none; CurInv = CurInv.Inventory )
    {
      // Don't allow non-categorized or Grenades
      if ( CurInv.InventoryGroup>0 && CurInv.InventoryGroup<=ArrayCount(Categorized) && KFWeapon(CurInv)!=None )
      {
        if ( CurInv==SelectedInventory )
        {
          SelectedInventoryCategory = CurInv.InventoryGroup - 1;
          SelectedInventoryIndex = Categorized[SelectedInventoryCategory];
        }
        ++Num;
        ++Categorized[CurInv.InventoryGroup - 1];
      }
    }

    if( Num<=1 )
      return; // Prevent runaway loop.

    // Now check for suitable next index.
    // Find next available category.
    if( SelectedInventoryCategory==-1 || SelectedInventoryIndex==(Categorized[SelectedInventoryCategory]-1) )
    {
      while( true )
      {
        if( ++SelectedInventoryCategory>=ArrayCount(Categorized) )
          SelectedInventoryCategory = 0;

        if( Categorized[SelectedInventoryCategory]>0 )
          break;
      }
      SelectedInventoryIndex = 0;
    }
    else ++SelectedInventoryIndex; // Simply go to next index.

    // Second pass, find our desired item.
    Num = 0;
    for ( CurInv = PawnOwner.Inventory; CurInv != none; CurInv = CurInv.Inventory )
    {
      if ( CurInv.InventoryGroup==(SelectedInventoryCategory+1) && KFWeapon(CurInv)!=None && (Num++)==SelectedInventoryIndex )
      {
        SelectedInventory = CurInv;
        break;
      }
    }
//    if( PawnOwner.Weapon!=SelectedInventory ) // Make sure not reselecting current weapon.
//      break;
//  }
}


// fuckup fix
// ServerPerks.SRHUDKillingFloor
function HideInventory()
{
  if (bDisplayInventory)
    super.HideInventory();
}


// =============================================================================
//                              DISABLE SOME OVERLAYS
// =============================================================================
// TO DO CHECK!!! DrawCinematicHUD()
simulated function PreBeginPlay()
{
  super.PreBeginPlay();

  // remove film grain effect
  SpectatorOverlay = none;
  default.SpectatorOverlay = none;
  // remove shitty sepia effect
  VisionOverlay = none;
  default.VisionOverlay = none;
  log("HIDEHUD SpectatorOverlay none");
}


// =============================================================================
//                       HIDE PORTRAITS WHEN SOMEONE CHATS
// =============================================================================

function DisplayTraderPortrait()
{
  // switch for portraits
  if (!class'Settings'.default.bHidePortraits)
    super.DisplayTraderPortrait();
}


function DisplayPortrait(PlayerReplicationInfo PRI)
{
  // switch for portraits
  if (!class'Settings'.default.bHidePortraits)
    super.DisplayPortrait(PRI);
}


// =============================================================================
//              fix for <'Pawn' is none> spam in logs... scrn (c)
// =============================================================================

function DrawDoorHealthBars(Canvas C)
{
  // log spam fix
  if (PlayerOwner.pawn != none)
    super.DrawDoorHealthBars(c);
}


// =============================================================================
// Hide health, vest bars while we are in iron sights
// =============================================================================
function DrawPlayerInfo(Canvas C, Pawn P, float ScreenLocX, float ScreenLocY)
{
  local float XL, YL, TempX, TempY, TempSize;
  local string PlayerName;
  local float Dist, OffsetX;
  local byte BeaconAlpha;
  local float OldZ;
  local Material TempMaterial, TempStarMaterial;
  local int i, TempLevel;
  // ADDITION!!! prevent shit tons of typecasting
  local KFPlayerReplicationInfo loc_KFPRI;

  if (KFPlayerReplicationInfo(P.PlayerReplicationInfo) == none || KFPRI == none || KFPRI.bViewingMatineeCinematic)
  {
    return;
  }

  // ADDITION!!!
  loc_KFPRI = KFPlayerReplicationInfo(P.PlayerReplicationInfo);

  Dist = vsize(P.Location - PlayerOwner.CalcViewLocation);
  Dist -= HealthBarFullVisDist;
  Dist = FClamp(Dist, 0, HealthBarCutoffDist-HealthBarFullVisDist);
  Dist = Dist / (HealthBarCutoffDist - HealthBarFullVisDist);
  BeaconAlpha = byte((1.f - Dist) * 255.f);

  if (BeaconAlpha == 0)
  {
    return;
  }

  OldZ = C.Z;
  C.Z = 1.0;
  C.Style = ERenderStyle.STY_Alpha;
  C.SetDrawColor(255, 255, 255, BeaconAlpha);
  C.Font = GetConsoleFont(C);
  PlayerName = Left(P.PlayerReplicationInfo.PlayerName, 16);
  C.StrLen(PlayerName, XL, YL);
  C.SetPos(ScreenLocX - (XL * 0.5), ScreenLocY - (YL * 0.75));
  C.DrawTextClipped(PlayerName);

  // ADDITION!!! Hide health, vest bars, perk icons while we are in iron sights
  if (PawnOwner != none && PawnOwner.weapon != none && KFWeapon(PawnOwner.weapon).bAimingRifle)
  {
    C.Z = OldZ;
    return;
  }

  BarLength = FMin(default.BarLength * (float(C.SizeX) / 1024.f),default.BarLength);
  BarHeight = FMin(default.BarHeight * (float(C.SizeX) / 1024.f),default.BarHeight);

  OffsetX = (36.f * VeterancyMatScaleFactor * 0.6) - (HealthIconSize + 2.0);

  if (loc_KFPRI.ClientVeteranSkill != none && loc_KFPRI.ClientVeteranSkill.default.OnHUDIcon != none)
  {
    if (loc_KFPRI.ClientVeteranSkillLevel > 5)
    {
      TempMaterial = loc_KFPRI.ClientVeteranSkill.default.OnHUDGoldIcon;
      TempStarMaterial = VetStarGoldMaterial;
      TempLevel = loc_KFPRI.ClientVeteranSkillLevel - 5;
    }
    else
    {
      TempMaterial = loc_KFPRI.ClientVeteranSkill.default.OnHUDIcon;
      TempStarMaterial = VetStarMaterial;
      TempLevel = loc_KFPRI.ClientVeteranSkillLevel;
    }

    TempSize = FMin((36 * VeterancyMatScaleFactor ) * (float(C.SizeX) / 1024.f),36 * VeterancyMatScaleFactor) ;
    VetStarSize = FMin(default.VetStarSize * (float(C.SizeX) / 1024.f),default.VetStarSize);
    TempX = ScreenLocX + ((BarLength + HealthIconSize) * 0.5) - (TempSize * 0.25) - OffsetX;
    TempY = ScreenLocY - YL - (TempSize * 0.75);

    C.SetPos(TempX, TempY);
    C.DrawTile(TempMaterial, TempSize, TempSize, 0, 0, TempMaterial.MaterialUSize(), TempMaterial.MaterialVSize());

    TempX += (TempSize - (VetStarSize * 0.75));
    TempY += (TempSize - (VetStarSize * 1.5));

    for (i = 0; i < TempLevel; i++)
    {
      C.SetPos(TempX, TempY);
      C.DrawTile(TempStarMaterial, VetStarSize * 0.7, VetStarSize * 0.7, 0, 0, TempStarMaterial.MaterialUSize(), TempStarMaterial.MaterialVSize());

      TempY -= VetStarSize * 0.7;
    }
  }

  // Health
  if (P.Health > 0)
    DrawKFBar(C, ScreenLocX - OffsetX, (ScreenLocY - YL) - 0.4 * BarHeight, FClamp(P.Health / P.HealthMax, 0, 1), BeaconAlpha);

  // Armor
  if (P.ShieldStrength > 0)
    DrawKFBar(C, ScreenLocX - OffsetX, (ScreenLocY - YL) - 1.5 * BarHeight, FClamp(P.ShieldStrength / 100.f, 0, 1), BeaconAlpha, true);

  C.Z = OldZ;
}


// =============================================================================
// W I P !!!
// try to make message spam protection
// =============================================================================

// kfo support
simulated function Message(PlayerReplicationInfo PRI, coerce string Msg, name MsgType)
{
  local Class<LocalMessage> LocalMessageClass;

  if ( PRI != None && MsgType == 'Say' || MsgType == 'TeamSay' )
  {
    DisplayPortrait(PRI);
  }

  switch( MsgType )
  {
    case 'Trader':
      Msg = TraderString$":" @ Msg;
      LocalMessageClass = class'SayMessagePlus';
      PRI = None;
      break;
    case 'Voice':
    case 'Say':
      if ( PRI == None )
        return;
      Msg = PRI.PlayerName $ ": " $ Msg;
      LocalMessageClass = class'SayMessagePlus';
      break;
    case 'TeamSay':
      if ( PRI == None )
        return;
      Msg = PRI.PlayerName $ " (" $ PRI.GetLocationName() $ "): " $ Msg;
      LocalMessageClass = class'TeamSayMessagePlus';
      break;
    case 'CriticalEvent':
      LocalMessageClass = class'KFCriticalEventPlus';
      LocalizedMessage(LocalMessageClass, 0, None, None, None, Msg);
      return;
    case 'DeathMessage':
      LocalMessageClass = class'xDeathMessage';
      break;
    case 'Msg_CashReward':
      LocalMessageClass = class'Msg_CashReward';
      break;
    default:
      LocalMessageClass = class'StringMessagePlus';
      break;
  }
  AddTextMessage(Msg, LocalMessageClass, PRI);
}


// do not show filtered messages in HUD
function AddTextMessage(string Msg, class<LocalMessage> MessageClass, PlayerReplicationInfo PRI)
{
  local int i;

  // log("HIDEHUD: AddTextMessage called. Message is " $ Msg);
  if (class'Settings'.static.bIsWordBanned(Msg) || PRI == none || len(Msg) == 0)
    return;

  // convert cyrilic into barbaric
  Msg = class'o_CyrillicEncodeUtilities'.static.EnCodeStringHide(Msg);

  if (bMessageBeep && MessageClass.Default.bBeep)
    PlayerOwner.PlayBeepSound();

  for (i = 0; i < ConsoleMessageCount; i++)
  {
    if (TextMessages[i].Text == "")
      break;
  }

  if (i == ConsoleMessageCount)
  {
    for (i = 0; i < ConsoleMessageCount - 1; i++)
      TextMessages[i] = TextMessages[i + 1];
  }

  TextMessages[i].Text = Msg;
  TextMessages[i].MessageLife = Level.TimeSeconds + MessageClass.default.LifeTime;
  TextMessages[i].TextColor = MessageClass.static.GetConsoleColor(PRI);

  // PRI fix
  if (MessageClass == class'SayMessagePlus' || MessageClass == class'TeamSayMessagePlus')
    TextMessages[i].PRI = PRI;
  else
    TextMessages[i].PRI = None;
}


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

defaultproperties
{
     KillIconList(0)=(ZedName="Clot",ZedPriority=7,iconTexture="KFZED_FX_T.Screen.Low_Zed_Head")
     KillIconList(1)=(ZedName="Crawler",ZedPriority=9,iconTexture="KFZED_FX_T.Screen.Low_Zed_Head")
     KillIconList(2)=(ZedName="Gorefast",ZedPriority=6,iconTexture="KFZED_FX_T.Screen.Low_Zed_Head")
     KillIconList(3)=(ZedName="Stalker",ZedPriority=8,iconTexture="KFZED_FX_T.Screen.Low_Zed_Head")
     KillIconList(4)=(ZedName="Bloat",ZedPriority=5,iconTexture="KFZED_FX_T.Screen.Mid_Zed_Head")
     KillIconList(5)=(ZedName="Siren",ZedPriority=4,iconTexture="KFZED_FX_T.Screen.Mid_Zed_Head")
     KillIconList(6)=(ZedName="Husk",ZedPriority=3,iconTexture="KFZED_FX_T.Screen.Mid_Zed_Head")
     KillIconList(7)=(ZedName="Flesh Pound",ZedPriority=1,iconTexture="KFZED_FX_T.Screen.Strong_Zed_Head")
     KillIconList(8)=(ZedName="Scrake",ZedPriority=2,iconTexture="KFZED_FX_T.Screen.Strong_Zed_Head")
     KillIconList(9)=(ZedName="Patriarch",iconTexture="KFZED_FX_T.Screen.Boss_Zed_Head")
     HealthBarPerkList(0)="KFMod.KFVetFieldMedic"
     AllyMaterial=Texture'HideMut.Extra.AllyHealthBar_D'
     AllyMaterialBackdrop=Texture'HideMut.Extra.AllyHealthBackdrop_D'
}
