special combat AI script
#1
Posté 27 avril 2012 - 10:53
#include "wr_musket"
void main(){// object oTarget = GetLocalObject(OBJECT_SELF, "X2_NW_I0_GENERIC_INTRUDER");// object oTarget = GetLastPerceived(); object oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); SpeakString("special combat script. target: " + GetName(oTarget));
if(GetIsEnemy(oTarget)){ SpeakString("enemy"); SpeakString("firing at: " + GetName(oTarget));
SpeakString("FIRE!"); } SetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK", 1);}
what gets me is that all of the speakstrings work, but only once. after that first round of combat, the NPC just stands there looking at the PC. i've tried all of the above object assignments that were commented out, but it shouldn't matter because the entire script only runs once.
am i missing something here?
#2
Posté 28 avril 2012 - 02:30
That might be the var that tells the special AI to only run once. Can't remember though if that is it or if there is another var that sets that up.
#3
Posté 28 avril 2012 - 02:39
The Special AI script runs every time DeterminCombatRound Is called. So it is more like a case of the other Events the NPC has attached to him and wether he even enters into combat mode, never being given a target by the Special AI script.
The Special AI does not run every round. It runs every combat Round.
The question is what are you tring to do and what scripts do you have attached to the other events on the NPC.
#4
Posté 28 avril 2012 - 03:09
#5
Posté 28 avril 2012 - 12:00
even if my PC is there wailing on the guy, he still just sits there after the first round.
i guess i'll check some of my other scripts.
#6
Posté 28 avril 2012 - 04:45
i couldn't figure out why the combat script wasn't working each round.
#7
Posté 28 avril 2012 - 05:50
acomputerdood wrote...
i cheated and just used the special combat script to set the target and then had a heartbeat script look for the target and attack each round.
i couldn't figure out why the combat script wasn't working each round.
Well that is why I was asking what you where trying to do. If you are just trying to controll the target To where the nearest hostile is always being attacked, try something like this.
#include "wr_musket"
#include "nw_i0_generic"
void main()
{
object oTarget = GetLocalObject(OBJECT_SELF, "X2_NW_I0_GENERIC_INTRUDER");
object oNewTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
SpeakString("special combat script. target: " + GetName(oTarget) + "NewTarget = " + GetName(oNewTarget) );
if(GetIsEnemy(oTarget))
{
SpeakString("enemy");
SpeakString("firing at: " + GetName(oTarget));
SpeakString("FIRE! // Only if closest target");
}
if( GetIsValid(oNewTarget) && oTarget != oNewTarget)
{
SpeakString("changing Targets");
DetermineCombatRound(oNewTarget);
SetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK", 1);
}
}
It may take a little bit to understand what is going on there. If you have trouble understanding it after giving it a little though, Post back and I will try and explain it in more depth.
#8
Posté 28 avril 2012 - 11:23
from reading other posts, i'm guessing that the problem is that somewhere the NPC's action queue was being canceled, adjusted, or cleared, which would remove him from the combat state. this would cause the script to not run again.
and since the onperception thing (that calls determinecombatround) only runs the first time he sees somebody, he would never re-enter combat until the PC hid and reappeared.
i think i'm correct about all that, yes?
#9
Posté 29 avril 2012 - 12:17
If memory serves me right, DetermineCombatRound is a recursive function, meaning that It is the main driving force behind calling itself back up. With the special AI script that you originaly wrote, It spoke a couple of strings and then stopped the rest of the function from running. This gave a state where nothing happened to ever cause or trigger the Function to be called again. With nothing happening combat basicly stopped for the NPC just as fast as it started. He did nothing, Simply because he was never told to do anything.
To fully understand the script that I posted above, You need to understand that the Special AI Script is called from within the DetermineCombatRound Function. This is important because when the script calls DetermineCombatRound itself, it is bascly calling itself to be ran again( A recursive script you could say). With the script calling itself again however you have to be carfull not to create an endless loop that never terminates.
With that in mind lets look at what the script does. Here it is again with the excess trimmed out.
void main()
{
object oTarget = GetLocalObject(OBJECT_SELF, "X2_NW_I0_GENERIC_INTRUDER");
object oNewTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
if( GetIsValid(oNewTarget) && oTarget != oNewTarget)
{
DetermineCombatRound(oNewTarget);
SetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK", 1);
}
}
1) We simply have one compairson To check if the Target we are attacking is the Target we want to be attacking, with an extra precaution check that target we want to attack is valid.
2) if the Current target is the that we have decided is the target we want to be attacking, the script does nothing and simply returns control to DetermineCombatRound to attack the currently correct target.
3) If the Target is not the Target we want it to attack. It changes the target by calling DetermineCombatRound with the correct target as a argument. This will in effect call this script again also. On the second calling of this script however the old and new target will match allowing the second run of the script to simply fall through and having DetermineCombatRound take over with the correct target.
4) If the targets did not match the first time the script was ran, after second call to DetermineCombatRound finishes ( The Call where we changed the target, made from the Special AI script) script excution returns to the line code following our call to DetermineCombatRound, At this point we set the X2_SPECIAL_COMBAT_AI_SCRIPT_OK flag to stop the DetermineCombatRound function from running again, since we have just ran it and already let it set up everything needed for that combat round.
Since we have let the DetermineCombatRound function decide how to handle the combat round, with the correct target, We can rest assured that combat will continue next round unless combat is over.
I hope I did a fair job of explaining that. Understanding recursive function is often one of the harder concepts to comprehend in code.
Modifié par Lightfoot8, 29 avril 2012 - 12:19 .
#10
Posté 29 avril 2012 - 12:47
To test it out create a placeable container ( a chest) and place this script in its OnOpen event.
void CountToTenAndBack(int nCount=0)
{
object oPC = GetLastOpenedBy();
nCount++;
SendMessageToPC(oPC,IntToString( nCount));
if(nCount < 10) CountToTenAndBack(nCount);
SendMessageToPC(oPC,IntToString( nCount));
}
void main()
{
CountToTenAndBack();
}
Modifié par Lightfoot8, 29 avril 2012 - 12:48 .
#11
Posté 01 mai 2012 - 09:05
anyway, i went with that, but now the NPC is running BOTH its special combat script and the heartbeat script. what's worse is that before it would only run the special script once, but now it runs it each and every attack my PC makes on him. this makes the NPC "fire" his musket 5+ times per round at the PC. he's only supposed to do it once. at this point, i'm going to attempt to paste in all the different code bits (i'll try to clean it up some, but i'd hate to cut out what i think is irrelevant because i could be wrong).
special combat script:
void main(){
object oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
if(LineOfSightObject(OBJECT_SELF, oTarget)){
// FIRE MUSKET
object oMusket = FindMusket(OBJECT_SELF);
if(GetIsObjectValid(oMusket)){
SendMessageToPC(oTarget, "combat");
FireMultipleShotMusket(oMusket, OBJECT_SELF, oTarget, GetLocation(oTarget));
SetLocalObject(OBJECT_SELF, "MUSKET_TARGET", oTarget);
}else{
SpeakString("invalid musket");
}
}else{
DeleteLocalObject(OBJECT_SELF, "MUSKET_TARGET");
}
SetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK", 1);
}
so, it should be getting the closest enemy, checking to see if there is a clear line of sight, checking himself for a musket, and then firing it. then i saved the target to himself because i was using that in my heartbeat script. if this combat script worked correctly, then i wouldn't need to do that.
and now for the parts of the other (BIG) script. bear with me here:
this one is simple enough.object FindMusket(object oShooter){
object oMusket;
int i=0;
oMusket = GetItemPossessedBy(oShooter, "Musket");
if(GetIsObjectValid(oMusket)){
return oMusket;
}
while(i < NUM_MUSKETS){
oMusket = GetItemPossessedBy(oShooter, "MUSKET_" + IntToString(i));
if(GetIsObjectValid(oMusket)){
return oMusket;
}
i++;
}
return OBJECT_INVALID;}
void FireMultipleShotMusket(object oMusket, object oMe, object oTarget, location lTarget){
string sMusketTag;
//make sure it's a recognized musket
sMusketTag = GetTag(oMusket);
if(!GetIsMusket(sMusketTag)){
return;
}
//check to see if we have updated constants for a special musket
// just sets some global variables
UpdateMusketConstants(sMusketTag, oMe);
//debugging - comment out
// DisplayConstants(oMe, oTarget);
//shots per round
float fDelay = 6.0/MUSKET_RATE_OF_FIRE;
//stagger shots a little bit if a bunch of people shoot at once
float f = Random(2)/5.0;
//FIXME: if shooter dies he still finishes his rounds of shots
while(f < 6.0){
DelayCommand(f, FireMusket(oMusket, oMe, oTarget, lTarget));
f += fDelay;
}
}
so i might have messed up some of the brackets there, but i think it's correct. my best guess is that the "DelayCommand(AssignCommand())" lines are breaking the combat state? but then i commented out the "FireMultipleShotMusket" call in the special combat script and he still stopped combatting. but then you were saying that he had to know to continue being in combat, so maybe that was a different problem?void FireMusket(object oMusket, object oCreature, object oTarget, location lTarget){
int iHit = 0;
int iDamage = 0;
location lMe;
vector vMe;
int iDamagePower;
//puff of smoke and spark
vMe = GetPosition(oCreature);
vMe.z += 1.0;
lMe = Location(GetArea(oCreature), vMe, 0.0);
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_COM_SPARKS_PARRY), lMe);
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SMOKE_PUFF), lMe);
ApplyEffectToObject (DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_S), oCreature);
iHit = RollRangedAttack (oCreature, oTarget, MUSKET_CRITICAL_RANGE, MUSKET_ATTACK_BONUS, MUSKET_RANGE_INCREMENT_FEET);
if (iHit > 0){
iDamage = RollDamage (oCreature, oTarget, MUSKET_DAMAGE_NUM_DICE, MUSKET_DAMAGE_DIE_TYPE, MUSKET_DAMAGE_BONUS)
effect eDamage = EffectDamage (iDamage, DAMAGE_TYPE_PIERCING);
if (GetObjectType (oTarget) == OBJECT_TYPE_CREATURE){
effect eBlood = EffectVisualEffect (VFX_COM_BLOOD_LRG_RED);
eBlood = EffectLinkEffects (EffectVisualEffect (VFX_COM_BLOOD_LRG_RED), eBlood);
eBlood = EffectLinkEffects (EffectVisualEffect (VFX_COM_BLOOD_LRG_RED), eBlood);
ApplyEffectAtLocation (DURATION_TYPE_INSTANT, eBlood, GetLocation (oTarget));
DelayCommand(0.01, AssignCommand(oCreature, ApplyDamage (oCreature, oTarget, iDamage, iDamagePower, DAMAGE_TYPE_PIERCING, 0.1)));
}else{
DelayCommand(0.01, AssignCommand(oCreature, ApplyDamage (oCreature, oTarget, iDamage, iDamagePower, DAMAGE_TYPE_PIERCING, 0.1, VFX_COM_BLOOD_SPARK_LARGE)));
}
}else{
}
}
}
so, in advance, thanks to all of you who like reading through source code
my last option is to just stop the NPC from firing in his combat script and only use that to set a target and signal the heartbeat script to start firing. that *should* still work, but it's not really precise (the delay seems to be anywhere from 4-8s), and it's infuriating because this *should* work.
and by all means don't shy away from speaking of recursion. i'm well versed in coding concepts. i just struggle with the toolset and its scripting language
WAIT! so would the special script run once per attack the NPC gets? for some reason i always thought that the DetermineCombatRound would only run once per combat round, and it would handle all the attacks of the NPC. it does kind of make sense now that it runs once per attack he's supposed to get. is that it? and if so, how can i get him to only shoot once per round?
Modifié par acomputerdood, 01 mai 2012 - 09:12 .
#12
Posté 05 mai 2012 - 03:44
On a side note you might want to add some impact damage too, with possible stagger or knockdown effects.
Modifié par ffbj, 05 mai 2012 - 03:47 .
#13
Posté 05 mai 2012 - 07:11
Action functions
Ever found those NPCs just don’t do what they have been scripted to do? This is often because of inappropriate use of ActionX commands versus straight X commands, and the ClearAllActions() function. ActionX eg ActionSpeakString() puts the X command at the back of the action queue, while an X command eg SpeakString() executes immediately. ClearAllActions() clears the queue. One problem is that the include files that code for special called functions often include ClearAllActions() functions, so a for example a DetermineCombatRound() will interfere with previous actions. Also basic NPC AI may activate various scripts that "rub out" your special commands, particularly if you have queued more than one. Finally, in a conversation, the quit conversation scripts may cancel out what you wanted the PC to do and they might WalkWaypoints() instead. These quit conversation scripts can be removed within the conversation editor (the tag to the right of "Other actions").
#14
Posté 06 mai 2012 - 08:56
After that is done your ai script can look more like this.
#include "x2_inc_switches"
#include "nw_i0_generic"
#include "Your Functions"
void main()
{
// first check for the musket and combat condition.
// Bail out early for performance reasons if there is no musket
// or Combat is already locked.
object oMusket = FindMusket(OBJECT_SELF);
if(!GetIsObjectValid(oMusket) || __InCombatRound()) return;
object oTarget = GetNearestCreature
(
CREATURE_TYPE_REPUTATION, //nFirstCriteriaType
REPUTATION_TYPE_ENEMY, //nFirstCriteriaValue
OBJECT_SELF, //oTarget
1, // nNth
CREATURE_TYPE_PERCEPTION, //nSecondCriteriaType
PERCEPTION_SEEN //nSecondCriteriaValue
);
if(GetDistanceToObject(oTarget)> 10.0 && LineOfSightObject(OBJECT_SELF, oTarget))
{
// FIRE MUSKET
SendMessageToPC(oTarget, "combat");
__TurnCombatRoundOn(TRUE);
FireMultipleShotMusket(oMusket, oTarget));
__TurnCombatRoundOn(FALSE);
ActionDoCommand(DetermineCombatRound());
SetCreatureOverrideAIScriptFinished();
}
}
There are also other examples of Special AI combat scripts in the toolset.
x2_ai_*
Modifié par Lightfoot8, 06 mai 2012 - 08:57 .





Retour en haut






