mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
301 lines
9.7 KiB
JavaScript
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'
|
|
});
|
|
}
|
|
})
|