Code viewer for World: Castle World (clone by Oki...

// Cloned by Okikiola Sanni on 3 Dec 2022 from World "Castle World" by Starter user 
// Please leave this clone trail here.
 



// ==== Starter World =================================================================================================
// This code is designed for use on the Ancient Brain site.
// This code may be freely copied and edited by anyone on the Ancient Brain site.
// To include a working run of this program on another site, see the "Embed code" links provided on Ancient Brain.
// ====================================================================================================================




// Demo of 3D model
// OBJ plus MTL

// A "Simple World" like chase inside an invisible arena inside the castle 
// Chase uses x,y,z rather than grid of squares 


  
  
  

// ===================================================================================================================
// === Start of tweaker's box ======================================================================================== 
// ===================================================================================================================

// The easiest things to modify are in this box.
// You should be able to change things in this box without being a JavaScript programmer.
// Go ahead and change some of these. What's the worst that could happen?


AB.clockTick       = 100;    

	// Speed of run: Step every n milliseconds.  
	
AB.maxSteps        = 500;    

	// Length of run: Maximum length of run in steps.  

AB.screenshotStep  = 50;   
  
	// Take screenshot on this step. (All resources should have finished loading.)  

	

//---- global constants: -------------------------------------------------------

	const OBJPATH = "/uploads/starter/";	// path of OBJ and MTL 
	const OBJNAME = "castle.obj";
	const MTLNAME = "castle.new.mtl";

// castle credit
// https://www.script-tutorials.com/webgl-with-three-js-lesson-6/
// http://www.script-tutorials.com/demos/409/index2.html


// =================================================================================
// Bug: Original MTL file fails with new Three.js  
// The original "castle.mtl" works with old Three.js but fails with new version.
// Solution: Edit the MTL file. See note here:
// https://ancientbrain.com/docs.three.php
// =================================================================================




	const TEXTURE_AGENT 	= '/uploads/starter/pacman.jpg' ;
	const TEXTURE_ENEMY 	= '/uploads/starter/ghost.3.png' ;

// credits:
// https://commons.wikimedia.org/wiki/Category:Pac-Man_icons
// https://commons.wikimedia.org/wiki/Category:Skull_and_crossbone_icons
	
	const MUSIC_BACK  = '/uploads/starter/Defense.Line.mp3' ;
	
	
const SKYCOLOR 		= 0xffffcc ;				// a number, not a string 
const LIGHTCOLOR 	= 0xffffff ;

const squaresize 	= 50;							// size of cube length  


// basic castle model size 
// see debug line to compute model size below 
// Xsize: 6158.792236328125 Ysize: 703.5784759521484 Zsize: 3791.05908203125

const MODELLENGTH 			= 6159;							
const MODELWIDTH			= 3791; 

const SCALE_CASTLE			= 0.5;							// scale it by this		 
const SCALEDMODELLENGTH 	= MODELLENGTH * SCALE_CASTLE;	 			 
const SCALEDMODELWIDTH	 	= MODELWIDTH  * SCALE_CASTLE;	 			 

const startRadiusConst 		= SCALEDMODELLENGTH * 0.5 ; 
const maxRadiusConst 		= SCALEDMODELLENGTH * 10 ; 


	// camera points at 0,0 
	// where to put castle? 
	// fit bottom LHS corner to here:
	
	const CX 	= - ( SCALEDMODELLENGTH * 0.3 );		 
	const CZ 	=   ( SCALEDMODELWIDTH  * 0.3 );		 

	
// how far can the agent and enemy wander inside the castle
// experiment to see where the inner box fits inside the model 

const MIN_X 	= CX + ( SCALEDMODELLENGTH * 0.1 ) ;
const MAX_X		= CX + ( SCALEDMODELLENGTH * 0.55 ) ;

const MIN_Z 	= CZ - ( SCALEDMODELWIDTH * 0.45 ) ;
const MAX_Z		= CZ - ( SCALEDMODELWIDTH * 0.1 ) ;



// how much do they move each step 

const MOVE = squaresize ;

// how close do they have to be to lose score 

const BADCLOSE = squaresize * 5;



//--- change ABWorld defaults: -------------------------------

ABHandler.GROUNDZERO		= true;						// "ground" exists at altitude zero

ABWorld.drawCameraControls = false; 


// ===================================================================================================================
// === End of tweaker's box ==========================================================================================
// ===================================================================================================================


// You will need to be some sort of JavaScript programmer to change things below the tweaker's box.








	
 // we have a splash screen
 // behind the splash screen, newRun is running, loading resources 
 // do not start the run loop until resources ready AND splash screen is dismissed 

	var resourcesLoaded = false;
	var splashClicked = false;
	
	

//--- 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;


	

 

var thecastle, theagent, theenemy;		  

var agent_texture, enemy_texture; 

var badsteps;
var goodsteps;

	

function loadResources()		// asynchronous file loads - call initScene when finished 
{

	// load castle model OBJ and materials MTL (which reference JPEGs)
	
	var m = new THREE.MTLLoader();
	m.setResourcePath ( OBJPATH );
	m.setPath         ( OBJPATH );
	
	m.load ( MTLNAME, function ( materials ) 
	{
		materials.preload(); 
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );
		o.setPath ( OBJPATH );
		
		o.load ( OBJNAME, function ( object ) 
		{
			thecastle = object;
			if ( asynchFinished() )	initScene();		 
		});
	});
	
	
	// load cube textures 
	var loader1 = new THREE.TextureLoader();
	var loader2 = new THREE.TextureLoader();
		
	loader1.load ( TEXTURE_AGENT, function ( thetexture )  	 
	{
		thetexture.minFilter  = THREE.LinearFilter;
		agent_texture = thetexture;
		if ( asynchFinished() )	initScene();		 
	});	
	
	loader2.load ( TEXTURE_ENEMY, function ( thetexture )  
	{
		thetexture.minFilter  = THREE.LinearFilter;
		enemy_texture = thetexture;
		if ( asynchFinished() )	initScene();		 
	});
}


