KFStatOutput: Difference between revisions

From Tripwire Interactive Wiki
Jump to navigation Jump to search
(Created page with "This is my stat generator for the killing floor weapon pages. --~~~~ <blockquote><pre>//==========================================================...")
 
mNo edit summary
 
(15 intermediate revisions by the same user not shown)
Line 1: Line 1:
This is my stat generator for the killing floor [[:Category:Weapons (KF)|weapon]] pages.  --[[User:Benjamin|Benjamin]] ([[User talk:Benjamin|talk]]) 20:42, 11 October 2012 (EDT)
This is my stat generator for the killing floor [[:Category:Weapons (KF)|weapon]] pages.  --[[User:Benjamin|Benjamin]] ([[User talk:Benjamin|talk]]) 20:42, 11 October 2012 (EDT)


<blockquote><pre>//=============================================================================
Result is stored [[Data:StatOutput1040|here]].
// Weapon stat generator 12-October-2012 Benjamin
 
{{Begin code|MyCode}}
//=============================================================================
// Weapon stat generator 08-November-2012 Benjamin
//
//
// KNOWN ISSUES
// KNOWN ISSUES
Line 9: Line 12:
// ammo (boomstick)
// ammo (boomstick)
//
//
// * Cannot detect penetration for all weapons.
// * Cannot detect penetration for all weapons, must check manually.
//
//
// INFO
// ===== INFO
//
//
// All instant-fire weapons rely on the DamageType for the head shot
// HEAD SHOTS
// multiplier.
//
//
// Any projectiles that derive from ShotgunBullet.uc use 'HeadShotDamageMult'  
// Every weapon's head shot multiplier is stored in its associated damagetype
// as the head damage multiplier. The following projectiles implement their own
// class, except (some of) those weapons whose projectiles implement
// with the same name:
// ProcessTouch themselves: CrossbowArrow, M99Bullet CrossbuzzsawBlade. These
//
// projectiles have their own HeadShotDamageMult variable.
// CrossbowArrow.uc
// HuskGunProjectile.uc
// M99Bullet.uc
//
//
// NOTES
// NOTES
//
//
// (1) Certain classes don't use AmmoPerFire: MP7MAltFire, M7A3MAltFire
// (1) Certain classes don't use AmmoPerFire: MP7MAltFire, M7A3MAltFire
// (2) Hardcoded penetration values: Deagle, MK23, 44Magnum  
// (2) Hardcoded penetration values: Deagle, MK23, 44Magnum, Crossbow, M99,
// * Also crossbow and M99
// and Buzzsaw bow. Look in script files manually for these stats.
// (3) Weapons only have a single ReloadRate stat
// (3) Weapons only have a single ReloadRate stat (none for alt-fire)
// (4) Weapons that auto-reload with a specific time:
//
//    Hunting shotgun - ReloadCountDown stored in BoomStick.uc
//    M79 - FireRate (fire + reload are same animation)
//    Crossbow - FireRate (fire + reload are same animation)
//    LAW - FireRate (fire + reload are same animation)
//    M99 - FireRate (fire + reload are same animation)
//    Buzzsaw Bow - FireRate (fire + reload are same animation)
//=============================================================================
//=============================================================================
class KFStatOutputMut extends Mutator;
class KFStatOutputMut extends Mutator;


// Weapons
var array< class<KFWeaponPickup> > AdditionalWeapons;
var class<KFWeaponPickup> Pickup;
var class<KFWeaponPickup> Pickup;
var class<KFWeapon> Weapon;
var class<KFWeapon> Weapon;
var class<WeaponFire> Fire[2];
var class<WeaponFire> Fire[2];
var class<KFMonster> KFM;
// Specimens
var array< class<KFMonster> > AdditionalSpecimens;
var array<float> GameDifficulty;
var array<float> BountyMultiplier;
var array<float> HealthMultiplier;
var array<float> HeadHealthMultiplier;
var array<float> SpeedMultiplier;
var array<float> DamageMultiplier;


function bool IsTrueFire(class<WeaponFire> Fire)
function OutputStatNum(string Title, float Stat, optional float AltStat)
{
OutputStat(Title, string(Stat), string(AltStat));
}
 
function OutputStat(string Title, string Stat, optional string AltStat)
{
local int A, x;
// Adjust stat name
Title $= ":";
A = Len(Title);
if (A < 14)
{
for (x = 0; x < 14 - A; x++)
Title $= " ";
}
if (Fire[1] != none && AltStat != "")
Log(Title $ Stat @ "(" $ AltStat $ ")");
else
Log(Title $ Stat);
}
 
function GenerateWeaponStats()
{
local string Name;
local string Perk;
local int Cost;
local int AmmoCost;
local int Weight;
local int Capacity[2];
local int MagazineSize[2];
local float FireRate[2];
local float ReloadSpeed[2];
local float Spread[2];
local int Damage[2];
local float DamageRadius[2];
local float HeadMultiplier[2];
local int Pellets[2];
local int MaxPens[2];
local float PenReduction[2];
local float Range;
local int i, x;
local array< class<Pickup> > WeaponList;
for (i = 0; i < ArrayCount(class'KFLevelRules'.default.ItemForSale); i++)
if (class'KFLevelRules'.default.ItemForSale[i] != none)
WeaponList[WeaponList.Length] = class'KFLevelRules'.default.ItemForSale[i];
for (i = 0; i < AdditionalWeapons.Length; i++)
WeaponList[WeaponList.Length] = AdditionalWeapons[i];
 
// Main loop
for (i = 0; i < WeaponList.Length; i++)
{
Pickup = class<KFWeaponPickup>(WeaponList[i]);
if (Pickup != none)
{
Weapon = class<KFWeapon>(Pickup.default.InventoryType);
if (Weapon != none)
{
for (x = 0; x < 2; x++)
{
if (Weapon.default.FireModeClass[x] != none && IsTrueFire(Weapon.default.FireModeClass[x]))
Fire[x] = Weapon.default.FireModeClass[x];
else
Fire[x] = none;
}
GetName(Name);
GetPerk(Perk);
GetCost(Cost);
GetAmmoCost(AmmoCost);
GetWeight(Weight);
GetCapacity(Capacity);
GetMagazineSize(MagazineSize);
GetFireRate(FireRate);
GetReloadSpeed(ReloadSpeed);
GetSpread(Spread);
GetDamage(Damage);
GetDamageRadius(DamageRadius);
GetHeadMultiplier(HeadMultiplier);
GetPellets(Pellets);
GetMaxPens(MaxPens);
GetPenReduction(PenReduction);
GetRange(Range);
OutputStat("Name", Name);
OutputStat("Perk", Perk);
OutputStatNum("Cost", Cost);
OutputStatNum("Weight", Weight);
OutputStatNum("Ammo cost", AmmoCost);
OutputStatNum("Capacity", Capacity[0], Capacity[1]);
OutputStatNum("Magazine", MagazineSize[0], MagazineSize[1]);
OutputStatNum("Damage", Damage[0], Damage[1]);
OutputStatNum("Radius", DamageRadius[0], DamageRadius[0]);
OutputStatNum("Head", HeadMultiplier[0], HeadMultiplier[1] );
OutputStatNum("Pellets", Pellets[0], Pellets[1]);
OutputStatNum("Spread", Spread[0], Spread[1]);
OutputStatNum("Max pens", MaxPens[0], MaxPens[1]);
OutputStatNum("Pen reduc", PenReduction[0], PenReduction[1]);
OutputStatNum("Fire rate", FireRate[0], FireRate[1]);
OutputStatNum("Reload sp", ReloadSpeed[0], ReloadSpeed[1]);
Log("");
}
}
}
Log("");
Log("");
}
 
