Aller au contenu

Photo

Undead player questions and scripting know how!


  • Veuillez vous connecter pour répondre
27 réponses à ce sujet

#1
Fester Pot

Fester Pot
  • Members
  • 1 393 messages
Hey, hey!

Got a sticky situation and need guidance toward adding this feature to Fate of the Auren - I've put it off too long - and with testing, I need to balance what one finds with this script added in.

So ... what's the best option in having the player cast Negative Energy spells on themselves to heal, while healing spells would harm?

nw_i0_spells - modify and check for Undead under the void spellsCure function?

OR ...

Modify all the cure spells individual to HARM rather than HEAL?
nw_s0_curminw
nw_s0_curlgtw
nw_s0_curmodw
nw_s0_curserw
nw_s0_curcrwn
nw_s0_heal
nw_s0_healcirc

That's the first question. I do like making a modification to one script, rather than multiple ones, so maybe I'm answering my own question, but would like some thoughts on doing so.

Second question!

How can I make the system think a player character - Human, Dwarf, Gnome, Elf, Half-Elf, Halfling - is Undead for said spells to do what I'd like?

The nw_i0_spells already does have a check set up for this (as do the spells it seems) -

if (GetRacialType(oTarget) != RACIAL_TYPE_UNDEAD)
    {
        //Figure out the amount of damage to heal
        nHeal = nDamage;
        //Set the heal effect
        eHeal = EffectHeal(nHeal);
        //Apply heal effect and VFX impact
        ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oTarget);
        ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis2, oTarget);
        //Fire cast spell at event for the specified target
        SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nSpellID, FALSE));


    }
    //Check that the target is undead
    else
    {
        int nTouch = TouchAttackMelee(oTarget);
        if (nTouch > 0)
        {
            if(!GetIsReactionTypeFriendly(oTarget))
            {
                //Fire cast spell at event for the specified target
                SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nSpellID));
                if (!MyPRCResistSpell(OBJECT_SELF, oTarget))
                {
                    eDam = EffectDamage(nDamage,DAMAGE_TYPE_NEGATIVE);
                    //Apply the VFX impact and effects
                    DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));
                    ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
                }
            }
        }
    }


Problem is that there is no SetRacialType, and being a single player game, players from Almraiven are either Human, Dwarf, Gnome, Half-Elf, Halfling or Elf.

FP!

#2
Shadooow

Shadooow
  • Members
  • 4 468 messages
Best way to do this is probably via core function hooking. Either way you have to recompile all scripts using GetRacialType(oTarget) == UNDEAD and either rewrite it to the MyGetRacialType(oTarget) == UNDEAD or with use of the core hooking spell can stay untouched (with the new functioon GetRacialType added into nw_i0_spells)

Depends probably on how much global this should be, whether you want to alter only these spells or you plan to add functionality of all undead-treated spells like control undead, undeath do dead etc.


Detailed info: Simple change in the spellsCure function inside nw_i0_spells will do nothing. To make it work you have to open all spells that uses this function and recompile them.,

EDIT: so it would looked like this

