1435 lines
53 KiB
Plaintext
1435 lines
53 KiB
Plaintext
//:://////////////////////////////////////////////////
|
|
//:: X0_I0_TREASURE
|
|
//:: Modified by Shayan on Jan/05/2005
|
|
//Include library for XP1 treasure generation system.
|
|
|
|
//--------------------------------------------------
|
|
|
|
//MODIFICATIONS
|
|
//:: Modifications: When asked to generate treasure for a creature, the script
|
|
//:: now takes into account the creature's Challenge rating and then rolls for
|
|
//:: either an item from unique or high or medium or low or even the junk chest. The percentage
|
|
//:: chance of getting items from each of these chests can be varied by adjusting
|
|
//:: the constant ints below.
|
|
|
|
|
|
//:: SETTING UP THIS SCRIPT
|
|
//
|
|
// Step 1: Import in and override (If you can read this then you've done it *Duh!*).
|
|
//
|
|
//
|
|
// Step 2: Create an area -inaccessible to players- with 5 chests.
|
|
// Change the tags of the chests to: X0_MOD_TREASURE_JUNK, X0_MOD_TREASURE_LOW, X0_MOD_TREASURE_MEDIUM
|
|
// X0_MOD_TREASURE_HIGH, and X0_MOD_TREASURE_UNIQUE.
|
|
//
|
|
// Step 3: Give all the chests an inventory, and fill them up with items that you want generated in random treasure
|
|
// generation. Make sure that you put appropriate items in appropriate chests. IE: Put items of little value in
|
|
// the junk chest, and items of great significance in the Unique chest.
|
|
//
|
|
// Step 4: Test out the treasure system. If you don't like the percentage of generation you can always change the values below.
|
|
//
|
|
//
|
|
|
|
|
|
//::
|
|
//:: Steps in treasure generation for a monster (with this script):
|
|
//::
|
|
//:: Step 1:
|
|
//:: Rolls a dice with 100 faces to see if a value of greater than BK_CHANCE_OF_N0_MONSTERTREASURE (refer to below)
|
|
//:: comes up. If not then it stops and no random treasure is generated.
|
|
//::
|
|
//:: Step 2:
|
|
//:: Checks for any area specific chest, if there is one then treasure is generated from there (Note: This is generated
|
|
//:: using BioWare's random treasure script). If there is no area specific chest, then it will look in the module for a
|
|
//:: Module wide monster chest (Note: This is generated using BioWare's random treasure script).
|
|
//::
|
|
//:: Step 3:
|
|
//:: Now if there aren't any monster specific or base area chest, then the treasure system looks for any general Module wide chests.
|
|
//:: HERE is where this script modification comes into work.
|
|
//:: - Assuming you have setup the system appropriately, there are 5 chests: Junk, Low, Medium, High and Unique.
|
|
//:: - Now the script takes into account the creature's challenge rating and rolls against the values as explained by
|
|
//:: Probablity of generation (see below).
|
|
//:: - In a nutshell, Creatures with high Challenge Rating (or CR) will have a better chance of spawning Unique item(s).
|
|
//:: Creatures with low CR will most likely spawn items from the junk,low or medium chest (it all depends on the dice rolls)
|
|
//:: - Essentially the higher the CR the better the items.
|
|
//::
|
|
|
|
//:: NOTE: Items in the "Unique" chest will/may spawn again. Unique does not mean once only. There is no database use in this script
|
|
// hence nothing is ever persistent. Unique chest is simply a chest that should ideally contain items of higher 'value' than
|
|
// the junk, low, medium, or high chests.
|
|
|
|
|
|
|
|
//:: Formula used for generation is explained in the appropriate method.
|
|
//:: Never fear... all other functions of the treasure script works as per normal
|
|
//:: IE: Generating a specific treasure from a chest, etc.
|
|
|
|
|
|
//
|
|
//:: Probability of Generation for Item from chest (Unique, High, Medium, or Low)
|
|
|
|
|
|
// XXXXX_SPAWN_MAX_PERCENTAGE
|
|
// = --------------------------------------------- X (Creature's challenge rating)^2
|
|
// (XXXXX_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR)^2)
|
|
|
|
|
|
//::NOTE: XXXX_SPAWN_MAX_PERCENTAGE and XXXX_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR refers to the constants below.
|
|
|
|
|
|
const int BK_CHANCE_OF_N0_MONSTERTREASURE = 65;
|
|
|
|
const float LOW_SPAWN_MAX_PERCENTAGE = 100.0;
|
|
const float LOW_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR = 60.0;
|
|
|
|
const float MEDIUM_SPAWN_MAX_PERCENTAGE = 100.0;
|
|
const float MEDIUM_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR = 80.0;
|
|
|
|
|
|
const float HIGH_SPAWN_MAX_PERCENTAGE = 100.0;
|
|
const float HIGH_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR = 100.0;
|
|
|
|
const float UNIQUE_SPAWN_MAX_PERCENTAGE = 100.0;
|
|
const float UNIQUE_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR = 200.0;
|
|
|
|
|
|
//NOTE: There is no adjustable value for the Junk chest. If all else fails, an item from the junk chest will be generated atleast.
|
|
|
|
|
|
|
|
// Example: Take a creature with CR 20. Given that it passes the Step 1 of the treasure generation phase,
|
|
// and there are no area specfic and base area chests, then:
|
|
// 100
|
|
// The chances of it spawning with item(s) from the 'Low' chest is: ------- X 20^2 = 11.1%
|
|
// 60^2
|
|
//
|
|
// 100
|
|
// The chances of it spawning with item(s) from the 'Medium' chest is: ------- X 20^2 = 6.25%
|
|
// 80^2
|
|
//
|
|
// 100
|
|
// The chances of it spawning with item(s) from the 'High' chest is: ------- X 20^2 = 4%
|
|
// 100^2
|
|
//
|
|
// 100
|
|
// The chances of it spawning with item(s) from the 'Unique' chest is: ------- X 20^2 = 0.05%
|
|
// 100^2
|
|
|
|
|
|
//So in this case the creature is most likely to spawn with an item from the Junk chest as the chances of it getting any item from the other
|
|
//chests are very low.
|
|
|
|
|
|
// Example 2: Take a creature with CR 40. Given that it passes the Step 1 of the treasure generation phase,
|
|
// and there are no area specfic and base area chests, then:
|
|
// 100
|
|
// The chances of it spawning with item(s) from the 'Low' chest is: ------- X 40^2 = 44.4%
|
|
// 60^2
|
|
//
|
|
// 100
|
|
// The chances of it spawning with item(s) from the 'Medium' chest is: ------- X 40^2 = 25%
|
|
// 80^2
|
|
//
|
|
// 100
|
|
// The chances of it spawning with item(s) from the 'High' chest is: ------- X 40^2 = 16%
|
|
// 100^2
|
|
//
|
|
// 100
|
|
// The chances of it spawning with item(s) from the 'Unique' chest is: ------- X 40^2 = 4%
|
|
// 100^2
|
|
|
|
//So in this case the creature is most likely to spawn with an item from the Low, or Medium chests as the chances of it getting any item from the other
|
|
//chests are less.
|
|
|
|
|
|
//Note: Depending on the the amount of treasure you want distributed adjust the above values. If you are running a low treasure module
|
|
// then lower the percentage the better. If you are running a module with valuable loot given by all monsters then, of course higher the
|
|
// percentage the better.
|
|
|
|
|
|
// Tags for the module-wide containers
|
|
string sModContJunk = "X0_MOD_TREASURE_JUNK";
|
|
string sModContLow = "X0_MOD_TREASURE_LOW";
|
|
string sModContMed = "X0_MOD_TREASURE_MEDIUM";
|
|
string sModContHigh = "X0_MOD_TREASURE_HIGH";
|
|
string sModContUniq = "X0_MOD_TREASURE_UNIQUE";
|
|
|
|
|
|
|
|
|
|
//:: Throw dices to determine whether the PC is eligable for an Item Spawn
|
|
//:: creatureCR is the spawned creature's (IE: OBJECT_SELF) challenge rating.
|
|
//:: pMax is the Maximum percentage chance of item generation at cMax.
|
|
//:: cMax is the Highest creature's challenge rating.
|
|
//:: IE: If the pMax == 100, and cMax == 40, then the chance of an item
|
|
//:: spawn on a creature with challenge rating of 40 is 100%.
|
|
//::
|
|
//:: For all values above @threshold, returns TRUE. IE: Will spawn item.
|
|
//:: The chance of item generation is determined using a quardratic formula:
|
|
//:: Probability of Generation = (pMax/(cMax)^2)*(creatureCR)^2
|
|
//::
|
|
//:: Using the above example, this means that the chance of a unique item spawn
|
|
//:: on a creature with CR 20 is: 25%
|
|
//::
|
|
//:: WARNING: cMax squared must be greater than pMax AND creatureCR must be less than cMax
|
|
//:: for this to function properly... other wise it will always return true. (Calculate the
|
|
//:: probability for yourself and see ;)!!
|
|
int RollForItemSpawn(float creatureCR, float pMax = 100.0, float cMax = 40.0);
|
|
|
|
//return the chest appropriate to reward the PC
|
|
string GetAppropriateChest(float fRating);
|
|
|
|
/*
|
|
MAIN CONCEPT:
|
|
The module builder creates some base containers and fills them
|
|
with instances of the treasure items that s/he wants to appear
|
|
randomly. The actual treasure containers then randomly pick from
|
|
the contents of these base containers to generate their own
|
|
contents.
|
|
|
|
--------------------------------------------------
|
|
|
|
SETUP:
|
|
|
|
Create four module-wide base containers (for low, medium, high,
|
|
and unique treasure items) and place them anywhere in your
|
|
module. Make sure that they are in locations inaccessible to the
|
|
players, though!
|
|
|
|
Give these module-wide base containers these tags (or just use the
|
|
blueprints):
|
|
|
|
X0_MOD_TREASURE_LOW - low-level treasure
|
|
X0_MOD_TREASURE_MED - medium-level treasure
|
|
X0_MOD_TREASURE_HIGH - high-level treasure
|
|
X0_MOD_TREASURE_UNIQ - unique treasure items
|
|
|
|
Fill the instances of these base containers with appropriate treasure
|
|
for your module.
|
|
|
|
For any areas where you want finer control over the treasure,
|
|
simply create additional base containers (you don't need all four
|
|
-- any one that you skip will fall back to the module-wide version)
|
|
and place them in the area. Give them these tags (same just without
|
|
the "MOD"):
|
|
|
|
X0_TREASURE_LOW - low-level treasure
|
|
X0_TREASURE_MED - medium-level treasure
|
|
X0_TREASURE_HIGH - high-level treasure
|
|
X0_TREASURE_UNIQ - unique treasure items
|
|
|
|
For any treasure container, use one of the following scripts
|
|
as BOTH the OnOpen/OnDeath handler:
|
|
|
|
Any Treasure: x0_o2_any{low,med,high,uniq}
|
|
Books (book, scroll): x0_o2_book{low,med,high,uniq}
|
|
Potions: x0_o2_potn{low,med,high,uniq}
|
|
Armor (armor, shield, helm, boots, etc): x0_o2_arm{low,med,high,uniq}
|
|
Weapon: x0_o2_weap{low,med,high,uniq}
|
|
Gold: x0_o2_gold{low,med,high,uniq}
|
|
|
|
Others may also be added.
|
|
|
|
MONSTER/NPC TREASURE
|
|
--------------------
|
|
If you would like to have special monster treasure (monster treasure
|
|
defaults to low treasure otherwise), you can also add any of these
|
|
base chests:
|
|
|
|
X0_TREASURE_MONSTER_<monster tag> - treasure found on monsters with
|
|
the given tag. This will strip off
|
|
any trailing digits, so for instance,
|
|
NW_KOBOLD1, NW_KOBOLD2, NW_KOBOLD005
|
|
would all end up checking the chest
|
|
X0_TREASURE_MONSTER_NW_KOBOLD.
|
|
|
|
X0_TREASURE_MONSTER_<racialtype> - treasure found on monsters of the given
|
|
racialtype. Ex:
|
|
X0_TREASURE_MONSTER_ELF
|
|
X0_TREASURE_MONSTER_UNDEAD
|
|
The spelling matches that used in the
|
|
RACIALTYPE_* constants.
|
|
|
|
X0_TREASURE_MONSTER - generic treasure found on all monsters/NPCs
|
|
in the module.
|
|
|
|
To use monster treasure, use the default OnSpawn script nw_c2_default9
|
|
or modify the OnSpawn script as follows:
|
|
|
|
- Replace #include "nw_o2_coninclude" with #include "x0_i0_treasure"
|
|
- Replace GenerateNPCTreasure(); with CTG_GenerateNPCTreasure();
|
|
- If you prefer to generate monster treasure from the general chests,
|
|
you can also add a parameter to CTG_GenerateNPCTreasure(); to
|
|
specify whether the treasure generated should be low/medium/high/uniq.
|
|
See the comments to that function for details.
|
|
|
|
--------------------------------------------------
|
|
|
|
DETAILS:
|
|
|
|
Each treasure container has a script that looks for the
|
|
nearest object with the tag matching the type of
|
|
treasure.
|
|
|
|
When the treasure-generating scripts look for a container,
|
|
they will take the nearest container in the local, then
|
|
fall back to the module-wide container. If no container can
|
|
be found, they will fall back on the standard random treasure
|
|
generation system.
|
|
|
|
The treasure generation system will randomly select from
|
|
1-3 items from whatever items are in the nearest container.
|
|
Each item has a 50/50 chance of being replaced by a random
|
|
amount of coin based on the standard treasure generation
|
|
system.
|
|
|
|
IMPORTANT NOTE ON PROBABILITY:
|
|
Each item in the chest has an equal probability of being
|
|
selected. To control the probability of any given item
|
|
appearing, simply place multiple copies of it in the container.
|
|
|
|
Because of the stacking rules, there is a slight modification
|
|
to this. Ammunition/throwing weapons will have a probability
|
|
equal to the number of stacks in the chest, since the max stack
|
|
size is the default stack size. Potions, scrolls, and gems, however,
|
|
will be counted individually to determine the probability even if
|
|
they are stacked, since their default stack size is 1.
|
|
|
|
To control the size of the stack that actually gets created, you
|
|
should create a blueprint that has the appropriate stack size.
|
|
You can adjust the stack size of the instance you stuff into the
|
|
base container to whatever you want afterwards; the blueprint's
|
|
stack size will control what
|
|
|
|
There are scripts that will cause only specific types of items
|
|
to be selected from the container of the specified level --
|
|
eg, only books/scrolls, or only weapons or armor, etc -- useful
|
|
for bookcases or for weapon racks, & so on.
|
|
|
|
The unique treasure chest will only place each item in the
|
|
chest once, will never place more than one item, and will
|
|
never replace the item with coin.
|
|
|
|
Advantages of this system:
|
|
- Complete control over which items can appear in a
|
|
module or in an area.
|
|
|
|
- Thematically-appropriate treasure can be placed in each
|
|
area.
|
|
|
|
- Easy for module builders to set up.
|
|
|
|
- Trivial to add new items to the treasure distribution.
|
|
|
|
- Trivial to adjust probabilities of items appearing.
|
|
|
|
- Easy to have unique treasure items showing up randomly.
|
|
|
|
Disadvantages of this system:
|
|
- No level-specific treasure generation. This system is thus
|
|
NOT appropriate for an area that is intended to scale to
|
|
radically-different levels. Rather, it is appropriate for
|
|
use where the levels of the characters are roughly known
|
|
to the module designer and the treasure can be designed to
|
|
match.
|
|
|
|
- No class-specific treasure generation planned for now.
|
|
(However, I may add something to increase probability of
|
|
certain items popping up, optionally. TBD.)
|
|
|
|
For XP1, we feel this is a good tradeoff, but this should
|
|
be kept in mind when deciding whether to use this system or
|
|
the original treasure generation system in user-made modules.
|
|
|
|
-- NN
|
|
|
|
*/
|
|
//:://////////////////////////////////////////////////
|
|
//:: Copyright (c) 2002 Floodgate Entertainment
|
|
//:: Created By: Naomi Novik
|
|
//:: Created On: 11/16/2002
|
|
//:://////////////////////////////////////////////////
|
|
|
|
// For legacy treasure generation
|
|
#include "nw_o2_coninclude"
|
|
/**********************************************************************
|
|
* CONSTANTS
|
|
**********************************************************************/
|
|
|
|
// * this is the percent chance that no treasure will be spawned by a creature dying
|
|
|
|
|
|
|
|
// Tags for the area-specific containers
|
|
string sContJunk = "X0_TREASURE_JUNK";
|
|
string sContLow = "X0_TREASURE_LOW";
|
|
string sContMed = "X0_TREASURE_MED";
|
|
string sContHigh = "X0_TREASURE_HIGH";
|
|
string sContUniq = "X0_TREASURE_UNIQ";
|
|
|
|
// Tag for the monster-specific containers
|
|
string sContMonster = "X0_TREASURE_MONSTER";
|
|
|
|
// Gold item ResRef
|
|
string sGoldResRef = "NW_IT_GOLD001";
|
|
|
|
// Varname holding the number of treasure items
|
|
string sNumTreasureItemsVarname = "X0_NUM_TREASURE_ITEMS";
|
|
|
|
// Varname holding the base type of each treasure item
|
|
string sBaseTypeVarname = "X0_BASE_TYPE_TREASURE";
|
|
// Varname holding a reference to each treasure item
|
|
string sTreasureItemVarname = "X0_TREASURE_ITEM";
|
|
|
|
// Varname indicating that the treasure container has been filled
|
|
string sTreasureGeneratedVarname = "X0_TREASURE_HAS_BEEN_GENERATED";
|
|
|
|
|
|
// Major treasure categories
|
|
|
|
int TREASURE_TYPE_LOW = 1;
|
|
int TREASURE_TYPE_MED = 2;
|
|
int TREASURE_TYPE_HIGH = 3;
|
|
int TREASURE_TYPE_UNIQUE = 4;
|
|
int TREASURE_TYPE_MONSTER = 5;
|
|
int TREASURE_TYPE_JUNK = 6;
|
|
|
|
// Special generalized base types.
|
|
// Giving these large values to make sure they never
|
|
// conflict with the actual base type values -- that
|
|
// would cause those base types to be non-specifiable.
|
|
int TREASURE_BASE_TYPE_WEAPON = 13000;
|
|
int TREASURE_BASE_TYPE_WEAPON_NOAMMO = 13001;
|
|
int TREASURE_BASE_TYPE_WEAPON_RANGED = 13002;
|
|
int TREASURE_BASE_TYPE_WEAPON_MELEE = 13003;
|
|
int TREASURE_BASE_TYPE_ARMOR = 13004;
|
|
int TREASURE_BASE_TYPE_CLOTHING = 13005;
|
|
|
|
// Probability of a single item being generated,
|
|
// in percentage
|
|
int TREASURE_PROBABILITY_1 = 70;
|
|
|
|
// Probability of two items being generated,
|
|
// in percentage
|
|
int TREASURE_PROBABILITY_2 = 20;
|
|
|
|
// Probability of an item being converted to gold
|
|
int TREASURE_GOLD_PROBABILITY = 35;
|
|
|
|
// Multiplier to use on the value of an item to convert it to gold
|
|
float X0_GOLD_MODIFIER = 0.50;
|
|
|
|
/**********************************************************************
|
|
* FUNCTION PROTOTYPES
|
|
*
|
|
* All functions prefixed with CTG for "container treasure generation".
|
|
**********************************************************************/
|
|
// Get the number of items to generate
|
|
// Returns an integer from 1-3, probabilities
|
|
// determined by the values of the constants
|
|
// TREASURE_PROBABILITY_1 & _2.
|
|
int CTG_GetNumItems();
|
|
|
|
// Get the number of items in a base container.
|
|
int CTG_GetNumItemsInBaseContainer(object oBaseCont);
|
|
|
|
// Determine whether an item should actually just be gold.
|
|
// Returns TRUE or FALSE.
|
|
// Probability controlled by constant TREASURE_GOLD_PROBABILITY
|
|
int CTG_IsItemGold();
|
|
|
|
// Find and return the right monster container, if
|
|
// available.
|
|
object CTG_GetMonsterBaseContainer(object oSource=OBJECT_SELF);
|
|
|
|
// Locate the base container of the appropriate type closest to
|
|
// oSource.
|
|
// Possible values for nTreasureType:
|
|
// TREASURE_TYPE_LOW
|
|
// TREASURE_TYPE_MED
|
|
// TREASURE_TYPE_HIGH
|
|
// TREASURE_TYPE_UNIQUE
|
|
object CTG_GetNearestBaseContainer(int nTreasureType, object oSource=OBJECT_SELF);
|
|
|
|
// Get the module-wide base container of the appropriate type.
|
|
// Possible values for nTreasureType:
|
|
// TREASURE_TYPE_LOW
|
|
// TREASURE_TYPE_MED
|
|
// TREASURE_TYPE_HIGH
|
|
// TREASURE_TYPE_UNIQUE
|
|
object CTG_GetModuleBaseContainer(int nTreasureType);
|
|
|
|
// Get the specified item out of the given base container's inventory
|
|
object CTG_GetTreasureItem(object oBaseCont, int nItemNum);
|
|
|
|
// Test if treasure has been generated in the given object
|
|
int CTG_GetIsTreasureGenerated(object oCont);
|
|
|
|
// Set whether treasure has been generated
|
|
void CTG_SetIsTreasureGenerated(object oCont, int bGenerated=TRUE);
|
|
|
|
// Create random treasure items of the appropriate type
|
|
// in the specified container. Will typically be called
|
|
// by a script on a treasure container.
|
|
//
|
|
// Possible values for nTreasureType:
|
|
// TREASURE_TYPE_LOW
|
|
// TREASURE_TYPE_MED
|
|
// TREASURE_TYPE_HIGH
|
|
// TREASURE_TYPE_UNIQUE
|
|
void CTG_CreateTreasure(int nTreasureType, object oAdventurer, object oCont=OBJECT_SELF);
|
|
|
|
// Starting from the specified item position, return the first
|
|
// item that matches one of the three base types.
|
|
//
|
|
// nBaseType1 may also be passed in as a special custom type,
|
|
// which will OVERRIDE any other specified base types:
|
|
//
|
|
// TREASURE_BASE_TYPE_WEAPON (for any weapon type)
|
|
// TREASURE_BASE_TYPE_WEAPON_NOAMMO (for any weapon but ammunition)
|
|
// TREASURE_BASE_TYPE_WEAPON_RANGED (for any ranged weapon)
|
|
// TREASURE_BASE_TYPE_WEAPON_MELEE (for any melee weapon)
|
|
// TREASURE_BASE_TYPE_ARMOR (for armor, shields)
|
|
// TREASURE_BASE_TYPE_CLOTHING (for belts, boots, bracers,
|
|
// cloaks, helms, gloves)
|
|
object CTG_GetSpecificBaseTypeTreasureItem(object oBaseCont, int nItemNum, int nBaseType1=BASE_ITEM_INVALID, int nBaseType2=BASE_ITEM_INVALID, int nBaseType3=BASE_ITEM_INVALID);
|
|
|
|
|
|
// Create treasure of the appropriate treasure level and matching one
|
|
// of up to three different base types in the specified container.
|
|
//
|
|
// Possible values for nTreasureType:
|
|
// TREASURE_TYPE_LOW
|
|
// TREASURE_TYPE_MED
|
|
// TREASURE_TYPE_HIGH
|
|
// TREASURE_TYPE_UNIQUE
|
|
//
|
|
// Possible values for nBaseType1/2/3: any BASE_ITEM_* constant.
|
|
//
|
|
// If nBaseType1 is passed in as invalid, NO TYPE CHECKING WILL BE DONE.
|
|
//
|
|
// nBaseType1 may also be passed in as a special custom type,
|
|
// which will OVERRIDE any other specified base types:
|
|
//
|
|
// TREASURE_BASE_TYPE_WEAPON (for any weapon type)
|
|
// TREASURE_BASE_TYPE_WEAPON_NOAMMO (for any weapon but ammunition)
|
|
// TREASURE_BASE_TYPE_WEAPON_RANGED (for any ranged weapon)
|
|
// TREASURE_BASE_TYPE_WEAPON_MELEE (for any melee weapon)
|
|
// TREASURE_BASE_TYPE_ARMOR (for armor, shields)
|
|
// TREASURE_BASE_TYPE_CLOTHING (for belts, boots, bracers,
|
|
// cloaks, helms, gloves)
|
|
//
|
|
void CTG_CreateSpecificBaseTypeTreasure(int nTreasureType, object oAdventurer, object oCont, int nBaseType1=BASE_ITEM_INVALID, int nBaseType2=BASE_ITEM_INVALID, int nBaseType3=BASE_ITEM_INVALID);
|
|
|
|
// Create gold treasure in the specified container.
|
|
void CTG_CreateGoldTreasure(int nTreasureType, object oAdventurer, object oCont=OBJECT_SELF);
|
|
|
|
// Create treasure on an NPC.
|
|
// This function will typically be called from within the
|
|
// NPC's OnSpawn handler.
|
|
// Note that this defaults to TREASURE_TYPE_MONSTER, which uses
|
|
// the monster-specific treasure chests and falls back to low-
|
|
// level treasure if none exist.
|
|
void CTG_GenerateNPCTreasure(int nTreasureType=5, object oNPC=OBJECT_SELF);
|
|
|
|
// Check if the item's base type is of the given base type
|
|
int CTG_GetIsBaseType(int nItemBaseType, int nBaseType1=BASE_ITEM_INVALID, int nBaseType2=BASE_ITEM_INVALID, int nBaseType3=BASE_ITEM_INVALID);
|
|
|
|
// Check if the item's base type is a weapon
|
|
int CTG_GetIsWeapon(int nItemBaseType);
|
|
|
|
// Check if the item's base type is a weapon but not ammunition
|
|
int CTG_GetIsWeaponNoammo(int nItemBaseType);
|
|
|
|
// Check if the item's base type is a ranged weapon
|
|
int CTG_GetIsRangedWeapon(int nItemBaseType);
|
|
|
|
// Check if the item's base type is a melee weapon
|
|
int CTG_GetIsMeleeWeapon(int nItemBaseType);
|
|
|
|
// Check if the item's base type is armor
|
|
int CTG_GetIsArmor(int nItemBaseType);
|
|
|
|
// Check if the item's base type is clothing
|
|
int CTG_GetIsClothing(int nItemBaseType);
|
|
|
|
// Tack on the appropriate racialtype suffix
|
|
string CTG_GetRacialtypeChestTag(string sBaseTag, object oSource);
|
|
|
|
/**********************************************************************
|
|
* PRIVATE FUNCTION DEFINITIONS
|
|
* These functions are deliberately not prototyped and not intended
|
|
* for use outside this library.
|
|
**********************************************************************/
|
|
|
|
//:: Throw dices to determine whether the PC is eligable for an Item Spawn
|
|
//:: creatureCR is the spawned creature's (IE: OBJECT_SELF) challenge rating.
|
|
//:: pMax is the Maximum percentage chance of item generation at cMax.
|
|
//:: cMax is the Highest creature's challenge rating.
|
|
//:: IE: If the pMax == 100, and cMax == 40, then the chance of an item
|
|
//:: spawn on a creature with challenge rating of 40 is 100%.
|
|
//::
|
|
//:: For all values above @threshold, returns TRUE. IE: Will spawn item.
|
|
//:: The chance of item generation is determined using a quardratic formula:
|
|
//:: Probability of Generation = (pMax/(cMax)^2)*(creatureCR)^2
|
|
//::
|
|
//:: Using the above example, this means that the chance of a unique item spawn
|
|
//:: on a creature with CR 20 is: 25%
|
|
//::
|
|
//:: WARNING: cMax squared must be greater than pMax AND creatureCR must be less than cMax
|
|
//:: for this to function properly... other wise it will always return true. (Calculate the
|
|
//:: probability for yourself and see ;)!!
|
|
|
|
int RollForItemSpawn(float creatureCR, float pMax = 40.0, float cMax = 100.0)
|
|
{
|
|
cMax = cMax*cMax;
|
|
creatureCR = creatureCR*creatureCR;
|
|
float oMax = pMax/cMax;
|
|
float detPercentage = oMax*creatureCR;
|
|
|
|
//to make detPercentage a whole number
|
|
detPercentage = detPercentage*1000.0;
|
|
if(detPercentage < 0.5)
|
|
{
|
|
detPercentage = 0.0;
|
|
}
|
|
int detChance = FloatToInt(detPercentage);
|
|
|
|
|
|
//returns TRUE if the dice roll
|
|
int nDiceRoll = Random(100000) + 1;
|
|
return (nDiceRoll <= detChance);
|
|
}
|
|
|
|
string GetAppropriateChest(float fRating)
|
|
{
|
|
int UniqueRoll = RollForItemSpawn(fRating, UNIQUE_SPAWN_MAX_PERCENTAGE, UNIQUE_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR);
|
|
int HighRoll = RollForItemSpawn(fRating, HIGH_SPAWN_MAX_PERCENTAGE, HIGH_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR);
|
|
int MediumRoll = RollForItemSpawn(fRating, MEDIUM_SPAWN_MAX_PERCENTAGE, MEDIUM_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR);
|
|
int LowRoll = RollForItemSpawn(fRating,LOW_SPAWN_MAX_PERCENTAGE, LOW_SPAWN_MAX_PERCENTAGE_AT_CREATURE_CR);
|
|
|
|
if(UniqueRoll)
|
|
{
|
|
return sModContUniq;
|
|
}
|
|
else if (HighRoll)
|
|
{
|
|
return sModContHigh;
|
|
}
|
|
else if (MediumRoll)
|
|
{
|
|
return sModContMed;
|
|
}
|
|
else if (LowRoll)
|
|
{
|
|
return sModContLow;
|
|
}
|
|
else
|
|
{ //or junk
|
|
return sModContJunk;
|
|
}
|
|
|
|
}
|
|
// This function deliberately not prototyped. Should not be used
|
|
// outside this library.
|
|
// Test whether a treasure container has been initialized for
|
|
// specific base treasure type use.
|
|
int CTG_GetIsContainerInitialized(object oBaseCont)
|
|
{
|
|
return GetLocalInt(oBaseCont, "X0_CONTAINER_INITIALIZED");
|
|
}
|
|
|
|
// This function deliberately not prototyped. Should not be used
|
|
// outside this library.
|
|
// Set whether a treasure container has been initialized for
|
|
// specific base treasure type use.
|
|
void CTG_SetIsContainerInitialized(object oBaseCont, int bInit=TRUE)
|
|
{
|
|
SetLocalInt(oBaseCont, "X0_CONTAINER_INITIALIZED", bInit);
|
|
}
|
|
|
|
// This function deliberately not prototyped. Should not be used
|
|
// outside this library.
|
|
// Initialize a treasure container to store the items contained
|
|
// inside as local variables on the container.
|
|
void CTG_InitContainer(object oBaseCont)
|
|
{
|
|
// don't do this twice
|
|
if (CTG_GetIsContainerInitialized(oBaseCont)) {return;}
|
|
// initialize
|
|
int nItems = CTG_GetNumItemsInBaseContainer(oBaseCont);
|
|
int i;
|
|
object oTmp = OBJECT_INVALID;
|
|
oTmp = GetFirstItemInInventory(oBaseCont);
|
|
for (i=0; i < nItems && GetIsObjectValid(oTmp); i++)
|
|
{
|
|
// Store the item and its base type as local vars on
|
|
// the container object itself.
|
|
string sIndex = IntToString(i);
|
|
string sVar = sBaseTypeVarname + sIndex;
|
|
SetLocalInt(oBaseCont, sVar, GetBaseItemType(oTmp));
|
|
sVar = sTreasureItemVarname + sIndex;
|
|
SetLocalObject(oBaseCont, sVar, oTmp);
|
|
|
|
oTmp = GetNextItemInInventory(oBaseCont);
|
|
}
|
|
|
|
// mark as initialized
|
|
CTG_SetIsContainerInitialized(oBaseCont);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION DEFINITIONS
|
|
**********************************************************************/
|
|
// Get the number of items to generate
|
|
// Returns an integer from 1-3, probabilities
|
|
// determined by the values of the constants
|
|
// TREASURE_PROBABILITY_1 & _2.
|
|
int CTG_GetNumItems()
|
|
{
|
|
int nRoll = d100();
|
|
|
|
if (nRoll < TREASURE_PROBABILITY_1) {
|
|
return 1;
|
|
} else if (nRoll < (TREASURE_PROBABILITY_1 + TREASURE_PROBABILITY_2)) {
|
|
return 2;
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
|
|
// Get the number of items in a base container
|
|
int CTG_GetNumItemsInBaseContainer(object oBaseCont)
|
|
{
|
|
int nItems = GetLocalInt(oBaseCont, sNumTreasureItemsVarname);
|
|
if (nItems != 0) {
|
|
return nItems;
|
|
}
|
|
|
|
// Haven't initialized the number of items yet
|
|
object oTmp = GetFirstItemInInventory(oBaseCont);
|
|
while (GetIsObjectValid(oTmp)) {
|
|
nItems++;
|
|
oTmp = GetNextItemInInventory(oBaseCont);
|
|
}
|
|
SetLocalInt(oBaseCont, sNumTreasureItemsVarname, nItems);
|
|
return nItems;
|
|
}
|
|
|
|
|
|
// Determine whether an item should actually just be gold.
|
|
// Returns TRUE or FALSE.
|
|
// Probability controlled by constant TREASURE_GOLD_PROBABILITY
|
|
int CTG_IsItemGold()
|
|
{
|
|
int nRoll = d100();
|
|
if (nRoll < TREASURE_GOLD_PROBABILITY)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
int GetIsInteger(string sChar)
|
|
{
|
|
return (sChar == "0"
|
|
|| sChar == "1"
|
|
|| sChar == "2"
|
|
|| sChar == "3"
|
|
|| sChar == "4"
|
|
|| sChar == "5"
|
|
|| sChar == "6"
|
|
|| sChar == "7"
|
|
|| sChar == "8"
|
|
|| sChar == "9");
|
|
}
|
|
|
|
// Find and return the right monster container, if
|
|
// available.
|
|
object CTG_GetMonsterBaseContainer(object oSource=OBJECT_SELF)
|
|
{
|
|
object oCont = OBJECT_INVALID;
|
|
string sContTag = "";
|
|
|
|
// For monster treasure, we first check for
|
|
// a chest with a tag matching the specific
|
|
// monster.
|
|
// We strip numbers off the end of the tag first
|
|
string sCreatureTag = GetTag(oSource);
|
|
while ( GetIsInteger(GetStringRight(sCreatureTag, 1)) ) {
|
|
sCreatureTag = GetStringLeft(sCreatureTag,
|
|
GetStringLength(sCreatureTag)-1);
|
|
}
|
|
|
|
sContTag = sContMonster + "_" + sCreatureTag;
|
|
oCont = GetObjectByTag(sContTag);
|
|
if (GetIsObjectValid(oCont)) {
|
|
return oCont;
|
|
}
|
|
|
|
// Didn't find that -- check for a chest for the
|
|
// appropriate racialtype.
|
|
sContTag = CTG_GetRacialtypeChestTag(sContMonster, oSource);
|
|
oCont = GetObjectByTag(sContTag);
|
|
if (GetIsObjectValid(oCont)) {
|
|
return oCont;
|
|
}
|
|
|
|
// Didn't find that -- check for a module-wide
|
|
// monster chest
|
|
oCont = GetObjectByTag(sContMonster);
|
|
return oCont;
|
|
}
|
|
|
|
|
|
// Locate the nearest base container of the appropriate type.
|
|
// Possible values for nTreasureType:
|
|
// TREASURE_TYPE_MONSTER
|
|
// TREASURE_TYPE_LOW
|
|
// TREASURE_TYPE_MED
|
|
// TREASURE_TYPE_HIGH
|
|
// TREASURE_TYPE_UNIQUE
|
|
object CTG_GetNearestBaseContainer(int nTreasureType,
|
|
object oSource=OBJECT_SELF)
|
|
{
|
|
object oCont = OBJECT_INVALID;
|
|
int nType = nTreasureType;
|
|
string sContTag = "";
|
|
|
|
if (nType == TREASURE_TYPE_MONSTER)
|
|
{
|
|
oCont = CTG_GetMonsterBaseContainer(oSource);
|
|
if (GetIsObjectValid(oCont))
|
|
{
|
|
return oCont;
|
|
}
|
|
//if there are no areas specific chests then roll module wide chest based
|
|
//on challenge rating.
|
|
else
|
|
{
|
|
float creatureCR = GetChallengeRating(oSource);
|
|
string ChestChosen = GetAppropriateChest(creatureCR);
|
|
oCont = GetObjectByTag(ChestChosen);
|
|
if(GetIsObjectValid(oCont))
|
|
{
|
|
return oCont;
|
|
}
|
|
}
|
|
|
|
// no monster-specific treasure set up or the module wide chests are missing
|
|
nType = TREASURE_TYPE_LOW;
|
|
}
|
|
if (nType == TREASURE_TYPE_JUNK) {
|
|
sContTag = sContJunk;
|
|
} else if (nType == TREASURE_TYPE_LOW) {
|
|
sContTag = sContLow;
|
|
} else if (nType == TREASURE_TYPE_MED) {
|
|
sContTag = sContMed;
|
|
} else if (nType == TREASURE_TYPE_HIGH) {
|
|
sContTag = sContHigh;
|
|
} else if (nType == TREASURE_TYPE_UNIQUE) {
|
|
sContTag = sContUniq;
|
|
} else {
|
|
// Invalid treasure type
|
|
return oCont;
|
|
}
|
|
|
|
oCont = GetNearestObjectByTag(sContTag);
|
|
if (!GetIsObjectValid(oCont)) {
|
|
// No area chest -- return the module-wide one
|
|
return CTG_GetModuleBaseContainer(nType);
|
|
}
|
|
return oCont;
|
|
}
|
|
|
|
// Get the module-wide base container of the appropriate type.
|
|
// Possible values for nTreasureType:
|
|
// TREASURE_TYPE_LOW
|
|
// TREASURE_TYPE_MED
|
|
// TREASURE_TYPE_HIGH
|
|
// TREASURE_TYPE_UNIQUE
|
|
object CTG_GetModuleBaseContainer(int nTreasureType)
|
|
{
|
|
object oCont = OBJECT_INVALID;
|
|
string sContTag = "";
|
|
|
|
if (nTreasureType == TREASURE_TYPE_LOW) {
|
|
sContTag = sModContLow;
|
|
} else if (nTreasureType == TREASURE_TYPE_MED) {
|
|
sContTag = sModContMed;
|
|
} else if (nTreasureType == TREASURE_TYPE_HIGH) {
|
|
sContTag = sModContHigh;
|
|
} else if (nTreasureType == TREASURE_TYPE_UNIQUE) {
|
|
sContTag = sModContUniq;
|
|
}
|
|
else {
|
|
// Invalid treasure type
|
|
return oCont;
|
|
}
|
|
|
|
oCont = GetObjectByTag(sContTag);
|
|
return oCont;
|
|
}
|
|
|
|
|
|
// Generate treasure using default method.
|
|
// Not prototyped -- this function should not be used outside
|
|
// this library.
|
|
void CTG_CreateDefaultTreasure(int nTreasureType, object oAdventurer, object oCont)
|
|
{
|
|
// generate treasure using default method from nw_o2_coninclude
|
|
if (nTreasureType == TREASURE_TYPE_LOW) {
|
|
GenerateLowTreasure(oAdventurer, oCont);
|
|
} else if (nTreasureType == TREASURE_TYPE_MED) {
|
|
GenerateMediumTreasure(oAdventurer, oCont);
|
|
} else if (nTreasureType == TREASURE_TYPE_HIGH) {
|
|
GenerateHighTreasure(oAdventurer, oCont);
|
|
} else if (nTreasureType == TREASURE_TYPE_UNIQUE) {
|
|
GenerateBossTreasure(oAdventurer, oCont);
|
|
}
|
|
}
|
|
|
|
// Get the specified item out of the given base container's inventory
|
|
object CTG_GetTreasureItem(object oBaseCont, int nItemNum)
|
|
{
|
|
if (!GetIsObjectValid(oBaseCont)) {return OBJECT_INVALID;}
|
|
|
|
// initialize the container -- this will automatically
|
|
// only happen once.
|
|
CTG_InitContainer(oBaseCont);
|
|
|
|
// Refs to the items are stored on the container itself
|
|
string sVar = sTreasureItemVarname + IntToString(nItemNum);
|
|
object oItem = GetLocalObject(oBaseCont, sVar);
|
|
return oItem;
|
|
}
|
|
|
|
// Starting from the specified item position, return the first
|
|
// item that matches one of the three base types.
|
|
// If nBaseType1 is passed in as invalid, NO TYPE CHECKING WILL BE DONE.
|
|
// nBaseType1 may also be passed in as a special custom type:
|
|
// TREASURE_BASE_TYPE_WEAPON (for any weapon type)
|
|
// TREASURE_BASE_TYPE_WEAPON_NOAMMO (for any weapon but ammunition)
|
|
// TREASURE_BASE_TYPE_WEAPON_RANGED (for any ranged weapon)
|
|
// TREASURE_BASE_TYPE_WEAPON_MELEE (for any melee weapon)
|
|
// TREASURE_BASE_TYPE_ARMOR (for armor, shields)
|
|
// TREASURE_BASE_TYPE_CLOTHING (for belts, boots, bracers,
|
|
// cloaks, helms, gloves)
|
|
object CTG_GetSpecificBaseTypeTreasureItem(object oBaseCont,
|
|
int nItemNum,
|
|
int nBaseType1=BASE_ITEM_INVALID,
|
|
int nBaseType2=BASE_ITEM_INVALID,
|
|
int nBaseType3=BASE_ITEM_INVALID)
|
|
{
|
|
if (!GetIsObjectValid(oBaseCont)) {return OBJECT_INVALID;}
|
|
|
|
// initialize the container -- this will automatically
|
|
// only happen once.
|
|
CTG_InitContainer(oBaseCont);
|
|
|
|
int nRollOverNum = CTG_GetNumItemsInBaseContainer(oBaseCont);
|
|
|
|
// If there are no items in the container, no chance of finding a matching one!
|
|
if (nRollOverNum == 0) return OBJECT_INVALID;
|
|
|
|
int i;
|
|
string sVar;
|
|
int nItemBaseType;
|
|
int nLooped = 0;
|
|
for (i=nItemNum; i != nItemNum || !nLooped ; i++) {
|
|
|
|
// The base type of each item is stored on the container itself
|
|
sVar = sBaseTypeVarname + IntToString(i);
|
|
nItemBaseType = GetLocalInt(oBaseCont, sVar);
|
|
|
|
// Check to see if the type matches our desired type.
|
|
if (
|
|
// Check for a weapon
|
|
(nBaseType1 == TREASURE_BASE_TYPE_WEAPON
|
|
&&
|
|
CTG_GetIsWeapon(nItemBaseType))
|
|
||
|
|
// non-ammo weapon
|
|
(nBaseType1 == TREASURE_BASE_TYPE_WEAPON_NOAMMO
|
|
&&
|
|
CTG_GetIsWeaponNoammo(nItemBaseType))
|
|
||
|
|
// ranged weapon
|
|
(nBaseType1 == TREASURE_BASE_TYPE_WEAPON_RANGED
|
|
&&
|
|
CTG_GetIsRangedWeapon(nItemBaseType))
|
|
||
|
|
// melee weapon
|
|
(nBaseType1 == TREASURE_BASE_TYPE_WEAPON_MELEE
|
|
&&
|
|
CTG_GetIsMeleeWeapon(nItemBaseType))
|
|
||
|
|
// armor
|
|
(nBaseType1 == TREASURE_BASE_TYPE_ARMOR
|
|
&&
|
|
CTG_GetIsArmor(nItemBaseType))
|
|
||
|
|
// clothing
|
|
(nBaseType1 == TREASURE_BASE_TYPE_CLOTHING
|
|
&&
|
|
CTG_GetIsClothing(nItemBaseType))
|
|
||
|
|
// other specific item type
|
|
( CTG_GetIsBaseType(nItemBaseType,
|
|
nBaseType1,
|
|
nBaseType2,
|
|
nBaseType3))
|
|
) // end of if statement
|
|
{
|
|
// we found a matching item -- return it
|
|
return CTG_GetTreasureItem(oBaseCont, i);
|
|
}
|
|
|
|
// Handle the wrap-around case
|
|
if ( (i+1) == nRollOverNum ) {
|
|
// set i back to 0
|
|
i = -1;
|
|
nLooped = 1;
|
|
}
|
|
}
|
|
|
|
// No matching item was found -- return null
|
|
return OBJECT_INVALID;
|
|
}
|
|
|
|
// Test if treasure has been generated in the given object
|
|
int CTG_GetIsTreasureGenerated(object oCont)
|
|
{
|
|
return GetLocalInt(oCont, sTreasureGeneratedVarname);
|
|
}
|
|
|
|
// Set whether treasure has been generated
|
|
void CTG_SetIsTreasureGenerated(object oCont, int bGenerated=TRUE)
|
|
{
|
|
SetLocalInt(oCont, sTreasureGeneratedVarname, bGenerated);
|
|
}
|
|
|
|
// Create random treasure items of the appropriate type
|
|
// in the specified container. Should be called
|
|
// by a script on a treasure container.
|
|
//
|
|
// Possible values for nTreasureType:
|
|
// TREASURE_TYPE_LOW
|
|
// TREASURE_TYPE_MED
|
|
// TREASURE_TYPE_HIGH
|
|
// TREASURE_TYPE_UNIQUE
|
|
void CTG_CreateTreasure(int nTreasureType,
|
|
object oAdventurer,
|
|
object oCont=OBJECT_SELF)
|
|
{
|
|
// To avoid code duplication, this actually just uses the specific
|
|
// version and passes an invalid item type
|
|
CTG_CreateSpecificBaseTypeTreasure(nTreasureType, oAdventurer, oCont);
|
|
}
|
|
|
|
|
|
|
|
// Create treasure of the appropriate treasure level and matching one
|
|
// of up to three different base types in the specified container.
|
|
//
|
|
// Possible values for nTreasureType:
|
|
// TREASURE_TYPE_LOW
|
|
// TREASURE_TYPE_MED
|
|
// TREASURE_TYPE_HIGH
|
|
// TREASURE_TYPE_UNIQUE
|
|
//
|
|
// Possible values for nBaseType1/2/3: any BASE_ITEM_* constant.
|
|
// Can also use these special values:
|
|
// TREASURE_BASE_TYPE_WEAPON (for any weapon type)
|
|
// TREASURE_BASE_TYPE_WEAPON_NOAMMO (for any weapon but ammunition)
|
|
// TREASURE_BASE_TYPE_WEAPON_RANGED (for any ranged weapon)
|
|
// TREASURE_BASE_TYPE_WEAPON_MELEE (for any melee weapon)
|
|
// TREASURE_BASE_TYPE_ARMOR (for armor, shields)
|
|
// TREASURE_BASE_TYPE_CLOTHING (for belts, boots, bracers,
|
|
// cloaks, helms, gloves)
|
|
//
|
|
void CTG_CreateSpecificBaseTypeTreasure(int nTreasureType,
|
|
object oAdventurer,
|
|
object oCont,
|
|
int nBaseType1=BASE_ITEM_INVALID,
|
|
int nBaseType2=BASE_ITEM_INVALID,
|
|
int nBaseType3=BASE_ITEM_INVALID)
|
|
{
|
|
// Prevent duplicate treasure generation
|
|
if (CTG_GetIsTreasureGenerated(oCont)) {return;}
|
|
CTG_SetIsTreasureGenerated(oCont);
|
|
|
|
// Locate the base container
|
|
object oBaseCont = CTG_GetNearestBaseContainer(nTreasureType,
|
|
oCont);
|
|
string oTag = GetTag(oBaseCont);
|
|
// 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(oAdventurer, oCont);
|
|
} else {
|
|
// Generate default treasure
|
|
CTG_CreateDefaultTreasure(nTreasureType, oAdventurer, oCont);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Get the number of available items
|
|
int nItemsInBaseCont = CTG_GetNumItemsInBaseContainer(oBaseCont);
|
|
|
|
// Special case: unique treasure or artifact items
|
|
if (nTreasureType == TREASURE_TYPE_UNIQUE)
|
|
{
|
|
// only give one item and only give it once
|
|
int nRandom = Random(nItemsInBaseCont);
|
|
|
|
object oItem;
|
|
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, oAdventurer, oCont);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Copy the item
|
|
// CreateItemOnObject(GetResRef(oItem), oCont);
|
|
// * do an actual copy BK Feb 2003. Less chance of resref errors
|
|
CopyItem(oItem, oCont, TRUE);
|
|
SetPlotFlag(oItem, FALSE);
|
|
// Destroy the original
|
|
DestroyObject(oItem, 1.0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Otherwise, we generate 1-3 items, possibly convert some
|
|
// to gold.
|
|
int nItems = CTG_GetNumItems();
|
|
int nItemsCreated = 0;
|
|
object oItem = OBJECT_INVALID;
|
|
|
|
// Keep track of items handed out to avoid dupes
|
|
object oItem1 = OBJECT_INVALID;
|
|
object oItem2 = OBJECT_INVALID;
|
|
|
|
// Random number -- position of item to hand out
|
|
int nRandom = 0;
|
|
|
|
while (nItemsCreated < nItems) {
|
|
nItemsCreated++;
|
|
if (nBaseType1 == BASE_ITEM_INVALID && CTG_IsItemGold() )
|
|
{
|
|
CTG_CreateGoldTreasure(nTreasureType, oAdventurer, 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))
|
|
{
|
|
// Ugh, no matching item found!
|
|
// Bad module designer, no cookie.
|
|
CTG_CreateDefaultTreasure(nTreasureType, oAdventurer, oCont);
|
|
}
|
|
else if ( nItemsCreated > 1 &&
|
|
(GetTag(oItem) == GetTag(oItem1) || GetTag(oItem) == GetTag(oItem2)) )
|
|
{
|
|
// Ugh, duplicate item. Make gold instead.
|
|
CTG_CreateGoldTreasure(nTreasureType, oAdventurer, oCont);
|
|
}
|
|
else
|
|
{
|
|
// Make the item
|
|
CopyItem(oItem, oCont, TRUE);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
//Re-roll for the the next item. IE: If the above item was one from the high
|
|
//chest the next one might not be ;)
|
|
if (nTreasureType == TREASURE_TYPE_MONSTER)
|
|
{
|
|
oBaseCont = CTG_GetNearestBaseContainer(nTreasureType, oCont);
|
|
nItemsInBaseCont = CTG_GetNumItemsInBaseContainer(oBaseCont);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create gold treasure in the specified container.
|
|
void CTG_CreateGoldTreasure(int nTreasureType,
|
|
object oAdventurer,
|
|
object oCont=OBJECT_SELF)
|
|
{
|
|
// Just use the default generation scripts
|
|
CreateGold(oCont, oAdventurer, nTreasureType);
|
|
}
|
|
|
|
|
|
// Create treasure on an NPC.
|
|
// This function will typically be called from within the
|
|
// NPC's OnSpawn handler.
|
|
// Note that this defaults to TREASURE_TYPE_MONSTER, which uses
|
|
// the monster-specific treasure chests and falls back to low-
|
|
// level treasure if none exist.
|
|
void CTG_GenerateNPCTreasure(int nTreasureType=5, object oNPC=OBJECT_SELF)
|
|
{
|
|
|
|
// Locate the base container
|
|
object oBaseCont = CTG_GetNearestBaseContainer(nTreasureType,
|
|
oNPC);
|
|
|
|
// Make sure we have a valid base container
|
|
if (!GetIsObjectValid(oBaseCont)) {
|
|
// if not, generate treasure using default method from
|
|
// nw_o2_coninclude.
|
|
if (nTreasureType == TREASURE_TYPE_MONSTER)
|
|
GenerateNPCTreasure(TREASURE_TYPE_MED, oNPC);
|
|
else
|
|
GenerateNPCTreasure(nTreasureType, oNPC);
|
|
return;
|
|
}
|
|
|
|
// Now we determine whether we put treasure on the NPC
|
|
int nRacialType = GetRacialType(oNPC);
|
|
switch (nRacialType) {
|
|
case RACIAL_TYPE_ANIMAL:
|
|
case RACIAL_TYPE_BEAST:
|
|
case RACIAL_TYPE_MAGICAL_BEAST:
|
|
case RACIAL_TYPE_VERMIN:
|
|
// No treasure, sorry.
|
|
return;
|
|
}
|
|
|
|
if (nTreasureType == TREASURE_TYPE_MONSTER) {
|
|
|
|
if (d100() < BK_CHANCE_OF_N0_MONSTERTREASURE) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise, generate as usual
|
|
CTG_CreateTreasure(nTreasureType, oNPC, oNPC);
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************
|
|
* Boring type-checking functions.
|
|
**********************************************************************/
|
|
|
|
|
|
// Check if the item's base type is of the given base type
|
|
int CTG_GetIsBaseType(int nItemBaseType,
|
|
int nBaseType1=BASE_ITEM_INVALID,
|
|
int nBaseType2=BASE_ITEM_INVALID,
|
|
int nBaseType3=BASE_ITEM_INVALID)
|
|
{
|
|
return ( (nItemBaseType != BASE_ITEM_INVALID)
|
|
&& (
|
|
nItemBaseType == nBaseType1
|
|
|| nItemBaseType == nBaseType2
|
|
|| nItemBaseType == nBaseType3) );
|
|
}
|
|
|
|
// Check if the item's base type is a weapon
|
|
int CTG_GetIsWeapon(int nItemBaseType)
|
|
{
|
|
return (
|
|
nItemBaseType == BASE_ITEM_ARROW
|
|
|| nItemBaseType == BASE_ITEM_BASTARDSWORD
|
|
|| nItemBaseType == BASE_ITEM_BATTLEAXE
|
|
|| nItemBaseType == BASE_ITEM_BOLT
|
|
|| nItemBaseType == BASE_ITEM_BULLET
|
|
|| nItemBaseType == BASE_ITEM_CBLUDGWEAPON
|
|
|| nItemBaseType == BASE_ITEM_CLUB
|
|
|| nItemBaseType == BASE_ITEM_CPIERCWEAPON
|
|
|| nItemBaseType == BASE_ITEM_CSLASHWEAPON
|
|
|| nItemBaseType == BASE_ITEM_CSLSHPRCWEAP
|
|
|| nItemBaseType == BASE_ITEM_DAGGER
|
|
|| nItemBaseType == BASE_ITEM_DART
|
|
|| nItemBaseType == BASE_ITEM_DIREMACE
|
|
|| nItemBaseType == BASE_ITEM_DOUBLEAXE
|
|
|| nItemBaseType == BASE_ITEM_GREATAXE
|
|
|| nItemBaseType == BASE_ITEM_GREATSWORD
|
|
|| nItemBaseType == BASE_ITEM_GRENADE
|
|
|| nItemBaseType == BASE_ITEM_HALBERD
|
|
|| nItemBaseType == BASE_ITEM_HANDAXE
|
|
|| nItemBaseType == BASE_ITEM_HEAVYCROSSBOW
|
|
|| nItemBaseType == BASE_ITEM_HEAVYFLAIL
|
|
|| nItemBaseType == BASE_ITEM_KAMA
|
|
|| nItemBaseType == BASE_ITEM_KATANA
|
|
|| nItemBaseType == BASE_ITEM_KUKRI
|
|
|| nItemBaseType == BASE_ITEM_LIGHTCROSSBOW
|
|
|| nItemBaseType == BASE_ITEM_LIGHTFLAIL
|
|
|| nItemBaseType == BASE_ITEM_LIGHTHAMMER
|
|
|| nItemBaseType == BASE_ITEM_LIGHTMACE
|
|
|| nItemBaseType == BASE_ITEM_LONGBOW
|
|
|| nItemBaseType == BASE_ITEM_LONGSWORD
|
|
|| nItemBaseType == BASE_ITEM_MORNINGSTAR
|
|
|| nItemBaseType == BASE_ITEM_QUARTERSTAFF
|
|
|| nItemBaseType == BASE_ITEM_RAPIER
|
|
|| nItemBaseType == BASE_ITEM_SCIMITAR
|
|
|| nItemBaseType == BASE_ITEM_SCYTHE
|
|
|| nItemBaseType == BASE_ITEM_SHORTBOW
|
|
|| nItemBaseType == BASE_ITEM_SHORTSPEAR
|
|
|| nItemBaseType == BASE_ITEM_SHORTSWORD
|
|
|| nItemBaseType == BASE_ITEM_SHURIKEN
|
|
|| nItemBaseType == BASE_ITEM_SICKLE
|
|
|| nItemBaseType == BASE_ITEM_SLING
|
|
|| nItemBaseType == BASE_ITEM_THROWINGAXE
|
|
|| nItemBaseType == BASE_ITEM_TWOBLADEDSWORD
|
|
|| nItemBaseType == BASE_ITEM_WARHAMMER);
|
|
}
|
|
|
|
// Check if the item's base type is a weapon but not ammunition
|
|
int CTG_GetIsWeaponNoammo(int nItemBaseType)
|
|
{
|
|
return ( CTG_GetIsWeapon(nItemBaseType)
|
|
&& (nItemBaseType != BASE_ITEM_ARROW
|
|
|| nItemBaseType != BASE_ITEM_BOLT
|
|
|| nItemBaseType != BASE_ITEM_BULLET)
|
|
);
|
|
}
|
|
|
|
// Check if the item's base type is a ranged weapon
|
|
int CTG_GetIsRangedWeapon(int nItemBaseType)
|
|
{
|
|
return (nItemBaseType == BASE_ITEM_DART
|
|
|| nItemBaseType == BASE_ITEM_GRENADE
|
|
|| nItemBaseType == BASE_ITEM_HEAVYCROSSBOW
|
|
|| nItemBaseType == BASE_ITEM_LIGHTCROSSBOW
|
|
|| nItemBaseType == BASE_ITEM_LONGBOW
|
|
|| nItemBaseType == BASE_ITEM_SHORTBOW
|
|
|| nItemBaseType == BASE_ITEM_SHURIKEN
|
|
|| nItemBaseType == BASE_ITEM_SLING
|
|
|| nItemBaseType == BASE_ITEM_THROWINGAXE);
|
|
}
|
|
|
|
// Check if the item's base type is a melee weapon
|
|
int CTG_GetIsMeleeWeapon(int nItemBaseType)
|
|
{
|
|
return (
|
|
nItemBaseType == BASE_ITEM_BASTARDSWORD
|
|
|| nItemBaseType == BASE_ITEM_BATTLEAXE
|
|
|| nItemBaseType == BASE_ITEM_CLUB
|
|
|| nItemBaseType == BASE_ITEM_DAGGER
|
|
|| nItemBaseType == BASE_ITEM_DIREMACE
|
|
|| nItemBaseType == BASE_ITEM_DOUBLEAXE
|
|
|| nItemBaseType == BASE_ITEM_GREATAXE
|
|
|| nItemBaseType == BASE_ITEM_GREATSWORD
|
|
|| nItemBaseType == BASE_ITEM_HALBERD
|
|
|| nItemBaseType == BASE_ITEM_HANDAXE
|
|
|| nItemBaseType == BASE_ITEM_HEAVYFLAIL
|
|
|| nItemBaseType == BASE_ITEM_KAMA
|
|
|| nItemBaseType == BASE_ITEM_KATANA
|
|
|| nItemBaseType == BASE_ITEM_KUKRI
|
|
|| nItemBaseType == BASE_ITEM_LIGHTFLAIL
|
|
|| nItemBaseType == BASE_ITEM_LIGHTHAMMER
|
|
|| nItemBaseType == BASE_ITEM_LIGHTMACE
|
|
|| nItemBaseType == BASE_ITEM_LONGSWORD
|
|
|| nItemBaseType == BASE_ITEM_MORNINGSTAR
|
|
|| nItemBaseType == BASE_ITEM_QUARTERSTAFF
|
|
|| nItemBaseType == BASE_ITEM_RAPIER
|
|
|| nItemBaseType == BASE_ITEM_SCIMITAR
|
|
|| nItemBaseType == BASE_ITEM_SCYTHE
|
|
|| nItemBaseType == BASE_ITEM_SHORTSPEAR
|
|
|| nItemBaseType == BASE_ITEM_SHORTSWORD
|
|
|| nItemBaseType == BASE_ITEM_SICKLE
|
|
|| nItemBaseType == BASE_ITEM_TWOBLADEDSWORD
|
|
|| nItemBaseType == BASE_ITEM_WARHAMMER);
|
|
}
|
|
|
|
// Check if the item's base type is armor
|
|
int CTG_GetIsArmor(int nItemBaseType)
|
|
{
|
|
return (nItemBaseType == BASE_ITEM_ARMOR
|
|
|| nItemBaseType == BASE_ITEM_LARGESHIELD
|
|
|| nItemBaseType == BASE_ITEM_SMALLSHIELD
|
|
|| nItemBaseType == BASE_ITEM_TOWERSHIELD);
|
|
}
|
|
|
|
// Check if the item's base type is clothing
|
|
int CTG_GetIsClothing(int nItemBaseType)
|
|
{
|
|
return (nItemBaseType == BASE_ITEM_BELT
|
|
|| nItemBaseType == BASE_ITEM_BOOTS
|
|
|| nItemBaseType == BASE_ITEM_BRACER
|
|
|| nItemBaseType == BASE_ITEM_CLOAK
|
|
|| nItemBaseType == BASE_ITEM_GLOVES);
|
|
}
|
|
|
|
// Tack on the appropriate racialtype suffix
|
|
string CTG_GetRacialtypeChestTag(string sBaseTag, object oSource)
|
|
{
|
|
string sCont = sBaseTag + "_";
|
|
switch (GetRacialType(oSource))
|
|
{
|
|
case RACIAL_TYPE_INVALID: return "";
|
|
case RACIAL_TYPE_ABERRATION: sCont = sCont + "ABERRATION"; break;
|
|
case RACIAL_TYPE_ANIMAL: sCont = sCont + "ANIMAL"; break;
|
|
case RACIAL_TYPE_BEAST: sCont = sCont + "BEAST"; break;
|
|
case RACIAL_TYPE_CONSTRUCT: sCont = sCont + "CONSTRUCT"; break;
|
|
case RACIAL_TYPE_DRAGON: sCont = sCont + "DRAGON"; break;
|
|
case RACIAL_TYPE_DWARF: sCont = sCont + "DWARF"; break;
|
|
case RACIAL_TYPE_ELEMENTAL: sCont = sCont + "ELEMENTAL"; break;
|
|
case RACIAL_TYPE_ELF: sCont = sCont + "ELF"; break;
|
|
case RACIAL_TYPE_FEY: sCont = sCont + "FEY"; break;
|
|
case RACIAL_TYPE_GIANT: sCont = sCont + "GIANT"; break;
|
|
case RACIAL_TYPE_GNOME: sCont = sCont + "GNOME"; break;
|
|
case RACIAL_TYPE_HALFELF: sCont = sCont + "HALFELF"; break;
|
|
case RACIAL_TYPE_HALFLING: sCont = sCont + "HALFLING"; break;
|
|
case RACIAL_TYPE_HALFORC: sCont = sCont + "HALFORC"; break;
|
|
case RACIAL_TYPE_HUMAN: sCont = sCont + "HUMAN"; break;
|
|
case RACIAL_TYPE_HUMANOID_GOBLINOID: sCont = sCont + "GOBLINOID"; break;
|
|
case RACIAL_TYPE_HUMANOID_MONSTROUS: sCont = sCont + "MONSTROUS"; break;
|
|
case RACIAL_TYPE_HUMANOID_ORC: sCont = sCont + "ORC"; break;
|
|
case RACIAL_TYPE_HUMANOID_REPTILIAN: sCont = sCont + "REPTILIAN"; break;
|
|
case RACIAL_TYPE_MAGICAL_BEAST: sCont = sCont + "MAGICAL_BEAST"; break;
|
|
case RACIAL_TYPE_OUTSIDER: sCont = sCont + "OUTSIDER"; break;
|
|
case RACIAL_TYPE_SHAPECHANGER: sCont = sCont + "SHAPECHANGER"; break;
|
|
case RACIAL_TYPE_UNDEAD: sCont = sCont + "UNDEAD"; break;
|
|
case RACIAL_TYPE_VERMIN: sCont = sCont + "VERMIN"; break;
|
|
}
|
|
|
|
return sCont;
|
|
}
|
|
|
|
|
|
//void main() {}
|
|
|