function GenerateSpecimenStats()
{
local string Name;
local int Bounty[5];
local int Health[5];
local float HeadHealth[5];
local float Speed[5];
local int Damage[5];
local float Range;
 
local KFGameType KF;
local int i;
local array < class<KFMonster> > Specimens;
KF = KFGameType(Level.Game);
Log("===========================================================");
Log("SPECIMEN STATS ============================================");
Log("===========================================================");
 
for (i = 0; i < KF.StandardMonsterClasses.Length; i++)
Specimens[Specimens.Length] = class<KFMonster>(DynamicLoadObject(
class'KFGameType'.default.StandardMonsterClasses[i].MClassName, class'class'));
for (i = 0; i < AdditionalSpecimens.Length; i++)
Specimens[Specimens.Length] = AdditionalSpecimens[i];
 
for (i = 0; i < Specimens.Length; i++)
{
KFM = Specimens[i];
GetSpecimenName(Name);
GetSpecimenBounty(Bounty);
GetSpecimenHealth(Health);
GetSpecimenHeadHealth(HeadHealth);
GetSpecimenSpeed(Speed);
GetSpecimenDamage(Damage);
GetSpecimenRange(Range);
Log("Name:      " $ Name);
Log("Bounty:    " $ Bounty[0] @ Bounty[1] @ Bounty[2] @ Bounty[3] @ Bounty[4]);
Log("Health:    " $ Health[0] @ Health[1] @ Health[2] @ Health[3] @ Health[4]);
Log("HeadHealth: " $ HeadHealth[0] @ HeadHealth[1] @ HeadHealth[2] @ HeadHealth[3] @ HeadHealth[4]);
Log("Speed:      " $ Speed[0] @ Speed[1] @ Speed[2] @ Speed[3] @ Speed[4]);
Log("Damage:    " $ Damage[0] @ Damage[1] @ Damage[2] @ Damage[3] @ Damage[4]);
Log("Range:      " $ Range);
Log("");
}
 
Log("");
}
 
function PostBeginPlay()
{
GenerateWeaponStats();
GenerateSpecimenStats();
}
 
///////////////////////////////////////////////////////////////////////////////
// SPECIMEN STAT CALCULATION
///////////////////////////////////////////////////////////////////////////////
 
function GetSpecimenName(out string Name)
{
Name = KFM.default.MenuName;
}
 
function GetSpecimenBounty(out int KillScore[5])
{
{
    if (Fire.Name == 'NoFire'
local int i;
        || Fire.Name == 'ShotgunLightFire'
        || Fire.Name == 'SingleALTFire'
for (i = 0; i < 5; i++)
        )
KillScore[i] = Max(1, int(KFM.default.ScoringValue * BountyMultiplier[i]));
        return false;
   
    return true;
}
}


function bool IsMeleeFire(class<WeaponFire> Fire)
function GetSpecimenHealth(out int Health[5])
{
{
    if (Fire == none)
local int i;
        return false;
 
    if (class<KFMeleeFire>(Fire) != none)
for (i = 0; i < 5; i++)
        return true;
Health[i] = Ceil(KFM.default.Health * HealthMultiplier[i]);
    return false;
}
}


function bool IgnoresLoad(class<WeaponFire> Fire)
function GetSpecimenHeadHealth(out float HeadHealth[5])
{
{
    if (class<MP7MAltFire>(Fire) != none
local int i;
        || class<M7A3MAltFire>(Fire) != none)
 
       
for (i = 0; i < 5; i++)
        return true;
HeadHealth[i] = Ceil(KFM.default.HeadHealth * HeadHealthMultiplier[i]);
   
    return false;
}
}


function OutputStatNum(string Title, float Stat, optional float AltStat)
function GetSpecimenSpeed(out float Speed[5])
{
{
    OutputStat(Title, string(Stat), string(AltStat));
local int i;
 
for (i = 0; i < 5; i++)
Speed[i] = KFM.default.GroundSpeed * SpeedMultiplier[i];
}
}


function OutputStat(string Title, string Stat, optional string AltStat)
function GetSpecimenDamage(out int Damage[5])
{
{
    local int A, x;
local int i;
   
 
    // Adjust stat name
for (i = 0; i < 5; i++)
    Title $= ":";
Damage[i] = KFM.default.MeleeDamage * DamageMultiplier[i];
    A = Len(Title);
    if (A < 14)
    {
        for (x = 0; x < 14 - A; x++)
            Title $= " ";
    }
   
    if (Fire[1] != none && AltStat != "")
        Log(Title $ Stat @ "(" $ AltStat $ ")");
    else
        Log(Title $ Stat);
}
}