int MyGetRacialType(object oTarget)
{
if(GetLevelByclass(class_type_monk,oTarget) > 19 return RACIAL_TYPE_OUTSIDER;
if(GetLevelByclass(class_type_palemaster,oTarget)>9 return RACIAL_TYPE_UNDEAD;
if(GetItemPossessedBy("undead_token",oTarget) != OBJECT_INVALID) return RACIAL_TYPE_UNDEAD;
return GetRacialType(oTarget);
}

Modifié par ShaDoOoW, 19 décembre 2012 - 08:40 .


#3
henesua

henesua
  • Members
  • 3 863 messages
For determining whether the PC is undead:

Create a function that checks whether a creature is undead

int GetIsUndead(object oCreature)
{
    if(    GetSubRace(oCreature")=="undead"       ||  GetRacialType(oCreature)==RACIAL_TYPE_UNDEAD
      )
        return TRUE;
   else          return FALSE;
}

alternatively instead of checking for the undead subrace you could create a local int "UNDEAD" You will need to set one or the other on the PC depending on which method you prefer. 

In the Cure/Harm spell you will want to substitute that function in place of the GetRacialType function in the if statement

With regards to healing versus harming:
I think you need to change each of those spells unless you want to write one spell and then add that spell script to each of the spells in spells.2da.  That script will need to check which spell indeax was used to determine potency.

#4
Thayan

Thayan
  • Members
  • 244 messages
An easy way to address this is to use NWNX and a plugin like nwnx_funcs to set the PC's racial type to undead. This can be done with nwnx_funcs using the NWNXFuncs_SetRace(oPC, RACIAL_TYPE_UNDEAD) function. It's pretty simple to do and saves a boatload of work with modifying all the spells. We have just used that for some events we have running and so far it works really slick - Turn Undead working vs. PCs, Negative Energy healing, Curing spells harming, etc is all taken care of without any script modifications.

Just thought I'd throw out that option for you.

Modifié par Thayan, 19 décembre 2012 - 09:20 .


#5
HipMaestro

HipMaestro
  • Members
  • 1 515 messages
The heal skill will heal undead but I think the script is hard-coded. You guys would know better than I.

#6
Fester Pot

Fester Pot
  • Members
  • 1 393 messages
Thanks everyone!

I've made the changes required, but in testing, I've come across a snag.

In allowing the player to cast Negative Energy Ray upon themselves. Currently, you can't, but if it's to be used as a healing spell, where do I go about making the required modification so you can target yourself?

For reference, I've gone the route Henesua suggested, which is the easiest for me to understand and work with.

FP!

#7
MagicalMaster

MagicalMaster
  • Members
  • 2 000 messages
Spells.2da

Not sure if there's a way to do it that doesn't require a hak file change.

#8
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Changes to spell targeting require a hak file change, if you care about the mouse cursor and where/on what you can click to cast it. Worse, it has to be a clientside hak edit, not just a serverside one - otherwise complications arise, the details of which I've forgotten (though I recall a definite reason - it's why our ball lightning's targeting is different from our firebrand's).

If, however, you don't care about the cursor, you can alter spell behavior simply through scripting, given a few limitations. That is, you can make any given spell create a sphere on damage centered on the target or target's location, effecting only hostiles, or you can make it have no effect other than to heal the caster, given the conditions you have in mind. That is, even on a spell that can only target hostiles, you can still have it heal the caster instead, though they WILL need a hostile to target. With most damage spells, though, you can target the ground, (and with some, the caster) so that shouldn't be a major issue, if you pick another spell - a ray spell is not great in this regard. Maybe NEB instead?

Funky

#9
DM_Vecna

DM_Vecna
  • Members
  • 280 messages
I am very interested in getting Thayan's suggestion here working with NWNX_funcs but I am finding conflicting reports as to the possibility of success. Does anyone know if Thayan is still active or have contact information?

#10
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
I don't know for certain if Thayan's suggestion is possible - I seem to recall issues with setting racial type with nwnx, but that may have been limited to leto - I just don't remember. I do know, however, that acaos, the guy who pioneered most of the nwnx_funcs functions, found it much simpler to route all the pos/neg spells to a single script with a serverside 2da edit, and handle it in nwscript.  He edited the following 2da lines, excluding our custom spells:


*snip*
Sigh at bioboards. Paring down to get a clean post that'll fit.

Funky

Modifié par FunkySwerve, 06 août 2013 - 03:50 .


#11
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
31
Cure_Critical_Wounds


32
Cure_Light_Wounds


33
Cure_Minor_Wounds


34
Cure_Moderate_Wounds


35
Cure_Serious_Wounds


77
Harm


79
Heal


114
Mass_Heal


431
Inflict_Minor_Wounds


432
Inflict_Light_Wounds


433
Inflict_Moderate_Wounds


434
Inflict_Serious_Wounds


435
Inflict_Critical_Wounds


567
Cure_Critical_Wounds_Others


611
BGInflictSerious


612
BGInflictCritical


759
UndeadSelfHarm


2137
HGSPELL_MASS_CURE_CRITICAL_WOUNDS


2138
HGSPELL_MASS_CURE_LIGHT_WOUNDS


2139
HGSPELL_MASS_CURE_MODERATE_WOUNDS


2140
HGSPELL_MASS_CURE_SERIOUS_WOUNDS


2149
HGSPELL_MASS_HARM


2150
HGSPELL_MASS_INFLICT_CRITICAL_WOUNDS


2151
HGSPELL_MASS_INFLICT_LIGHT_WOUNDS


2152
HGSPELL_MASS_INFLICT_MODERATE_WOUNDS


2153
HGSPELL_MASS_INFLICT_SERIOUS_WOUNDS

#12
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
hgs_gen_posneg is the name of the script that fires for all those spells. Here's a sample line, in case you haven't done much 2da editing:

31    Cure_Critical_Wounds    781    is_curcrwnds    C    T    vs    0x3F    0x2B    hgs_gen_posneg    4    4    5    ****    ****    ****    5    1500    head    vco_mehanheal03    ****    ****    sco_mehanheal03    vs_chant_conj_hm    vs_chant_conj_hf    touch    1000    ****    ****    ****    ****    0    ****    ****    ****    ****    ****    ****    1    ****    ****    ****    ****    ****    5    ****    1    16883343    1    1    ****    0    ****    ****    ****    0   

All you have to do is sub out the bioware script in those lines with the name of the spell script you use in its place. Acaos did a lot of _gen_ (general) spells - 37 in total - when he worked on our spell scripts, as they make mass edits far easier, and save mod resources to boot. Here is a list of the names, in case you're curious - the hgs is short for higher ground spell, and is the prefix we use on our all custom spellscripts.




hgs_gen_abilbuff.nss


hgs_gen_beam.nss


hgs_gen_bolt.nss


hgs_gen_broken.nss


hgs_gen_buff.nss


hgs_gen_cone.nss


hgs_gen_damred.nss


hgs_gen_damshld.nss


hgs_gen_dispel.nss


hgs_gen_dot.nss


hgs_gen_dragbrth.nss


hgs_gen_elemres.nss


hgs_gen_font.nss


hgs_gen_monabil.nss


hgs_gen_monaura.nss


hgs_gen_monbolt.nss


hgs_gen_monbuff.nss


hgs_gen_moncone.nss


hgs_gen_mondrag.nss


hgs_gen_mongaze.nss


hgs_gen_monhowl.nss


hgs_gen_monpulse.nss


hgs_gen_monsumm.nss


hgs_gen_orb.nss


hgs_gen_poly.nss


hgs_gen_posneg.nss


hgs_gen_ray.nss


hgs_gen_restore.nss


hgs_gen_shifter.nss


hgs_gen_skilbuff.nss


hgs_gen_sphere.nss


hgs_gen_spmantle.nss


hgs_gen_summon.nss


hgs_gen_trap.nss


hgs_gen_tr_enter.nss


hgs_gen_wpndbuff.nss


hgs_gen_wpnebuff.nss

#13
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
And last, hgs_gen_posneg itself - sorry for all the split posts.

#include "hg_inc"
#include "ac_spell_inc"

const int ENERGY_RANK_HARM       = -6;
const int ENERGY_RANK_INF_CRIT   = -5;
const int ENERGY_RANK_INF_SER    = -4;
const int ENERGY_RANK_INF_MOD    = -3;
const int ENERGY_RANK_INF_LIGHT  = -2;
const int ENERGY_RANK_INF_MINOR  = -1;
const int ENERGY_RANK_INVALID    =  0;
const int ENERGY_RANK_CURE_MINOR =  1;
const int ENERGY_RANK_CURE_LIGHT =  2;
const int ENERGY_RANK_CURE_MOD   =  3;
const int ENERGY_RANK_CURE_SER   =  4;
const int ENERGY_RANK_CURE_CRIT  =  5;
const int ENERGY_RANK_HEAL       =  6;

void ApplyEnergyVisual (object oTarget, int nRank, int nClass, float fDelay) {
    int nVis = -1, nSnd = -1;

    if (GetRacialType(oTarget) == RACIAL_TYPE_UNDEAD) {
        switch (nRank) {
            case ENERGY_RANK_HARM:       nVis = CEPVFX_IMP_HARM_YELLOW;                      nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_CRIT:   nVis = VFX_IMP_SUNSTRIKE;                           nSnd = -1;                   break;
            case ENERGY_RANK_INF_SER:    nVis = VFX_IMP_SUNSTRIKE;                           nSnd = -1;                   break;
            case ENERGY_RANK_INF_MOD:    nVis = VFX_IMP_SUNSTRIKE;                           nSnd = -1;                   break;
            case ENERGY_RANK_INF_LIGHT:  nVis = VFX_IMP_SUNSTRIKE;                           nSnd = -1;                   break;
            case ENERGY_RANK_INF_MINOR:  nVis = VFX_IMP_SUNSTRIKE;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_MINOR: nVis = CEPVFX_IMP_HEALING_S_ORANGE;                 nSnd = CEPSFX_IMP_HEALING_S; break;
            case ENERGY_RANK_CURE_LIGHT: nVis = CEPVFX_IMP_HEALING_S_ORANGE;                 nSnd = CEPSFX_IMP_HEALING_S; break;
            case ENERGY_RANK_CURE_MOD:   nVis = CEPVFX_IMP_HEALING_M_ORANGE;                 nSnd = CEPSFX_IMP_HEALING_M; break;
            case ENERGY_RANK_CURE_SER:   nVis = CEPVFX_IMP_HEALING_L_ORANGE;                 nSnd = CEPSFX_IMP_HEALING_L; break;
            case ENERGY_RANK_CURE_CRIT:  nVis = CEPVFX_IMP_HEALING_G_ORANGE;                 nSnd = CEPSFX_IMP_HEALING_G; break;
            case ENERGY_RANK_HEAL:       nVis = PRCVFX_IMP_HEALING_X_UNDEAD; nSnd = -1;                   break;
            default:                     return;
        }
    } else if (GetRacialType(oTarget) == RACIAL_TYPE_CONSTRUCT) {
        switch (nRank) {
            case ENERGY_RANK_HARM:       nVis = VFX_IMP_HARM;                                nSnd = -1;                   break;
            case ENERGY_RANK_INF_CRIT:   nVis = PRCVFX_IMP_INFLICT_G;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_SER:    nVis = PRCVFX_IMP_INFLICT_L;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MOD:    nVis = PRCVFX_IMP_INFLICT_M;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_LIGHT:  nVis = PRCVFX_IMP_INFLICT_S;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MINOR:  nVis = VFX_IMP_HEAD_EVIL;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_MINOR: nVis = CEPVFX_IMP_HEALING_S_PURPLE;                 nSnd = CEPSFX_IMP_HEALING_S; break;
            case ENERGY_RANK_CURE_LIGHT: nVis = CEPVFX_IMP_HEALING_S_PURPLE;                 nSnd = CEPSFX_IMP_HEALING_S; break;
            case ENERGY_RANK_CURE_MOD:   nVis = CEPVFX_IMP_HEALING_M_PURPLE;                 nSnd = CEPSFX_IMP_HEALING_M; break;
            case ENERGY_RANK_CURE_SER:   nVis = CEPVFX_IMP_HEALING_L_PURPLE;                 nSnd = CEPSFX_IMP_HEALING_L; break;
            case ENERGY_RANK_CURE_CRIT:  nVis = CEPVFX_IMP_HEALING_G_PURPLE;                 nSnd = CEPSFX_IMP_HEALING_G; break;
            case ENERGY_RANK_HEAL:       nVis = CEPVFX_IMP_HEALING_X_PURPLE;                 nSnd = CEPSFX_IMP_HEALING_X; break;
            default:                     return;
        }
    } else if (GetRacialType(oTarget) == RACIAL_TYPE_PLANT || nClass == CLASS_TYPE_DRUID) {
        switch (nRank) {
            case ENERGY_RANK_HARM:       nVis = CEPVFX_IMP_HARM_ORANGE;                      nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_CRIT:   nVis = PRCVFX_IMP_INFLICT_G;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_SER:    nVis = PRCVFX_IMP_INFLICT_L;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MOD:    nVis = PRCVFX_IMP_INFLICT_M;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_LIGHT:  nVis = PRCVFX_IMP_INFLICT_S;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MINOR:  nVis = VFX_IMP_HEAD_EVIL;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_MINOR: nVis = VFX_IMP_HEAD_HEAL;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_LIGHT: nVis = CEPVFX_IMP_HEALING_S_GREEN;                  nSnd = CEPSFX_IMP_HEALING_S; break;
            case ENERGY_RANK_CURE_MOD:   nVis = CEPVFX_IMP_HEALING_M_GREEN;                  nSnd = CEPSFX_IMP_HEALING_M; break;
            case ENERGY_RANK_CURE_SER:   nVis = CEPVFX_IMP_HEALING_L_GREEN;                  nSnd = CEPSFX_IMP_HEALING_L; break;
            case ENERGY_RANK_CURE_CRIT:  nVis = CEPVFX_IMP_HEALING_G_GREEN;                  nSnd = CEPSFX_IMP_HEALING_G; break;
            case ENERGY_RANK_HEAL:       nVis = CEPVFX_IMP_HEALING_X_GREEN;                  nSnd = CEPSFX_IMP_HEALING_X; break;
            default:                     return;
        }
    } else if (nClass == CLASS_TYPE_CLERIC) {
        switch (nRank) {
            case ENERGY_RANK_HARM:       nVis = VFX_IMP_HARM;                                nSnd = -1;                   break;
            case ENERGY_RANK_INF_CRIT:   nVis = PRCVFX_IMP_INFLICT_G;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_SER:    nVis = PRCVFX_IMP_INFLICT_L;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MOD:    nVis = PRCVFX_IMP_INFLICT_M;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_LIGHT:  nVis = PRCVFX_IMP_INFLICT_S;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MINOR:  nVis = VFX_IMP_HEAD_EVIL;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_MINOR: nVis = VFX_IMP_HEAD_HEAL;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_LIGHT: nVis = CEPVFX_IMP_HEALING_S_BLUE;                   nSnd = CEPSFX_IMP_HEALING_S; break;
            case ENERGY_RANK_CURE_MOD:   nVis = CEPVFX_IMP_HEALING_M_BLUE;                   nSnd = CEPSFX_IMP_HEALING_M; break;
            case ENERGY_RANK_CURE_SER:   nVis = CEPVFX_IMP_HEALING_L_BLUE;                   nSnd = CEPSFX_IMP_HEALING_L; break;
            case ENERGY_RANK_CURE_CRIT:  nVis = CEPVFX_IMP_HEALING_G_BLUE;                   nSnd = CEPSFX_IMP_HEALING_G; break;
            case ENERGY_RANK_HEAL:       nVis = CEPVFX_IMP_HEALING_X_BLUE;                   nSnd = CEPSFX_IMP_HEALING_X; break;
            default:                     return;
        }
    } else if (nClass == CLASS_TYPE_BARD) {
        switch (nRank) {
            case ENERGY_RANK_HARM:       nVis = VFX_IMP_HARM;                                nSnd = -1;                   break;
            case ENERGY_RANK_INF_CRIT:   nVis = PRCVFX_IMP_INFLICT_G;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_SER:    nVis = PRCVFX_IMP_INFLICT_L;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MOD:    nVis = PRCVFX_IMP_INFLICT_M;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_LIGHT:  nVis = PRCVFX_IMP_INFLICT_S;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MINOR:  nVis = VFX_IMP_HEAD_EVIL;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_MINOR: nVis = VFX_IMP_HEAD_HEAL;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_LIGHT: nVis = CEPVFX_IMP_HEALING_S_CYAN;                   nSnd = CEPSFX_IMP_HEALING_S; break;
            case ENERGY_RANK_CURE_MOD:   nVis = CEPVFX_IMP_HEALING_M_CYAN;                   nSnd = CEPSFX_IMP_HEALING_M; break;
            case ENERGY_RANK_CURE_SER:   nVis = CEPVFX_IMP_HEALING_L_CYAN;                   nSnd = CEPSFX_IMP_HEALING_L; break;
            case ENERGY_RANK_CURE_CRIT:  nVis = CEPVFX_IMP_HEALING_G_CYAN;                   nSnd = CEPSFX_IMP_HEALING_G; break;
            case ENERGY_RANK_HEAL:       nVis = CEPVFX_IMP_HEALING_X_CYAN;                   nSnd = CEPSFX_IMP_HEALING_X; break;
            default:                     return;
        }
    } else {
        switch (nRank) {
            case ENERGY_RANK_HARM:       nVis = VFX_IMP_HARM;                                nSnd = -1;                   break;
            case ENERGY_RANK_INF_CRIT:   nVis = PRCVFX_IMP_INFLICT_G;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_SER:    nVis = PRCVFX_IMP_INFLICT_L;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MOD:    nVis = PRCVFX_IMP_INFLICT_M;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_LIGHT:  nVis = PRCVFX_IMP_INFLICT_S;                        nSnd = CEPSFX_IMP_HARM;      break;
            case ENERGY_RANK_INF_MINOR:  nVis = VFX_IMP_HEAD_EVIL;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_MINOR: nVis = VFX_IMP_HEAD_HEAL;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_LIGHT: nVis = VFX_IMP_HEALING_S;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_MOD:   nVis = VFX_IMP_HEALING_M;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_SER:   nVis = VFX_IMP_HEALING_L;                           nSnd = -1;                   break;
            case ENERGY_RANK_CURE_CRIT:  nVis = VFX_IMP_HEALING_G;                           nSnd = -1;                   break;
            case ENERGY_RANK_HEAL:       nVis = VFX_IMP_HEALING_X;                           nSnd = -1;                   break;
            default:                     return;
        }
    }

    if (nSnd >= 0)
        DelayCommand(fDelay, ApplyVisualToObject(nSnd, oTarget));
    DelayCommand(fDelay, ApplyVisualToObject(nVis, oTarget));
}


void main() {
    struct SpellInfo si = GetSpellInfo();
    if (si.id < 0)
        return;

    if (GetLocalInt(si.item, "Unlimited") > 0 &&
        GetLocalInt(si.item, "Unlimited") == GetBaseItemType(si.item)) {

        string sSourceTag = GetLocalString(si.item, "UnlimitedSourceTag");
        if (sSourceTag != "" && !GetIsObjectValid(GetItemPossessedBy(si.caster, sSourceTag))) {
            SetPlotFlag(si.item, FALSE);
            DestroyObject(si.item);
        } else
            SetItemStackSize(si.item, 99);

        if (GetLocalInt(si.item, "UnlimitedSelfOnly") && si.target != si.caster) {
            FloatingTextStringOnCreature("You can only use this item on yourself!", si.caster, FALSE);
            return;
        }

        SetItemCursedFlag(si.item, TRUE);
    }

    int nHeal = GetSkillRank(SKILL_HEAL, OBJECT_SELF);
    if (nHeal < 4)
        nHeal = 4;
    else if (GetIsObjectValid(si.item) && nHeal > 20 && (nHeal > si.clevel * 2))
        nHeal = si.clevel * 2;

    int nDice = 0, nBonus = 0, nSides = 8, nRank = 0, nFocus = GetCasterSpellFocus(si.school);
    int nAmount, nRace, nHealRace = -1, nHarmRace = -1, nSaveType = -1, nDamType = DAMAGE_TYPE_POSITIVE;
    float fDelay, fRadius = 0.0;

    switch (si.id) {
        case SPELLABILITY_BG_INFLICT_SERIOUS_WOUNDS:
            FloatingTextStringOnCreature("Please use the Flame Weapon spell from your Blackguard spellbook.", si.caster, FALSE);
            return;

        case SPELLABILITY_BG_INFLICT_CRITICAL_WOUNDS:
            FloatingTextStringOnCreature("Please use the Bestow Curse spell from your Blackguard spellbook.", si.caster, FALSE);
            return;

        case SPELL_CURE_MINOR_WOUNDS:
            nRank   = ENERGY_RANK_CURE_MINOR;
            nDice   = 1;
            nSides  = 1;
            nBonus  = nHeal - 1;
            si.id   = SPELL_CURE_MINOR_WOUNDS;
            break;

        case HGSPELL_MASS_CURE_LIGHT_WOUNDS:
            fRadius = RADIUS_SIZE_MEDIUM;
        case SPELL_CURE_LIGHT_WOUNDS:
            nRank   = ENERGY_RANK_CURE_LIGHT;
            nDice   = nHeal / 4;
            nBonus  = (si.clevel > 5 ? 5 : si.clevel);
            si.id   = SPELL_CURE_LIGHT_WOUNDS;
            break;

        case SPELL_HEALING_CIRCLE:
        case HGSPELL_MASS_CURE_MODERATE_WOUNDS:
            fRadius = (si.id == SPELL_HEALING_CIRCLE ? RADIUS_SIZE_LARGE : RADIUS_SIZE_MEDIUM);
        case SPELL_CURE_MODERATE_WOUNDS:
            nRank   = ENERGY_RANK_CURE_MOD;
            nDice   = nHeal / 2;
            nBonus  = (si.clevel > 10 ? 10 : si.clevel);
            si.id   = SPELL_CURE_MODERATE_WOUNDS;
            break;

        case HGSPELL_MASS_CURE_SERIOUS_WOUNDS:
            fRadius = RADIUS_SIZE_MEDIUM;
        case SPELL_CURE_SERIOUS_WOUNDS:
            nRank   = ENERGY_RANK_CURE_SER;
            nDice   = (nHeal * 3) / 4;
            nBonus  = (si.clevel > 15 ? 15 : si.clevel);
            si.id   = SPELL_CURE_SERIOUS_WOUNDS;
            if (GetLocalInt(si.caster, "HER_PICKUP")) {
                if (!GetIsDead(si.target)) {
                    effect eEff;
                    int nRemoved = 0;
                    for (eEff = GetFirstEffect(si.target); GetIsEffectValid(eEff); eEff = GetNextEffect(si.target)) {
                        if (GetEffectTrueType(eEff) == EFFECT_TRUETYPE_KNOCKDOWN) {
                            nRemoved++;
                            RemoveEffect(si.target, eEff);
                        }
                    }
                    if (nRemoved > 0) {
                        effect eImmob = ExtraordinaryEffect(EffectCutsceneImmobilize());
                        eImmob = EffectLinkEffects(eImmob, EffectVisualEffect(PRCVFX_DUR_AIR1));
                        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eImmob, si.target, 2.0);
                    }
                }
                DeleteLocalInt(si.caster, "HER_PICKUP");
                return;
            }
            break;

        case HGSPELL_MASS_CURE_CRITICAL_WOUNDS:
            fRadius = RADIUS_SIZE_MEDIUM;
        case SPELL_CURE_CRITICAL_WOUNDS:
            nRank   = ENERGY_RANK_CURE_CRIT;
            nDice   = nHeal;
            nBonus  = (si.clevel > 20 ? 20 : si.clevel);
            si.id   = SPELL_CURE_CRITICAL_WOUNDS;
            break;

        case SPELL_MASS_HEAL:
            fRadius = RADIUS_SIZE_MEDIUM;
        case SPELL_HEAL:
            nRank   = ENERGY_RANK_HEAL;
            si.id   = SPELL_HEAL;

            if (GetLocalInt(GetArea(si.caster), "SkillBasedHeal")) {
                nDice  = nHeal * 2;
                nBonus = 200;
            } else
                nDice = -1;
            break;


        case SPELL_INFLICT_MINOR_WOUNDS:
            nRank     = ENERGY_RANK_CURE_MINOR;
            nDice     = 1;
            nSides    = 1;
            nBonus    = nHeal - 1;
            nDamType  = DAMAGE_TYPE_NEGATIVE;
            si.id     = SPELL_INFLICT_MINOR_WOUNDS;
            break;

        case HGSPELL_MASS_INFLICT_LIGHT_WOUNDS:
            fRadius = RADIUS_SIZE_MEDIUM;
        case SPELL_INFLICT_LIGHT_WOUNDS:
            nRank    = ENERGY_RANK_CURE_LIGHT;
            nDice    = nHeal / 4;
            nBonus   = (si.clevel > 5 ? 5 : si.clevel);
            nDamType = DAMAGE_TYPE_NEGATIVE;
            si.id    = SPELL_INFLICT_LIGHT_WOUNDS;
            break;

        case HGSPELL_MASS_INFLICT_MODERATE_WOUNDS:
            fRadius = RADIUS_SIZE_MEDIUM;
        case SPELL_INFLICT_MODERATE_WOUNDS:
            nRank    = ENERGY_RANK_CURE_MOD;
            nDice    = nHeal / 2;
            nBonus   = (si.clevel > 10 ? 10 : si.clevel);
            nDamType = DAMAGE_TYPE_NEGATIVE;
            si.id    = SPELL_INFLICT_MODERATE_WOUNDS;
            break;

        case HGSPELL_MASS_INFLICT_SERIOUS_WOUNDS:
            fRadius = RADIUS_SIZE_MEDIUM;
        case SPELL_INFLICT_SERIOUS_WOUNDS:
            nRank    = ENERGY_RANK_CURE_SER;
            nDice    = (nHeal * 3) / 4;
            nBonus   = (si.clevel > 15 ? 15 : si.clevel);
            nDamType = DAMAGE_TYPE_NEGATIVE;
            si.id    = SPELL_INFLICT_SERIOUS_WOUNDS;
            break;

        case HGSPELL_MASS_INFLICT_CRITICAL_WOUNDS:
            fRadius = RADIUS_SIZE_MEDIUM;
        case SPELL_INFLICT_CRITICAL_WOUNDS:
            nRank    = ENERGY_RANK_CURE_CRIT;
            nDice    = nHeal;
            nBonus   = (si.clevel > 20 ? 20 : si.clevel);
            nDamType = DAMAGE_TYPE_NEGATIVE;
            si.id    = SPELL_INFLICT_CRITICAL_WOUNDS;
            break;

        case HGSPELL_MASS_HARM:
            fRadius = RADIUS_SIZE_MEDIUM;
        case SPELL_HARM:
        case 759:               /* Undead Self Harm */
            nRank    = ENERGY_RANK_HEAL;
            nDamType = DAMAGE_TYPE_NEGATIVE;
            si.id    = SPELL_HARM;

            if (GetLocalInt(GetArea(si.caster), "SkillBasedHeal")) {
                nDice  = nHeal * 2;
                nBonus = 200;
            } else
                nDice = -1;
            break;

        default:
            FloatingTextStringOnCreature("An invalid pos/neg spell #" + IntToString(si.id) + " was cast. Please inform a DM.", si.caster);
            return;
    }


    if (nHealRace < 0 && nHarmRace < 0 && nSaveType < 0) {
        if (nDamType == DAMAGE_TYPE_POSITIVE) {
            nHealRace = RACIAL_TYPE_ALL;
            nHarmRace = RACIAL_TYPE_UNDEAD;
            nSaveType = SAVING_THROW_TYPE_POSITIVE;
        } else {
            nHealRace = RACIAL_TYPE_UNDEAD;
            nHarmRace = RACIAL_TYPE_ALL;
            nSaveType = SAVING_THROW_TYPE_NEGATIVE;
        }
    }


    if (si.class == CLASS_TYPE_CLERIC && nHealRace == RACIAL_TYPE_ALL && GetHasFeat(FEAT_HEALING_DOMAIN_POWER, si.caster))
        nDice = (nDice * 3) / 2;

    if (!GetIsObjectValid(si.item) && si.class == CLASS_TYPE_CLERIC && nFocus > 1)
        nFocus = (nFocus - 1) * 2;
    else
        nFocus = 0;

    if (fRadius > 0.0) {
        if (si.id == SPELL_HEALING_CIRCLE)
            ApplyVisualAtLocation(VFX_FNF_LOS_HOLY_20, si.loc);
        else if (nDamType == DAMAGE_TYPE_NEGATIVE)
            ApplyVisualAtLocation(VFX_FNF_LOS_EVIL_10, si.loc);
        else if (si.class == CLASS_TYPE_DRUID)
            ApplyVisualAtLocation(HGVFX_FNF_LOS_GREEN_10, si.loc);
        else
            ApplyVisualAtLocation(VFX_FNF_LOS_HOLY_10, si.loc);

        si.target = GetFirstObjectInShape(SHAPE_SPHERE, fRadius, si.loc, TRUE);
    }

    while (GetIsObjectValid(si.target)) {
        nRace  = GetRacialType(si.target);
        fDelay = GetSpellEffectDelay(si.loc, si.target);

        if (nRace == RACIAL_TYPE_UNDEAD && GetLocalInt(si.target, "HealType") == DAMAGE_TYPE_POSITIVE)
            nRace = RACIAL_TYPE_HUMAN;

        if (GetIsSpellTarget(si, si.target, TARGET_TYPE_ALLY) || nRace == nHealRace || GetLocalInt(si.target, "AllowHeal")) {
            if (GetSpellBuffLevel(si, si.target) > 0) {
                if (nRace != nHarmRace                                   &&
                    (nHealRace == RACIAL_TYPE_ALL || nRace == nHealRace) &&
                    !GetIsQuasiclass(QUASICLASS_ACCURSED_PARIAH, si.target)) {

                    SignalEvent(si.target, EventSpellCastAt(si.caster, si.id, FALSE));

                    if (nDice < 0) {
                        if (GetLocalInt(si.target, "SkillBasedHeal"))
                            nAmount = MetaPower(si, GetSkillRank(SKILL_HEAL, si.caster) * 2, 8, 200);
                        else
                            nAmount = GetMaxHitPoints(si.target);
                    } else if (nSides > 1) {
                        if (si.clevel > 40)
                            nAmount = MetaPower(si, nDice, nSides + 4, nBonus);
                        else if (si.clevel > 20)
                            nAmount = MetaPower(si, nDice, nSides + 2, nBonus);
                        else
                            nAmount = MetaPower(si, nDice, nSides, nBonus);
                    } else
                        nAmount = MetaPower(si, nDice, nSides, nBonus);

                    effect eHeal = EffectHeal(nAmount);

                    ApplyEnergyVisual(si.target, nRank, si.class, fDelay);
                    DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, si.target));
                }

                /* Heal can heal persistent wounds at CL41+ */
                if (si.id == SPELL_HEAL) {
                    if (GetLocalInt(si.target, "PersistentWound")) {
                        if (si.clevel > 40) {
                            DeleteLocalInt(si.target, "PersistentWound");
                            RemoveEffectsFromSpell(HGEFFECT_PERSISTENT_WOUND, si.target);

                            FloatingTextStringOnCreature("Your wound closes!", si.target, FALSE);
                        } else
                            FloatingTextStringOnCreature("The Heal spell was too weak to close the wound!", si.target, FALSE);
                    }
                }
            }
        } else if (nRace == nHarmRace || nHarmRace == RACIAL_TYPE_ALL) {
            int bTouch;

            if (fRadius > 0.0) {
                si.dc += nFocus;
                bTouch = !GetSpellSaved(si, SAVING_THROW_REFLEX, si.target, nSaveType, fDelay);
                si.dc -= nFocus;
            } else
                bTouch = TouchAttackMelee(si.target);

            SignalEvent(si.target, EventSpellCastAt(si.caster, si.id));

            if (bTouch > 0 && !GetSpellResisted(si, si.target, fDelay)) {
                if (nDice >= 0) {
                    nAmount = MetaPower(si, nDice, nSides, nBonus);

                    if (bTouch == 2)
                        nAmount *= 2;
                    if (GetSpellSaved(si, SAVING_THROW_WILL, si.target, nSaveType, fDelay))
                        nAmount /= 2;
                } else if (GetObjectType(si.target) == OBJECT_TYPE_CREATURE) {
                    if (GetLocalInt(si.target, "SkillBasedHarm"))
                        nAmount = MetaPower(si, GetSkillRank(SKILL_HEAL, si.caster) * 2, 8, 200);
                    else
                        nAmount = GetCurrentHitPoints(si.target) - d4();
                } else
                    nAmount = 0;

                if (nAmount > 0) {
                    effect eDam = EffectDamage(nAmount, nDamType);

                    if (GetIsPC(si.caster))
                        MinLocalInt(si.target, "AttackerLevel", GetHitDiceIncludingLLs(si.caster));

                    ApplyEnergyVisual(si.target, -nRank, si.class, fDelay);
                    DelayCommand(fDelay + 1.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, si.target));
                }
            }
        }

        if (fRadius > 0.0)
            si.target = GetNextObjectInShape(SHAPE_SPHERE, fRadius, si.loc, TRUE);
        else
            si.target = OBJECT_INVALID;
    }
}

