/* 
    BattleRPG Copyright (C) 2007-2008 Nico de Vries.

    This file is part of BattleRPG.

    BattleRPG is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/.
*/

`include (BattleRPG/Classes/BattleRPG.uci)

class BattleStaticFunctions extends Object;
// Most of the functions here simulate multiple inheritance. An alternative would have been to attach
// items to other objects like BattleGenInvItem does it, but this aproach is more efficient and probably 
// easier to maintain. Especially for distributed data.

var const color Black, Gold;
 
// Simulate multiple inheritance by adding this data to UTPlayerReplicationInfo, UTDuelPRI and UTOnslaughtPRI.

// An alternative to this could be using UTLinkedReplicationInfo (CustomReplicationInfo) but as far as I can see
// this would result in more network overhead, which will become bigger if more data is added to BPRIDATA. 
// Especially since there are only 2 units that change often: XP and Mana, both integers. If this aproach would not
// be used, when a lot of abilities become available with RPG the data structures will become quite large and traffic will
// explode when this is combined with a lot of players. Anothes issue, as mentioned in the Slaughterhouse gametype made
// by Mysterial from Epic (SlaughterGRI.uc), is that large replication units can become unsendable due to size.

struct BPRIDATA {
// Client + server (replicated), this data will be replicated for all clients to all clients, so everyone sees everything
  var int Level; 
  var int XP;
  var int NeededXP;
  var string WeaponMagicDescription;
  var bool bAllowDodgeJumping;
  var int Mana;

// Server only
  var int PrevScore;

// Make sure we can reach this from everywhere
  var BattlePersistentPlayerDatabase BPD;
};

// Simulate multiple inheritance by adding this data to all weapon classes

struct BWEAPDATA {
  var BattleWeaponMagic ActiveBWM;
  var int ActiveBWMLevel;
  var Material MagicMaterial;
};

static simulated function WriteWEAP (UTWeapon UTW, BWEAPDATA BW)
{
  if (BattleWeapon_Avril(UTW) != None) {
    BattleWeapon_Avril(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_Avril(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_Avril(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_Avril(UTW).Rename();
  } else if (BattleWeapon_BioRifle(UTW) != None) {
    BattleWeapon_BioRifle(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_BioRifle(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_BioRifle(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_BioRifle(UTW).Rename();
  } else if (BattleWeapon_Enforcer(UTW) != None) {
    BattleWeapon_Enforcer(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_Enforcer(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_Enforcer(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_Enforcer(UTW).Rename();
  } else if (BattleWeapon_FlakCannon(UTW) != None) {
    BattleWeapon_FlakCannon(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_FlakCannon(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_FlakCannon(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_FlakCannon(UTW).Rename();
  } else if (BattleWeapon_ImpactHammer(UTW) != None) {
    BattleWeapon_ImpactHammer(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_ImpactHammer(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_ImpactHammer(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_ImpactHammer(UTW).Rename();
  } else if (BattleWeapon_LinkGun(UTW) != None) {
    BattleWeapon_LinkGun(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_LinkGun(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_LinkGun(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_LinkGun(UTW).Rename();
  } else if (BattleWeapon_RocketLauncher(UTW) != None) {
    BattleWeapon_RocketLauncher(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_RocketLauncher(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_RocketLauncher(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_RocketLauncher(UTW).Rename();
  } else if (BattleWeapon_ShockRifle(UTW) != None) {
    BattleWeapon_ShockRifle(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_ShockRifle(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_ShockRifle(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_ShockRifle(UTW).Rename();
  } else if (BattleWeapon_SniperRifle(UTW) != None) {
    BattleWeapon_SniperRifle(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_SniperRifle(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_SniperRifle(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_SniperRifle(UTW).Rename();
  } else if (BattleWeapon_Stinger(UTW) != None) {
    BattleWeapon_Stinger(UTW).ActiveBWM = BW.ActiveBWM;
    BattleWeapon_Stinger(UTW).ActiveBWMLevel = BW.ActiveBWMLevel;
    BattleWeapon_Stinger(UTW).MagicMaterial = BW.MagicMaterial;
    BattleWeapon_Stinger(UTW).Rename();
  }
}

static simulated function BWEAPDATA ReadWEAP (UTWeapon UTW)
{
  local BWEAPDATA Result;
  Result.ActiveBWM = None;
  Result.ActiveBWMLevel = 0;
  if (BattleWeapon_Avril(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_Avril(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_Avril(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_Avril(UTW).MagicMaterial;
  } else if (BattleWeapon_BioRifle(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_BioRifle(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_BioRifle(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_BioRifle(UTW).MagicMaterial;
  } else if (BattleWeapon_Enforcer(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_Enforcer(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_Enforcer(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_Enforcer(UTW).MagicMaterial;
  } else if (BattleWeapon_FlakCannon(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_FlakCannon(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_FlakCannon(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_FlakCannon(UTW).MagicMaterial;
  } else if (BattleWeapon_ImpactHammer(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_ImpactHammer(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_ImpactHammer(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_ImpactHammer(UTW).MagicMaterial;
  } else if (BattleWeapon_LinkGun(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_LinkGun(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_LinkGun(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_LinkGun(UTW).MagicMaterial;
  } else if (BattleWeapon_RocketLauncher(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_RocketLauncher(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_RocketLauncher(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_RocketLauncher(UTW).MagicMaterial;
  } else if (BattleWeapon_ShockRifle(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_ShockRifle(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_ShockRifle(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_ShockRifle(UTW).MagicMaterial;
  } else if (BattleWeapon_SniperRifle(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_SniperRifle(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_SniperRifle(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_SniperRifle(UTW).MagicMaterial;
  } else if (BattleWeapon_Stinger(UTW) != None) {
    Result.ActiveBWM = BattleWeapon_Stinger(UTW).ActiveBWM;
    Result.ActiveBWMLevel = BattleWeapon_Stinger(UTW).ActiveBWMLevel;
    Result.MagicMaterial = BattleWeapon_Stinger(UTW).MagicMaterial;
  }
  return Result;
}

static simulated function bool HasWEAP (UTWeapon UTW)
{
  if (BattleWeapon_Avril(UTW) != None) {
  } else if (BattleWeapon_BioRifle(UTW) != None) {
  } else if (BattleWeapon_Enforcer(UTW) != None) {
  } else if (BattleWeapon_FlakCannon(UTW) != None) {
  } else if (BattleWeapon_ImpactHammer(UTW) != None) {
  } else if (BattleWeapon_LinkGun(UTW) != None) {
  } else if (BattleWeapon_RocketLauncher(UTW) != None) {
  } else if (BattleWeapon_ShockRifle(UTW) != None) {
  } else if (BattleWeapon_SniperRifle(UTW) != None) {
  } else if (BattleWeapon_Stinger(UTW) != None) {
  } else {
    return false;
  }
  return true;
}


static simulated function BPRIDATA ReadBPRI (PlayerReplicationInfo PRI)
{
  local BPRIDATA Result;
  if (BattlePRIONS (PRI) != None) {
    Result.Level = BattlePRIONS (PRI).Level;
    Result.XP = BattlePRIONS (PRI).XP;
    Result.NeededXP = BattlePRIONS (PRI).NeededXP;
    Result.WeaponMagicDescription = BattlePRIONS (PRI).WeaponMagicDescription;
    Result.bAllowDodgeJumping = BattlePRIONS (PRI).bAllowDodgeJumping;
    Result.Mana = BattlePRIONS (PRI).Mana;
    Result.PrevScore = BattlePRIONS (PRI).PrevScore;
    Result.BPD = BattlePRIONS (PRI).BPD;
  } else if (BattlePRI (PRI) != None) {
    Result.Level = BattlePRI (PRI).Level;
    Result.XP = BattlePRI (PRI).XP;
    Result.NeededXP = BattlePRI (PRI).NeededXP;
    Result.WeaponMagicDescription = BattlePRI (PRI).WeaponMagicDescription;
    Result.bAllowDodgeJumping = BattlePRI (PRI).bAllowDodgeJumping;
    Result.Mana = BattlePRI (PRI).Mana;
    Result.PrevScore = BattlePRI (PRI).PrevScore;
    Result.BPD = BattlePRI (PRI).BPD;
  } else if (BattlePRIDuel (PRI) != None) {
    Result.Level = BattlePRIDuel (PRI).Level;
    Result.XP = BattlePRIDuel (PRI).XP;
    Result.NeededXP = BattlePRIDuel (PRI).NeededXP;
    Result.WeaponMagicDescription = BattlePRIDuel (PRI).WeaponMagicDescription;
    Result.bAllowDodgeJumping = BattlePRIDuel (PRI).bAllowDodgeJumping;
    Result.Mana = BattlePRIDuel (PRI).Mana;
    Result.PrevScore = BattlePRIDuel (PRI).PrevScore;
    Result.BPD = BattlePRIDuel (PRI).BPD;
`if(!`isdefined(BRPG_BuildRegular))  
  } else if (BattlePRIInvasion (PRI) != None) {
    Result.Level = BattlePRIInvasion (PRI).Level;
    Result.XP = BattlePRIInvasion (PRI).XP;
    Result.NeededXP = BattlePRIInvasion (PRI).NeededXP;
    Result.WeaponMagicDescription = BattlePRIInvasion (PRI).WeaponMagicDescription;
    Result.bAllowDodgeJumping = BattlePRIInvasion (PRI).bAllowDodgeJumping;
    Result.Mana = BattlePRIInvasion (PRI).Mana;
    Result.PrevScore = BattlePRIInvasion (PRI).PrevScore;
    Result.BPD = BattlePRIInvasion (PRI).BPD;
`endif
  }
  return Result;
}

