Ir al contenido

Foto

FAQ on custom classes, spells, and feats. (Repost)


  • Por favor identifícate para responder
67 respuestas en este tema

#26
kevL

kevL
  • Members
  • 4.052 mensajes

to go back to your first question,

i don't know much about this, but can't the Classes available on levelup be intercepted? (granted, this isn't possible for base classes since the character isn't created yet) If you can hook that, maybe a custom function can 'ghost' those PrCs that the function itself says are inappropiate

ie:
if (GetDeity() == "joe"
    || GetDeity() == "frank")
// show PrC
else
// ghost the PrC selection



( evading the H-word ..)



#27
Dann-J

Dann-J
  • Members
  • 3.161 mensajes

That may be what the VAR option in CLS_PRES_xxx was for, however it seems to be broken in NWN2. If it was as simple as setting a variable to 1 or 0 on the character then all would be tickety-boo.

 

[Edit: setting the VAR variable to 1 on the party member blocked the prestige class in NWN]

 

Changing the deity afterward is also problematic for me, since I'm creating an Arvoreen's Warder PrC. Kelemvor is easy, since clerics only have to be lawful, but Arvoreen clerics can only be LG, NG or LN. I was planning to set the PrC alignment restriction to non-evil and non-chaotic, but that would allow NN to slip through. Any NN cleric who tried to take Arvoreen's Warder would be forced to change to Arvoreen (somehow), but if they ever took another cleric level they'd be forced to change deity again. Unless I can also force NN alignments to change to NG or LN on taking the PrC.



#28
kevL

kevL
  • Members
  • 4.052 mensajes
I tried the VAR on the PC, according to both the nwn2.wiki and nwn.wiki

no go. Child of Night, using either 0 or 1 on a simple test_int_variable, and neither ghosted the selection. ( although setting CLASSOR = ShadowDancer did, while ofc PC was not SD )


Now here's an idea: create a set of Feats in Feats.2da.
Each Deity in your NwN2_deities.2da corresponds to a feat, which you then grant properly to PC on module load say, via a custom function.

then just use the FEAT parameter in the Cls_pres_* files
 
 
 
In fact if you do this, pls reserve lines (so I don't have to go swapping out my Feat.2da -- and a simple merge works)

Editado por kevL, 10 abril 2014 - 02:07 .


#29
Dann-J

Dann-J
  • Members
  • 3.161 mensajes

Yes, that's one way to do it. I was trying to avoid having to alter any other 2DAs or introduce any special scripting, but I may not be able to.

 

The PrC description says that you're supposed to be judged worthy by a cleric of Arvoreen before being able to take the class, so I could assign the special feat via a conversation with such a cleric, provided you meet all the criteria (being a halfling and worshipping Arvoreen would usually be sufficient). I could even tie in a special quest that needs to be done to prove your worthiness. You'd still have to satisfy all the other feat/skill/alignment prerequisites though.

 

Now I'm thinking of creating the Arvoreen's Keeper PrC as well, which is basically the same as a Warder only with divine spell progression. It'd use most of the same CLS_xxx 2DAs, with slightly different prerequisites (at least 1 level of divine spell casting).



#30
kevL

kevL
  • Members
  • 4.052 mensajes

sounds good, sounds doable :)

 

although i'd test that FEAT param first .. just to be sure



#31
Dann-J

Dann-J
  • Members
  • 3.161 mensajes

Once you've got a custom feat in, there shouldn't be a problem. The CLS_PRES_xxx.2DA is only looking for a line number in feats.2DA. That's pretty much how the Neverwinter Nine and Shadowthief PrCs originally worked in the OC.



#32
Dann-J

Dann-J
  • Members
  • 3.161 mensajes

I can see how Doomguide works now. There's a separate column in NWN2_Deities.2DA specifically for the class, with a 1 in only one row (Kelemvor). If you take a level of Doomguide, you have only one deity choice available. That means you don't actually have to worship Kelemvor to become a Doomguide (but you *will* after that, whether you like it or not). Somehow I doubt I can create a new class column in the deity 2DA and have the game engine recognise it - I suspect the dreaded H-word is involved.

 

I've now created NWN2 versions for four prestige classes just for halflings; Arvoreen's Keeper (divine spellcaster / trapper), Arvoreen's Warder (shortsword fighter / trapper), Halfling Whistler (bard spellcasting with druid/ranger feats), and Warsling Sniper (bullet-slinging dynamo).

 