LMK if you have questions.

Funky

#14
Thayan

Thayan
  • Members
  • 244 messages
Not dead yet. ;)

Right now we are using NWNX to set race for our PC subraces and undead (vampires) and it seems to be working fine, albeit we haven't had a large sampling of undead PCs who had their race change since our end-of-world events in January. Earlier this year we ended up making a hak for Thay, and in that the necessary changes to the spells.2da allow the negative energy spells to be cast on one's self, so as far as spells that should affect undead, I have made no changes since that takes care of them. Another benefit of using NWNX to change race is that some of the hardcoded stuff like item AC or AB bonuses vs. race do end up affecting these PCs as well.

However, a drawback is that this does affect other systems which 'assume' all PCs are of one of the 6 standarad races. For example, off the top of my head I do know I had to modify the Bioware horse scripts to avoid appearance changes when logging in. But other than that I guess I'm not sure why there are reports it doesn't work. However if someone has specifics I'd be more than happy to test them with our systems to try either verifying or disproving.

#15
DM_Vecna

DM_Vecna
  • Members
  • 280 messages
Thayan, are you running Linux or windows nwnx_funncs?

#16
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
When it comes to setting racial types, you can do it with either lin or win, though win might require leto to do it - I just don't know, since we use lin. As for the safety of setting racial types, I got ahold of acaos and discussed it with him:

