Aller au contenu

Photo

Scaling Mummy Dust & Dragon Knight


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

#1
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

I have a modified/renamed version of the epic spell Mummy Dust for my module. It spawns an undead for most casters but for druids it spawns a treant. This works just fine. What I'd like to do however, is make it scale with the caster's level a bit.

 

Here's the script currently:

#include "x2_inc_spellhook"
#include "inc_epicspells"
void main()
{
    if (!X2PreSpellCastCode())
    {
        return;
    }
    if (GetCanCastSpell(OBJECT_SELF, MUMDUST_DC, MUMDUST_S, MUMDUST_XP))
    {
    //Declare major variables
    int nDuration = 24;
    //effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD);
    effect eSummon;
    //NC:EDIT
    // Add check for druid for different summon

   int nDruid = GetLevelByClass(CLASS_TYPE_DRUID, OBJECT_SELF);
   string sSummon;

    if (nDruid >=20)
        {
        sSummon = "s_treant_001";
        }
    else
        {
        sSummon = "s_morglord_001";
        }

   //NC:EDIT

    //Summon the appropriate creature based on the summoner level
    //Warrior Mummy
    //NC:EDIT
    //Treant Druid only
    eSummon = EffectSummonCreature(sSummon,496,1.0f);
    //NC:EDIT

    eSummon = ExtraordinaryEffect(eSummon);
    //Apply the summon visual and summon the undead.
    //ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetSpellTargetLocation());
    ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eSummon, GetSpellTargetLocation(), HoursToSeconds(nDuration));
}


}

I'd like the spell to summon stronger minions every 5 levels. So the one we have in the script are the base versions. Basically at 25, 30, 35, and 40 the spell summons a stronger version of the minion. I also want to do the same with my modified Dragon Knight spell. How can I edit the scripts to do that?

#include "x2_inc_toollib"
#include "inc_epicspells"
#include "x2_inc_spellhook"
void main()
{
    if (!X2PreSpellCastCode())
    {
        return;
    }
    if (GetCanCastSpell(OBJECT_SELF, DRG_KNI_DC, DRG_KNI_S, DRG_KNI_XP))
    {
        //Declare major variables
        int nDuration = 20;
        effect eSummon;
        effect eVis = EffectVisualEffect(460);
        eSummon = EffectSummonCreature("s_drake_001",481,0.0f,TRUE);

        // * make it so dragon cannot be dispelled
        eSummon = ExtraordinaryEffect(eSummon);
        //Apply the summon visual and summon the dragon.
        ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eSummon,GetSpellTargetLocation(), RoundsToSeconds(nDuration));
        DelayCommand(1.0f,ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eVis,GetSpellTargetLocation()));
    }
}




#2
Tarot Redhand

Tarot Redhand
  • Members
  • 2 674 messages

