// Cloned by Przemyslaw Majda on 29 Nov 2022 from World "Pig Shooter" by Przemyslaw Majda
// Please leave this clone trail here.
// Cloned by Przemyslaw Majda on 28 Nov 2022 from World "Password Websockets boxes " 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 Websockets functionality added to a 3D World
// Run with two or more users
// You have to tell the users the password you will be using
// Click button to change a random box's texture on all clients running this World
// Pick a side and compete against an opponent!
AB.clockTick = 40;
// Speed of run: Step every n milliseconds. Default 100.
AB.maxSteps = 1200; // run for ~48 seconds
// Length of run: Maximum length of run in steps. Default 1000.
AB.screenshotStep = 100;
// ABHandler.GROUNDZERO = true;
// Take screenshot on this step. (All resources should have finished loading.) Default 50.
// wolf 3D model credit
// https://free3d.com/3d-model/wolf-rigged-and-game-ready-42808.html
// cannot get MTL to laod, getting "m.setTexturePath is not a function" error
// so we are going with an all black lego model
const OBJPATH = "/uploads/przemyslawmajda/";
const OBJNAME = "Wolf_obj.obj";
//const MTLNAME = "legoobj.mtl";
const FILE_ARRAY = [
"/uploads/przemyslawmajda/pig.png", // array 0 to 4 are the earths
"/uploads/przemyslawmajda/grassHD.jpg",
"/uploads/przemyslawmajda/fence_texture2.png"
];
const SKYCOLOR = 0x7dd6ff; // very light blue
// a number, not a string
// 0xFFB6C1; // light pink
// 0xccffcc; // light green
const MAXPOS = 29000 ; // start things within these bounds
const ARMYSIZE = 200; // an "army" of objects
const skyboxConst = MAXPOS * 2 ;
const objectsize = 700 ;
const WALKSTEP = 20; // bounds of the random move per timestep
// try 50 (vibrating sheet) versus 1000 (cloud)
const startRadiusConst = MAXPOS ; // distance from centre to start the camera at
const maxRadiusConst = MAXPOS * 10 ; // maximum distance from camera we will render things
ABHandler.MAXCAMERAPOS = MAXPOS * 2 ; // allow camera go far away
ABWorld.drawCameraControls = false;
AB.drawRunControls = false;
var THEARMY = new Array( ARMYSIZE );
var textureArray = new Array ( FILE_ARRAY.length );
var puss;
var CONFIRMED_KILLS = [];
var score = 0;
// player movement
// this code was taken from "User-controlled Model World" by Starter User
// https://ancientbrain.com/world.php?world=4606438712
var pussRotation = 0;
const PUSS_STEP = 350;
const rotateConstant = Math.PI / 20;
const KEY_UP = "ArrowUp";
const KEY_LEFT = "ArrowLeft";
const KEY_RIGHT = "ArrowRight";
const KEY_DOWN = "ArrowDown";
var OURKEYS = [KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_DOWN];
function ourKeys(event) {
console.log(event.key);
return OURKEYS.includes(event.key);
}
function keyHandler(event) {
if (!AB.runReady) return true; // not ready yet
// if not handling this key, send it to default:
if (!ourKeys(event)) return true;
// else handle it and prevent default:
if (event.key == KEY_UP) {
// move a bit along angle we are facing
puss.position.x =
puss.position.x + PUSS_STEP * Math.sin(pussRotation);
puss.position.z =
puss.position.z + PUSS_STEP * Math.cos(pussRotation);
}
if (event.key == KEY_DOWN) {
// move a bit along angle we are facing
puss.position.x =
puss.position.x - PUSS_STEP * Math.sin(pussRotation);
puss.position.z =
puss.position.z - PUSS_STEP * Math.cos(pussRotation);
}
if (event.key == KEY_LEFT) {
// rotate in place
pussRotation = pussRotation + rotateConstant;
puss.rotation.set(0, pussRotation, 0);
}
if (event.key == KEY_RIGHT) {
pussRotation = pussRotation - rotateConstant;
puss.rotation.set(0, pussRotation, 0);
}
// lookat/follow depend on change in agent position/rotation:
// setLookatFollow();
event.stopPropagation();
event.preventDefault();
return false;
}
// skybox
function loadSkybox()
{
var geometry = new THREE.BoxGeometry(maxRadiusConst, maxRadiusConst, maxRadiusConst);
//image credit for skybox: http://www.humus.name/index.php?page=Textures&ID=138
var cubeMaterials =
[
new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/negz.jpg"), side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/posz.jpg"), side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/posy.jpg"), side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/negy.jpg"), side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/posx.jpg"), side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/negx.jpg"), side: THREE.DoubleSide } )
];
var cubeMaterial = new THREE.MeshFaceMaterial( cubeMaterials );
var cube = new THREE.Mesh( geometry, cubeMaterial );
ABWorld.scene.add(cube);
}
function loadPlayer()
{
var manager = new THREE.LoadingManager();
var loader = new THREE.OBJLoader( manager );
loader.load(OBJPATH + OBJNAME, buildpuss);
}
function buildpuss( object )
{
object.scale.multiplyScalar(4000);
console.log(object.children);
puss = object;
puss.position.x = AB.randomIntAtoB ( -MAXPOS, MAXPOS );
puss.position.z = AB.randomIntAtoB ( -MAXPOS, MAXPOS );
puss.position.y = -350;
threeworld.scene.add(puss);
}
function loadResources() // asynchronous file loads - call initScene() when all finished
{
for ( var i = 0; i < FILE_ARRAY.length; i++ )
startFileLoad ( i ); // launch n asynchronous file loads
}
function startFileLoad ( n ) // asynchronous file load of texture n
{
var loader = new THREE.TextureLoader();
loader.load ( FILE_ARRAY[n], function ( thetexture )
{
thetexture.minFilter = THREE.LinearFilter;
textureArray[n] = thetexture;
if ( asynchFinished() ) {
loadSkybox();
setFloor();
initArmy();
loadPlayer();
buildFence();
}
});
}
function asynchFinished() // all file loads returned
{
for ( var i = 0; i < FILE_ARRAY.length; i++ )
if ( ! textureArray[i] )
return false;
return true;
}
function setFloor()
{
var floor = new THREE.BoxGeometry( 60000, 1, 60000 );
var material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
var mesh = new THREE.Mesh(floor);
mesh.position.x = 0;
mesh.position.y = -350;
mesh.position.z = 0;
mesh.material = new THREE.MeshBasicMaterial({map: textureArray[1]});
ABWorld.scene.add(mesh);
}
function buildFence() {
// z axis fence
for (let i = 0; i < 30; i++) {
let fence = new THREE.BoxGeometry(2000, 2000, 1);
let fence_mesh = new THREE.Mesh(fence);
fence_mesh.position.x = (-29000 + (2000 * i));
fence_mesh.position.y = 650;
fence_mesh.position.z = 30000;
fence_mesh.material = new THREE.MeshBasicMaterial({map: textureArray[2]});
ABWorld.scene.add(fence_mesh);
}
for (let i = 0; i < 30; i++) {
let fence = new THREE.BoxGeometry(2000, 2000, 1);
let fence_mesh = new THREE.Mesh(fence);
fence_mesh.position.x = (-29000 + (2000 * i));
fence_mesh.position.y = 650;
fence_mesh.position.z = -30000;
fence_mesh.material = new THREE.MeshBasicMaterial({map: textureArray[2]});
ABWorld.scene.add(fence_mesh);
}
for (let i = 0; i < 30; i++) {
let fence = new THREE.BoxGeometry(1, 2000, 2000);
let fence_mesh = new THREE.Mesh(fence);
fence_mesh.position.x = 30000;
fence_mesh.position.y = 650;
fence_mesh.position.z = (-29000 + (2000 * i));
fence_mesh.material = new THREE.MeshBasicMaterial({map: textureArray[2]});
ABWorld.scene.add(fence_mesh);
}
for (var i = 0; i < 30; i++) {
var fence = new THREE.BoxGeometry(1, 2000, 2000);
var fence_mesh = new THREE.Mesh(fence);
fence_mesh.position.x = -30000;
fence_mesh.position.y = 650;
fence_mesh.position.z = (-29000 + (2000 * i));
fence_mesh.material = new THREE.MeshBasicMaterial({map: textureArray[2]});
ABWorld.scene.add(fence_mesh);
}
}
// pig sound source https://www.youtube.com/watch?v=Bg2BlPy1I30
var oink = new Audio("/uploads/przemyslawmajda/pig.mp3");
// based on Password Websocket boxes by Starter User
// https://ancientbrain.com/world.php?world=9623346805
function initArmy() // called when all textures ready
{
var t = 0;
for ( var c=1 ; c <= ARMYSIZE ; c++ )
{
// var shape = new THREE.SphereGeometry ( objectsize, 30, 30 );
var shape = new THREE.BoxGeometry( objectsize, objectsize, objectsize );
var theobject = new THREE.Mesh( shape );
theobject.position.x = AB.randomIntAtoB ( -MAXPOS, MAXPOS );
theobject.position.z = AB.randomIntAtoB ( -MAXPOS, MAXPOS );
theobject.position.y = 0;
// pig image credit http://clipart-library.com/clipart/344716.htm
var r = AB.randomIntAtoB ( 0, 4 ); // random one of the earths
theobject.material = new THREE.MeshBasicMaterial ( { map: textureArray[0] } );
ABWorld.scene.add(theobject);
THEARMY[t] = theobject; // save it for later
t++;
}
// can start the run loop
ABWorld.render();
AB.runReady = true;
AB.msg ( ` <hr> <p> Multi-user game. Kill as many pigs as you can before they escape! Player with the most pig kills in 40 seconds wins. <p id="score">Score: 0<p>
<p id="highest_score">Highest Score (All players): 0</p>
<p id="final_score"></p>` );
}
function moveArmy() // move all the objects
{
for ( var i = 0; i < THEARMY.length; i++ )
{
if ( THEARMY[i] ) // in case initArmy() not called yet
{
THEARMY[i].position.x = THEARMY[i].position.x + AB.randomIntAtoB(-WALKSTEP *10,WALKSTEP*10) ;
THEARMY[i].position.z = THEARMY[i].position.z + AB.randomIntAtoB(-WALKSTEP*10,WALKSTEP*10) ;
// dont allow pigs outside the zone
ABWorld.scene.add( THEARMY[i] );
}
}
}
function checkArmy() {
for ( var i = 0; i < THEARMY.length; i++ ) {
if (THEARMY[i]){
if ( THEARMY[i].position.x > 29000 || THEARMY[i].position.x < -29000 || THEARMY[i].position.z > 29000 || THEARMY[i].position.z < -29000){
console.log(THEARMY[i].position.x);
console.log(THEARMY[i].position.z);
// experimental:
ABWorld.scene.remove(THEARMY[i]);
delete THEARMY[i];
console.log("BOUNDARY CROSSED: ELIMINATE PIG");
}
}
}
}
function killPig() {
var pussx = puss.position.x;
var pussz = puss.position.z;
for ( var i = 0; i < THEARMY.length; i++ ) {
if (THEARMY[i]){
let pigx = THEARMY[i].position.x;
let pigz = THEARMY[i].position.z;
if (pigx <= (pussx + 900) && pigx >= (pussx - 900)){
if (pigz <= (pussz + 900) && pigz >= (pussz - 900)) {
console.log(THEARMY[i].position.x);
console.log(THEARMY[i].position.z);
score = score + 1;
updateScore(score);
ABWorld.scene.remove(THEARMY[i]);
delete THEARMY[i];
CONFIRMED_KILLS.push(i);
console.log("PIG LOCATED AND ELIMINATED");
oink.play();
}
}
}
}
}
// makign sure our hero cannot escape the RANCH
function checkPussPosition() {
if (puss.position.x > 29000) {
puss.position.x = 29000;
}
else if (puss.position.x < -29000) {
puss.position.x = -29000;
}
else if (puss.position.z > 29000) {
puss.position.z = 29000;
}
else if (puss.position.z < -29000) {
puss.position.z = -29000;
}
}
function updateScore(n) {
pContent = document.getElementById("score");
pContent.innerHTML = "Score: " + n;
}
function finalScore() {
if (AB.step >= 1200) {
pContent = document.getElementById("final_score");
if (high <= score) {
pContent.innerHTML = "You won! Your score was " + score;
}
else {
pContent.innerHTML = "You were NOT the winner :( The highest score was " + high + ". Better luck next time!";
}
}
}
AB.world.newRun = function()
{
AB.newSplash();
AB.splashHtml ( `
<h1> Password only Websocket boxes </h1>
A version of Websocket boxes that only certain users can join. <br>
You enter a password. You only join with users who enter the same password (not with all users of the World). <br>
The first user to run the World can enter any password! But other users must know what password they will use.
<p>
Enter password:
<input style='width:25vw;' maxlength='2000' NAME="p" id="p" VALUE='' >
<button onclick='start();' class=ab-normbutton >Start</button>
<p>
<div id=errordiv name=errordiv> </div> ` );
AB.runReady = false;
ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
// var ambient = new THREE.AmbientLight();
// ABWorld.scene.add(ambient);
var stageLight = new THREE.DirectionalLight(0xa2a2a2, 1.5);
stageLight.position.set(startRadiusConst, startRadiusConst, startRadiusConst);
ABWorld.scene.add(stageLight);
loadResources(); // aynch file loads
document.onkeydown = keyHandler;
// calls initArmy() when it returns
};
AB.world.nextStep = function()
{
moveArmy();
checkArmy();
killPig();
checkPussPosition();
sendScore();
highestScore();
finalScore();
};
//--- start Socket -----------------------------------------------------
function start() // user has entered a password
{
var password = jQuery("input#p").val();
password = password.trim();
if ( ! alphanumeric ( password ) )
{
$("#errordiv").html( "<font color=red> <B> Error: Password must be alphanumeric. </b></font> " );
return;
}
// else we have a password, start the socket run with this password
AB.socketStart ( password );
AB.removeSplash();
}
function alphanumeric ( str ) // return if string is just alphanumeric
{
var rc = str.match(/^[0-9a-zA-Z]+$/); // start of line "[0-9a-zA-Z]+" end of line
if ( rc ) return true;
else return false;
}
//--- Socket functionality -----------------------------------------------------
// functions called by buttons
// baby and skull are textures 5 and 6 in the array:
var high = 0;
function highestScore() {
pContent = document.getElementById("highest_score");
if (high > score) {
pContent.innerHTML = "Highest Score (All players): " + high;
}
else {
pContent.innerHTML = "Highest Score (All players): " + score + " (you)";
}
}
function sendScore() {
AB.socketOut(score);
}
AB.socketIn = function(n) // incoming data on socket, i.e. clicks of other player
{
if ( ! AB.runReady ) return;
high = n;
};