Funky: one last question, came up on the bioboards - what was the issue with setting racial types of pcs, and was it specific to leto only, not funcs?
acaos: it hasn't been tested and could do nasty things, given how deeply entwined races are in the system
acaos: you'll definitely need to boot them and make them relog
acaos: after changing their race
Funky: I don't plan to
Funky: was a question on the bioboards only :P
acaos: it's more of a 'this could be really dangerous'
acaos: than a 'we are aware of isues'
Funky: ah ok
acaos: since races have lots of hard-coded special behavior for PCs
acaos: you wouldn't muck with their classes either
acaos: same reason
Funky: is that why you chose to make switches in negpos and route all healing/neg spells through that, instead?
acaos: er?
Funky: you know how all heal spells go through neg/pos, and we control undead racial stuff through it?
acaos: right, but that's no different than how NWN handles it in base scripting
Funky: someone on bioboards is considering nwnx_funcs editing races
acaos: just it has it in a dozen scripts
acaos: and we have it in one


Funky

#17
WhiZard

WhiZard
  • Members
  • 1 204 messages
I'm not sure what acaos is referring to with "nasty things", "hard-coded special behavior", and "it hasn't been tested." Standard polymorphs already change the race for PCs and the problematic issues typically encountered are for leveling up and compatibility with 1.69 horse scripts.

