Modifying Creature AI
#1
Posté 11 février 2014 - 02:50
#2
Posté 11 février 2014 - 04:55
#3
Posté 11 février 2014 - 05:40
#4
Posté 11 février 2014 - 06:50
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
Posté 11 février 2014 - 09:50
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
Posté 12 février 2014 - 02:25
#7
Posté 12 février 2014 - 04:24
#8
Posté 12 février 2014 - 04:30
#9
Posté 12 février 2014 - 04:56
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
Posté 12 février 2014 - 08:19
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
Posté 12 février 2014 - 09:04
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
Posté 12 février 2014 - 11:52
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
Posté 12 février 2014 - 02:22
#14
Posté 12 février 2014 - 05:21
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
Posté 12 février 2014 - 05:39
!
#16
Posté 12 février 2014 - 06:03
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
Posté 12 février 2014 - 06:36
not in onSpawn (although you could certainly 'assign' it from there, but i doubt there's a need to)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?
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
Posté 12 février 2014 - 07:00
// "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
Posté 12 février 2014 - 07:20
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
Posté 12 février 2014 - 07:52
now you get to learn to script
#21
Posté 12 février 2014 - 08:10
#22
Posté 12 février 2014 - 08:44
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.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?
Ie. the values, for those variables, are whatever you choose.
depends what you mean by 'custom script'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?
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
Posté 13 février 2014 - 02:44
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
Posté 13 février 2014 - 04:27
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
I haven't used GetFactionMostDamagedMember() myself but hey give it a shot
Modifié par kevL, 13 février 2014 - 04:40 .
#25
Posté 13 février 2014 - 04:32





Retour en haut