static simulated function WriteBPRI (PlayerReplicationInfo PRI, BPRIDATA BD)
{
  local bool Dirty;
  // Minimize network overhead by only processing changes and using small units
  Dirty = false;
  if (BattlePRIONS (PRI) != None) {
    if (BattlePRIONS (PRI).Level != BD.Level) BattlePRIONS (PRI).Level = BD.Level;
    if (BattlePRIONS (PRI).XP != BD.XP) {
      BattlePRIONS (PRI).XP = BD.XP;
      Dirty = true;
    }
    if (BattlePRIONS (PRI).NeededXP != BD.NeededXP) BattlePRIONS (PRI).NeededXP = BD.NeededXP;
    if (BattlePRIONS (PRI).WeaponMagicDescription != BD.WeaponMagicDescription) BattlePRIONS (PRI).WeaponMagicDescription = BD.WeaponMagicDescription;
    if (BattlePRIONS (PRI).bAllowDodgeJumping != BD.bAllowDodgeJumping) BattlePRIONS (PRI).bAllowDodgeJumping = BD.bAllowDodgeJumping;
    if (BattlePRIONS (PRI).Mana != BD.Mana) BattlePRIONS (PRI).Mana = BD.Mana;
    BattlePRIONS (PRI).PrevScore = BD.PrevScore; // Don't waste CPU cycles
    if (BD.BPD != None) BattlePRIONS (PRI).BPD = BD.BPD; // Make sure we can always access this
  } else if (BattlePRI (PRI) != None) {
    if (BattlePRI (PRI).Level != BD.Level) BattlePRI (PRI).Level = BD.Level;
    if (BattlePRI (PRI).XP != BD.XP) {
      BattlePRI (PRI).XP = BD.XP;
      Dirty = true;
    }
    if (BattlePRI (PRI).NeededXP != BD.NeededXP) BattlePRI (PRI).NeededXP = BD.NeededXP;
    if (BattlePRI (PRI).WeaponMagicDescription != BD.WeaponMagicDescription) BattlePRI (PRI).WeaponMagicDescription = BD.WeaponMagicDescription;
    if (BattlePRI (PRI).bAllowDodgeJumping != BD.bAllowDodgeJumping) BattlePRI (PRI).bAllowDodgeJumping = BD.bAllowDodgeJumping;
    if (BattlePRI (PRI).Mana != BD.Mana) BattlePRI (PRI).Mana = BD.Mana;
    BattlePRI (PRI).PrevScore = BD.PrevScore;
    if (BD.BPD != None) BattlePRI (PRI).BPD = BD.BPD; // Make sure we can always access this
  } else if (BattlePRIDuel (PRI) != None) {
    if (BattlePRIDuel (PRI).Level != BD.Level) BattlePRIDuel (PRI).Level = BD.Level;
    if (BattlePRIDuel (PRI).XP != BD.XP) {
      BattlePRIDuel (PRI).XP = BD.XP;
      Dirty = true;
    }
    if (BattlePRIDuel (PRI).NeededXP != BD.NeededXP) BattlePRIDuel (PRI).NeededXP = BD.NeededXP;
    if (BattlePRIDuel (PRI).WeaponMagicDescription != BD.WeaponMagicDescription) BattlePRIDuel (PRI).WeaponMagicDescription = BD.WeaponMagicDescription;
    if (BattlePRIDuel (PRI).bAllowDodgeJumping != BD.bAllowDodgeJumping) BattlePRIDuel (PRI).bAllowDodgeJumping = BD.bAllowDodgeJumping;
    if (BattlePRIDuel (PRI).Mana != BD.Mana) BattlePRIDuel (PRI).Mana = BD.Mana;
    BattlePRIDuel (PRI).PrevScore = BD.PrevScore;
    if (BD.BPD != None) BattlePRIDuel(PRI).BPD = BD.BPD; // Make sure we can always access this
`if(!`isdefined(BRPG_BuildRegular))  
  } else if (BattlePRIInvasion (PRI) != None) {
    if (BattlePRIInvasion (PRI).Level != BD.Level) BattlePRIInvasion (PRI).Level = BD.Level;
    if (BattlePRIInvasion (PRI).XP != BD.XP) {
      BattlePRIInvasion (PRI).XP = BD.XP;
      Dirty = true;
    }
    if (BattlePRIInvasion (PRI).NeededXP != BD.NeededXP) BattlePRIInvasion (PRI).NeededXP = BD.NeededXP;
    if (BattlePRIInvasion (PRI).WeaponMagicDescription != BD.WeaponMagicDescription) BattlePRIInvasion (PRI).WeaponMagicDescription = BD.WeaponMagicDescription;
    if (BattlePRIInvasion (PRI).bAllowDodgeJumping != BD.bAllowDodgeJumping) BattlePRIInvasion (PRI).bAllowDodgeJumping = BD.bAllowDodgeJumping;
    if (BattlePRIInvasion (PRI).Mana != BD.Mana) BattlePRIInvasion (PRI).Mana = BD.Mana;
    BattlePRIInvasion (PRI).PrevScore = BD.PrevScore;
    if (BD.BPD != None) BattlePRIInvasion(PRI).BPD = BD.BPD; // Make sure we can always access this
`endif
  }
  if (Dirty) {
    if (BattlePRIDuel(PRI) != None && BattlePRIDuel(PRI).BPD != None) {
      BattlePRIDuel(PRI).BPD.SavePlayerData (PRI); // Save data to persistent database as well
    }
    if (BattlePRI(PRI) != None && BattlePRI(PRI).BPD != None) {
      BattlePRI(PRI).BPD.SavePlayerData (PRI); // Save data to persistent database as well
    }
    if (BattlePRIONS(PRI) != None && BattlePRIONS(PRI).BPD != None) {
      BattlePRIONS(PRI).BPD.SavePlayerData (PRI); // Save data to persistent database as well
    }
`if(!`isdefined(BRPG_BuildRegular))  
    if (BattlePRIInvasion(PRI) != None && BattlePRIInvasion(PRI).BPD != None) {
      BattlePRIInvasion(PRI).BPD.SavePlayerData (PRI); // Save data to persistent database as well
    }
`endif
  }
}

