Please help me script this interface trick
#1
Posté 21 septembre 2010 - 09:51
1. Start a melee attack on that foe from a distance, which starts me moving toward the foe.
2. Use a hotkey to order all associates to Guard Me, which starts them moving toward the foe also.
3. Click behind myself so I'm now running away from the foe.
Result: associates charge in to engage the foe for me while I use spells, engage another foe, or just plain run away.
Now, running a VPnP game, I want to create an item that will do the same thing - send associates to attack - when the item is activated and a foe is clicked on.
Unfortunately, I'm not quite sure what happens when the Guard Me command is issued through the interface. I've looked in the Lexicon, and the best guess I've come up with is to use DetermineCombatRound(). However, using this line in a script sometimes works, often doesn't, and I can't quite figure out why:
AssignCommand(oAssociate, DetermineCombatRound(oClicked));
I'm stumped. Ideally, I'd like the function to be able to let the standard AI decide how to attack, but when I start looking at functions like TalentMeleeAttack and TalentSpellAttack, I feel like I'm trying to rewrite DetermineCombatRound(), and I doubt that's the right thing to do.
Any ideas how I can do this?
#2
Posté 21 septembre 2010 - 10:07
void bchSicEm(object oWielder, object oClicked, int bHenchmen = FALSE, int bSummoned = FALSE, int bFamiliar = FALSE, int bAnimalCompanion = FALSE, int bDominated = FALSE){
object oAssociate;
string sMessage = GetName(oWielder) + " orders associates to attack " + GetName(oClicked);
FloatingTextStringOnCreature(sMessage, oWielder);
if (bFamiliar){
oAssociate = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oWielder);
AssignCommand(oAssociate, DetermineCombatRound(oClicked));
}
if (bAnimalCompanion){
oAssociate = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oWielder);
AssignCommand(oAssociate, DetermineCombatRound(oClicked));
}
if (bSummoned){
oAssociate = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oWielder);
AssignCommand(oAssociate, DetermineCombatRound(oClicked));
}
if (bDominated){
oAssociate = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oWielder);
AssignCommand(oAssociate, DetermineCombatRound(oClicked));
}
if (bHenchmen){
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oWielder, 1);
if (GetIsObjectValid(oAssociate)) {
AssignCommand(oAssociate, DetermineCombatRound(oClicked));
// if we have one hench, check for more
int i;
for (i = 2; i < 7; i++){
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oWielder, i);
AssignCommand(oAssociate, DetermineCombatRound(oClicked));
}
}
}
}
Modifié par BCH, 21 septembre 2010 - 10:07 .
#3
Posté 21 septembre 2010 - 11:39
BCH wrote...
Unfortunately, I'm not quite sure what happens when the Guard Me command is issued through the interface.
When you give any of the voice commands, the only thing the PC does is shout the command.
From there it is up to the NPC/compaion to react. That is handled by the copmaions OnConversation event and the listening patterns it is set up to listen for.
Sorry I dont have the time to give a better answer right now.
Hope that helps.
#4
Posté 21 septembre 2010 - 03:55
Set all the henchmen to guard me when you're walking around.
In your script instead of: AssignCommand(oAssociate, DetermineCombatRound(oClicked));
Put:
AssignCommand(oAssociate,ClearAllActions());
DelayCommand(1.0, AssignCommand(oAssociate,ActionAttack( oClicked )));
This tells the associates to attack the oClicked. If they are already attacking then they will continue to do so. If you want them to leave the current attack then replace the ClearAllActions line with:
AssignCommand(oAssociate,ClearAllActions(TRUE));
#5
Posté 21 septembre 2010 - 04:04
#include "x0_i0_assoc"
SetAssociateState( NW_ASC_MODE_DEFEND_MASTER, TRUE );
So that would be:
AssignCommand( oAssociate, SetAssociateState( NW_ASC_MODE_DEFEND_MASTER, TRUE ));
Modifié par Mudeye, 21 septembre 2010 - 04:05 .
#6
Posté 21 septembre 2010 - 07:18
#7
Posté 23 septembre 2010 - 01:18
Lightfoot8 - I think I can figure out how to script a "Guard Me" shout, but that will not get me the whole effect I'm looking for. With the Attack > Guard Me > Retreat interface trick I described, the end result is the same as saying "associates, attack that exact enemy." That's what I can't figure out how to do.
Mudeye - Since I last posted, I've tried using ActionAttack() and WrapperActionAttack(), but those only seem to work for attacks with equipped weapons. This is good, but it does not seem to include spells or spell-like abilities. One of my test associates is a summoned fire beetle with Fire Bolt and Fire Stream, and it does not use these when commanded with ActionAttack or WrapperActionAttack. However, it does use it normal combat, where it is picking it's own targets, so I know the AI is choosing those attacks somehow.
I've tried using variations on DetermineCombatRound() and ChooseTactics(), but they only work sporadically, for reasons I have not yet been able to puzzle out. Anyone got any idea why those would not work in my script above?
My next step is adding script to decide whether the associate should use TalentSpellAttack() or something similar, but since I can't find extensive documentation on all these TalentBlahBlah combat functions, I fear I may end up on a long wild goose chase.
Again, thanks for any help anyone can offer!
#8
Posté 23 septembre 2010 - 05:00
I believe this tells your associate to use ranged tactics:
SetCombatCondition( X0_COMBAT_FLAG_RANGED, TRUE, oAssociate );
This determines the tactics as set above to used on the target oClicked:
SpecialTactics( oClicked );
The combat conditions are:
X0_COMBAT_FLAG_AMBUSHER
X0_COMBAT_FLAG_COWARDLY
X0_COMBAT_FLAG_DEFENSIVE
X0_COMBAT_FLAG_RANGED
For simple Melee they are all set to false.
Modifié par Mudeye, 23 septembre 2010 - 05:01 .
#9
Posté 23 septembre 2010 - 10:11
(Sorry, not trying to be difficult! :innocent: )
Since the last time I posted, I've seen very limited success using TalentSpellAttack(). Using that in the script, associates will attack with spells sometimes. It appears that if they are out of range for the spell or spell-like ability that the function chooses, they do nothing.
I'm still trying to find the AI that tells associates "if you are out of range of that target, move into range and cast." If there is such a thing.
I'm also still trying to puzzle out why DetermineCombatRound() and ChooseTactics() aren't working for me.
#10
Posté 24 septembre 2010 - 08:13
After more testing, it appears that TalentSpellAttack() does not cause my associates to do anything... until I walk them within range of whatever spell was just chosen by the AI for them to cast.
I've consistently seen the following:
- I order my two associates to TalentSpellAttack() while 30-40 meters from the target.
- They do nothing. (Actually, the sorcerer puts away his crossbow and draws his sickle, then does nothing.)
- Then I wander closer to the target.
- At a distance of about 20 meters, both associates start casting spells. The sorcerer starts with Protection from Evil, then Sleep, then Fireball. The fire beetle goes straight to alternating Fire Bolt and Fire Stream.
Then I added a few lines to the script, hoping to send the associates in on their own, like so:
if(GetDistanceBetween(oAssociate, oClicked) > 19.0 ){
AssignCommand(oAssociate, ActionMoveToObject(oClicked, FALSE, 19.0) );
}
As a result of this new code... no change. The associates do not seem to respond to the ActionMoveToObject function at all.
I've tried adding SetIsTemporaryEnemy() to the script to make the associates hate the target, and to make the target hate my associates. The target responds by attacking! My associates still stand there like idiots.
Any ideas? It's almost like the associates are holding the TalentSpellAttack in their action queues, but aren't willing to leave my side.
#11
Posté 27 septembre 2010 - 04:46
You might try looking at the perception range of your associates. They might not be perceiving the enemy yet.
Does the location you tell them to move to conflict with the the distance they are supposed to stay from the PC?
You might want to put a clear actions before the move. If they have other stuff in their action queue it could prevent them from taking the action.
#12
Posté 28 septembre 2010 - 12:34
http://nwvault.ign.c....Detail&id=1600
#13
Posté 28 septembre 2010 - 12:39
#14
Posté 28 septembre 2010 - 12:42
if(the caster has XXXspell)
{
cast it
}
if(the caster has XXXspell)
{
cast it
}
So on and so forth with buffs being the first, unless of course you are going to check the closeness of enemies and want to cast spells to freeze them in place first, then buff, then blast.
#15
Posté 29 septembre 2010 - 05:14
Mudeye wrote...
Here's a couple of thoughts.
You might try looking at the perception range of your associates. They might not be perceiving the enemy yet.
Does the location you tell them to move to conflict with the the distance they are supposed to stay from the PC?
You might want to put a clear actions before the move. If they have other stuff in their action queue it could prevent them from taking the action.
I'm pretty sure the associates can see their targets... I haven't changed their perception ranges, and they do have good ranks in Spot and Listen. In some cases, I've used enemies that started off Neutral rather than Hostile, so it was possible to walk my associates right past the target before ordering the attack.
Technically, yes, they would have to leave my side to attack the target, so that does conflict with their set following distances. However, when they decide to attack on their own, they have no problems chasing after enemies. They just don't respond to the ActionMoveToObject() function when I add it to the manual attack script.
I did try ClearAllActions() and ClearAllActions(TRUE) in the script. Sorry, I should have specified that. Still no success on my part.
Baragg wrote...
I remember reading something about this on the old forums, I believe,
but could be wrong, the idea grew up to be a series of if statements
where it read out like: [snip]
Yeah, that's basically how ChooseTactics() reads. Unfortunately, I can't find the line or lines in ChooseTactics() that is keeping the associates from doing what I want them to do.
Thanks, though. I appreciate the help.
#16
Posté 29 septembre 2010 - 10:21
#17
Posté 30 septembre 2010 - 03:06
All the problems seem to occur in the "attack with magic" parts. Associates do nothing if they are out of range of the spell or ability chosen by the script, until they are walked within range of the enemy, at which point they start casting.
#include "NW_I0_GENERIC"
// runs a function that returns an integer and discards the return value
void bchFunctionIgnoreInt(int FunctionThatReturnsInt){ }
void bchGetEm(object oAssociate, object oClicked, object oWielder, int bCast = FALSE){
//--------------------------------------------------------------------------
// This (setting associates to Stand Your Ground = No, Guard Me = No)
// seems to be necessary. Without this, TalentSpellAttack and ActionAttack
// don't seem to work except randomly, occasionally.
// Not sure if other settings would work as well.
// Maybe they just need *something* set?
SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE);
SetAssociateState(NW_ASC_MODE_DEFEND_MASTER, FALSE);
//--------------------------------------------------------------------------
if(bCast){ // this associate should attack with magic
// make the associate hostile to the target (so they are more willing to attack?)
//SetIsTemporaryEnemy(oClicked, oAssociate);
// make the target hostile to the associate,
// so their spells will affect them,
// even if they start out neutral or friendly
SetIsTemporaryEnemy(oAssociate, oClicked);
if(GetDistanceBetween(oAssociate, oClicked) > 19.0 ){
AssignCommand(oAssociate, ClearAllActions() );
AssignCommand(oAssociate, ActionMoveToObject(oClicked, FALSE, 19.0) );
}
// hopefully, this will work for all spell-like abilities as well as spells
// currently, it only seems to work when an enemy is within range of their spells
AssignCommand(oAssociate, bchFunctionIgnoreInt(TalentSpellAttack(oClicked) ) );
}else{
// this works for equipped weapons (ranged or melee)
AssignCommand(oAssociate, WrapperActionAttack(oClicked) );
}
}
// Commands some or all associates to attack oClicked
// First five switches control which associates receive the command.
// bCast == TRUE causes a spell or magic ability attack,
// bCast == FALSE causes a physical attack.
void bchSicEm(object oWielder, object oClicked, int bHenchmen = FALSE,
int bSummoned = FALSE, int bFamiliar = FALSE,
int bAnimalCompanion = FALSE, int bDominated = FALSE, int bCast = FALSE){
object oAssociate;
string sMessage = GetName(oWielder) + " orders associates to attack " + GetName(oClicked);
FloatingTextStringOnCreature(sMessage, oWielder);
SendMessageToAllDMs(sMessage);
if (bFamiliar){
oAssociate = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oWielder);
if(GetIsObjectValid(oAssociate)){
bchGetEm(oAssociate, oClicked, oWielder, bCast);
}
}
if (bAnimalCompanion){
oAssociate = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oWielder);
if(GetIsObjectValid(oAssociate)){
bchGetEm(oAssociate, oClicked, oWielder, bCast);
}
}
if (bSummoned){
oAssociate = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oWielder);
if(GetIsObjectValid(oAssociate)){
bchGetEm(oAssociate, oClicked, oWielder, bCast);
}
}
if (bDominated){
oAssociate = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oWielder);
if(GetIsObjectValid(oAssociate)){
bchGetEm(oAssociate, oClicked, oWielder, bCast);
}
}
if (bHenchmen){
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oWielder, 1);
if (GetIsObjectValid(oAssociate)) {
bchGetEm(oAssociate, oClicked, oWielder, bCast);
// if we have one hench, check for more
int i;
for (i = 2; i < 7; i++){
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oWielder, i);
if(GetIsObjectValid(oAssociate)){
bchGetEm(oAssociate, oClicked, oWielder, bCast);
}
}
}
}
}
Modifié par BCH, 30 septembre 2010 - 03:22 .
#18
Posté 30 septembre 2010 - 04:16
if(GetDistanceBetween(oAssociate, oClicked) > 19.0 ){
AssignCommand(oAssociate, ClearAllActions() );
AssignCommand(oAssociate, ActionMoveToObject(oClicked, FALSE, 19.0) );
}
// hopefully, this will work for all spell-like abilities as well as spells
// currently, it only seems to work when an enemy is within range of their spells
AssignCommand(oAssociate, bchFunctionIgnoreInt(TalentSpellAttack(oClicked) ) );
This line looks questionable to me:
AssignCommand(oAssociate, bchFunctionIgnoreInt(TalentSpellAttack(oClicked) ) );
I'm not sure that it captures the TalentSpellAttack(oClicked) call. It might be trying to call TalentSpellAttack immediately and pass the result into bchFunctionIgnoreInt which is then called later. That would also explain why it only works if you are already within spell range. If you are too far away, then the immediate call to TalentSpellAttack doesn't do anything since it actually happens before the move command.
How about trying these two things.
----------------------------
1) make a new function:
void MyCast( object target )
{
SpeakString("Casting Spell");
ActionCastSpellAtObject(SPELL_MAGIC_MISSILE, target, METAMAGIC_ANY, TRUE );
}
Replace the line:
AssignCommand(oAssociate, bchFunctionIgnoreInt(TalentSpellAttack(oClicked) ) );
with
AssignCommand(oAssociate, MyCast(oClicked) );
See if that runs. Hopefully the speak and the spell will get cast.
--------------------------------------
2) If the first one works then make a new function:
#include "x0_i0_talent"
void MySpellAttack( object target )
{
TalentSpellAttack(target);
}
Replace the line:
AssignCommand(oAssociate, bchFunctionIgnoreInt(TalentSpellAttack(oClicked) ) );
with
AssignCommand(oAssociate, MySpellAttack(oClicked) );
Modifié par Mudeye, 30 septembre 2010 - 04:21 .
#19
Posté 09 octobre 2010 - 04:25
Mudeye, I think you were right about that recommendation, and I think that I was not successfully putting TalentSpellAttack() into the associates action queues.
Unfortunately, it seems that there's a lot more to my problem. The more I test, the more I think that my attempts are being thwarted by the associate AI that are already running, but I don't know how to properly hook into that AI to give the right commands.
Thanks for all your help, though. I'll just have to keep reading through Bioware's code until I find out the proper way to do this. There must be something I'm missing in DetermineCombatRound(), as that seems to be Bioware's go-to function for making attacks happen.





Retour en haut






