[Script] Système de spawn pour module persistant / semi-persistant

Répondre
Partager Rechercher
Et bien voilà, maintenant que je commence à avoir un peu de bouteille en nwscript (notamment grâce à ce forum et à ses estimés autochtones ), voici ma première et modeste contribution, qui j'espère sera utile à certains d'entre vous.

L'idée de base de ce système est de reproduire plus ou moins le système qu'utilise Ultima Online pour spawner ses créatures : un "point de spawn" est placé sur la carte, et celui-ci est paramétré pour spawner telle créature, tel nombre, etc. Je le préfère personnellement au sytème classique de rencontres par triggers de NWN, pas vraiment flexible et vraiment ch... à gérer.

Comment ça marche en pratique : on utilise des waypoints personnalisés transformés pour l'occasion en "spawnpoints", que l'on place sur la carte. On peut spawner des créatures ET des plaçables ainsi (utilité pour les plaçables : ils peuvent être détruits, déverrouillés, etc, sans souci, ils seront respawnés intacts).

Chaque "spawnpoint" possède un tag particulier du type SP_a_b_c_d :

- a est le type d'objet à spawner, créature (C), plaçable (P), spécial (S). Personnellement j'utilise le type spécial pour les PNJ majeurs et créatures uniques.
- b est le tag de l'objet à spawner.
- c est le nombre d'objets à spawner sur ce spawnpoint.
- d est la fréquence d'apparition, exprimée F_(un nombre entre 1 et 20), représentant les chances de spawn sur un jet de d20. Ce dernier paramètre peut être absent si la rencontre n'est pas aléatoire, mais planifiée (100% de chances de spawn).

Par exemple, pour spawner 1d6 orques de tag ORC et de resref orc001 sur 12 ou moins sur un jet de d20(fréquence), le tag du spawnpoint sera le suivant: SP_C_ORC_1D6_F12

Les spawnpoints sont activés dès qu'un personnage entre dans la zone, et les créatures et plaçables ainsi créés détruits dès que la zone est vide pour économiser les ressources. A noter que les spawns sont uniques, quand une zone est vide de spawns, elle le reste tant que tous les joueurs présents n'ont pas quitté la zone. Un délai personnalisable permet, éventuellement, de retarder la destruction lorsque la zone est vide, pour éviter le camping de spawns en quittant / revenant dans la zone.

Avec une liste de spawnpoints précréés dans la palette, on arrive à un système, je trouve, très souple, car on peut recréer en deux clics des "tables de rencontres" papier, de manière individuelle pour chaque point de spawn, entièrement basées sur le hasard. En retravaillant un peu le script, on peut facilement arriver à permettre de créer également plusieurs types de créatures avec un seul point de spawn.

ATTENTION : le CR et le nombre des créatures spawnées est totalement indépendant du niveau et du nombre des personnages. Tout est uniquement basé sur les paramètres des "points de spawn", que le builder peut placer comme il le souhaite dans ses zones.

Premier script, à placer sur le OnEnter de vos zones :

Code PHP:

#include "lib_spwn_crtrs"
#include "lib_spwn_plcbls"
#include "lib_spwn_spcls"

void ActivateSpawnpoint(object oSpawnpointstring sSpawnpoint)
{
    
/*On récupère les coordonnées du spawnpoint dans la zone
et le type d'objet à spawner, puis on spawne l'objet d'après
une liste dans la bibliothèque correspondante.*/

    
location lSpawnpoint GetLocation(oSpawnpoint);

    if ((
GetStringLeft(sSpawnpoint5) == "SP_C_"))
    {
        
string sCreature GetSpawnType_Creature(sSpawnpoint);

        
CreateObject(OBJECT_TYPE_CREATUREsCreaturelSpawnpoint);
    }

    else if ((
GetStringLeft(sSpawnpoint5) == "SP_P_"))
    {
        
string sPlaceable GetSpawnType_Placeable(sSpawnpoint);

        
CreateObject(OBJECT_TYPE_PLACEABLEsPlaceablelSpawnpoint);
    }

    else if ((
GetStringLeft(sSpawnpoint5) == "SP_S_"))
    {
        
string sSpecial GetSpawnType_Special(sSpawnpoint);

        
CreateObject(OBJECT_TYPE_CREATUREsSpeciallSpawnpoint);
    }
}

void SearchForSpawnpoints()
{
    
/*On fouille la zone à la recherche de spawnpoints, et on
vérifie qu'ils n'ont pas déjà spawné un objet, auquel cas on
active le spawn et on place une variable locale indiquant qu'il a
fait son job.*/

    
object oSpawnpoint GetFirstObjectInArea();

    while (
GetIsObjectValid(oSpawnpoint))
    {
        
string sSpawnpoint GetTag(oSpawnpoint);

        if ((
GetStringLeft(sSpawnpoint3) == "SP_")&&(GetLocalInt(oSpawnpoint"NO_SPAWN") == FALSE))
        {
            
SetLocalInt(oSpawnpoint"NO_SPAWN"TRUE);
            
ActivateSpawnpoint(oSpawnpointsSpawnpoint);
        }

        
oSpawnpoint GetNextObjectInArea();
    }
}