// With multiple inheritance this might have been a method of UTPlayerReplicationInfo, UTDuelPRI and UTOnslaughtPRI
static simulated function PRITick (UTPlayerReplicationInfo PRI)
{
  local int Score;
  local BPRIDATA BD;
  local UTPawn P;
  local UTWeapon W;
  local BWEAPDATA BWEAP;
  local string WeaponMagicDescription;
  local BattleRPG BRPG;

  BRPG = WorldInfo2BatleRPG (PRI.WorldInfo);

  Score = int(PRI.Score);
  BD = class'BattleStaticFunctions'.static.ReadBPRI (PRI);
  if (BD.Level == 0 || Score != BD.PrevScore || BD.Level < BRPG.MinimumLevel) {
    if (BD.Level == 0) { // Initialize RPG data
      BD.Level = 1;
    }
    if (BD.NeededXP == 0) { // Initialize RPG data
      BD.NeededXP = 2;
    }

    // Prototype RPG scoring system, this needs to become configurable and overridable
    // The system is easy, for every point you get you also get 1 XP
    // The first 25 levels are very easy to get players to learn how RPG works
    // Reaching level 80 requires some serious playing but is doable
    // Level 100 is tougher to reach
    // Over level 100 it becomes hard
    // Over level 110 it becomes extremely hard
    if (Score > BD.PrevScore) {
      BD.XP += Score - BD.PrevScore;
    }
    while (BD.XP >= BD.NeededXP || BD.Level < BRPG.MinimumLevel) {
      BD.XP -= BD.NeededXP;
      BD.Level++;
      if (BD.Level < BRPG.MinimumLevel) {
        BD.Level = BRPG.MinimumLevel;
        BD.XP = 0;
      }
      if (BD.Level < 25) {
        BD.NeededXP = 2 * BD.Level;               // 1=>2:2 .. 24=>25:48
      } else if (BD.Level < 80) {
        BD.NeededXP = BD.Level * 5 - 75;          // 25=>26:50 .. 79=>80:320
      } else if (BD.Level < 100) {
        BD.NeededXP = BD.Level * 100 - 7600;      // 80=>81:400 .. 99=>100:2300
      } else if (BD.Level <= 110) {
        BD.NeededXP = BD.Level * 1000 - 97000;    // 100=>101:3000 .. 109=>110:12000
      } else {
        BD.NeededXP = BD.Level * 10000 - 1080000; // 110=>111:20000 .. 120=>121:120000 .. 200=>201:920000
      }
      BD.NeededXP = Max (1, (BD.NeededXP * BRPG.NeededXPFactor) / 100);
    } 

    // Prototype Mana scoring system, this needs to become configurable and overridable
    if (Score > BD.PrevScore) {
      if (BD.Mana <= 100) { // Allow all kinds of Mana bonuses
        if (InStr(PRI.WorldInfo.GetGameClass().name, "Ons")!=-1) {  
          BD.Mana += 1; // Make Mana harder to collect for Warfare
        } else {
          BD.Mana += 2 + (Score-BD.PrevScore); // Mostly less mana for achievements, more for killing
        }
        if (BD.Mana > 100) BD.Mana = 100;
      }
    }

    BD.PrevScore = Score;
    class'BattleStaticFunctions'.static.WriteBPRI (PRI, BD);
  }
  
  WeaponMagicDescription = "";
  if ((Controller(PRI.Owner) != None) && (UTPawn(Controller(PRI.Owner).Pawn) != None)) {  
    P = UTPawn(Controller(PRI.Owner).Pawn);
    if (P != None) {
      W = UTWeapon (P.Weapon);
      if (W != None) {
        BWEAP = class'BattleStaticFunctions'.static.ReadWEAP (w);
        if (BWEAP.ActiveBWM != None) {
          WeaponMagicDescription = BWEAP.ActiveBWM.MagicDescription (BWEAP.ActiveBWMLevel);
        }
      }
    }
  }    
  if (WeaponMagicDescription != BD.WeaponMagicDescription) {
    BD.WeaponMagicDescription = WeaponMagicDescription;
    class'BattleStaticFunctions'.static.WriteBPRI (PRI, BD);
  }
}

