overte-lubosz/script-archive/drylake/ratCreator.js
2016-05-25 11:11:06 -07:00

471 lines
12 KiB
JavaScript

//
// ratCreator.js
//
// Created by James B. Pollack @imgntn on 12/7/2015
// Copyright 2015 High Fidelity, Inc.
//
// This script spawns some rats that have simple steering behaviors applied to them.
// Run it in the 'drylake' environment, or adjust all object locations to match your scene.
//
// Steering bevhaviors from ratSteer.js:
// The rats will move from a spawning point toward their nest.
// They will avoid avoider blocks moving across the alley
// They will avoid avatars running createAvatarDetector.js
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include('ratSteer.js');
var steer = loadSteer();
Script.include('../libraries/tween.js');
var TWEEN = loadTween();
var USE_CONSTANT_SPAWNER = true;
var RAT_SPAWNER_LOCATION = {
x: 1000.5,
y: 98,
z: 1040
};
var RAT_NEST_LOCATION = {
x: 1003.5,
y: 99,
z: 964.2
};
var RAT_DIMENSIONS = {
x: 0.22,
y: 0.32,
z: 1.14
};
var RAT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/models/rat_model.fbx';
var RAT_IDLE_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/idle.fbx';
var RAT_WALKING_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/walk.fbx';
var RAT_RUNNING_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/run.fbx';
var RAT_DEATH_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/death.fbx';
var RAT_IN_NEST_DISTANCE = 4;
//how many milliseconds between rats
var RAT_SPAWN_RATE = 2500;
var RAT_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Rats_Squeaks_Active.wav';
var ratRunningSound = SoundCache.getSound(RAT_SOUND_URL);
function playRatRunningAnimation(rat) {
var animationSettings = JSON.stringify({
running: true
});
Entities.editEntity(rat, {
animationURL: RAT_RUNNING_ANIMATION_URL,
animation: {
url: RAT_RUNNING_ANIMATION_URL,
running: true,
fps: 30
},
});
}
function playRatDeathAnimation(rat) {
var animationSettings = JSON.stringify({
running: true
});
Entities.editEntity(rat, {
animationURL: RAT_DEATH_ANIMATION_URL,
animationSettings: animationSettings
});
}
var modelRatProperties = {
name: 'rat',
type: 'Model',
modelURL: RAT_MODEL_URL,
dimensions: RAT_DIMENSIONS,
position: RAT_SPAWNER_LOCATION,
shapeType: 'Box',
damping: 0.8,
angularDamping: 0.99,
friction: 0.75,
dynamic: true,
collisionless: false,
gravity: {
x: 0,
y: -9.8,
z: 0
},
lifetime: 30,
userData: JSON.stringify({
grabbableKey: {
grabbable: false
}
})
};
var targetProperties = {
name: 'Hifi-Rat-Nest',
type: 'Box',
color: {
red: 0,
green: 255,
blue: 0
},
dimensions: {
x: 1,
y: 1,
z: 1
},
visible: false,
position: RAT_NEST_LOCATION
};
var target = Entities.addEntity(targetProperties);
function addRat() {
var rat = Entities.addEntity(modelRatProperties);
return rat;
}
//every sixth rat will play a sound
var RAT_SOUND_RATE = 6;
//spawn rate will be multiplied by this to clear any sounds hanging around
var RAT_SOUND_CLEAR_RATE = 3;
var rats = [];
var metaRats = [];
var ratCount = 0;
var AVOIDER_Y_HEIGHT = 99;
var FIRST_AVOIDER_START_POSITION = {
x: 1004,
y: AVOIDER_Y_HEIGHT,
z: 1019
};
var FIRST_AVOIDER_FINISH_POSITION = {
x: 997,
y: AVOIDER_Y_HEIGHT,
z: 1019
};
var SECOND_AVOIDER_START_POSITION = {
x: 998,
y: AVOIDER_Y_HEIGHT,
z: 998
};
var SECOND_AVOIDER_FINISH_POSITION = {
x: 1005,
y: AVOIDER_Y_HEIGHT,
z: 999
};
var THIRD_AVOIDER_START_POSITION = {
x: 1001.5,
y: 100,
z: 978
};
var THIRD_AVOIDER_FINISH_POSITION = {
x: 1005,
y: 100,
z: 974
};
cleanupLeftoverAvoidersBeforeStart();
var avoiders = [];
addAvoiderBlock(FIRST_AVOIDER_START_POSITION);
addAvoiderBlock(SECOND_AVOIDER_START_POSITION);
addAvoiderBlock(THIRD_AVOIDER_START_POSITION);
function addAvoiderBlock(position) {
var avoiderProperties = {
name: 'Hifi-Rat-Avoider',
type: 'Box',
color: {
red: 255,
green: 0,
blue: 255
},
dimensions: {
x: 1,
y: 1,
z: 1
},
position: position,
dynamic: false,
collisionless: true,
visible: false
};
var avoider = Entities.addEntity(avoiderProperties);
avoiders.push(avoider);
}
tweenAvoider(avoiders[0], FIRST_AVOIDER_START_POSITION, FIRST_AVOIDER_FINISH_POSITION);
tweenAvoider(avoiders[1], SECOND_AVOIDER_START_POSITION, SECOND_AVOIDER_FINISH_POSITION);
tweenAvoider(avoiders[2], THIRD_AVOIDER_START_POSITION, THIRD_AVOIDER_FINISH_POSITION);
function tweenAvoider(entityID, startPosition, endPosition) {
var ANIMATION_DURATION = 4200;
var begin = {
x: startPosition.x,
y: startPosition.y,
z: startPosition.z
};
var end = endPosition;
var original = startPosition;
var tweenHead = new TWEEN.Tween(begin).to(end, ANIMATION_DURATION);
function updateTo() {
Entities.editEntity(entityID, {
position: {
x: begin.x,
y: begin.y,
z: begin.z
}
});
}
function updateBack() {
Entities.editEntity(entityID, {
position: {
x: begin.x,
y: begin.y,
z: begin.z
}
})
}
var tweenBack = new TWEEN.Tween(begin).to(original, ANIMATION_DURATION).onUpdate(updateBack);
tweenHead.onUpdate(function() {
updateTo()
});
tweenHead.chain(tweenBack);
tweenBack.chain(tweenHead);
tweenHead.start();
}
function updateTweens() {
TWEEN.update();
}
function createRatSoundInjector() {
var audioOptions = {
volume: 0.05,
loop: false
};
var injector = Audio.playSound(ratRunningSound, audioOptions);
return injector;
}
function moveRats() {
rats.forEach(function(rat) {
//remove the rat if its near the nest
checkDistanceFromNest(rat);
//see if there are avatars to run from
var avatarFlightVectors = steer.fleeAllAvatars(rat);
var averageAvatarFlight;
var i;
for (i = 0; i < avatarFlightVectors.length; i++) {
if (i === 0) {
averageAvatarFlight = avatarFlightVectors[0];
} else {
averageAvatarFlight = Vec3.sum(avatarFlightVectors[i - 1], avatarFlightVectors[i]);
}
}
averageAvatarFlight = Vec3.multiply(averageAvatarFlight, 1 / avatarFlightVectors.length);
//see if there are avoiders to flee
var avoidBlockVectors = steer.fleeAvoiderBlocks(rat);
var averageAvoiderFlight;
var j;
for (j = 0; j < avoidBlockVectors.length; j++) {
if (j === 0) {
averageAvoiderFlight = avoidBlockVectors[0];
} else {
averageAvoiderFlight = Vec3.sum(avoidBlockVectors[j - 1], avoidBlockVectors[j]);
}
};
averageAvoiderFlight = Vec3.multiply(averageAvoiderFlight, 1 / avoidBlockVectors.length);
//add all of the vectors and divide them by total to get average vector
//start by trying to go toward the nest
var seek = steer.arrive(rat, target);
var averageVector = seek;
var divisorCount = 1;
//if there are avatars to run away from
if (avatarFlightVectors.length > 0) {
divisorCount++;
averageVector = Vec3.sum(averageVector, averageAvatarFlight);
}
//or if there are avoider blocks to run away from
if (avoidBlockVectors.length > 0) {
divisorCount++;
averageVector = Vec3.sum(averageVector, averageAvoiderFlight);
}
averageVector = Vec3.multiply(averageVector, 1 / divisorCount);
var thisRatProps = Entities.getEntityProperties(rat, ["position", "rotation"]);
var ratPosition = thisRatProps.position;
var ratToNest = Vec3.subtract(RAT_NEST_LOCATION, ratPosition);
var ratRotation = Quat.rotationBetween(Vec3.UNIT_Z, ratToNest);
var eulerAngle = Quat.safeEulerAngles(ratRotation);
eulerAngle.x = 0;
eulerAngle.z = 0;
var constrainedRotation = Quat.fromVec3Degrees(eulerAngle);
Entities.editEntity(rat, {
velocity: averageVector,
rotation: constrainedRotation,
});
//have to make a 'meta' rat object to keep track of rats for updating sound injector locations. parenting sounds would make this easy.
var metaRat = getMetaRatByRat(rat);
if (metaRat !== undefined) {
if (metaRat.injector !== undefined) {
if (metaRat.injector.playing === true) {
metaRat.injector.options = {
loop: true,
position: ratPosition
};
}
}
}
})
}
Script.update.connect(moveRats)
Script.update.connect(updateTweens);
function checkDistanceFromNest(rat) {
var ratProps = Entities.getEntityProperties(rat, "position");
var distance = Vec3.distance(ratProps.position, RAT_NEST_LOCATION);
if (distance < RAT_IN_NEST_DISTANCE) {
//at nest
removeRatFromScene(rat);
}
}
function removeRatFromScene(rat) {
var index = rats.indexOf(rat);
if (index > -1) {
rats.splice(index, 1);
Entities.deleteEntity(rat);
}
var metaRatIndex = findWithAttr(metaRats, 'rat', rat);
if (metaRatIndex > -1) {
metaRats[index].injector.stop();
metaRats.splice(index, 1);
}
}
function popRatFromStack(entityID) {
var index = rats.indexOf(entityID);
if (index > -1) {
rats.splice(index, 1);
}
var metaRatIndex = findWithAttr(metaRats, 'rat', entityID);
if (metaRatIndex > -1) {
metaRats[index].injector.stop();
metaRats.splice(index, 1);
}
}
function findWithAttr(array, attr, value) {
for (var i = 0; i < array.length; i += 1) {
if (array[i][attr] === value) {
return i;
}
}
}
function getMetaRatByRat(rat) {
var result = metaRats.filter(function(metaRat) {
return rat === metaRat.rat;
});
return result[0];
}
Entities.deletingEntity.connect(popRatFromStack);
function cleanupLeftoverAvoidersBeforeStart() {
//sometimes if we crash or something there could be extra avoider blocks around. clear them out.
var nearbyEntities = Entities.findEntities(RAT_SPAWNER_LOCATION, 100);
var entityIndex;
for (entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) {
var entityID = nearbyEntities[entityIndex];
var entityProps = Entities.getEntityProperties(entityID);
if (entityProps.name === 'Hifi-Rat-Avoider') {
Entities.deleteEntity(entityID);
}
}
}
function cleanup() {
while (rats.length > 0) {
Entities.deleteEntity(rats.pop());
}
while (avoiders.length > 0) {
Entities.deleteEntity(avoiders.pop());
}
Entities.deleteEntity(target);
Script.update.disconnect(moveRats);
Script.update.disconnect(updateTweens);
Entities.deletingEntity.disconnect(popRatFromStack);
Script.clearInterval(ratSpawnerInterval);
}
Script.scriptEnding.connect(cleanup);
var ratSpawnerInterval;
if (USE_CONSTANT_SPAWNER === true) {
ratSpawnerInterval = Script.setInterval(function() {
var rat = addRat();
playRatRunningAnimation(rat);
rats.push(rat);
ratCount++;
if (ratCount % RAT_SOUND_RATE === 0) {
var metaRat = {
rat: rat,
injector: createRatSoundInjector()
}
metaRats.push(metaRat);
Script.setTimeout(function() {
//if we have too many injectors hanging around there are problems
metaRat.injector.stop();
delete metaRat.injector;
}, RAT_SPAWN_RATE * RAT_SOUND_CLEAR_RATE;
}
}, RAT_SPAWN_RATE);
}