The latter two are probably the most interesting. The Whistler is geared more towards avoiding combat via stealth, while buffing party members. The Sniper packs quite a punch at higher levels, especially if you give them a decent amount of strength. They have a tendency to eat through ammo quickly though. A Sniper with skiprocks is quite useful against mobs.



#33
Kanis-Greataxe

Kanis-Greataxe
  • Members
  • 158 mensajes

those all sound like interesting class.



#34
kamal_

kamal_
  • Members
  • 5.235 mensajes

I can see how Doomguide works now. There's a separate column in NWN2_Deities.2DA specifically for the class, with a 1 in only one row (Khelemvor). If you take a level of Doomguide, you have only one deity choice available. That means you don't actually have to worship Khelemvor to become a Doomguide (but you *will* after that, whether you like it or not). Somehow I doubt I can create a new class column in the deity 2DA and have the game engine recognise it - I suspect the dreaded H-word is involved.

If you intercept the level up, you can likely add columns to the 2da and get away with it. The default level up process is almost assuredly checking the deities 2da as part of choosing Doomguide. I don't know how to do it but intercepting the level up is something that can be done.



#35
Happycrow

Happycrow
  • Members
  • 612 mensajes

Is there any particular reason why one couldn't close Armor Skin or its equivalents, change the TLK references, and then re-use them for other packages, even if the data/scripting called on itself is hardcoded?  If so, makes the difference between an elegant feat addition and an inelegant script solution.  tnx in advance.



#36
Dann-J

Dann-J
  • Members
  • 3.161 mensajes

Is there any particular reason why one couldn't close Armor Skin or its equivalents, change the TLK references, and then re-use them for other packages, even if the data/scripting called on itself is hardcoded?  If so, makes the difference between an elegant feat addition and an inelegant script solution.  tnx in advance.

 

