OK, if the aim is to let you set up custom Unlimited Ammunition types on individual items, I think I’d suggest altering this whole setup a little bit.
I’ve excessively commented this for you for TEH EDUMACASHUNS. Not the solution I recommend ultimately, because (reasons below), but read this:
A script to be hooked into the module OnPlayerEquipItem event.
// This is a constant integer, turning a number (hard to memorize!) into readable text.
// A constant cannot be altered from within other functions, but it can be read by them.
// If you set up constants in a library script, you can #include that library into other scripts, and reuse the constants.
const int UNLIMITED_AMMO_TYPE_COLD_PLUS_1 = 1;
void main()
{
object oPC = GetPCItemLastEquippedBy(); // The player who is equipping the item.
object oItem = GetPCItemLastEquipped(); // The item that is being equipped.
// A local int is checked on the item that is being equipped.
int nAmmoType = GetLocalInt(oItem, "UNLIMITED_AMMUNITION_TYPE");
// If that local int is not zero, we proceed.
// Otherwise, the script ends here, since it doesn't contain anything beyond this check at it's root.
if (nAmmoType)
{
// If the item does not have the Unlimited Ammunition item property set on it, abort the script.
if (!GetItemHasItemProperty(oItem, ITEM_PROPERTY_UNLIMITED_AMMUNITION))
return;
// Determine ammunition item depending on baseitemtype of equipped item:
object oAmmunition;
switch (GetBaseItemType(oItem))
{
// If it's either a light or a heavy crossbow, it uses bolts.
case BASE_ITEM_LIGHTCROSSBOW:
case BASE_ITEM_HEAVYCROSSBOW:
oAmmunition = GetItemInSlot(INVENTORY_SLOT_BOLTS, oPC);
break;
// If it's either a short- or a longbow, it uses arrows.
case BASE_ITEM_LONGBOW:
case BASE_ITEM_SHORTBOW:
oAmmunition = GetItemInSlot(INVENTORY_SLOT_ARROWS, oPC);
break;
// If it's a sling, it uses bullets.
case BASE_ITEM_SLING:
oAmmunition = GetItemInSlot(INVENTORY_SLOT_BULLETS, oPC);
break;
}
// Remove all item properties from the ammunition item.
// First, check the first item property on the item:
itemproperty ipRemove = GetFirstItemProperty(oAmmunition);
// As long as ipRemove is a valid item property, do the following:
while (GetIsItemPropertyValid(ipRemove))
{
// Remove the item property from the target.
RemoveItemProperty(oAmmunition, ipRemove);
// Alter ipRemove, to check the next item property on the item.
ipRemove = GetNextItemProperty(oAmmunition);
// Once GetNextItemProperty no longer returns a valid item property,
// the condition of the while loop is no longer being met, so the while loop ends.
}
// Determine item properties to add depending on UNLIMITED_AMMUNITION_TYPE value:
itemproperty ipAdd;
switch (nAmmoType)
{
// Note that the constant we set up at the top of the script is being used here:
case UNLIMITED_AMMO_TYPE_COLD_PLUS_1:
ipAdd = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_COLD, IP_CONST_DAMAGEBONUS_1);
break;
// In theory, more different custom unlimited ammo types should go in here.
// That's the only thing that justifies the switch case statement here. If there
// is only a single custom unlimited ammo type, then this entire step is unnecessary.
}
// Add the new item property.
AddItemProperty(DURATION_TYPE_PERMANENT, ipAdd, oAmmunition);
}
}
^- This variant goes with the “Modify the entire OnPlayerEquipItem event modulewide”-route that’s currently going on. In this case, you have a single local variable set on the item, which should work for any ranged weapon that has the Unlimited Ammunition item property on it. Every item that is being equipped by a player is checked for whether it possesses this local variable. If it doesn’t have the variable, nothing further happens, so that’s relatively unobtrusive.
Reasons why this may not be a good idea: Local vars on items aren’t persistent past character export in singleplayer, though. Also, it opens up potential for a console cheat in singleplayer, since players can add local vars to items via the console, there. So, I don’t think this is actually the best route to go.
If you very specifically want to set up a special item, identified by it’s unique tag, that has a custom unlimited ammunition type, then I vote for tag-based scripting:
Set up a script named to match the tag of the custom crossbow.
#include "x2_inc_switches"
void main()
{
// If the event that has caused this tag-based item event to trigger is
// the equip-item event:
if (GetUserDefinedItemEventNumber() == X2_ITEM_EVENT_EQUIP)
{
// Safety check in case the crossbow loses it's Unlimited Ammunition item property by some means:
// If the item does not have the Unlimited Ammunition item property set on it, abort the script.
if (!GetItemHasItemProperty(GetPCItemLastEquipped(), ITEM_PROPERTY_UNLIMITED_AMMUNITION))
return;
// Find the object that equipped the item, and their bolt ammunition (which will have been automatically created via the Unlimited Ammunition itemproperty):
object oPC = GetPCItemLastEquippedBy();
object oBolts = GetItemInSlot(INVENTORY_SLOT_BOLTS, oPC);
// Remove the current itemproperties from the ammo.
itemproperty ipRemove = GetFirstItemProperty(oBolts);
while (GetIsItemPropertyValid(ipRemove))
{
RemoveItemProperty(oBolts, ipRemove);
ipRemove = GetNextItemProperty(oBolts);
}
// Add the new itemproperty to the ammo.
itemproperty ipAdd = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_COLD, IP_CONST_DAMAGEBONUS_1);
AddItemProperty(DURATION_TYPE_PERMANENT, ipAdd, oBolts);
}
}
Tag-based scripting is activated by default, these days. If you have an item with a custom tag and want it to behave in special ways, you just need to set up a script for that tag, include x2_inc_switches, check for which event is being triggered, and then put in the code for that event.
This route will be persistent across singleplayer character export. I think it might even be persistent across different modules if the tagbased item script is exported and installed via the override folder - if only within modules in which tagbased scripting is activated.
Also, to try to explain what was happening with the “It only works if there is only a single crossbow in the player’s inventory”-thing: GetItemPossessedBy - NWN Lexicon
GetPCItemEquippedBy() is the one if you want to reliably identify the item that has just been equipped, thereby triggering the OnPlayerEquipItem event. Trusting the Lexicon on this one - GetItemPossessedBy() apparently scans through the player’s inventory before it scans through the player’s equipment, so it should never be able to find the crossbow you just equipped if the player has another item with the same tag sitting in their inventory.
It’s odd that the script doesn’t work with the second crossbow in the inventory, if the second crossbow has the local int set on it too, though; the if (GetLocalInt(oItemx, “COLD_AMMO”) == 1)-check that leads to the item properties of the ammunition being altered should also equal TRUE if the local int is set on the crossbow in the inventory, as opposed to on the crossbow that is being equipped. Wild guess: Are you reusing an exported character in singleplayer? That way around, you might have one crossbow that has the variable set on it, and one that doesn’t - and if you equip the one that has the variable set on it, the script finds the one that doesn’t have the variable set on it, and then the check equals FALSE.
That’s my guess for what’s going on there, anyway.
@Orth also had a very good thought there that warrants being put into words explicitly, so it doesn’t go overlooked: Descriptive variable names are your friend! Try to stick to descriptive variable names when you can, especially if you’re still learning the ropes. You’ll thank yourself for building up that habit later on, if you have to backtrack and edit your own code once some time has passed and you’ve forgotten what exactly is going on in it. x_X