hello again,
things didn't turn out to be quite as simple as I'd hoped; you see, the curve tends to lay down along the x-axis as Levels (
y) get higher and higher. Hence low rolls (
z - high value) were dropping an inordinate amount of Rogue Stones (the highest value gem). So it took a while to introduce a suitable parameter to raise that part of the curve up off the x-axis.
[b]x=(cos(y)^(1/z))+(100z/(y^a))[/b]
where
y is the level of difficulty
z is a random input (dice roll) from 0.0001 to 1.0000
and
x is the weighted output, from 0.0 to 1.0+
Here's the complete script:
// kL_Loot
// by kevL's, 2010 Dec 20
// - rudimentary Treasure system, to spawn in gems for use in
// crafting with TCC (the Complete Craftsman) playing OC + MotB.
// - insert into creatures' OnSpawn.
void CreateLoot(string sLoot)
{ CreateItemOnObject(sLoot);
}
void main()
{ // 25% chance of Loot
if (d4() > 1) return;
// dice roll, low is good
int iGemRoll = Random(10000) + 1;
float fGemRoll = IntToFloat(iGemRoll);
float fGemModif = fGemRoll / 10000.0f;
// Character Level (for single-player only)
int iCharLevel = GetHitDice(GetFirstPC(TRUE));
// Monster Level
int iMonstLevel = GetHitDice(OBJECT_SELF);
// doubling the Monster Level makes it contribute more to the weight
iMonstLevel *= 2;
// Effective Level, with some arbitrary adjustments
int iEffLevel = (iMonstLevel + iCharLevel) / 2;
// +d20 helps low levels get those special drops
iEffLevel = iEffLevel + 10 + d20();
// don't ever let the cosine go below 0 or to 90;
// values of 12 to 75 preserve moderation
if (iEffLevel < 12) iEffLevel = 12;
else if (iEffLevel > 75) iEffLevel = 75;
float fEffLevel = IntToFloat(iEffLevel);
// 'Availability' - raising and lowering this adjusts extremes;
// increasing it means increasing very high-value drops and lowering
// the lowest-value drops (while maintaining a proper overall weighting),
// while lowering this value might make the highest-value drops impossible and
// very low-value drops common (regardless of the cosine angle).
// It's similar to the cosine angle below, but useful for fine tuning.
// Think of it as the availability of better items. nb, the end result
// will never go below zero but a low fAvail will sent the curve above 1.0
// - adjust in tenths of a point (very sensitive)
float fAvail = 2.0f;
// As the cosine angle, y, increases so does the chance of better items;
// when it's very high, there's no chance of a low-value drop.
// When it's low, there's still a very slight chance of a better or best item.
// - a low roll gives a low result and should be interpreted as high value items!
// (cos(y)^(1/z))+(100z/(y^a))
float fGemType = (10000.0f * ((pow(cos(fEffLevel), 1.0f / fGemModif)) + (100.0f * fGemModif / (pow(fEffLevel, fAvail)))));
int iGemType = FloatToInt(fGemType);
// Loot table of gems, based strictly on GP value out of 9562 total
string sLoot;
if (iGemType < 7) sLoot = "cft_gem_13"; // Rogue Stone
else if (iGemType < 14) sLoot = "cft_gem_12"; // Blue Diamond
else if (iGemType < 22) sLoot = "cft_gem_15"; // King's Tear
else if (iGemType < 31) sLoot = "cft_gem_10"; // Star Sapphire
else if (iGemType < 41) sLoot = "cft_gem_14"; // Beljuril
else if (iGemType < 51) sLoot = "cft_gem_09"; // Canary Diamond
else if (iGemType < 69) sLoot = "cft_gem_11"; // Jacinth
else if (iGemType < 100) sLoot = "nw_it_gem005"; // Diamond
else if (iGemType < 135) sLoot = "nw_it_gem008"; // Sapphire
else if (iGemType < 172) sLoot = "nw_it_gem012"; // Emerald
else if (iGemType < 214) sLoot = "nw_it_gem009"; // Fire Opal
else if (iGemType < 260) sLoot = "nw_it_gem006"; // Ruby
else if (iGemType < 312) sLoot = "nw_it_gem010"; // Topaz
else if (iGemType < 374) sLoot = "nw_it_gem013"; // Alexandrite
else if (iGemType < 478) sLoot = "nw_it_gem011"; // Garnet
else if (iGemType < 634) sLoot = "nw_it_gem001"; // Greenstone
else if (iGemType < 842) sLoot = "nw_it_gem004"; // Phenalope
else if (iGemType < 1091) sLoot = "nw_it_gem014"; // Aventurine
else if (iGemType < 1403) sLoot = "nw_it_gem003"; // Amethyst
else if (iGemType < 2027) sLoot = "nw_it_gem002"; // Fire Agate
else if (iGemType < 2807) sLoot = "cft_gem_01"; // Bloodstone
else if (iGemType < 4366) sLoot = "nw_it_gem015"; // Fluorspar
else if (iGemType < 6445) sLoot = "cft_gem_03"; // Obsidian
else sLoot = "nw_it_gem007"; // Malachite
DelayCommand(0.1f, CreateLoot(sLoot));
}
As you can see it takes a 10,000 sided die-roll (
z) and weights the result by one-half the result of twice the monster-level plus the character-level (further, adds 10 plus d20) to get the Effective Level
y; that's the cosine angle, restricted between 12 and 75 degrees. The variable
a, Availability, is there to lift the curve off the x-axis, especially at low
z, & changing it will vary the availability of the highest & lowest value drops preferentially. It doesn't so much change the weighting as make it possible or impossible to get extreme results. Sorry, I can't describe Availability well, at all : it's icing on the steak, the seasoning on a cake!! wait a sec ..
If you limit the high angle on the cosine, it limits how much the table gets weighted towards the better drops (lowers the maximum value of the weighting). And when you increase the availability it increases the availability of high value drops, without significantly changing the weighting of the table (decreasing availability decreases the possibility of high value drops). Notice well, however, that this whole system is predicated on the notion that a low roll gives a high value item, and vice versa. Lowering the minimum cosine below, say, 10 puts the roll on a really lousy curve, while 90 would mean you always get the highest valued drop.
Using this system you could theoretically use a drop table with equal probability for all items and simply adjust the function in order to keep the value of your drops proportional to monster and character levels (although I don't recommend it). A good method to test tables and weightings is to use a crate and put your script into the OnOpen event, making necessary changes like assigning an integer or random integer for monster & PC levels. A conditional loop will fill the crate quickly, to see the distribution of items.
So far the script works sweet, with low levels getting lots of malachite, and higher levels getting a more even distribution of gems throughout. Further tweaking has to be done with actual playing (when I next run through the OC + MotB). The goal is to have to make decisions on what to craft with a well-rounded TCC, which I'm modifying and expanding to +8 items (etc) for 20+ level characters. Later I'll be working OC Essences into this script as well.
If you'd like to use this for your module or adapt it for your PW, hey great! Feel free to change the table to your own custom drops or further modify the function. I'm not a stickler for credit (Lord knows we've all had stuff stolen by the wind ..)
p.s. Thanks to Ivan Johansen's
Graph 4.3As a very final note I'd just like to say that that table is the screwiest thing I've ever worked with; the odds of the highest to lowest item (gem) are about 1:1500. The beauty of it is, if the function can be made to work with disparities like that, Let's Party!