I'm guessing you'd have to modify the original feat entry, as the feat number is probably important to the hardcoding (since hardcoded feats often don't seem to have scripts attached to them).



#37
Happycrow

Happycrow
  • Members
  • 612 mensajes

Got it.  Thanks, DannJ.



#38
Laugh out loud

Laugh out loud
  • Members
  • 109 mensajes

If you are making a passive feat that adds bonuses to skills, how should the reflective spells.2da file be st up?  What fields should be populated and which ones should be blank?

Also other than the feats.2da, the spell.2da and the actual script, do you need to do anything else to any other game resources (load scripts, on resting scripts, etc.)?



#39
kevL

kevL
  • Members
  • 4.052 mensajes

If you are making a passive feat that adds bonuses to skills, how should the reflective spells.2da file be st up? What fields should be populated and which ones should be blank?
Also other than the feats.2da, the spell.2da and the actual script, do you need to do anything else to any other game resources (load scripts, on resting scripts, etc.)?


if it's a passive feat, I don't think you really need a corresponding spellscript or Spells.2da entry. Well it depends. If the skill-boost is permanent, fire off a script that has SetBaseSkillRank()

else if the skill-boost is module specific, use the module's OnEnter event to check for the feat and add a permanent supernatural effect or grant an item with an appropriate itemproperty


As to how this is actually done in the .2das, i suggest finding entries for feats and spellabilities that are similar to what you want.

nwn2wiki has a fair bit of good information on Spells.2da and Feat.2da ...


- more than this and the issue should be taken to another thread, cheers

#40
Laugh out loud

Laugh out loud
  • Members
  • 109 mensajes

I got my first two custom feats to work but now for the third custom feat I need a script that can detect if the PC has a shield in their left hand.  Does anyone know how to write such a script?  If you do should I put it in the module custom hearbeat or on-rest slots?



#41
4760

4760
  • Members
  • 1.203 mensajes

Well, you can always use a function like the one below (works for me):

int IsShieldEquipped(object oPC)
{
    object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
    if ((GetBaseItemType(oItem) == BASE_ITEM_LARGESHIELD) ||
	(GetBaseItemType(oItem) == BASE_ITEM_SMALLSHIELD) ||  (GetBaseItemType(oItem) == BASE_ITEM_TOWERSHIELD))
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

Call IsShiedEquipped after you have set the object oPC in your main script.

 

If you want to use it from a conversation, replace "int IsShieldEquipped" by "int StartingConditional" and name the script something like gc_is_shield_equipped.

 

 

Now, it all depends on when you want to check if the shield is equipped. If you want to know at all times, yes, use the heartbeat. If it's only when a new area is entered, on_enter will do, etc...


  • A Laugh out loud le gusta esto

#42
Laugh out loud

Laugh out loud
  • Members
  • 109 mensajes

I am trying to write a script associated with a feat that will add the characters intelligence modifier to their armor class while they also have a shield in their left hand.  Here is my script so far;

 

int IsShieldEquipped(object oPC)
{
    object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
    if ((GetBaseItemType(oItem) == BASE_ITEM_LARGESHIELD) ||
(GetBaseItemType(oItem) == BASE_ITEM_SMALLSHIELD) ||  (GetBaseItemType(oItem) == BASE_ITEM_TOWERSHIELD)&&(GetHasFeat(2858,oPC)))
{
int nAbility = GetAbilityModifier(ABILITY_INTELLIGENCE,oPC)
effect eFF = SupernaturalEffect(EffectACIncrease(nAbility, AC_DEFLECTION_BONUS, AC_VS_DAMAGE_TYPE_ALL,FALSE));
    ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEffect, oPC, HoursToSeconds(72));
    SendMessageToPC(GetFirstPC(), "Defend flank activated!");
}
 
else
{
return FALSE;
}
}
 
But I cannnot get it to compile.  I do admit that I have gotten a lot of help thus far but, I just need some help to get it to tie together.  (I added the SendMessageToPC script to better help me troubleshoot it.)


#43
4760

4760
  • Members
  • 1.203 mensajes

But I cannnot get it to compile.  I do admit that I have gotten a lot of help thus far but, I just need some help to get it to tie together.  (I added the SendMessageToPC script to better help me troubleshoot it.)

 

What error message do you get? Probably something about all paths not returning a value I guess.

If that's the problem you face, I suggest the code below:

int IsShieldEquipped(object oPC)
{
    object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
    // first change here: check if (holding a shield) and (having the right feat)
    if (((GetBaseItemType(oItem) == BASE_ITEM_LARGESHIELD) || (GetBaseItemType(oItem) == BASE_ITEM_SMALLSHIELD)
    ||  (GetBaseItemType(oItem) == BASE_ITEM_TOWERSHIELD)) && (GetHasFeat(2858,oPC)))
{
int nAbility = GetAbilityModifier(ABILITY_INTELLIGENCE,oPC)
effect eFF = SupernaturalEffect(EffectACIncrease(nAbility, AC_DEFLECTION_BONUS, AC_VS_DAMAGE_TYPE_ALL,FALSE));
    ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEffect, oPC, HoursToSeconds(72));
    SendMessageToPC(GetFirstPC(), "Defend flank activated!");
    // second change here: the function is supposed to return an integer
    return TRUE;
}

else
{
return FALSE;
}
}

And, to make sure there's no conflict with the OR (||) and AND (&&), I usually group the checks by nature. In this case, we want the function to return TRUE if the character is holding a shield and has the feat #2858. So, all the BASE_ITEM_*SHIELD are in the same set of parenthesis.


  • A Laugh out loud le gusta esto

#44
Laugh out loud

Laugh out loud
  • Members
  • 109 mensajes

What error message do you get? Probably something about all paths not returning a value I guess.

If that's the problem you face, I suggest the code below:

int IsShieldEquipped(object oPC)
{
    object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
    // first change here: check if (holding a shield) and (having the right feat)
    if (((GetBaseItemType(oItem) == BASE_ITEM_LARGESHIELD) || (GetBaseItemType(oItem) == BASE_ITEM_SMALLSHIELD)
    ||  (GetBaseItemType(oItem) == BASE_ITEM_TOWERSHIELD)) && (GetHasFeat(2858,oPC)))
{
int nAbility = GetAbilityModifier(ABILITY_INTELLIGENCE,oPC)
effect eFF = SupernaturalEffect(EffectACIncrease(nAbility, AC_DEFLECTION_BONUS, AC_VS_DAMAGE_TYPE_ALL,FALSE));
    ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEffect, oPC, HoursToSeconds(72));
    SendMessageToPC(GetFirstPC(), "Defend flank activated!");
    // second change here: the function is supposed to return an integer
    return TRUE;
}

else
{
return FALSE;
}
}