Use switch command. In pseudo code (ie can't remember exact way of determining class)

int index = (level - 20) / 5
 
int IsDruid = // test if pc is a druid
 
if IsDruid
 
switch(index)
{
case 0: summon base monster
            break;
case 1: summon next monster up
             break;
//cut
 
case 4: summon toughest monster
}
else
//repeat above switch case substituting non druidic creature summons

 

TR


  • Nic Mercy aime ceci

#3
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

For my scaling needs it seems that since these are epic spells (which usually have fixed values) the game has a difficult time finding the caster's level for purposes of scaling.

 

I was able to figure out a script on my own that works but it seems... well... inelegant. But it DOES work so I can't complain.

 

Here's the scripts in case anyone wants to do something similar with the epic summons (though I imagine it could be done much better than I did). DO bear in mind I am using Boneshank's Epic Spell System so the script calls on his include.

#include "x2_inc_spellhook"
#include "inc_epicspells"
void main()
{
    if (!X2PreSpellCastCode())
    {
        return;
    }
    if (GetCanCastSpell(OBJECT_SELF, MUMDUST_DC, MUMDUST_S, MUMDUST_XP))
    {
    //Declare major variables
    int nDuration = 24;
    //effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD);
    effect eSummon;
    //NC:EDIT
    // Add check for druid for different summon
    //KA: EDIT
    // Add summon scaling every five levels

   int nDruid = GetLevelByClass(CLASS_TYPE_DRUID, OBJECT_SELF);
   int nWizard = GetLevelByClass(CLASS_TYPE_WIZARD, OBJECT_SELF);
   int nSorcerer = GetLevelByClass(CLASS_TYPE_SORCERER, OBJECT_SELF);
   int nCleric = GetLevelByClass(CLASS_TYPE_CLERIC, OBJECT_SELF);
   string sSummon;

    if ((nDruid > 19) && (nDruid <=24))
         {
          sSummon = "s_treant_001";
         }
          else if ((nDruid >= 25) && (nDruid <= 29))
         {
          sSummon = "s_treant_002";
          }
          else if ((nDruid >= 30) && (nDruid <= 34))
         {
          sSummon = "s_treant_003";
         }
          else if ((nDruid >= 35) && (nDruid <= 39))
         {
          sSummon = "s_treant_004";
         }
          else if ((nDruid == 40))
         {
          sSummon = "s_treant_005";
         }
    else
          if ((nWizard > 19) && (nWizard <=24))
         {
          sSummon = "s_morglord_001";
         }
          else if ((nWizard >= 25) && (nWizard <= 29))
         {
          sSummon = "s_morglord_002";
          }
          else if ((nWizard >= 30) && (nWizard <= 34))
         {
          sSummon = "s_morglord_003";
         }
          else if ((nWizard >= 35) && (nWizard <= 39))
         {
          sSummon = "s_morglord_004";
         }
          else if ((nWizard == 40))
         {
          sSummon = "s_morglord_005";
         }
    else
          if ((nSorcerer > 19) && (nSorcerer <=24))
         {
          sSummon = "s_morglord_001";
         }
          else if ((nSorcerer >= 25) && (nSorcerer <= 29))
         {
          sSummon = "s_morglord_002";
          }
          else if ((nSorcerer >= 30) && (nSorcerer <= 34))
         {
          sSummon = "s_morglord_003";
         }
          else if ((nSorcerer >= 35) && (nSorcerer <= 39))
         {
          sSummon = "s_morglord_004";
         }
          else if ((nSorcerer == 40))
         {
          sSummon = "s_morglord_005";
         }
    else
          if ((nCleric > 19) && (nCleric <=24))
         {
          sSummon = "s_morglord_001";
         }
          else if ((nCleric >= 25) && (nCleric <= 29))
         {
          sSummon = "s_morglord_002";
          }
          else if ((nCleric >= 30) && (nCleric <= 34))
         {
          sSummon = "s_morglord_003";
         }
          else if ((nCleric >= 35) && (nCleric <= 39))
         {
          sSummon = "s_morglord_004";
         }
          else if ((nCleric == 40))
         {
          sSummon = "s_morglord_005";
         }

   //NC:EDIT

    //Summon the appropriate creature based on the summoner level
    //Warrior Mummy
    //NC:EDIT
    //Treant Druid only
    eSummon = EffectSummonCreature(sSummon,496,1.0f);
    //NC:EDIT

    eSummon = ExtraordinaryEffect(eSummon);
    //Apply the summon visual and summon the undead.
    //ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetSpellTargetLocation());
    ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eSummon, GetSpellTargetLocation(), HoursToSeconds(nDuration));
}


}

I did basically the same thing with Dragon Knight for each class.



#4
Tarot Redhand

Tarot Redhand
  • Members
  • 2 674 messages

In fact for code clarity and maintenance it might pay to make the 2 switch statements and associated code, into 2 small functions.

 

TR



#5
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

In fact for code clarity and maintenance it might pay to make the 2 switch statements and associated code, into 2 small functions.

 

TR

 

>_>  unfortunately I have no idea what that means... my scripting skills (ha! skills! as if!) are extremely limited.



#6
Shadooow

Shadooow
  • Members
  • 4 468 messages

>_>  unfortunately I have no idea what that means... my scripting skills (ha! skills! as if!) are extremely limited.

you can write

switch(iLevel)

{

case 20:

case 21

case 22:

case 23:

case 24:

case 25:

something;

case 26:

case.......

}

 

absolutely best is what Tarot posted you, this is a something between his and yours version, easier to understand


  • Nic Mercy aime ceci

#7
Tarot Redhand

Tarot Redhand
  • Members
  • 2 674 messages
OK (huh nearly said orange juice - J & K are next to each other on keyboard so I had to correct OJ to OK). I have gone through your routine posted above. Checking the function prototype for GetLevelByClass there is a default parameter which means that you don't need to include OBJECT_SELF, which changes
 
GetLevelByClass(CLASS_TYPE_DRUID, OBJECT_SELF);
to
GetLevelByClass(CLASS_TYPE_DRUID);
 
