Animal Companion AI Scripts
#1
Posté 27 octobre 2013 - 01:24
Specifically, I'm editing ac5, ac6, and ac8 (Attacked, Damaged, and Disturbed) and compiling them/recompiling them/rebuilding the whole doesn't seem to make any changes. Originally I couldn't even get a debugging string to go off at the start of each script.
Then (and I still have no idea HOW I managed to do this) the string magically started going off. But when I then changed the string to be specific to the individual script I'm back in the same problem of it not having any effect (it's "stuck" on the original identical debugging strings).
Aka, originally, nothing would go off.
Now it will finally say (somehow): "Firing OnAttacked."
But it won't update to "Firing OnAttacked"/"Firing OnDamaged"/"Firing OnDisturbed" now.
Am I missing something obvious here?
#2
Posté 27 octobre 2013 - 04:59
It could be that you are having trouble with a character file identifying scripts by means of the ID the compiler uses (not sure if this is true, but it is worth checking).
EDIT: Looking at a few character files, it does not look like there is a field for the familiar/animal companions script. The only data I see are for a name and the type of creature.
Modifié par WhiZard, 27 octobre 2013 - 05:18 .
#3
Posté 27 octobre 2013 - 05:58
Why would OnAttacked fire for OnDamaged for Animal Companions?
The reason I'm fiddling with these at all is to improve the controllability of the companions with a script I'm using. Trying to make it so that if I tell the companion to attack a certain creature that its AI doesn't make it change its mind -- setting a local int for a short time period when it is ordered to attack and then trying to make the AI script simply return if the local int is detected.
Basically a way of trying to say "I don't care what you think you want to do, just do what I just told you to do."
#4
Posté 27 octobre 2013 - 06:34
#5
Posté 27 octobre 2013 - 06:43
#6
Posté 27 octobre 2013 - 08:17
#7
Posté 28 octobre 2013 - 06:11
I decided to use another method -- basically detects whether the master has commanded the companion within the last nine seconds (round and a half) and if so the relevant AI scripts simply immediately return.
Currently have this applied to...
OnAttacked
OnHeartbeat
OnPerceived
OnSpellCastAt
OnCombatEnd
OnDialogue
I also set it up for OnDamaged but obviously this is irrelevant for the Animal Companion specifically since it uses the OnAttacked script twice. I may be avoid to avoid returning on some of those scripts but I played it safe as it doesn't seem to hurt anything and all were firing in combat and appear to have the potential to change the behavior of the creature.
This method appears to have solved my issues -- effectively shuts the AI combat decision making down for nine seconds each time the player gives a command and allows a LOT of custom control over the pet.
This can obviously be applied to any henchmen/animal companion/familiar that isn't reliant on spells or other special abilities quite easily. Probably would be possible to script something and tell casters to cast a spell instead of attack. But thought people might find it interesting.
Modifié par MagicalMaster, 28 octobre 2013 - 06:12 .
#8
Posté 28 octobre 2013 - 08:14
AI Behavior Priority
Modifié par Lightfoot8, 28 octobre 2013 - 08:16 .
#9
Posté 28 octobre 2013 - 08:23
Which means my question would be, what's more efficient? Is calling the special AI script less work for the computer than calling the normal scripts but simply stopping?
#10
Posté 28 octobre 2013 - 08:52
special AI is more efficient if you are ok with short answer, explaining why could take hours...MagicalMaster wrote...
Well, effectively seems I took the four lines of code (including two lines of curly braces) that would go into the special combat AI script and placed them in the other scripts themselves. So instead of the special AI script firing and preventing the other AI scripts from firing it fires the individual scripts then stops.
Which means my question would be, what's more efficient? Is calling the special AI script less work for the computer than calling the normal scripts but simply stopping?
#11
Posté 28 octobre 2013 - 09:27
Assuming there are no other issues, is there any downside to using that special AI?
#12
Posté 29 octobre 2013 - 12:15
Here's the code I used for the new script:
void main()
{
// Don't do anything if we have have been recently commanded
if (GetLocalInt(OBJECT_SELF, "commandstatus"))
{
SetLocalInt(OBJECT_SELF,"X2_SPECIAL_COMBAT_AI_SCRIPT_OK",TRUE);
}
}
Modifié par MagicalMaster, 29 octobre 2013 - 12:15 .
#13
Posté 29 octobre 2013 - 01:59
it basicly stops the core AI when X2_SPECIAL_COMBAT_AI_SCRIPT_OK is set. So within the script you need to tell the creature what to do if the flag is set.
void main()
{
// Don't do anything if we have have been recently commanded
if (GetLocalInt(OBJECT_SELF, "commandstatus"))
{
ActoinAttack( ??);
SetLocalInt(OBJECT_SELF,"X2_SPECIAL_COMBAT_AI_SCRIPT_OK",TRUE);
}
}
Allong with whatever checks you want to use to make sure the Target is still valid.
I Have many times just called DetermineCombatRound again with a direct attack target. In doing that though you will need to place controls so that the functiont does not just keep calling itself and creating an endless loop.
#14
Posté 29 octobre 2013 - 04:11
But it seemed to suppress the AI even when commandstatus was 0 and thus the normal AI should have run.
#15
Posté 29 octobre 2013 - 04:53
The script above is not on any EVENT.
it is connected to the creature by setting a local on the creature. i.e.
SetLocalString(OBJECT_SELF,"X2_SPECIAL_COMBAT_AI_SCRIPT", "Name of AI script" );
Is that how you have it set up?
AND for the record the function DetermineCombatRound runs at least once every combat round. Many time it runs more then once.
Modifié par Lightfoot8, 29 octobre 2013 - 04:55 .
#16
Posté 29 octobre 2013 - 08:14
SetLocalString(oAnimal,"X2_SPECIAL_COMBAT_AI_SCRIPT", "sh_commandcheck" );
(oAnimal has been verified to be the correct target)
and then "sh_commandcheck" is a new script that is
void main()
{
// Don't do anything if we have have been recently commanded
if (GetLocalInt(OBJECT_SELF, "commandstatus"))
{
SetLocalInt(OBJECT_SELF,"X2_SPECIAL_COMBAT_AI_SCRIPT_OK",TRUE);
}
}With the idea that as long as commandstatus is considered TRUE that the rest of the AI would be suppressed.
Modifié par MagicalMaster, 29 octobre 2013 - 08:15 .
#17
Posté 29 octobre 2013 - 08:28
#18
Posté 29 octobre 2013 - 10:22
WhiZard wrote...
Waiting for the special AI to trigger means more exposure to scripted checks and changing tasks. On the other hand, DetermineCombatRound() does do a good job at clearing the action queue, however, this is not always enforced if it is cut off with special AI scripts.
Yeah, my code literally looks something like this...
// On Attacked/Perceived/Whatever
int main()
{
if (Busy)
{
return;
}
<Actual AI stuff for the script goes here>
}Also, whenever my command script gives a new order it clears all actions first to prevent any kind of action queue pile-up. And then the AI proceeds normally after a set time passes (9 seconds by default) without the PC giving an order.
#19
Posté 30 octobre 2013 - 12:40
#include "x2_inc_switches"
#include "nw_i0_generic"
void main()
{
if ( GetCreatureOverrideAIScriptTarget()== OBJECT_INVALID)
{
string sScript =GetLocalString(OBJECT_SELF,CREATURE_VAR_CUSTOM_AISCRIPT);
SetCreatureOverrideAIScript(OBJECT_SELF,"");
DetermineCombatRound(GetAttackTarget());
SetCreatureOverrideAIScript(OBJECT_SELF,sScript);
SetCreatureOverrideAIScriptFinished();
}
}
To answer the question on efficiency, I believe you method is more efficient. I myself however like everything going through a single combat function. But it does not sound like you are blocking the combat AI anyway. You are just blocking the other events from firing.
#20
Posté 30 octobre 2013 - 01:10
Also, any ideas on what might seem to randomly cause this or good ways to debug it? This never happens without an animal companion but I *sometimes* see it with one -- it doesn't even happen every fight.

