Code viewer for World: Dodge Cube
//Student Name: Robert Szocs //
//Student ID: 20722375 //

const	 	CLOCKTICK 	= 100;		// Speed of run: Step every n milliseconds.
const		MAXSTEPS 	= 1000;		// Length of run: Maximum length of run in steps.


const  SCREENSHOT_STEP = 50;    


function World() {//------------Word Start-------------


    var powerUps = []; // Array to store active power-ups
    var powerUpDuration = 5000; // Duration of power-up in milliseconds (5 seconds)
    var freezeActive = false; // Flag to track if freeze power-up is active
    var clearRowActive = false; // Flag to track if clear row power-up is active

    var timeLimit = 60000; // Time limit in milliseconds (60 seconds)
    var remainingTime = timeLimit;
    var timerInterval;
    
    
    const POWER_UP_FREEZE = 0;
    const POWER_UP_CLEAR_ROW = 1;

    const gridsize = 150;					            // length of each side	   
    const noSquares = 10;					            // size of square in pixels
    const MAXPOS = gridsize * noSquares;		        // length of one side in pixels 
    const MAX = 135;
    
    const SKYCOLOR 	= 0x000000;				            // a number, not a string 
    
    const startRadiusConst	 	= MAXPOS*0.2  ;		// distance from centre to start the camera at
    const maxRadiusConst 		= MAXPOS * 3 ;          // maximum distance from camera we will render things  
    
    var grid;
    var floor;
    var player;
    var playerX =5;
    var playerZ =0;
    var playerY =0;
    var occupied = false;
    
    var difficulty = 1;
    var noCubes = 5 + difficulty;
    var cubeHeight = difficulty;

    var floorSpacesLeft;
    var collided = false;
    
    
    //----------------------Grid Start-------------------------
    function random(lowerLimit, upperLimit){
        return Math.floor(Math.random() * upperLimit) + lowerLimit; 
    }
    
    //----------------------Grid Start-------------------------
    
    function initGrid(){
        grid = new THREE.GridHelper(gridsize,noSquares);//create the 2d grid on the floor.
        threeworld.scene.add(grid);
        
        grid = new Array(noSquares);
        for (var x = 0; x < noSquares ; x++){//create a 3d array to store which spaces are occupied by cubes.
          grid[x] = new Array(noSquares);
          for (var z = 0; z < noSquares ; z++){
              grid[x][z] = new Array(noSquares);
            for(var y=-1; y<noSquares; y++){
                grid[x][z][y] = occupied;// start by setting them all to be unoccupied.
            }
          }
        }
        
        floor = new Array(noSquares);//Store each cube for the floor.
        for(var fx=0; fx<noSquares; fx++){
            floor[fx] = new Array(noSquares);
            for(var fz=0; fz<noSquares; fz++){
                floor[fx][fz] = false;
            }
        }
    }
    //----------------------Grid End-------------------------
    
    //----------------------Cube Manipulation Start-------------------------
    
    function drawCube(size, height, x, z) {
  var side = size * 2;
  var geometry = new THREE.BoxGeometry(side, side * height, side);
  var material = new THREE.MeshStandardMaterial({
    color: 0xFFA500,      // Orange color
    emissive: 0xFFA500,   // Emissive color also set to orange
  });
  var cube = new THREE.Mesh(geometry, material);

  cube.position.y = (gridsize / noSquares) + ((height - 1) * size);
  cube.position.z = (gridsize - size) - (side * z);
  cube.position.x = (gridsize - size) - (side * x);

  threeworld.scene.add(cube);
  return cube;
}
    
    function drawPlayerCube(size, height, x, z) {
  var material = new THREE.MeshStandardMaterial({
    color: 0x0000FF,       // Blue color
    emissive: 0x0000FF,    // Emissive color also set to blue
  });
  var cube = drawCube(size, height, x, z);
  cube.material = material;
  threeworld.scene.add(cube);
  player = cube;
}
    
    function randomCube(y,height){//randomise the position of each cube.
        var size = 15;
        if(height >= 10){
            height = random(1,10);
        }
        var x = random(0,10);
        var z = random(0,10);
        var cube = drawCube(size,height,x,z);
        cube.position.y = cube.position.y + (y*30);
        grid[x][z][y] = cube;
        
    }
    
    function spawnPowerUp(x, z, y) {
        var size = 15;
        var geometry = new THREE.BoxGeometry(size, size, size);
        var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // Green color for power-ups
        var cube = new THREE.Mesh(geometry, material);

        cube.position.y = cube.position.y + (y * 30);
        cube.position.z = (gridsize - size) - (size * z);
       cube.position.x = (gridsize - size) - (size * x);

        threeworld.scene.add(cube);
        powerUps.push({ cube: cube, type: randomPowerUpType() });
    }
    
    
    function spawnCube(y, cubeNo,cubeHeight){//spawn in cubes.
        for(var i=0; i<cubeNo; i++)
            randomCube(y,cubeHeight);
    }

    function move(){//moves all falling cubes down and if they hit the floor, change their color.
        for (var x = 0; x < noSquares ; x++){
          for (var z = 0; z < noSquares ; z++){
            for(var y=-1; y<noSquares; y++){

                if(grid[x][z][-1] != occupied){
                    grid[x][z][-1].material = new THREE.MeshBasicMaterial({color: 0xffffff, wireframe: true});
                    floor[x][z] = true;
                }
                
                if((grid[x][z][y] != occupied) && (y>=0)){
                    grid[x][z][y].position.y = grid[x][z][y].position.y - 30;
                    grid[x][z][y-1] = grid[x][z][y];
                    grid[x][z][y] = occupied;
                }
                
            }
          }
        }
    }

    function isFloorFull(){//returns the number of squares on the floor left to fill.
        var fullFloor = noSquares*noSquares;
        for(var x=0; x<noSquares; x++){
            for(var z=0; z<noSquares; z++){
                if(floor[x][z] === true){
                    fullFloor--;
                }
            }
        }
        return fullFloor;
    }
    //----------------------Cube Manipulation End-------------------------
    //--------------Controls start-----------------
    function handleKeyDown (event){
     
      var code = event.keyCode;
      
      if (code == 37){  // left
          if(player.position.x <= -MAX){
            //console.log("x: "+player.position.x) ;  
          }else{
            playerX--;
            spawnCube(4,noCubes,cubeHeight);
            move();
            moveSound();
            player.position.x = player.position.x -30;
            
          }
      }   
      if (code == 38){  // right == up (based on inital camera position)
          if(player.position.z <= -MAX){
            //console.log("z: "+player.position.z) ;  
          }else{
            playerZ++;
            spawnCube(4,noCubes,cubeHeight);
            move();
            moveSound();
            player.position.z = player.position.z -30;
            
          }
      }  
     
      if (code == 39){  // up == right (based on inital camera position)
          if(player.position.x >= MAX){
            //console.log("x: "+player.position.x) ;  
          }else{
            playerX++;            
            spawnCube(4,noCubes,cubeHeight);
            move();
            moveSound();
            player.position.x = player.position.x +30;
          }
      } 
      
      if (code == 40){  // back
          if(player.position.z >= MAX){
           // console.log("z: "+player.position.z) ;  
          }else{
            playerZ--;            
            spawnCube(4,noCubes,cubeHeight);
            move();
            moveSound();
            player.position.z = player.position.z +30;
          }
      }    
    
     } 
     
     //--------------Controls End-----------------
     
     function gameOver(){//When the game ends, remove instructions and say if they won/lost.
        var s = "<h2> </h2>";
 	    $("#user_span1").html( s ); 
 	    
 	    var text = "";
         $("#user_span3").text( text );
        
        if(collided === true){
           var s = "<font color=red><center> <h1> You Lose! </h1> </center></font>";
 	        $("#user_span1").html( s ); 
        }else{
            var s = "<font color=green><center> <h1> You Win! </h1> </center></font>";
 	         $("#user_span1").html( s );
        }
        
         
        for (var x = 0; x < noSquares ; x++){
          for (var z = 0; z < noSquares ; z++){
            for(var y=0; y<noSquares; y++){
                threeworld.scene.remove(grid[x][z][y]);// remove all cubes in the air.
            }
          }
        }
        threeworld.scene.remove(player);//remove the player from the scene so they dont try to keep playing.
        
     }
     
     function initInstructions(){//display instructions
        var s = "<center> <strong>Instructions:</strong>Use arrow keys to move, each time you move so do the falling blocks... don't get hit!<hr><strong>Aim:</strong> to fill the floor with boxes.  </center>";
 	    $("#user_span1").html( s );
     }
     
     function printDetails() {
        floorSpaceLeft = isFloorFull();
        text = "Floor Spaces Left: " + floorSpaceLeft + " | Time Left: " + Math.floor(remainingTime / 1000) + "s";
        $("#user_span3").text(text);
     }
    
    //----------------------Running Functions Start-------------------------
	this.endCondition;
	
	this.newRun = function()
	{
  		this.endCondition = false;		 
        
		if ( true  )
  		{
  		    threeworld.init3d ( startRadiusConst,maxRadiusConst, SKYCOLOR );//init 3d world
  		    document.addEventListener( 'keydown', handleKeyDown   );//add arrow key listeners

            initMusic();//start music.
            initGrid();//init the grids
            initInstructions();//display instructions.

            drawPlayerCube(15,1,playerX,playerZ);//Draw the player cube.
		}
		
		    // Start the timer
        timerInterval = setInterval(function() {
        remainingTime -= 1000; // Reduce by 1 second
        if (remainingTime <= 0) {
            clearInterval(timerInterval);
            gameOver();
        }
    }, 1000);
};


	this.getState = function()
	{
	  state = true;
	  return ( state );  				// State format defined by this World.
	};


this.nextStep = function()		 
{
 var action = 4;

		if ( true  ){   
            printDetails();
            
            if(grid[playerX][playerZ][playerY] != occupied){//if a falling cube && the player are in the same space, end the game
                collided = true;
                this.endCondition = true;
            }
		}

		if(isFloorFull() === 0){//if the floor is full, end the game.
		    this.endCondition = true;
		}
};


	this.endRun = function()
	{
        if(true){
	        if(this.endCondition===true){
	            if((isFloorFull() === 0) || (collided == true)){
	                gameOver();
	            }
	        }
	    }
	};


	this.getScore = function()
 	{
	    return (isFloorFull());		
	};
    //----------------------Running Functions End-------------------------
    
    
}//------------World End-------------

function initMusic(){//init the background music
  	var x="<audio id=gameSound src=/uploads/oneilf27/dark-techno-city.mp3   autoplay  >";
  	$("#user_span6").html( x );
} 

function moveSound(){
 	x = "</audio><audio  id=moveSound  src=/uploads/oneilf27/beepmult.mp3   autoplay  > </audio>";
  	$("#user_span5").html( x );
}