From df731f3bd4a7efffd9215ed5650eef1699a65526 Mon Sep 17 00:00:00 2001 From: Jaysyn904 <68194417+Jaysyn904@users.noreply.github.com> Date: Mon, 27 Apr 2026 20:49:54 -0400 Subject: [PATCH] 2026/04/27 Evening Update Added PRC Option to change player model to appearances in racialappear.2da Added class heartbeat script and updated Soul Eater's onHit to work with NPCs and cohorts. --- nwn/nwnprc/trunk/include/inc_2dacache.nss | 1 - nwn/nwnprc/trunk/include/inc_cache_setup.nss | 30 +++++++ nwn/nwnprc/trunk/include/inc_switch_setup.nss | 5 +- nwn/nwnprc/trunk/include/prc_inc_function.nss | 2 +- nwn/nwnprc/trunk/include/prc_inc_switch.nss | 4 + nwn/nwnprc/trunk/include/prc_inc_unarmed.nss | 7 ++ nwn/nwnprc/trunk/scripts/prc_souleater.nss | 47 +++++++++++ nwn/nwnprc/trunk/scripts/prc_switchesc.nss | 84 +++++++++++++++++-- 8 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 nwn/nwnprc/trunk/scripts/prc_souleater.nss diff --git a/nwn/nwnprc/trunk/include/inc_2dacache.nss b/nwn/nwnprc/trunk/include/inc_2dacache.nss index 7ef2c616..51c5671c 100644 --- a/nwn/nwnprc/trunk/include/inc_2dacache.nss +++ b/nwn/nwnprc/trunk/include/inc_2dacache.nss @@ -142,7 +142,6 @@ string GetBiowareDBName() if(GetPRCSwitch(MARKER_Q)) sReturn += "q"; return sReturn; - } /** diff --git a/nwn/nwnprc/trunk/include/inc_cache_setup.nss b/nwn/nwnprc/trunk/include/inc_cache_setup.nss index a061fa2b..f31990cd 100644 --- a/nwn/nwnprc/trunk/include/inc_cache_setup.nss +++ b/nwn/nwnprc/trunk/include/inc_cache_setup.nss @@ -355,8 +355,38 @@ void Cache_Appearance(int nRow = 0) DelayCommand(1.0, Cache_Soundset()); } +void Cache_RacialAppear(int nRow = 0) +{ + if(nRow < GetPRCSwitch(FILE_END_RACIALAPPEAR)) + { + Get2DACache("racialappear", "Label", nRow); + Get2DACache("racialappear", "Male1", nRow); + Get2DACache("racialappear", "Male2", nRow); + Get2DACache("racialappear", "Male3", nRow); + Get2DACache("racialappear", "Male4", nRow); + Get2DACache("racialappear", "Male5", nRow); + Get2DACache("racialappear", "Female1", nRow); + Get2DACache("racialappear", "Female2", nRow); + Get2DACache("racialappear", "Female3", nRow); + Get2DACache("racialappear", "Female4", nRow); + Get2DACache("racialappear", "Female5", nRow); + Get2DACache("racialappear", "Wing1", nRow); + Get2DACache("racialappear", "Wing2", nRow); + Get2DACache("racialappear", "Wing3", nRow); + Get2DACache("racialappear", "Tail1", nRow); + Get2DACache("racialappear", "Tail2", nRow); + Get2DACache("racialappear", "Tail3", nRow); + + nRow++; + DelayCommand(0.1, Cache_RacialAppear(nRow)); + } + else + DelayCommand(1.0, Cache_Appearance()); +} + void Cache_2da_data() { Cache_Appearance(); + Cache_RacialAppear(); } diff --git a/nwn/nwnprc/trunk/include/inc_switch_setup.nss b/nwn/nwnprc/trunk/include/inc_switch_setup.nss index 2f38bbdc..9fab92d1 100644 --- a/nwn/nwnprc/trunk/include/inc_switch_setup.nss +++ b/nwn/nwnprc/trunk/include/inc_switch_setup.nss @@ -835,8 +835,8 @@ void SetDefaultFileEnds() //isnt read in here yet. may be later though if(GetPRCSwitch(FILE_END_MANUAL)) return; - SetPRCSwitch(FILE_END_CLASSES, PRCGetFileEnd("classes")); - SetPRCSwitch(FILE_END_RACIALTYPES, PRCGetFileEnd("racialtypes")); + SetPRCSwitch(FILE_END_CLASSES, PRCGetFileEnd("classes")); + SetPRCSwitch(FILE_END_RACIALTYPES, PRCGetFileEnd("racialtypes")); SetPRCSwitch(FILE_END_GENDER, 1);//overriden to 1 for convoCC m/f only choice SetPRCSwitch(FILE_END_PORTRAITS, PRCGetFileEnd("portraits")); SetPRCSwitch(FILE_END_SKILLS, PRCGetFileEnd("skills")); @@ -851,6 +851,7 @@ void SetDefaultFileEnds() SetPRCSwitch(FILE_END_SPELLS, PRCGetFileEnd("spells")); //SetPRCSwitch(FILE_END_SPELLSCHOOL, PRCGetFileEnd("spellschools")); SetPRCSwitch(FILE_END_APPEARANCE, PRCGetFileEnd("appearance")); + SetPRCSwitch(FILE_END_RACIALAPPEAR, PRCGetFileEnd("racialappear")); SetPRCSwitch(FILE_END_WINGS, PRCGetFileEnd("wingmodel")); SetPRCSwitch(FILE_END_TAILS, PRCGetFileEnd("tailmodel")); SetPRCSwitch(FILE_END_BASEITEMS, PRCGetFileEnd("baseitems")); diff --git a/nwn/nwnprc/trunk/include/prc_inc_function.nss b/nwn/nwnprc/trunk/include/prc_inc_function.nss index 758f773a..99568a35 100644 --- a/nwn/nwnprc/trunk/include/prc_inc_function.nss +++ b/nwn/nwnprc/trunk/include/prc_inc_function.nss @@ -226,7 +226,7 @@ void SetupCharacterData(object oPC) case CLASS_TYPE_SOLDIER_OF_LIGHT: sScript = "prc_soldoflight"; break; case CLASS_TYPE_SORCERER: iData |= 0x03; break; case CLASS_TYPE_SOULBORN: sScript = "moi_soulborn"; break; - case CLASS_TYPE_SOUL_EATER: iShifting = TRUE; break; + case CLASS_TYPE_SOUL_EATER: sScript = "prc_souleater"; iShifting = TRUE; break; case CLASS_TYPE_SOULKNIFE: sScript = "psi_sk_clseval"; break; case CLASS_TYPE_SPELLSWORD: sScript = "prc_spellswd"; iData |= 0x04; break; case CLASS_TYPE_SPINEMELD_WARRIOR: sScript = "moi_spinemeld"; break; diff --git a/nwn/nwnprc/trunk/include/prc_inc_switch.nss b/nwn/nwnprc/trunk/include/prc_inc_switch.nss index 56a97747..2afe613c 100644 --- a/nwn/nwnprc/trunk/include/prc_inc_switch.nss +++ b/nwn/nwnprc/trunk/include/prc_inc_switch.nss @@ -1397,6 +1397,10 @@ const string FILE_END_SPELLS = "FILE_END_SPELLS"; * This will be set automatically to a default unless FILE_END_MANUAL is turned on */ const string FILE_END_APPEARANCE = "FILE_END_APPEARANCE"; +/** Last line of racalappear.2da + * This will be set automatically to a default unless FILE_END_MANUAL is turned on */ +const string FILE_END_RACIALAPPEAR = "FILE_END_RACIALAPPEAR"; + /** Last line of wingmodel.2da * This will be set automatically to a default unless FILE_END_MANUAL is turned on */ const string FILE_END_WINGS = "FILE_END_WINGS"; diff --git a/nwn/nwnprc/trunk/include/prc_inc_unarmed.nss b/nwn/nwnprc/trunk/include/prc_inc_unarmed.nss index 8f629cb7..c10c0115 100644 --- a/nwn/nwnprc/trunk/include/prc_inc_unarmed.nss +++ b/nwn/nwnprc/trunk/include/prc_inc_unarmed.nss @@ -86,6 +86,7 @@ float DamageAvg(int iDamage); //#include "prc_spell_const" //#include "inc_utility" #include "prc_inc_natweap" +#include "prc_inc_onhit" ////////////////////////////////////////////////// /* Function defintions */ @@ -546,6 +547,12 @@ void UnarmedFists(object oCreature) AssignCommand(oCreature,ActionEquipItem(oWeapL, INVENTORY_SLOT_CWEAPON_L)); } } + + // Add Soul Eater onHit property if applicable + if (GetLevelByClass(CLASS_TYPE_SOUL_EATER, oCreature) > 0) + { + Add_OnHitUniquePower(oWeapL, "prc_uni_shift"); + } int iKi = (iMonkEq > 9) ? 1 : 0; iKi = (iMonkEq > 12) ? 2 : iKi; diff --git a/nwn/nwnprc/trunk/scripts/prc_souleater.nss b/nwn/nwnprc/trunk/scripts/prc_souleater.nss new file mode 100644 index 00000000..3259cc5f --- /dev/null +++ b/nwn/nwnprc/trunk/scripts/prc_souleater.nss @@ -0,0 +1,47 @@ +//:://///////////////////////////////////////////// +//:: Name: prc_souleater +//:: FileName: prc_souleater.nss +//::////////////////////////////////////////////// +/* + Soul Eater class heartbeat script + Ensures onHit energy drain is applied to unarmed attacks +*/ +//::////////////////////////////////////////////// +//:: Created By: Jaysyn +//:: Created On: 2026-04-27 12:35:59 +//::////////////////////////////////////////////// + +#include "prc_inc_combat" +#include "prc_inc_onhit" + +void main() +{ + object oPC = OBJECT_SELF; + + // Only proceed if character is a Soul Eater + if(GetLevelByClass(CLASS_TYPE_SOUL_EATER, oPC) == 0) + return; + + // Only proceed if unarmed (no melee weapon equipped) + if(!GetIsUnarmed(oPC)) + return; + + // Get the unarmed weapon (creature weapon or gloves) + object oUnarmedWeapon = GetUnarmedWeapon(oPC); + + // Apply onHit property if weapon is valid + if(GetIsObjectValid(oUnarmedWeapon)) + { + // Remove any existing temporary properties first + RemoveSpecificProperty(oUnarmedWeapon, ITEM_PROPERTY_ONHITCASTSPELL, + IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", -1, DURATION_TYPE_TEMPORARY); + + // Set up the signature for event routing + SetLocalInt(oUnarmedWeapon, PRC_CUSTOM_ONHIT_SIGNATURE + "prc_uni_shift", TRUE); + + // Add temporary onHit property + IPSafeAddItemProperty(oUnarmedWeapon, + ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), + 9999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } +} \ No newline at end of file diff --git a/nwn/nwnprc/trunk/scripts/prc_switchesc.nss b/nwn/nwnprc/trunk/scripts/prc_switchesc.nss index f2478316..0ef8d7b0 100644 --- a/nwn/nwnprc/trunk/scripts/prc_switchesc.nss +++ b/nwn/nwnprc/trunk/scripts/prc_switchesc.nss @@ -480,8 +480,8 @@ void main() AddChoice("LA Buyoff.", 41); if(!GetPRCSwitch(PRC_DISABLE_ENCOUNTERS)) AddChoice("Visit an encounter area", 42); - if(DEBUG) //this doesn't work at all - //if(!GetPRCSwitch(PRC_APPEARNCE_CHANGE_DISABLE)) + //if(DEBUG) //this doesn't work at all + if(!GetPRCSwitch(PRC_APPEARNCE_CHANGE_DISABLE)) AddChoice("Change appearance.", 10); AddChoice("Miscellaneous options.", 11); if(DEBUG)//TO DO: add separate switch @@ -1195,7 +1195,64 @@ void main() MarkStageSetUp(nStage, oPC); } - else if (nStage == STAGE_WILDSHAPE_SLOTS) + else if(nStage == STAGE_APPEARANCE) + { + SetHeader("Select an appearance for your race:"); + + int nRace = GetRacialType(oPC); + int nGender = GetGender(oPC); + + // Debug: Check what race we're dealing with + string sRaceName = Get2DACache("racialtypes", "Label", nRace); + FloatingTextStringOnCreature("Race: " + sRaceName + " (" + IntToString(nRace) + ")", oPC, FALSE); + + if(nGender == GENDER_MALE) + { + int i; + for(i = 1; i <= 5; i++) + { + string sColumn = "Male" + IntToString(i); + string sAppearance = Get2DACache("racialappear", sColumn, nRace); + + // Debug: Show what we're getting + FloatingTextStringOnCreature("Column " + sColumn + ": '" + sAppearance + "'", oPC, FALSE); + + if(sAppearance != "" && sAppearance != "****") + { + int nAppearance = StringToInt(sAppearance); + string sName = Get2DACache("appearance", "LABEL", nAppearance); + if(sName == "") + sName = "Appearance " + sAppearance; + AddChoice(sName, nAppearance, oPC); + } + } + } + else // Female + { + int i; + for(i = 1; i <= 5; i++) + { + string sColumn = "Female" + IntToString(i); + string sAppearance = Get2DACache("racialappear", sColumn, nRace); + + // Debug: Show what we're getting + FloatingTextStringOnCreature("Column " + sColumn + ": '" + sAppearance + "'", oPC, FALSE); + + if(sAppearance != "" && sAppearance != "****") + { + int nAppearance = StringToInt(sAppearance); + string sName = Get2DACache("appearance", "LABEL", nAppearance); + if(sName == "") + sName = "Appearance " + sAppearance; + AddChoice(sName, nAppearance, oPC); + } + } + } + + AddChoice("Back", CHOICE_RETURN_TO_PREVIOUS); + MarkStageSetUp(nStage, oPC); + } + else if (nStage == STAGE_WILDSHAPE_SLOTS) { SetHeader("You attune yourself to nature, recallling animal forms."); AddChoice(GetStringByStrRef(8178), 401);//Brown Bear @@ -1740,7 +1797,9 @@ void main() else if(nChoice == 52) nStage = STAGE_WIELDING; else if(nChoice == 10) - nStage = STAGE_APPEARANCE; + { + nStage = STAGE_APPEARANCE; + } else if(nChoice == 11) nStage = STAGE_MISC_OPTIONS; else if(nChoice == 12) @@ -2586,7 +2645,22 @@ void main() MarkStageSetUp(nStage, oPC); } } - // Store the stage value. If it has been changed, this clears out the choices + else if(nStage == STAGE_APPEARANCE) + { + if(nChoice == CHOICE_RETURN_TO_PREVIOUS) + { + nStage = STAGE_ENTRY; + } + else + { + // Apply the selected appearance + SetCreatureAppearanceType(oPC, nChoice); + FloatingTextStringOnCreature("Appearance changed", oPC, FALSE); + nStage = STAGE_ENTRY; + } + MarkStageNotSetUp(nStage, oPC); + } + // Store the stage value. If it has been changed, this clears out the choices SetStage(nStage, oPC); } }