Trying to check exactly when AC5 (OnAttacked/OnDamaged) is firing to see if it gets triggered a bunch in a split second or something but figured I could ask here as well.
Modifié par MagicalMaster, 30 octobre 2013 - 01:13 .
#21
Posté 30 octobre 2013 - 01:51
Here's the code running for the acid cloud ("sh_blank" is simply blank and that's what the persistent AoE scripts are designated so there's no persistent AoE scripts running):
void Spit2(location lTarget, int nDur, int nDam)
{
int nAdjust;
object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
while (GetIsObjectValid(oTarget))
{
if (GetIsEnemy(oTarget))
{
nAdjust = AdjustDamage(nDam, oTarget);
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nAdjust), oTarget);
}
oTarget = GetNextObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
}
if (!GetIsDead(OBJECT_SELF) && nDur > 0)
{
nDam = (3*nDam)/2;
if (nDam > 1000)
{
nDam = 1000;
}
DelayCommand(1.0, Spit2(lTarget, --nDur, nDam));
}
}
Seems pretty basic -- loop through a 4.0 meter circle, apply damage if it's an enemy, adjust the damage for the next tick, then call itself again as long there is duration remaining.
Modifié par MagicalMaster, 30 octobre 2013 - 01:52 .
#22
Posté 30 octobre 2013 - 04:40
Ths is the acid cloud code.
void Spit2(location lTarget, int nDur, int nDam)
{
int nAdjust;
object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
while (GetIsObjectValid(oTarget))
{
if (GetIsEnemy(oTarget))
{
nAdjust = AdjustDamage(nDam, oTarget);
AssignCommand(oTarget, SpeakString("Took damage from cloud"));
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nAdjust), oTarget);
}
oTarget = GetNextObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
}
if (!GetIsDead(OBJECT_SELF) && nDur > 0)
{
nDam = (3*nDam)/2;
if (nDam > 1000)
{
nDam = 1000;
}
DelayCommand(1.0, Spit2(lTarget, --nDur, nDam));
}
}This is the code at the top of AC5:
void main()
{
// Don't do anything if we have have been recently commanded
if (GetLocalInt(OBJECT_SELF, "commandstatus"))
{
return;
}
AdjustLocalInt(OBJECT_SELF, "test", 1);
SpeakString("OnAttacked: " + IntToString(GetLocalInt(OBJECT_SELF, "test")));
So, in essence, every time the creature gets attacked or damage it says "On Attacked: x" where "x" is the number of times this event has occcurred.
It took me several tries to reproduce this bug and I'm still not sure what's triggering it -- doesn't happen every time.


You can see how the cloud damaged the bear 14 times *instantly* before the AssignCommand could even make the bear say it took damage form the cloud (then it overloaded).
But how in the world does that loop hit ANYTHING 14+ times instantly?
Modifié par MagicalMaster, 30 octobre 2013 - 04:42 .
#23
Posté 30 octobre 2013 - 02:34
#24
Posté 30 octobre 2013 - 03:40
int main (void)
{
return;
}The only thing actually doing damage is the script I posted which does an AoE check each second -- so even if the creature enters and leaves the area the area is only checked every second. I'll try putting a debug string in blank when I can to see if anything is weird but the only script DOING anything is the one posted.
Modifié par MagicalMaster, 30 octobre 2013 - 03:41 .
#25
Posté 30 octobre 2013 - 05:37
Half thinking I should just set a local int on the first loop and then remove it with a second loop to see if that helps.
But I don't see why I should *need* to do this.





Retour en haut






