Factions, cri et attaquer mes ennemis .


NdT : important différenciez bien "party" qui est un groupe de joueurs de "partie" qui a le sens habituel

Qu'est ce qu'est au juste une faction?


Une faction est une de ces trois choses : une party de PC & leur associés (mercenaires, familiers...), un joueur seul sans membre dans la partie & ses associés ou un groupe de NPC ou un NPC seul a qui ont été assignés des réactions par défault envers les autres factions. Cela ne doit pas être confondu avec une "organisation" comme une guilde de voleurs ou la garnison d'un ville. Les joueurs de peuvent pas "rejoindre" une factions , et chaque organisation peut contenir plusieurs factions.

Que font donc les factions ?

Juste ce qu'elles sont supposé faire : fixer le comportement par défaut d'un NPC face aux autres factions. L'IA générique donne une position comme amical (Friendly) ou hostile (Hostile) et le NPC réagit conformement au script. Un NPC maintiendra cette position par défaut jusqu'à et à moins qu'il lui soit demandé de faire autre chose.

Ce qui est probablement le plus importa nt à comprendre à propos des factions est qu'il y a cependant une différence entre ce que la faction "ressent" et ce que le NPC seul "ressent". Si un NPC individuel sans faction est attaqué, il deviendra hostile et se défencra tout seul. Il restera aussi hostile jusqu'à ce qu'on lui demande autrechose. Cependant, les autres membres de la faction non prészent ne seront pas nécessairement hostile comme lui. La différence de réaction est du à la "réputation", qui est différent de la réputation de quelqu'un avec la faction.

La commande NWscript se réfère, malheureusement, aux deux types de réputation de la même manière, donc faire la distinction entre les deux peut être difficile.

Pour mieux expliquer la différence, considerez ceci :
Une party de PC sont dans une taverne. Le voleur de la party décide d'attaquer un roturier à proximité (qui est membre de la faction Commoner). Puisqu'il est attaqué, le roturier devient hostile envers le voleur...ce qui entrainera aussi une attaque de la part de tous les NPC à proximité qui sont amicaux avec cette faction et qui deviendront aussi hostile envers le voleur.Le reste de la party n'a encore rien fait donc personne n'est hostile envers elle. Si le reste de la party est engagée dans le combat, les NPC déjà hostile avec le voleur le seront avec toute la partie. Admettons maintenant que la party entière quitte la taverne tout de suite et échappe à ces poursuivants...ces individus resteront hostile avec le voleur (et les autres membres de la party ayant réagit) jusqu'à ce qu'on leur dise de réagir autrement. Les autres membres de la faction Commoner réagiront normallement.

La seule raison que ce ne soit pas le cas est si la case "Global Effect" est cochée (Elle peut être trouvé dans le Faction Editor). Les individus écartés (dans l'espace) dans une faction maintiendront des relations différentes...mais, si vous en attaqué un et qu'il devient hostile, tous les membres de la faction seront hostiles. Tous les changement dans les relations d'un membre seront appliqués à la faction entière. (Il y a certains moment où c'est pratique dans un module...mais soyez conscient que toute nouvelle faction à cette option activée).

Que fait l'IA standard avec les factions?

Pour mieux comprendre pourquoi quelque chose se passe ou ne se passe pas, voici un résumé détaillé de comment les factions affectent l'IA standart :

* Dans l'événement OnPerception (qui se lance rappelez vous uniquement quand la cible entre dans la limite de perception, ou si le type de percepption change),si un NPC perçoit un hostile et qu'il n'est pas encore en combat le lançe le cri "NW_I_WAS_ATTACKED" (regardez ci dessous) et attaque.

* Si la créature est attaquée mais pas encore en combat, elle lance les cris "NW_I_WAS_ATTACKED" et "NW_ATTACK_MY_TARGET" puis attaque son attaquant.

* Si la créature est tuée ça lance automatiquement les cris "NW_ATTACK_MY_TARGET" et "NW_I_AM_DEAD".

Et que font ces cris ?
D'une part, il y a les mots simplements parlés qu'on avait demandé à la créature de dire, les même que ceux que peut taper le PC dans son système de Chat. Ces cris cependant sont silencieux...les joueurs ne les entendent ou ne les voient pas flotter, mais les PC oui, et le script OnSpawn leur demande de les écouter et de réagir. Les cris ne sont pas entendus à travers les murs ou les portes, et ils ont une portée limitée.