And, to make sure there's no conflict with the OR (||) and AND (&&), I usually group the checks by nature. In this case, we want the function to return TRUE if the character is holding a shield and has the feat #2858. So, all the BASE_ITEM_*SHIELD are in the same set of parenthesis.

Thanks for the help but I am still getting the same error on row 9 "Parsing varaiable list".  I do not know what this is trying to tell me when it is compiling.



#45
4760

4760
  • Members
  • 1.203 mensajes

I should have written the change in the toolset... Add a ; at the end of the line 

int nAbility = GetAbilityModifier(ABILITY_INTELLIGENCE,oPC)

  • A Laugh out loud le gusta esto

#46
Laugh out loud

Laugh out loud
  • Members
  • 109 mensajes

 

I should have written the change in the toolset... Add a ; at the end of the line 

int nAbility = GetAbilityModifier(ABILITY_INTELLIGENCE,oPC)

Thanks this managed to compile but it did not fire when I put in the OnHeartbeat slot of the module?  Also, how long should I have the temporary duration if I want the effect to turn off shortly after they unequip the shield and then reactivate if they re-equip the shield?  (Set the HoursToSeconds to 8 or less?)  I do want it to know and fire every time a shiled is equiped and every time it is unequiped.  I just made sure that the left hand is the only slot that allows you to equip a shield when I started the process.



#47
4760

4760
  • Members
  • 1.203 mensajes

it did not fire when I put in the OnHeartbeat slot of the module.

Yes, it's a function that returns either TRUE (if shield in left hand and feat available) or FALSE is at least one of the previous conditions is missing. So you'll have to add a line like 

if (IsShiedEquipped(GetFirstPC()) == TRUE)
{
// code or message to show when the conditions are met
}
else
{
// code or message to show when they're not
}

in the OnHeartBeat script of you module.

Or change the int IsShieldEquipped(object oPC) by

void main()
object oPC = GetFirstPC(FALSE);
// copy-paste all the rest of the script

and put the name of the script in the OnHeartbeat slot, but in that case, only the player controlled characters will be checked.

 

Hope that makes sense, I'm a bit in a hurry.


  • A Laugh out loud le gusta esto

#48
Laugh out loud

Laugh out loud
  • Members
  • 109 mensajes

OK I know that I attempted to go in my own direction but I tried your method before I did. For the OnHeartbeat script I put:

 

 

object oPC = GetFirstPC(FALSE);
int IsShieldEquipped(object oPC)
{
    object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
    // first change here: check if (holding a shield) and (having the right feat)
    if (((GetBaseItemType(oItem) == BASE_ITEM_LARGESHIELD) || (GetBaseItemType(oItem) ==(BASE_ITEM_SMALLSHIELD)
    ||  (GetBaseItemType(oItem) == BASE_ITEM_TOWERSHIELD)) && (GetHasFeat(2858,oPC)))
    ExecuteScript("feat_defend_flank", oPC);
}

 

 

And for the "feat_defend_flank" script it is instructed to fire on heartbeat I have:

 

 

const int FEAT_DEFEND_FLANK = 2858;
object oPC = GetFirstPC(FALSE);
int IsShieldEquipped(object oPC)
{
    int nAbility = GetAbilityModifier(ABILITY_INTELLIGENCE,oPC);
    effect eFF = SupernaturalEffect(EffectACIncrease(nAbility, AC_DEFLECTION_BONUS, AC_VS_DAMAGE_TYPE_ALL,FALSE));
    ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEffect, oPC, HoursToSeconds(12));
    SendMessageToPC(GetFirstPC(), "Defend flank activated!");
    // second change here: the function is supposed to return an integer
    return TRUE;
}

 

I can get both to compile without an error but I cannot get the script to fire on heartbeat even when the character has the shield in hand.



#49
rjshae

rjshae
  • Members
  • 4.477 mensajes

Personally I'd just call:

int nType = GetBaseItemType(oItem)

then use nType, rather than making repeated function calls.



#50
Laugh out loud

Laugh out loud
  • Members
  • 109 mensajes

Personally I'd just call:

int nType = GetBaseItemType(oItem)

then use nType, rather than making repeated function calls.

Whatever combination of scripts that will get the game to automatically detect whenever a character with the feat wields a shield and has the feat's bonus active while the character has the shield in their hand.  This combination has been harder than it sounds to get to work.