// function asynchFinished()		 
// {
// 	if ( thecastle && agent_texture && enemy_texture )  return true;  
// 	else return false;
// }	
	
	
	



// function initScene()		// file loads have returned 
// {
	
//  // add castle object to scene  

// 	thecastle.scale.multiplyScalar ( SCALE_CASTLE );    	  

//     thecastle.position.y = - (squaresize * 0.5);				// adjust so cubes (centred on 0) appear exactly above castle floor 
//     thecastle.position.x = CX;
//     thecastle.position.z = CZ;  
 
//   	ABWorld.scene.add ( thecastle );
			
// 	// calculate OBJ object size:
// 	//	console.log ( "Xsize: " + ABWorld.objectXsize(thecastle) + " Ysize: " + ABWorld.objectYsize(thecastle) + " Zsize: " + ABWorld.objectZsize(thecastle) );

	

//  // set up enemy  
   
// 	 shape    = new THREE.BoxGeometry ( squaresize, squaresize, squaresize );			 
// 	 theenemy = new THREE.Mesh( shape );
//  	 theenemy.material =  new THREE.MeshBasicMaterial( { map: enemy_texture } );
// 	 ABWorld.scene.add(theenemy);

//   // set position 
// 	 theenemy.position.y = 0;
// 	 theenemy.position.x = AB.randomIntAtoB( MIN_X, MAX_X );
// 	 theenemy.position.z = AB.randomIntAtoB( MIN_Z, MAX_Z );
	  
// 	/*
// 	// examine where different points are and are they inside the castle:
	
// 	 theenemy.position.x = 0;
// 	 theenemy.position.z = 0;
	
// 	 theenemy.position.x = MIN_X;
// 	 theenemy.position.z = MIN_Z;

// 	 theenemy.position.x = MAX_X;
// 	 theenemy.position.z = MAX_Z;
// 	*/
	
	
	
//  // set up agent 
   
// 	 shape    = new THREE.BoxGeometry ( squaresize, squaresize, squaresize );			 
// 	 theagent = new THREE.Mesh( shape );
// 	 theagent.material =  new THREE.MeshBasicMaterial( { map: agent_texture } );
// 	 ABWorld.scene.add(theagent);
	 
//   // put agent some distance from enemy 
// 	 do
// 	 {
// 	  x = AB.randomIntAtoB( MIN_X, MAX_X );
// 	  z = AB.randomIntAtoB( MIN_Z, MAX_Z );
// 	 }
// 	 while ( AB.distance2D ( x, z, theenemy.position.x, theenemy.position,z ) < BADCLOSE );  	
	 
// 	 theagent.position.y = 0;
// 	 theagent.position.x = x;
// 	 theagent.position.z = z;
	 
	 
//  // ready to start run loop? 
	
// 	ABWorld.render();

// 	console.log ( "Resources loaded." );
// 	resourcesLoaded = true;
	
// 	if ( resourcesLoaded && splashClicked ) 
// 		AB.runReady = true;  		// start run loop 
	