* Le cri "NW_ATTACK_ MY_TARGET" a seulement un effet sur les NPC qui ont activé (enlevé les "//") de la ligne "SetSpawnInCondition (NW_FLAG_SHOUT_ ATTACK_MY_TARGET) ;" dans leur script OnSpawn. Si il a été entendu par un allié (un membre d'un faction amicale envers celui qui a poussé le cri), il fixe les relations de sa faction en hostile avec celui qui a attaqué le crieur (si ce n'était pas déjà le cas,) arrête ce qu'il fait et attaque tout ennemi à portée.

* Le cri "NW_I_WAS_ATTACKED" peut être entendu par n'importe qui. Si celui qui l'entend est un allié du crieur et n'est ni en combat ni a moins de 10 niveaux de roturier, il attaquera le crieur.

* Le cri "NW_I_AM_DEAD" peut aussi être entendu par n'importe qui. Son effet est le même que le cri "I was attacked" ci dessuss.

Comment puis-je faire attaquer mes NPC ?

Si est créature démarre hostile et détecte un PC, son événement OnPerception se lancera et elle attaquera immédiatement. Pas de porblème dans ce cas. Cependant, la situation devient souvent plus compliquée si vous avez un NPC qui était neutre face au PC quand l'événement OnPerception se lance et elle deviendra hostile lors du dialogue ou du script.

Pour qu'un NPC devienne hostile face à un PC et l'attaque, vous devez d'abord identifier la cible. Dans le dialoque c'est facile :


object oTarget = GetPCSpeaker();

En dehors d'un dialogue, dans un script normal, ça peut varrier :


// la cible est le PC le plus proche de celuui à qui appartient le script.
object oTarget = GetNearestCreature (CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
// C'est le dernier objet perçu, doit être seulement utilisé dans le OnPerception.
object oTarget = GetLastPerceived();

Ci dessous, vous avez deux possibilitées.Sans problèmes, une fois que vous avez identifié votre cible, vous pouvez appeler le reste de la commande normallement. Pour le script suivant, nous nous assurons que nous allons commencer le combat à la fin du dialogue (en plaçant ce script dans la section "Action Taken" de la ligne de dialogue ou nous voulons que le combat commence).

Premièrement, est ce que je veux que l'attaquant change la faction entière en hostile ? Si c'est le cas, j'utilise ça :

#include "NW_I0_GENERIC"
void main()
{
// Baisse la relation de ma faction avec le PC de 100 (jusqu'à hostile)
AdjustReputation (GetPCSpeaker(), OBJECT_SELF, -100);
// Démarre le combat (Cela nécessite le fichier générique nw_i0_generic ci dessus)
DetermineCombatRound();
}

Et c'est tout (c'est aussi le script générique "nw_dl_attoned").

Maintenant, je veux juste que l'attaqué soit hostile (et lui seul) :


#include "NW_I0_GENERIC"
void main()
{
// me fixe comme hostile face au PC
// Notez que le "temporary" (temporaire) dure 3 minutes sans une valeur de durée précisée dans la commande.

SetIsTemporaryEnemy (GetPCSpeaker());
// démarre le combat (ça nécessite le fichier générique nw_iO_generic, ci dessus)
DetermineCombatRound();
}

Mais je supose qui j'ai d'autres amis à côté que je voudrais voir prendre part au combat. Si j'utilise AdjustReputation pour rendre leur faction hostile, il n'attaqueront toujours pas. Pourquoi ? Regardez dans le paragraphe à propos de l'IA générique...je n'ai pas attaqué le premier et je suis déjà en combat, c'est pourquoi je ne pousserais pas de cri. Nous avons tous déjà perçut le PC et il n'était pas hostile. Et je ne suis pas mort...donc mes amis resteront la jusqu'à ce qu'ils soient attaqué ou que je meurs. J'ai deux choix.

Soit leur envoyer la commande via AssignCommande comme ceci :


#include "NW_I0_GENERIC"
void main()
{
// Ce script marche bien si vous savez exactement qui enroler dans le combat
// et qu'ils ont des resrefs uniques

object oGoblin2 = GetObjectByTag ("GOBLIN2");
object oGoblin3 = GetObjectByTag ("GOBLIN3");
// Baisse ma valeur de réaction avec le PC de 100 (jusqu'à hostile)
AdjustReputation (GetPCSpeaker(), OBJECT_SELF, -100);
// demande à mes amis de débuter le combat
AssignCommand (oGoblin2, DetermineCombatRound());
AssignCommand (oGoblin3, DetermineCombatRound());
// démarre le combat (ça nécessite le fichier générique nw_iO_generic, ci dessus)
DetermineCombatRound();
}