function PostBeginPlay()
function GetSpecimenRange(out float Range)
{
{
    local string Name;
Range = KFM.default.MeleeRange;
    local int Cost;
    local int Weight;
    local int Capacity[2];
    local int MagazineSize[2];
    local float FireRate[2];
    local float ReloadSpeed[2];
    local float Spread[2];
    local int Damage[2];
    local float DamageRadius[2];
    local float HeadMultiplier[2];
    local int Pellets[2];
    local int MaxPens[2];
    local float PenReduction[2];
   
    local int i, x;
   
    // Main loop
    for (i = 0; class'KFLevelRules'.default.ItemForSale[i] != none; i++)
    {
        Pickup = class<KFWeaponPickup>(class'KFLevelRules'.default.ItemForSale[i]);
        if (Pickup != none)
        {
            Weapon = class<KFWeapon>(Pickup.default.InventoryType);
            if (Weapon != none)
            {
                for (x = 0; x < 2; x++)
                {
                    if (Weapon.default.FireModeClass[x] != none && IsTrueFire(Weapon.default.FireModeClass[x]))
                        Fire[x] = Weapon.default.FireModeClass[x];
                    else
                        Fire[x] = none;
                }
                       
                GetName(Name);
                GetCost(Cost);
                GetWeight(Weight);
                GetCapacity(Capacity);
                GetMagazineSize(MagazineSize);
                GetFireRate(FireRate);
                GetReloadSpeed(ReloadSpeed);
                GetSpread(Spread);
                GetDamage(Damage);
                GetDamageRadius(DamageRadius);
                GetHeadMultiplier(HeadMultiplier);
                GetPellets(Pellets);
                GetMaxPens(MaxPens);
                GetPenReduction(PenReduction);
               
                OutputStat  ("Name", Name);
                OutputStatNum("Cost", Cost);
                OutputStatNum("Weight", Weight);
                OutputStatNum("Capacity", Capacity[0], Capacity[1]);
                OutputStatNum("Magazine", MagazineSize[0], MagazineSize[1]);
                OutputStatNum("Fire rate", FireRate[0], FireRate[1]);
                OutputStatNum("Reload sp", ReloadSpeed[0], ReloadSpeed[1]);
                OutputStatNum("Spread", Spread[0], Spread[1]);
                OutputStatNum("Damage", Damage[0], Damage[1]);
                OutputStatNum("Radius", DamageRadius[0], DamageRadius[0]);
                OutputStatNum("Head", HeadMultiplier[0], HeadMultiplier[1] );
                OutputStatNum("Pellets", Pellets[0], Pellets[1]);
                OutputStatNum("Max pens", MaxPens[0], MaxPens[1]);
                OutputStatNum("Pen reduc", PenReduction[0], PenReduction[1]);
                Log("");
            }
        }
    }
   
    Destroy();
}
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// STAT CALCULATION
// WEAPON STAT CALCULATION
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


function GetName(out string Name)
function GetName(out string Name)
{
{
    Name = Pickup.default.ItemName;
Name = Pickup.default.ItemName;
}
 
function GetPerk(out string Perk)
{
Perk = KFGameType(Level.Game).default.
LoadedSkills[Pickup.default.CorrespondingPerkIndex].default.VeterancyName;
}
}


function GetCost(out int Cost)
function GetCost(out int Cost)
{
{
    Cost = Pickup.default.Cost;
Cost = Pickup.default.Cost;
}
 
function GetAmmoCost(out int AmmoCost)
{
AmmoCost = Pickup.default.AmmoCost;
}
}


function GetWeight(out int Weight)
function GetWeight(out int Weight)
{
{
    Weight = Pickup.default.Weight;
Weight = Pickup.default.Weight;
}
}


function GetCapacity(out int Capacity[2], optional int Index)
function GetCapacity(out int Capacity[2], optional int Index)
{
{
    Capacity[Index] = 0;
Capacity[Index] = 0;
    if (Index == 0) GetCapacity(Capacity, 1);
if (Index == 0) GetCapacity(Capacity, 1);


    if (Fire[Index] != none && !IsMeleeFire(Fire[Index]) && Fire[Index].default.AmmoClass != none)
if (Fire[Index] != none && !IsMeleeFire(Fire[Index]) && Fire[Index].default.AmmoClass != none)
        Capacity[Index] = Fire[Index].default.AmmoClass.default.MaxAmmo;
Capacity[Index] = Fire[Index].default.AmmoClass.default.MaxAmmo;
}
}


function GetMagazineSize(out int MagazineSize[2])
function GetMagazineSize(out int MagazineSize[2])
{
{
    MagazineSize[0] = Weapon.default.MagCapacity;
MagazineSize[0] = Weapon.default.MagCapacity;
    MagazineSize[1] = 0; // Can't be obtained normally
MagazineSize[1] = 0; // Can't be obtained normally
}
}


function GetFireRate(out float FireRate[2], optional int Index)
function GetFireRate(out float FireRate[2], optional int Index)
{
{
    FireRate[Index] = 0;
FireRate[Index] = 0;
    if (Index == 0) GetFirerate(FireRate, 1);
if (Index == 0) GetFirerate(FireRate, 1);
 
    if (Fire[Index] != none)
if (IsAutoReloadingWeapon(Weapon)) // (4) Weapons that fire-reload in one animation
        FireRate[Index] = Fire[Index].default.FireRate;
FireRate[Index] = 0;
else
{
if (Fire[Index] != none)
FireRate[Index] = Fire[Index].default.FireRate;
}
}
}


function GetReloadSpeed(out float ReloadSpeed[2], optional int Index)
function GetReloadSpeed(out float ReloadSpeed[2], optional int Index)
{
{
    ReloadSpeed[Index] = 0;
ReloadSpeed[Index] = 0;
    if (Index == 0) GetReloadSpeed(ReloadSpeed, 1);
if (Index == 0) GetReloadSpeed(ReloadSpeed, 1);
   
    if (Fire[Index] != none)
if (IsAutoReloadingWeapon(Weapon)) // (4) Weapons that fire-reload in one animation
        ReloadSpeed[Index] = Weapon.default.ReloadRate; // (3)
if (Fire[Index] != none)
ReloadSpeed[Index] = Fire[Index].default.FireRate;
else
{
if (Fire[Index] != none)
ReloadSpeed[Index] = Weapon.default.ReloadRate; // (3)
}
}
}


function GetSpread(out float Spread[2], optional int Index)
function GetSpread(out float Spread[2], optional int Index)
{
{
    Spread[Index] = 0;
Spread[Index] = 0;
    if (Index == 0) GetSpread(Spread, 1);
if (Index == 0) GetSpread(Spread, 1);
   
    if (Fire[Index] != none)
if (Fire[Index] != none)
        Spread[Index] = Fire[Index].default.Spread;
Spread[Index] = Fire[Index].default.Spread;
}
}


function GetDamage(out int Damage[2], optional int Index)
function GetDamage(out int Damage[2], optional int Index)
{
{
    Damage[Index] = 0;
Damage[Index] = 0;
    if (Index == 0) GetDamage(Damage, 1);
if (Index == 0) GetDamage(Damage, 1);


    if (Fire[Index] != none)
if (Fire[Index] != none)
    {
{
        if (class<InstantFire>(Fire[Index]) != none) // HITSCAN
if (class<InstantFire>(Fire[Index]) != none) // HITSCAN
            Damage[Index] = class<InstantFire>(Fire[Index]).default.DamageMax;
Damage[Index] = class<InstantFire>(Fire[Index]).default.DamageMax;
        else if(class<BaseProjectileFire>(Fire[Index]) != none) // PROJECTILE
else if(class<BaseProjectileFire>(Fire[Index]) != none) // PROJECTILE
        {
{
            if (class<MP7MMedicGun>(Weapon) != none)
if (class<MP7MMedicGun>(Weapon) != none)
                Damage[Index] = class<MP7MMedicGun>(Weapon).default.HealBoostAmount;
Damage[Index] = class<MP7MMedicGun>(Weapon).default.HealBoostAmount;
            else if (class<M7A3MMedicGun>(Weapon) != none)
else if (class<M7A3MMedicGun>(Weapon) != none)
                Damage[Index] = class<M7A3MMedicGun>(Weapon).default.HealBoostAmount;
Damage[Index] = class<M7A3MMedicGun>(Weapon).default.HealBoostAmount;
            else
else
                Damage[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass.default.Damage;
Damage[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass.default.Damage;
        }
}
        else if (class<KFMeleeFire>(Fire[Index]) != none) // MELEE
else if (class<KFMeleeFire>(Fire[Index]) != none) // MELEE
        {
{
            Damage[Index] = class<KFMeleeFire>(Fire[Index]).default.DamageConst +
Damage[Index] = class<KFMeleeFire>(Fire[Index]).default.MeleeDamage;
                class<KFMeleeFire>(Fire[Index]).default.MaxAdditionalDamage;
}
        }
}
    }
}
}


function GetDamageRadius(out float DamageRadius[2], optional int Index)
function GetDamageRadius(out float DamageRadius[2], optional int Index)
{
{
    local class<DamageType> DT;
local class<DamageType> DT;
   
    DamageRadius[Index] = 0;
DamageRadius[Index] = 0;
    if (Index == 0) GetDamageRadius(DamageRadius, 1);
if (Index == 0) GetDamageRadius(DamageRadius, 1);
   
    if (Fire[Index] != none && class<BaseProjectileFire>(Fire[Index]) != none)
if (Fire[Index] != none && class<BaseProjectileFire>(Fire[Index]) != none)
    {
{
        DT = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass.default.MyDamageType;
DT = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass.default.MyDamageType;
        if (class<KFWeaponDamageType>(DT) != none)
if (class<KFWeaponDamageType>(DT) != none)
        {
{
            if (class<KFWeaponDamageType>(DT).default.bIsExplosive)
if (class<KFWeaponDamageType>(DT).default.bIsExplosive)
                DamageRadius[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass.default.DamageRadius;
DamageRadius[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass.default.DamageRadius;
        }
}
    }
}
}
}


function GetHeadMultiplier(out float HeadMultiplier[2], optional int Index)
function GetHeadMultiplier(out float HeadMultiplier[2], optional int Index)
{
{
    local class<Projectile> P;
local class<Projectile> P;


    HeadMultiplier[Index] = 1.0;
HeadMultiplier[Index] = 1.0;
    if (Index == 0) GetHeadMultiplier(HeadMultiplier, 1);
if (Index == 0) GetHeadMultiplier(HeadMultiplier, 1);
   
    if (Fire[Index] != none)
if (Fire[Index] != none)
    {
{
        if (class<InstantFire>(Fire[Index]) != none) // HITSCAN
if (class<InstantFire>(Fire[Index]) != none) // HITSCAN
        {
{
            if (class<KFWeaponDamageType>(class<InstantFire>(Fire[Index]).default.DamageType) != none)
if (class<KFWeaponDamageType>(class<InstantFire>(Fire[Index]).default.DamageType) != none)
                HeadMultiplier[Index] = class<KFWeaponDamageType>(
HeadMultiplier[Index] = class<KFWeaponDamageType>(
                    class<InstantFire>(Fire[Index]).default.DamageType).default.HeadShotDamageMult;
class<InstantFire>(Fire[Index]).default.DamageType).default.HeadShotDamageMult;
        }
}
        else if (class<BaseProjectileFire>(Fire[Index]) != none) // PROJECTILE
else if (class<BaseProjectileFire>(Fire[Index]) != none) // PROJECTILE
        {
{
            P = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass;
P = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass;
           
 
            if (class<ShotgunBullet>(P) != none) // MOST PROJECTILES
if (P == class'CrossbuzzsawBlade') // Buzzsaw bow
                HeadMultiplier[Index] = class<ShotgunBullet>(P).default.HeadShotDamageMult;
HeadMultiplier[Index] = class<CrossbuzzsawBlade>(P).default.HeadShotDamageMult;
            else if (class<CrossbowArrow>(P) != none) // CROSSBOW
else if (P == class'CrossbowArrow') // CROSSBOW
                HeadMultiplier[Index] = class<CrossbowArrow>(P).default.HeadShotDamageMult;
HeadMultiplier[Index] = class<CrossbowArrow>(P).default.HeadShotDamageMult;
            else if (class<HuskGunProjectile>(P) != none) // HUSK GUN
else if (P == class'M99Bullet') // M99
                HeadMultiplier[Index] = class<HuskGunProjectile>(P).default.HeadShotDamageMult;
HeadMultiplier[Index] = class<M99Bullet>(P).default.HeadShotDamageMult;
            else if (class<M99Bullet>(P) != none) // M99
else if ( P.default.MyDamageType != none &&
                HeadMultiplier[Index] = class<M99Bullet>(P).default.HeadShotDamageMult;
class<KFWeaponDamageType>(P.default.MyDamageType) != none ) // ANY OTHER PROJECTILES USE DAMAGETYPE
        }
HeadMultiplier[Index] = class<KFWeaponDamageType>(P.default.MyDamageType).default.HeadShotDamageMult;
    }
}
else if (class<KFMeleeFire>(Fire[Index]) != none) // MELEE
{
if (class<DamTypeMelee>(class<KFMeleeFire>(Fire[Index]).default.hitDamageClass) != none)
HeadMultiplier[Index] = class<DamTypeMelee>(class<KFMeleeFire>(Fire[Index]).default.hitDamageClass).default.HeadShotDamageMult;
}
}
}
}


function GetPellets(out int Pellets[2], optional int Index)
function GetPellets(out int Pellets[2], optional int Index)
{
{
    Pellets[Index] = 0;
Pellets[Index] = 0;
    if (Index == 0) GetPellets(Pellets, 1);
if (Index == 0) GetPellets(Pellets, 1);


    if (Fire[Index] != none && class<KFShotgunFire>(Fire[Index]) != none)
if (Fire[Index] != none && class<KFShotgunFire>(Fire[Index]) != none)
    {
{
        if (IgnoresLoad(Fire[Index])) // (1) see note
if (IgnoresLoad(Fire[Index])) // (1) see note
            Pellets[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjPerFire;
Pellets[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjPerFire;
        else
else
        Pellets[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjPerFire *
Pellets[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjPerFire *
            class<BaseProjectileFire>(Fire[Index]).default.AmmoPerFire;
class<BaseProjectileFire>(Fire[Index]).default.AmmoPerFire;
    }
}
}
}


function GetMaxPens(out int MaxPens[2], optional int Index)
function GetMaxPens(out int MaxPens[2], optional int Index)
{
{
    local class<Projectile> P;
local class<Projectile> P;


    MaxPens[Index] = 0;
MaxPens[Index] = 0;
    if (Index == 0) GetMaxPens(MaxPens, 1);
if (Index == 0) GetMaxPens(MaxPens, 1);
   
    if (class<InstantFire>(Fire[Index]) != none) // HITSCAN
if (class<InstantFire>(Fire[Index]) != none) // HITSCAN
    {
{
        // (2) Deagle, MK23, 44Magnum
// (2) Deagle, MK23, 44Magnum
    }
}
    else if (class<BaseProjectileFire>(Fire[Index]) != none) // PROJECTILE
else if (class<BaseProjectileFire>(Fire[Index]) != none) // PROJECTILE
    {
{
        P = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass;
P = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass;
       
        if (class<ShotgunBullet>(P) != none)
if (class<ShotgunBullet>(P) != none)
            MaxPens[Index] = class<ShotgunBullet>(P).default.MaxPenetrations;
MaxPens[Index] = class<ShotgunBullet>(P).default.MaxPenetrations;
    }
}
}
}


function GetPenReduction(out float PenReduction[2], optional int Index)
function GetPenReduction(out float PenReduction[2], optional int Index)
{
{
    local class<Projectile> P;
local class<Projectile> P;
 
PenReduction[Index] = 0;
if (Index == 0) GetPenReduction(PenReduction, 1);
if (class<BaseProjectileFire>(Fire[Index]) != none)
{
P = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass;
if (class<ShotgunBullet>(P) != none)
PenReduction[Index] = class<ShotgunBullet>(P).default.PenDamageReduction;
}
}


    PenReduction[Index] = 0;
function GetRange(out float Range)
    if (Index == 0) GetPenReduction(PenReduction, 1);
{
   
if (class<KFMeleeFire>(Fire[0]) != none)
    if (class<BaseProjectileFire>(Fire[Index]) != none)
Range = class<KFMeleeFire>(Fire[0]).default.WeaponRange;
    {
else
        P = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass;
Range = 0;
        if (class<ShotgunBullet>(P) != none)
}
            PenReduction[Index] = class<ShotgunBullet>(P).default.PenDamageReduction;
 
    }
// Utilities
 
function bool IsTrueFire(class<WeaponFire> Fire)
{
if (Fire.Name == 'NoFire'
|| Fire.Name == 'ShotgunLightFire'
|| Fire.Name == 'SingleALTFire'
)
return false;
return true;
}
 
function bool IsMeleeFire(class<WeaponFire> Fire)
{
if (Fire == none)
return false;
if (class<KFMeleeFire>(Fire) != none)
return true;
return false;
}
 
function bool IgnoresLoad(class<WeaponFire> Fire)
{
if (class<MP7MAltFire>(Fire) != none
|| class<M7A3MAltFire>(Fire) != none)
return true;
return false;
}
 
function bool IsAutoReloadingWeapon(class<Weapon> Weapon) // (4)
{
if (Weapon == class'KFMod.Boomstick')
return true;
else if (Weapon == class'KFMod.M79GrenadeLauncher')
return true;
else if (Weapon == class'KFMod.Crossbow')
return true;
else if (Weapon == class'KFMod.LAW')
return true;
else if (Weapon == class'KFMod.M99SniperRifle')
return true;
else if (Weapon == class'KFMod.Crossbuzzsaw')
return true;
 
return false;
}
}


defaultproperties
defaultproperties
{
{
    GroupName="KFStatOutputMut"
GroupName="KFStatOutputMut"
    FriendlyName"KFStatOutput"
FriendlyName"KFStatOutput"
    Description="..."
Description="..."
}</pre></blockquote>
GameDifficulty=(1.0, 2.0, 4.0, 5.0, 7.0)
BountyMultiplier=(2.0, 1.0, 0.85, 0.65, 0.65)
HealthMultiplier=(0.5, 1.0, 1.35, 1.55, 1.75)
HeadHealthMultiplier=(0.5, 1.0, 1.35, 1.55, 1.75)
SpeedMultiplier=(0.95, 1.0, 1.15, 1.22, 1.3)
DamageMultiplier=(0.3, 1.0, 1.25, 1.50, 1.75)
AdditionalSpecimens=(class'KFChar.ZombieBoss')
}
{{End code}}

Latest revision as of 13:11, 23 November 2012

This is my stat generator for the killing floor weapon pages. --Benjamin (talk) 20:42, 11 October 2012 (EDT)

Result is stored here.

:

//============================================================================= // Weapon stat generator 08-November-2012 Benjamin // // KNOWN ISSUES // // * Cannot detect where different fire modes share the same // ammo (boomstick) // // * Cannot detect penetration for all weapons, must check manually. // // ===== INFO // // HEAD SHOTS // // Every weapon's head shot multiplier is stored in its associated damagetype // class, except (some of) those weapons whose projectiles implement // ProcessTouch themselves: CrossbowArrow, M99Bullet CrossbuzzsawBlade. These // projectiles have their own HeadShotDamageMult variable. // // NOTES // // (1) Certain classes don't use AmmoPerFire: MP7MAltFire, M7A3MAltFire // (2) Hardcoded penetration values: Deagle, MK23, 44Magnum, Crossbow, M99, // and Buzzsaw bow. Look in script files manually for these stats. // (3) Weapons only have a single ReloadRate stat (none for alt-fire) // (4) Weapons that auto-reload with a specific time: // // Hunting shotgun - ReloadCountDown stored in BoomStick.uc // M79 - FireRate (fire + reload are same animation) // Crossbow - FireRate (fire + reload are same animation) // LAW - FireRate (fire + reload are same animation) // M99 - FireRate (fire + reload are same animation) // Buzzsaw Bow - FireRate (fire + reload are same animation) //============================================================================= class KFStatOutputMut extends Mutator;

// Weapons var array< class<KFWeaponPickup> > AdditionalWeapons; var class<KFWeaponPickup> Pickup; var class<KFWeapon> Weapon; var class<WeaponFire> Fire[2]; var class<KFMonster> KFM;

// Specimens var array< class<KFMonster> > AdditionalSpecimens; var array<float> GameDifficulty; var array<float> BountyMultiplier; var array<float> HealthMultiplier; var array<float> HeadHealthMultiplier; var array<float> SpeedMultiplier; var array<float> DamageMultiplier;

function OutputStatNum(string Title, float Stat, optional float AltStat) { OutputStat(Title, string(Stat), string(AltStat)); }

function OutputStat(string Title, string Stat, optional string AltStat) { local int A, x;

// Adjust stat name Title $= ":"; A = Len(Title); if (A < 14) { for (x = 0; x < 14 - A; x++) Title $= " "; }

if (Fire[1] != none && AltStat != "") Log(Title $ Stat @ "(" $ AltStat $ ")"); else Log(Title $ Stat); }

function GenerateWeaponStats() { local string Name; local string Perk; local int Cost; local int AmmoCost; local int Weight; local int Capacity[2]; local int MagazineSize[2]; local float FireRate[2]; local float ReloadSpeed[2]; local float Spread[2]; local int Damage[2]; local float DamageRadius[2]; local float HeadMultiplier[2]; local int Pellets[2]; local int MaxPens[2]; local float PenReduction[2]; local float Range;

local int i, x; local array< class<Pickup> > WeaponList;

for (i = 0; i < ArrayCount(class'KFLevelRules'.default.ItemForSale); i++) if (class'KFLevelRules'.default.ItemForSale[i] != none) WeaponList[WeaponList.Length] = class'KFLevelRules'.default.ItemForSale[i];

for (i = 0; i < AdditionalWeapons.Length; i++) WeaponList[WeaponList.Length] = AdditionalWeapons[i];

// Main loop for (i = 0; i < WeaponList.Length; i++) { Pickup = class<KFWeaponPickup>(WeaponList[i]); if (Pickup != none) { Weapon = class<KFWeapon>(Pickup.default.InventoryType); if (Weapon != none) { for (x = 0; x < 2; x++) { if (Weapon.default.FireModeClass[x] != none && IsTrueFire(Weapon.default.FireModeClass[x])) Fire[x] = Weapon.default.FireModeClass[x]; else Fire[x] = none; }

GetName(Name); GetPerk(Perk); GetCost(Cost); GetAmmoCost(AmmoCost); GetWeight(Weight); GetCapacity(Capacity); GetMagazineSize(MagazineSize); GetFireRate(FireRate); GetReloadSpeed(ReloadSpeed); GetSpread(Spread); GetDamage(Damage); GetDamageRadius(DamageRadius); GetHeadMultiplier(HeadMultiplier); GetPellets(Pellets); GetMaxPens(MaxPens); GetPenReduction(PenReduction); GetRange(Range);

OutputStat("Name", Name); OutputStat("Perk", Perk); OutputStatNum("Cost", Cost); OutputStatNum("Weight", Weight); OutputStatNum("Ammo cost", AmmoCost); OutputStatNum("Capacity", Capacity[0], Capacity[1]); OutputStatNum("Magazine", MagazineSize[0], MagazineSize[1]); OutputStatNum("Damage", Damage[0], Damage[1]); OutputStatNum("Radius", DamageRadius[0], DamageRadius[0]); OutputStatNum("Head", HeadMultiplier[0], HeadMultiplier[1] ); OutputStatNum("Pellets", Pellets[0], Pellets[1]); OutputStatNum("Spread", Spread[0], Spread[1]); OutputStatNum("Max pens", MaxPens[0], MaxPens[1]); OutputStatNum("Pen reduc", PenReduction[0], PenReduction[1]); OutputStatNum("Fire rate", FireRate[0], FireRate[1]); OutputStatNum("Reload sp", ReloadSpeed[0], ReloadSpeed[1]); Log(""); } } }

Log(""); Log(""); }

function GenerateSpecimenStats() { local string Name; local int Bounty[5]; local int Health[5]; local float HeadHealth[5]; local float Speed[5]; local int Damage[5]; local float Range;

local KFGameType KF; local int i; local array < class<KFMonster> > Specimens;

KF = KFGameType(Level.Game);

Log("==========================================================="); Log("SPECIMEN STATS ============================================"); Log("===========================================================");

for (i = 0; i < KF.StandardMonsterClasses.Length; i++) Specimens[Specimens.Length] = class<KFMonster>(DynamicLoadObject( class'KFGameType'.default.StandardMonsterClasses[i].MClassName, class'class'));

for (i = 0; i < AdditionalSpecimens.Length; i++) Specimens[Specimens.Length] = AdditionalSpecimens[i];

for (i = 0; i < Specimens.Length; i++) { KFM = Specimens[i];

GetSpecimenName(Name); GetSpecimenBounty(Bounty); GetSpecimenHealth(Health); GetSpecimenHeadHealth(HeadHealth); GetSpecimenSpeed(Speed); GetSpecimenDamage(Damage); GetSpecimenRange(Range);

Log("Name: " $ Name); Log("Bounty: " $ Bounty[0] @ Bounty[1] @ Bounty[2] @ Bounty[3] @ Bounty[4]); Log("Health: " $ Health[0] @ Health[1] @ Health[2] @ Health[3] @ Health[4]); Log("HeadHealth: " $ HeadHealth[0] @ HeadHealth[1] @ HeadHealth[2] @ HeadHealth[3] @ HeadHealth[4]); Log("Speed: " $ Speed[0] @ Speed[1] @ Speed[2] @ Speed[3] @ Speed[4]); Log("Damage: " $ Damage[0] @ Damage[1] @ Damage[2] @ Damage[3] @ Damage[4]); Log("Range: " $ Range); Log(""); }

Log(""); }

function PostBeginPlay() { GenerateWeaponStats(); GenerateSpecimenStats(); }

/////////////////////////////////////////////////////////////////////////////// // SPECIMEN STAT CALCULATION ///////////////////////////////////////////////////////////////////////////////

function GetSpecimenName(out string Name) { Name = KFM.default.MenuName; }

function GetSpecimenBounty(out int KillScore[5]) { local int i;

for (i = 0; i < 5; i++) KillScore[i] = Max(1, int(KFM.default.ScoringValue * BountyMultiplier[i])); }

function GetSpecimenHealth(out int Health[5]) { local int i;

for (i = 0; i < 5; i++) Health[i] = Ceil(KFM.default.Health * HealthMultiplier[i]); }

function GetSpecimenHeadHealth(out float HeadHealth[5]) { local int i;

for (i = 0; i < 5; i++) HeadHealth[i] = Ceil(KFM.default.HeadHealth * HeadHealthMultiplier[i]); }

function GetSpecimenSpeed(out float Speed[5]) { local int i;

for (i = 0; i < 5; i++) Speed[i] = KFM.default.GroundSpeed * SpeedMultiplier[i]; }

function GetSpecimenDamage(out int Damage[5]) { local int i;

for (i = 0; i < 5; i++) Damage[i] = KFM.default.MeleeDamage * DamageMultiplier[i]; }

function GetSpecimenRange(out float Range) { Range = KFM.default.MeleeRange; }

/////////////////////////////////////////////////////////////////////////////// // WEAPON STAT CALCULATION ///////////////////////////////////////////////////////////////////////////////

function GetName(out string Name) { Name = Pickup.default.ItemName; }

function GetPerk(out string Perk) { Perk = KFGameType(Level.Game).default. LoadedSkills[Pickup.default.CorrespondingPerkIndex].default.VeterancyName; }

function GetCost(out int Cost) { Cost = Pickup.default.Cost; }

function GetAmmoCost(out int AmmoCost) { AmmoCost = Pickup.default.AmmoCost; }

function GetWeight(out int Weight) { Weight = Pickup.default.Weight; }

function GetCapacity(out int Capacity[2], optional int Index) { Capacity[Index] = 0; if (Index == 0) GetCapacity(Capacity, 1);

if (Fire[Index] != none && !IsMeleeFire(Fire[Index]) && Fire[Index].default.AmmoClass != none) Capacity[Index] = Fire[Index].default.AmmoClass.default.MaxAmmo; }

function GetMagazineSize(out int MagazineSize[2]) { MagazineSize[0] = Weapon.default.MagCapacity; MagazineSize[1] = 0; // Can't be obtained normally }

function GetFireRate(out float FireRate[2], optional int Index) { FireRate[Index] = 0; if (Index == 0) GetFirerate(FireRate, 1);

if (IsAutoReloadingWeapon(Weapon)) // (4) Weapons that fire-reload in one animation FireRate[Index] = 0; else { if (Fire[Index] != none) FireRate[Index] = Fire[Index].default.FireRate; } }

function GetReloadSpeed(out float ReloadSpeed[2], optional int Index) { ReloadSpeed[Index] = 0; if (Index == 0) GetReloadSpeed(ReloadSpeed, 1);

if (IsAutoReloadingWeapon(Weapon)) // (4) Weapons that fire-reload in one animation if (Fire[Index] != none) ReloadSpeed[Index] = Fire[Index].default.FireRate; else { if (Fire[Index] != none) ReloadSpeed[Index] = Weapon.default.ReloadRate; // (3) } }

function GetSpread(out float Spread[2], optional int Index) { Spread[Index] = 0; if (Index == 0) GetSpread(Spread, 1);

if (Fire[Index] != none) Spread[Index] = Fire[Index].default.Spread; }

function GetDamage(out int Damage[2], optional int Index) { Damage[Index] = 0; if (Index == 0) GetDamage(Damage, 1);

if (Fire[Index] != none) { if (class<InstantFire>(Fire[Index]) != none) // HITSCAN Damage[Index] = class<InstantFire>(Fire[Index]).default.DamageMax; else if(class<BaseProjectileFire>(Fire[Index]) != none) // PROJECTILE { if (class<MP7MMedicGun>(Weapon) != none) Damage[Index] = class<MP7MMedicGun>(Weapon).default.HealBoostAmount; else if (class<M7A3MMedicGun>(Weapon) != none) Damage[Index] = class<M7A3MMedicGun>(Weapon).default.HealBoostAmount; else Damage[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass.default.Damage; } else if (class<KFMeleeFire>(Fire[Index]) != none) // MELEE { Damage[Index] = class<KFMeleeFire>(Fire[Index]).default.MeleeDamage; } } }

function GetDamageRadius(out float DamageRadius[2], optional int Index) { local class<DamageType> DT;

DamageRadius[Index] = 0; if (Index == 0) GetDamageRadius(DamageRadius, 1);

if (Fire[Index] != none && class<BaseProjectileFire>(Fire[Index]) != none) { DT = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass.default.MyDamageType; if (class<KFWeaponDamageType>(DT) != none) { if (class<KFWeaponDamageType>(DT).default.bIsExplosive) DamageRadius[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass.default.DamageRadius; } } }

function GetHeadMultiplier(out float HeadMultiplier[2], optional int Index) { local class<Projectile> P;

HeadMultiplier[Index] = 1.0; if (Index == 0) GetHeadMultiplier(HeadMultiplier, 1);

if (Fire[Index] != none) { if (class<InstantFire>(Fire[Index]) != none) // HITSCAN { if (class<KFWeaponDamageType>(class<InstantFire>(Fire[Index]).default.DamageType) != none) HeadMultiplier[Index] = class<KFWeaponDamageType>( class<InstantFire>(Fire[Index]).default.DamageType).default.HeadShotDamageMult; } else if (class<BaseProjectileFire>(Fire[Index]) != none) // PROJECTILE { P = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass;

if (P == class'CrossbuzzsawBlade') // Buzzsaw bow HeadMultiplier[Index] = class<CrossbuzzsawBlade>(P).default.HeadShotDamageMult; else if (P == class'CrossbowArrow') // CROSSBOW HeadMultiplier[Index] = class<CrossbowArrow>(P).default.HeadShotDamageMult; else if (P == class'M99Bullet') // M99 HeadMultiplier[Index] = class<M99Bullet>(P).default.HeadShotDamageMult; else if ( P.default.MyDamageType != none && class<KFWeaponDamageType>(P.default.MyDamageType) != none ) // ANY OTHER PROJECTILES USE DAMAGETYPE HeadMultiplier[Index] = class<KFWeaponDamageType>(P.default.MyDamageType).default.HeadShotDamageMult; } else if (class<KFMeleeFire>(Fire[Index]) != none) // MELEE { if (class<DamTypeMelee>(class<KFMeleeFire>(Fire[Index]).default.hitDamageClass) != none) HeadMultiplier[Index] = class<DamTypeMelee>(class<KFMeleeFire>(Fire[Index]).default.hitDamageClass).default.HeadShotDamageMult; } } }

function GetPellets(out int Pellets[2], optional int Index) { Pellets[Index] = 0; if (Index == 0) GetPellets(Pellets, 1);

if (Fire[Index] != none && class<KFShotgunFire>(Fire[Index]) != none) { if (IgnoresLoad(Fire[Index])) // (1) see note Pellets[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjPerFire; else Pellets[Index] = class<BaseProjectileFire>(Fire[Index]).default.ProjPerFire * class<BaseProjectileFire>(Fire[Index]).default.AmmoPerFire; } }

function GetMaxPens(out int MaxPens[2], optional int Index) { local class<Projectile> P;

MaxPens[Index] = 0; if (Index == 0) GetMaxPens(MaxPens, 1);

if (class<InstantFire>(Fire[Index]) != none) // HITSCAN { // (2) Deagle, MK23, 44Magnum } else if (class<BaseProjectileFire>(Fire[Index]) != none) // PROJECTILE { P = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass;

if (class<ShotgunBullet>(P) != none) MaxPens[Index] = class<ShotgunBullet>(P).default.MaxPenetrations; } }

function GetPenReduction(out float PenReduction[2], optional int Index) { local class<Projectile> P;

PenReduction[Index] = 0; if (Index == 0) GetPenReduction(PenReduction, 1);

if (class<BaseProjectileFire>(Fire[Index]) != none) { P = class<BaseProjectileFire>(Fire[Index]).default.ProjectileClass; if (class<ShotgunBullet>(P) != none) PenReduction[Index] = class<ShotgunBullet>(P).default.PenDamageReduction; } }

function GetRange(out float Range) { if (class<KFMeleeFire>(Fire[0]) != none) Range = class<KFMeleeFire>(Fire[0]).default.WeaponRange; else Range = 0; }

// Utilities

function bool IsTrueFire(class<WeaponFire> Fire) { if (Fire.Name == 'NoFire' || Fire.Name == 'ShotgunLightFire' || Fire.Name == 'SingleALTFire' ) return false;

return true; }

function bool IsMeleeFire(class<WeaponFire> Fire) { if (Fire == none) return false; if (class<KFMeleeFire>(Fire) != none) return true; return false; }

function bool IgnoresLoad(class<WeaponFire> Fire) { if (class<MP7MAltFire>(Fire) != none || class<M7A3MAltFire>(Fire) != none)

return true;

return false; }

function bool IsAutoReloadingWeapon(class<Weapon> Weapon) // (4) { if (Weapon == class'KFMod.Boomstick') return true; else if (Weapon == class'KFMod.M79GrenadeLauncher') return true; else if (Weapon == class'KFMod.Crossbow') return true; else if (Weapon == class'KFMod.LAW') return true; else if (Weapon == class'KFMod.M99SniperRifle') return true; else if (Weapon == class'KFMod.Crossbuzzsaw') return true;

return false; }

defaultproperties { GroupName="KFStatOutputMut" FriendlyName"KFStatOutput" Description="..." GameDifficulty=(1.0, 2.0, 4.0, 5.0, 7.0) BountyMultiplier=(2.0, 1.0, 0.85, 0.65, 0.65) HealthMultiplier=(0.5, 1.0, 1.35, 1.55, 1.75) HeadHealthMultiplier=(0.5, 1.0, 1.35, 1.55, 1.75) SpeedMultiplier=(0.95, 1.0, 1.15, 1.22, 1.3) DamageMultiplier=(0.3, 1.0, 1.25, 1.50, 1.75) AdditionalSpecimens=(class'KFChar.ZombieBoss') }