Apart from that I have reworked your code and came to a realisation. There is one case that hasn't been considered before by either of us. That is what happens when you have a level 20 druid who is also a level 20 other type of spellcaster? Which version of the spell is to be cast? I didn't really handle it in the code that follows, just exited the routine. I also noticed that for the three other classes they all summoned the same creature at the same level, so I combined them into 1 routine.
 
Given that
#include "inc_epicspells"
and
GetCanCastSpell(OBJECT_SELF, MUMDUST_DC, MUMDUST_S, MUMDUST_XP)
are (AFAIK) not part of standard 1.69 code, I have not actually tested the code that follows because I don't seem to have access to those bits. What I can say is that it should work, just I am not going to guarantee it. Should this code work then it should work for all cases where the PC has between 20 and 39 levels overall. When you get to level 40 there is the case mentioned in the preceding paragraph.
 
#include "x2_inc_spellhook"
#include "inc_epicspells"
 
string GetDruidCreature(int iCreature)
 
// determines which creature a druid will summon
 
{
    string sCreatureName;
    switch(iCreature)
    {
        case 0:
               sCreatureName = "s_treant_001";
               break;
        case 1:
               sCreatureName = "s_treant_002";
               break;
        case 2:
               sCreatureName = "s_treant_003";
               break;
        case 3:
               sCreatureName = "s_treant_004";
               break;
        Case default:
               sCreatureName = "s_treant_005";
               break;
    }
    return sCreatureName;
}
 
string GetNonDruidCreature(int iCreature)
 
// determines which creature a non-druid will summon
 
{
    string sCreatureName;
    switch(index)
    {
        case 0:
               sCreatureName = "s_morglord_001";
               break;
        case 1:
               sCreatureName = "s_morglord_002";
               break;
        case 2:
               sCreatureName = "s_morglord_003";
               break;
        case 3:
               sCreatureName = "s_morglord_004";
               break;
        Case default:
               sCreatureName = "s_morglord_005";
               break;
    }
    return sCreatureName;
}
 
void main()
{
    if (!X2PreSpellCastCode())
        return;
 
    if (GetCanCastSpell(OBJECT_SELF, MUMDUST_DC, MUMDUST_S, MUMDUST_XP))
    {
        //Declare major variables
 
        int nDuration = 24;
 
        //effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD);
 
        effect eSummon;
 
        //NC:EDIT
        // Add check for druid for different summon
        //KA: EDIT
        // Add summon scaling every five levels
 
        int iClassCount = 0; //just making sure that the variable is zero probably unnecessary
        int iIndex;
        int nDruid = GetLevelByClass(CLASS_TYPE_DRUID);
        int nWizard = GetLevelByClass(CLASS_TYPE_WIZARD);
        int nSorcerer = GetLevelByClass(CLASS_TYPE_SORCERER);
        int nCleric = GetLevelByClass(CLASS_TYPE_CLERIC);
 
// This section of code counts the number of classes the PC has that qualify to be able to cast the spell
// Because I am lazy I also made it so that iIndex is configured for the switch statements
 
        if(nDruid >= 20)
        {
            iClassCount++;
            iIndex = (nDruid - 20) / 5;
        }
        if(nWizard >= 20)
        {
            iClassCount++;
            iIndex = (nWizard - 20) / 5;
        }
        if(nSorcerer >= 20)
        {
            iClassCount++;
            iIndex = (nSorcerer - 20) / 5;
        }
        if(nCleric >= 20)
        {
            iClassCount++;
            iIndex = (nCleric - 20) / 5;
        }
 
//The next 2 lines make sure that the PC has 1 and only 1 qualifying class
 
        if(iClassCount != 1)
            return;
 
        string sSummon;
 
//if PC is a druid get druidic creature name otherwise get other name
 
        if(nDruid>=20)
            sSummon = GetDruidCreature(iIndex);
        else
            sSummon = GetNonDruidCreature(iIndex);
 
        //NC:EDIT
 
        //Summon the appropriate creature based on the summoner level
        //Warrior Mummy
        //NC:EDIT
        //Treant Druid only
 
        eSummon = EffectSummonCreature(sSummon,496,1.0f);
 
        //NC:EDIT
 
        eSummon = ExtraordinaryEffect(eSummon);
 
        //Apply the summon visual and summon the undead.
        //ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetSpellTargetLocation());
 
        ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eSummon, GetSpellTargetLocation(), HoursToSeconds(nDuration));
    }
}
 