// With multiple inheritance this might have been a method of UTCTFScoreboardPanel, UTDuelQueueScoreboardPanel,
// UTONSScoreboardPanel, UTScoreboardPanel and UTTDMScoreboardPanel
static simulated function string GetExtraRightMisc (UTPlayerReplicationInfo PRI, int Deaths, int Score, optional bool bIncludeDeaths=true)
{
  local int TotalSeconds, PPH;
  local BPRIDATA BD;

  BD = class'BattleStaticFunctions'.static.ReadBPRI (PRI);
  TotalSeconds = PRI.WorldInfo.GRI.ElapsedTime - PRI.StartTime;
  PPH = int(float(Score) / (float(TotalSeconds+1)/float(3600)));
  if (BD.Level > 1) {
    if (bIncludeDeaths) {
        return "Deaths "$Deaths$"   "$"PPH "$PPH$"   "$"Level "$BD.Level;
    } else {
      return "PPH "$PPH$"   "$"Level "$BD.Level;
    }
  } else {
    if (bIncludeDeaths) {
      return "Deaths "$Deaths$"   "$"PPH "$PPH;
    } else {
      return "PPH "$PPH;
    }
  }
}

// With multiple inheritance this might have been a method of UTCTFScoreboardPanel, UTDuelQueueScoreboardPanel,
// UTONSScoreboardPanel, UTScoreboardPanel and UTTDMScoreboardPanel
static simulated function string GetExtraRightName (UTPlayerReplicationInfo PRI)
{
  local BPRIDATA BD;

  BD = class'BattleStaticFunctions'.static.ReadBPRI (PRI);
  if (BD.Level > 1) {
    return " - lvl "$BD.Level;
  } else {
    return "";
  }
}