Ou comme cela :


#include "NW_I0_GENERIC"
void main()
{
// Ce script fait que tous les allié dans la zone actuelle attaquent
location oHere = GetLocation (OBJECT_SELF);
// Baisse ma valeur de réaction avec le PC de 100 (jusqu'à hostile)
AdjustReputation (GetPCSpeaker(), OBJECT_SELF, -100);
// maintenant, cicle tous les objets dans la zone
object oFriend = GetFirstObjectInArea (GetArea(OBJECT_SELF));
while (GetIsObjectValid(oFriend))
{
// Si c'est objet fait parti de ma faction et que c'est une créature
if (GetFactionEqual(oFriend) && (GetObjectType(oFriend) == OBJECT_TYPE_CREATURE))
{
// et qu'ils peuvent voi le PC
if (GetObjectSeen (GetPCSpeaker(), oFriend))
{
// leur demande de commancer le combat
AssignCommand (oFriend, DetermineCombatRound());
}
// si il ne peut pas le voir
else
{
// lui demande d'arrêter ce qu'il fait
AssignCommand (oFriend, ClearAllActions());
// and come to my location
AssignCommand (oFriend, ActionMoveToLocation(oHere, TRUE));
}
}
oFriend = GetNextObjectInArea (GetArea(OBJECT_SELF));
}
// maintenant, je démarre le combat moi même
DetermineCombatRound();
}

Il y a aussi des variation à faire...ce ne sont que des exemples. Une autre manière de faire commencer un combat à un groupe est de vérifier que tous ont la ligne:


SetSpawnInCondition (NW_FLAG_SHOUT_ATTACK_MY_TARGET);

activée dans leur script OnSpawn. Puis, quand vous commencez à attaquer, vous pouvez juste fare cela:


#include "NW_I0_GENERIC"
void main()
{
// Baisse ma valeur de réaction avec le PC de 100 (jusqu'à hostile)
AdjustReputation (GetPCSpeaker(), OBJECT_SELF, -100);
// démarre le combat (ça nécessite le fichier générique nw_iO_generic, ci dessus)
DetermineCombatRound();
// et cri à tous mes alliés dans le voisinage de se joindre à moi
SpeakString ("NW_ATTACK_MY_TARGET", TALKVOLUME_SILENT_TALK);
}


Vous ne verrez pas la phrase, mais tous les alliés avec une portée auditive qui peuvent voir la cible coureront à l'attaque.

Comment je fais revenir mes NPC à une attitude neutre?

Si vous voulez rendre un NPC non hostile une fois qu'il est en combat avec les PC (si le PC est mort ou pour d'autres raisons), vous devez être sur que deux choses se sont passées : un, que la faction ait une attitude non hostile face au joueur (soit neutre soit amicale). Deux, si la faction n'a pas "Global Effect" cochée, vous devez changer aussi tous les sentiments personnels des NPC face au PC.

Les factions "standard" dans le jeu (Commoner, Defender, etc) sont les seules pour lequels vous pouvez faire les deux dans une seule commande. Cela est fait à travers la commande SetStandardFaction, et un exemple de celle ci est dans le script par défault de mort ("nw_o0_death"), qui rement toutes les factions standarts à la neutralité à la mort du joueur.

Note: de nombreuses personnes ont rapportées que la faction defender n'est pas remise à "zéro" avec cette commande, mais vous pouvez le faire en la traitant comme une faction personnalisée. Cf. ci dessous

Pour les factions personnalisées (Custom factions), vous aveez besoin de deux commandes : AdjustReputation pour changer les relation de la faction avec la cible et ClearPersonnalReputation pour changer tous les sentiments du NPC. Dans les deux cas, vous devez isoler la cible qui esi un membre d'un faction (ce qui peut être difficile si vous avez beaucoup de membres avec des tags différents ou qui peuvent être mort)...les factions personnalisées ne sont pas identifiées par un nom, vous pouvez seulement dire "la faction a qui appartient un tel et un tel". et si vous voulez effacer les réputations personnelles du PC avec une faction personnalisée entière, vous devez créer un cycle.

