Aller au contenu

Photo

Modifying Creature AI


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

#1
CPK87

CPK87
  • Members
  • 73 messages
While messing with some of the creatures in the Toolset, I noticed that certain ones seem to behave differently than others, no matter what you change of their properties.  For instance, some creatures just don't want to seem to cast spells, no matter what I set their class/level to or what spells or special abilities I provide them.  I would basically like to know if there is a way to get a creature that normally doesn't cast spells to do so, and where I can access those sources.  Thanks!

#2
Dann-J

Dann-J
  • Members
  • 3 161 messages
Spell casters rely on a specific ability score (wisdom for clerics, intelligence for wizards, charisma for sorcerers, etc). It needs to be at least 10 plus the level of spells you want them to cast to. So if you want a wizard to cast level 4 spells, their intelligence score needs to be at least 14.

#3
CPK87

CPK87
  • Members
  • 73 messages
Well, in this case I have a fire elemental that I want to cast Lesser Orb of Fire, and I've set its intelligence to 14 so it should be more than capable of casting the spell. I gave it 10 casts of the spell both as a spell and a special ability, but it just goes straight into melee combat. I assume this isn't due to the fact that it can't cast the spell, but because it is scripted somewhere to prioritize melee combat over casting spells. If I take a lich, on the other hand, it will cast spells I give it because it's already scripted to be a spellcasting creature.

#4
kevL

kevL
  • Members
  • 4 052 messages
search, X2_SPECIAL_COMBAT_AI_SCRIPT

I believe ColorsFade wrote up a custom package for this. Basically there's a hook in the creature AI routine ( TonyK's as well as stock AI ) where a user-script gets called, then the AI for that round can either be halted or left to continue.

the alternative is to screw around with Talents, which I don't think anyone really wants to do...

note, there's a bit-wise .2da that influences spellcasting, but it's totally bitwise (and I can't find a copy of Pain's editor).


