This is basically a copy-and-paste from an email I sent Chris the other day, with some questions from me afterward:
-----------------
One of the most useful pieces of feedback I think we've gotten on the shield-network thing thus far is that people really don't value most of the bonus ship types. It's not really surprising, given the whole middle-of-a-beta thing, but regardless of what happens with the shield-network, I'd like to really nail this particular problem soon. Some of it is undoubtedly just a matter of user education and/or tastes, but I think a lot of it is that much of the game still hasn't recovered from the various "Balance Destroyer" changes we've made over the past months.
I've been thinking about this topic for a while, and I'd like to approach it in a more systematic way than we (or at least I) have done so in the past. It may be a harebrained scheme, dunno, but it seems like it would have more inherent sanity-check and get much closer to an "apples and apples" comparison between the different fleet ship types. Not that any system could really make it apples-and-apples without stripping out practically all the special mechanics, etc.
Anyway, I've thought through and done numeric models of a few different approaches, and the best one I've tried is inspired by the World of Warcraft "itemization" system. In that system, each discrete stat or property that an item can have (+health, +spellDamage, +hitRating, etc) has a value in "item levels", and each item has an iLvl that is the sum of its properties' values. They then go on to say that you can only use a green item with iLvl < (your level + 10), or a blue item with iLvl < (your level + 20), etc. I'm sure I'm off on the numbers there and of course the iLvls detach from player level when they get into the endgame content of each expansion (though the mechanism is maintained for establishing which "tier" an item is in).
So that's the kind of model I've been hacking at between excel and VS (to get the game spitting out the desired csv files, probably pretty similar to the old secondary exports) a good part of today. I'm certain that the actual functions I'm using to compute the "point value" of a value for each stat is way the waaay off, the first thing is to try to nail down a framework that makes any kind of sense and then iterate refinements to the actual computations. It may be that such a framework is not achievable or at least that mine is too flawed to seriously try, dunno.
Very important is that we don't need to go _nearly_ as far as WoW took things, since our main goal right now is to just get all the types into the same ballpark of usefulness. So we can keep things really simple and ignore tons of stuff and still reach an acceptable level of balance. The stats I'm currently tracking are:
Survivability
1) Cap Health. Meaning MaxHealth * EffectiveShipCap (this is all on High Caps, since that's the numeric starting point).
2) Armor Rating
Ease of Positioning
3) Move Speed
4) Attack Range
Ease of Support
5) Cap Metal + Crystal Cost
6) Cap Energy Cost
Stopping Power
7) Armor Piercing
Cap DPS Vs Scout Hull Type
9) Cap DPS Vs Light Hull Type
10) Cap DPS Vs Medium Hull Type
11) Cap DPS Vs Heavy Hull Type
12) Cap DPS Vs Artillery Hull Type
13) Cap DPS Vs Neutron Hull Type
14) Cap DPS Vs Swarmer Hull Type
15) Cap DPS Vs UltraLight Hull Type
16) Cap DPS Vs CloseCombat Hull Type
17) Cap DPS Vs CommandGrade Hull Type
18) Cap DPS Vs Refractive Hull Type
19) Cap DPS Vs Composite Hull Type
20) Cap DPS Vs Turret Hull Type
21) Cap DPS Vs UltraHeavy Hull Type
22) Cap DPS Vs Structural Hull Type
23) Cap DPS Vs Polycrystal Hull Type
Having one dps stat per hull type is more cumbersome than I'd like, but I've been a through several approaches on that and it's the most transparent way I can think of handling it. Certainly couldn't have pulled this off before hull types, wow.
So this leaves out tons of stuff, like:
- Specific values for attack power, seconds-per-salvo, shots-per-salvo, or attack bonuses.
- Ratio of metal to crystal.
- Engine health (infinite engine health would be a separate property).
- Whether ship cap is large or small; the relevant stats compute based on total-at-cap.
Not that those don't impact balance, we just don't need to be that fine-grained with this.
I'm also ignoring knowledge cost since all bonus types have the same cost for mk2 and mk3. If we started comparing turrets and/or starships obviously knowledge cost comes into play.
Only mkI ships are compared, it's assumed that higher mark versions have a linear progression of the relevant stats and that if the mkIs are all balanced the higher ones will at least not be too far off.
Also, for my first pass I'm only balancing those fleet ships that don't have any serious special feature, they're just there for dps. Meaning no cloaking, no teleporting, no debuff/buff, no self-attrition, no kamikaze, etc. If we can sort this out for the "just dps" ships we can work out towards those more exotic cases. The types I'm looking at now are:
AcidSprayer
AntiArmorShip
ArmorShip
Bomber
BulletproofFighter
Fighter
LazerGatling
MicroFighter
MissileShip
MLRS
Raider
SentinelFrigate
Sniper
SpaceTank
ZenithBombardment
ZenithChameleon
ZenithElectricBomber
Not even all those are pure vanilla (obviously the game isn't going to have many of those, it wouldn't be very interesting), but close enough for my purposes.
So, here's the current point cost computations in my working copy:
private static int GetPickCostForStatValue( BalanceCategory BalanceCategory, int StatValue )
{
switch ( BalanceCategory )
{
case BalanceCategory.CapHealth:
if ( StatValue <= 250000 )
return 0;
return ( StatValue - 250000 ) / 25000;
case BalanceCategory.ArmorRating:
return StatValue / 10;
case BalanceCategory.ArmorPiercing:
if ( StatValue > 20000 )
return 250;
if ( StatValue > 5000 )
return 125;
return StatValue / 20;
case BalanceCategory.MoveSpeed:
if ( StatValue <= 22 )
return 0;
return ( StatValue - 22 ) * 10;
case BalanceCategory.CapMetalCrystalCost:
if ( StatValue <= 4000 ) // 10 m + 10 c
return 300;
else if ( StatValue <= 8000 )
return 260;
else if ( StatValue <= 12000 )
return 240;
else if ( StatValue <= 16000 )
return 230;
else if ( StatValue <= 20000 )
return 220;
else if ( StatValue <= 26000 )
return 210;
else if ( StatValue <= 30000 )
return 200;
else if ( StatValue <= 40000 ) // 100 m + 100 c
return 180;
else if ( StatValue <= 200000 ) // 500 m + 500 c
return 100 + ( ( -( StatValue - 200000 ) ) / 2500 );
else if ( StatValue <= 500000 ) // 1250 m + 1250 c
return ( ( -( StatValue - 500000 ) ) / 3100 );
else
return 0;
case BalanceCategory.CapEnergyCost:
if ( StatValue <= 2000 ) // 10 e
return 300;
else if ( StatValue <= 4000 ) // 20 e
return 250;
else if ( StatValue <= 6000 ) // 30 e
return 200;
else if ( StatValue <= 8000 ) // 40 e
return 170;
else if ( StatValue <= 10000 ) // 50 e
return 140;
else if ( StatValue <= 15000 ) // 75 e
return 120;
else if ( StatValue <= 20000 ) // 100 e
return 100;
else if ( StatValue <= 30000 ) // 150 e
return 80;
else if ( StatValue <= 40000 ) // 200 e
return 60;
else if ( StatValue <= 50000 )
return 50;
else if ( StatValue <= 60000 )
return 40;
else if ( StatValue <= 70000 )
return 30;
else if ( StatValue <= 80000 )
return 20;
else if ( StatValue <= 90000 )
return 10;
else if ( StatValue <= 100000 )
return 0;
else
return 0;
case BalanceCategory.AttackRange:
if ( StatValue <= 0 ) // melee
return -50;
else if ( StatValue <= 2000 )
return 0;
else if ( StatValue <= 3000 )
return 5;
else if ( StatValue <= 4000 )
return 10;
else if ( StatValue <= 5500 )
return 20;
else if ( StatValue <= 7000 )
return 30;
else if ( StatValue <= 8500 )
return 40;
else if ( StatValue <= 10000 )
return 50;
else if ( StatValue <= 20000 )
return 75;
else if ( StatValue <= 30000 )
return 100;
else if ( StatValue <= 40000 )
return 110;
else // sniper
return 250;
case BalanceCategory.CapDPSVs_Scout:
case BalanceCategory.CapDPSVs_CommandGrade:
return StatValue / 2000;
case BalanceCategory.CapDPSVs_Light:
case BalanceCategory.CapDPSVs_Medium:
case BalanceCategory.CapDPSVs_Heavy:
case BalanceCategory.CapDPSVs_Artillery:
case BalanceCategory.CapDPSVs_Neutron:
case BalanceCategory.CapDPSVs_Swarmer:
case BalanceCategory.CapDPSVs_UltraLight:
case BalanceCategory.CapDPSVs_CloseCombat:
case BalanceCategory.CapDPSVs_Refractive:
case BalanceCategory.CapDPSVs_Composite:
case BalanceCategory.CapDPSVs_Turret:
case BalanceCategory.CapDPSVs_UltraHeavy:
case BalanceCategory.CapDPSVs_Structural:
case BalanceCategory.CapDPSVs_Polycrystal:
return StatValue / 1000;
}
return 0;
}
Like I said, I'm sure those numbers are way the waaay off
And I've attached the spreadsheet of the data output from that for the aforementioned types (actually all the other types are in there too, but I've got it filtered). It's interesting to note that even as bad as my first-guess algorithms are, it seems pretty clear that the LazerGatling and the ZenithChameleon are not playing by the same rules balance wise
-----------------
Anyway, my questions for y'all are:
1) Do you think this framework has a chance of being useful in achieving all-bonus-types-in-the-same-ballpark-of-usefulness? Obviously the more exotic ships require additional consideration, etc.
2) Assuming the answer to 1 is "yes", do you have suggestions on the computation of "pick cost" for each stat? For instance:
- The various dps-vs-hull stats need different valuation because some are much more useful to be able to kill than others, etc, but some (like UltraHeavy) also have tons more hp in general.
- Speaking of that, the value of the health stat should probably vary by the ship's own hull type; having 10 million hp as an ultra-light is very different than 10 million hp as ultra-heavy.
- The energy and m+c cost stats could stand to be less step-wise, certainly. We can consider logarithmic functions and other such fun stuff if it seems necessary; it's not like this is being computed in the middle of a battle
- Range perhaps should simply multiply the total cost of the damage stats rather than having its own stat. This might apply to other things too (like armor rating multiplying the cost of health).
- In general, the relative weighting between stats is just way off
- Etc; these certainly aren't me saying "this is how important this stat is, period", they're just first guesses to get some kind of example output from the framework.
This certainly would not be our only tool for finding a good balance, but it is helpful for spotting obvious outliers (hello, ZenithChameleon?) and for having some kind of objective-scalar baseline when anecdotal feedback from players can vary from "it's massively OP" to "it's massively UP"
Note: the attached .xls file has the filter and sorting already applied and should be readable through either Office or OpenOffice; but if you want a truly opens-anywhere file the csv is provided though it doesn't have the filter or sort applied.