From 4cdfc06eb12c648f0d7b3a88ed15731c2c3d7c29 Mon Sep 17 00:00:00 2001 From: b5635 Date: Fri, 22 Sep 2023 01:35:36 -0700 Subject: [PATCH] moved party function into a shared library, implemented stealth, animal empathy, pickpocket, and dialogue skill xp closes #630 closes #239 --- src/nss/ai_onheartb.nss | 5 + src/nss/ai_onspawn.nss | 5 + src/nss/dlg_adventp1c.nss | 5 +- src/nss/dlg_avista_pers.nss | 2 + src/nss/dlg_bluf_chk.nss | 2 + src/nss/dlg_chk_bluf.nss | 3 + src/nss/dlg_chk_blufrob.nss | 4 +- src/nss/dlg_chk_inti.nss | 3 + src/nss/dlg_chk_inticrim.nss | 3 + src/nss/dlg_chk_pers.nss | 3 + src/nss/dlg_chk_perscrim.nss | 2 + src/nss/dlg_divine_pers.nss | 2 + src/nss/dlg_ferry_pers.nss | 2 + src/nss/dlg_q_chk_advb.nss | 2 + src/nss/dlg_q_chk_advb1.nss | 2 + src/nss/dlg_q_chk_advb2.nss | 2 + src/nss/dlg_q_chk_advb3.nss | 2 + src/nss/dlg_q_chk_advb4.nss | 2 + src/nss/dlg_q_chk_advb5.nss | 2 + src/nss/dlg_q_chk_advb6.nss | 2 + src/nss/dlg_q_chk_advb7.nss | 2 + src/nss/dlg_q_chk_advb8.nss | 2 + src/nss/dlg_q_chk_advb9.nss | 2 + src/nss/dlg_rob_intim.nss | 3 + src/nss/dlg_ship_pers1.nss | 3 +- src/nss/dlg_ship_pers2.nss | 2 + src/nss/fol_hirepe.nss | 3 +- src/nss/fol_hirepem.nss | 3 +- src/nss/hen_hirepe.nss | 3 +- src/nss/inc_party.nss | 159 ++++++++++++++++++++++++++++++ src/nss/inc_xp.nss | 37 ++++++- src/nss/on_mod_heartb.nss | 64 +++++++++++- src/nss/on_pc_skilla.nss | 76 +++++++++++++++ src/nss/on_trap_disarm.nss | 10 +- src/nss/party_credit.nss | 184 ++++------------------------------- 35 files changed, 430 insertions(+), 178 deletions(-) create mode 100644 src/nss/inc_party.nss diff --git a/src/nss/ai_onheartb.nss b/src/nss/ai_onheartb.nss index 9068bc67..5dc60b81 100644 --- a/src/nss/ai_onheartb.nss +++ b/src/nss/ai_onheartb.nss @@ -57,6 +57,11 @@ void main() int nCombat = GetIsInCombat(OBJECT_SELF); + if (!nCombat && !GetHasFeat(FEAT_KEEN_SENSE) && !GetHasFeat(FEAT_BLINDSIGHT_60_FEET)) + { + SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, TRUE); + } + object oPC = GetFirstPC(); int bEnemyPCSeen = FALSE; diff --git a/src/nss/ai_onspawn.nss b/src/nss/ai_onspawn.nss index fb39f5b9..351865eb 100644 --- a/src/nss/ai_onspawn.nss +++ b/src/nss/ai_onspawn.nss @@ -72,6 +72,11 @@ void main() //set action matrix gsAISetActionMatrix(gsAIGetDefaultActionMatrix()); + if (!GetHasFeat(FEAT_KEEN_SENSE) && !GetHasFeat(FEAT_BLINDSIGHT_60_FEET)) + { + SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, TRUE); + } + //set random facing //SetFacing(IntToFloat(Random(360))); diff --git a/src/nss/dlg_adventp1c.nss b/src/nss/dlg_adventp1c.nss index 0b5aefa9..a0212b3c 100644 --- a/src/nss/dlg_adventp1c.nss +++ b/src/nss/dlg_adventp1c.nss @@ -34,6 +34,7 @@ #include "inc_adv_assassin" #include "inc_ai_combat" #include "inc_ctoken" +#include "inc_xp" string GenerateInsult(object oPC) { @@ -168,8 +169,10 @@ int StartingConditional() if (GetScriptParam("pcintimidate") != "") { - if (GetIsSkillSuccessful(oPC, SKILL_INTIMIDATE, 18 + GetHitDice(OBJECT_SELF))) + int nDC = 18 + GetHitDice(OBJECT_SELF); + if (GetIsSkillSuccessful(oPC, SKILL_INTIMIDATE, nDC)) { + GiveDialogueSkillXP(oPC, nDC, SKILL_INTIMIDATE); sMessage = "You know, I think " + (nAdventurerPartySize > 1 ? "we" : "I") + " might have bitten off a bit more than " + (nAdventurerPartySize > 1 ? "we" : "I") + " can chew this time. Farewell... for now."; SetCustomToken(CTOKEN_ADVENTURER_DIALOGUE, sMessage); int i; diff --git a/src/nss/dlg_avista_pers.nss b/src/nss/dlg_avista_pers.nss index 198cd012..2eadd834 100644 --- a/src/nss/dlg_avista_pers.nss +++ b/src/nss/dlg_avista_pers.nss @@ -1,6 +1,7 @@ #include "inc_gold" #include "inc_persist" #include "inc_general" +#include "inc_xp" int StartingConditional() { @@ -20,6 +21,7 @@ int StartingConditional() } else { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "persuade_succeeded"); TakeGoldFromCreature(nCost, oPC, TRUE); return TRUE; diff --git a/src/nss/dlg_bluf_chk.nss b/src/nss/dlg_bluf_chk.nss index 1cae29d2..4abca6ab 100644 --- a/src/nss/dlg_bluf_chk.nss +++ b/src/nss/dlg_bluf_chk.nss @@ -1,5 +1,6 @@ #include "inc_persist" #include "inc_general" +#include "inc_xp" int StartingConditional() { @@ -15,6 +16,7 @@ int StartingConditional() } else { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); return TRUE; } diff --git a/src/nss/dlg_chk_bluf.nss b/src/nss/dlg_chk_bluf.nss index cdcbc8d7..94b9d000 100644 --- a/src/nss/dlg_chk_bluf.nss +++ b/src/nss/dlg_chk_bluf.nss @@ -1,5 +1,7 @@ #include "inc_persist" #include "inc_general" +#include "inc_xp" + // Bluff against bluff_dc local variable, offer 1 attempt per 15min // set script param "dc" to use that instead @@ -26,6 +28,7 @@ int StartingConditional() SetTemporaryInt(GetPCPublicCDKey(oPC, TRUE)+GetName(oPC)+"_"+GetTag(OBJECT_SELF)+"_bluf", 1, 900.0); return FALSE; } + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); return TRUE; } diff --git a/src/nss/dlg_chk_blufrob.nss b/src/nss/dlg_chk_blufrob.nss index c89b71f6..f8618b0d 100644 --- a/src/nss/dlg_chk_blufrob.nss +++ b/src/nss/dlg_chk_blufrob.nss @@ -1,4 +1,6 @@ #include "inc_persist" +#include "inc_xp" + // Bluff against bluff_dc local variable, offer 1 attempt per 15min // set script param "dc" to use that instead @@ -19,7 +21,7 @@ int StartingConditional() { return FALSE; } - + GiveDialogueSkillXP(oPC, nDC, nSkill); int nGold = StringToInt(GetScriptParam("gold")); GiveGoldToCreature(oPC, nGold); diff --git a/src/nss/dlg_chk_inti.nss b/src/nss/dlg_chk_inti.nss index d5890b80..d4091f2a 100644 --- a/src/nss/dlg_chk_inti.nss +++ b/src/nss/dlg_chk_inti.nss @@ -1,4 +1,6 @@ #include "inc_persist" +#include "inc_xp" + // Intimidate against intimidate_dc local variable, offer 1 attempt per 15min // set script param "dc" to use that instead @@ -21,5 +23,6 @@ int StartingConditional() return FALSE; } + GiveDialogueSkillXP(oPC, nDC, nSkill); return TRUE; } diff --git a/src/nss/dlg_chk_inticrim.nss b/src/nss/dlg_chk_inticrim.nss index d1d1994e..17ad6eb7 100644 --- a/src/nss/dlg_chk_inticrim.nss +++ b/src/nss/dlg_chk_inticrim.nss @@ -1,3 +1,5 @@ +#include "inc_xp" + //#include "inc_general" // DC is 11 + level // set script param "dc" to use that instead @@ -21,5 +23,6 @@ int StartingConditional() return FALSE; } //IncrementPlayerStatistic(oPC, "intimidate_succeeded"); + GiveDialogueSkillXP(oPC, nDC, nSkill); return TRUE; } diff --git a/src/nss/dlg_chk_pers.nss b/src/nss/dlg_chk_pers.nss index 2445c98b..657dc0b7 100644 --- a/src/nss/dlg_chk_pers.nss +++ b/src/nss/dlg_chk_pers.nss @@ -1,5 +1,7 @@ #include "inc_persist" #include "inc_general" +#include "inc_xp" + // Persuade against persuade_dc local variable, offer 1 attempt per 15min // set script param "dc" to use that instead @@ -22,6 +24,7 @@ int StartingConditional() SetTemporaryInt(GetPCPublicCDKey(oPC, TRUE)+GetName(oPC)+"_"+GetTag(OBJECT_SELF)+"_pers", 1, 900.0); return FALSE; } + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "persuade_succeeded"); return TRUE; } diff --git a/src/nss/dlg_chk_perscrim.nss b/src/nss/dlg_chk_perscrim.nss index 2495469c..1777718e 100644 --- a/src/nss/dlg_chk_perscrim.nss +++ b/src/nss/dlg_chk_perscrim.nss @@ -1,4 +1,5 @@ #include "inc_general" +#include "inc_xp" // DC is 13 + level // set script param "dc" to use that instead @@ -20,6 +21,7 @@ int StartingConditional() IncrementPlayerStatistic(oPC, "persuade_failed"); return FALSE; } + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "persuade_succeeded"); return TRUE; } diff --git a/src/nss/dlg_divine_pers.nss b/src/nss/dlg_divine_pers.nss index cbda1619..4d1cc2f0 100644 --- a/src/nss/dlg_divine_pers.nss +++ b/src/nss/dlg_divine_pers.nss @@ -1,6 +1,7 @@ #include "inc_gold" #include "inc_persist" #include "inc_general" +#include "inc_xp" int StartingConditional() { @@ -21,6 +22,7 @@ int StartingConditional() } else { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "persuade_succeeded"); TakeGoldFromCreature(nCost, oPC, TRUE); location lLocation = GetLocation(GetObjectByTag(GetLocalString(OBJECT_SELF, "warden_tele_wp"))); diff --git a/src/nss/dlg_ferry_pers.nss b/src/nss/dlg_ferry_pers.nss index 37dcc895..5ce1f271 100644 --- a/src/nss/dlg_ferry_pers.nss +++ b/src/nss/dlg_ferry_pers.nss @@ -1,6 +1,7 @@ #include "inc_gold" #include "inc_persist" #include "inc_general" +#include "inc_xp" int StartingConditional() { @@ -21,6 +22,7 @@ int StartingConditional() } else { + GiveDialogueSkillXP(oPC, nDC, nSkill); TakeGoldFromCreature(nCost, oPC, TRUE); IncrementPlayerStatistic(oPC, "persuade_succeeded"); IncrementPlayerStatistic(oPC, "gold_spent_on_ferries", nCost); diff --git a/src/nss/dlg_q_chk_advb.nss b/src/nss/dlg_q_chk_advb.nss index 169cdfd1..8d37cb2b 100644 --- a/src/nss/dlg_q_chk_advb.nss +++ b/src/nss/dlg_q_chk_advb.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -10,6 +11,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, nTarget, TRUE); return TRUE; diff --git a/src/nss/dlg_q_chk_advb1.nss b/src/nss/dlg_q_chk_advb1.nss index eeeb0879..8caa7ace 100644 --- a/src/nss/dlg_q_chk_advb1.nss +++ b/src/nss/dlg_q_chk_advb1.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -9,6 +10,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, 1, TRUE); return TRUE; diff --git a/src/nss/dlg_q_chk_advb2.nss b/src/nss/dlg_q_chk_advb2.nss index 1ba237f5..0ad0ac44 100644 --- a/src/nss/dlg_q_chk_advb2.nss +++ b/src/nss/dlg_q_chk_advb2.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -9,6 +10,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, 2, TRUE); return TRUE; diff --git a/src/nss/dlg_q_chk_advb3.nss b/src/nss/dlg_q_chk_advb3.nss index 2ba58572..69918b1e 100644 --- a/src/nss/dlg_q_chk_advb3.nss +++ b/src/nss/dlg_q_chk_advb3.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -9,6 +10,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, 3, TRUE); return TRUE; diff --git a/src/nss/dlg_q_chk_advb4.nss b/src/nss/dlg_q_chk_advb4.nss index 2cbd85dd..a357fd5e 100644 --- a/src/nss/dlg_q_chk_advb4.nss +++ b/src/nss/dlg_q_chk_advb4.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -9,6 +10,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, 4, TRUE); return TRUE; diff --git a/src/nss/dlg_q_chk_advb5.nss b/src/nss/dlg_q_chk_advb5.nss index 3c1e57c7..9d9be33a 100644 --- a/src/nss/dlg_q_chk_advb5.nss +++ b/src/nss/dlg_q_chk_advb5.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -9,6 +10,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, 5, TRUE); return TRUE; diff --git a/src/nss/dlg_q_chk_advb6.nss b/src/nss/dlg_q_chk_advb6.nss index 48e23914..c0045b06 100644 --- a/src/nss/dlg_q_chk_advb6.nss +++ b/src/nss/dlg_q_chk_advb6.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -9,6 +10,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, 6, TRUE); return TRUE; diff --git a/src/nss/dlg_q_chk_advb7.nss b/src/nss/dlg_q_chk_advb7.nss index 6f671ebd..ab21660f 100644 --- a/src/nss/dlg_q_chk_advb7.nss +++ b/src/nss/dlg_q_chk_advb7.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -9,6 +10,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, 7, TRUE); return TRUE; diff --git a/src/nss/dlg_q_chk_advb8.nss b/src/nss/dlg_q_chk_advb8.nss index 64338f0b..7dbfb355 100644 --- a/src/nss/dlg_q_chk_advb8.nss +++ b/src/nss/dlg_q_chk_advb8.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -9,6 +10,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, 8, TRUE); return TRUE; diff --git a/src/nss/dlg_q_chk_advb9.nss b/src/nss/dlg_q_chk_advb9.nss index 2ab86067..c91378e1 100644 --- a/src/nss/dlg_q_chk_advb9.nss +++ b/src/nss/dlg_q_chk_advb9.nss @@ -1,4 +1,5 @@ #include "inc_quest" +#include "inc_xp" int StartingConditional() { @@ -9,6 +10,7 @@ int StartingConditional() AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, BLUFF_CHAOS_SHIFT, FALSE); if(GetIsSkillSuccessful(oPC, nSkill, nDC)) { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "bluff_succeeded"); AdvanceQuest(OBJECT_SELF, oPC, 9, TRUE); return TRUE; diff --git a/src/nss/dlg_rob_intim.nss b/src/nss/dlg_rob_intim.nss index 5179e657..d362e5de 100644 --- a/src/nss/dlg_rob_intim.nss +++ b/src/nss/dlg_rob_intim.nss @@ -1,4 +1,5 @@ #include "inc_persist" +#include "inc_xp" int StartingConditional() { @@ -16,6 +17,8 @@ int StartingConditional() } else { + GiveDialogueSkillXP(oPC, nDC, nSkill); + int nGold = StringToInt(GetScriptParam("gold")); GiveGoldToCreature(oPC, nGold); diff --git a/src/nss/dlg_ship_pers1.nss b/src/nss/dlg_ship_pers1.nss index b45e7800..dd5bc5d2 100644 --- a/src/nss/dlg_ship_pers1.nss +++ b/src/nss/dlg_ship_pers1.nss @@ -1,6 +1,7 @@ #include "inc_persist" #include "inc_ship" #include "inc_general" +#include "inc_xp" int StartingConditional() { @@ -15,7 +16,7 @@ int StartingConditional() IncrementPlayerStatistic(oPC, "persuade_failed"); return FALSE; } - + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "persuade_succeeded"); PayShipAndTravel(OBJECT_SELF, GetPCSpeaker(), 1, TRUE); diff --git a/src/nss/dlg_ship_pers2.nss b/src/nss/dlg_ship_pers2.nss index 6612ab5b..6cfc193d 100644 --- a/src/nss/dlg_ship_pers2.nss +++ b/src/nss/dlg_ship_pers2.nss @@ -1,6 +1,7 @@ #include "inc_persist" #include "inc_ship" #include "inc_general" +#include "inc_xp" int StartingConditional() { @@ -15,6 +16,7 @@ int StartingConditional() IncrementPlayerStatistic(oPC, "persuade_failed"); return FALSE; } + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "persuade_succeeded"); PayShipAndTravel(OBJECT_SELF, GetPCSpeaker(), 2, TRUE); diff --git a/src/nss/fol_hirepe.nss b/src/nss/fol_hirepe.nss index 3c5270c1..7171489d 100644 --- a/src/nss/fol_hirepe.nss +++ b/src/nss/fol_hirepe.nss @@ -2,7 +2,7 @@ #include "inc_persist" #include "inc_general" #include "nw_i0_generic" - +#include "inc_xp" int StartingConditional() { @@ -18,6 +18,7 @@ int StartingConditional() } else { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "persuade_succeeded"); SetFollowerMaster(OBJECT_SELF, oPC); SetAssociateState(NW_ASC_USE_RANGED_WEAPON, FALSE); diff --git a/src/nss/fol_hirepem.nss b/src/nss/fol_hirepem.nss index 842bb3d6..d7139cfb 100644 --- a/src/nss/fol_hirepem.nss +++ b/src/nss/fol_hirepem.nss @@ -2,7 +2,7 @@ #include "inc_persist" #include "inc_general" #include "nw_i0_generic" - +#include "inc_xp" int StartingConditional() { @@ -18,6 +18,7 @@ int StartingConditional() } else { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "persuade_succeeded"); SetFollowerMaster(OBJECT_SELF, oPC); SetAssociateState(NW_ASC_USE_RANGED_WEAPON, FALSE); diff --git a/src/nss/hen_hirepe.nss b/src/nss/hen_hirepe.nss index abeca3b4..7cd8c379 100644 --- a/src/nss/hen_hirepe.nss +++ b/src/nss/hen_hirepe.nss @@ -1,7 +1,7 @@ #include "inc_henchman" #include "inc_persist" #include "inc_general" - +#include "inc_xp" int StartingConditional() { @@ -18,6 +18,7 @@ int StartingConditional() } else { + GiveDialogueSkillXP(oPC, nDC, nSkill); IncrementPlayerStatistic(oPC, "persuade_succeeded"); IncrementPlayerStatistic(oPC, "henchman_recruited"); SetMaster(OBJECT_SELF, oPC); diff --git a/src/nss/inc_party.nss b/src/nss/inc_party.nss new file mode 100644 index 00000000..65e60f79 --- /dev/null +++ b/src/nss/inc_party.nss @@ -0,0 +1,159 @@ +#include "inc_general" +#include "inc_xp" +#include "inc_loot" + +// SetScriptParam: "exclusivelooter" to ObjectToString(oPC) to make oPC get everything. + +// The max distance in meters a party member can be from +// a target killed by oKiller and still get xp. (no lower than 5!) +const float PARTY_DST_MAX = 40.0; + +struct Party +{ + int PlayerSize; + int HenchmanSize; + int TotalSize; + int LevelGap; + int HighestLevel; + float AverageLevel; +}; + +// * Simulates retrieving an object from an array on an object. +object GetLocalArrayObject(object oObject, string sArrayName, int nIndex) +{ + string sVarName = sArrayName + IntToString(nIndex); + return GetLocalObject(oObject, sVarName); +} + +// * Simulates storing a local object in an array on an object. +void SetLocalArrayObject(object oObject, string sArrayName, int nIndex, object oValue) +{ + SetLocalObject(oObject, sArrayName + IntToString(nIndex), oValue); +} + +int GetLocalArrayInt(object oObject, string sArrayName, int nIndex) +{ + string sVarName = sArrayName + IntToString(nIndex); + return GetLocalInt(oObject, sVarName); +} + +void SetLocalArrayInt(object oObject, string sArrayName, int nIndex, int nValue) +{ + SetLocalInt(oObject, sArrayName + IntToString(nIndex), nValue); +} + +// This global variable is used to store party data during the first loop +struct Party Party; + +void SetPartyData(object oTarget = OBJECT_SELF) +{ + int nLow = 80; + int nHigh, nLevel = 0; + int nPlayerSize = 0; + int nHenchmanSize = 0; + int nTotalSize = 0; + int nTotalLevels = 0; + int nHighestLevel = 0; + int nAssociateType; + float fAverageLevel = 0.0; + + float fDst = PARTY_DST_MAX; + location lLocation = GetLocation(oTarget); + + object oMbr = GetFirstObjectInShape(SHAPE_SPHERE, fDst, lLocation, FALSE, OBJECT_TYPE_CREATURE); + + object oExclusiveLooter = StringToObject(GetScriptParam("exclusivelooter")); + if (GetIsObjectValid(oExclusiveLooter)) + { + nPlayerSize = 1; + nTotalSize = 1; + nTotalLevels = GetLevelFromXP(GetXP(oExclusiveLooter)); + nHighestLevel = nTotalLevels; + nHigh = nTotalLevels; + nLow = nTotalLevels; + fAverageLevel = IntToFloat(nTotalLevels); + SetLocalArrayObject(oTarget, "Players", 1, oExclusiveLooter); + } + else + { + while(oMbr != OBJECT_INVALID) + { + nAssociateType = GetAssociateType(oMbr); + if(GetIsPC(oMbr)) + { + nPlayerSize++; + nTotalSize++; + nLevel = GetLevelFromXP(GetXP(oMbr)); + nTotalLevels = nTotalLevels + nLevel; + + if (nLevel > nHighestLevel) nHighestLevel = nLevel; + + + if(nLow > nLevel) nLow = nLevel; + if(nHigh < nLevel) nHigh = nLevel; + SetLocalArrayObject(oTarget, "Players", nPlayerSize, oMbr); + } + + // all associates except dominated and pets should count for xp purposes + else if (!GetIsDead(oMbr) && !GetIsPC(oMbr) && nAssociateType > 0 + && nAssociateType != ASSOCIATE_TYPE_DOMINATED + && nAssociateType != ASSOCIATE_TYPE_FAMILIAR + && nAssociateType != ASSOCIATE_TYPE_ANIMALCOMPANION + && GetLocalInt(oMbr, "no_xp_penalty") != 1) + { + nTotalSize++; + if (GetStringLeft(GetResRef(oMbr), 3) == "hen") // only named henchman count for loot distro + { + nHenchmanSize++; + SetLocalArrayObject(oTarget, "Henchmans", nHenchmanSize, oMbr); + } + nLevel = GetHitDice(oMbr); + if (nLevel > nHighestLevel) nHighestLevel = nLevel; + nTotalLevels = nTotalLevels + nLevel; + } + + oMbr = GetNextObjectInShape(SHAPE_SPHERE, fDst, lLocation, FALSE, OBJECT_TYPE_CREATURE); + } + // Used for loot debugging: if there were no "real" party members in range, add + // the module developer as a party member + // This forces the loot code to always be run + if (nTotalSize == 0 && ShouldDebugLoot()) + { + object oDev = GetLocalObject(GetModule(), "dev_lootvortex"); + nPlayerSize++; + nTotalSize++; + nLevel = GetLevelFromXP(GetXP(oMbr)); + nTotalLevels = nTotalLevels + nLevel; + nHighestLevel = nLevel; + nLow = nLevel; + nHigh = nLevel; + if (GetIsObjectValid(oDev) && GetIsDeveloper(oDev)) + { + SetLocalArrayObject(oTarget, "Players", nPlayerSize, oDev); + } + else + { + SetLocalArrayObject(oTarget, "Players", nPlayerSize, OBJECT_INVALID); + } + } + } + + + if (nPlayerSize > 0) fAverageLevel = IntToFloat(nTotalLevels) / IntToFloat(max(1, nTotalSize)); + + float fHighestLevel = IntToFloat(nHighestLevel); + if (fHighestLevel > fAverageLevel) fAverageLevel = fHighestLevel; + + SendDebugMessage("Player size: "+IntToString(nPlayerSize)); + SendDebugMessage("Henchman size: "+IntToString(nHenchmanSize)); + SendDebugMessage("Total size: "+IntToString(nTotalSize)); + SendDebugMessage("Party gap: "+IntToString(nHigh - nLow)); + SendDebugMessage("Party highest level: "+IntToString(nLevel)); + SendDebugMessage("Party average: "+FloatToString(fAverageLevel)); + + Party.PlayerSize = nPlayerSize; + Party.HenchmanSize = nHenchmanSize; + Party.TotalSize = nTotalSize; + Party.AverageLevel = fAverageLevel; + Party.LevelGap = nHigh - nLow; +} diff --git a/src/nss/inc_xp.nss b/src/nss/inc_xp.nss index 319f70f0..e700d744 100644 --- a/src/nss/inc_xp.nss +++ b/src/nss/inc_xp.nss @@ -64,6 +64,14 @@ const int PARTY_GAP_MAX = 4; // with a base mod of 5 and a party of two, the xp percentage is 83% const float PARTY_SIZE_BASE_MOD = 5.0; // increase to decrease XP penalty +// how much XP is awarded through stealth +const float STEALTH_XP_PERCENTAGE = 0.5; + +// how much XP is awarded through pickpocketing or animal empathy +const float SKILL_XP_PERCENTAGE = 0.25; + +// note: the sum of both of the numbers must NOT be above 1.0, or it may result in negative XP!! + // **** SYSTEM SETTINGS END - YOU SHOULD NOT MODIFY ANYTHING BELOW THIS! ******************************************* @@ -77,7 +85,7 @@ void GiveXP(object oKiller); // Gives nXpAmount to oPC, wisdom adjusted -void GiveXPToPC(object oPC, float fXpAmount, int bQuest = FALSE); +void GiveXPToPC(object oPC, float fXpAmount, int bQuest = FALSE, string sSource = ""); // Gives nXp amount to oPC, wisdom adjusted // with bonuses/penalties related to the target level @@ -133,7 +141,7 @@ float Truncate(float fFloat) return StringToFloat(FloatToString(fFloat, 3, 2)); } -void GiveXPToPC(object oPC, float fXpAmount, int bQuest = FALSE) +void GiveXPToPC(object oPC, float fXpAmount, int bQuest = FALSE, string sSource = "") { // Dead PCs do not get any XP, unless it came from a quest if (!bQuest && GetIsDead(oPC)) return; @@ -218,8 +226,13 @@ void GiveXPToPC(object oPC, float fXpAmount, int bQuest = FALSE) //fAdjustedXpAmount = IntToFloat(FloatToInt(fAdjustedXpAmount*10.0))/10.0; fAdjustedXpAmount = Truncate(fAdjustedXpAmount); + string sSourceFeedback = ""; + if (sSource != "") + { + sSourceFeedback = " from "+sSource; + } - SendMessageToPC(oPC, "Experience Points Gained: "+RemoveTrailingZeros(FloatToString(fAdjustedXpAmount, 3, 2)) + sRestedBonus); + SendMessageToPC(oPC, "Experience Points Gained"+sSourceFeedback+": "+RemoveTrailingZeros(FloatToString(fAdjustedXpAmount, 3, 2)) + sRestedBonus); int iStoredRemainderXP = SQLocalsPlayer_GetInt(oPC, "xp_remainder"); @@ -517,5 +530,23 @@ void UpdateXPBarUI(object oPC) } } +void GiveDialogueSkillXP(object oPC, int nDC, int nSkill) +{ + float fXP = 2.0 + IntToFloat((nDC - 10) / 2); + + if (fXP > 16.0) fXP = 16.0; + if (fXP < 3.0) fXP = 3.0; + + string sSkill; + switch (nSkill) + { + case SKILL_PERSUADE: sSkill = "Persuasion"; break; + case SKILL_BLUFF: sSkill = "Intimidation"; break; + case SKILL_INTIMIDATE: sSkill = "Bluffing"; break; + } + + GiveXPToPC(oPC, fXP, FALSE, sSkill); +} + //void main(){} diff --git a/src/nss/on_mod_heartb.nss b/src/nss/on_mod_heartb.nss index 2e4f9994..e4280f52 100644 --- a/src/nss/on_mod_heartb.nss +++ b/src/nss/on_mod_heartb.nss @@ -4,7 +4,7 @@ #include "inc_penalty" #include "inc_quest" #include "inc_sql" -#include "inc_general" +#include "inc_party" #include "inc_sqlite_time" #include "inc_weather" #include "nwnx_player" @@ -19,6 +19,67 @@ int GetIsDeadOrPetrified(object oCreature) return FALSE; } +void RewardStealthXP(object oPC) +{ + if (!GetStealthMode(oPC)) return; + + // if the player has any of these effects, don't reward stealth XP as it's a kinda cheaty + if (GetHasEffect(EFFECT_TYPE_INVISIBILITY, oPC)) return; + if (GetHasEffect(EFFECT_TYPE_SANCTUARY, oPC)) return; + if (GetHasEffect(EFFECT_TYPE_ETHEREAL, oPC)) return; + if (GetHasEffect(EFFECT_TYPE_DARKNESS, oPC)) return; + + location lLocation = GetLocation(oPC); + float fRadius = 10.0; + + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fRadius, lLocation, TRUE, OBJECT_TYPE_CREATURE); + + while(GetIsObjectValid(oTarget)) + { + string sIdentifier = "stealth_xp_"+GetName(oPC) + GetPCPublicCDKey(oPC); + if (!GetIsPC(oTarget) && + !GetIsDead(oTarget) && + GetIsEnemy(oPC, oTarget) && + !GetObjectSeen(oPC, oTarget) && + !GetObjectHeard(oPC, oTarget) && + GetLocalInt(oTarget, sIdentifier) != 1 && + !GetHasEffect(EFFECT_TYPE_BLINDNESS, oTarget) && //check these as well, kinda cheaty if the NPC has these effects + !GetHasEffect(EFFECT_TYPE_DARKNESS, oTarget)) + { + SetLocalInt(oTarget, sIdentifier, 1); + DelayCommand(1800.0, DeleteLocalInt(oTarget, sIdentifier)); // reset in 30 minutes + SetPartyData(oTarget); + + int bAmbush = FALSE; + if (GetLocalInt(oTarget, "ambush") == 1) + { + bAmbush = TRUE; + } + + int bBoss = GetLocalInt(oTarget, "boss"); + int bSemiBoss = GetLocalInt(oTarget, "semiboss"); + int bRare = GetLocalInt(oTarget, "rare"); + float fMultiplier = 1.0; + if (bBoss == 1) + { + fMultiplier = 3.0; + } + else if (bSemiBoss == 1 || bRare == 1) + { + fMultiplier = 2.0; + } + + + // stealth XP is worth half of a kill + float fXP = GetPartyXPValue(oTarget, bAmbush, Party.AverageLevel, Party.TotalSize, fMultiplier) * STEALTH_XP_PERCENTAGE; + + GiveXPToPC(oPC, fXP, FALSE, "Stealth"); + } + + oTarget = GetNextObjectInShape(SHAPE_SPHERE, fRadius, lLocation, TRUE, OBJECT_TYPE_CREATURE); + } +} + void ResetFactionsAttempt(object oPC) { // reset it if in combat @@ -281,6 +342,7 @@ void main() } else if (!GetIsDead(oPC)) { + RewardStealthXP(oPC); SQLocalsPlayer_DeleteInt(oPC, "DEAD"); if (GetCurrentHitPoints(oPC) >= GetMaxHitPoints(oPC)/2) diff --git a/src/nss/on_pc_skilla.nss b/src/nss/on_pc_skilla.nss index 21b24700..cf90bb85 100644 --- a/src/nss/on_pc_skilla.nss +++ b/src/nss/on_pc_skilla.nss @@ -2,6 +2,50 @@ #include "nwnx_object" #include "inc_horse" #include "inc_general" +#include "inc_party" + +void GiveSkillXP(object oTarget, object oPC, string sSkill) +{ + if (GetIsDead(oTarget)) return; + if (GetIsPC(oTarget)) return; + + string sIdentifier = "skill_xp_"+GetName(oPC) + GetPCPublicCDKey(oPC); + + // this cannot be awarded again until reset + if (GetLocalInt(oTarget, sIdentifier) == 1) return; + + + SetLocalInt(oTarget, sIdentifier, 1); + DelayCommand(1800.0, DeleteLocalInt(oTarget, sIdentifier)); // reset in 30 minutes + + // we use party data to award XP. That means to maximize XP value, you must be solo or far away enough from people. + // Does that seem odd? Yes. But this is the way to make sure that the correct XP is accounted for if they kill the creature with a party + // (players can get more potential XP if they use a skill in a party versus killing them without any successful skill uses) + // another way to think about it, pickpocketing NPCs or using animal empathy with a party is less risky because you have a party to back you up in case shit hits the fan and they aggro + + int bAmbush = FALSE; + if (GetLocalInt(oTarget, "ambush") == 1) + { + bAmbush = TRUE; + } + + int bBoss = GetLocalInt(oTarget, "boss"); + int bSemiBoss = GetLocalInt(oTarget, "semiboss"); + int bRare = GetLocalInt(oTarget, "rare"); + float fMultiplier = 1.0; + if (bBoss == 1) + { + fMultiplier = 3.0; + } + else if (bSemiBoss == 1 || bRare == 1) + { + fMultiplier = 2.0; + } + + float fXP = GetPartyXPValue(oTarget, bAmbush, Party.AverageLevel, Party.TotalSize, fMultiplier) * SKILL_XP_PERCENTAGE; + + GiveXPToPC(oPC, fXP, FALSE, sSkill); +} void main() { @@ -13,6 +57,9 @@ void main() if (StringToInt(NWNX_Events_GetEventData("ACTION_RESULT"))) { IncrementPlayerStatistic(OBJECT_SELF, "pickpockets_succeeded"); + + object oTarget = StringToObject(NWNX_Events_GetEventData("TARGET_OBJECT_ID")); + GiveSkillXP(oTarget, OBJECT_SELF, "Pick Pocketing"); } else { @@ -29,5 +76,34 @@ void main() object oMaster = GetLocalObject(oTarget, "master"); SetIsTemporaryEnemy(OBJECT_SELF, oMaster); + + if (GetIsPC(OBJECT_SELF) && StringToInt(NWNX_Events_GetEventData("ACTION_RESULT"))) + { + GiveSkillXP(oTarget, OBJECT_SELF, "Animal Empathy"); + } + } + /* this doesnt seem to work :( + else if (StringToInt(NWNX_Events_GetEventData("SKILL_ID")) == SKILL_OPEN_LOCK) + { + object oTarget = StringToObject(NWNX_Events_GetEventData("TARGET_OBJECT_ID")); + + if (GetIsPC(OBJECT_SELF) && StringToInt(NWNX_Events_GetEventData("ACTION_RESULT"))) //&& !GetLocked(oTarget)) + { + float fXP = IntToFloat(GetLockUnlockDC(oTarget)); + + if (fXP > 0.0) + { + fXP = fXP/4.0; + } + else + { + return; + } + + if (fXP > 12.0) fXP = 12.0; + + GiveXPToPC(OBJECT_SELF, fXP, FALSE, "Lockpicking"); + } } + */ } diff --git a/src/nss/on_trap_disarm.nss b/src/nss/on_trap_disarm.nss index ba6423b0..a7d0db64 100644 --- a/src/nss/on_trap_disarm.nss +++ b/src/nss/on_trap_disarm.nss @@ -9,12 +9,14 @@ void main() if (nXP == 0) nXP = GetLocalInt(OBJECT_SELF, "trap_dc"); + float fXP = IntToFloat(nXP); + // cap - if (nXP > 50) nXP = 50; + if (fXP > 50.0) fXP = 50.0; - if (nXP > 0) + if (fXP > 0.0) { - nXP = nXP/9; + fXP = fXP/9.0; } else { @@ -23,7 +25,7 @@ void main() if (GetIsPC(oPC)) { - GiveXPToPC(oPC, IntToFloat(nXP)); + GiveXPToPC(oPC, fXP, FALSE, "Trap Disarming"); IncrementPlayerStatistic(oPC, "traps_disarmed"); } } diff --git a/src/nss/party_credit.nss b/src/nss/party_credit.nss index 2e71ebc4..3dd90450 100644 --- a/src/nss/party_credit.nss +++ b/src/nss/party_credit.nss @@ -1,57 +1,14 @@ -#include "inc_xp" #include "inc_quest" -#include "inc_loot" #include "inc_treasure" #include "inc_henchman" #include "inc_nwnx" #include "inc_key" -#include "inc_general" - -// SetScriptParam: "exclusivelooter" to ObjectToString(oPC) to make oPC get everything. - -// The max distance in meters a party member can be from -// a target killed by oKiller and still get xp. (no lower than 5!) -const float PARTY_DST_MAX = 40.0; +#include "inc_party" // The max distance in meters a player can be from // a target killed by a trap, and still get xp. const float TRAP_DST_MAX = 100.0; - -struct Party -{ - int PlayerSize; - int HenchmanSize; - int TotalSize; - int LevelGap; - int HighestLevel; - float AverageLevel; -}; - -// * Simulates retrieving an object from an array on an object. -object GetLocalArrayObject(object oObject, string sArrayName, int nIndex) -{ - string sVarName = sArrayName + IntToString(nIndex); - return GetLocalObject(oObject, sVarName); -} - -// * Simulates storing a local object in an array on an object. -void SetLocalArrayObject(object oObject, string sArrayName, int nIndex, object oValue) -{ - SetLocalObject(oObject, sArrayName + IntToString(nIndex), oValue); -} - -int GetLocalArrayInt(object oObject, string sArrayName, int nIndex) -{ - string sVarName = sArrayName + IntToString(nIndex); - return GetLocalInt(oObject, sVarName); -} - -void SetLocalArrayInt(object oObject, string sArrayName, int nIndex, int nValue) -{ - SetLocalInt(oObject, sArrayName + IntToString(nIndex), nValue); -} - void SendLootMessage(object oHench, object oItem) { @@ -244,123 +201,6 @@ json _AddItemToPartyMemberAssignments(json jAssignments, object oItem, int nInde return jAssignments; } - -// This global variable is used to store party data during the first loop -struct Party Party; - -void SetPartyData() -{ - int nLow = 80; - int nHigh, nLevel = 0; - int nPlayerSize = 0; - int nHenchmanSize = 0; - int nTotalSize = 0; - int nTotalLevels = 0; - int nHighestLevel = 0; - int nAssociateType; - float fAverageLevel = 0.0; - - float fDst = PARTY_DST_MAX; - location lLocation = GetLocation(OBJECT_SELF); - - object oMbr = GetFirstObjectInShape(SHAPE_SPHERE, fDst, lLocation, FALSE, OBJECT_TYPE_CREATURE); - - object oExclusiveLooter = StringToObject(GetScriptParam("exclusivelooter")); - if (GetIsObjectValid(oExclusiveLooter)) - { - nPlayerSize = 1; - nTotalSize = 1; - nTotalLevels = GetLevelFromXP(GetXP(oExclusiveLooter)); - nHighestLevel = nTotalLevels; - nHigh = nTotalLevels; - nLow = nTotalLevels; - fAverageLevel = IntToFloat(nTotalLevels); - SetLocalArrayObject(OBJECT_SELF, "Players", 1, oExclusiveLooter); - } - else - { - while(oMbr != OBJECT_INVALID) - { - nAssociateType = GetAssociateType(oMbr); - if(GetIsPC(oMbr)) - { - nPlayerSize++; - nTotalSize++; - nLevel = GetLevelFromXP(GetXP(oMbr)); - nTotalLevels = nTotalLevels + nLevel; - - if (nLevel > nHighestLevel) nHighestLevel = nLevel; - - - if(nLow > nLevel) nLow = nLevel; - if(nHigh < nLevel) nHigh = nLevel; - SetLocalArrayObject(OBJECT_SELF, "Players", nPlayerSize, oMbr); - } - - // all associates except dominated and pets should count for xp purposes - else if (!GetIsDead(oMbr) && !GetIsPC(oMbr) && nAssociateType > 0 - && nAssociateType != ASSOCIATE_TYPE_DOMINATED - && nAssociateType != ASSOCIATE_TYPE_FAMILIAR - && nAssociateType != ASSOCIATE_TYPE_ANIMALCOMPANION - && GetLocalInt(oMbr, "no_xp_penalty") != 1) - { - nTotalSize++; - if (GetStringLeft(GetResRef(oMbr), 3) == "hen") // only named henchman count for loot distro - { - nHenchmanSize++; - SetLocalArrayObject(OBJECT_SELF, "Henchmans", nHenchmanSize, oMbr); - } - nLevel = GetHitDice(oMbr); - if (nLevel > nHighestLevel) nHighestLevel = nLevel; - nTotalLevels = nTotalLevels + nLevel; - } - - oMbr = GetNextObjectInShape(SHAPE_SPHERE, fDst, lLocation, FALSE, OBJECT_TYPE_CREATURE); - } - // Used for loot debugging: if there were no "real" party members in range, add - // the module developer as a party member - // This forces the loot code to always be run - if (nTotalSize == 0 && ShouldDebugLoot()) - { - object oDev = GetLocalObject(GetModule(), "dev_lootvortex"); - nPlayerSize++; - nTotalSize++; - nLevel = GetLevelFromXP(GetXP(oMbr)); - nTotalLevels = nTotalLevels + nLevel; - nHighestLevel = nLevel; - nLow = nLevel; - nHigh = nLevel; - if (GetIsObjectValid(oDev) && GetIsDeveloper(oDev)) - { - SetLocalArrayObject(OBJECT_SELF, "Players", nPlayerSize, oDev); - } - else - { - SetLocalArrayObject(OBJECT_SELF, "Players", nPlayerSize, OBJECT_INVALID); - } - } - } - - - if (nPlayerSize > 0) fAverageLevel = IntToFloat(nTotalLevels) / IntToFloat(max(1, nTotalSize)); - - float fHighestLevel = IntToFloat(nHighestLevel); - if (fHighestLevel > fAverageLevel) fAverageLevel = fHighestLevel; - - SendDebugMessage("Player size: "+IntToString(nPlayerSize)); - SendDebugMessage("Henchman size: "+IntToString(nHenchmanSize)); - SendDebugMessage("Total size: "+IntToString(nTotalSize)); - SendDebugMessage("Party gap: "+IntToString(nHigh - nLow)); - SendDebugMessage("Party highest level: "+IntToString(nLevel)); - SendDebugMessage("Party average: "+FloatToString(fAverageLevel)); - - Party.PlayerSize = nPlayerSize; - Party.HenchmanSize = nHenchmanSize; - Party.TotalSize = nTotalSize; - Party.AverageLevel = fAverageLevel; - Party.LevelGap = nHigh - nLow; -} - // Take the loot item (in the treasure area chest) and return the index of the party member that should recieve it. int DeterminePartyMemberThatGetsItem(object oItem, int nStartWeights=1000) { @@ -903,15 +743,31 @@ void main() nPCLevel = GetLevelFromXP(GetXP(oPC)); - GiveXPToPC(oPC, fXP); + float fTotalXPPercent = 1.0; + + string sStealthIdentifier = "stealth_xp_"+GetName(oPC) + GetPCPublicCDKey(oPC); + if (GetLocalInt(OBJECT_SELF, sStealthIdentifier) == 1) + { + fTotalXPPercent -= STEALTH_XP_PERCENTAGE; + } + + string sSkillIdentifier = "skill_xp_"+GetName(oPC) + GetPCPublicCDKey(oPC); + if (GetLocalInt(OBJECT_SELF, sSkillIdentifier) == 1) + { + fTotalXPPercent -= SKILL_XP_PERCENTAGE; + } + + float fXPAfterReductions = fXP * fTotalXPPercent; + + GiveXPToPC(oPC, fXPAfterReductions); AdvanceQuest(OBJECT_SELF, oPC, GetLocalInt(OBJECT_SELF, "quest_kill")); if (oKiller == oPC) { // Number of personal kills is in ai_ondeath already - IncrementPlayerStatistic(oPC, "kill_xp_value", FloatToInt(fXP*100.0)); + IncrementPlayerStatistic(oPC, "kill_xp_value", FloatToInt(fXPAfterReductions*100.0)); } - IncrementPlayerStatistic(oPC, "total_xp_from_partys_kills", FloatToInt(fXP*100.0)); + IncrementPlayerStatistic(oPC, "total_xp_from_partys_kills", FloatToInt(fXPAfterReductions*100.0)); } // only proceed with loot code if container exists