KFStatOutput: Difference between revisions
(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) | ||
Result is stored [[Data:StatOutput1040|here]]. | |||
// Weapon stat generator | |||
{{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 | ||
// | // | ||
// | // 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 | // 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, | ||
// | // 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 | 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 | function GetSpecimenHealth(out int Health[5]) | ||
{ | { | ||
local int i; | |||
for (i = 0; i < 5; i++) | |||
Health[i] = Ceil(KFM.default.Health * HealthMultiplier[i]); | |||
} | } | ||
function | function GetSpecimenHeadHealth(out float HeadHealth[5]) | ||
{ | { | ||
local int i; | |||
for (i = 0; i < 5; i++) | |||
HeadHealth[i] = Ceil(KFM.default.HeadHealth * HeadHealthMultiplier[i]); | |||
} | } | ||
function | function GetSpecimenSpeed(out float Speed[5]) | ||
{ | { | ||
local int i; | |||
for (i = 0; i < 5; i++) | |||
Speed[i] = KFM.default.GroundSpeed * SpeedMultiplier[i]; | |||
} | } | ||
function | function GetSpecimenDamage(out int Damage[5]) | ||
{ | { | ||
local int i; | |||
for (i = 0; i < 5; i++) | |||
Damage[i] = KFM.default.MeleeDamage * DamageMultiplier[i]; | |||
} | } | ||
function | function GetSpecimenRange(out float Range) | ||
{ | { | ||
Range = KFM.default.MeleeRange; | |||
} | } | ||
/////////////////////////////////////////////////////////////////////////////// | /////////////////////////////////////////////////////////////////////////////// | ||
// STAT CALCULATION | // WEAPON STAT CALCULATION | ||
/////////////////////////////////////////////////////////////////////////////// | /////////////////////////////////////////////////////////////////////////////// | ||
function GetName(out string Name) | 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) | function GetCost(out int 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; | |||
} | } | ||
function GetCapacity(out int Capacity[2], optional int Index) | 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]) | 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) | 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) | 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) | 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) | 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) | 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) | 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) | 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) | 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) | 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 | 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') | |||
} | |||
{{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') }