Lots of progress on the treasure distribution. I’ve tweaked the XP1 Treasure system so it only needs module-wide containers and pulls from four rarities: Common, Uncommon, Rare, and Very Rare.
The rarities I’ve pulled from a 5th Edition PDF on the WotC website that lists rarities for magical items. This document gives a good baseline to work from. Its available from https://media.wizards.com/2014/downloads/dnd/MagicItemsRarity.pdf
The rarities I’ve defined in the library include correspond directly to the XP1 Type Values as follows:
- Common = Low
- Uncommon = Medium
- Rare = High
- Very Rare = Unique
I could have used the original constants but I wanted the variable names to directly correlate to the rarity of the item.
I can also use the original XP1 Treasure System if I so desire as all the changes I’ve made are self-contained into their own library include.
The tweaked system uses one script in the OnOpen/OnDeath event of the container:
#include "tr_i0_treasure"
void main()
{
//prevent duplicate treasure generation
if (CTG_GetIsTreasureGenerated(OBJECT_SELF))
return;
object oPC = GetLastOpener();
//set the items in the container
int i;
int iMax = tr_GetNumItems(oPC);
int nRoll;
int nHasRare = 0;
int nType;
for (i = 0; i < iMax; i++)
{
nRoll = d100();
if (nHasRare == 1 && nRoll > 95)
{
nRoll = 90;
}
if(nRoll > 95)
{
nType = TREASURE_TYPE_VERYRARE;
nHasRare = 1;
}
else if(nRoll > 85)
nType = TREASURE_TYPE_RARE;
else if(nRoll > 60)
nType = TREASURE_TYPE_UNCOMMON;
else
nType = TREASURE_TYPE_COMMON;
tr_CreateTreasure(nType, GetLastOpener(), OBJECT_SELF);
}
//mark to prevent duplicate treasure generation
CTG_SetIsTreasureGenerated(OBJECT_SELF);
}
The modified library functions are:
/*
Pstemarie: 11-30-2019
Treasure Include Library
*/
#include "x0_i0_treasure"
//------------------------------------------------------------------------------
// DEFINE CONSTANTS
//constants for treasure categories - use in place of those defined in x0_i0_treasure
const int TREASURE_TYPE_COMMON = 1;
const int TREASURE_TYPE_UNCOMMON = 2;
const int TREASURE_TYPE_RARE = 3;
const int TREASURE_TYPE_VERYRARE = 4;
//const int TREASURE_TYPE_MONSTER = 5; //defined in x0_i0_treasure
//------------------------------------------------------------------------------
// FUNCTION DEFINITIONS
// * Gets the number of items in the treasure container
int tr_GetNumItems(object oPC);
// * Create random treasure item of the appropriate type in the specified
// * container.
// *
// * Possible values for nTreasureType:
// * TREASURE_TYPE_COMMON
// * TREASURE_TYPE_UNCOMMON
// * TREASURE_TYPE_RARE
// * TREASURE_TYPE_VERYRARE
// * TREASURE_TYPE_MONSTER
void tr_CreateTreasure(int nType, object oPC, object oCont = OBJECT_SELF);
//------------------------------------------------------------------------------
// FUNCTION IMPLEMENTATION
int tr_GetNumItems(object oPC)
{
int nNum = d3();
int nLevel = GetHitDice(oPC);
switch (nLevel)
{
case 1:
case 2:
case 3:
case 4:
case 5:
//use the default value of d3()
break;
case 6:
case 7:
case 8:
nNum = nNum+1; //from 2-4 items
break;
case 9:
case 10:
nNum = d4()+1; //from 2-5 items
break;
case 11:
case 12:
case 13:
nNum = nNum+d3(); //from 2-6 items
break;
case 14:
case 15:
case 16:
nNum = nNum+d4(); //from 2-7 items
break;
default:
nNum = nNum+d4()+1; //from 3-8 items
break;
}
return nNum;
}
void tr_CreateSpecificBaseTypeTreasure(int nTreasureType, object oPC, object oCont, int nBaseType1=BASE_ITEM_INVALID, int nBaseType2=BASE_ITEM_INVALID, int nBaseType3=BASE_ITEM_INVALID)
{
// Locate the base container
object oBaseCont = CTG_GetNearestBaseContainer(nTreasureType, oCont);
// Make sure we have a valid base container
if (!GetIsObjectValid(oBaseCont))
{
// if not, generate treasure using default method
if (nBaseType1 == BASE_ITEM_BOOK || nBaseType1 == BASE_ITEM_SPELLSCROLL)
{
// Make book treasure
GenerateBookTreasure(oPC, oCont);
}
else
{
// Generate default treasure
CTG_CreateDefaultTreasure(nTreasureType, oPC, oCont);
}
return;
}
int nItemsCreated = 0;
object oItem = OBJECT_INVALID;
// Keep track of items handed out to avoid dupes
object oItem1 = OBJECT_INVALID;
object oItem2 = OBJECT_INVALID;
// Get the number of available items
int nItemsInBaseCont = CTG_GetNumItemsInBaseContainer(oBaseCont);
// Random number -- position of item to hand out
int nRandom = 0;
if (nBaseType1 == BASE_ITEM_INVALID && CTG_IsItemGold() )
{
CTG_CreateGoldTreasure(nTreasureType, oPC, oCont);
}
else
{
nRandom = Random(nItemsInBaseCont);
if (nBaseType1 == BASE_ITEM_INVALID)
{
// we're not checking base types
oItem = CTG_GetTreasureItem(oBaseCont, nRandom);
}
else
{
oItem = CTG_GetSpecificBaseTypeTreasureItem(oBaseCont, nRandom, nBaseType1, nBaseType2, nBaseType3);
}
if (!GetIsObjectValid(oItem))
{
CTG_CreateDefaultTreasure(nTreasureType, oPC, oCont);
}
else if ( nItemsCreated > 1 && ( GetTag(oItem) == GetTag(oItem1) || GetTag(oItem) == GetTag(oItem2) ) )
{
//Make gold instead
CTG_CreateGoldTreasure(nTreasureType, oPC, oCont);
}
else
{
// Make the item
CopyItem(oItem, oCont);
if (nItemsCreated == 1)
{
oItem1 = oItem;
}
else
{
// if this is the third item, it doesn't matter
// anyway, so we might as well save the conditional.
oItem2 = oItem;
}
}
}
}
void tr_CreateTreasure(int nType, object oPC, object oCont = OBJECT_SELF)
{
tr_CreateSpecificBaseTypeTreasure(nType, oPC, oCont);
}
What I’ve done with tr_CreateSpecificBaseTypeTreasure() is eliminate the random number of pulls BioWare used in the original function CTG_CreateSpecificBaseTypeTreasure(). This allows me to have a chest pull more than the maximum of 3 items - as defined in BioWare’s system - and allows for each item to possibly be pulled from a different rarity. Thus a chest could contain 1 common, 1 uncommon, and 1 very rare item as opposed to 3 items of all the same rarity.