Aller au contenu

Photo

What is the best way to customize a Creature's Combat AI?


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

#1
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
I'm in the middle of working on an encounter, and I want to script custom AI for a "boss" type creature, so that it performs buffs/attacks/spells in a certain way and selects targets a certain way. 

I'm not sure the best way to do this. I was looking at KevL's scripts for the OC Dragons rewrite to see if I could gain some clarity but I'm not sure I gleaned much from it. It looks like the bulk of his scripting for those encounters is done on the End Combat Round script and I am wondering if that's the best place? 

Or is it simpler/easier to write a script that hooks into the default scripts? I'm looking at the NW_C2_DEFAULT3 script and it seems like there's a place to hook in with a User Defined Script, but I am not quite sure how that works. I can't even find documentation on how the User Defined Script works or how to use it. 

Open to suggestions on this. Thanks guys. 

#2
MasterChanger

MasterChanger
  • Members
  • 686 messages
There's a special combat script hook meant just for this. You set it as a local variable on the creature. This is in the DetermineCombatRound function, so it's only run while in combat and supplements the heartbeat. The script checks if there's a script with a certain name stored on the creature and then runs it if so.

I'll have to take a look later to figure out what the variable is supposed to be called. Something like SPECIAL_COMBAT_SCRIPT.

#3
kevL

kevL
  • Members
  • 4 056 messages
X2_SPECIAL_COMBAT_AI_SCRIPT = "custom_ai"

where X2.. is the varname and "custom_ai" is the string ( set on the creature like MC says )

DetermineCombatRound() and the more advanced HenchDetermineCombatRound() -- not sure what diff. but i think hDCR pertains more to companions etc, and one or the other is called from the default nw_c2_default* ai (event slots on creatures) -- both functions should make a call to check the creature for that string_var and if true run the custom script by that name as part of the combat ai.

See 'x2_ai_demo.nss' for more details ...

#4
kevL

kevL
  • Members
  • 4 056 messages
postscript:
UserDefinedEvents were widely touted when they came out in NwN1, but I think they're awkward and have seen at least one other person deprecate them. On the other hand, they work .. but you'd have to change the spawn script for the creature(s) using them, to enable calling the event (usually, i think, unless perhaps it's signalled by SignalEvent()...). i haven't played with them much, like i say they're awkward (but good to know about because you'll see them used here and there),

#5
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
Okay, that works. I can see my script debug lines firing. Thanks guys!

#6
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
Edit, I'm wrong. This isn't working. My scrip isn't firing at all. Never did.

Here's what I am doing:

The the bottom of the Properties for the NPC, I am adding a Variable:

Name=X2_SPECIAL_COMBAT_AI_SCRIPT
ValueString = "my_custom_combat_script"

Not working...

#7
kevL

kevL
  • Members
  • 4 056 messages
.. worked fine here.

Creature (hostile) w/ var set & onPerception NW_C2_DEFAULT2 called HenchDetermineCombatRound() when perceiving my PC.

hDCR fires 'hench_o0_ai', which has this code:


// -----------------------------------------------------------------------
// July 27/2003 - Georg Zoeller,
// Added to allow a replacement for determine combat round
// If a creature has a local string variable named X2_SPECIAL_COMBAT_AI_SCRIPT
// set, the script name specified in the variable gets run instead
// see x2_ai_behold for details:
// -----------------------------------------------------------------------------
string sSpecialAI = GetLocalString(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT");
if (sSpecialAI != "")
{
  if (GetCurrentAction() == ACTION_INVALID)
  {
    // Jug_Debug(GetName(OBJECT_SELF) + " special AI " + sSpecialAI);
    SetLocalObject(OBJECT_SELF, "X2_NW_I0_GENERIC_INTRUDER", oIntruder);
    ExecuteScript(sSpecialAI, OBJECT_SELF);

    if (GetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK"))
    {
      // Jug_Debug(GetName(OBJECT_SELF) + " exit special AI " + sSpecialAI);
      DeleteLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK");

      return;
    }
  }
  else
  {
    return;
  }
}


- copy-pasted both varname and StringValue from your post onto the hydra (faction, Hostile) and put this in the script:
void main()
{
  SendMessageToPC(GetFirstPC(FALSE), ". . my_custom_combat_script");
}

kaboomzer!

#8
kevL

kevL
  • Members
  • 4 056 messages
ps. that's with TonyK's ai, but it should be nearly identical

pps. Dragons in the OC uses a combination of this method (not CombatRoundEnd per se) and UserDefined HB to reset the breath/buffet/slap abilities ... the odd thing there is that it sets X2_SPECIAL.. on the onSpawn scripts

Modifié par kevL, 16 mars 2013 - 12:56 .


#9
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
 Okay, I figured it out and got it working. Here's the deal: 

Not all of the hench_i0_ai scripts are the same.

If you have OC, MotB and SoZ installed, like I do, you have three versions of this script. I don't know if it was the MotB version or the SoZ version of nw_c2_default2 that was set to default on my character, but it did not work because it called a version of henc_i0_ai that did not check for the presence of X2_SPECIAL_COMBAT_AI_SCRIPT variable. So, I dug through the scripts and found out that the OC script does do this (or at least I am assuming it is the OC script as it's the first one listed of the three available scripts). 

Once I switched to the *first* listed nw_c2_default2 script for OnPerception, my script got executed. 

So... another tidbit of information that goes into my master notes pile... 

Thanks guys! I'm happy about this now. One thing I've really wanted to do is script out caster enemy actions. Casters are so dumb... Mine are going to be a little better I hope. Saves should matter now :)

#10
kevL

kevL
  • Members
  • 4 056 messages
cool CF,

- for the record i just did a compare of nw_c2_default2 scripts from OC / MotB / SoZ

OC uses DetermineCombatRound()
MotB uses HenchDetermineCombatRound()
SoZ identical to MotB.

DCR is found in 'nw_i0_generic' ( OC only ) : this function Executes the specialAI directly, line #658.

hDCR is found in 'hench_i0_ai' ( identical in all 3 campaigns (double checked.. /shrug) ) : this function Executes 'hench_o0_ai', line #244. Here the AI can be tapped with a string_var on the creature "AIScript", but i see no option to halt the regular AI from continuing. TonyK's 'hench_i0_ai' is a bit different.

- 'hench_o0_ai' is the same for all 3 campaigns. Buried on line #518 is the call for X2_SPECIAL_COMBAT_AI_SCRIPT, with an option to halt the regular AI. TonyK's 'hench_o0_ai' is substantially different but this part is identical.



It was never like this in NwN /rant ;)

ps. Tony's source can be found on the Vault in the .Erf
pps. When using a script that's found in more than one official campaign, the engine will use the latest version ( <- my 'working' hypothesis ).

#11
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
kevL, is the custom script supposed to run every round? Mine seems to run like twice and then quit...

Also, I had to copy the SoZ version of nw_c2_default2 and rename it and use that in the OnPerception for the NPC. If I selected the SoZ version, and then saved and later checked the NPC again, it was using the first version. The property editor apparently doesn't stick real well when multiple scripts have the same name. So I copied it, renamed a custom version, and set that on the OnPerception and it stuck.

#12
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
ugh, this is getting to be a real F*cking headache.

The script won't run, again. I don't know what to do anymore...

#13
kevL

kevL
  • Members
  • 4 056 messages
ok, I'm sure the dropdown box when selecting a script is misleading. It does not choose the script version. Only the name; for example, when i look in a gff editor (for a creature, eg.) I've seen only the name there -- never seen anything that hints at what 'version' to use. Only the script name ...

So the engine is going to hunt for the script in this order: .hak, override, campaign, module, default. Hak is trump. Now, what i've done to get around most of these headaches is

a) pull all the scripts out of \\Data ( and \\Campaigns ) and store them for reference somewhere. Use subdirectories to keep them sorted.