Modifié par WhiZard, 08 août 2013 - 03:13 .


#18
Thayan

Thayan
  • Members
  • 244 messages

DM_Vecna wrote...

Thayan, are you running Linux or windows nwnx_funncs?



I run Windows nwnx_funcs.

So far I've not noticed any crazy things with changing PC racial types other than some scripting modifications that were needed (the horse scripts, for example). We only change subrace PCs to the Outsider, Undead, Goblinoid, and Orc races though. We'd had some of our subrace PCs up to level 14-ish, so through multiple level-ups I've not had reports of anything screwed up. That's not to say it couldn't/won't happen....it just nothing show-stopping has happened yet and we've been running with this for ~8 months.

#19
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

WhiZard wrote...

I'm not sure what acaos is referring to with "nasty things", "hard-coded special behavior", and "it hasn't been tested." Standard polymorphs already change the race for PCs and the problematic issues typically encountered are for leveling up and compatibility with 1.69 horse scripts.

We're discussing setting of  racial type directly, not polymorphing, which is a completely different ball of wax (though also one of the more crash-and-bug-prone aspects of NWN). A better example would've been monks becoming outsiders at level 20, but that's something that's been extensively playtested already.

He was saying HE hadn't tested setting of racial types (and he did an awful lot of testing when poking around in the engine to make the various eXalt plugins, including the one under discussion, funcs). Further, he was talking about hard-coded issues, at the engine level, things not accessible via nwscript - nothing like the 1.69 horse scripts.