// }



// // --- check a proposed move is inside castle -----------------------------------
	
// function withinBounds ( x, z )
// {
// 	return ( 	( x > MIN_X ) &&	( x < MAX_X ) &&
// 				( z > MIN_Z ) &&	( z < MAX_Z ) );
// }
 
 

// // --- take actions -----------------------------------


// function moveLogicalEnemy()
// { 

// // move towards agent 
// // put some randomness in so it won't get stuck with barriers 
 
// // proposed move   

// 	var x = theenemy.position.x;
// 	var z = theenemy.position.z;
 
// 		if ( x < theagent.position.x ) 	x = x + AB.randomIntAtoB ( 0, MOVE ) ; 
// 		else						 	x = x - AB.randomIntAtoB ( 0, MOVE ) ; 

// 		if ( z < theagent.position.z ) 	z = z + AB.randomIntAtoB ( 0, MOVE ) ; 
// 		else 						 	z = z - AB.randomIntAtoB ( 0, MOVE ) ; 
	
// 	if ( withinBounds ( x, z ) )		// if in bounds 
// 	  if ( AB.distance2D ( x, z, theagent.position.x, theagent.position.z ) > squaresize * 2 )  	// limit to how close we go to agent 
// 	  {
// 		theenemy.position.x = x;
// 		theenemy.position.z = z;
// 	  }
// 	// else just miss a turn
// }



// function moveLogicalAgent( a )			// this is called by the infrastructure that gets action a from the Mind 
// { 
// // proposed move

//  var x = theagent.position.x;
//  var z = theagent.position.z;
 
//       if ( a == ACTION_LEFT ) 	x = x - MOVE ;
//  else if ( a == ACTION_RIGHT ) 	x = x + MOVE ;
//  else if ( a == ACTION_UP ) 	z = z + MOVE ;
//  else if ( a == ACTION_DOWN ) 	z = z - MOVE ;

//  if ( withinBounds ( x, z ) )		 
//   if ( AB.distance2D ( x, z, theenemy.position.x, theenemy.position.z ) >= squaresize   )  	// limit to how close we go to enemy 
//   {
// 		theagent.position.x = x;
// 		theagent.position.z = z;
//   }
//  // else ignore action 
// }



// // --- score: -----------------------------------


// function badstep()			 
// {
//  if ( AB.distance2D ( theagent.position.x, theagent.position.z, theenemy.position.x, theenemy.position.z ) < BADCLOSE ) return true;
//  else return false;
// }

 
// function   updateStatus()		 
// {
//  var score = AB.world.getScore();
//  AB.msg ( "Step: " + AB.step + " out of " + AB.maxSteps + ". Score: " + score ); 
// }






// AB.world.newRun = function() 
// {
// 	badsteps = 0;		
// 	goodsteps = 0;

// 	ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR  ); 

// 	// newRun can run behind splash screen 
// 	// do not start run loop until resources ready AND splash screen is dismissed 
	
// 	AB.runReady = false; 
	
// 	loadResources();		// aynch file loads		
// 							// calls initScene() when it returns 
 
//     var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, 3 );
//     thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
//     ABWorld.scene.add(thelight);
		
// };



// AB.world.getState = function()
// {
//  var x = [ theagent.position.x, theagent.position.z, theenemy.position.x, theenemy.position.z ];
//   return ( x );  
// };


// AB.world.takeAction = function ( a )
// {
//   moveLogicalAgent(a);

//  // if ( ( AB.step % 2 ) == 0 )              // slow the enemy down to every nth step
//   moveLogicalEnemy();

//   if ( badstep() )  badsteps++;
//   else   			goodsteps++;

//   updateStatus();
// };


// AB.world.getScore = function()
// {
//  return goodsteps;
// };





 


	
// // --- Splash screen --------------------------------------------------------------
// // Splash screen is to get audio started on mobile/Chrome by user interaction.
// // This should works on all platforms - plays background music.

	
// 	// display standard splash screen (World title, World image, Start button) 
	
// 	AB.newSplash();

	
// 	// when user clicks/touches button on splash screen, audio starts and run starts:
		
// 	AB.splashClick ( function ()        
// 	{
// 		// audio linked to user interaction:  
// 		AB.backgroundMusic ( MUSIC_BACK );	
		
// 		AB.removeSplash();			// remove splash screen 
		
// 		// ready to start run loop? 
		
// 		splashClicked = true;
		
// 		if ( resourcesLoaded && splashClicked ) 
// 			AB.runReady = true;  		// start run loop 
		
// 	});