Offhand, i suggest giving your Fire Elemental some levels of wizard/sorcerer, and force it to cast its Orb via a custom_combat_script (under conditions you'd specify in the custom script). Try not to use ActionCastSpell...(w/ bCheat = TRUE) because the 'cheat' does not input correct values for spellcasts

#5
Dann-J

Dann-J
  • Members
  • 3 161 messages

CPK87 wrote...

Well, in this case I have a fire elemental that I want to cast Lesser Orb of Fire, and I've set its intelligence to 14 so it should be more than capable of casting the spell. I gave it 10 casts of the spell both as a spell and a special ability, but it just goes straight into melee combat. I assume this isn't due to the fact that it can't cast the spell, but because it is scripted somewhere to prioritize melee combat over casting spells. If I take a lich, on the other hand, it will cast spells I give it because it's already scripted to be a spellcasting creature.


Fire elementals and liches use the same script set, as do most of the creature blueprints.

Not all spells granted as special abilities are handled by the AI.  I think they have to be in henchspells.2DA to be used. Since the orb spells were introduced in one of the expansions, I doubt they'd work as special abilities.

If a fire elemental's creature weapons cause more fire damage than the lesser orb spell does, and it's got levels in a class that allow it to cast spells normally (rather than as special abilities), then the AI may be choosing the attack that does the most damage.

There also seems to be a degree of randomness in the AI. If you spawn several identical creatures from the same blueprint, some might choose to use special abilities or spells, and others might not.

#6
CPK87

CPK87
  • Members
  • 73 messages
Lol, DannJ... you're absolutely right. I tried having it cast fireball instead and it did so without trouble. So, on that note, I went to check out the henchspells.2DA file, but couldn't make any sense from it as it was just a bunch of numbers without labels. Is there a way to edit the file to add spells that aren't currently on there?

#7
Dann-J

Dann-J
  • Members
  • 3 161 messages
I suspect one of the columns in henchspells.2DA refers to line numbers in spells.2DA.

#8
CPK87

CPK87
  • Members
  • 73 messages
I assumed that as well, but I couldn't find any of the orb spells in there, either. I'll try messing around with it though and see if I can manage to add an extra spell in there. Thanks for the info.

#9
Dann-J

Dann-J
  • Members
  • 3 161 messages
Henchspells.2DA doesn't seem to have changed much since NWN. One way to create new special abilities is to find an old one that probably doesn't work in NWN2, determine what spell script it uses in spells.2DA, then make a copy of the spell script you want to use and rename it to match the old unused script name.

The special ability will still have the old name, but in your own module it will fire your local renamed copy of the other script. That's how I got a bunch of 'new' dragon breath scripts to work without having to modify henchspells.2DA.

#10
kevL

kevL
  • Members
  • 4 052 messages
well, not saying much but i found a link to Pain's NeverWorker.

This is what henchspells.2da looks like w/ Acid Fog selected:

500x343http://imageshack.com/a/img855/7483/lp5e.png[/img]

500x343http://imageshack.com/a/img585/6741/3frm.png[/img]

and here's some coffeeTime reading.

#11
CPK87

CPK87
  • Members
  • 73 messages
Alright, good to know. I've also been able to get enemies to cast AOE spells even with allies nearby, by creating a custom faction for AOE casters. I am now attempting to get a creature to heal its allies, but without much success. Additionally, I tried giving an air elemental its own special ability, Pulse Whirlwind, and it won't even use that as a special ability. Sorry for the constant questions, but I'm pretty new to AI scripting and would like to learn more about it. Thanks again.

Edit:  Just saw your post kevL, I'll take a look at that as well, thanks for sharing.

Modifié par CPK87, 12 février 2014 - 09:08 .


#12
kevL

kevL
  • Members
  • 4 052 messages
note that NeverWorker is a wip as-is (make backups, although it's hard to say of what exactly because it has far-reaching ability: it is/was primarily designed to automagically merge 'stuff'.)

but yeh, eventually i want to try using it to change casting-weights, the heal you mention, Prayer for clerics should have more weight, etc.

[edit] Ultimately i think you'll have to go with a custom combat script (not that difficult to do) just to get a 'for sure' pulse attack, etc.

Modifié par kevL, 12 février 2014 - 11:56 .


#13
andysks

andysks
  • Members
  • 1 645 messages
KevL, how powerful is this Neverworker actually? Are we talking hardcoded stuff alterations and what not? Or a more comprehended view of what everything does? Sorry to change the topic for a bit, I just never saw this thing before :).

#14
kevL

kevL
  • Members
  • 4 052 messages
nothing that a reinstall wouldn't fix

the impression I get is it's alpha-beta software. However, pain's a really good programmer


i haven't used it enough to say more. But if a guy's on a time-schedule don't trust it with mission critical stuff,

Modifié par kevL, 12 février 2014 - 05:35 .


#15
kevL

kevL
  • Members
  • 4 052 messages
the point, here, is that it's the only app that has any hope of understanding/ altering henchspells.2da

!

#16
CPK87

CPK87
  • Members
  • 73 messages

kevL wrote...

note that NeverWorker is a wip as-is (make backups, although it's hard to say of what exactly because it has far-reaching ability: it is/was primarily designed to automagically merge 'stuff'.)

but yeh, eventually i want to try using it to change casting-weights, the heal you mention, Prayer for clerics should have more weight, etc.

[edit] Ultimately i think you'll have to go with a custom combat script (not that difficult to do) just to get a 'for sure' pulse attack, etc.



So when you write your custom combat scripts, do you just put it into the On Spawn On Script of the creature itself?  I still don't fully understand where the X2_SPECIAL_COMBAT_AI_SCRIPT=script_name is supposed to go exactly.  Would you mind clarifying?

#17
kevL

kevL
  • Members
  • 4 052 messages

CPK87 wrote...

So when you write your custom combat scripts, do you just put it into the On Spawn On Script of the creature itself?  I still don't fully understand where the X2_SPECIAL_COMBAT_AI_SCRIPT=script_name is supposed to go exactly.  Would you mind clarifying?

not in onSpawn (although you could certainly 'assign' it from there, but i doubt there's a need to)

The var goes directly on the NPC that will run your AI

It's a string_var that defines the name of the custom ai-script. That is, if you script up "fire_elemental_ai.nss/ncs", then set

X2_SPECIAL_COMBAT_AI_SCRIPT=fire_elemental_ai

on the fire elemental. (Since it's just a string variable you *could* do this onSpawn, but just plonk it straight onto a blueprint or instance is enough.) Then when the stock AI runs, by using more-or-less default event scripts ( nw_c2_default* ) in the NPC's event slots, eg. onHB, onCombatRoundEnd, onPerception, etc. etc

those AI scripts check for and hook your custom script.



Apart from writing the script, the only other thing to do is handle the return to the stock AI from your custom AI. The former looks for another variable, this time an INT that you either set or don't set in the latter.

X2_SPECIAL_COMBAT_AI_SCRIPT_OK

eg. SetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK", TRUE);

and that will stop the rest of the stock AI from running (and generally conflicting with your custom AI commands for that combat round). Once it's all set up, this process repeats itself every round the NPC is in combat mode.


ps. The custom ai script just goes in your module folder or wherever it can be accessed IG

Modifié par kevL, 12 février 2014 - 06:40 .


#18
kevL

kevL
  • Members
  • 4 052 messages
here's something to start out with, if you like

// "generic_ai_script"

void main()
{
    SendMessageToPC(GetFirstPC(FALSE), "run ( generic_ai_script ) for "
        + GetName(OBJECT_SELF));

    if (Random(2)) // 50-50
    {
        SendMessageToPC(GetFirstPC(FALSE), ". do custom AI this round");

        // creature should just stand there and get smaked around for 6 sec.

        SetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK", TRUE);
    }
    else
    {
        SendMessageToPC(GetFirstPC(FALSE), ". do stock AI this round");
        // creature should behave normally for 6 sec.
    }
}


#19
CPK87

CPK87
  • Members
  • 73 messages
Ahh.. so you don't even have to attach the script to anything to be called, as long as there is a variable attached to the creature? In this case, you would go to Variables under Scripts in the creature's properties, set the VariableType as a string, the variable name to fire_elemental_ai, and ValueString to X2_SPECIAL_COMBAT_AI_SCRIPT_OK (not sure what the OK is for at the end, btw)?

Edit:  Just gave it a try, and it worked.  This makes a lot more sense now, thanks for clearing that up for me!

Modifié par CPK87, 12 février 2014 - 07:37 .


#20
kevL

kevL
  • Members
  • 4 052 messages
glad to hear it.

now you get to learn to script :)

#21
CPK87

CPK87
  • Members
  • 73 messages
Yep, making some progress. Just curious, are X2_SPECIAL_COMBAT_AI_SCRIPT and X2_SPECIAL_COMBAT_AI_SCRIPT_OK just names you chose for the variables, or are they defined somewhere? If I were to put my own custom script on the creature, would I just insert it right below the SetLocalInt(...) in the first IF statement? Or does that go somewhere else?

#22
kevL

kevL
  • Members
  • 4 052 messages

CPK87 wrote...

Yep, making some progress. Just curious, are X2_SPECIAL_COMBAT_AI_SCRIPT and X2_SPECIAL_COMBAT_AI_SCRIPT_OK just names you chose for the variables, or are they defined somewhere?

The stock AI scripting looks for those varnames specifically (you can find the call and change it if you want to although that's not a good idea). But you've already defined its string-value yourself, in the Vars table on the NPC.

Ie. the values, for those variables, are whatever you choose.

If I were to put my own custom script on the creature, would I just insert it right below the SetLocalInt(...) in the first IF statement? Or does that go somewhere else?

depends what you mean by 'custom script'
the 'generic_ai_script' is the custom script, in this case; and you've hooked it already. (Replace it with your own, change its name to something you prefer, whatever)
but if you mean 'custom scripting' ... that which goes into the custom script ... then yes most of it goes into the first IF statement. It doesn't matter whether you put scripting above or below the SetLocal call (although i tend to leave that at the very top or bottom of scope if{ } because it's important and stands out better that way)


Also though, you see the "if (Random(2))" there? That's what everything centers around, that's your primary decision expression. I just left it really, really simple as a 50-50 random decision whether to do the custom or fallthrough to the stock AI.

in the game, it's not bad to just leave the primary decision simple like that. But somewhere you're going to need to define a target (the primary PC, the closest enemy, a chair, the NPC itself) IF the custom-action requires a target ofc. And if it's a spell or special ability you'll want to check whether the NPC actually has a use to expend, ELSE don't waste a round when the stock AI can be doing something nasty.

like i say, you're into creative scripting now...

Let's say you have a Lich with Power Word Kill memm'd: you might wait until the PC is down below 100 hps, then WHAM (as long as the stock AI hasn't expended its uses already) etc ad infinitum.

Modifié par kevL, 12 février 2014 - 08:56 .


#23
CPK87

CPK87
  • Members
  • 73 messages
Alright, based on you and ColorsFade's sample scripts, I threw something together of my own.  It's having a few issues though.  First, the creature insists on waiting until its allies are almost dead to cast a healing spell, despite the fact that I told it to heal when any of their hitpoints drop below max.  It would be nice to be able to control that range.

Second, neither Mass Cure Light Wounds nor Inspire Regeneration heal any of the creatures, even though the creature casts it successfully.

Lastly, I only wanted the creature to cast Inspire Regeneration once, so I set an int after it went through the first time so it wouldn't go through again, but the creature just spams it over and over.  Here's the scripting:


        SetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK", TRUE);

int nSpellId = SPELL_MASS_CURE_LIGHT_WOUNDS;
int nFeatId = FEAT_BARDSONG_INSPIRE_REGENERATION;
int nUsedRegen;  
object oTarget = GetFactionMostDamagedMember(OBJECT_SELF,TRUE);
int nMaxHP = GetMaxHitPoints(oTarget);
int nCurHP = GetCurrentHitPoints(oTarget);


if(nCurHP << nMaxHP)
{
  if(GetHasSpell(nSpellId))
  {
   ClearAllActions(TRUE);
   ActionCastSpellAtObject(nSpellId, oTarget);

   FinishAI(OBJECT_SELF);  
  }
}
if(GetHasFeat(nFeatId) && nUsedRegen == 0)
{
                   ClearAllActions(TRUE);
   ActionUseFeat(nFeatId, OBJECT_SELF);
   FinishAI(OBJECT_SELF);
nUsedRegen = 1;    
}
return;


I'm not sure what I'm doing wrong here, any thoughts?

#24
kevL

kevL
  • Members
  • 4 052 messages
ok let's start this from the beginning...


void main()
{
    int bCustomRound = FALSE;

    int nSpellId = SPELL_MASS_CURE_LIGHT_WOUNDS;
    int nFeatId = FEAT_BARDSONG_INSPIRE_REGENERATION;

    int nUsedRegen = GetLocalInt(OBJECT_SELF, "UsedRegen");

    object oTarget = GetFactionMostDamagedMember(); // defaults are Ok.
    // debug:
    SendMessageToPC(GetFirstPC(FALSE), "most damaged faction member = "
        + GetName(oTarget));

    int nMaxHP = GetMaxHitPoints(oTarget);
    int nCurHP = GetCurrentHitPoints(oTarget);
    if (nCurHP < nMaxHP && GetHasSpell(nSpellId))
    {
        SendMessageToPC(GetFirstPC(FALSE), ". casting Mass Cure Light");

        ClearAllActions(TRUE);
        location lTarget = GetLocation(oTarget);
        ActionCastSpellAtLocation(nSpellId, lTarget);

        bCustomRound = TRUE;
    }
    else if (GetHasFeat(nFeatId) && nUsedRegen == 0)
    {
        SendMessageToPC(GetFirstPC(FALSE), ". inspiring Regen");

        ClearAllActions(TRUE);
        ActionUseFeat(nFeatId, OBJECT_SELF);
        SetLocalInt(OBJECT_SELF, "UsedRegen", TRUE);

        bCustomRound = TRUE;
    }

    if (bCustomRound)
    {
        SetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK", TRUE);
    }
}


prob 1. nUsedRegen is a temporary variable; it disappears as soon as the script is finished. You should SetLocal on the NPC to keep track of whether Regen has been used from one combat round to the next.
prob 2. less-than is < .... << is a bitwise shift :o

I haven't used GetFactionMostDamagedMember() myself but hey give it a shot :)

Modifié par kevL, 13 février 2014 - 04:40 .


#25
CPK87

CPK87
  • Members
  • 73 messages
GetFactionMostDamagedMember() is one of the things that actually worked. On the bitwise shift... oops.