voxel-maze
Version:
Creates mazes and translates in voxels
529 lines (408 loc) • 17.1 kB
JavaScript
/*-------------------------------------------------------------------------------------------------------------------
ADD-ON EXPORT
--------------------------------------------------------------------------------------------------------------------*/
module.exports = function(game, opts) {
// TODO add some code :D
this.game = game
this.width = his.opts.width || 5
this.height = this.opts.height || 5
return new Labyrinthe(this.width, this.height)
}
module.exports.Labyrinthe = Labyrinthe
function Labyrinthe(game, opts) {
// protect against people who forget 'new'
if (!(this instanceof Labyrinthe)) return new Labyrinthe(game, opts)
// we need to store the passed in variables on 'this'
// so that they are available to the .prototype methods
this.game = game
this.opts = opts || {}
this.width = his.opts.width || 5
this.height = this.opts.height || 5
}
/*-------------------------------------------------------------------------------------------------------------------
CELLULE
--------------------------------------------------------------------------------------------------------------------*/
var Cellule = function(){
/* différent mur, 1 = mur et 0 = pas de mur*/
this.nord = 1; // mur nord
this.ouest = 1; // mur ouest
this.sud = 1; // mur sud
this.est = 1; // mur est
this.visited = 0; // indique si la case a déjà été visitée ou non (1 = visitée, 0 = pas visitée)
}
/*-------------------------------------------------------------------------------------------------------------------
LABYRINTHE
--------------------------------------------------------------------------------------------------------------------*/
var Labyrinthe = function(width, height){
this.tabLaby = new Array(); // tableau Labyrinthe contenant les celulles
this.pileLaby = new Array(); // tableau pile contenant les coordonnées
this.width = width; // largeur du tableau
this.height = height; //hauteur du tableau
}
/* fonction principale : génére un labyrinthe aléatoirement */
Labyrinthe.prototype.generationAlea = function(){
this.init(); // initialisation du labyrinthe
this.caseDepart(); // choix de la case départ
/* tant qu'on ne revient pas à la cellule de départ et qu'il n'y a pas de vosin de libre*/
do{
if( this.aVoisinLibre() ){
var newVoisin = this.choixVoisin(); //on choisit un nouveau voisin aléatoirement
this.casserMur(newVoisin.newPosX, newVoisin.newPosY); // on casse les murs en fonction
this.tabLaby[newVoisin.newPosY][newVoisin.newPosX].visited = 1; // la nouvelle cellule est considéré comme visitée
/*stockage de la nouvelle cellule dans la pile*/
this.pileLaby[this.pileLaby.length] = [newVoisin.newPosX, newVoisin.newPosY];
}
else{
this.pileLaby.pop();
}
}while( this.pileLaby.length > 0)
}
/* initialisation du labyrinthe */
Labyrinthe.prototype.init = function(){
for(var i = 0; i < this.height; i++){
this.tabLaby[i] = new Array();
for(var j = 0; j < this.width; j++){
this.tabLaby[i][j] = new Cellule();
}
}
}
/* case départ du labyrinthe */
Labyrinthe.prototype.caseDepart = function(){
var randX = parseInt(Math.random()*this.width); // choix aléatoire d'une position X
var randY = parseInt(Math.random()*this.height); // choix aléatoire d'une position Y
this.tabLaby[randY][randX].visited = 1; // la case départ est visitée
/* placement de cette cellule dans la pile*/
this.pileLaby[0] = new Array();
this.pileLaby[0][0] = randX;
this.pileLaby[0][1] = randY;
}
/* vérification si cellule en cours a un voisin */
Labyrinthe.prototype.aVoisinLibre = function(){
posX = this.pileLaby[this.pileLaby.length-1][0]; // la position X de la cellule en cours
posY = this.pileLaby[this.pileLaby.length-1][1]; // la position Y de la cellule en cours
/* on vérifie la case de gauche */
if(posX > 0 && this.tabLaby[posY][posX-1].visited == 0){
return true;
}
/* on vérifie la case de droite */
if(posX != this.width-1 && this.tabLaby[posY][posX+1].visited == 0){
return true;
}
/* on vérifie la case du haut*/
if(posY > 0 && this.tabLaby[posY-1][posX].visited == 0){
return true;
}
/* on vérifie la case du bas*/
if(posY != this.height-1 && this.tabLaby[posY+1][posX].visited == 0){
return true;
}
/*si pas de voisin de libre*/
return false;
}
/* choix aléatoire du voisin */
Labyrinthe.prototype.choixVoisin = function(){
var randVoisin; //nombre aléatoire pour choisir le nouveau voisin
var stockage = this.stockVoisin(); //stockage des voisins
randVoisin = parseInt(Math.random()*(stockage.length)); //choix d'un voisin
return {newPosX : stockage[randVoisin][0], newPosY : stockage[randVoisin][1]};
}
/* stocke tous les voisins possibles de la cellules en cours */
Labyrinthe.prototype.stockVoisin = function(){
posX = this.pileLaby[this.pileLaby.length -1][0]; // la position X de la cellule en cours
posY = this.pileLaby[this.pileLaby.length -1][1]; // la position Y de la cellule en cours
var tab = new Array();
var i = 0;
/* stock la case de gauche si libre */
if(posX != 0 && this.tabLaby[posY][posX-1].visited == 0){
tab[i] = new Array();
tab[i][0] = posX-1;
tab[i][1] = posY;
i++;
}
/* stock la case de droite si libre*/
if(posX != this.width-1 && this.tabLaby[posY][posX+1].visited == 0){
tab[i] = new Array();
tab[i][0] = posX+1;
tab[i][1] = posY;
i++;
}
/* stock la case du haut si libre*/
if(posY != 0 && this.tabLaby[posY-1][posX].visited == 0){
tab[i] = new Array();
tab[i][0] = posX;
tab[i][1] = posY-1;
i++;
}
/* stock la case du bas si libre*/
if(posY != this.height-1 && this.tabLaby[posY+1][posX].visited == 0){
tab[i] = new Array();
tab[i][0] = posX;
tab[i][1] = posY+1;
}
return tab;
}
/* détruit le mur de la cellule en courss ainsi que du nouveau voisin */
Labyrinthe.prototype.casserMur = function(newVoisinX, newVoisinY){
posX = this.pileLaby[this.pileLaby.length -1][0]; // la position X de la cellule en cours
posY = this.pileLaby[this.pileLaby.length -1][1]; // la position Y de la cellule en cours
newPosX = newVoisinX; // la position X du voisin choisi
newPosY = newVoisinY; // la position Y du voisin choisi
/* si nouveau voisin correspond à la cellule gauche de la cellule en cours*/
if (newPosX == posX-1 && newPosY == posY){
this.tabLaby[posY][posX].ouest = 0; //on casse le mur ouest de la cellule en cours
this.tabLaby[newPosY][newPosX].est = 0; //on casse le mur est de la nouvelle cellule
}
/* si nouveau voisin correspond à la cellule droite de la cellule en cours*/
if (newPosX == posX+1 && newPosY == posY){
this.tabLaby[posY][posX].est = 0; //on casse le mur est de la cellule en cours
this.tabLaby[newPosY][newPosX].ouest = 0; //on casse le mur ouest de la nouvelle cellule
}
/* si nouveau voisin correspond à la cellule haute de la cellule en cours*/
if ( newPosX == posX && newPosY == posY-1){
this.tabLaby[posY][posX].nord = 0; //on casse le mur nord de la cellule en cours
this.tabLaby[newPosY][newPosX].sud = 0; //on casse le mur sud de la nouvelle cellule
}
/* si nouveau voisin correspond à la cellule basse de la cellule en cours*/
if (newPosX == posX && newPosY == posY+1){
this.tabLaby[posY][posX].sud = 0; //on casse le mur sud de la cellule en cours
this.tabLaby[newPosY][newPosX].nord = 0; //on casse le mur nord de la nouvelle cellule
}
}
/* résolution automatique du Labyrinthe */
Labyrinthe.prototype.reso = function(departX, departY, finX, finY){
var posX = departX;
var posY = departY;
//initialisation des cellules visitées dans le Labyrinthe
for(var i = 0; i < this.height; i++){
for(var j = 0; j < this.width; j++){
this.tabLaby[i][j].visited = 0;
}
}
this.pileLaby[0] = [posX, posY]; // on initialise la pile avec la case de départ
this.tabLaby[posY][posX].visited = 1; // le départ est déjà visitée
do{
//on vérifie la case de gauche
if(posX > 0 && this.tabLaby[posY][posX-1].visited == 0 && this.tabLaby[posY][posX].ouest == 0){
posX = posX-1; // la position change
this.tabLaby[posY][posX].visited = 1; // la case devient visitée
this.pileLaby[this.pileLaby.length] = [posX, posY]; //on met à jour la pile
}
/* on vérifie la case de droite */
else if(posX != this.width-1 && this.tabLaby[posY][posX+1].visited == 0 && this.tabLaby[posY][posX].est == 0){
posX = posX+1; // la position change
this.tabLaby[posY][posX].visited = 1; // la case devient visitée
this.pileLaby[this.pileLaby.length] = [posX, posY]; //on met à jour la pile
}
/* on vérifie la case du haut*/
else if(posY > 0 && this.tabLaby[posY-1][posX].visited == 0 && this.tabLaby[posY][posX].nord == 0){
posY = posY-1; // la position change
this.tabLaby[posY][posX].visited = 1; // la case devient visitée
this.pileLaby[this.pileLaby.length] = [posX, posY]; //on met à jour la pile
}
/* on vérifie la case du bas*/
else if(posY != this.height-1 && this.tabLaby[posY+1][posX].visited == 0 && this.tabLaby[posY][posX].sud == 0){
posY = posY+1; // la position change
this.tabLaby[posY][posX].visited = 1; // la case devient visitée
this.pileLaby[this.pileLaby.length] = [posX, posY]; //on met à jour la pile
}
/* si aucun voisin possible on retourne en arriere*/
else{
/* on ne ne soustrait que lorsqu'elle ne sera pas vide */
if(this.pileLaby.length>1){
this.pileLaby.pop(); // retour dans la pile
/* mise à jour des nouvelles coordonnées en cours */
posX = this.pileLaby[this.pileLaby.length-1][0];
posY = this.pileLaby[this.pileLaby.length-1][1];
}
}
}while(posX != finX || posY != finY) // la boucle s'effectue tant qu'on est pas arrivé à la case départ
return this.pileLaby;
}
/*
Pour passer de la 2D à la 3D, il faut changer l'échelle du labyrinthe.
On compte les lignes ( murs Horizontaux) composant le labyrinthe pour faire une map binaire.
*/
Labyrinthe.prototype.compterMursHorizontaux = function() {
// 0 = pas de block, 1 = block;
// 1 mur = une case;
var murHorizontal = [0,1,1,1,1,1,1,0];
var murCasse = [0,0,0,0,0,0,0,0];
this.mursHorizontaux = new Array();
for ( var ligne = 0; ligne <= this.tabLaby.length; ligne++){
this.mursHorizontaux[ligne] = new Array();
for (var index = 0; index < this.tabLaby[0].length; index++){
if ( ligne < this.tabLaby.length){
if ( this.tabLaby[ligne][index].nord == 1 ) {
this.mursHorizontaux[ligne].push(1,1,1,1,1,1,1,1);
}
else if ( this.tabLaby[ligne][index].nord == 0) {
this.mursHorizontaux[ligne].push(0,0,0,0,0,0,0,0);
}
}
else {
if ( this.tabLaby[ligne-1][index].sud == 1 ) {
this.mursHorizontaux[ligne].push(1,1,1,1,1,1,1,1);
}
else if ( this.tabLaby[ligne-1][index].sud == 0) {
this.mursHorizontaux[ligne].push(0,0,0,0,0,0,0,0);
}
}
}
}
}
/*
Puis on compte les colonnes / murs verticaux
*/
Labyrinthe.prototype.compterMursVerticaux = function() {
var murVertical = [0,1,1,1,1,1,1,0];
var murCasse = [0,0,0,0,0,0,0,0];
this.mursVerticaux = new Array();
for ( var colonne = 0; colonne <= this.tabLaby[0].length; colonne++){
this.mursVerticaux[colonne] = new Array();
for (var index = 0; index < this.tabLaby[0].length; index++){
if ( colonne < this.tabLaby[0].length ){
if ( this.tabLaby[index][colonne].ouest == 1 ) {
this.mursVerticaux[colonne].push(1,1,1,1,1,1,1,1);
}
else if ( this.tabLaby[index][colonne].ouest == 0) {
this.mursVerticaux[colonne].push(0,0,0,0,0,0,0,0);
}
}
else {
if ( this.tabLaby[index][colonne-1].est == 1 ) {
this.mursVerticaux[colonne].push(1,1,1,1,1,1,1,1);
}
else if ( this.tabLaby[index][colonne-1].est == 0) {
this.mursVerticaux[colonne].push(0,0,0,0,0,0,0,0);
}
}
}
}
}
/*
Dessine une portion du labyrinthe en 3D
game = sert à passer le canvas 3D entre les fonctions
chunkedHz = où commence la portion à tracer horizontalement
chunkedVt = où commence la portion à tracer verticalement
*/
Labyrinthe.prototype.draw3D = function(chunkedHz, chunkedVt) {
var chunkedHzMax = chunkedHz*4 + 4;
var chunkedVtMax = chunkedVt*4 + 4;
for (var colonne = chunkedVt*4; colonne <= chunkedVtMax; colonne++){
for (var index = chunkedHz * 8; index <= chunkedHzMax * 8; index++){
if ( colonne < this.mursVerticaux.length){
if ( this.mursVerticaux[colonne][index] == 1 ){
this.game.createBlock([colonne*8, 2, index], 'brick' );
this.game.createBlock([colonne*8, 3, index], 'grass' );
this.game.createBlock([colonne*8, 4, index], 'grass' );
this.game.createBlock([colonne*8, 5, index], 'brick' );
}
}
}
}
for ( var ligne = chunkedHz * 4; ligne <= chunkedHzMax ; ligne++){
for ( index = chunkedVt * 8; index <= chunkedVtMax *8; index++){
if ( ligne < this.mursHorizontaux.length) {
if ( this.mursHorizontaux[ligne][index] == 1 ){
this.game.createBlock([index, 2, ligne*8], 'brick' );
this.game.createBlock([index, 3, ligne*8], 'grass' );
this.game.createBlock([index, 4, ligne*8], 'grass' );
this.game.createBlock([index, 5, ligne*8], 'brick' );
}
}
}
}
}
/*----------------------------------------------------------------------------------------------------------------------
crée un chemin entre le joueur et la sortie du labyrinthe
-----------------------------------------------------------------------------------------------------------------------*/
Labyrinthe.prototype.resoudre = function(game) {
var reso = this.reso(unconvert(departX), unconvert(departZ), finX, finZ)
for (var i = 0; i < reso.length; i++){
this.game.createBlock([convert(reso[i][0]), 8, convert(reso[i][1])], 'brick');
}
}
/*----------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------*/
document.getElementById("new_game").addEventListener('click', newGame);
document.getElementById("continuer").addEventListener('click', continuer);
document.getElementById("quitter").addEventListener('click', quitter);
window.addEventListener('keydown', keyDown, true);
/*----------------------------------------------------------------------------------------------------------------------
crée une minimap en 2D, à partir de divs
-----------------------------------------------------------------------------------------------------------------------*/
Labyrinthe.prototype.minimap = function () {
var marg = 0;
var div = document.createElement('div');
for( var i = 0; i < this.height; i++){
for (var j = 0; j < this.width; j++){
var cellId = "cell"+cpt;
var inner = document.createElement('div');
inner.setAttribute('id', cellId);
inner.setAttribute('class', 'cellule');
inner.style.marginLeft = marg+"px";
inner.style.marginTop = i*10+"px";
if(this.tabLaby[i][j].nord == 1){
inner.style.borderTop = "1px solid";
}
if(this.tabLaby[i][j].sud == 1){
inner.style.borderBottom = "1px solid";
}
if(this.tabLaby[i][j].ouest == 1){
inner.style.borderLeft = "1px solid";
}
if(this.tabLaby[i][j].est == 1){
inner.style.borderRight = "1px solid";
}
div.appendChild(inner);
marg = marg + 10;
cpt++;
}
marg = 0;
}
document.body.appendChild(div);
}
// convertit unité cellule en unité voxel
convert = function(pos){
return pos*8+4;
}
//convertit unité voxel en unité cellule
unconvert = function(pos){
return parseInt(pos/8);
}
function keyDown(evt) {
if ( evt.keyCode == 76) {
quitter();
console.log('key down');
}
}
/*----------------------------------------------------------------------------------------------------------------------
Création d'un labyrinthe, définition de variables globales
-----------------------------------------------------------------------------------------------------------------------*/
var departX = 0;
var departZ = 0;
var finX = Maze.width-1;
var finZ = Maze.height-1;
var cpt = 0;
var marg = 0;
var tilemap3D = new Array();
/*----------------------------------------------------------------------------------------------------------------------
Fonctions relatives à la partie et à l'initialisation 3D
-----------------------------------------------------------------------------------------------------------------------*/
function newGame(aMaze) {
var containerLaby = document.getElementById("scene");
aMaze.generationAlea();
document.getElementById("menu").style.display = 'none';
document.getElementById("quitter").style.display = 'block';
jouer();
}
function continuer(aMaze) {
document.getElementById("menu").style.display = 'none';
document.getElementById("quitter").style.display = 'block';
jouer();
}
function quitter() {
document.getElementById("scene").removeChild(document.getElementById("scene").firstChild);
document.getElementById("quitter").style.display = 'none';
document.getElementById("menu").style.display = 'block';
}