2025/12/24 Update

Hooked up new GUI module event.
Updated 2DAs.
Updated TLK.
Updated PRC8 includes.
Updated nasher.cfg.
This commit is contained in:
Jaysyn904
2025-12-24 02:22:19 -05:00
parent d75ae2c41d
commit d6045e2edf
99 changed files with 35612 additions and 23948 deletions

View File

@@ -13,7 +13,7 @@
//:: Created On: 2003-05-09
//:: Last Updated On: 2003-10-14
//:://////////////////////////////////////////////
#include "prc_x2_itemprop"
struct craft_struct
{
@@ -44,22 +44,25 @@ const string X2_CI_CRAFTSKILL_CONV ="x2_p_craftskills";
/* moved to be code switches
const int X2_CI_BREWPOTION_MAXLEVEL = 3; // Max Level for potions
const int X2_CI_BREWPOTION_COSTMODIFIER = 50; // gp Brew Potion XPCost Modifier
const int PRC_X2_BREWPOTION_COSTMODIFIER = 50; // gp Brew Potion XPCost Modifier
// Scribe Scroll related constants
const int X2_CI_SCRIBESCROLL_COSTMODIFIER = 25; // Scribescroll Cost Modifier
const int PRC_X2_SCRIBESCROLL_COSTMODIFIER = 25; // Scribescroll Cost Modifier
// Craft Wand related constants
const int X2_CI_CRAFTWAND_MAXLEVEL = 4;
const int X2_CI_CRAFTWAND_COSTMODIFIER = 750;
const int PRC_X2_CRAFTWAND_MAXLEVEL = 4;
const int PRC_X2_CRAFTWAND_COSTMODIFIER = 750;
*/
const int X2_CI_BREWPOTION_FEAT_ID = 944; // Brew Potion feat simulation
const int X2_CI_SCRIBESCROLL_FEAT_ID = 945;
const int X2_CI_CRAFTWAND_FEAT_ID = 946;
const int X2_CI_CRAFTROD_FEAT_ID = 2927;
const int X2_CI_CRAFTROD_EPIC_FEAT_ID = 3490;
const int X2_CI_CRAFTSTAFF_FEAT_ID = 2928;
const int X2_CI_CRAFTSTAFF_EPIC_FEAT_ID = 3491;
const int X2_CI_BREWPOTION_FEAT_ID = 944; // Brew Potion feat simulation
const int X2_CI_SCRIBESCROLL_FEAT_ID = 945;
const int X2_CI_CRAFTWAND_FEAT_ID = 946;
const int X2_CI_CRAFTROD_FEAT_ID = 2927;
const int X2_CI_CRAFTROD_EPIC_FEAT_ID = 3490;
const int X2_CI_CRAFTSTAFF_FEAT_ID = 2928;
const int X2_CI_CRAFTSTAFF_EPIC_FEAT_ID = 3491;
const int X2_CI_CREATEINFUSION_FEAT_ID = 25960;
const int X2_CI_CRAFTSCEPTER_FEAT_ID = 25962;
const string X2_CI_BREWPOTION_NEWITEM_RESREF = "x2_it_pcpotion"; // ResRef for new potion item
const string X2_CI_SCRIBESCROLL_NEWITEM_RESREF = "x2_it_pcscroll"; // ResRef for new scroll item
const string X2_CI_CRAFTWAND_NEWITEM_RESREF = "x2_it_pcwand";
@@ -185,6 +188,17 @@ int CheckAlternativeCrafting(object oPC, int nSpell, struct craft_cost_struct co
// Returns the maximum of caster level used and other effective levels from emulating spells
int GetAlternativeCasterLevel(object oPC, int nLevel);
// -----------------------------------------------------------------------------
// Create and Return an herbal infusion with an item property
// matching nSpellID.
// -----------------------------------------------------------------------------
object CICreateInfusion(object oCreator, int nSpellID);
// -----------------------------------------------------------------------------
// Returns TRUE if the player successfully performed Create Infusion
// -----------------------------------------------------------------------------
int CICraftCheckCreateInfusion(object oSpellTarget, object oCaster, int nID = 0);
//////////////////////////////////////////////////
/* Include section */
//////////////////////////////////////////////////
@@ -194,6 +208,7 @@ int GetAlternativeCasterLevel(object oPC, int nLevel);
#include "prc_inc_newip"
#include "prc_inc_spells"
#include "prc_add_spell_dc"
#include "inc_infusion"
//////////////////////////////////////////////////
/* Function definitions */
@@ -261,7 +276,9 @@ int CIGetIsCraftFeatBaseItem(object oItem)
nBt == BASE_ITEM_BLANK_SCROLL ||
nBt == BASE_ITEM_BLANK_WAND ||
nBt == BASE_ITEM_CRAFTED_ROD ||
nBt == BASE_ITEM_CRAFTED_STAFF)
nBt == BASE_ITEM_CRAFTED_STAFF ||
nBt == BASE_ITEM_CRAFTED_SCEPTER ||
nBt == BASE_ITEM_MUNDANE_HERB)
return TRUE;
else
return FALSE;
@@ -287,7 +304,7 @@ object CICraftBrewPotion(object oCreator, int nSpellID )
return OBJECT_INVALID;
}
/* //just a tad retarded, don't you think? other crafting feats are not similarly restricted
/* //just a tad silly, don't you think? other crafting feats are not similarly restricted
//Uses per day
int nUsesAllowed;
@@ -453,11 +470,159 @@ object CICraftCraftWand(object oCreator, int nSpellID )
// -----------------------------------------------------------------------------
// Georg, 2003-06-12
// Create and Return a magic wand with an item property
// matching nSpellID. Charges are set to d20 + casterlevel
// capped at 50 max
// Create and Return a magic scroll with an item property
// matching nSpellID.
// -----------------------------------------------------------------------------
object CICraftScribeScroll(object oCreator, int nSpellID)
{
if (DEBUG) DoDebug("CICraftScribeScroll: Enter (nSpellID=" + IntToString(nSpellID) + ")");
// Keep original and compute one-step master (if subradial)
int nSpellOriginal = nSpellID;
int nSpellMaster = nSpellOriginal;
if (GetIsSubradialSpell(nSpellOriginal))
{
nSpellMaster = GetMasterSpellFromSubradial(nSpellOriginal);
if (DEBUG) DoDebug("CICraftScribeScroll: subradial detected original=" + IntToString(nSpellOriginal) + " master=" + IntToString(nSpellMaster));
}
// Prefer iprp mapping for the original, fallback to master
int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellOriginal);
int nSpellUsedForIP = nSpellOriginal;
if (nPropID < 0)
{
if (DEBUG) DoDebug("CICraftScribeScroll: no iprp for original " + IntToString(nSpellOriginal) + ", trying master " + IntToString(nSpellMaster));
nPropID = IPGetIPConstCastSpellFromSpellID(nSpellMaster);
nSpellUsedForIP = nSpellMaster;
}
// If neither original nor master has an iprp row, we can still try templates,
// but most templates expect a matching iprp. Bail out now if nothing found.
if (nPropID < 0)
{
if (DEBUG) DoDebug("CICraftScribeScroll: no iprp_spells entry for original/master -> aborting");
FloatingTextStringOnCreature("This spell cannot be scribed (no item property mapping).", oCreator, FALSE);
return OBJECT_INVALID;
}
if (DEBUG) DoDebug("CICraftScribeScroll: using spell " + IntToString(nSpellUsedForIP) + " (iprp row " + IntToString(nPropID) + ") for item property");
// Material component check (based on resolved iprp row)
string sMat = GetMaterialComponentTag(nPropID);
if (sMat != "")
{
object oMat = GetItemPossessedBy(oCreator, sMat);
if (oMat == OBJECT_INVALID)
{
FloatingTextStrRefOnCreature(83374, oCreator); // Missing material component
return OBJECT_INVALID;
}
else
{
DestroyObject(oMat);
}
}
// Resolve class and scroll template
int nClass = PRCGetLastSpellCastClass();
string sClass = "";
switch (nClass)
{
case CLASS_TYPE_WIZARD:
case CLASS_TYPE_SORCERER: sClass = "Wiz_Sorc"; break;
case CLASS_TYPE_CLERIC:
case CLASS_TYPE_OCULAR:
case CLASS_TYPE_UR_PRIEST: sClass = "Cleric"; break;
case CLASS_TYPE_PALADIN: sClass = "Paladin"; break;
case CLASS_TYPE_DRUID:
case CLASS_TYPE_BLIGHTER: sClass = "Druid"; break;
case CLASS_TYPE_RANGER: sClass = "Ranger"; break;
case CLASS_TYPE_BARD: sClass = "Bard"; break;
case CLASS_TYPE_ASSASSIN: sClass = "Assassin"; break;
}
object oTarget = OBJECT_INVALID;
string sResRef = "";
// Try to find a class-specific scroll template.
if (sClass != "")
{
// Try original first (so if you made a subradial-specific template it will be used)
sResRef = Get2DACache(X2_CI_2DA_SCROLLS, sClass, nSpellOriginal);
if (sResRef == "")
{
// fallback to the spell that matched an iprp row (master or original)
sResRef = Get2DACache(X2_CI_2DA_SCROLLS, sClass, nSpellUsedForIP);
}
if (sResRef != "")
{
oTarget = CreateItemOnObject(sResRef, oCreator);
if (DEBUG) DoDebug("CICraftScribeScroll: created template " + sResRef + " for class " + sClass);
// Ensure template uses the correct cast-spell property: replace the template's cast-spell IP with ours
if (oTarget != OBJECT_INVALID)
{
itemproperty ipIter = GetFirstItemProperty(oTarget);
while (GetIsItemPropertyValid(ipIter))
{
if (GetItemPropertyType(ipIter) == ITEM_PROPERTY_CAST_SPELL)
{
RemoveItemProperty(oTarget, ipIter);
break;
}
ipIter = GetNextItemProperty(oTarget);
}
itemproperty ipSpell = ItemPropertyCastSpell(nPropID, IP_CONST_CASTSPELL_NUMUSES_SINGLE_USE);
AddItemProperty(DURATION_TYPE_PERMANENT, ipSpell, oTarget);
}
}
}
// If no template or sClass was empty, create generic scroll and add itemprop.
if (oTarget == OBJECT_INVALID)
{
sResRef = "craft_scroll";
oTarget = CreateItemOnObject(sResRef, oCreator);
if (oTarget == OBJECT_INVALID)
{
WriteTimestampedLogEntry("CICraftScribeScroll: failed to create craft_scroll template.");
return OBJECT_INVALID;
}
// Remove existing default IP and add correct one
itemproperty ipFirst = GetFirstItemProperty(oTarget);
if (GetIsItemPropertyValid(ipFirst))
RemoveItemProperty(oTarget, ipFirst);
itemproperty ipSpell = ItemPropertyCastSpell(nPropID, IP_CONST_CASTSPELL_NUMUSES_SINGLE_USE);
AddItemProperty(DURATION_TYPE_PERMANENT, ipSpell, oTarget);
}
// Add PRC metadata (use the same spell that matched the iprp row so metadata and IP line up)
if (GetPRCSwitch(PRC_SCRIBE_SCROLL_CASTER_LEVEL))
{
int nCasterLevel = GetAlternativeCasterLevel(oCreator, PRCGetCasterLevel(oCreator));
itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpellUsedForIP, nCasterLevel);
AddItemProperty(DURATION_TYPE_PERMANENT, ipLevel, oTarget);
itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpellUsedForIP, PRCGetMetaMagicFeat());
AddItemProperty(DURATION_TYPE_PERMANENT, ipMeta, oTarget);
int nDC = PRCGetSpellSaveDC(nSpellUsedForIP, GetSpellSchool(nSpellUsedForIP), OBJECT_SELF);
itemproperty ipDC = ItemPropertyCastSpellDC(nSpellUsedForIP, nDC);
AddItemProperty(DURATION_TYPE_PERMANENT, ipDC, oTarget);
}
if (oTarget == OBJECT_INVALID)
{
WriteTimestampedLogEntry("prc_x2_craft::CICraftScribeScroll failed - Resref: " + sResRef + " Class: " + sClass + "(" + IntToString(nClass) + ") " + " SpellID " + IntToString(nSpellID));
return OBJECT_INVALID;
}
if (DEBUG) DoDebug("CICraftScribeScroll: Success - created scroll " + sResRef + " for spell " + IntToString(nSpellUsedForIP));
return oTarget;
}
/* object CICraftScribeScroll(object oCreator, int nSpellID)
{
int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellID);
object oTarget;
@@ -491,6 +656,7 @@ object CICraftScribeScroll(object oCreator, int nSpellID)
break;
case CLASS_TYPE_CLERIC:
case CLASS_TYPE_UR_PRIEST:
case CLASS_TYPE_OCULAR:
sClass = "Cleric";
break;
case CLASS_TYPE_PALADIN:
@@ -506,6 +672,9 @@ object CICraftScribeScroll(object oCreator, int nSpellID)
case CLASS_TYPE_BARD:
sClass = "Bard";
break;
case CLASS_TYPE_ASSASSIN:
sClass = "Assassin";
break;
}
string sResRef;
if (sClass != "")
@@ -542,6 +711,7 @@ object CICraftScribeScroll(object oCreator, int nSpellID)
}
return oTarget;
}
*/
// -----------------------------------------------------------------------------
// Returns TRUE if the player used the last spell to brew a potion
@@ -593,7 +763,7 @@ These dont work as IPs since they are hardcoded */
// -------------------------------------------------------------------------
// check if spell is below maxlevel for brew potions
// -------------------------------------------------------------------------
int nPotionMaxLevel = GetPRCSwitch(X2_CI_BREWPOTION_MAXLEVEL);
int nPotionMaxLevel = GetPRCSwitch(PRC_X2_BREWPOTION_MAXLEVEL);
if(nPotionMaxLevel == 0)
nPotionMaxLevel = 3;
@@ -624,7 +794,7 @@ These dont work as IPs since they are hardcoded */
// -------------------------------------------------------------------------
// XP/GP Cost Calculation
// -------------------------------------------------------------------------
int nCostModifier = GetPRCSwitch(X2_CI_BREWPOTION_COSTMODIFIER);
int nCostModifier = GetPRCSwitch(PRC_X2_BREWPOTION_COSTMODIFIER);
if(nCostModifier == 0)
nCostModifier = 50;
int nCost = CIGetCraftGPCost(nLevel, nCostModifier, PRC_BREW_POTION_CASTER_LEVEL);
@@ -698,7 +868,6 @@ These dont work as IPs since they are hardcoded */
}
// -----------------------------------------------------------------------------
// Returns TRUE if the player used the last spell to create a scroll
// -----------------------------------------------------------------------------
@@ -728,7 +897,7 @@ int CICraftCheckScribeScroll(object oSpellTarget, object oCaster, int nID = 0)
// XP/GP Cost Calculation
// -------------------------------------------------------------------------
int nLevel = CIGetSpellInnateLevel(nID,TRUE);
int nCostModifier = GetPRCSwitch(X2_CI_SCRIBESCROLL_COSTMODIFIER);
int nCostModifier = GetPRCSwitch(PRC_X2_SCRIBESCROLL_COSTMODIFIER);
if(nCostModifier == 0)
nCostModifier = 25;
int nCost = CIGetCraftGPCost(nLevel, nCostModifier, PRC_SCRIBE_SCROLL_CASTER_LEVEL);
@@ -884,7 +1053,7 @@ These dont work as IPs since they are hardcoded */
// -------------------------------------------------------------------------
// check if spell is below maxlevel for craft want
// -------------------------------------------------------------------------
int nMaxLevel = GetPRCSwitch(X2_CI_CRAFTWAND_MAXLEVEL);
int nMaxLevel = GetPRCSwitch(PRC_X2_CRAFTWAND_MAXLEVEL);
if(nMaxLevel == 0)
nMaxLevel = 4;
if (nLevel > nMaxLevel)
@@ -896,7 +1065,7 @@ These dont work as IPs since they are hardcoded */
// -------------------------------------------------------------------------
// XP/GP Cost Calculation
// -------------------------------------------------------------------------
int nCostMod = GetPRCSwitch(X2_CI_CRAFTWAND_COSTMODIFIER);
int nCostMod = GetPRCSwitch(PRC_X2_CRAFTWAND_COSTMODIFIER);
if(nCostMod == 0)
nCostMod = 750;
int nCost = CIGetCraftGPCost(nLevel, nCostMod, PRC_CRAFT_WAND_CASTER_LEVEL);
@@ -966,6 +1135,169 @@ These dont work as IPs since they are hardcoded */
return FALSE;
}
// -----------------------------------------------------------------------------
// Returns TRUE if the player used the last spell to craft a scepter
// -----------------------------------------------------------------------------
int CICraftCheckCraftScepter(object oSpellTarget, object oCaster, int nSpellID = 0)
{
if(nSpellID == 0) nSpellID = PRCGetSpellId();
int nCasterLevel = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster));
int bSuccess = TRUE;
int nCount = 0;
itemproperty ip = GetFirstItemProperty(oSpellTarget);
int nMetaMagic = PRCGetMetaMagicFeat();
while(GetIsItemPropertyValid(ip))
{
if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL)
nCount++;
ip = GetNextItemProperty(oSpellTarget);
}
if(nCount >= 2) //:: Scepters are limited to two spells
{
FloatingTextStringOnCreature("* Failure - Too many castspell itemproperties *", oCaster);
return TRUE;
}
if(!GetHasFeat(X2_CI_CRAFTSCEPTER_FEAT_ID, oCaster))
{
FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item
return TRUE; // tried item creation but do not know how to do it
}
if(CIGetIsSpellRestrictedFromCraftFeat(nSpellID, X2_CI_CRAFTSCEPTER_FEAT_ID))
{
FloatingTextStrRefOnCreature(16829169, oCaster); // can not be used with this feat
return TRUE;
}
// Get the base spell level (circle) before metamagic adjustments
int nBaseLevel = CIGetSpellInnateLevel(nSpellID, TRUE);
// Check if spell circle is 7th level or lower
if (nBaseLevel > 7)
{
//FloatingTextStrRefOnCreature(83623, oCaster); // Spell level too high
FloatingTextStringOnCreature("* Failure - scepters can not hold spells higher than level 7", oCaster);
return TRUE;
}
int nLevel = nBaseLevel;
if(GetPRCSwitch(PRC_CRAFT_SCEPTER_CASTER_LEVEL))
{
switch(nMetaMagic)
{
case METAMAGIC_EMPOWER:
nLevel += 2;
break;
case METAMAGIC_EXTEND:
nLevel += 1;
break;
case METAMAGIC_MAXIMIZE:
nLevel += 3;
break;
/* case METAMAGIC_QUICKEN:
nLevel += 1;
break;
case METAMAGIC_SILENT:
nLevel += 5;
break;
case METAMAGIC_STILL:
nLevel += 6;
break;
These dont work as IPs since they are hardcoded */
}
}
int nCostMod = GetPRCSwitch(PRC_X2_CRAFTSCEPTER_COSTMODIFIER);
if(!nCostMod) nCostMod = 750;
int nLvlRow = IPGetIPConstCastSpellFromSpellID(nSpellID);
int nCLevel = StringToInt(Get2DACache("iprp_spells","CasterLvl",nLvlRow));
int nCost = CIGetCraftGPCost(nLevel, nCostMod, PRC_CRAFT_SCEPTER_CASTER_LEVEL);
//discount for second spell
if(nCount+1 == 2)
nCost = (nCost/2);
//takes epic xp costs into account
struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_CRAFT_SCEPTER, (nMetaMagic > 0));
if(costs.nGoldCost < 1) costs.nXPCost = 1;
if(costs.nXPCost < 1) costs.nXPCost = 1;
//if(GetGold(oCaster) < nGoldCost) // enough gold?
if (!GetHasGPToSpend(oCaster, costs.nGoldCost))
{
FloatingTextStrRefOnCreature(3786, oCaster); // Item Creation Failed - not enough gold!
return TRUE;
}
int nHD = GetHitDice(oCaster);
int nMinXPForLevel = (nHD * (nHD - 1)) * 500;
int nNewXP = GetXP(oCaster) - costs.nXPCost;
//if (nMinXPForLevel > nNewXP || nNewXP == 0 )
if (!GetHasXPToSpend(oCaster, costs.nXPCost))
{
FloatingTextStrRefOnCreature(3785, oCaster); // Item Creation Failed - Not enough XP
return TRUE;
}
//check spell emulation
if(!CheckAlternativeCrafting(oCaster, nSpellID, costs))
{
FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE);
return TRUE;
}
int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellID);
if (nPropID == 0 && nSpellID != 0)
{
FloatingTextStrRefOnCreature(84544,oCaster);
return TRUE;
}
if (nPropID != -1)
{
AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE),oSpellTarget);
if(GetPRCSwitch(PRC_CRAFT_SCEPTER_CASTER_LEVEL))
{
AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellCasterLevel(nSpellID, nCasterLevel),oSpellTarget);
AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellMetamagic(nSpellID, PRCGetMetaMagicFeat()),oSpellTarget);
AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellDC(nSpellID, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF)),oSpellTarget);
}
}
else
bSuccess = FALSE;
if(bSuccess)
{
//TakeGoldFromCreature(nGoldCost, oCaster, TRUE);
//SetXP(oCaster, nNewXP);
SpendXP(oCaster, costs.nXPCost);
SpendGP(oCaster, costs.nGoldCost);
//DestroyObject (oSpellTarget);
FloatingTextStrRefOnCreature(8502, oCaster); // Item Creation successful
//advance time here
if(!costs.nTimeCost) costs.nTimeCost = 1;
AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost));
string sName;
sName = GetName(oCaster)+"'s Magic Scepter";
SetItemCharges(oSpellTarget, 50);
//sName = Get2DACache("spells", "Name", nID);
//sName = "Wand of "+GetStringByStrRef(StringToInt(sName));
SetName(oSpellTarget, sName);
SetItemCursedFlag(oSpellTarget, FALSE);
SetDroppableFlag(oSpellTarget, TRUE);
return TRUE;
}
else
{
FloatingTextStrRefOnCreature(76417, oCaster); // Item Creation Failed
return TRUE;
}
return TRUE;
}
// -----------------------------------------------------------------------------
// Returns TRUE if the player used the last spell to craft a staff
// -----------------------------------------------------------------------------
int CICraftCheckCraftStaff(object oSpellTarget, object oCaster, int nSpellID = 0)
{
@@ -1027,7 +1359,7 @@ int CICraftCheckCraftStaff(object oSpellTarget, object oCaster, int nSpellID = 0
These dont work as IPs since they are hardcoded */
}
}
int nCostMod = GetPRCSwitch(X2_CI_CRAFTSTAFF_COSTMODIFIER);
int nCostMod = GetPRCSwitch(PRC_X2_CRAFTSTAFF_COSTMODIFIER);
if(!nCostMod) nCostMod = 750;
int nLvlRow = IPGetIPConstCastSpellFromSpellID(nSpellID);
int nCLevel = StringToInt(Get2DACache("iprp_spells","CasterLvl",nLvlRow));
@@ -1114,6 +1446,9 @@ These dont work as IPs since they are hardcoded */
return TRUE;
}
// -----------------------------------------------------------------------------
// Returns TRUE if the player used the last spell to craft a rod
// -----------------------------------------------------------------------------
int CICraftCheckCraftRod(object oSpellTarget, object oCaster, int nSpellID = 0)
{
@@ -1175,7 +1510,7 @@ int CICraftCheckCraftRod(object oSpellTarget, object oCaster, int nSpellID = 0)
These dont work as IPs since they are hardcoded */
}
}
int nCostMod = GetPRCSwitch(X2_CI_CRAFTROD_COSTMODIFIER);
int nCostMod = GetPRCSwitch(PRC_X2_CRAFTROD_COSTMODIFIER);
if(!nCostMod) nCostMod = 750;
int nLvlRow = IPGetIPConstCastSpellFromSpellID(nSpellID);
int nCLevel = StringToInt(Get2DACache("iprp_spells","CasterLvl",nLvlRow));
@@ -1260,6 +1595,7 @@ These dont work as IPs since they are hardcoded */
return TRUE;
}
int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, int nSpell = 0)
{
if(!GetIsObjectValid(oCaster)) oCaster = OBJECT_SELF;
@@ -1310,6 +1646,11 @@ int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALI
if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject();
int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster));
//:: Check for Inscribe Epic Runes and cap CL at 20 if it doesn't exist.
int bEpicRunes = GetHasFeat(EPIC_FEAT_INSCRIBE_EPIC_RUNES, oCaster);
if (!bEpicRunes) { if(nCaster > 20) nCaster = 20; }
int nDC = PRCGetSaveDC(oTarget, oCaster);
if(!nSpell) nSpell = PRCGetSpellId();
int nSpellLevel = 0;
@@ -1332,6 +1673,7 @@ int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALI
// Minimum level.
if (nSpellLevel == 0) nSpellLevel = 1;
// This will be modified with Runecaster code later.
int nCharges = 1;
if (GetLocalInt(oCaster, "RuneCharges")) nCharges = nCount;
@@ -1440,9 +1782,14 @@ int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALI
// If they have this active, the bonuses are already added, so skip
// If they don't, add the bonuses down below
if(GetHasSpellEffect(SPELL_RUNE_CHANT))
nRuneChant = 0;
nRuneChant = 0;
itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpell, PRCGetCasterLevel());
//:: Check for Inscribe Epic Runes and cap CL at 20 if it doesn't exist.
nCaster = PRCGetCasterLevel();
if (!bEpicRunes) { if(nCaster > 20) nCaster = 20; }
itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpell, nCaster);
AddItemProperty(DURATION_TYPE_PERMANENT,ipLevel,oRune);
itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpell, PRCGetMetaMagicFeat());
AddItemProperty(DURATION_TYPE_PERMANENT,ipMeta,oRune);
@@ -1544,18 +1891,28 @@ int AttuneGem(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID,
// No point scribing Gems from items, and its not allowed.
if (oItem != OBJECT_INVALID)
{
FloatingTextStringOnCreature("You cannot scribe a Gem from an item.", oCaster, FALSE);
FloatingTextStringOnCreature("You cannot attune a Gem from an item.", oCaster, FALSE);
return TRUE;
}
// oTarget here should be the gem. If it's not, fail.
if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject();
// Only accepts bioware gems
if (GetStringLeft(GetResRef(oTarget), 5) == "it_gem")
// Only accepts bioware gems & Craftable Natural Resources gems, but not gem dust.
int bIsBioGem = (GetStringLeft(GetResRef(oTarget), 5) == "it_gem");
int bIsCNRGem = (GetStringLeft(GetResRef(oTarget), 6) == "cnrgem");
int bIsDust = (GetStringLeft(GetResRef(oTarget), 10) == "cnrgemdust");
if (!(bIsBioGem || bIsCNRGem) || bIsDust)
{
FloatingTextStringOnCreature("Spell target is not a valid gem.", oCaster, FALSE);
return TRUE;
}
/* if ((GetStringLeft(GetResRef(oTarget), 5) == "it_gem") || (GetStringLeft(GetResRef(oTarget), 6) == "cnrgem") && (GetStringLeft(GetResRef(oTarget), 10) != "cnrgemdust"))
{
FloatingTextStringOnCreature("Spell target is not a valid gem.", oCaster, FALSE);
// And out we go
return TRUE;
}
} */
int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster));
int nDC = PRCGetSaveDC(oTarget, oCaster);
@@ -1952,6 +2309,19 @@ int CIGetSpellWasUsedForItemCreation(object oSpellTarget)
nRet = CICraftCheckCraftStaff(oSpellTarget,oCaster);
break;
case BASE_ITEM_CRAFTED_SCEPTER :
// -------------------------------------------------
// Craft Scepter
// -------------------------------------------------
nRet = CICraftCheckCraftScepter(oSpellTarget,oCaster);
break;
case BASE_ITEM_MUNDANE_HERB :
// -------------------------------------------------
// Create Infusion
// -------------------------------------------------
nRet = CICraftCheckCreateInfusion(oSpellTarget,oCaster);
break;
// you could add more crafting basetypes here....
}
@@ -2740,6 +3110,16 @@ int GetMagicalArtisanFeat(int nCraftingFeat)
nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_SKULL_TALISMAN;
break;
}
case FEAT_CREATE_INFUSION:
{
nReturn = FEAT_MAGICAL_ARTISAN_CREATE_INFUSION;
break;
}
case FEAT_CRAFT_SCEPTER:
{
nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_SCEPTER;
break;
}
default:
{
if(DEBUG) DoDebug("GetMagicalArtisanFeat: invalid crafting feat");
@@ -2941,6 +3321,306 @@ int GetAlternativeCasterLevel(object oPC, int nLevel)
return nLevel;
}
// -----------------------------------------------------------------------------
// Returns TRUE if the player successfully performed Create Infusion
// -----------------------------------------------------------------------------
int CICraftCheckCreateInfusion(object oSpellTarget, object oCaster, int nID = 0)
{
if (nID == 0) nID = PRCGetSpellId();
int bIsSubradial = GetIsSubradialSpell(nID);
if(bIsSubradial)
{
nID = GetMasterSpellFromSubradial(nID);
}
// -------------------------------------------------------------------------
// Check if the caster has the Create Infusion feat
// -------------------------------------------------------------------------
if (!GetHasFeat(FEAT_CREATE_INFUSION, oCaster))
{
FloatingTextStrRefOnCreature(40487, oCaster); // Missing feat
return TRUE;
}
// -------------------------------------------------------------------------
// Divine spellcasters only
// -------------------------------------------------------------------------
int nClass = PRCGetLastSpellCastClass();
if (!GetIsDivineClass(nClass))
{
FloatingTextStringOnCreature("Only divine casters can create infusions.", oCaster, FALSE);
return TRUE;
}
// -------------------------------------------------------------------------
// Check if spell is restricted for Create Infusion
// -------------------------------------------------------------------------
if (CIGetIsSpellRestrictedFromCraftFeat(nID, X2_CI_CREATEINFUSION_FEAT_ID))
{
FloatingTextStrRefOnCreature(83451, oCaster); // Spell not allowed
return TRUE;
}
// -------------------------------------------------------------------------
// Optional PnP Herb check
// -------------------------------------------------------------------------
int bPnPHerbs = GetPRCSwitch(PRC_CREATE_INFUSION_OPTIONAL_HERBS);
if(bPnPHerbs)
{
int nSpellschool = GetSpellSchool(nID);
int nHerbSchool = GetHerbsSpellSchool(oSpellTarget);
int nSpellLevel = PRCGetSpellLevelForClass(nID, nClass);
int nHerbLevel = GetHerbsInfusionSpellLevel(oSpellTarget);
if(nSpellschool != nHerbSchool)
{
// Herb is for wrong spellschool
FloatingTextStringOnCreature("This herb isn't appropriate for this spell school", oCaster);
return TRUE;
}
if(nSpellLevel > nHerbLevel)
{
// Herb spell circle level too low
FloatingTextStringOnCreature("This herb isn't appropriate for this spell level", oCaster);
return TRUE;
}
}
// -------------------------------------------------------------------------
// XP/GP Cost Calculation
// -------------------------------------------------------------------------
int nLevel = CIGetSpellInnateLevel(nID, TRUE);
int nCostModifier = GetPRCSwitch(PRC_X2_CREATEINFUSION_COSTMODIFIER);
if (nCostModifier == 0)
nCostModifier = 25;
int nCost = CIGetCraftGPCost(nLevel, nCostModifier, PRC_CREATE_INFUSION_CASTER_LEVEL);
struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_CREATE_INFUSION, FALSE);
// Adjust level for metamagic
if (GetPRCSwitch(PRC_CREATE_INFUSION_CASTER_LEVEL))
{
int nMetaMagic = PRCGetMetaMagicFeat();
switch(nMetaMagic)
{
case METAMAGIC_EMPOWER: nLevel += 2; break;
case METAMAGIC_EXTEND: nLevel += 1; break;
case METAMAGIC_MAXIMIZE: nLevel += 3; break;
// Unsupported metamagic IPs not added
}
}
// -------------------------------------------------------------------------
// Check Gold
// -------------------------------------------------------------------------
if (!GetHasGPToSpend(oCaster, costs.nGoldCost))
{
FloatingTextStrRefOnCreature(3786, oCaster); // Not enough gold
return TRUE;
}
// -------------------------------------------------------------------------
// Check XP
// -------------------------------------------------------------------------
if (!GetHasXPToSpend(oCaster, costs.nXPCost))
{
FloatingTextStrRefOnCreature(3785, oCaster); // Not enough XP
return TRUE;
}
// -------------------------------------------------------------------------
// Check alternative spell emulation requirements
// -------------------------------------------------------------------------
if (!CheckAlternativeCrafting(oCaster, nID, costs))
{
FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE);
return TRUE;
}
// -------------------------------------------------------------------------
// Create the infused herb item
// -------------------------------------------------------------------------
object oInfusion = CICreateInfusion(oCaster, nID);
if (GetIsObjectValid(oInfusion))
{
// Get the spell's display name from spells.2da via TLK
int nNameStrRef = StringToInt(Get2DAString("spells", "Name", nID));
string sSpellName = GetStringByStrRef(nNameStrRef);
// Rename the item
string sNewName = "Infusion of " + sSpellName;
SetName(oInfusion, sNewName);
// Post-creation actions
SetIdentified(oInfusion, TRUE);
ActionPlayAnimation(ANIMATION_FIREFORGET_READ, 1.0);
SpendXP(oCaster, costs.nXPCost);
SpendGP(oCaster, costs.nGoldCost);
DestroyObject(oSpellTarget);
FloatingTextStrRefOnCreature(8502, oCaster); // Item creation successful
if (!costs.nTimeCost) costs.nTimeCost = 1;
AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost));
return TRUE;
}
else
{
FloatingTextStringOnCreature("Infusion creation failed", oCaster); // Item creation failed
FloatingTextStrRefOnCreature(76417, oCaster); // Item creation failed
return TRUE;
}
/* // -------------------------------------------------------------------------
// Create the infused herb item
// -------------------------------------------------------------------------
object oInfusion = CICreateInfusion(oCaster, nID);
if (GetIsObjectValid(oInfusion))
{
SetIdentified(oInfusion, TRUE);
ActionPlayAnimation(ANIMATION_FIREFORGET_READ, 1.0);
SpendXP(oCaster, costs.nXPCost);
SpendGP(oCaster, costs.nGoldCost);
DestroyObject(oSpellTarget);
FloatingTextStrRefOnCreature(8502, oCaster); // Item creation successful
if (!costs.nTimeCost) costs.nTimeCost = 1;
AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost));
return TRUE;
}
else
{
FloatingTextStringOnCreature("Infusion creation failed", oCaster); // Item creation failed
FloatingTextStrRefOnCreature(76417, oCaster); // Item creation failed
return TRUE;
} */
return FALSE;
}
// -----------------------------------------------------------------------------
// Create and return an herbal infusion with an item property matching nSpellID
// -----------------------------------------------------------------------------
object CICreateInfusion(object oCreator, int nSpellID)
{
if (DEBUG) DoDebug("prc_x2_craft >> CICreateInfusion: Entering function");
// Keep the original spell id the engine gave us (may be a subradial)
int nSpellOriginal = nSpellID;
if (DEBUG) DoDebug("prc_x2_craft >> CICreateInfusion: nSpellOriginal is "+IntToString(nSpellOriginal)+".");
// Compute the master if this is a subradial. Keep original intact.
int nSpellMaster = nSpellOriginal;
if (GetIsSubradialSpell(nSpellOriginal))
{
nSpellMaster = GetMasterSpellFromSubradial(nSpellOriginal);
if (DEBUG) DoDebug("CICreateInfusion: detected subradial " + IntToString(nSpellOriginal) + " master -> " + IntToString(nSpellMaster));
}
if (DEBUG) DoDebug("prc_x2_craft >> CICreateInfusion: nSpellMaster is "+IntToString(nSpellMaster)+".");
// Try to find an iprp_spells row for the original subradial first (preferred).
int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellOriginal);
int nSpellUsedForIP = nSpellOriginal;
// If not found for original, fall back to the master/base spell.
if (nPropID < 0)
{
if (DEBUG) DoDebug("CICreateInfusion: no iprp row for original " + IntToString(nSpellOriginal) + ", trying master " + IntToString(nSpellMaster));
nPropID = IPGetIPConstCastSpellFromSpellID(nSpellMaster);
nSpellUsedForIP = nSpellMaster;
}
// If still invalid, bail out with a helpful message
if (nPropID < 0)
{
if (DEBUG) DoDebug("CICreateInfusion: No iprp_spells entry for either original " + IntToString(nSpellOriginal) + " or master " + IntToString(nSpellMaster));
FloatingTextStringOnCreature("This spell cannot be infused (no item property mapping).", oCreator, FALSE);
return OBJECT_INVALID;
}
if (DEBUG) DoDebug("CICreateInfusion: using spell " + IntToString(nSpellUsedForIP) + " (iprp row " + IntToString(nPropID) + ") for item property");
// Optional: check for material component (use the resolved iprp row)
string sMat = GetMaterialComponentTag(nPropID);
if (sMat != "")
{
object oMat = GetItemPossessedBy(oCreator, sMat);
if (oMat == OBJECT_INVALID)
{
FloatingTextStrRefOnCreature(83374, oCreator); // Missing material component
return OBJECT_INVALID;
}
else
{
DestroyObject(oMat);
}
}
// Only allow divine spellcasters
int nClass = PRCGetLastSpellCastClass();
if (!GetIsDivineClass(nClass))
{
FloatingTextStringOnCreature("Only divine casters can use Create Infusion.", oCreator, FALSE);
return OBJECT_INVALID;
}
// Create base infusion item (herb)
string sResRef = "prc_infusion_000";
object oTarget = CreateItemOnObject(sResRef, oCreator);
if (oTarget == OBJECT_INVALID)
{
WriteTimestampedLogEntry("Create Infusion failed: couldn't create item with resref " + sResRef);
return OBJECT_INVALID;
}
// Confirm that the item is a herb
int nBaseItem = GetBaseItemType(oTarget);
if (nBaseItem != BASE_ITEM_INFUSED_HERB)
{
FloatingTextStringOnCreature("Only herbs may be infused.", oCreator, FALSE);
DestroyObject(oTarget);
return OBJECT_INVALID;
}
// Remove all non-material item properties from the herb
itemproperty ipRemove = GetFirstItemProperty(oTarget);
while (GetIsItemPropertyValid(ipRemove))
{
itemproperty ipNext = GetNextItemProperty(oTarget);
if (GetItemPropertyType(ipRemove) != ITEM_PROPERTY_MATERIAL)
RemoveItemProperty(oTarget, ipRemove);
ipRemove = ipNext;
}
// Add the cast-spell itemproperty using the iprp row we resolved
itemproperty ipSpell = ItemPropertyCastSpell(nPropID, IP_CONST_CASTSPELL_NUMUSES_SINGLE_USE);
AddItemProperty(DURATION_TYPE_PERMANENT, ipSpell, oTarget);
// Optional PRC casting metadata: use the SAME spell id that matched the iprp row
// so caster level/DC/meta line up with the actual cast property on the item.
if (GetPRCSwitch(PRC_CREATE_INFUSION_CASTER_LEVEL))
{
int nCasterLevel = GetAlternativeCasterLevel(oCreator, PRCGetCasterLevel(oCreator));
// nSpellUsedForIP is either original (if that had an iprp row) or the master (fallback)
itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpellUsedForIP, nCasterLevel);
AddItemProperty(DURATION_TYPE_PERMANENT, ipLevel, oTarget);
itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpellUsedForIP, PRCGetMetaMagicFeat());
AddItemProperty(DURATION_TYPE_PERMANENT, ipMeta, oTarget);
int nDC = PRCGetSpellSaveDC(nSpellUsedForIP, GetSpellSchool(nSpellUsedForIP), OBJECT_SELF);
itemproperty ipDC = ItemPropertyCastSpellDC(nSpellUsedForIP, nDC);
AddItemProperty(DURATION_TYPE_PERMANENT, ipDC, oTarget);
}
return oTarget;
}
// Test main
//void main(){}
// void main(){}