void main()
{
    
/*On vérifie qu'au moment où un personnage entre dans la
zone, aucun autre joueur n'est déjà présent. Si oui, on déclenche
le processus de spawn et on incrémente le compteur individuel
de personnages dans la zone de 1.*/

    
int nPCNumber GetLocalInt(OBJECT_SELF"PC_NUMBER");
    
object oEntering GetEnteringObject();

    if (
GetIsPC(oEntering))
    {
        
nPCNumber  ++;
        
SetLocalInt(OBJECT_SELF"PC_NUMBER"nPCNumber);

        if  (
nPCNumber == 1)
            
SearchForSpawnpoints();
    }

Les trois bibliothèques correspondantes aux trois différents types d'objet à spawner (avec quelques exemples représentatifs, à vous de modifier les tags et les resrefs correspondants). Les fonctions Random vous permettent de définir plusieurs blueprints d'une même créature (avec, par exemple, un équipement différent), dont un sera choisi aléatoirement par le script :

Créatures :

Code PHP:

string GetSpawnType_Creature(string sSpawnpoint)
{
    
int nRoll d20();
    
int nNumber;

// RANDOM ENCOUNTERS

    
if (sSpawnpoint == "SP_C_KOBOLD_1_F8")
    {
        if (
nRoll <= 8)
        {
            switch (
Random(4))
            {
                case 
: return "kobold001"; break;
                case 
: return "kobold002"; break;
                case 
: return "kobold003"; break;
                case 
: return "kobold004"; break;
            }
        }
    }

    else if (
sSpawnpoint == "SP_C_KOBOLD_1D2_F14")
    {
        if (
nRoll <= 14)
        {
            for (
nNumber d2(); nNumber 0nNumber --)
            {
                switch (
Random(4))
                {
                    case 
: return "kobold001"; break;
                    case 
: return "kobold002"; break;
                    case 
: return "kobold003"; break;
                    case 
: return "kobold004"; break;
                }
            }
        }    
    }

// PLANNED ENCOUNTERS

    
else if (sSpawnpoint == "SP_C_KOBOLD_GUARD_1")
    {
        switch (
Random(2))
        {
            case 
: return "koboldguard001"; break;
            case 
: return "koboldguard002"; break;
        }
    }

    return 
"default_crtr";

Plaçables :

Code PHP:

string GetSpawnType_Placeable (string sSpawnpoint)
{
    
// MISCELLANEOUS INTERIOR

    
if (sSpawnpoint == "SP_P_CHAIR")
        return 
"chair001";

    else if (
sSpawnpoint == "SP_P_COUCH")
        return 
"couch001";

    
// CONTAINERS

    
else if (sSpawnpoint == "SP_P_HERB")
        return 
"herb001";

    else if (
sSpawnpoint == "SP_P_FERN")
        return 
"fern001";

    return 
"default_plcbl";

Spécial :

Code PHP:

string GetSpawnType_Special(string sSpawnpoint)
{
    if (
sSpawnpoint == "SP_S_DELDUIN")
        return 
"delduin001";

    else if (
sSpawnpoint == "SP_S_WALDO")
        return 
"waldo001";

    return 
"default_spcl";

Dernier script à placer en OnExit de vos zones :

Code PHP:

void DestroyObjectsInArea()
{
    
/*Fonction de destruction de tous les objets présents dans la
zone, exceptés les créatures dont le tag commence par PERM_ et
les plaçables dont le tag commence par STATIC_*/

    
object oObjectToDestroy GetFirstObjectInArea();

    while (
GetIsObjectValid(oObjectToDestroy))
    {
        if  (
            
GetObjectType(oObjectToDestroy) == OBJECT_TYPE_ITEM ||
            
GetObjectType(oObjectToDestroy) == OBJECT_TYPE_CREATURE &&
            
GetStringLeft(GetTag(oObjectToDestroy), 5) != "PERM_" ||
            
GetObjectType(oObjectToDestroy) == OBJECT_TYPE_PLACEABLE &&
            
GetStringLeft(GetTag(oObjectToDestroy), 7) != "STATIC_"
            
)
            
DestroyObject(oObjectToDestroy);

        
oObjectToDestroy GetNextObjectInArea();
    }
}

void UpdateSpawnpoints()
{
    
//Fonction qui retire le "no spawn".

    
object oSpawnpoint GetFirstObjectInArea();

    while (
GetIsObjectValid(oSpawnpoint))
    {
        if (
GetStringLeft(GetTag(oSpawnpoint), 3) == "SP_")
            
DeleteLocalInt(oSpawnpoint"NO_SPAWN");

        
oSpawnpoint GetNextObjectInArea();
    }
}

void UpdateArea()
{

    
/*On décrémente le compteur de 1 chaque fois qu'un joueur
quitte la zone, rétroactivement selon le délai fixé en fonction
principale. Si la zone est vide à ce moment, on détruit les objets
présents et on retire la variable locale empêchant le spawn des
spawnpoints.*/

    
int nPCNumber GetLocalInt(OBJECT_SELF"PC_NUMBER");

    if (
GetIsPC(GetExitingObject()))
    {
        
nPCNumber --;
        
SetLocalInt(OBJECT_SELF"PC_NUMBER"nPCNumber);

        if (
nPCNumber == 0)
        {
            
DestroyObjectsInArea();
            
UpdateSpawnpoints();
        }
    }
}

void main()
{
    
/*Délai à modifier ou supprimer selon le temps que l'on
souhaite avant remise à zéro du système.*/

    
DelayCommand(30.0UpdateArea());

Un systeme qui se rapproche assez de ce que j'avais fait ^^

A noter surtout que Fenryss parle evidemment des resref, et non pas des TAG, pour les objets a spawner


Merci Fenryss pour cette contribution qui servira certainement a tout ceux qui cherchent des alternatives aux rencontres biowares (qui ne sont pas si mal, quand même )
En fait j'utilise les tags des objets pour plus de facilité dans l'écriture des tags de "spawnpoints", c'est la bibliothèque qui fait la liaison tag - resref (plusieurs créatures d'un même tag pouvant avoir des apparences et des équipements différents, avec chaque fois un resref distinct, pour plus de variété, tiré au hasard aussi) Il est tout à fait possible de mettre directement les resrefs.
Oki. Tu devrait preciser, alors, dans l'intro, que les creature doivent avoir un resref et un tag identique, alors.
(puisque ca s'adresse forcement plus a des gens qui scriptent peu qu'a des gens comme RAT qui de toute facon vont le reecrire juste pour le plaisir d'ajouter 1500 lignes de code...)





pataper... pataper...
Justement, les deux n'ont pas besoin d'être identiques puisque la correspondance est gérée dans la bibliothèque (en fait c'est justement pour qu'un même tag puisse générer plusieurs resrefs différents)

J'ai plus le temps là, mais je repasse plus tard regarder si je l'ai correctement expliqué.
ah oui, je comprend ce que tu veux dire. mais moi, je voulais dire que dans l'intro, tu dis ca :
Citation :
Par exemple, pour spawner 1d6 orques de tag ORC sur 12 ou moins sur un jet de d20(fréquence), le tag du spawnpoint sera le suivant: SP_C_ORC_1D6_F12
or, c'est inexact.
(mais oui, j'ai vu que le TAG du waypoint gerait des RESREF de creature.. ^^)
2 petites questions :

- Les creatures respawn au bout d'un certains tps ? et si oui où est ce parametre ? (ou faut il quitter la zone et revenir ? une fois morte)

- Ce systeme peut supporter un grd nombre de zone ? efficace niveau economie ?
Citation :
Les creatures respawn au bout d'un certains tps ? et si oui où est ce parametre ? (ou faut il quitter la zone et revenir ? une fois morte)
Le respawn s'effectue lorsqu'un joueur entre dans une zone où aucun joueur n'est présent. Il faut donc quitter la zone et revenir effectivement. A noter que le DelayCommand en fonction principale gère le temps minimum pendant lequel la zone doit être vide de joueurs pour que le respawn ait lieu.

J'en profite pour signaler un bug que j'ai découvert récemment : à cause d'un bug incompréhensible du DelayCommand, les instructions qui lui sont affectées sont perdues lors du déchargement de la zone sur le client (en français dans le texte : quand tu quittes la zone, l'instruction de respawn après le DelayCommand est "oubliée"). En attendant que ce bug soit corrigé (un jour peut-être...), il faut soit virer purement et simplement le DelayCommand, soit remplacer la totalité du script OnExit par un DelayCommand(ExecuteScript) qui pointera sur un script indépendant dans lequel se trouveront les instructions du script OnExit actuel... :/

Citation :
Ce systeme peut supporter un grd nombre de zone ? efficace niveau economie ?
A priori, je dirais oui, mais il n'a pas encore été testé à "grande" échelle.
Et si tu demandes à la zone d'effectuer la commande ?
Code PHP:

AssignCommand(GetArea(OBJECT_SELF), DelayCommand(30.0UpdateArea()));

// ou au module

AssignCommand(GetModule(), DelayCommand(30.0UpdateArea())); 
Tiens j'avais pas pensé à ça, merci Archamedes. Je n'aurai malheureusement pas la possibilité de tester ça avant un bon moment, j'ai comme qui dirait un "empêchement professionnel de longue durée"
Répondre

Connectés sur ce fil

 
1 connecté (0 membre et 1 invité) Afficher la liste détaillée des connectés