// Cloned by Przemyslaw Majda on 29 Nov 2022 from World "User-controlled Model World (clone by DistroByte)" by DistroByte
// Please leave this clone trail here.
// Cloned by DistroByte on 24 Nov 2022 from World "User-controlled Model 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.
// ====================================================================================================================
// User controlled (not Mind controlled) 3D model World with various features:
// - User controlled UP/LEFT/RIGHT arrows move model
// - "First Person View" - Camera rotates with direction you are facing
// Best effect is with "Move with" camera
// - Can have one or multiple skeletons chasing you
// Smooth movement
// UP moves forward in whatever angle you are at
// LEFT/RIGHT rotate by small angle
// Uses x,y,z rather than grid of squares
// Has collision detection for skeleton moves
// Things to do:
// - Initialise skeletons so they are not already colliding
// - Collision detection for agent moves
// ===================================================================================================================
// === 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. Default 100.
AB.maxSteps = 10000;
// Length of run: Maximum length of run in steps. Default 1000.
AB.screenshotStep = 100;
// Take screenshot on this step. (All resources should have finished loading.) Default 50.
AB.drawRunControls = false;
// Scrap the Run/Step/Pause controls
//---- global constants: -------------------------------------------------------
// number of skeletons:
const numPlayers = 50;
const playerObject = "/uploads/distro/player.obj";
const objPath = "/uploads/distro/"; // path of OBJ and MTL (Peter Parker model)
const objName = "doll.obj";
const mtlName = "doll.mtl";
// multiply model sizes by some amount:
const scalePlayer = 100;
// random size skeleton with each run:
const scaleDoll = AB.randomFloatAtoB(1, 6);
const dollTexture = "/uploads/distro/doll.png";
const maxWidth = 3500; // length of one side of the arena
const skyColour = 0xffffcc; // a number, not a string
const lightColour = 0xffffff;
const startRadiusConst = maxWidth; // distance from centre to start the camera at
const maxRadiusConst = maxWidth * 10; // maximum distance from camera we will render things
// how much agent moves each step
const agentStep = 100;
// how much enemy moves each step
const enemyStep = 0;
// this is as close as models come to other models
const minDistanceLimit = 50;
//--- lookat and follow -----------------------------------------------------------------
// camera on "Move With" should move up/down in y axis as we re-scale objects
// to place the follow camera just above the agent, something like:
const followY= scalePlayer * 4; // going high up (or forward/back) means we get it out of agent's hair
// to point the camera at skeleton's face, something like:
const lookatY = scaleDoll * 40;
//const CAMERASHIFT = 0 ;// put camera exactly inside agent's head
//const CAMERASHIFT = SCALE_HERO * 2 ;// shift camera ahead of agent along line of sight
const cameraShift = -scalePlayer * 2; // shift camera behind agent, looking past agent along line of sight
//--- change ABWorld defaults: -------------------------------
ABHandler.MAXCAMERAPOS = maxRadiusConst;
ABHandler.GROUNDZERO = true; // "ground" exists at altitude zero
// --- Rotations -----------------------
// rotate by some amount of radians from the normal position
// default is 0 which has the model facing DOWN (towards increasing z value) as far as the initial camera sees
const rotateConstant = Math.PI / 20; // rotate amount in radians (PI = 180 degrees)
//--- skybox: -------------------------------
// urban photographic skyboxes, credit:
// http://opengameart.org/content/urban-skyboxes
const SKYBOX_ARRAY = [
"/uploads/starter/posx.jpg",
"/uploads/starter/negx.jpg",
"/uploads/starter/posy.jpg",
"/uploads/starter/negy.jpg",
"/uploads/starter/posz.jpg",
"/uploads/starter/negz.jpg",
];
// ===================================================================================================================
// === End of tweaker's box ==========================================================================================
// ===================================================================================================================
// You will need to be some sort of JavaScript programmer to change things below the tweaker's box.
var players = new Array(numPlayers);
var player, doll;
var agentRotation = 0;
var enemyRotation = 0;
function loadResources() {
// asynchronous file loads - call initScene() when all finished
// load skeleton - OBJ that we will paint with a texture
var loader = new THREE.OBJLoader(new THREE.LoadingManager());
loader.load(playerObject, function (object) {
player = object; // canonical enemy object - may be copied multiple times
if (asynchFinished()) initScene();
});
// load Peter Parker model - OBJ plus MTL (plus TGA files)
// old code:
// THREE.Loader.Handlers.add ( /.tga$/i, new THREE.TGALoader() );
THREE.DefaultLoadingManager.addHandler(/\.tga$/i, new THREE.TGALoader());
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) {
doll = object;
if (asynchFinished()) initScene();
});
});
// load textures
var loader2 = new THREE.TextureLoader();
loader2.load(dollTexture, function (texture) {
texture.minFilter = THREE.LinearFilter;
dollTexture = texture;
if (asynchFinished()) initScene();
});
}
function asynchFinished() {
// all file loads returned
if (dollTexture && doll && player) return true;
else return false;
}
function initScene() {
// all file loads have returned
// add agent model
// start at center
player.position.y = 0;
player.position.x = 0;
player.position.z = 0;
player.scale.multiplyScalar(scalePlayer); // scale it
ABWorld.scene.add(player);
// set up lookat and follow in direction agent is pointing in
setLookatFollow();
// add enemies
// first paint and size the canonical enemy object
theenemy.traverse(function (child) {
if (child instanceof THREE.Mesh) child.material.map = skeleton_texture;
});
theenemy.scale.multiplyScalar(SCALE_SKELETON); // scale it
// add perhaps multiple enemy models, starting near outside of arena
for (var i = 0; i < NO_SKELETONS; i++) {
var object = theenemy.clone(); // copy the object multiple times
object.position.y = 0;
object.position.x = AB.randomPick(
AB.randomIntAtoB(-MAXPOS / 2, -MAXPOS / 3),
AB.randomIntAtoB(MAXPOS / 3, MAXPOS / 2)
);
object.position.z = AB.randomPick(
AB.randomIntAtoB(-MAXPOS / 2, -MAXPOS / 3),
AB.randomIntAtoB(MAXPOS / 3, MAXPOS / 2)
);
ABWorld.scene.add(object);
// save in array for later
THESKELETONS[i] = object;
}
// finally skybox
ABWorld.scene.background = new THREE.CubeTextureLoader().load(
SKYBOX_ARRAY,
function () {
ABWorld.render();
AB.removeLoading();
AB.runReady = true;
}
);
}
// --- lookat and follow -----------------------------------
// we do NOT automatically look at enemy / enemies
// instead we look in direction we are facing
function setLookatFollow() {
// set up lookat and follow
// follow - camera position
// start with agent centre, then adjust it by small amount
ABWorld.follow.copy(player.position);
ABWorld.follow.y = FOLLOW_Y;
// shifted forward/back along line linking agent with direction it is facing
ABWorld.follow.x = ABWorld.follow.x + CAMERASHIFT * Math.sin(agentRotation);
ABWorld.follow.z = ABWorld.follow.z + CAMERASHIFT * Math.cos(agentRotation);
// lookat - look at point in distance along line we are facing
// start with agent centre, then adjust it along line by huge amount
ABWorld.lookat.copy(player.position);
ABWorld.lookat.y = LOOKAT_Y;
ABWorld.lookat.x =
ABWorld.lookat.x + startRadiusConst * 3 * Math.sin(agentRotation);
ABWorld.lookat.z =
ABWorld.lookat.z + startRadiusConst * 3 * Math.cos(agentRotation);
}
// --- enemy move -----------------------------------
// angle between two points (x1,y1) and (x2,y2)
function angleTwoPoints(x1, y1, x2, y2) {
return Math.atan2(x2 - x1, y2 - y1);
}
function dist(a, b) {
// distance between them when a,b are Three vectors
return a.distanceTo(b);
}
function collision(proposedMove, k) {
// proposed move FOR skeleton k
if (dist(proposedMove, player.position) < CLOSE_LIMIT) return true;
for (var i = 0; i < NO_SKELETONS; i++)
if (i != k)
if (dist(proposedMove, THESKELETONS[i].position) < CLOSE_LIMIT)
return true;
// else
return false;
}
function moveEnemy() {
for (var i = 0; i < NO_SKELETONS; i++) {
var e = THESKELETONS[i];
// rotate enemy to face agent
enemyRotation = angleTwoPoints(
e.position.x,
e.position.z,
player.position.x,
player.position.z
);
e.rotation.set(0, enemyRotation, 0);
// move along that line, if no collision with any other model
var proposedMove = new THREE.Vector3(
e.position.x + ENEMY_STEP * Math.sin(enemyRotation),
e.position.y,
e.position.z + ENEMY_STEP * Math.cos(enemyRotation)
);
if (!collision(proposedMove, i)) e.position.copy(proposedMove);
// else enemy i just misses a turn
}
}
//--- key control of agent -------------------------------------------------------------
const KEY_UP = 38;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;
var OURKEYS = [KEY_UP, KEY_LEFT, KEY_RIGHT];
function ourKeys(event) {
return OURKEYS.includes(event.keyCode);
}
// UP moves forward in whatever angle you are at
// LEFT/RIGHT rotate by small angle
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.keyCode == KEY_UP) {
// move a bit along angle we are facing
player.position.x =
player.position.x + AGENT_STEP * Math.sin(agentRotation);
player.position.z =
player.position.z + AGENT_STEP * Math.cos(agentRotation);
}
if (event.keyCode == KEY_LEFT) {
// rotate in place
agentRotation = agentRotation + ROTATE_AMOUNT;
player.rotation.set(0, agentRotation, 0);
}
if (event.keyCode == KEY_RIGHT) {
agentRotation = agentRotation - ROTATE_AMOUNT;
player.rotation.set(0, agentRotation, 0);
}
// lookat/follow depend on change in agent position/rotation:
setLookatFollow();
event.stopPropagation();
event.preventDefault();
return false;
}
AB.world.newRun = function () {
AB.loadingScreen();
AB.runReady = false;
ABWorld.init3d(startRadiusConst, maxRadiusConst, skyColour);
loadResources(); // aynch file loads
// calls initScene() when it returns
// light
var ambient = new THREE.AmbientLight();
ABWorld.scene.add(ambient);
var stageLight = new THREE.DirectionalLight(lightColour, 3);
stageLight.position.set(startRadiusConst, startRadiusConst, startRadiusConst);
ABWorld.scene.add(stageLight);
document.onkeydown = keyHandler;
};
AB.world.nextStep = function () {
moveEnemy(); // enemies moves on their own clock
};