What lightfoot said. We use a variable-based system to do this. The only trick is to use clear all actions to prevent them from avoiding the restrictions (there are common tricks to avoiding such restrictions, though I'm not going to go into specifics, just offer the fix).
Here's the core nugget of code on our module onequip:
if (!GetItemIsUsable(oItem, oPC)) {
ForceUnequip(oPC, oItem);
return;
}
Here's the ForceUnequip function, which prevents the common tricks to avoid scripted ILR :
void ForceUnequip (object oTarget, object oItem) {
if (!GetIsObjectValid(oTarget) || GetObjectType(oTarget) != OBJECT_TYPE_CREATURE)
return;
if (!GetIsObjectValid(GetArea(oTarget))) {
DelayCommand(5.0, ForceUnequip(oTarget, oItem));
return;
}
if (GetIsDead(oTarget)) {
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectResurrection(), oTarget);
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(GetMaxHitPoints(oTarget)), oTarget);
DelayCommand(0.1, ForceUnequip(oTarget, oItem));
} else {
AssignCommand(oTarget, ClearAllActions(TRUE));
AssignCommand(oTarget, ActionUnequipItem(oItem));
AssignCommand(oTarget, ActionDoCommand(SetCommandable(TRUE)));
AssignCommand(oTarget, SetCommandable(FALSE));
}
}
I've cut out a few server-specific items, but the core functionality is there.
As for the GetItemIsUsable function, it's an include of it's own. I'm including it to show you a variable-based ILR system that can restrict usage based on numerous factors besides level, like class, ability score, and the like. The code is acaos':
int GetHasItemFeatRequirements (object oItem, object oUser=OBJECT_SELF) {
int nBaseType = GetBaseItemType(oItem);
if (nBaseType == BASE_ITEM_SMALLSHIELD ||
nBaseType == BASE_ITEM_LARGESHIELD ||
nBaseType == BASE_ITEM_TOWERSHIELD) {
if (!GetHasFeat(FEAT_SHIELD_PROFICIENCY, oUser)) {
/* check specifically for the Ur-Ward and Impasse; consider
* examining the item's properties in the future instead of
* having a specific resref check */
string sRes = GetResRef(oItem);
return (sRes == "randsh104" || sRes == "randsh105");
}
return TRUE;
}
if (nBaseType == BASE_ITEM_ARMOR) {
int nBaseAC = GetItemACBase(oItem);
if (nBaseAC >= 6)
return GetHasFeat(FEAT_ARMOR_PROFICIENCY_HEAVY, oUser);
if (nBaseAC >= 4)
return GetHasFeat(FEAT_ARMOR_PROFICIENCY_MEDIUM, oUser);
if (nBaseAC >= 1)
return GetHasFeat(FEAT_ARMOR_PROFICIENCY_LIGHT, oUser);
return TRUE;
}
/* XXX: add support for weapon proficiency feats in the future */
return TRUE;
}
string GetHasItemTagRequirements (object oItem, object oUser=OBJECT_SELF) {
string sTag = GetTag(oItem);
string sRes = GetResRef(oItem);
if (GetStringLeft(sTag, 4) == "rtyg") {
if (!GetPCAccomplishment(oUser, PCACC_IMMORTAL))
return "You are not immortal and cannot use that!";
return "";
}
if (sRes == "lolthsmortalcoil" || sRes == "lolthsguard" || sRes == "bandofthedarkque" || sRes == "thespidersignet") {
if (!GetPCAccomplishment(oUser, PCACC_LOLTH))
return "You have not banished Lolth from this plane and cannot use that!";
return "";
}
if (GetStringLeft(sRes, == "dachitem") {
if (!GetPCAccomplishment(oUser, PCACC_DEEPONE))
return "You have not slain Dach'xilith'az'ichityl and cannot use that!";
return "";
}
if (GetStringLeft(sRes, 6) == "ssith5" && !GetItemIsAmmo(oItem)) {
if (!GetPCAccomplishment(oUser, PCACC_SSITHRAK))
return "You have not bathed in the blood of Ssssy'is and cannot use that!";
return "";
}
if (GetStringLeft(sRes, == "dustitem" || GetStringLeft(sRes, == "dustbbow") {
if (!GetPCAccomplishment(oUser, PCACC_DUSTBONE))
return "You have not slain Dustbone and cannot use that!";
return "";
}
if (GetStringLeft(sRes, 12) == "illithiditem") {
if (!GetPCAccomplishment(oUser, PCACC_ILLITHID))
return "You have not mastered the Overmind and cannot use that!";
/* don't return because some illithid items have additional checks below */
}
int nItemHellLevel = GetLocalInt(oItem, "HL");
if (nItemHellLevel > 0) {
if (nItemHellLevel > GetPCAccomplishment(oUser, PCACC_HELLS) || GetHitDiceIncludingLLs(oUser) < 55)
return "You have not conquered enough layers of the Hells to use that!";
}
/* ---- Sequencers for Casters only---- */
if (GetItemIsSequencer(oItem) &&
GetLevelByclass(class_TYPE_WIZARD, oUser) < 1 && GetLevelByclass(class_TYPE_BARD, oUser) < 1 &&
GetLevelByclass(class_TYPE_CLERIC, oUser) < 1 && GetLevelByclass(class_TYPE_DRUID, oUser) < 1 &&
GetLevelByclass(class_TYPE_SORCERER, oUser) < 1) {
return "You lack sufficient magical power to use that!";
}
/* Holy Symbols */
if ((sRes == "illithiditem023") && ((GetLevelByclass(class_TYPE_CLERIC, oUser) < 30) ||
(GetAlignmentGoodEvil(oUser) != ALIGNMENT_GOOD)))
return "You lack the faith and heart needed to wield that!";
if ((sRes == "illithiditem024") && ((GetLevelByclass(class_TYPE_CLERIC, oUser) < 30) ||
(GetAlignmentGoodEvil(oUser) != ALIGNMENT_EVIL)))
return "You lack the faith and evil needed to wield thatl!";
if (sTag == "HolySymbolofCyric" && ((GetAlignmentGoodEvil(oUser) != ALIGNMENT_EVIL) ||
(GetAlignmentLawChaos(oUser) != ALIGNMENT_CHAOTIC)))
return "You lack the faith needed to wield that!";
if (sTag == "HolySymbolofLathander" && ((GetAlignmentGoodEvil(oUser) != ALIGNMENT_GOOD) ||
(GetAlignmentLawChaos(oUser) != ALIGNMENT_NEUTRAL)))
return "You lack the faith needed to wield that!";
/* Exclusive Items */
int nExclusive = GetLocalInt(oItem, "Exclusive");
if (nExclusive) {
int i, nCheckExcl;
object oCheck;
for (i = 0; i < NUM_INVENTORY_SLOTS; i++) {
object oCheck = GetItemInSlot(i, oUser);
if (!GetIsObjectValid(oCheck = GetItemInSlot(i, oUser)) || oCheck == oItem)
continue;
int nCheckExcl = GetLocalInt(oCheck, "Exclusive");
if (nCheckExcl & nExclusive)
return "You may not use that in conjunction with your " + GetName(oCheck) + "!";
}
}
/* Ego items and relics */
int nRelic = GetLocalInt(oItem, "Relic");
if (nRelic) {
int i, nCheckRelic;
object oCheck;
for (i = 0; i < NUM_INVENTORY_SLOTS; i++) {
object oCheck = GetItemInSlot(i, oUser);
if (!GetIsObjectValid(oCheck = GetItemInSlot(i, oUser)) || oCheck == oItem)
continue;
int nCheckRelic = GetLocalInt(oCheck, "Relic");
if (nCheckRelic && (nRelic < 0 || nCheckRelic < 0 || nRelic != nCheckRelic))
return "You may not use that in conjunction with your " + GetName(oCheck) + "!";
}
}
int nTag = GetLocalInt(oItem, "ITR");
if (nTag && !GetPCAccomplishment(oUser, nTag))
return "You do not have the necessary accomplishment to use that!";
if (GetStringLeft(sRes, 7) == "abyweap") {
if (!GetPCAccomplishment(oUser, PCACC_ABYSS_DEMOGORGON_MINI1) &&
!GetPCAccomplishment(oUser, PCACC_ABYSS_GRAZZT_MINI1) &&
!GetPCAccomplishment(oUser, PCACC_ABYSS_JUIBLEX_MINI1) &&
!GetPCAccomplishment(oUser, PCACC_ABYSS_OBOXOB_MINI1) &&
!GetPCAccomplishment(oUser, PCACC_ABYSS_ORCUS_MINI1))
return "You do not have the necessary accomplishment to use that!";
}
return "";
}
int GetItemIsUsable (object oItem, object oUser=OBJECT_SELF, int bFeedback=TRUE) {
int nItemLevel = GetLocalInt(oItem, "ILR");
if (nItemLevel > GetHitDiceIncludingLLs(oUser)) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You must be level " + IntToString(nItemLevel) + " to use that!</c>",
oUser, FALSE);
return FALSE;
}
string sItemTag = GetHasItemTagRequirements(oItem, oUser);
if (sItemTag != "") {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>" + sItemTag + "</c>", oUser, FALSE);
return FALSE;
}
int nItemUMD = GetLocalInt(oItem, "IUMDR");
if (nItemUMD > 0 && GetSkillRank(SKILL_USE_MAGIC_DEVICE, oUser, TRUE) >= nItemUMD) {
if (bFeedback > 0)
FloatingTextStringOnCreature("<cþ>Your Use Magic Device skill allows you to use this item!></c>", oUser, FALSE);
return TRUE;
}
string sItemclass = GetLocalString(oItem, "ICLR");
if (sItemclass != "") {
int i, j, nAndCount, nOrCount;
nAndCount = GetStringSubStrings(sItemclass);
for (i = 0; i < nAndCount; i++) {
int nOr = 0;
string sAnd = GetStringSubString(sItemclass, i);
if (FindSubString(sAnd, "<") >= 0) {
int nclass = StringToInt(GetStringSubString(sAnd, 0, "<"));
int nRequire = StringToInt(GetStringSubString(sAnd, 1, "<"));
if (GetLevelByclass(nclass, oUser) >= nRequire) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>Your class levels are too high to use that!</c>",
oUser, FALSE);
return FALSE;
}
continue;
}
nOr = 0;
nOrCount = GetStringSubStrings(sAnd, "|");
for (j = 0; j < nOrCount; j++) {
string sOr = GetStringSubString(sAnd, j, "|");
int nclass = StringToInt(GetStringSubString(sOr, 0, "="));
int nRequire = StringToInt(GetStringSubString(sOr, 1, "="));
if (GetLevelByclass(nclass, oUser) >= nRequire)
nOr++;
}
if (nOrCount > 0 && nOr < 1) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You do not have the necessary class levels to use that!</c>",
oUser, FALSE);
return FALSE;
}
}
}
string sItemAbilReq = GetLocalString(oItem, "IAR");
if (sItemAbilReq != "") {
int i, j, nOr, nAndCount, nOrCount;
nAndCount = GetStringSubStrings(sItemAbilReq);
for (i = 0; i < nAndCount; i++) {
string sAnd = GetStringSubString(sItemAbilReq, i);
if (FindSubString(sAnd, "<") >= 0) {
int nRequire, nAbility = StringToInt(GetStringSubString(sAnd, 0, "<"));
string sRequire = GetStringSubString(sAnd, 1, "<");
if (GetStringLeft(sRequire, 1) == "*")
nRequire = GetAbilityScore(oUser, StringToInt(GetSubString(sRequire, 1, 1)));
else
nRequire = StringToInt(sRequire);
if (GetAbilityScore(oUser, nAbility, TRUE) >= nRequire) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>Your ability scores are too high to use that!</c>",
oUser, FALSE);
return FALSE;
}
continue;
}
nOr = 0;
nOrCount = GetStringSubStrings(sAnd, "|");
for (j = 0; j < nOrCount; j++) {
string sOr = GetStringSubString(sAnd, j, "|");
int nAbility = StringToInt(GetStringSubString(sOr, 0, "="));
int nRequire = StringToInt(GetStringSubString(sOr, 1, "="));
if (GetAbilityScore(oUser, nAbility, TRUE) >= nRequire)
nOr++;
}
if (nOrCount > 0 && nOr < 1) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You do not have the necessary ability scores to use that!</c>",
oUser, FALSE);
return FALSE;
}
}
}
string sItemFeatReq = GetLocalString(oItem, "IFR");
if (sItemFeatReq != "") {
int i, j, nOr, nAndCount, nOrCount;
nAndCount = GetStringSubStrings(sItemFeatReq);
for (i = 0; i < nAndCount; i++) {
string sAnd = GetStringSubString(sItemFeatReq, i);
nOr = 0;
nOrCount = GetStringSubStrings(sAnd, "|");
for (j = 0; j < nOrCount; j++) {
string sOr = GetStringSubString(sAnd, j, "|");
int nFeat = StringToInt(sOr);
if (GetKnowsFeat(nFeat, oUser))
nOr++;
}
if (nOrCount > 0 && nOr < 1) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You do not have the necessary feats to use that!</c>",
oUser, FALSE);
return FALSE;
}
}
}
string sItemSkillReq = GetLocalString(oItem, "ISR");
if (sItemSkillReq != "") {
int i, j, nOr, nAndCount, nOrCount;
nAndCount = GetStringSubStrings(sItemSkillReq);
for (i = 0; i < nAndCount; i++) {
string sAnd = GetStringSubString(sItemSkillReq, i);
if (FindSubString(sAnd, "<") >= 0) {
int nSkill = StringToInt(GetStringSubString(sAnd, 0, "<"));
int nRequire = StringToInt(GetStringSubString(sAnd, 1, "<"));
if (GetSkillRank(nSkill, oUser, TRUE) >= nRequire) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>Your skills are too high to use that!</c>",
oUser, FALSE);
return FALSE;
}
continue;
}
nOr = 0;
nOrCount = GetStringSubStrings(sAnd, "|");
for (j = 0; j < nOrCount; j++) {
string sOr = GetStringSubString(sAnd, j, "|");
int nSkill = StringToInt(GetStringSubString(sOr, 0, "="));
int nRequire = StringToInt(GetStringSubString(sOr, 1, "="));
if (GetSkillRank(nSkill, oUser, TRUE) >= nRequire)
nOr++;
}
if (nOrCount > 0 && nOr < 1) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You do not have the necessary skills to use that!</c>",
oUser, FALSE);
return FALSE;
}
}
}
int nItemQuasi = GetLocalInt(oItem, "IQCR");
if (nItemQuasi != 0) {
if (nItemQuasi < 0 && GetQuasiclass(oUser) != 0) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You cannot use that if you belong to a quasiclass!</c>", oUser, FALSE);
return FALSE;
} else if (nItemQuasi > 0 && GetQuasiclass(oUser) != nItemQuasi) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You do not belong to the right quasiclass to use that!</c>", oUser, FALSE);
return FALSE;
}
}
int nItemRace = GetLocalInt(oItem, "IRR");
if (nItemRace != 0) {
int bExclude = FALSE, nRace = GetRacialType(oUser);
if (nItemRace < 0) {
bExclude = TRUE;
nItemRace = -nItemRace;
}
if (GetStringLowerCase(GetSubRace(oUser)) == "juggernaut" ||
GetQuasiclass(oUser) == QUASIclass_ACCURSED_PARIAH)
nRace = RACIAL_TYPE_CONSTRUCT;
nItemRace -= 1;
if (bExclude && nRace == nItemRace) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>Your race cannot use that!</c>", oUser, FALSE);
return FALSE;
} else if (!bExclude && nRace != nItemRace) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You do not have the necessary race to use that!</c>", oUser, FALSE);
return FALSE;
}
}
int nItemAlign = GetLocalInt(oItem, "IAGER");
if (nItemAlign > 0 && GetAlignmentGoodEvil(oUser) != nItemAlign) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You do not have the necessary alignment to use that!</c>", oUser, FALSE);
return FALSE;
}
nItemAlign = GetLocalInt(oItem, "IALCR");
if (nItemAlign > 0 && GetAlignmentLawChaos(oUser) != nItemAlign) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You do not have the necessary alignment to use that!</c>", oUser, FALSE);
return FALSE;
}
string sItemInv = GetLocalString(oItem, "IIR");
if (sItemInv != "" && !GetIsObjectValid(GetItemPossessedBy(oUser, sItemInv))) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You do not have the necessary item in inventory to use that!</c>", oUser, FALSE);
return FALSE;
}
if (GetLocalInt(oItem, "NoOffhand") &&
oItem == GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oUser)) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You cannot use that in your off hand!</c>", oUser, FALSE);
return FALSE;
}
int nDisequip = GetLocalInt(oItem, "Disequipped");
if (nDisequip) {
int nRealTime = GetLocalInt(GetModule(), "realtime");
if (nRealTime > 0 && nDisequip > nRealTime) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>You are still recovering that item!</c>", oUser, FALSE);
return FALSE;
}
}
int nDiseaseBlock = GetLocalInt(oUser, "DiseaseBlockEquip");
if (nDiseaseBlock && GetIsObjectValid(oItem)) {
int i;
for (i = 0; i < 11; i++) {
if (!(nDiseaseBlock & (1 << i)))
continue;
if (oItem == GetItemInSlot(i, oUser)) {
if (bFeedback)
FloatingTextStringOnCreature("<cþ>Your limbs are too rotted to equip that!</c>", oUser, FALSE);
return FALSE;
}
}
}
return TRUE;
}
Funky