Voici un exemple de script qui ajustera la faction à neutre et fera un cycle à l'intérieur de celle ci pour restaurer pour tous les membres une réaction normale avec le PC. Cette faction n'a pas "Global Effect" cochée, et a des membres avec le même tag "GOBLIN01". Le PC est mort et ce script est dans son OnDeath event:


// Je crée ici une commande "ClearAll FactionMembers'" personnalisée
// premièrement déclarer les paramêtres de la nouvelle commande, puis ce qu'elle sera
// j'aurais pu raisonnablement les membres dans un autre script et utiliser
// #include pour le mettre dans ce script (ou dans un autre)

void ClearAllFactionMembers (object oMember, object oPlayer)
{
object oClear = GetFirstFactionMember (oMember, FALSE);
while (GetIsObjectValid(oClear) == TRUE)
{
ClearPersonalReputation (oPlayer, oClear);
oClear = GetNextFactionMember (oMember, FALSE);
}
}
// Voici le corp principal de mon script
void main()
{
// identifie le joueur
object oPlayer = GetLastPlayerDied();
// identifie une membre de la faction, je m'assure que ces membres sont vivants.
// Ceêndant, je pourrais essyer plusieurs choses pour modifier un membre qui est // valid puis d'utiliser la commande if

object oGoblin = GetObjectByTag("GOBLIN1");
if (GetIsObjectValid(oGoblin))
{
// augmente la relation de la faction de 100
AdjustReputation (oPlayer, oGoblin, 100);
// utilise ma commande personnalisée dans la faction
ClearAllFactionMembers (oGoblin, oPlayer);
}
}

Ce qu'il y a de bien avec les cris

Quelques trucs à essayer ou à utiliser en rapport avec les cris.

Le premier:
J'ai toujours détesté le fait que un NPC qui entend le cri "NW_ATTACK_ MY_TARGET" mais ne voit pas le crieur ne fait rien. Il devient hostile mais n'attaque pas tant que l'ennemi ne vient pas dans sa ligne de vue. Il yt a un moyen que j'ai utilisé pour qu'il vienne vers l'ondroit d'où provient le cri
.
Etape 1 - La première chose qui doit être faite est qu tous les membres de la factions qui repondront au cri doivent avoir leur script OnSpawn paramêtré de la même manière. Allez dans leur script OnSpawn et activez les lignes suivantes (enlevez les "//"):


SetSpawnInCondition (NW_FLAG_SHOUT_ ATTACK_MY_TARGET);
SetSpawnInCondition (NW_FLAG_ ON_DIALOGUE_EVENT);

Puis re sauvez le script sous un nom différent.

Etape 2 - Souvenez vous qu'une créature qu'une créature ne criera "attack myt target" uniquement si elle est attaquée en première ou si elle est tuée. Si vous voulez qu'elle crie, elle même, vous devez mettre ceci dans un script. Il y en a un fournit dans l'exemple ci dessous dans lequel la créature devient hostile et pousse le cri à la fin du dialogue.

Etape 3 - Mettez le script suivant dans le OnUserDefined event de chaque créature:


// Ce script fait que le récepteur d'un cri"NW_ATTACK_MY_TARGET" vienne
// et regarde si il ne voit pas un ennemi. Une fois qu'il s'est approché du crieur,
// si il voit un ennemi, son script OnPerception sera activé et il attaquera.
// Une modification interessante de ce script serait d'avoir que les récepteur du cri
// situé hors de vue pousse un deuxième cri qui porte plus loin que le cri original...
// cela nécessiterait cependant un nouveau cri et une nouvelle définition du
// comportement