The kinds of 'nasty things' he has in mind are, among other things, potential crash bugs, which are all too common when tinkering with this kind of engine-level editing. Similar issues arise, as he alluded, when you tinker with other character data, like class - I suspect that that came first to his mind because he did a lot of work trying to hack in additional classes like favored soul without the accompanying mandatory client-side haks.

Some edits the engine will outright reject, yeiling unstable or unpredictable behavior, while others simply have unintended consequences. The most recent example from our servers that comes to mind is spellslots. One of our devs decided to expand on some spellslot functionality acaos had added, by using nwnx functions to add some additional commands:

-- "!sb save # NAME" - This will save your current spell books into slot
# (0-9) with name NAME. This works for all non-spontaneous classes.
-- "!sb load #" - This will load saved spellbook # (0-9) into your active spell book.
-- "!sb list" - This will list your saved spell books.

The dev in question didn't fully understand the nwnx functions he used (and the shortcomings which led acaos not to implement those particular spellbook commands in the first place). As a result, a crash bug was born, by virtue of doing something to the character in memory that the engine just wasn't prepared to cope with. As is often the case with engine-level tinkering (which is bereft of the safety of repeated testing the rest of the engine has been subjected to over the years), unintended consequences resulted, leading an old and previously unproblematic command, !sb empty, to crash the server. The thread on our forums is here, should you be curious:

Click Me

In that case, the correlation between the problem and the edit that caused it was sufficiently strong to allow us to track it down and fix it quickly. That's not always the case, however, and the more things an edit touches on, the more difficult it can be to track down, especially if you don't notice any problems initially. Racial types touch on a lot of things.

I'd sum it up by saying 'here be dragons', but in this case, the dragons often turn out to be real in a metaphorical sense. Nevertheless, experimentation is how the community adds to its knowledge base, so by all means, dive in. Just do so forewarned that this is not necessarily a simpler alternative to nwscript edits to healing/neg spells, which is what the poster in question seemed to be after.

Funky

#20
DM_Vecna

DM_Vecna
  • Members
  • 280 messages
Thanks Funky, your knowledge and experience from your pw team has defined a lot of my experience and fun here over the years.

I am still interested to hear from Thayan which version he is running as I am getting almost no effect when trying to set race on a PC using the Linux version and have confirmed similar issues with _Guile aka Genesis's subrace system using Linux as well. At this point I have not confirmed anyone successfully setting PC race with the Linux version. I have used Elvens nwnx_ funcs and he even tinkered around with my server reporting some nwnx_funcs issues. So perhaps that nwnx plugin is just broken for that function. If you happen to talk to Acaos again maybe he can confirm.

However I was only trying to use Undead and Fey (Wemic, Brownie) races so maybe it's just easier to script the effects that getting it to work. :)