// With multiple inheritance this might have been a method of UTHUD, UTCTFHUD, UTDuelHUD, UTOnslaughtHUD and UTTeamHUD
static simulated function DrawTile (UTHUD H, String Style, float X, float Y, float Wi, float He)
{
  // Simulate a virtual 1024*768 screen  
  X = X * H.ResolutionScaleX;
  Y = Y * H.ResolutionScale;
  H.Canvas.SetPos(X, Y);
  H.Canvas.DrawColorizedTile(H.AltHudTexture, Wi * H.ResolutionScale, He * H.ResolutionScale, 489, 395,183, 44, H.TeamHudColor);
}

// With multiple inheritance this might have been a method of UTHUD, UTCTFHUD, UTDuelHUD, UTOnslaughtHUD and UTTeamHUD
static simulated function DrawText (UTHUD H, float X, float Y, string Text, optional string Font="large")
{
  if (H == None || H.Canvas == None) return;

  // Simulate a virtual 1024*768 screen  
  X = X * H.ResolutionScaleX;
  Y = Y * H.ResolutionScale;

  if (Font == "tiny") {
    H.Canvas.Font = class'Engine'.static.GetTinyFont();
  } else if (Font == "small") {
    H.Canvas.Font = class'Engine'.static.GetSmallFont();
  } else if (Font == "medium") {
    H.Canvas.Font = class'Engine'.static.GetMediumFont();
  } else if (Font == "large") {
    H.Canvas.Font = class'Engine'.static.GetLargeFont();
  }

  H.Canvas.DrawColor = default.Black;
  if (Font == "tiny" || Font == "small" || Font == "medium") H.Canvas.SetPos (X+1, Y+1); else H.Canvas.SetPos (X+2, Y+2);
  H.Canvas.DrawTextClipped (Text, false);
  H.Canvas.DrawColor = default.Gold;
  H.Canvas.SetPos (X, Y);
  H.Canvas.DrawTextClipped (Text, false);
}