void main()
{
int nEvent = GetUserDefinedEventNumber();
if (nEvent == 1004) // OnDialog event
{
// Cela regarde si le cri correspont au comportement fixé par le
// 'SetListeningPatterns' dans OnSpawn

int nMatch = GetListenPatternNumber();
object oShouter = GetLastSpeaker();
object oIntruder;
// si je reconnais ce cri et que le crieur est valide et amical avec le NPC
if(nMatch != -1 && GetIsObjectValid (oShouter) && !GetIsPC(oShouter) && GetIsFriend (oShouter))
{
// et que le cri est "NW_ATTACK_MY_TARGET"
if (nMatch == 5)
{
// essaie de trouver l'ennemi
oIntruder = GetLastHostileActor (oShouter);
if(!GetIsObjectValid (oIntruder))
{
oIntruder = GetAttemptedAttackTarget();
if(!GetIsObjectValid (oIntruder))
{
oIntruder = GetAttemptedSpellTarget();
if(!GetIsObjectValid (oIntruder))
{
oIntruder = OBJECT_INVALID;
}
}
}
// si je ne peux trouver ni le crieur ni son ennemi
if (GetIsObjectValid (oShouter) && !GetObjectSeen (oIntruder) && !GetObjectSeen (oShouter))
{
// défini l'emeplacement du crieur
location lShouter = GetLocation(oShouter);
// arrête ce que je suis en train de faire
ClearAllActions();
// et bouge jusqu'à cette emplacement
ActionMoveToLocation (lShouter, TRUE);
}
// ou si je peux voir le crieur mais pas l'ennemi
else if (GetIsObjectValid (oShouter) && !GetObjectSeen (oIntruder) && GetObjectSeen (oShouter))
{
// arrête ce que je fais
ClearAllActions();
// et bouge plus près du crieur
ActionMoveToObject (oShouter, TRUE);
}
}
}
}
}

La seconde:
Voici un exercice iteressant qui démontre comment faire votre propre cri. Dans cet exemple, je veux avoir un dialogue qui lance un cri et entraine que tous ceux qui l'entendent joue l'animation "worship" (ils pensent que le PC est un devin.)

Etape 1 - Nous allons faire le dialogue à la fin duquel le NPC poussera le cri. Faisons le très simple...une ligne seulement "C'e'st un devin !" et puis le script suivant est attaché à la zone "Action Taken" de cette ligne:


void main()
{
// entre une variable sur moi même éguale au PC qui est en train de me parler
SetLocalObject (OBJECT_SELF, "worship", GetPCSpeaker());
// lance le cri "BOWDOWN"
SpeakString ("BOWDOWN", TALKVOLUME_SILENT_TALK);
// faite l'animation worship pendant 30 secondes
ActionPlayAnimation (ANIMATION_LOOPING_WORSHIP, 0.5, 30.0);
}

Etape 2 - Maintenant, nous devons paramétrer les autre NPC pour qu'ils entendent le cri. Pour cela, nous devons activer la ligne


SetSpawnInCondition (NW_FLAG_ ON_DIALOGUE_EVENT);

dans leur scriptOnSpawn et ajouter la ligne suivant n'importe où :


SetListenPattern (OBJECT_SELF, "BOWDOWN", 100);

Cela rend la chaine de caractère "BOWDOWN" égale à 100 (pris au hasard) et reconnaissable par les NPC qui doivent l'entendre (en utilisant la commande SetListening (OBJECT_SELF, TRUE)... qui est lancée par la commande du OnSpawn SetListeningPatterns).

Etape 3 - Maintenat, nous devons juste demander au NPC que faire quand ils entendent cette commande. Toute chaine de caractères dite fait partie de leur OnDialogue event... donc le script suivant peut être placé dans le OnUserDefined event:


void main()
{
int nEvent = GetUserDefinedEventNumber();
// si je recois OnDialogue event
if (nEvent == 1004)
{
// regarde ce que j'ai entendu
int nMatch = GetListenPatternNumber();
// identifie le locuteur
object oShouter = GetLastSpeaker();
// et attrappe la variable du NPC qui s'incline
object oWorship = GetLocalObject(oShouter, "worship");
// Si la chaine de caractèreest une que je reconnais et que l'émetteur est un NPC valid et amical
if(nMatch != -1 && GetIsObjectValid (oShouter) && !GetIsPC (oShouter) && GetIsFriend (oShouter))
{
// et que la chaine de caractère "BOWDOWN" est égale à 100
if(nMatch == 100)
{
// se tourner face au PC avec lequel le NPC parle
ActionDoCommand (SetFacingPoint (GetPosition(oWorship)));
// puis joue l'animation worship pendant 30 secondes
ActionPlayAnimation (ANIMATION_LOOPING_WORSHIP, 0.5, 30.0);
}
}
}
}

Ce n'est évidemmement pas tout ce qu'il faut savoir à propos des factions et des cris, mais ça devrais, je l'espère être utile à un grand nombre de personnes.

__________________
Théranthil, Mage Elfe prêtre de Mystra Membre des GdE
Parti Elmotiste pour un parler jowilien: Ministre supérieur de la corruption

"Les langues vont toujours bon train. Les pieds ont du mal à suivre."
(les bourses aussi NdT )
Mespert de la porte de Baldur