#21
WhiZard

WhiZard
  • Members
  • 1 204 messages

FunkySwerve wrote...
We're discussing setting of  racial type directly, not polymorphing, which is a completely different ball of wax


That is exactly what I am questioning- whether the character file field provides any further problems to the hard-code that the polymorph does not.  Leto changes to races have been going on for a long time without problematic behavior.  Polymorph race changes encounter hard-code in numerous places.  When leveling the prestige class assumes your race to be that of your polymorph and feats assume your ability score is modified by the racial modifier of the race of your polymorph.  GetRacialType(), a hard-coded function, again looks at the polymorph race.  Furthermore, unpolymorphing actually writes into the character file.  For example, if I used SetCreatureAppearanceType() to a badger before polymorphing, then after polymorphing and unpolymorphing my creature size would be tiny.  Polymorph just uses data from what I was before (in this case the creature size bug came from it storing an appearance and using that appearance's index to find creature size), writing that in when the unpolymorph occurs.  Given the vast number of times the current race, rather than the character file value, hits the hard-code, I am not sure if there are any problems that are character file value only.

#22
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

WhiZard wrote...

FunkySwerve wrote...
We're discussing setting of  racial type directly, not polymorphing, which is a completely different ball of wax


That is exactly what I am questioning- whether the character file field provides any further problems to the hard-code that the polymorph does not.


That's also off-topic. Vecna was asking about using funcs, which edits in-memory, not the character file. The short answer, though, is yes, it's possible to do things with classes that will muck things up. Of course, leto and funcs can do things outside of what happens when polymorphing, so again, proceed with caution.

Funky

#23
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

DM_Vecna wrote...

Thanks Funky, your knowledge and experience from your pw team has defined a lot of my experience and fun here over the years.

I am still interested to hear from Thayan which version he is running as I am getting almost no effect when trying to set race on a PC using the Linux version and have confirmed similar issues with _Guile aka Genesis's subrace system using Linux as well. At this point I have not confirmed anyone successfully setting PC race with the Linux version. I have used Elvens nwnx_ funcs and he even tinkered around with my server reporting some nwnx_funcs issues. So perhaps that nwnx plugin is just broken for that function. If you happen to talk to Acaos again maybe he can confirm.

However I was only trying to use Undead and Fey (Wemic, Brownie) races so maybe it's just easier to script the effects that getting it to work. :)


I'm asking acaos about this; hopefully he'll remember.

Funky

#24
Thayan

Thayan
  • Members
  • 244 messages

DM_Vecna wrote...
I am still interested to hear from Thayan which version he is running..


As I mentioned above, I'm using the Windows version of nwnx_funcs. If you're looking for the specific build number, we're on 0.9.41. With nwnx_funcs, we've had problems with the SetTag function crashing the server, but the SetRace function has not caused any crashes (yet?) of which I'm aware.

#25
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Sounds like there are significant differences between lin and win nwnx_funcs - we use SetTag with no issues at all in lin (it's used for our unique id system, which is ubiquitous). Acaos said he'd have to check the source for setting racial type in lin, when I pasted him the bit above about it not working.

Funky