overte-lubosz/script-archive/airship/airship.js

301 lines
9.7 KiB
JavaScript

//
// airship.js
//
// Animates a pirate airship that chases people and shoots cannonballs at them
//
// Created by Philip Rosedale on March 7, 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function () {
var entityID,
minimumDelay = 100, // milliseconds
distanceScale = 2.0, // distance at which 100% chance of hopping
timeScale = 300.0, // crash
hopStrength = 0.4, // meters / second
spotlight = null,
wantDebug = false,
timeoutID = undefined,
bullet = null,
particles = null,
nearbyAvatars = 0,
nearbyAvatarsInRange = 0,
averageAvatarLocation = { x: 0, y: 0, z: 0 },
properties,
lightTimer = 0,
lightTimeoutID = undefined,
audioInjector = null;
var OVERTE_PUBLIC_CDN = networkingConstants.PUBLIC_BUCKET_CDN_URL;
var cannonSound = SoundCache.getSound(OVERTE_PUBLIC_CDN + "philip/cannonShot.wav");
var explosionSound = SoundCache.getSound(OVERTE_PUBLIC_CDN + "philip/explosion.wav");
var NO_SHOOT_COLOR = { red: 100, green: 100, blue: 100 };
var MAX_TARGET_RANGE = 200;
function printDebug(message) {
if (wantDebug) {
print(message);
}
}
var LIGHT_UPDATE_INTERVAL = 50;
var LIGHT_LIFETIME = 700;
function randomVector(size) {
return { x: (Math.random() - 0.5) * size,
y: (Math.random() - 0.5) * size,
z: (Math.random() - 0.5) * size };
}
function makeLight(parent, position, colorDivisor) {
// Create a flickering light somewhere for a while
if (spotlight !== null) {
// light still exists, do nothing
printDebug("light still there");
return;
}
printDebug("making light");
var colorIndex = 180 + Math.random() * 50;
spotlight = Entities.addEntity({
type: "Light",
name: "Test Light",
intensity: 50.0,
falloffRadius: 20.0,
dimensions: {
x: 150,
y: 150,
z: 150
},
position: position,
parentID: parent,
color: {
red: colorIndex,
green: colorIndex / colorDivisor,
blue: 0
},
lifetime: LIGHT_LIFETIME * 2
});
lightTimer = 0;
lightTimeoutID = Script.setTimeout(updateLight, LIGHT_UPDATE_INTERVAL);
};
function updateLight() {
lightTimer += LIGHT_UPDATE_INTERVAL;
if ((spotlight !== null) && (lightTimer > LIGHT_LIFETIME)) {
printDebug("deleting light!");
Entities.deleteEntity(spotlight);
spotlight = null;
} else {
Entities.editEntity(spotlight, {
intensity: 5 + Math.random() * 50,
falloffRadius: 5 + Math.random() * 10
});
lightTimeoutID = Script.setTimeout(updateLight, LIGHT_UPDATE_INTERVAL);
}
}
function move() {
var HOVER_DISTANCE = 30.0;
var RUN_TOWARD_STRENGTH = 0.002;
var VELOCITY_FOLLOW_RATE = 0.01;
var range = Vec3.distance(properties.position, averageAvatarLocation);
var impulse = { x: 0, y: 0, z: 0 };
// move in the XZ plane
var away = Vec3.subtract(properties.position, averageAvatarLocation);
away.y = 0.0;
if (range > HOVER_DISTANCE) {
impulse = Vec3.multiply(-RUN_TOWARD_STRENGTH, Vec3.normalize(away));
}
var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, properties.velocity);
Entities.editEntity(entityID, {velocity: Vec3.sum(properties.velocity, impulse), rotation: Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE)});
}
function countAvatars() {
var workQueue = AvatarList.getAvatarIdentifiers();
var averageLocation = {x: 0, y: 0, z: 0};
var summed = 0;
var inRange = 0;
for (var i = 0; i < workQueue.length; i++) {
var avatar = AvatarList.getAvatar(workQueue[i]), avatarPosition = avatar && avatar.position;
if (avatarPosition) {
averageLocation = Vec3.sum(averageLocation, avatarPosition);
summed++;
if (Vec3.distance(avatarPosition, properties.position) < MAX_TARGET_RANGE) {
inRange++;
}
}
}
if (summed > 0) {
averageLocation = Vec3.multiply(1 / summed, averageLocation);
}
nearbyAvatars = summed;
nearbyAvatarsInRange = inRange;
averageAvatarLocation = averageLocation;
//printDebug(" Avatars: " + summed + "in range: " + nearbyAvatarsInRange);
//Vec3.print(" location: ", averageAvatarLocation);
return;
}
function shoot() {
if (bullet !== null) {
return;
}
if (Vec3.distance(MyAvatar.position, properties.position) > MAX_TARGET_RANGE) {
return;
}
var SPEED = 7.5;
var GRAVITY = -2.0;
var DIAMETER = 0.5;
var direction = Vec3.subtract(MyAvatar.position, properties.position);
var range = Vec3.distance(MyAvatar.position, properties.position);
var timeOfFlight = range / SPEED;
var fall = 0.5 * -GRAVITY * (timeOfFlight * timeOfFlight);
var velocity = Vec3.multiply(SPEED, Vec3.normalize(direction));
velocity.y += 0.5 * fall / timeOfFlight * 2.0;
var DISTANCE_TO_DECK = 3;
var bulletStart = properties.position;
bulletStart.y -= DISTANCE_TO_DECK;
makeLight(entityID, Vec3.sum(properties.position, randomVector(10.0)), 2);
bullet = Entities.addEntity({
type: "Sphere",
name: "cannonball",
position: properties.position,
dimensions: { x: DIAMETER, y: DIAMETER, z: DIAMETER },
color: { red: 10, green: 10, blue: 10 },
velocity: velocity,
damping: 0.01,
dynamic: true,
ignoreForCollisions: true,
gravity: { x: 0, y: GRAVITY, z: 0 },
lifetime: timeOfFlight * 2
});
Audio.playSound(cannonSound, {
position: Vec3.sum(MyAvatar.position, velocity),
volume: 1.0
});
makeParticles(properties.position, bullet, timeOfFlight * 2);
Script.setTimeout(explode, timeOfFlight * 1000);
}
function explode() {
var properties = Entities.getEntityProperties(bullet);
var direction = Vec3.normalize(Vec3.subtract(MyAvatar.position, properties.position));
makeLight(null, properties.position, 10);
Audio.playSound(explosionSound, { position: Vec3.sum(MyAvatar.position, direction), volume: 1.0 });
bullet = null;
}
function maybe() { // every user checks their distance and tries to claim if close enough.
var PROBABILITY_OF_SHOOT = 0.015;
properties = Entities.getEntityProperties(entityID);
countAvatars();
if (nearbyAvatars && (Math.random() < 1 / nearbyAvatars)) {
move();
}
if (nearbyAvatarsInRange && (Math.random() < PROBABILITY_OF_SHOOT / nearbyAvatarsInRange)) {
shoot();
}
var TIME_TO_NEXT_CHECK = 33;
timeoutID = Script.setTimeout(maybe, TIME_TO_NEXT_CHECK);
}
this.preload = function (givenEntityID) {
printDebug("preload airship v1...");
entityID = givenEntityID;
properties = Entities.getEntityProperties(entityID);
timeoutID = Script.setTimeout(maybe, minimumDelay);
};
this.unload = function () {
printDebug("unload airship...");
if (timeoutID !== undefined) {
Script.clearTimeout(timeoutID);
}
if (lightTimeoutID !== undefined) {
Script.clearTimeout(lightTimeoutID);
}
if (spotlight !== null) {
Entities.deleteEntity(spotlight);
}
};
function makeParticles(position, parent, lifespan) {
particles = Entities.addEntity({
type: 'ParticleEffect',
position: position,
parentID: parent,
color: {
red: 70,
green: 70,
blue: 70
},
isEmitting: 1,
maxParticles: 1000,
lifetime: lifespan,
lifespan: lifespan / 3,
emitRate: 80,
emitSpeed: 0,
speedSpread: 1.0,
emitRadiusStart: 1,
polarStart: -Math.PI/8,
polarFinish: Math.PI/8,
azimuthStart: -Math.PI/4,
azimuthFinish: Math.PI/4,
emitAcceleration: { x: 0, y: 0, z: 0 },
particleRadius: 0.25,
radiusSpread: 0.1,
radiusStart: 0.3,
radiusFinish: 0.15,
colorSpread: {
red: 100,
green: 100,
blue: 0
},
colorStart: {
red: 125,
green: 125,
blue: 125
},
colorFinish: {
red: 10,
green: 10,
blue: 10
},
alpha: 0.5,
alphaSpread: 0,
alphaStart: 1,
alphaFinish: 0,
emitterShouldTrail: true,
textures: 'https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png'
});
}
})