B) put the latest version of each script in Override. Use a duplicate searching utility like AllDup to keep the override happy. (I leave the campaign scripts under Campaigns tho)

( Yes there is a potential bork there )


A simpler way is as you suggest: copy the script to your module and rename it. But i suggest finding and using specific scripts that are given and copy those to your module, or someplace higher than default. Find the set that takes the AI routine through, the way you're looking for


It should run every round. In my little test with the hydra it ran four times before i got clobbered.. with the dragons i tested extensively and it just kept going and going ... ironically I'm wondering what actually does keep DCR going. it's not onPerception (that only fires when entering and leaving perception) -- it's probably nw_c2_default3 (onEndCombatRound), although onPhysAttacked/onSpellCastAt/onDamaged may sometimes preempt onEndCombatRound, as long as a new call to DCR is made for the next round.

hey, i wish i could just say here ya go but short of wiping everything and using CSL it don't work that way

#14
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
OnEndCombatRound fires the HenchDetermineCombatRound() method. So that's how it keeps up every round.

Unfortunately, despite all of my attempts, it will not fire my X2_SPECIAL_COMBAT_AI_SCRIPT. I have several SendMessageToPC() statements printing output during the OnPerception And OnCombatRoundEnd scripts that I copied, but it's just not getting to my custom script.

My override folder is empty of any scripts (just walkmesh helpers). The scripts I've copied and renamed for OnPerception and OnEndCombatRound are Campaign scripts, so they should be far enough up the chain (and indeed, they are firing, because I am seeing the SendMessageToPC() statements I added to them, so I know that works).

I know that all of the HenchDetermineCombatRound() methods look for and call the X2_SPECIAL_COMBAT_AI_SCRIPT if it's set on the object.

At this point in time I'm thinking I might just have to delete and repaint the NPC. Maybe that will work. Because even though I have the variable set on it, it's acting like it's not there.

The only time I've been able to get this to work at all was when I accidentally set the hench_o0_ai script as the OnPerception script... That was the only time I saw my custom fight script print out its debugs.

#15
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
All right, progress.

There was a typo that was causing issues. Sigh. I had it as X2_SPECIAL_COMBAT_AI_SCRIPT=, which means in the properties window I finally saw X2_SPECIAL_COMBAT_AI_SCRIPT = = my_combat_script. Fixing that caused my script to fire.

However - it doesn't fire every round. That part has me puzzled. I'm going to look at the logic and see if I'm missing something.

#16
kevL

kevL
  • Members
  • 4 056 messages
k so far so good,
I wrote this before your last post but i'm gonna put it here for anyone following and yer basic refresh:


... 'hench_o0_ai' is called from 'hench_i0_ai' function hDCR ...

I can't see anything in hDCR (SoZ) itself that would stop short, although in 'hench_o0_ai' there are a number of conditions that can stop that script before reaching X2_Special : puppet mode, queued action, isPC, EventClearAllActions, no hostile actor if called by a PC-faction char. blah blah do heal, bash door, flee tactics, don't attack friendlies, follow, no valid target,

then it gets to X2_Special.

----
typo ... :|_~

/java


Copy 'hench_o0_ai' & 'hench_i0_ai' (SoZ) to your module and put messageDebug at the top of hDCR function and at the top of 'hench_o0_ai', compile you know the drill ... *

if good, start putting tactical debug in lower down the line, ( although the current issue might be up higher, somewhere around endCombatRnd, ie somehow not re-firing DCR .. )


*edit. it might be worthwhile copying all the (SoZ) nw_c2_default scripts to your module if you put debug in the #include there ( hopefully, re-compiling these would find the right hDCR )

Modifié par kevL, 16 mars 2013 - 08:47 .


#17
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
I just got closer.

X2_SPECIAL_COMBAT_AI_SCRIPT seems unreliable. I mean, really unreliable. No matter what I do, the NPC begins the first round by casting a stupid Level 0 spell. I have a very specific set of events I want him to do.