Hope this makes things clearer.
 
TR

  • Nic Mercy aime ceci

#8
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

I wont deny my eyes crossed a bit while looking at the code you posted but I did understand a bit of what its doing. Thank you for taking the time to show me an example! I'll definitely keep this handy for reference.

 

Just to note, trying to compile the above script spits out the following error:

 

Error: NSC1040: Syntax error at "default"
 



#9
meaglyn

meaglyn
  • Members
  • 807 messages

Try making the uppercase "Case" lowercase :)



#10
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

Try making the uppercase "Case" lowercase :)

 

I actually DID try that! But no effect :(



#11
meaglyn

meaglyn
  • Members
  • 807 messages

Take out the "case" there. I should have read it more closely... default is the keyword by itself, no case.



#12
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

cool i'll give it a shot!

 

EDIT: In regards to     switch(index)     it spits out:

 

Error: NSC1020: Undeclared identifier "index"
Compilation aborted with errors.



#13
Tarot Redhand

Tarot Redhand
  • Members
  • 2 674 messages

Oooops little syntax error. In the second function change switch(index) to switch(iCreature). Should fix. Don't hesitate to report any other errors. (I did mention it was untested).

 

One other thing, look at this thread and follow the first link.

 

TR



#14
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

he he  thank's Tarot! I'll give it a test and let you know the results!



#15
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

After testing, it works exactly as it should! Now here's a question... My mod allows half of a palemaster's levels to add to his casting level. So if you have 30 palemaster levels (and assuming 10 levels of wizard or sorcerer) your spells go off as a level 25 caster. How would I take that into consideration for this script? If I also wanted to have a version that counted ALL of palemaster's levels as part of his casting level for this spell what would that look like as well? I'd like to see both just so I can see (and hopefully understand) how its done for future works.

 

EDIT: also to note. Thanks to what you showed me with Mummy Dust, I was able to adapt your script to the Dragon Knight spell as well and it works perfectly!



#16
MagicalMaster

MagicalMaster
  • Members
  • 2 000 messages

You still trying to figure out the PM caster level bit?



#17
Baaleos

Baaleos
  • Members
  • 1 329 messages

The thing I never liked about the EffectSummon command, is that it breaks out of an object centric approach.

CreateObject gives you an object to play with - you can apply effects and modify ability / skills with more nwscript.

 

EffectSummon doesnt return an object that you can directly interact with from within the same script.

(Might be possible through GetAssociate)

 

If it is a case of scaling the creature with your level, you could potentially just increase its ability scores, feats, and skills etc, with nwnx_funcs - if you have access to it.

Of course getting an object reference to the creature summoned by Mummy Dust might be the challenge.



#18
meaglyn

meaglyn
  • Members
  • 807 messages

Fwiw, I make a call to a routine right after applying the summon effect that uses a GetAssociate(ASSOCIATE_TYPE_SUMMONED, oCaster, nNth) loop. When it finds the one that has not been marked it marks it at returns that object.  Then I can augment the summon as needed. Works nicely.  I allow multiple summons so the marking part is needed. If you don't allow multiples you should be able to just do the single GetAssociate call.

 

EffectSummon returns an effect, just like all the other effect constructors. It would be really broken if it returned an object ;)



#19
WhiZard

WhiZard
  • Members
  • 1 204 messages
EffectSummon doesnt return an object that you can directly interact with from within the same script.

(Might be possible through GetAssociate)

 

Interestingly, the GetMaster() does not work the same way.  A summon does not have a master until after the OnSpawn



#20
Baaleos

Baaleos
  • Members
  • 1 329 messages

Its possible to add this functionality through nwnx however.

In order to fix animal companions and familiars for levels 41+ the summoning code basically had to be rewrote to not fail when trying to get level 41+ resrefs.

(Yes - I know this was done in the Unofficial Community Patch, I generally like to code things myself, so I know how they work)

 

By rewriting the code, there are key areas where hooks could be added, in order to return or override the resref used by the summoning script.

Also possible to return the creature being summoned in a hook: and then enhance that creature prior to its onspawn script firing.



#21
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

You still trying to figure out the PM caster level bit?

 

 

