Major script update for 8 class support. prc_wipeNSB.nss PRC_S_spellb.nss prc_amagsys_gain.nss - AMSCompatibilityCheck() prc_prereq.nss - Dragonheart(), KnightWeave() prc_onenter.nss - OnEnter_AMSCompatibilityCheck() prc_metamagic.nss - GetHasSpontaneousNSBClass() prc_feats.nss prc_dracactive.nss prc_debug_hfeatm.nss prc_cbtmed_spnhl.nss psi_powconv.nss psi_pow_bstpwr.nss x2_pc_umdcheck.nss
405 lines
15 KiB
Plaintext
405 lines
15 KiB
Plaintext
//:: Updated for .35 by Jaysyn 2023/03/11
|
|
|
|
#include "inc_leto_prc"
|
|
#include "x2_inc_switches"
|
|
#include "prc_inc_teleport"
|
|
#include "prc_inc_leadersh"
|
|
#include "prc_inc_domain"
|
|
#include "prc_inc_shifting"
|
|
#include "true_inc_trufunc"
|
|
#include "prc_craft_inc"
|
|
#include "prc_inc_dragsham"
|
|
#include "shd_inc_myst"
|
|
#include "prc_inc_template"
|
|
|
|
/**
|
|
* Reads the 2da file onenter_locals.2da and sets local variables
|
|
* on the entering PC accordingly. 2da format same as personal_switches.2da,
|
|
* see prc_inc_switches header comment for details.
|
|
*
|
|
* @param oPC The PC that just entered the module
|
|
*/
|
|
void DoAutoLocals(object oPC)
|
|
{
|
|
int i = 0;
|
|
string sSwitchName, sSwitchType, sSwitchValue;
|
|
// Use Get2DAString() instead of Get2DACache() to avoid caching.
|
|
while((sSwitchName = Get2DAString("onenter_locals", "SwitchName", i)) != "")
|
|
{
|
|
// Read rest of the line
|
|
sSwitchType = Get2DAString("onenter_locals", "SwitchType", i);
|
|
sSwitchValue = Get2DAString("onenter_locals", "SwitchValue", i);
|
|
|
|
// Determine switch type and set the var
|
|
if (sSwitchType == "float")
|
|
SetLocalFloat(oPC, sSwitchName, StringToFloat(sSwitchValue));
|
|
else if(sSwitchType == "int")
|
|
SetLocalInt(oPC, sSwitchName, StringToInt(sSwitchValue));
|
|
else if(sSwitchType == "string")
|
|
SetLocalString(oPC, sSwitchName, sSwitchValue);
|
|
|
|
// Increment loop counter
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
//Restore appearance
|
|
void RestoreAppearance(object oCreature)
|
|
{
|
|
if(!RestoreTrueAppearance(oCreature) && DEBUG)
|
|
DoDebug("prc_onenter: RestoreAppearance failed");
|
|
}
|
|
|
|
void CopyAMSArray(object oHideToken, object oAMSToken, int nClass, string sArray, int nMin, int nMax, int nLoopSize = 100)
|
|
{
|
|
int i = nMin;
|
|
while(i < nMin + nLoopSize && i < nMax)
|
|
{
|
|
int nSpell = array_get_int(oAMSToken, sArray, i);
|
|
int nSpellbookID = RealSpellToSpellbookID(nClass, nSpell);
|
|
if(nSpellbookID)
|
|
array_set_int(oHideToken, sArray, i, nSpellbookID);
|
|
i++;
|
|
}
|
|
if(i < nMax)
|
|
DelayCommand(0.0, CopyAMSArray(oHideToken, oAMSToken, nClass, sArray, i, nMax));
|
|
}
|
|
|
|
void DoRestoreAMS(object oPC, int nClass, object oHideToken, object oAMSToken)
|
|
{
|
|
string sSpellbook;
|
|
int nSpellbookType = GetSpellbookTypeForClass(nClass);
|
|
if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS)
|
|
{
|
|
sSpellbook = "Spellbook"+IntToString(nClass);
|
|
if(DEBUG) DoDebug("DoRestoreAMS: "+sSpellbook);
|
|
if(persistant_array_exists(oPC, sSpellbook))
|
|
array_delete(oHideToken, sSpellbook);
|
|
array_create(oHideToken, sSpellbook);
|
|
int nSize = array_get_size(oAMSToken, sSpellbook);
|
|
if(DEBUG) DoDebug("DoRestoreAMS: array size = "+IntToString(nSize));
|
|
if(nSize)
|
|
DelayCommand(0.0, CopyAMSArray(oHideToken, oAMSToken, nClass, sSpellbook, 0, nSize));
|
|
}
|
|
else if(nSpellbookType == SPELLBOOK_TYPE_PREPARED)
|
|
{
|
|
int i;
|
|
for(i = 0; i <= 9; i++)
|
|
{
|
|
sSpellbook = "Spellbook_Known_"+IntToString(nClass)+"_"+IntToString(i);
|
|
if(DEBUG) DoDebug("DoRestoreAMS: "+sSpellbook);
|
|
if(array_exists(oHideToken, sSpellbook))
|
|
array_delete(oHideToken, sSpellbook);
|
|
array_create(oHideToken, sSpellbook);
|
|
int nSize = array_get_size(oAMSToken, sSpellbook);
|
|
if(DEBUG) DoDebug("DoRestoreAMS: array size = "+IntToString(nSize));
|
|
if(nSize)
|
|
DelayCommand(0.0, CopyAMSArray(oHideToken, oAMSToken, nClass, sSpellbook, 0, nSize));
|
|
array_delete(oHideToken, "Spellbook"+IntToString(i)+"_"+IntToString(nClass));
|
|
array_delete(oHideToken, "NewSpellbookMem_"+IntToString(nClass));
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnEnter_AMSCompatibilityCheck(object oPC)
|
|
{
|
|
object oAMSToken = GetHideToken(oPC, TRUE);
|
|
object oHideToken = GetHideToken(oPC);
|
|
|
|
//check PRC version
|
|
string sVersion = GetLocalString(oAMSToken, "ams_version");
|
|
//DoDebug("AMS version = "+sVersion);
|
|
if(sVersion != AMS_VERSION)
|
|
{
|
|
SetLocalInt(oPC, "AMS_RESTORE", 1);
|
|
int i;
|
|
for(i = 1; i <= 8; i++)
|
|
{
|
|
int nClass = GetClassByPosition(i, oPC);
|
|
DelayCommand(0.2, DoRestoreAMS(oPC, nClass, oHideToken, oAMSToken));
|
|
}
|
|
DelayCommand(2.0, WipeSpellbookHideFeats(oPC));
|
|
SetLocalString(oAMSToken, "ams_version", AMS_VERSION);
|
|
DelayCommand(5.0, DeleteLocalInt(oPC, "AMS_RESTORE"));
|
|
}
|
|
}
|
|
|
|
void main()
|
|
{
|
|
//The composite properties system gets confused when an exported
|
|
//character re-enters. Local Variables are lost and most properties
|
|
//get re-added, sometimes resulting in larger than normal bonuses.
|
|
//The only real solution is to wipe the skin on entry. This will
|
|
//mess up the lich, but only until I hook it into the EvalPRC event -
|
|
//hopefully in the next update
|
|
// -Aaon Graywolf
|
|
object oPC = GetEnteringObject();
|
|
|
|
//FloatingTextStringOnCreature("PRC on enter was called", oPC, FALSE);
|
|
|
|
// Since OnEnter event fires for the PC when loading a saved game (no idea why,
|
|
// since it makes saving and reloading change the state of the module),
|
|
// make sure that the event gets run only once
|
|
// but not in MP games, so check if it's single player or not!!
|
|
if(GetLocalInt(oPC, "PRC_ModuleOnEnterDone") && (GetPCPublicCDKey(oPC) == ""))
|
|
return;
|
|
// Use a local integer to mark the event as done for the PC, so that it gets
|
|
// cleared when the character is saved.
|
|
else
|
|
SetLocalInt(oPC, "PRC_ModuleOnEnterDone", TRUE);
|
|
|
|
//if server is loading, boot player
|
|
if(GetLocalInt(GetModule(), PRC_PW_LOGON_DELAY+"_TIMER"))
|
|
{
|
|
BootPC(oPC);
|
|
return;
|
|
}
|
|
|
|
DoDebug("Running modified prc_onenter with PW CD check");
|
|
// Verify players CD key
|
|
if(GetPRCSwitch(PRC_PW_SECURITY_CD_CHECK))
|
|
{
|
|
DoDebug("PRC_PW_SECURITY_CD_CHECK switch set on the module");
|
|
if(ExecuteScriptAndReturnInt("prc_onenter_cd", oPC))
|
|
return;
|
|
}
|
|
DoDebug("CD-check OK - continue executing prc_onenter");
|
|
|
|
|
|
// Prevent Items on area creation from trigering the GetPCSkin script
|
|
if (GetObjectType(oPC) != OBJECT_TYPE_CREATURE)
|
|
return;
|
|
|
|
// return here for DMs as they don't need all this stuff
|
|
if(GetIsDM(oPC))
|
|
return;
|
|
|
|
// Setup class info for EvalPRCFeats()
|
|
SetupCharacterData(oPC);
|
|
|
|
//do this first so other things dont interfere with it
|
|
if(GetPRCSwitch(PRC_USE_LETOSCRIPT))
|
|
LetoPCEnter(oPC);
|
|
if(GetPRCSwitch(PRC_CONVOCC_ENABLE) && !GetPRCSwitch(PRC_CONVOCC_CUSTOM_START_LOCATION) && ExecuteScriptAndReturnInt("prc_ccc_main", OBJECT_SELF))
|
|
return;
|
|
|
|
// ebonfowl: taking a risk here and commenting this out, it seems obsolete and it is very hard to fix without having an AMS token
|
|
//check spellbooks for compatibility with other PRC versions
|
|
//DelayCommand(10.0f, OnEnter_AMSCompatibilityCheck(oPC));
|
|
|
|
object oSkin = GetPCSkin(oPC);
|
|
ScrubPCSkin(oPC, oSkin);
|
|
DeletePRCLocalInts(oSkin);
|
|
|
|
// Gives people the proper spells from their bonus domains
|
|
// This should run before EvalPRCFeats, because it sets a variable
|
|
DelayCommand(0.0, CheckBonusDomains(oPC));
|
|
// Set the uses per day for domains
|
|
DelayCommand(0.0, BonusDomainRest(oPC));
|
|
// Clear old variables for AMS
|
|
DelayCommand(0.0, ClearLawLocalVars(oPC));
|
|
DelayCommand(0.0, ClearMystLocalVars(oPC));
|
|
DelayCommand(0.0, ClearLegacyUses(oPC));
|
|
DelayCommand(0.0, DeleteLocalInt(oPC, "RestTimer"));
|
|
|
|
//remove effects from hides, can stack otherwise
|
|
effect eTest=GetFirstEffect(oPC);
|
|
|
|
while (GetIsEffectValid(eTest))
|
|
{
|
|
if(GetEffectSubType(eTest) == SUBTYPE_SUPERNATURAL
|
|
&& (GetEffectType(eTest) == EFFECT_TYPE_MOVEMENT_SPEED_DECREASE
|
|
|| GetEffectType(eTest) == EFFECT_TYPE_MOVEMENT_SPEED_INCREASE
|
|
//add other types here
|
|
)
|
|
&& !GetIsObjectValid(GetEffectCreator(eTest))
|
|
)
|
|
RemoveEffect(oPC, eTest);
|
|
eTest=GetNextEffect(oPC);
|
|
}
|
|
|
|
SetLocalInt(oPC,"ONENTER",1);
|
|
// Make sure we reapply any bonuses before the player notices they are gone.
|
|
DelayCommand(0.1, EvalPRCFeats(oPC));
|
|
DelayCommand(0.1, FeatSpecialUsePerDay(oPC));
|
|
// Check to see which special prc requirements (i.e. those that can't be done)
|
|
// through the .2da's, the entering player already meets.
|
|
ExecuteScript("prc_prereq", oPC);
|
|
ExecuteScript("prc_psi_ppoints", oPC);
|
|
ResetTouchOfVitality(oPC);
|
|
DelayCommand(0.15, DeleteLocalInt(oPC,"ONENTER"));
|
|
|
|
if(GetPRCSwitch(PRC_LETOSCRIPT_FIX_ABILITIES) && !GetIsDM(oPC))
|
|
PRCLetoEnter(oPC);
|
|
|
|
//PW tracking starts here
|
|
|
|
if(GetPRCSwitch(PRC_PNP_DEATH_ENABLE))
|
|
{
|
|
//cleanup.
|
|
int nStatus = GetPersistantLocalInt(oPC, "STATUS");
|
|
if (nStatus != ALIVE)
|
|
AddEventScript(oPC, EVENT_ONHEARTBEAT, "prc_timer_dying", TRUE, FALSE);
|
|
// Make us fall over if we should be on the floor.
|
|
if (nStatus == BLEEDING || STABLE || DEAD)
|
|
AssignCommand(oPC, DelayCommand(0.03, PlayAnimation(ANIMATION_LOOPING_DEAD_BACK, 1.0, 4.0)));
|
|
// If PRC Death is enabled we require HP tracking too
|
|
SetPRCSwitch(PRC_PW_HP_TRACKING, TRUE);
|
|
}
|
|
if(GetPRCSwitch(PRC_PW_HP_TRACKING))
|
|
{
|
|
// Make sure we actually have stored HP data to read
|
|
if(GetPersistantLocalInt(oPC, "persist_HP_stored") == -1)
|
|
{
|
|
// Read the stored level and set accordingly.
|
|
int nHP = GetPersistantLocalInt(oPC, "persist_HP");
|
|
int nDamage = GetCurrentHitPoints(oPC)-nHP;
|
|
|
|
if (nDamage >= 1) ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamage, DAMAGE_TYPE_MAGICAL), oPC);
|
|
}
|
|
|
|
}
|
|
if(GetPRCSwitch(PRC_PW_TIME))
|
|
{
|
|
struct time tTime = GetPersistantLocalTime(oPC, "persist_Time");
|
|
//first pc logging on
|
|
if(GetIsObjectValid(GetFirstPC())
|
|
&& !GetIsObjectValid(GetNextPC()))
|
|
{
|
|
SetTimeAndDate(tTime);
|
|
}
|
|
RecalculateTime();
|
|
}
|
|
if(GetPRCSwitch(PRC_PW_LOCATION_TRACKING))
|
|
{
|
|
struct metalocation lLoc = GetPersistantLocalMetalocation(oPC, "persist_loc");
|
|
DelayCommand(6.0, AssignCommand(oPC, JumpToLocation(MetalocationToLocation(lLoc))));
|
|
}
|
|
if(GetPRCSwitch(PRC_PW_MAPPIN_TRACKING)
|
|
&& !GetLocalInt(oPC, "PRC_PW_MAPPIN_TRACKING_Done"))
|
|
{
|
|
//this local is set so that this is only done once per server session
|
|
SetLocalInt(oPC, "PRC_PW_MAPPIN_TRACKING_Done", TRUE);
|
|
int nCount = GetPersistantLocalInt(oPC, "MapPinCount");
|
|
int i;
|
|
for(i=1; i<=nCount; i++)
|
|
{
|
|
struct metalocation mlocLoc = GetPersistantLocalMetalocation(oPC, "MapPin_"+IntToString(i));
|
|
CreateMapPinFromMetalocation(mlocLoc, oPC);
|
|
}
|
|
}
|
|
if(GetPRCSwitch(PRC_PW_DEATH_TRACKING))
|
|
{
|
|
if(GetPersistantLocalInt(oPC, "persist_dead"))
|
|
{
|
|
int nDamage=9999;
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(DAMAGE_TYPE_MAGICAL, nDamage), oPC);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(), oPC);
|
|
}
|
|
}
|
|
if(GetPRCSwitch(PRC_PW_SPELL_TRACKING))
|
|
{
|
|
string sSpellList = GetPersistantLocalString(oPC, "persist_spells");
|
|
string sTest;
|
|
string sChar;
|
|
while(GetStringLength(sSpellList))
|
|
{
|
|
sChar = GetStringLeft(sSpellList,1);
|
|
if(sChar == "|")
|
|
{
|
|
int nSpell = StringToInt(sTest);
|
|
DecrementRemainingSpellUses(oPC, nSpell);
|
|
sTest == "";
|
|
}
|
|
else
|
|
sTest += sChar;
|
|
sSpellList = GetStringRight(sSpellList, GetStringLength(sSpellList)-1);
|
|
}
|
|
}
|
|
//check for persistant golems
|
|
if(persistant_array_exists(oPC, "GolemList"))
|
|
{
|
|
MultisummonPreSummon(oPC, TRUE);
|
|
int i;
|
|
for(i=1;i<persistant_array_get_size(oPC, "GolemList");i++)
|
|
{
|
|
string sResRef = persistant_array_get_string(oPC, "GolemList",i);
|
|
effect eSummon = SupernaturalEffect(EffectSummonCreature(sResRef));
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSummon, oPC);
|
|
}
|
|
}
|
|
|
|
if(GetPRCSwitch(PRC_PNP_ANIMATE_DEAD) || GetPRCSwitch(PRC_MULTISUMMON))
|
|
{
|
|
MultisummonPreSummon(oPC, TRUE);
|
|
}
|
|
|
|
// Create map pins from marked teleport locations if the PC has requested that such be done.
|
|
if(GetLocalInt(oPC, PRC_TELEPORT_CREATE_MAP_PINS))
|
|
DelayCommand(10.0f, TeleportLocationsToMapPins(oPC));
|
|
|
|
if(GetPRCSwitch(PRC_XP_USE_SIMPLE_RACIAL_HD)
|
|
&& !GetXP(oPC))
|
|
{
|
|
int nRealRace = GetRacialType(oPC);
|
|
int nRacialHD = StringToInt(Get2DACache("ECL", "RaceHD", nRealRace));
|
|
int nRacialClass = StringToInt(Get2DACache("ECL", "RaceClass", nRealRace));
|
|
if(nRacialHD)
|
|
{
|
|
if(!GetPRCSwitch(PRC_XP_USE_SIMPLE_RACIAL_HD_NO_FREE_XP))
|
|
{
|
|
int nNewXP = nRacialHD*(nRacialHD+1)*500; //+1 for the original class level
|
|
SetXP(oPC, nNewXP);
|
|
if(GetPRCSwitch(PRC_XP_USE_SIMPLE_LA))
|
|
DelayCommand(1.0, SetPersistantLocalInt(oPC, sXP_AT_LAST_HEARTBEAT, nNewXP));
|
|
}
|
|
if(GetPRCSwitch(PRC_XP_USE_SIMPLE_RACIAL_HD_NO_SELECTION))
|
|
{
|
|
int i;
|
|
for(i=0;i<nRacialHD;i++)
|
|
{
|
|
LevelUpHenchman(oPC, nRacialClass, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Insert various debug things here
|
|
if(DEBUG)
|
|
{
|
|
// Duplicate PRCItemPropertyBonusFeat monitor
|
|
SpawnNewThread("PRC_Duplicate_IPBFeat_Mon", "prc_debug_hfeatm", 30.0f, oPC);
|
|
}
|
|
|
|
if(GetHasFeat(FEAT_SPELLFIRE_WIELDER, oPC))
|
|
SpawnNewThread("PRC_Spellfire", "prc_spellfire_hb", 6.0f, oPC);
|
|
|
|
//if the player logged off while being registered as a cohort
|
|
if(GetPersistantLocalInt(oPC, "RegisteringAsCohort"))
|
|
AssignCommand(GetModule(), CheckHB(oPC));
|
|
|
|
// If the PC logs in shifted, unshift them
|
|
if(GetPersistantLocalInt(oPC, SHIFTER_ISSHIFTED_MARKER))
|
|
UnShift(oPC);
|
|
if(GetPRCSwitch(PRC_ON_ENTER_RESTORE_APPEARANCE))
|
|
DelayCommand(2.0f, RestoreAppearance(oPC));
|
|
|
|
// Set up local variables based on a 2da file
|
|
DelayCommand(1.0 ,DoAutoLocals(oPC));
|
|
|
|
ExecuteScript("tob_evnt_recover", oPC);
|
|
|
|
// This prevents the LA function from eating XP on login
|
|
SetLocalInt(oPC, "PRC_ECL_Delay", TRUE);
|
|
SetPersistantLocalInt(oPC, sXP_AT_LAST_HEARTBEAT, GetXP(oPC));
|
|
DelayCommand(10.0, DeleteLocalInt(oPC, "PRC_ECL_Delay"));
|
|
|
|
// Execute scripts hooked to this event for the player triggering it
|
|
//How can this work? The PC isnt a valid object before this. - Primogenitor
|
|
//Some stuff may have gone "When this PC next logs in, run script X" - Ornedan
|
|
ExecuteAllScriptsHookedToEvent(oPC, EVENT_ONCLIENTENTER);
|
|
|
|
// Start the PC HeartBeat for PC's
|
|
if(GetIsPC(oPC))
|
|
AssignCommand(GetModule(), ExecuteScript("prc_onhb_indiv", oPC));
|
|
} |