KFStatOutput

From Tripwire Interactive Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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') }