Well, turns out, henc_i0_ai has a spot in it where it checks for the presence of a variable on the NPC called "AIScript". So, I set that to my attack script and bingo - he started casting spells in exactly the order I wanted.

The only issue I had with it is that after he casts the first spell, he sits there for a round before he starts casting the rest of the spells. But that's okay - might have something to do with the way I'm targeting or something. I didn't write any sophisticated scripting yet to get him to cycle through targets... So we'll see if that works.

BTW - I took both of those hench_ scripts and copied them to my Campaign directory, put debug lines in and compiled. They are not being run - the game appears to be running the default ones, because I don't see my debug statements (I didn't rename them).

#18
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
This just gets more and more frustrating.. Every time I think I have an answer, something fails to work.

I have to say: this should not be this hard. I've spent all day on this. I cannot remember the last time I spent all day on a problem like this. This is getting ridiculous.

I'm just about the point where I'm going to give up and say screw it, let the dumb AI cast dumb spells in dumb orders. But dammit.. I wanted to make a really cool, challenging mod. This should not be this difficult.

Using AIScript seems like the script only fires one time. It's not fired every combat round. Just once. So I it seems like I need to put my own logic in there to keep looping until the NPC is dead or all the PC's are dead.... Lame.

#19
Morbane

Morbane
  • Members
  • 1 883 messages
maybe try to remove the dumb spells from the NPC's spellbook?

#20
ColorsFade

ColorsFade
  • Members
  • 1 267 messages

Morbane wrote...

maybe try to remove the dumb spells from the NPC's spellbook?


That was my next thought. 

I'm really disappointed that this is so difficult. 

Example: The NPC has False Life as a 2nd level spell. They are standing a ways away from the party when the battle starts, and protected by a few placeables and other creatures so it's not easy for the PC or anyone from his/her party to reach the NPC that fast. The NPC can get False Life off first and that's what I want. But nope... not reliably. 

My script has gotten more and more complicated as I try and make this work. It's sad. It just won't work right. I'm very close to just abandoning the whole idea for right now. I wish the developers would have made this easier. 

There SHOULD have been a simple hook - OnDoAttack would have worked just fine. It should fire once per round, as the character goes to make their attack for that round. Then it should just be a matter of us modders writing a simple script determines which type of attack to make this round. 

That's allI want. But nooooooo. Couldn't make that very simple, could we?

I don't want to have to rewrite the AI from the ground up just to get something really simple to work. That seems dumb. 

#21
kevL

kevL
  • Members
  • 4 056 messages
I just did a test with a clean player folder. It worked right out of the box, round after round. no need to rename or copy scripts here....

#22
Morbane

Morbane
  • Members
  • 1 883 messages
ok - if in doubt (b/c kevL is too brainy) i set up a trigger system to force cast the spell(s) usually it is just one so such a system can get clunky fast - but i thought i'd just throw it out there...

#23
kevL

kevL
  • Members
  • 4 056 messages
i guess i'm saying strip it back to bare bones. Even create a new module ( empty override ) set down a critter that can last a few rounds (faction hostile), set the X2_Special string on it, create and compile a simple debug script by that name, and import a PC that can last a few rounds ...

creature event scripts: all 'nw_c2_default*'

I used an ogre, and right before it took its swing each round, the customAI fired out its message.

#24
ColorsFade

ColorsFade
  • Members
  • 1 267 messages
Good plan kevL. I'll give that a try.

I wonder: I have the NPC as a custom Faction when he's painted down, and then on a script trigger I change his faction to hostile along with a host of skeletons around him. Could that be an issue?

I'll report back on the barebones attempt shortly.

#25
kevL

kevL
  • Members
  • 4 056 messages

ColorsFade wrote...

I wonder: I have the NPC as a custom Faction when he's painted down, and then on a script trigger I change his faction to hostile along with a host of skeletons around him. Could that be an issue?

no, i can't see it.

As soon as the boss goes red and enters DCR/hDCR it should start triggering,