Valà ... Je n'ai pas le temps de faire avancer mes projets à NwN mais j'avais promis à Ballesto que le matériel qu'il m'avait fourni serait redonné à la communauté, alors ... Voici donc une caverne aux parois recouvertes de glace :
C'est fait avec le hak pack, ice mines d'elesias. La vu d'ensemble est la suivante :
Au centre, on a l'entrée de mine comportant deux pentes, ici une vers le nord et une autre vers le sud. La glace nous porte la nouvelle d'une gamelle de nos aventuriers. Sur ces deux pentes sont posés des triggers, respectivement GlissadeNord et GlissadeSud. sur le OnEnter de ces triggers on a le script suivant :
void main()
{
object oPC= GetEnteringObject();
object oDestination = GetNearestObjectByTag("WP_"+GetTag(OBJECT_SELF)+"_"+IntToString(d3()));
location lDestination = GetLocation(oDestination);
string sGeulante_1 = "CIEL ! JE GLISSE !";
string sGeulante_2 = "GASPATURE ! JE CHOIS !";
string sGeulante_3 = "AAAAAARRRRRRGH ! !";
SetLocalInt(oPC,"Glisse",TRUE);
AssignCommand(oPC,ClearAllActions());
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectMovementSpeedIncrease(500),oPC,1.5f);
AssignCommand(oPC,SetFacing(GetFacingFromLocation(lDestination)));
switch(d3()){
case 1 :
AssignCommand(oPC,ActionSpeakString(sGeulante_1,TALKVOLUME_SHOUT));
break;
case 2 :
AssignCommand(oPC,ActionSpeakString(sGeulante_2,TALKVOLUME_SHOUT));
break;
case 3 :
AssignCommand(oPC,ActionSpeakString(sGeulante_3,TALKVOLUME_SHOUT));
break;
}
AssignCommand(oPC,ActionForceMoveToObject(oDestination,TRUE,0.0f));
DelayCommand(0.01f,SetCommandable(FALSE,oPC));
}
L'aventurier qui glisse se dirige de manière aléatoire vers un waypoint. Les trois waypoints du nord s'appellent WP_GlissadeNord_1, 2 et 3 et les trois du sud WP_GlissadeSud_1, 2 et 3. Au pied de chacun de ces waypoints on trouve un trigger qui porte sur son OnEnter :
void main()
{
object oPC=GetEnteringObject();
if(GetLocalInt(oPC,"Glisse")==TRUE){
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectKnockdown(),oPC,3.0f);
object oInvisible = GetNearestObjectByTag("InvisibleObject");
switch(d3()){
case 1 :
AssignCommand(oInvisible,DelayCommand(0.5f,PlaySound("bf_med_flesh")));
break;
case 2 :
AssignCommand(oInvisible,DelayCommand(0.5f,PlaySound("bf_med_hard")));
break;
case 3 :
AssignCommand(oInvisible,DelayCommand(0.5f,PlaySound("bf_small")));
break;
}
DelayCommand(3.0f,SetCommandable(TRUE,oPC));
SetLocalInt(oPC,"Glisse",FALSE);
}
}
C'est un trigger générique qui peut être copier/coller pour chacun des waypoints. L'aventurier après sa glissade sur une des pentes va heurter un rocher, une paroi trébucher sur un gros caillou et tomber et rester sonné quelques secondes. Un objet invisible générique est utilisé pour jouer un son pour chacun des waypoints.
On récapitule : les aventuriers rentrent dans une caverne de glace, glissent sur une pente, tombent et restent sonnés quelque secondes. Les voici donc obligés de trouver une sortie à travers les mines/cavernes de glaces. Ce bidule était originellement prévu dans le cadre de la traversée d'un montagne dont le col était fâcheusement gardé par un géant du froid et un dragon blanc en cours de dressage, dont on peut voir les silhouettes ci dessous en haut à gauche :
Ce qui amenait les aventuriers à trouver un autre passage. En faisant le tour de cette caverne, on trouve au nord est une tache bleue claire. Voilà ce qu'est cette tâche lorsque l'on est en face :
Une espèce de porte avec un visage au centre ... C'est ici que je remercie Ballesto de m'avoir fourni le premier essai concernant cette porte que voici :
dr_03.tga
A mettre dans son répertoire override faute de mieux. Actuellement, ne sachant pas comment faire une nouvelle porte, cette dernière prend la place des portes en pierre.
La porte ne s'ouvre pas. Et en plus elle se fout de la gueule des aventuriers :
void main()
{
object oPC = GetClickingObject();
if(!GetLocalInt(OBJECT_SELF,"PremierEssai")){
ActionSpeakString("YOUUUUUUUU !");
DelayCommand(1.5,ActionSpeakString("SHALLLLLLLLLLLL !"));
DelayCommand(3.0,ActionSpeakString("NOT"));
DelayCommand(4.5,ActionSpeakString("PASSSSSSSSSSS !"));
DelayCommand(7.5,ActionSpeakString("mais qu'est-ce que je raconte moi ? 8o"));
DelayCommand(9.0,ActionSpeakString("Mot de passe ?"));
SetLocalInt(OBJECT_SELF,"PremierEssai",1);
}
else{
switch(d3()){
case 1 :
ActionSpeakString("Mot de passe ?");
break;
case 2 :
ActionSpeakString("Passe de mot ?");
break;
case 3 :
ActionSpeakString("PAPIER BITTE ! nan j'deconne ... ");
break;
}
}
}
Sur le OnFailToOpen.
Si on l'attaque et la blesse il se passe cela :
qui se fait avec ça :
void main()
{
object oPC = GetLastAttacker();
object oWeapon = GetLastWeaponUsed(oPC);
location lweaponporte = GetLocation(OBJECT_SELF);
string sTorchTag = "NW_IT_TORCH001";
//This is the tag for the default torch, be sure to change it if you're using a torch by any other name.
if(!GetLocalInt(OBJECT_SELF,"DejaEndomagee")){
CreateObject(OBJECT_TYPE_PLACEABLE,"invisobj001",lweaponporte);
SetLocalInt(OBJECT_SELF,"DejaEndomagee",TRUE);
}
if(!GetWeaponRanged(oWeapon) && !(GetTag(GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC)) == sTorchTag ||
GetTag(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC)) == sTorchTag ))
{
AssignCommand(oPC,ClearAllActions());
FloatingTextStringOnCreature("Votre arme reste coincee ...",oPC);
AssignCommand(GetNearestObjectByTag("InvisibleObject"),ActionTakeItem(oWeapon,oPC));
DelayCommand(1.5f,FloatingTextStringOnCreature("se recouvre de glace ... ",oPC));
DelayCommand(3.0f,FloatingTextStringOnCreature("et finit pas disparaitre dans la porte!",oPC));
}
}
Sur le OnDamaged.
Mais finalement, les aventuriers trouvent dans la caverne les restes d'un feu avec des squelettes autour, et dans le feu de quoi faire une torche ... En attaquant la porte en portant une torche à la main il se passe celà :
void main()
{
object oPC = GetLastAttacker();
//location lSource = GetLocation(GetObjectByTag("FirePlace"));
//Where to start Fire
string sTorchTag = "NW_IT_TORCH001";
//This is the tag for the default torch, be sure to change it if you're using a torch by any other name.
if(!GetLocalInt(OBJECT_SELF,"DejaAttaquee")){
ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectDamageResistance(DAMAGE_TYPE_PIERCING,1,0),OBJECT_SELF);
ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectDamageResistance(DAMAGE_TYPE_COLD,1,0),OBJECT_SELF);
SetLocalInt(OBJECT_SELF,"DejaAttaquee",TRUE);
}
//Does the attacker have a torch at the ready?
if ((GetDistanceToObject(oPC)<7.0) &&
(GetTag(GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC)) == sTorchTag ||
GetTag(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC)) == sTorchTag )){
//Then light him up!
AssignCommand(oPC,ClearAllActions());
//AssignCommand(oPC,ActionMoveToLocation(lSource,TRUE));
//AssignCommand(oPC,ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW,0.5,2.0));
object oTorche = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC);
AssignCommand(oPC,ActionPutDownItem(oTorche));
location lSource = GetLocation(oPC);//Where to start Fire
AssignCommand(oPC,ActionMoveAwayFromLocation(lSource,FALSE,2.0));
AssignCommand(oPC,ActionDoCommand(
AssignCommand(CreateObject(OBJECT_TYPE_PLACEABLE,"flamelarge001", lSource),
PlaySound("al_cv_firebowl1"))));
AssignCommand(oPC,ActionDoCommand(
DelayCommand(1.0,ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY,
EffectVisualEffect(VFX_IMP_FLAME_M),lSource,10.0))));
AssignCommand(oPC,DelayCommand(5.0f,SetFacing(GetFacingFromLocation(lSource))));
DestroyObject(oTorche,7.0f);
AssignCommand(oPC,ActionWait(7.0));
AssignCommand(oPC,ActionDoCommand(
AssignCommand(CreateObject(OBJECT_TYPE_PLACEABLE,"plc_waterdrip", lSource),
PlaySound("as_an_drip1"))));
DelayCommand(8.0f,SoundObjectPlay(GetObjectByTag("CaCoule")));
DelayCommand(9.5,ActionSpeakString("Hey mais ... JE FOND !"));
DelayCommand(11.0,ActionSpeakString("OK c'est bon je vous laisse passer ... "));
DelayCommand(12.5,ActionOpenDoor(OBJECT_SELF));
DelayCommand(13.0,DestroyObject(GetNearestObjectByTag("InvisibleObject")));
DelayCommand(14.0,ActionSpeakString("Vous trouverez vos armes sur mon seuil."));
}
}
On voit qu'il nous faut ici un objet son de tag CaCoule ... C'est un objet personnalisé dans lequel j'ai mis tous les sons d'eau qui me convenait. On a également besoin d'un objet personnalisé, une Large Flame sur le OnHeartBeat duquel on met
DestroyObject(OBJECT_SELF,1.0);
De manière a ce que la flamme disparaisse rapidement.
Voilà, la porte est ouverte les aventuriers ont récupéré leur matériel et l'aventure peut continuer. Mais l'action de la porte n'est pas terminée ... D'abord elle refuse de se fermer sous l'action des aventuriers (ActionOpenDoor(OBJECT_SELF); sur le OnClose) De plus, elle a un visage sur chacune de ses faces et une fois tous les aventuriers passés elle se ferme brutalement et définitivement et se met à Hurler "LA GARDE LA GARDE" ce qui ne manque pas d'attirer les orcs esclavagistes locaux et en théorie de faire fuir nos aventuriers qui sont supposés passer discrètement.
Les pbs :
La flamme créée n'apparaît par toujours. C'est un pb de timing avec le OnHeartBeat. Si la flamme est créée juste au moment où l'on commence un cycle, le OnHeartBeat est immédiatement exécuté et la flamme détruite avant d'avoir été créée. Le même pb est rencontré avec la porte, qui, quand ça se passe bien après avoir été ouverte ne bouge pas d'un iotas malgré les injonctions des PJs, mais qui de temps en temps se laisse fermer pour se rouvrir aussitôt ... Un autre truc qui me déplait c'est que le personnage après avoir mis le feu au pieds de la porte, doit s'éloigner de la porte avec un fleeaway. Mais le fleeaway ça marche moyen, le perso part dans n'importe quelle direction et s'il est coincé par du décor il bouge pas ... Je pense qu'il serait préférable de mettre un waypoint et de faire aller le PJ à ce waypoint. Enfin dans le dernier script, je tente de faire jouer un son à la création du water drips ... Je crois que ce son n'est pas joué et c'est pour ça que je fais un soundobjectplay après ...