Bonjour à tous !
voilà j'ai terminé un script qui a pour but d'améliorer les modules persistants (ou pas
)
La reflexion vient de deux constats :
- Le Boss d'un donon, méchant pas beau par excellence, se fait défoncer la tronche 5 à 6 fois par jour pendant des semaines voir des mois.. Il doit donc se douter que son donjon est tout pourri et que les aventuriers le parcours trop facilement
ou alors il a vraiment pas de chance et la réunion internationale des Gros Bills se tient juste à côté de chez lui...
bref il faut qu'il change son donjon
- Dans une ville les gens gardent la même maison ad vitam eternam...
bref tout ceci entraîne de la monotonie chez le joueur (j'ai été joueur, style GB... et les technique pour éviter tous les pièges genre 2 pas à droite, 4 à gauche et 3 devant, je connais
) et il faut au(x) concepteur(s) beaucoup de travail pour refaire les donjons.. Mais 2eme constats : quand on changeait les donjons, je les redécouvrait avec autant de plaisir qu'un môme qui ouvre ses paquets de noël
donc j'ai fais un script pour que les transitions soient calculées aléatoirement et stockées sur une base de données que l'on peut effacer à tout moment. Il subsiste 1 problèmes et 2 contraintes malheureuses pour le MD
PROBLEME : le joueur apparait face à la porte (ça marche aussi avec des zones de transition) quand il arrive dans la nouvelle zone... je sais pas pourquoi, pourtant j'ai essayé de modifier la direction avec un +180.0 ou -180.0 mais rien n'y fait.. bon au final c'est pas trop grave, le joueur doit se retourner mais bon c'est chiant.. si quelqu'un trouve la solution
CONTRAINTES :
- en générant les zones de transitions aléatoirement, le donjon peut etre tout petit... genre la zone de fin dès le début. Les maisons peuvent être de la même façon gigantesques. Mais le problème n'est que statistique et il suffit de générer assez de zones afin de le limiter. Il se peut aussi qu'il y ait des zones inutilisées (j'expliquerai ensuite comment faire pour limiter ceci)
- Pour éviter les incohérences le donjon ou les maisons doivent soit s'enfoncer soit monter mais pas les deux (les donjons c'est moins grave car s'ils sont magiques c'est possibles).. En gros le truc c'est de décider d'un même système d'étage (les escaliers soit avant, soit après la transitions, mais pas les deux). Sinon on peut se retrouver avec un accès à la cave ou au sous sol par le troisièmes étage
LE SYSTEME : les donjons / maisons / zones sont définies par des portes ou des zones de transitions, et le donjon doit avoir une architecture comme telle : porte de niveau N => porte de niveau n+1. Dans la même zone que la porte n+1, une porte de niveau N => porte de niveau N+1 etc etc..
n peut le faire sur une même grande zone ou bien des zones séparées
Donc les portes d'entrée sont les portes n+1 et les portes de sorties les porte n
On peut faire des cul de sac, avec un partie qu'avec une porte d'entrée. Si on veut exploiter toutes les zones, il faut un nombre de portes n+1 supérieur à des portes de niveau n. (c'est compliqué hein
) un petit schéma grâce à mes talents de dessinateurs impressionants )
LES PREREQUIS
- faire des portes et/ou zones de transitions avec untag de base unique. Rajouter ET pour une entrée de zone à la fin du tag et RC pour une sortie de zone. ensuite numérotée les portes/transitions avec 01, 02, 03, etc
EXEMPLE : une porte avec pour tag de base TAGPORTE
Donc les portes d'entrée auront pour tag TAGPORTEET01, TAGPORTEET02, TAGPORTEET03, etc et les portes de sortie TAGPORTERC01, TAGPORTERC02, TAGPORTERC03, etc..
remarquez que dans ce système la porte d'entrée même du donjon (s'il est complèteent aléatoire) sera une porte de sortie en fait
Mettre le script de base sur le OnClick des portes/transitions
Pour les portes : changer les propriétés et NE PAS METTRE "aucune transition" (pas besoin de renseigner le tag de la cible), sinon l'évenement OnClick ne fonctionnera pas. Bien sûr le Onclick ne fonctionne que sur une porte OUVERTE (donc il faut la dévérouiller et désarmer avant
)
J'ai pris des fonctions du script nw_io_position pour les modifier dès que je saurai comment faire se retourner ce joueur
Script de bibliothèque
// pris de nw_io_position
// **********************************
float GetChangeInX(float fDistance, float fAngle);
float GetChangeInY(float fDistance, float fAngle);
vector GetChangedPosition(vector vOriginal, float fDistance, float fAngle);
location GenerateNewLocation(object oTarget, float fDistance, float fAngle, float fOrientation);
// **********************************
// renvoie la location à 130°de la porte ouverte : 130 ° fonctionne avec tout
// devant la porte pour un joueur, c'est en fait derrière pour le programe
location KTXGetBehindDoor (object oTarget, float fDistance=1.0);
// renvoie "01", "02", "03" depuis un entier 1, 2 , 3
string ComptToString (int iCompt);
// compte le nombre de porte et l'enregistre sur le module
void CalculPorte(string sTag);
// renvoie le résultat d'une boucle décalée aléatoire
int GetBoucle (int iNbBoucle, int iRandomBoucle, int iBoucleMax);
// non utilisé
int BackBoucle (int iNbBBoucle, int iBBoucleMax);
// renvoi "ET" ou 3RC3 à partir du tag de la porte
string GetTypePorte(object oPorteInit);
// renvoie le tag de base de la porte
string GetTagBase (object obase);
calcul la cible à partir de la porte
object SetDestination (object oPorte);
const string sBDD = "BASEdeDONNEES";
location KTXGetBehindDoor(object oTarget, float fDistance = 1.0)
{
float fDir = GetFacing(oTarget);
float fAngleOpposite = GetFacing(oTarget) - 130.0;
return GenerateNewLocation(oTarget,
fDistance,
fAngleOpposite,
fDir);
}
location GenerateNewLocation(object oTarget, float fDistance, float fAngle, float fOrientation)
{
object oArea = GetArea(oTarget);
vector vNewPos = GetChangedPosition(GetPosition(oTarget),
fDistance,
fAngle);
return Location(oArea, vNewPos, fOrientation);
}
vector GetChangedPosition(vector vOriginal, float fDistance, float fAngle)
{
vector vChanged;
vChanged.z = vOriginal.z;
vChanged.x = vOriginal.x + GetChangeInX(fDistance, fAngle);
if (vChanged.x < 0.0)
vChanged.x = - vChanged.x;
vChanged.y = vOriginal.y + GetChangeInY(fDistance, fAngle);
if (vChanged.y < 0.0)
vChanged.y = - vChanged.y;
return vChanged;
}
// This returns the change in X coordinate that should be made to
// cause an object to be fDistance away at fAngle.
float GetChangeInX(float fDistance, float fAngle)
{
return fDistance * cos(fAngle);
}
// This returns the change in Y coordinate that should be made to
// cause an object to be fDistance away at fAngle.
float GetChangeInY(float fDistance, float fAngle)
{
return fDistance * sin(fAngle);
}
string ComptToString (int iCompt)
{
string sCompt;
if (iCompt < 10) sCompt = "0" + IntToString (iCompt);
else sCompt = IntToString (iCompt);
return sCompt;
}
void CalculPorte (string sTag)
{
string stNb;
int tNb = 1;
object oPorte = GetObjectByTag (sTag+ComptToString (tNb));
while (oPorte != OBJECT_INVALID)
{
tNb ++;
oPorte = GetObjectByTag (sTag + ComptToString(tNb));
}
tNb --;
SetLocalInt (GetModule(),sTag + "nb",tNb);
}
// prend une boucle et donne le résultat sur une boucle décalée
// Rajoute iRandomBoucle tout en restant en dessous du max
// sert à se servir d'une fonction aléatoire avec condition
// sans risquer de toujours tomber toujours sur un mauvais objet
int GetBoucle (int iNbBoucle, int iRandomBoucle, int iBoucleMax)
{
int iResult = iRandomBoucle + iNbBoucle ;
if (iResult > iBoucleMax) iResult = iResult - iBoucleMax;
return iResult;
}
// revient au résultat initial en partant de la boucle décalé de iNbBBoucle
int BackBoucle (int iNbBBoucle, int iBBoucleMax)
{
int iResult = iNbBBoucle - iBBoucleMax;
if (iResult <= 0) iResult = iResult + iBBoucleMax;
return iResult;
}
string GetTypePorte(object oPorteInit)
{
string sTagoPorte = GetTag(oPorteInit);
// prend les caractères 3-4 en partant de la droite pour le style
// ("RC" et "ET" suivant leurs destinations)
string sType = GetStringLeft(GetStringRight(sTagoPorte,4),2);
return sType;
}
string GetTagBase (object obase)
{
string sbase = GetTag(obase);
string sTagBase = GetStringLeft (sbase ,GetStringLength(sbase)-4);
return sTagBase;
}
object SetDestination (object oPorte)
{
object oDest = OBJECT_INVALID;
string sTypeDest;
int FlagAttrib = GetCampaignInt (sBDD,GetTag(oPorte)+"attrib");
if (FlagAttrib == TRUE)
{
string sTagDest = GetCampaignString (sBDD,GetTag(oPorte)+"dest");
oDest = GetObjectByTag (sTagDest);
}
else
{
if (GetTypePorte(oPorte) == "ET") sTypeDest = "RC";
if (GetTypePorte(oPorte) == "RC") sTypeDest = "ET";
string sTagBase = GetTagBase (oPorte) + sTypeDest;
int iNbrPrt = GetLocalInt (GetModule(), sTagBase +"nb");
if (iNbrPrt == 0)
{
CalculPorte (sTagBase);
iNbrPrt = GetLocalInt (GetModule(), sTagBase +"nb");
}
FlagAttrib = FALSE;
int FlagDest;
int tNb = 0;
int iRand = Random (iNbrPrt) +1;
while ((tNb <= iNbrPrt)&&(FlagAttrib == FALSE))
{
tNb ++;
oDest = GetObjectByTag (sTagBase + ComptToString (GetBoucle (tNb, iRand, iNbrPrt)));
FlagDest = GetCampaignInt (sBDD,GetTag(oDest)+"attrib");
if ((FlagDest == FALSE)&&(oDest!= OBJECT_INVALID))
{
SetCampaignInt (sBDD,GetTag(oDest)+"attrib",TRUE);
SetCampaignInt (sBDD,GetTag(oPorte)+"attrib",TRUE);
SetCampaignString (sBDD,GetTag(oDest)+"dest",GetTag(oPorte));
SetCampaignString (sBDD,GetTag(oPorte)+"dest",GetTag(oDest));
FlagAttrib = TRUE;
}
}
}
return oDest;
}
et enfin le script à metre sur les event OnClick
#include "ktx_portealeat_include" // remplacer par le nom du script de bibliothèque
void main ()
{
object oPCclk = GetClickingObject();
string sTag = GetTag(OBJECT_SELF);
object oDestination = SetDestination (OBJECT_SELF);
AssignCommand (oDestination, ActionOpenDoor(oDestination));
AssignCommand (oPCclk, ClearAllActions ());
location lRandomLocation = KTXGetBehindDoor(oDestination);
DelayCommand (1.0, AssignCommand (oPCclk, JumpToLocation (lRandomLocation)));
// j'ai mis un delay d'une seconde mais on peut l'enlever
}