Yes actually. My mod already allows half of a Palemaster's levels to add to casting level. I don't know how to make that be taken into account for the mummy dust/dragon knight script since they don't use a standard get caster level thing. I also wanted to have a version that counted ALL of palemaster's levels (added to their base casting class levels) as part of his casting level for the mummy dust spell specifically, while using half their palemaster levels (added to their base casting class levels) for Dragon Knight.

 

The technical aspects of that scripting is just out of my league to come up with on my own. I didn't press that specific scripting cause I've been asking for scripting help a great deal recently and I don't want to abuse people's good graces too much.



#22
MagicalMaster

MagicalMaster
  • Members
  • 2 000 messages

How are you determining base casting class levels (in a general sense)?  For example, say I have a 21 Cleric/3 Wizard/16 Pale Master who knows Mummy Dust.  Should my caster level be...

 

A. 21 (cleric levels)

B. 37 (cleric levels plus pale master levels)

C. 19 (wizard levels plus pale master levels because pale master levels trump cleric levels for an undead spell)

D. something else entirely



#23
WhiZard

WhiZard
  • Members
  • 1 204 messages

Just so the OP knows the behavior of GetCasterLevel() in feats, the caster level would be the class level (in feats this is unaffected by level drain) of the class that took the feat while leveling up.  So a Fighter 8/Bard 4/Palemaster 28 would have a caster level of 28 when casting epic spells, as palemaster would be the only class that would be able to select one.  Epic spells can only be selected by a class if that class has reached a certain level (for clerics, druids, wizards, and sorcerers this is 21, for palemaster this is 15).  Qualifying by one class does not mean that another class is qualified.



#24
Nic Mercy

Nic Mercy
  • Members
  • 181 messages

How are you determining base casting class levels (in a general sense)?  For example, say I have a 21 Cleric/3 Wizard/16 Pale Master who knows Mummy Dust.  Should my caster level be...

 

A. 21 (cleric levels)

B. 37 (cleric levels plus pale master levels)

C. 19 (wizard levels plus pale master levels because pale master levels trump cleric levels for an undead spell)

D. something else entirely

 

The caster levels for the palemaster are a total of the arcane casting class and half the palemaster levels. So in your example your casting level would be 11 with arcane spells (this is assuming only half of the palemaster levels counting) and 21 with divine spells. The palemaster levels don't benefit divine casting level. For your example the mummy dust spell would use the cleric levels for its casting level because its higher than the arcane level. So because of the low casting level (21) that character would summon the weakest of the mummy dust summons.

 

It's all tied to the class of spells being cast. If you were say sorc3/wiz7/pale30 your casting level with wizard spells would be 22 but with sorc spells it would be 18... at leas that's how I THINK it works. I'm not exactly the poster child for the comprehension of scripting here.



#25
MagicalMaster

MagicalMaster
  • Members
  • 2 000 messages

The caster levels for the palemaster are a total of the arcane casting class and half the palemaster levels.

 

Huh?  You said you wanted Mummy Dust to use all of the Pale Master levels...was that a typo?

 

Assuming it wasn't, you should be able to change this section

int iClassCount = 0; //just making sure that the variable is zero probably unnecessary
        int iIndex;
        int nDruid = GetLevelByClass(CLASS_TYPE_DRUID);
        int nWizard = GetLevelByClass(CLASS_TYPE_WIZARD);
        int nSorcerer = GetLevelByClass(CLASS_TYPE_SORCERER);
        int nCleric = GetLevelByClass(CLASS_TYPE_CLERIC);

to

int iClassCount = 0; //just making sure that the variable is zero probably unnecessary
        int iIndex;
        int nDruid = GetLevelByClass(CLASS_TYPE_DRUID);
        int nWizard = GetLevelByClass(CLASS_TYPE_WIZARD) + GetLevelByClass(CLASS_TYPE_PALEMASTER);
        int nSorcerer = GetLevelByClass(CLASS_TYPE_SORCERER) + GetLevelByClass(CLASS_TYPE_PALEMASTER);
        int nCleric = GetLevelByClass(CLASS_TYPE_CLERIC);

Part of my concern here is that spells overlap.  If you simply have the script find the highest caster level when you cast Bull's Strength, for example...then it'll assume Bull's Strength was cast as a level 21 Cleric rather than as a level 11 Wizard/Pale Master due to the fact that both spellbooks have Bull's Strength.  See the issue?

 

This isn't a problem for Epic Spells (since they're not class specific) or spells specific to that class (or, more specifically, unique to that "half" or "third" of the character)...but it definitely can be an issue if all you do is check the highest class level.