// With multiple inheritance this might have been a method of UTHUD, UTCTFHUD, UTDuelHUD, UTOnslaughtHUD and UTTeamHUD
static simulated function BattleEnhanceHUD (UTHUD H)
{
  local BPRIDATA BD;
  local BattlePlayerController BPC;
  local float YCursor;

  BD = ReadBPRI(H.UTOwnerPRI);
  BPC = BattlePlayerController (H.UTOwnerPRI.Owner);

  if (BD.Level > 1 || BD.XP > 0) { // Don't show if unknown (yet), e.g. because of warmup mode
    YCursor = 400;    
    DrawText (H, 10, YCursor, "Level:" @ BD.Level @ "(" $ BD.XP $ "/" $ BD.NeededXP $ ")", "medium"); YCursor += 20;
    DrawText (H, 10, YCursor, "Mana: " $ BD.Mana, "medium"); YCursor += 20;
    if (BPC.SelectedArtifactName != "") {
      DrawText (H, 10, YCursor, "Artifact:" @ BPC.SelectedArtifactName, "medium"); YCursor += 20;
    }
    YCursor += 5; 
    if (BD.WeaponMagicDescription != "") {
      DrawText (H, 10, YCursor, "Magic:" @ BD.WeaponMagicDescription, "tiny"); YCursor += 15.0 / H.ResolutionScale;
    }
    DrawText (H, 10, YCursor, "Press L for the Battle"$"RPG"@`BRPG_Version@"menu", "tiny"); YCursor += 15.0 / H.ResolutionScale;
    if (BPC.SelectedArtifactName != "") {
      DrawText (H, 10, YCursor, "Press [ ] and U for artifacts (cost:"@BPC.SelectedArtifactCost$")", "tiny"); YCursor += 15.0 / H.ResolutionScale;
    }
  }
}

static function BattleRPG WorldInfo2BatleRPG (Worldinfo WI)
{
  local Mutator BM;
 
  if (WI != None && WI.Game != None) for (BM = WI.Game.BaseMutator; BM != None; BM = BM.NextMutator) {
    if (BattleRPG(BM) != None) return BattleRPG(BM);
  }
  return None;
}

static function Message2Pawn (Pawn P, string Type, string Msg)
{
  if (P == None || BattlePlayerController(P.Controller) == None) return;
  BattlePlayerController(P.Controller).Message2PC (Type, Msg);
}

static function Message2PC (PlayerController PC, string Type, string Msg)
{
  if (BattlePlayerController (PC) == None) return;

  if (Type == "newweaponmagic") {
    PC.ClientMessage (Msg);
  } else if (Type == "artifactaction") {
    PC.ClientMessage (Msg);
  } else if (Type == "healedfrom") {
    PC.ClientMessage (Msg);
  } else if (Type == "healedto") {
    PC.ClientMessage (Msg);
  } else if (Type == "poisonedfrom") {
    PC.ClientMessage (Msg);
  } else if (Type == "poisonedto") {
    PC.ClientMessage (Msg);
  } else {
    PC.ClientMessage (Msg);
  }
}

defaultproperties
{
  Black=(B=0,G=0,R=0,A=255)
  Gold=(B=11,G=183,R=255,A=200)
  Name="BattleStaticFunctions"
}