Générateur et Nettoyeur de Monstres.
Voici le système que j'utilise sur le module de Beckrunes Section Persistant/RP (je fais ma pub au passage

) .
Le principal problème avec les déclencheurs est qu'ils sont très gourmand en ressources et qu'ils ralentissent le chargement des maps.
Plus le nombres de rencontres augmentent et plus le problème deviendra perceptible.
La solution consiste à n'utiliser aucun déclencheur et tous contrôler grâce à des scripts.
Pour que le système soit accessible au plus grand nombre je vais tacher d'expliquer le plus clairement possible la théorie.
1) Il faut savoir si quelqu'un est dans la zone.
2) S'il y a quelqu'un alors on génère des monstres.
On génère mais on fixe un nombre maximum de monstres par zone, sinon on pourra rapidement se retrouver avec une centaine de monstres (ce qui est pas génial non plus)
3) S'il n'y a plus personne dans la zone, on réinitialise la zone.
L'initialisation consiste à :
- Fermer toutes les portes (ce qui est un gros soucis)
On déclenche une tempo pour nettoyer la zone :
- des drops
- des monstres.
Maintenant la même chose en script

:
Tout d'abord on va compter le nombre de joueurs dans chaque zone pour savoir si on génère des monstres ou pas.
Ce script sera placé sur le OnEnter de la zone (de toutes les zones du module)
Il incrément une variable "nbjoueurs" qui est propre à chaque zone.
///////////////////////////////////////////////
//
// Dimencia 13 fev 2008
//
// Incremente une variable "nbjoueurs" pour
// Chaque zone.
//
///////////////////////////////////////////////
void main (){
object oPC = GetEnteringObject();
if (GetIsPC(oPC) == TRUE){
object oZone = GetArea(OBJECT_SELF);
int iNb_joueurs = GetLocalInt(oZone, "nbjoueurs") + 1;
// SendMessageToPC(oPC, "nb joueurs ds zone:"+IntToString(iNb_joueurs));
SetLocalInt(oZone, "nbjoueurs",iNb_joueurs);
}
}
Maintenant de la même manière on va s'occuper de la sortie des joueurs
et décrémenter la variable de zone "nbjoueurs".
Lorsqu'il ni à plus de joueur dans la zone on lancera alors un script de nettoyage.
Ce script est placé dans le OnExit de la zone (de toutes les zones du module)
///////////////////////////////////////////////
//
// Dimencia 13 fev 2008
//
// Decremente une variable "nbjoueurs" pour
// Chaque zone.
//
///////////////////////////////////////////////
void main (){
object oPC = GetExitingObject();
if (GetIsPC(oPC) == TRUE){
object oZone = GetArea(OBJECT_SELF);
int iNb_joueurs = GetLocalInt(oZone, "nbjoueurs") - 1;
// SendMessageToPC(oPC, "nb joueurs ds zone:"+IntToString(iNb_joueurs));
if (iNb_joueurs > 0) {
SetLocalInt(oZone, "nbjoueurs",iNb_joueurs);
}
else {
DeleteLocalInt(oZone, "nbjoueurs");
// SendMessageToPC(oPC,"Demarrage du Nettoyeur dans "+ GetName(oZone));
ExecuteScript("a_icn_nettoyeur", OBJECT_SELF);
}
}
}
Passons maintenant au nettoyage. Ce script est une version modifié que
Icien m'avait fourni pour le premier module de Beckrunes (neverwinter1).
comment fonctionne ce script?
- Il est lancé dès qu'une zone se retrouve avec "nbjoueurs" < 1 (Voir script du OnExit de zone)
- Dès le début on va refermer toute les portes ce qui en soit sera déjà un soucis de moins!
- Ensuite on lance une tempo. Ici 70 secondes, en dur dans le code, ce qui est un temps maximum pour charger une zone et laisse la possibilité au joueur de faire des aller et retour dans la zone sans que les monstres disparaissent de suite.
- Au bout des 70 secondes, on regarde s'il n'y a toujours aucun joueur dans la zone et on nettoies les drops et les monstres sinon, on abandonne le nettoyage.
// nettoyeur de zones (drop, coffre, rencontres)
// a mettre dans le OnExit de la zone
// Icien decembre04
//
// Dimencia
// Controle de la zone avant nettoyage apres TEMPO secondes (fevrier 08)
// Le script de nettoyage se lance apres TEMPO secondes
// Ajout Fermeture des portes des le depart du dernier joueur (mars 08)
//
//
void init_map(object oSource, object oZone);
void main() {
float TEMPO = 70.0; //temps avant nettoyage en secondes
object oPC = GetExitingObject();
object oCrea = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oPC, 1);
object oZone = OBJECT_SELF;
int iNb_joueurs = GetLocalInt(oZone, "nbjoueurs");
// SendMessageToPC(oPC, "Le script de Nettoyage voit "+IntToString(iNb_joueurs)+" joueurs ds la zone : "+GetName(oZone));
// Je retest mais normalment c'est zero au lancement de ce script
// Le script de nettoyage à Icien sera lancer au bout de TEMPO secondes
if (iNb_joueurs < 1) {
// On ferme toutes les portes
object oObj = GetFirstObjectInArea(oZone);
int iType, iPortes = 0;
while (GetIsObjectValid(oObj)) {
iType = GetObjectType(oObj);
if (iType == OBJECT_TYPE_DOOR) {
iPortes++;
AssignCommand(oObj, ActionCloseDoor(oObj));
}
oObj = GetNextObjectInArea(oZone);
}
// SendMessageToPC(oPC, "fermeture de "+IntToString(iPortes)+" porte(s)");
// SendMessageToPC(oPC,"Le script de Nettoyage se lancera dans : "+IntToString(FloatToInt(TEMPO))+" secondes");
DelayCommand(TEMPO , init_map(oPC, OBJECT_SELF));
}
else{
// SendMessageToPC(oPC,"La zone n'est plus vide.");
}
}
void init_map(object oPC,object oZone) {
int iPnj = 0;
int iDrop = 0;
int iItem = 0;
//SendMessageToPC(oPC,"Le processus de nettoyage de "+ GetName(oZone) + " commence");
int iNb_joueurs = GetLocalInt(oZone, "nbjoueurs");
// Le script de nettoyage à Icien est lancé, TEMPO secondes se sont écoulées
// Je regarde à nouveau si la zone est vide sinon, je ne nettoies pas
if (iNb_joueurs < 1) {
object oSource = GetFirstObjectInArea(oZone);
object oCrea = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oSource, 1);
object oObj;
object oItem;
int nN=1;
int iType;
if (oCrea == OBJECT_INVALID && !(GetIsPC(oSource))) {
oObj = oSource;
while (GetIsObjectValid(oObj)) {
iType = GetObjectType(oObj);
if (GetTag(oObj) == "BodyBag")//1 Supprime les Drops
{
oItem=GetFirstItemInInventory(oObj);
while(GetIsObjectValid(oItem))
{
DestroyObject(oItem);
oItem=GetNextItemInInventory(oObj);
iDrop++;
}
DestroyObject(oObj);
}
else
{
//iType = GetObjectType(oObj);
switch (iType)
{
case OBJECT_TYPE_CREATURE://2 Supprime les spawns
AssignCommand(oObj, ClearAllActions()); //Ajout
AssignCommand(oObj, DestroyObject(oObj, 1.0));
iPnj++;
break;
case OBJECT_TYPE_ITEM://3 detruit les objets par terre
AssignCommand(oObj, DestroyObject(oObj, 0.0));
iItem++;
break;
default:
break;
}
}
nN++;
oObj = GetNearestObject(OBJECT_TYPE_ALL, oSource, nN);
}
// SendMessageToPC(oPC,IntToString(iDrop)+" Drop(s) detruit(s)");
// SendMessageToPC(oPC,IntToString(iPnj)+" Creature(s) detruite(s)");
// SendMessageToPC(oPC,IntToString(iItem)+" Item(s) detruit(s)");
// SendMessageToPC(oPC,"nettoyage de la Zone: "+ GetName(oZone) +" terminé");
}
DeleteLocalInt(oZone, "init");
DeleteLocalInt(oZone, "pop");
DeleteLocalInt(oZone, "valrepop");
DeleteLocalInt(oZone, "nbjoueurs");
// SendMessageToPC(oPC,"Zone prête à etre initialisée");
}
else {
// SendMessageToPC(oPC,"Nettoyage abondonné pour la zone "+ GetName(oZone));
// SendMessageToPC(oPC,"car un joueur vient d'y entrer");
}
}
Ca, c'était pour la partie générique, on compte maintenant les joueurs dans la zone et on est capable de tout nettoyer dès qu'il n'y a plus personne.
Mais pour le moment on a généré aucun monstre.
Maintenant on va devoir créer des scripts personnalisé pour chacune des zones, car chaque zone possèdera sa propre population. (oui je sais pour les débutant c'est un peu la galère.. mais faut passer par là !

)
Le script sera placé dans le OnHeartBeat (ou script récurrent) de la zone.
Il a toujours la même structure (voir script ci dessous) :
Tout d'abord il y a une fonction de création de monstres :
void createMonstre(string sTag,string sPtSpawn)
Cette fonction a besoin de 2 paramètres :
- Le tag du monstre
- Le tag du point de Spawn
Ensuite Il existe encore une fois plusieurs variables qui vont pouvoir contrôler le fonctionnement et la population de la zone.
- Init (c'est un booleen, soit il est à 0 et l'init n'est pas faites, soit il est à 1 et la zone est initialisée)
- iMaxPop = la valeur maximum de monstres dans la zone
- iRepop = la valeur à laquelle la zone régénère des monstres
//////////////////////////////////////////////////////////////
//
// 10/02/08 Dimencia (modele)
// Zone Beckrunes (Exterieur)
//
// iMaxPop : Population maxi de la zone
// iRepop : Valeur de respawn
//
// 11/03/08
//////////////////////////////////////////////////////////////
void createMonstre(string sTag,string sPtSpawn) {
location lSpawn = GetLocation(GetObjectByTag(sPtSpawn,0));
object oMonstre = CreateObject(OBJECT_TYPE_CREATURE,sTag,lSpawn,FALSE,"");
SetLocalInt(oMonstre,"X2_L_SPAWN_USE_AMBIENT",1);
}
void main () {
//------------ Init params ---------------
//
// Valeur de population max de la zone
int iMaxPop = 30;
// temps de respawn * 6
int iRepop = 2;
//----------------------------------------
object oZone = GetArea(OBJECT_SELF);
int iNb_joueurs = GetLocalInt(oZone, "nbjoueurs");
int iInit = GetLocalInt(oZone, "init");
string sPtSpawn;
string sTag;
//
// Un joueur est au moins present et la zone déjà initialisée: on genere des monstres
//
if(iNb_joueurs > 0 && (iInit == 1 )) {
// Y a des joueurs dans la zone
// La zone est dejà init
int iPop = GetLocalInt(oZone, "pop");
int iRepop_Val = GetLocalInt(oZone, "valrepop");
iRepop_Val++;
if((iPop < iMaxPop) && (iRepop_Val >= iRepop)) {
// On defini les monstres ici
// sTag ="le tag du monstre";
// sPtSpawn = "le tag du point de spawn";
createMonstre(sTag,sPtSpawn);
iPop = iPop + 1;
SetLocalInt(oZone, "pop", iPop);
iRepop_Val=0;
}
else {
if(iRepop_Val > iRepop) {
iRepop_Val=0;
}
}
SetLocalInt(oZone, "valrepop",iRepop_Val);
}
//
// Le joueur vient d'entrer on creer tout les monstres initiaux
// Les boss, les marchands... ce qu'on veut.
//
if(iNb_joueurs > 0 && (iInit != 1 )) {
// Initialisation de la zone
SetLocalInt(oZone, "init",1);
// Ici Spawn PNJ à l'initialisation de la zone
SetLocalInt(oZone, "pop",0);
}
}
Bon d'accord, pour les novices c'est vraiment la partie la plus lourde à comprendre.
Alors voici un exemple simple. On lance l'éditeur sans grommeler
Copier/coller les 3 premiers scripts dans un module.
Ouvrez une zone exterieure par exemple et posez dessus 5 points de passage (type Waypoint)
Ouvrez à present le contenu de la zone, les proprietes.
Cliquez les points de Spawn et taggez les pt_spawn1, pt_spawn2, pt_spawn3, pt_spawn4 et pt_spawn5
Noubliez pas d'affecter les scripts du OnEnter et OnExit à la zone !
Pour le script recurent on va mettre :
int iMaxPop = 30; (30 monstres maxi)
int iRepop = 2; (12 secondes pour repeupler la zone)
sTag = "c_rat"; (le tag du monstre)
iNum = Random( 5 )+1; Le choix au hasard du point de spawn
sPtSpawn = "pt_spawn"+IntToString(iNum); l'assemblage en "string" du tag du point de spawn
Noter egalement que dans la partie init de la zone, on va generer 4 rats des l'entree du joueur dans la zone
Il ne faut pas alors oublier de mettre la variable de "pop" à jour :
SetLocalInt(oZone, "pop",4);
Ce sera la population de départ
//////////////////////////////////////////////////////////////
//
// 10/02/08 Dimencia (modele)
// Zone Exemple
//
// iMaxPop : Population maxi de la zone
// iRepop : Valeur de respawn
//
// 21/09/08
//////////////////////////////////////////////////////////////
void createMonstre(string sTag,string sPtSpawn) {
location lSpawn = GetLocation(GetObjectByTag(sPtSpawn,0));
object oMonstre = CreateObject(OBJECT_TYPE_CREATURE,sTag,lSpawn,FALSE,"");
SetLocalInt(oMonstre,"X2_L_SPAWN_USE_AMBIENT",1);
}
void main () {
//------------ Init params ---------------
//
// Valeur de population max de la zone
int iMaxPop = 30;
// temps de respawn * 6 secondes
int iRepop = 2;
//----------------------------------------
object oZone = GetArea(OBJECT_SELF);
int iNb_joueurs = GetLocalInt(oZone, "nbjoueurs");
int iInit = GetLocalInt(oZone, "init");
string sPtSpawn;
string sTag;
int iType;
int iNum;
//
// Un joueur est au moins present et la zone déjà initialisée: on genere des monstres
//
if(iNb_joueurs > 0 && (iInit == 1 )) {
// Y a des joueurs dans la zone
// La zone est dejà init
int iPop = GetLocalInt(oZone, "pop");
int iRepop_Val = GetLocalInt(oZone, "valrepop");
iRepop_Val++;
if((iPop < iMaxPop) && (iRepop_Val >= iRepop)) {
iNum = Random(5)+1;
sPtSpawn = "pt_spawn"+IntToString(iNum);
sTag = "c_rat";
createMonstre(sTag,sPtSpawn);
iPop = iPop + 1;
SetLocalInt(oZone, "pop", iPop);
iRepop_Val=0;
}
else {
if(iRepop_Val > iRepop) {
iRepop_Val=0;
}
}
SetLocalInt(oZone, "valrepop",iRepop_Val);
}
//
// Le joueur vient d'entrer on creer tout les monstres initiaux
// Les boss, les marchands... ce qu'on veut.
//
if(iNb_joueurs > 0 && (iInit != 1 )) {
// Initialisation de la zone
SetLocalInt(oZone, "init",1);
// Ici Spawn PNJ
iNum = 0;
while (iNum < 4) {
iNum = Random(5)+1;
sPtSpawn = "pt_spawn"+IntToString(iNum);
sTag = "c_rat";
createMonstre(sTag,sPtSpawn);
iNum++;
}
SetLocalInt(oZone, "pop",4);
}
}
Ah il manque un truc encore !
oui si on tue un monstre, la variable "pop" ne sera pas mis à jour et au bout d'un certain temps, plus aucun monstre ne sera generé.
C'est totalement vrai, donc sur chaque monstre qui sera generer sur votre module vous allez devoir rajouter ce script dans l'evevement OnDeath (à la mort du monstre) :
void main () {
object oZone = GetArea(OBJECT_SELF);
int iPop = GetLocalInt(oZone, "pop");
iPop = iPop - 1;
if (iPop < 0) {
iPop = 0;
}
SetLocalInt(oZone, "pop", iPop);
ExecuteScript("nw_c2_default7", OBJECT_SELF);
}
Ce script met à jour la variable "pop" de la zone et lance toujours le script par defaut de mort : nw_c2_default7
Ensuite avec ce système en utilisant des if, vous pouvez faire comme un MD, 50% de chances que ce soit un rat et 50% une chauve souris en lançant un D100 par exemple.
Il suffit de rajouter ce bout de code au niveau de l'affectation de sTag.
Ou bien encore limité la naissance des rats au nord de la zone selon la disposition de vos points de spawn.
Sur mon module, en testant l'heure, j'ai generé des loups garous la nuit et rien le jour.
Laisser votre imagination vous guider
Bon test à tous!
Un module de test est dispo à cette adresse :
http://beckrunes.info/download.php#Gen_PNJ