// Cloned by Okikiola Sanni on 4 Dec 2022 from World "Spooky Skeleton Survival" by Cillian Rice // Please leave this clone trail here.// =============================================================================================// Spooky Skeleton Survival// Cillian Rice, 2016.// Student No. 14446238// CASE 3//// Built using code from Mark Humphrys Complex World// Enemy actively chases agent when it has a vertical or horizontal line of sight and searches// randomly for it when it doesn't. See line 146 for further LOS explanation.// Survive until the end of the game by hiding from the skeleton.// =============================================================================================// =============================================================================================// Scoring:// Bad steps = steps where enemy is within one step of agent.// Good steps = steps where enemy is further away. // Score = good steps as percentage of all steps.// =============================================================================================// World must define these:const CLOCKTICK =100;// speed of run - move things every n millisecondsconst MAXSTEPS =1000;// length of a run before final score//---- global constants: -------------------------------------------------------const gridsize =50;// number of squares along side of world const NOBOXES =Math.trunc ((gridsize * gridsize)/10);// density of maze - number of internal boxes// (bug) use trunc or can get a non-integer const squaresize =100;// size of square in pixelsconst MAXPOS = gridsize * squaresize;// length of one side in pixels const SKYCOLOR =0xddffdd;// a number, not a string const BLANKCOLOR = SKYCOLOR ;// make objects this color until texture arrives (from asynchronous file read)const show3d =true;// Switch between 3d and 2d view (both using Three.js) const startRadiusConst = MAXPOS *0.8;// distance from centre to start the camera atconst skyboxConst = MAXPOS *3;// where to put skybox const maxRadiusConst = MAXPOS *10;// maximum distance from camera we will render things //--- Mind can pick one of these actions -----------------const ACTION_LEFT =0;const ACTION_RIGHT =1;const ACTION_UP =2;const ACTION_DOWN =3;const ACTION_STAYSTILL =4;// in initial view, (smaller-larger) on i axis is aligned with (left-right)// in initial view, (smaller-larger) on j axis is aligned with (away from you - towards you)// contents of a grid squareconst GRID_BLANK =0;const GRID_WALL =1;const GRID_MAZE =2;// --- some useful random functions -------------------------------------------function randomfloatAtoB ( A, B ){return( A +(Math.random()*(B-A)));}function randomintAtoB ( A, B ){return(Math.round ( randomfloatAtoB ( A, B )));}function randomBoolean(){if(Math.random()<0.5){returnfalse;}else{returntrue;}}//---- start of World class -------------------------------------------------------functionWorld(){// most of World can be private // regular "var" syntax means private variables:var BOXHEIGHT;// 3d or 2d box height var GRID =newArray(gridsize);// can query GRID about whether squares are occupied, will in fact be initialised as a 2D array var WALLS =newArray(4* gridsize );// need to keep handles to wall and maze objects so can find them later to paint them var MAZE =newArray( NOBOXES );var theagent, theenemy;var agentRotation =0;var enemyRotation =0;// with 3D models, current rotation away from default orientation// enemy and agent position on squaresvar ei, ej, ai, aj;var badsteps;var goodsteps;var step;var self =this;// needed for private fn to call public fn - see below var LOS =0;//Unobstructed enemy Line of Sight to agent/* LOS is reset every time the player is spotted and decremented while they
are not in view. When its value is zero the enemyAgent reverts to searching. When its
value is greater than zero it knows where the player is and follows it. This allows
the enemy to chase the player for a period of time after spotting it. If sight is lost
for long enough the enemy will go back to searching randomly.*/// regular "function" syntax means private functions:function initGrid(){for(var i =0; i < gridsize ; i++){
GRID[i]=newArray(gridsize);// each element is an array for(var j =0; j < gridsize ; j++){
GRID[i][j]= GRID_BLANK ;}}}function occupied ( i, j )// is this square occupied{if(( ei == i )&&( ej == j ))returntrue;// variable objects if(( ai == i )&&( aj == j ))returntrue;if( GRID[i][j]== GRID_WALL )returntrue;// fixed objects if( GRID[i][j]== GRID_MAZE )returntrue;returnfalse;}// logically, coordinates are: y=0, x and z all positive (no negative) // logically my dimensions are all positive 0 to MAXPOS// to centre everything on origin, subtract (MAXPOS/2) from all dimensions function translate ( x ){return( x -( MAXPOS/2));}//--- skybox ----------------------------------------------------------------------------------------------function initSkybox(){// x,y,z positive and negative faces have to be in certain order in the array // spooky skybox, credit:// https://marketplace.secondlife.com/p/WM-Spooky-Forest-Mesh-Skybox-Unfurnished/6958081?id=6958081&slug=WM-Spooky-Forest-Mesh-Skybox-Unfurnishedvar materialArray =[(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/cillianrice1/spookyforest2.jpg"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/cillianrice1/spookyforest2.jpg"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/cillianrice1/spookyforest4.jpg"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/cillianrice1/spookyforest5.jpg"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/cillianrice1/spookyforest1.jpg"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/cillianrice1/spookyforest1.jpg"), side: THREE.BackSide}))];var skyGeometry =new THREE.CubeGeometry( skyboxConst, skyboxConst, skyboxConst );var skyMaterial =new THREE.MeshFaceMaterial( materialArray );var theskybox =new THREE.Mesh( skyGeometry, skyMaterial );
threeworld.scene.add( theskybox );// We are inside a giant cube}// loader return can call private functionfunction loadTextures(){var manager =new THREE.LoadingManager();var loader =new THREE.OBJLoader( manager );
loader.load("/uploads/starter/skelet.obj", buildenemy );//load simple OBJ//loader.load( "/uploads/starter/male02.obj", buildagent );// load OBJ plus MTL (plus TGA files)
THREE.Loader.Handlers.add(/.tga$/i,new THREE.TGALoader());var m =new THREE.MTLLoader();
m.setTexturePath ("/uploads/starter/");
m.setPath ("/uploads/starter/");
m.load("Peter_Parker.mtl",function( materials ){
materials.preload();var o =new THREE.OBJLoader();
o.setMaterials ( materials );
o.setPath ("/uploads/starter/");
o.load("Peter_Parker.obj",function( object ){
addparker ( object );});});var loader1 =new THREE.TextureLoader();
loader1.load ('/uploads/cillianrice1/grey.jpg',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
paintWalls (new THREE.MeshBasicMaterial({ map: thetexture }));});var loader2 =new THREE.TextureLoader();
loader2.load ('/uploads/cillianrice1/somename.jpg',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
paintMaze (new THREE.MeshBasicMaterial({ map: thetexture }));});}function buildenemy ( object ){
object.scale.multiplyScalar (3);// make 3d object n times bigger
object.traverse( paintEnemy );
theenemy = object;
threeworld.scene.add( theenemy );}function paintEnemy ( child ){if( child instanceof THREE.Mesh){
child.material.map = THREE.ImageUtils.loadTexture("/uploads/starter/ghost.3.png");}}function addparker ( object ){
object.scale.multiplyScalar (70);
theagent = object;
threeworld.scene.add( theagent );}// --- add fixed objects ---------------------------------------- function initLogicalWalls()// set up logical walls in data structure, whether doing graphical run or not {for(var i =0; i < gridsize ; i++)for(var j =0; j < gridsize ; j++)if(( i==0)||( i==gridsize-1)||( j==0)||( j==gridsize-1)){
GRID[i][j]= GRID_WALL ;}}function initThreeWalls()// graphical run only, set up blank boxes, painted later {var t =0;for(var i =0; i < gridsize ; i++)for(var j =0; j < gridsize ; j++)if( GRID[i][j]== GRID_WALL ){var shape =new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );var thecube =new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate ( i * squaresize );// translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
thecube.position.z = translate ( j * squaresize );
thecube.position.y =0;
threeworld.scene.add(thecube);
WALLS[t]= thecube;// save it for later
t++;}}function paintWalls ( material ){for(var i =0; i < WALLS.length; i++){if( WALLS[i]) WALLS[i].material = material;}}function initLogicalMaze(){for(var c=1; c <= NOBOXES ; c++){var i = randomintAtoB(1,gridsize-2);// inner squares are 1 to gridsize-2var j = randomintAtoB(1,gridsize-2);
GRID[i][j]= GRID_MAZE ;}}function initThreeMaze(){var t =0;for(var i =0; i < gridsize ; i++)for(var j =0; j < gridsize ; j++)if( GRID[i][j]== GRID_MAZE ){var shape =new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );var thecube =new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate ( i * squaresize );
thecube.position.z = translate ( j * squaresize );
thecube.position.y =0;
threeworld.scene.add(thecube);
MAZE[t]= thecube;// save it for later
t++;}}function paintMaze ( material ){for(var i =0; i < MAZE.length; i++){if( MAZE[i]) MAZE[i].material = material;}}// --- enemy functions -----------------------------------function drawEnemy()// given ei, ej, draw it {var x = translate ( ei * squaresize );var z = translate ( ej * squaresize );var y =0;
theenemy.position.x = x;
theenemy.position.y = y;
theenemy.position.z = z;
threeworld.lookat.copy ( theenemy.position );// if camera moving, look back at where the enemy is
threeworld.lookat.y =( squaresize *1.5);// point camera higher up}function initLogicalEnemy(){// start in random location:var i, j;do{
i = randomintAtoB(1,gridsize-2);
j = randomintAtoB(1,gridsize-2);}while( occupied(i,j));// search for empty square
ei = i;
ej = j;}function moveSearchingEnemy(){// small random movevar oldi = ei;var oldj = ej;var i = randomintAtoB ( ei-1, ei+1);var j = randomintAtoB ( ej-1, ej+1);if(i ==(oldi+1)){
rotateEnemyTowards (3*(Math.PI /2));}if(i ==(oldi-1)){
rotateEnemyTowards (1*(Math.PI /2));}if(j ==(oldj+1)){
rotateEnemyTowards (0*(Math.PI /2));}if(j ==(oldj-1)){
rotateEnemyTowards (2*(Math.PI /2));}if(! occupied(i,j))// if no obstacle then move, else just miss a turn{
ei = i;
ej = j;}}function moveLogicalEnemy(){// move towards agent // put some randomness in so it won't get stuck with barriers var i, j;if( ei < ai ){
i = randomintAtoB(ei, ei+1);
rotateEnemyTowards (3*(Math.PI /2));}if( ei == ai ) i = ei;if( ei > ai ){
i = randomintAtoB(ei-1, ei);
rotateEnemyTowards (1*(Math.PI /2));}if( ej < aj ){
j = randomintAtoB(ej, ej+1);
rotateEnemyTowards (0*(Math.PI /2));}if( ej == aj ) j = ej;if( ej > aj ){
j = randomintAtoB(ej-1, ej);
rotateEnemyTowards (2*(Math.PI /2));}if(! occupied(i,j))// if no obstacle then move, else just miss a turn{
ei = i;
ej = j;}}function rotateEnemyTowards ( newRotation ){if( enemyRotation == newRotation )return;// else var x =( enemyRotation + newRotation )/2;
theenemy.rotation.set(0, x,0);
enemyRotation = x;}// --- agent functions -----------------------------------function drawAgent()// given ai, aj, draw it {var x = translate ( ai * squaresize );var z = translate ( aj * squaresize );var y =0;
theagent.position.x = x;
theagent.position.y = y;
theagent.position.z = z;
threeworld.follow.copy ( theagent.position );// follow vector = agent position (for camera following agent)
threeworld.follow.y =( squaresize *1.5);// put camera higher up}function initLogicalAgent(){// start in random location:var i, j;do{
i = randomintAtoB(1,gridsize-2);
j = randomintAtoB(1,gridsize-2);}while( occupied(i,j));// search for empty square
ai = i;
aj = j;}function initThreeAgent(){var shape =new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
theagent =new THREE.Mesh( shape );
theagent.material.color.setHex( BLANKCOLOR );
drawAgent();}function moveLogicalAgent( a )// this is called by the infrastructure that gets action an from the Mind {var i = ai;var j = aj;if( a == ACTION_LEFT ) i--;elseif( a == ACTION_RIGHT ) i++;elseif( a == ACTION_UP ) j++;elseif( a == ACTION_DOWN ) j--;if(! occupied(i,j)){
ai = i;
aj = j;}}function keyHandler(e)// user control // Note that this.takeAction(a) is constantly running at same time, redrawing the screen.{if(e.keyCode ==37) moveLogicalAgent ( ACTION_LEFT );if(e.keyCode ==38) moveLogicalAgent ( ACTION_DOWN );if(e.keyCode ==39) moveLogicalAgent ( ACTION_RIGHT );if(e.keyCode ==40) moveLogicalAgent ( ACTION_UP );}function rotateAgentTowards ( newRotation ){if( agentRotation == newRotation )return;// else var x =( agentRotation + newRotation )/2;
theagent.rotation.set(0, x,0);
agentRotation = x;}// --- score: -----------------------------------function badstep()// is the enemy within one square of the agent{if((Math.abs(ei - ai)<2)&&(Math.abs(ej - aj)<2))returntrue;elsereturnfalse;}function agentBlocked()// agent is blocked on all sides, run over{return( occupied (ai-1,aj)&&
occupied (ai+1,aj)&&
occupied ( ai,aj+1)&&
occupied ( ai,aj-1));}function updateStatusBefore(a)// this is called before anyone has moved on this step, agent has just proposed an action// update status to show old state and proposed move {var x = self.getState();var status =" Step: <b> "+ step +" </b> x = ("+ x.toString()+") ";
$("#user_span3").html( status );}function updateStatusAfter()// agent and enemy have moved, can calculate score{// new state after both have movedvar y = self.getState();var status =" y = ("+ y.toString()+") <BR> ";
$("#user_span4").html( status );var score = self.getScore();var status =" Bad steps: "+ badsteps +" Good steps: "+ goodsteps +" Score: "+ score.toFixed(2)+"% "+"<b> The skeleton will chase you if he sees you! You can lose him by staying out of sight for a while. Arrow keys to move!</b>";
$("#user_span5").html( status );}function los(){var foundAgent =false;//Check right for Line of Sightfor(var i =(ei+1); i < gridsize-1; i++){if(occupied(i,ej)){if((ai==i)&&(aj==ej)){
LOS =100;
foundAgent =true;break;}else{
LOS--;
foundAgent =false;break;}}}//Check left for LOSif(foundAgent ==false){for(var i = ei-1; i>0; i--){if(occupied(i,ej)){if((ai==i)&&(aj==ej)){
LOS =100;
foundAgent =true;break;}else{
LOS--;
foundAgent =false;break;}}}}//Check up for LOSif(foundAgent ==false){for(var i = ej+1; i<gridsize; i++){if(occupied(ei,i)){if((aj==i)&&(ai==ei)){
LOS =100;
foundAgent =true;break;}else{
LOS--;
foundAgent =false;break;}}}}//Check down for LOSif(foundAgent ==false){for(var i = ej-1; i>0; i--){if(occupied(ei,i)){if((aj==i)&&(ai==ei)){
LOS =100;
foundAgent =true;break;}else{
LOS--;
foundAgent =false;break;}}}}return LOS;}//--- public functions / interface / API ----------------------------------------------------------this.endCondition;// If set to true, run will end. this.newRun =function(){// (subtle bug) must reset variables like these inside newRun (in case do multiple runs)this.endCondition =false;
badsteps =0;
goodsteps =0;
step =0;// for all runs:
initGrid();
initLogicalWalls();
initLogicalMaze();
initLogicalAgent();
initLogicalEnemy();// for graphical runs only:if(true){if( show3d ){
BOXHEIGHT = squaresize;
threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );}else{
BOXHEIGHT =1;
threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR );}
initSkybox();
initMusic();// Set up objects first:
initThreeWalls();
initThreeMaze();
initThreeAgent();// Then paint them with textures - asynchronous load of textures from files. // The texture file loads return at some unknown future time in some unknown order.// Because of the unknown order, it is probably best to make objects first and later paint them, rather than have the objects made when the file reads return.// It is safe to paint objects in random order, but might not be safe to create objects in random order.
loadTextures();
document.onkeydown = keyHandler;}};this.getState =function(){var x =[ ai, aj, ei, ej ];return( x );};this.takeAction =function( a ){
step++;if(true)
updateStatusBefore(a);// show status line before moves
moveLogicalAgent(a);if( los()>0)// check if enemy has line of sight
moveLogicalEnemy();// chase enemy if it has line of sightelse
moveSearchingEnemy();//Search for it if it doesntif( badstep())
badsteps++;else
goodsteps++;if(true){
drawAgent();
drawEnemy();
updateStatusAfter();// show status line after moves }if(((ei == ai+1)&&(ej == aj))||((ei == ai-1)&&(ej == aj))||((ei == ai)&&(ej == aj-1))||((ei == ai)&&(ej == aj+1)||((ei == ai+1)&&(ej == aj+1))||((ei == ai+1)&&(ej == aj-1))||((ei == ai-1)&&(ej == aj+1))||((ei == ai-1)&&(ej == aj-1)))){this.endCondition =true;}if( agentBlocked())// if agent blocked in, the skeleton kills you. {this.endCondition =true;
goodsteps =0;// you score zero as far as database is concerned if(true){
musicPause();
soundAlarm();}}};this.endRun =function(){if(true){
musicPause();if(this.endCondition )
$("#user_span6").html(" <font color=red> <B> You have been caught by the spooky skeleton! </B> </font> ");else
$("#user_span6").html(" <font color=red> <B> You survived! </B> </font> ");}};this.getScore =function(){return(( goodsteps / step )*100);};}//---- end of World class -------------------------------------------------------// --- music and sound effects ----------------------------------------// credits:// http://www.dl-sounds.com/royalty-free/defense-line/// http://soundbible.com/1542-Air-Horn.html function initMusic(){// put music element in one of the spansvar x ="<audio id=theaudio src=http://student.computing.dcu.ie/~ricec8/Spooky%20Scary%20Skeletons%20(Trap%20Remix).mp3 autoplay loop> </audio>";
$("#user_span1").html( x );}function musicPlay(){// jQuery does not seem to parse pause() etc. so find the element the old way:
document.getElementById('theaudio').play();}function musicPause(){
document.getElementById('theaudio').pause();}function soundAlarm(){var x ="<audio src=/uploads/starter/air.horn.mp3 autoplay > </audio>";
$("#user_span2").html( x );}