xxxxxxxxxx
895
// this is a randomly generated maze with a player that can be freely navigated
// this iteration adds sound effects!
nunits = 7;
maxfactor = 15;
factormode = 0 ; // 0: look for factors. 1: look for products
//global constants
let space;
let rightborder, downboarder;
let rows, columns;
let winstate = 0, wintoggle=0; // 0 = playing 1 = win 2 = lose
let pickuplist = [];
let enemylist = [];
let mazemap; //0 = empty -1 = portal other integers = index of pickup
let enemymap; //-1 = empty, integer = index of enemy
let portal, hero, shot;
// assets
let herowalksound, spiderwalksound, spiderkillsound, wallhitsound, bombsound, winsound, losesound, pickupsound, dropsound, portaldropsound;
// utility functions
function initArray(rows, columns, value){
//create a dynamic array of size rows x columns and fill it with values
let a = new Array(rows);
for (var i=0;i<a.length;i++){
a[i] = new Array(columns);
for (var j=0;j<a[i].length;j++){
a[i][j] = value;
}
}
return a;
}
function rand(maxval){
return ~~(Math.random()*maxval);
}
// classes
class Portal{
constructor(row, col, factor1, factor2){
this.row = row;
this.col = col;
this.toggle = 0;
this.blinkspeed = 10;
this.factor1 = factor1;
this.factor2 = factor2;
this.value = 1;
if (factormode==0){
this.string = (factor1*factor2).toString();
} else {
this.string = factor1.toString() + ' x ' + factor2.toString();
}
this.state = 0;//modes: 0: initial state 1: intermediate state (something in the portal but expecting more 2: correct state 3: wrong state)
this.intermediate='';
mazemap[row][col] = -1;
}
drop(value){
portaldropsound.play()
this.value = this.value*value;
if (this.value==this.factor1*this.factor2){
winstate = 1;
}
else {
if (factormode==1){
winstate = 2;
}
else {
if (this.state==0){
this.state = 1;
this.intermediate = value.toString();
}
else {
winstate = 2;
}
}
}
}
draw(){
mazemap[this.row][this.col] = -1;
[x1,y1] = topleftpoint(this.row, this.col);
[x2,y2] = bottomrightpoint(this.row, this.col);
x1 = x1 + space/10;
y1 = y1+space/10;
x2 = x2-space/10;
y2 = y2-space/10;
this.toggle++;
if (this.toggle<this.blinkspeed){
if (this.state==0){
stroke(0);
} else if (this.state==1){
stroke(100,0,200);
} else if (this.state==2){
stroke(0,200,100);
} else {
stroke(255,0,0);
}
fill(255);
}
else if (this.toggle<this.blinkspeed*2){
stroke(255);
fill(0);
}
else if (this.toggle==this.blinkspeed*2){
this.toggle=0;
}
strokeWeight(space/20);
//fill(100,0,255);
rectMode(CORNERS);
rect(x1,y1,x2,y2);
if (this.toggle<this.blinkspeed){
fill(0);
} else {
fill(255);
}
textAlign(CENTER);
[x1,y1] = middlepoint(this.row, this.col);
strokeWeight(space/30);
if (factormode==0){
textSize(space/2.5);
text(this.string,x1,y1+space/8);
}
else {
textSize(space/4);
text(this.string, x1, y1);
}
if (this.state==1){
textSize(space/4);
strokeWeight(0);
fill(0,200,100);
[x1,y1] = bottomrightpoint(this.row, this.col);
text(this.intermediate, x1-space/5, y1-space/5);
}
}
}
class Enemy{
constructor(index){
[row, col] = getEmpty();
this.row = row;
this.col = col;
this.index = index;
enemymap[this.row][this.col] = this.index;
this.orientation = 3; // 0=top, 1=bottom, 2=left, 3=right
this.anisteps = 20 + rand(6)-3;
this.animation = rand(this.anisteps);
this.anitoggle=1;
this.movecycle = 50;
this.movesteps = rand(this.movecycle);
this.enabled=1;
}
move(){
let movelist = [];
if (this.row>0){
if (downborder[this.row-1][this.col]==0){
if (mazemap[this.row-1][this.col]==0){
movelist[movelist.length] = [this.row-1, this.col,0];
}
}
}
if (this.row<rows-1){
if (downborder[this.row][this.col]==0){
if (mazemap[this.row+1][this.col]==0){
movelist[movelist.length] = [this.row+1, this.col,1];
}
}
}
if (this.col>0){
if (rightborder[this.row][this.col-1]==0){
if(mazemap[this.row][this.col-1]==0){
movelist[movelist.length] = [this.row, this.col-1, 2];
}
}
}
if (this.col<columns-1){
if (rightborder[this.row][this.col]==0){
if(mazemap[this.row][this.col+1]==0){
movelist[movelist.length] = [this.row, this.col+1,3];
}
}
}
if (movelist.length>0){
//spiderwalksound.play();
index = rand(movelist.length);
[this.row, this.col, this.orientation] = movelist[index];
}
}
drawUp(xx,yy){
let yy1 = yy-space/8;
let yy2 = yy+space/8;
let x,y;
[x,y] = topleftpoint(this.row, this.col);
line(x+space/8,y+space/8,xx,yy2);
[x,y] = toprightpoint(this.row, this.col);
line(x-space/8,y+space/8,xx,yy2);
[x,y] = bottomleftpoint(this.row, this.col);
line(x+space/8,y-space/8,xx,yy1);
[x,y] = bottomrightpoint(this.row, this.col);
line(x-space/8,y-space/8,xx,yy1);
circle(xx, yy1, space/4);
circle(xx, yy2, space/3);
}
drawDown(xx,yy){
let yy1 = yy+space/8;
let yy2 = yy-space/8;
let x,y;
[x,y] = topleftpoint(this.row, this.col);
line(x+space/8,y+space/8,xx,yy1);
[x,y] = toprightpoint(this.row, this.col);
line(x-space/8,y+space/8,xx,yy1);
[x,y] = bottomleftpoint(this.row, this.col);
line(x+space/8,y-space/8,xx,yy2);
[x,y] = bottomrightpoint(this.row, this.col);
line(x-space/8,y-space/8,xx,yy2);
circle(xx, yy1, space/4);
circle(xx, yy2, space/3);
}
drawLeft(xx,yy){
let xx1 = xx-space/8;
let xx2 = xx+space/8;
let x,y;
[x,y] = topleftpoint(this.row, this.col);
line(x+space/8,y+space/8,xx1,yy);
[x,y] = toprightpoint(this.row, this.col);
line(x-space/8,y+space/8,xx2,yy);
[x,y] = bottomleftpoint(this.row, this.col);
line(x+space/8,y-space/8,xx1,yy);
[x,y] = bottomrightpoint(this.row, this.col);
line(x-space/8,y-space/8,xx2,yy);
circle(xx1, yy, space/4);
circle(xx2, yy, space/3);
}
drawRight(xx,yy){
let xx1 = xx+space/8;
let xx2 = xx-space/8;
let x,y;
[x,y] = topleftpoint(this.row, this.col);
line(x+space/8,y+space/8,xx2,yy);
[x,y] = toprightpoint(this.row, this.col);
line(x-space/8,y+space/8,xx1,yy);
[x,y] = bottomleftpoint(this.row, this.col);
line(x+space/8,y-space/8,xx2,yy);
[x,y] = bottomrightpoint(this.row, this.col);
line(x-space/8,y-space/8,xx1,yy);
circle(xx1, yy, space/4);
circle(xx2, yy, space/3);
}
draw(){
if (this.enabled==1){
enemymap[this.row][this.col] = this.index;
if (this.row==hero.row && this.col==hero.col){
winstate=2;
}
stroke(50,0,100);
fill(255,0,0);
strokeWeight(space/10);
let start = -space/5,end=space/5, step = (end-start)/this.anisteps, cur;
if (this.anitoggle==1){
cur = start+step*this.animation ;
} else {
cur = end -step*this.animation;
}
let x, y;
[x,y] = middlepoint(this.row, this.col);
if (this.orientation==0){
this.drawUp(x,y+cur);
}
if (this.orientation==1){
this.drawDown(x,y+cur);
}
if (this.orientation==2){
this.drawLeft(x+cur,y);
}
if (this.orientation==3){
this.drawRight(x+cur,y);
}
this.animation = this.animation+1;
if (this.animation>this.anisteps){
this.animation=0;
this.anitoggle = this.anitoggle*-1;
}
this.movesteps ++;
if (this.movesteps>this.movecycle){
this.movesteps=0;
this.move();
}
}
}
}
class Pickup{
constructor(string, index){
[row, col] = getEmpty();
this.row = row;
this.col = col;
this.string = string;
this.index = index;
this.portaled = 0; //determines if the pickup is in the portal or not
mazemap[row][col] = index;
}
draw(){
if (this.portaled==0){
mazemap[this.row][this.col] = this.index;
}
[x1, y1] = topleftpoint(this.row, this.col);
[x2, y2] = bottomrightpoint(this.row, this.col);
x1 = x1+space/5;
y1 = y1+space/5;
x2 = x2-space/5;
y2 = y2-space/5;
strokeWeight(space/7);
fill(150,100,0);
stroke(100,50,0);
rect(x1,y1, x2,y2);
[x1,y1] = middlepoint(this.row, this.col);
stroke(0);
strokeWeight(space/30);
fill(0);
textSize(space/3);
text(this.string, x1,y1+space/10);
}
}
class Hero{
constructor(row, col, orientation){
this.row = row;
this.col = col;
this.dir = orientation;
this.pickupstate = 0;
this.toggle=0;
this.blinkspeed = 10;
this.pickupindex = -1;
this.string = "";
}
goLeft(){
this.dir = 2;
if (this.col>0 && rightborder[this.row][this.col-1]==0){
if (this.pickupstate==0){
this.col = this.col-1;
herowalksound.play();
} else {
if (mazemap[this.row][this.col-1]<=0){
this.col = this.col-1;
herowalksound.play()
pickuplist[this.pickupindex].row = this.row;
pickuplist[this.pickupindex].col = this.col;
}
}
}else{
wallhitsound.play();
}
}
goRight(){
this.dir = 3;
if (this.col<columns-1 && rightborder[this.row][this.col]==0){
if (this.pickupstate==0){
this.col = this.col+1;
herowalksound.play();
} else {
if (mazemap[this.row][this.col+1]<=0){
this.col = this.col+1;
herowalksound.play();
pickuplist[this.pickupindex].row = this.row;
pickuplist[this.pickupindex].col = this.col;
}
}
}else{
wallhitsound.play();
}
}
goUp(){
this.dir = 0;
if (this.row>0 && downborder[this.row-1][this.col]==0){
if (this.pickupstate==0){
this.row = this.row-1;
herowalksound.play();
} else {
if (mazemap[this.row-1][this.col]<=0){
this.row = this.row-1;
herowalksound.play();
pickuplist[this.pickupindex].row = this.row;
pickuplist[this.pickupindex].col = this.col;
}
}
}else{
wallhitsound.play();
}
}
goDown(){
this.dir = 1;
if (this.row<rows-1 && downborder[this.row][this.col]==0){
if (this.pickupstate==0){
this.row = this.row+1;
herowalksound.play();
} else {
if (mazemap[this.row+1][this.col]<=0){
this.row = this.row+1;
herowalksound.play();
pickuplist[this.pickupindex].row = this.row;
pickuplist[this.pickupindex].col = this.col;
}
}
}else{
wallhitsound.play();
}
}
pickUp(){
if (mazemap[this.row][this.col]>0){
this.pickupstate = 1;
this.pickupindex = mazemap[this.row][this.col];
this.string = pickuplist[this.pickupindex].string;
pickupsound.play()
}
}
drop(){
if (this.pickupstate==1){
this.pickupstate=0;
dropsound.play();
if (portal.row==this.row && portal.col == this.col){
portal.drop(pickuplist[this.pickupindex].string);
}
}
}
shoot(){
if (this.dir==0){
if (this.row>0){
if (downborder[this.row-1][this.col]==0){
shot.row = this.row-1;
shot.col = this.col;
shot.orientation=0;
shot.enabled=1;
}
}
}
if (this.dir==1){
if (this.row<rows-1){
if (downborder[this.row][this.col]==0){
shot.row=this.row+1;
shot.col=this.col;
shot.orientation=1;
shot.enabled=1;
}
}
}
if (this.dir==2){
if (this.col>0){
if (rightborder[this.row][this.col-1]==0){
shot.row=this.row;
shot.col=this.col-1;
shot.orientation=2;
shot.enabled=1;
}
}
}
if (this.dir==3){
if (this.col<columns-1){
if (rightborder[this.row][this.col]==0){
shot.row=this.row;
shot.col=this.col+1;
shot.orientation=3;
shot.enabled=1;
}
}
}
if (shot.enabled==0){
wallhitsound.play();
}
}
draw(){
// directions 0: top 1: bottom 2: left 3: right
let x,y;
[x,y] = middlepoint(this.row,this.col);
stroke(0);
strokeWeight(max(space/20,1));
fill(70,100,170);
if (this.dir==0){
x1 = x-space/8;
x2 = x+space/8;
y1 = y-space/2.5;
y2 = y-space/2.5;
}
if (this.dir==1){
x1 = x-space/8;
x2 = x+space/8;
y1 = y+space/2.5;
y2 = y+space/2.5;
}
if (this.dir==2){
x1 = x-space/2.5;
x2 = x-space/2.5;
y1 = y-space/8;
y2 = y+space/8;
}
if (this.dir==3){
x1 = x+space/2.5;
x2 = x+space/2.5;
y1 = y-space/8;
y2 = y+space/8;
}
triangle(x,y,x1,y1, x2,y2);
fill(50,250,250);
if (this.pickupstate==0){
stroke(0);
strokeWeight(max(space/20,1));
circle(x,y,space/1.5);
} else {
strokeWeight(max(space/10,1));
stroke(0,100,150)
circle(x,y, space/1.5);
[x,y] = middlepoint(this.row, this.col);
textAlign(CENTER);
strokeWeight(space/20);
fill(0);
textSize(space/3);
text(this.string.toString(), x, y);
}
}
}
class Shot{
constructor(){
this.row=0;
this.col=0;
this.orientation =3;
this.enabled=0;
this.iter=0;
this.anisteps=20;
}
draw(){
if (this.enabled==1){
if (this.iter==0){
bombsound.play()
}
let x,y,x1,y1;
strokeWeight(0);
let smallrad = max(space/5,3);
let bigrad = space;
[x,y] = middlepoint(this.row, this.col);
if (this.iter<this.anisteps){
fill(0);
let step = space/(2*this.anisteps);
if (this.orientation==0){
[x1,y1] = bottomleftpoint(this.row, this.col);
y = y1-step*this.iter;
}
if (this.orientation==1){
[x1,y1] = topleftpoint(this.row, this.col);
y = y1+step*this.iter;
}
if (this.orientation==2){
[x1,y1] = toprightpoint(this.row, this.col);
x = x1-step*this.iter;
}
if (this.orientation==3){
[x1,y1] = topleftpoint(this.row, this.col);
x = x1+step*this.iter;
}
circle(x,y,smallrad);
} else {
let step = (bigrad-smallrad)/this.anisteps;
let colorstep = 255/this.anisteps;
fill(255,colorstep*(this.iter-this.anisteps),0);
circle(x,y,smallrad + step*(this.iter-this.anisteps));
}
this.iter ++;
if (this.iter>this.anisteps*2){
this.iter=0;
this.enabled=0;
}
// kill enemy
if (this.iter==this.anisteps){
if (enemymap[this.row][this.col]>-1){
enemylist[enemymap[this.row][this.col]].enabled=0;
bombsound.stop()
spiderkillsound.play();
}
}
}
}
}
// maze generation
function generateMaze(){
i = rand(rows);
j = rand(columns);
visited = initArray(rows,columns,0);
mazeiter(i,j,visited);
}
function mazeiter(r,c,visited){
visited[r][c] = 1;
while (true){
neighbours = [];
if (r>0){
if (visited[r-1][c]==0){
neighbours[neighbours.length] = [r-1,c];
}
}
if (c>0){
if (visited[r][c-1]==0){
neighbours[neighbours.length] = [r,c-1];
}
}
if (r<rows-1){
if (visited[r+1][c]==0){
neighbours[neighbours.length] = [r+1,c];
}
}
if (c<columns-1){
if (visited[r][c+1]==0){
neighbours[neighbours.length] = [r,c+1];
}
}
if (neighbours.length>0){
index = rand(neighbours.length);
[nr,nc] = neighbours[index];
if (r==nr){
rightborder[r][min(c,nc)] = 0;
}
if (c==nc){
downborder[min(r,nr)][c] = 0;
}
mazeiter(nr,nc,visited);
}
else {
break;
}
}
}
// drawing functions
function topleftpoint(row, col){
return [col*space,row*space];
}
function toprightpoint(row, col){
return [(col+1)*space, row*space];
}
function bottomleftpoint(row, col){
return [col*space, (row+1)*space];
}
function bottomrightpoint(row, col){
return [(col+1)*space, (row+1)*space];
}
function middlepoint(row, col){
return [col*space + space/2, row*space+space/2];
}
function drawWin(){
rectMode(CORNERS);
stroke(0,200,150);
strokeWeight(space/2);
fill(255,255,255);
rect(windowWidth/4, windowHeight/4, windowWidth*3/4, windowHeight*3/4);
textSize(windowWidth/10);
strokeWeight(0);
fill(0,200,150);
textAlign(CENTER);
text('You Win!', windowWidth/2, windowHeight/2);
}
function drawLose(){
rectMode(CORNERS);
stroke(255,0,0);
strokeWeight(space/2);
fill(255,255,255);
rect(windowWidth/4, windowHeight/4, windowWidth*3/4, windowHeight*3/4);
textSize(windowWidth/10);
strokeWeight(0);
fill(255,0,0);
textAlign(CENTER);
text('You Lose!', windowWidth/2, windowHeight/2);
}
function drawBorder(){
[x1,y1] = topleftpoint(0,0);
[x2,y2] = toprightpoint(0,columns-1);
[x3,y3] = bottomrightpoint(rows-1, columns-1);
[x4,y4] = bottomleftpoint(rows-1,0);
strokeWeight(space/4);
stroke(0);
line(x1,y1,x2,y2);
line(x2,y2,x3,y3);
line(x3,y3,x4,y4);
line(x4,y4,x1,y1);
}
function drawMaze(){
stroke(0);
strokeWeight(max(space/8,1));
for (var i=0;i<rows;i++){
for (var j=0;j<columns;j++){
if (rightborder[i][j]==1){
[x1,y1] = toprightpoint(i,j);
[x2, y2] = bottomrightpoint(i,j);
line(x1,y1,x2,y2);
}
if (downborder[i][j]==1){
[x1,y1] = bottomleftpoint(i,j);
[x2,y2] = bottomrightpoint(i,j);
line(x1,y1,x2,y2);
}
}
}
}
// game functions
function win(){
if (wintoggle==0){
winsound.play();
wintoggle=1;
}
}
function lose(){
if (wintoggle==0){
losesound.play();
wintoggle=1;
}
}
function getEmpty(){
while(true){
row = rand(rows);
col = rand(columns);
if (mazemap[row][col] ==0 && enemymap[row][col]==-1){
return [row, col];
}
}
}
//pygame functions
// function preload(){
// herowalksound = loadSound('assets/herowalk.mp3');
// bombsound = loadSound('assets/bomb.mp3');
// dropsound = loadSound('assets/drop.mp3');
// losesound = loadSound('assets/lose.mp3');
// pickupsound = loadSound('assets/pickup.mp3');
// portaldropsound = loadSound('assets/portaldrop.mp3');
// spiderkillsound = loadSound('assets/spiderkill.mp3');
// spiderwalksound = loadSound('assets/spiderwalk.mp3');
// wallhitsound = loadSound('assets/wallhit.mp3');
// winsound = loadSound('assets/wiin.mp3');
// console.log('finished preloading');
// }
function setup() {
herowalksound = loadSound('assets/herowalk.mp3');
bombsound = loadSound('assets/bomb.mp3');
dropsound = loadSound('assets/drop.mp3');
losesound = loadSound('assets/lose.mp3');
pickupsound = loadSound('assets/pickup.mp3');
portaldropsound = loadSound('assets/portaldrop.mp3');
spiderkillsound = loadSound('assets/spiderkill.mp3');
spiderwalksound = loadSound('assets/spiderwalk.mp3');
wallhitsound = loadSound('assets/wallhit.mp3');
winsound = loadSound('assets/win.mp3');
console.log('finished preloading');
console.log('Setting up...')
createCanvas(windowWidth, windowHeight);
// determine maze constants
console.log('Generating maze...');
var dimension = min(windowWidth, windowHeight);
space = dimension/nunits;
// rows go vertically and columns go horizontally
rows = ~~(windowHeight/space);
columns = ~~(windowWidth/space);
rightborder = initArray(rows, columns, 1);
downborder = initArray(rows, columns, 1);
mazemap = initArray(rows, columns,0);
enemymap = initArray(rows, columns, -1);
generateMaze();
console.log('Generating hero...');
[heroRow, heroCol] = getEmpty();
hero = new Hero(heroRow, heroCol, 0);
console.log('Generatingn pickups...');
factor1 = max(2,rand(maxfactor));
factor2 = max(2,rand(maxfactor));
[row, col] = getEmpty();
portal = new Portal(row, col, factor1, factor2);
let maxdim = max(rows, columns);
pickuplist[0] = 0; //otherwise it will mess with mazemap
if (factormode==0){
pickuplist[pickuplist.length] = new Pickup(factor1, pickuplist.length);
pickuplist[pickuplist.length] = new Pickup(factor2, pickuplist.length);
while(pickuplist.length<maxdim){
pickuplist[pickuplist.length] = new Pickup(rand(maxfactor), pickuplist.length);
}
}
if (factormode==1){
pickuplist[pickuplist.length] = new Pickup(factor1*factor2, pickuplist.length);
while(pickuplist.length<maxdim){
pickuplist[pickuplist.length] = new Pickup(rand(maxfactor)*rand(maxfactor), pickuplist.length);
}
}
console.log('Generating enemies...');
while(enemylist.length<maxdim*2/3){
enemylist[enemylist.length] = new Enemy(enemylist.length);
}
shot = new Shot();
console.log('Finished setup');
}
function draw() {
mazemap = initArray(rows, columns,0);
enemymap = initArray(rows, columns, -1);
background(220);
drawMaze();
drawBorder();
for (var i=1;i<pickuplist.length;i++){
pickuplist[i].draw();
}
for (var i=0;i<enemylist.length;i++){
enemylist[i].draw();
}
portal.draw();
hero.draw()
shot.draw();
// checkGameState();
if (winstate==1){
drawWin();
win();
}
if (winstate==2){
drawLose();
lose();
}
}
function keyPressed(){
if (winstate==0){
if (keyCode == LEFT_ARROW){
hero.goLeft();
}
if (keyCode == RIGHT_ARROW){
hero.goRight();
}
if (keyCode == UP_ARROW){
hero.goUp();
}
if (keyCode == DOWN_ARROW){
hero.goDown();
}
if (key == ' '){
hero.shoot();
}
if (keyCode==SHIFT){
if (hero.pickupstate==0){
hero.pickUp();
} else {
if (hero.pickupstate==1){
hero.drop();
}
}
}
}
}