333 lines
No EOL
10 KiB
JavaScript
333 lines
No EOL
10 KiB
JavaScript
// Sounds from:
|
|
// Whispher: https://freesound.org/people/DRFX/sounds/350763/
|
|
|
|
|
|
(function () {
|
|
|
|
var Sound = Script.require("http://hifi-content.s3-us-west-1.amazonaws.com/robin/dev/utils/Sound.js?v" + Math.random());
|
|
|
|
var overlayID;
|
|
var entityID;
|
|
|
|
var endCondition = false;
|
|
|
|
var startPosition;
|
|
|
|
var distances = [8, 6, 2, 1.5, 1];
|
|
var count = 0;
|
|
|
|
var MIN_TIME_INVISIBLE = 100;
|
|
var MAX_TIME_INVISIBLE = 2000;
|
|
|
|
|
|
var MIN_TIME_VISIBLE = 500;
|
|
var MAX_TIME_VISIBLE = 1500;
|
|
|
|
var MIN_WAIT_TIME_JUMPSCARE = 5000;
|
|
var MAX_WAIT_TIME_JUMPSCARE = 6000;
|
|
|
|
var TIME_FINAL_MOVE_VISIBLE = 2000;
|
|
var TIME_JUMPSCARE_VISIBLE = 1500;
|
|
|
|
var SOUND_WHISPER_URL = "http://hifi-content.s3-us-west-1.amazonaws.com/robin/dev/domains/halloween/flashingLight/whisper1.wav";
|
|
var SOUND_THUMP_URL = "https://hifi-content.s3.amazonaws.com/alan/dev/Audio/thud1.wav";
|
|
var SOUND_FINAL_THUMP_URL = "https://hifi-content.s3.amazonaws.com/milad/ROLC/Organize/O_Projects/Hifi/Scripts/Halloween/sounds/fleshed/_robin_hit_stereo.wav";
|
|
var SOUND_SCREAM_URL = "https://hifi-content.s3.amazonaws.com/milad/ROLC/Organize/O_Projects/Hifi/Scripts/Halloween/sounds/fleshed/_robin_scream_mono.wav";
|
|
|
|
var soundWhisper;
|
|
var soundFinalThump;
|
|
var soundThump;
|
|
var soundScream;
|
|
|
|
var overlayProperties = {
|
|
type: "model",
|
|
name: "statue_apparition",
|
|
url: "https://hifi-content.s3.amazonaws.com/alan/dev/Statue-Scary3.fbx",
|
|
dimensions: { x: 0.6769, y: 1.7771, z: 0.7370 },
|
|
visible: false
|
|
};
|
|
|
|
var utils = {
|
|
|
|
position: {
|
|
findSurfaceCollision: function (pos) {
|
|
// from position above ground, finds collision with ground
|
|
var result = Entities.findRayIntersection({
|
|
origin: pos,
|
|
direction: { x: 0.0, y: -1.0, z: 0.0 }
|
|
}, true);
|
|
|
|
if (result.intersects) {
|
|
return result.intersection;
|
|
}
|
|
return pos;
|
|
},
|
|
getNext: function () {
|
|
// statue movement using distances array
|
|
var deltaMove;
|
|
var direction;
|
|
|
|
var isEnding = count >= distances.length;
|
|
var is2ndLast = count === distances.length - 2;
|
|
var is3rdLast = count === distances.length - 3;
|
|
|
|
if (isEnding) {
|
|
// start jumpscare
|
|
endCondition = true;
|
|
return null;
|
|
|
|
} else if (is2ndLast) {
|
|
// right
|
|
direction = Quat.getRight(MyAvatar.orientation);
|
|
|
|
} else if (is3rdLast) {
|
|
// left
|
|
direction = Quat.inverse(Quat.getRight(MyAvatar.orientation));
|
|
|
|
} else {
|
|
// forward for all other cases
|
|
direction = Quat.getForward(MyAvatar.orientation);
|
|
|
|
}
|
|
|
|
deltaMove = Vec3.multiply(distances[count], Vec3.normalize(direction));
|
|
count++;
|
|
|
|
var newPos = {
|
|
x: MyAvatar.position.x + deltaMove.x,
|
|
y: MyAvatar.position.y + 2, // 2 m above ground for hills
|
|
z: MyAvatar.position.z + deltaMove.z
|
|
};
|
|
|
|
// place on ground
|
|
var surfacePos = this.findSurfaceCollision(newPos);
|
|
newPos.y = surfacePos.y + (overlayProperties.dimensions.y / 2);
|
|
|
|
return newPos;
|
|
},
|
|
getStart: function (position) {
|
|
// starts as invisible overlay so do not see where this places the overlay
|
|
var surfacePos = this.findSurfaceCollision(position);
|
|
var startPos = {
|
|
x: surfacePos.x,
|
|
y: surfacePos.y + (overlayProperties.dimensions.y / 2),
|
|
z: surfacePos.z
|
|
};
|
|
|
|
return startPos;
|
|
},
|
|
updateModel: function (nextPlacement) {
|
|
// updates the model with nextPlacement position
|
|
var properties = {
|
|
visible: true,
|
|
position: nextPlacement.position,
|
|
rotation: nextPlacement.rotation ? nextPlacement.rotation : Quat.cancelOutRollAndPitch(Quat.lookAtSimple(nextPlacement.position, MyAvatar.position))
|
|
};
|
|
|
|
if (endCondition) {
|
|
properties.parentID = MyAvatar.sessionUUID;
|
|
properties.jointIndex = MyAvatar.getJointIndex("Head")
|
|
? MyAvatar.getJointIndex("Head")
|
|
: MyAvatar.getJointIndex("Hips");
|
|
}
|
|
|
|
Overlays.editOverlay(overlayID, properties);
|
|
}
|
|
|
|
},
|
|
createStatue: function () {
|
|
// creates a new statue
|
|
if (!overlayID) {
|
|
overlayID = Overlays.addOverlay("model", overlayProperties);
|
|
}
|
|
},
|
|
getRandom: function (min, max) {
|
|
// get random number between min and max
|
|
return Math.floor(Math.random() * (max - min)) + min;
|
|
},
|
|
getSoundPositionFromObject: function (position) {
|
|
// makes the sounds sound close to the user
|
|
|
|
var headIdx = MyAvatar.getJointIndex("Head");
|
|
var headPos = MyAvatar.getJointPosition(headIdx);
|
|
|
|
var focusPosition = position;
|
|
|
|
return Vec3.sum(Vec3.multiply(0.2, Vec3.normalize(Vec3.subtract(focusPosition, headPos))), headPos);
|
|
}
|
|
};
|
|
|
|
function startJumpScare() {
|
|
// begin the final jump scare
|
|
|
|
Script.setTimeout(function () {
|
|
|
|
var screamTimeBuffer = 1000;
|
|
var visibleTimeBuffer = 500;
|
|
|
|
soundScream.playSoundStaticPosition({
|
|
position: MyAvatar.position,
|
|
localOnly: true
|
|
}, screamTimeBuffer);
|
|
|
|
Script.setTimeout(
|
|
// scream starts just before the jump scare
|
|
jumpScare,
|
|
visibleTimeBuffer
|
|
);
|
|
},
|
|
utils.getRandom(MIN_WAIT_TIME_JUMPSCARE, MAX_WAIT_TIME_JUMPSCARE));
|
|
|
|
}
|
|
|
|
function jumpScare() {
|
|
// do the jumpscare
|
|
|
|
var modelFacePosition = { x: 0, y: -0.5, z: -0.5 };
|
|
var endPosition = Vec3.sum(Camera.position, Vec3.multiplyQbyV(Camera.orientation, modelFacePosition));
|
|
|
|
var modelEndPosition = {
|
|
position: endPosition,
|
|
rotation: Quat.cancelOutRollAndPitch(Quat.lookAtSimple(endPosition, Camera.position))
|
|
};
|
|
|
|
utils.position.updateModel(modelEndPosition);
|
|
|
|
Script.setTimeout(
|
|
scriptEnding,
|
|
TIME_JUMPSCARE_VISIBLE
|
|
);
|
|
|
|
}
|
|
|
|
function makeVisible() {
|
|
// statue appears in new position
|
|
|
|
var nextPosition = utils.position.getNext();
|
|
var isLast = count === distances.length;
|
|
|
|
utils.position.updateModel({ position: nextPosition });
|
|
var soundPosition = utils.getSoundPositionFromObject(nextPosition);
|
|
|
|
if (endCondition) {
|
|
|
|
// begin jump scare
|
|
makeInvisible();
|
|
startJumpScare();
|
|
|
|
} else if (isLast) {
|
|
|
|
// final move
|
|
soundFinalThump.playSoundStaticPosition({
|
|
position: soundPosition,
|
|
localOnly: true
|
|
});
|
|
|
|
Script.setTimeout(
|
|
makeInvisible,
|
|
TIME_FINAL_MOVE_VISIBLE
|
|
);
|
|
|
|
} else {
|
|
|
|
// regular move
|
|
soundThump.playSoundStaticPosition({
|
|
position: soundPosition,
|
|
localOnly: true
|
|
});
|
|
|
|
Script.setTimeout(
|
|
makeInvisible,
|
|
utils.getRandom(MIN_TIME_VISIBLE, MAX_TIME_VISIBLE)
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
function makeInvisible() {
|
|
// statue disappears
|
|
|
|
Overlays.editOverlay(overlayID, {
|
|
visible: false
|
|
});
|
|
|
|
Script.setTimeout(
|
|
function () {
|
|
if (!endCondition) {
|
|
makeVisible();
|
|
}
|
|
},
|
|
utils.getRandom(MIN_TIME_INVISIBLE, MAX_TIME_INVISIBLE)
|
|
);
|
|
|
|
}
|
|
|
|
function start() {
|
|
|
|
endCondition = false;
|
|
|
|
if (soundWhisper.isLoaded()) {
|
|
|
|
soundWhisper.playSoundStaticPosition({
|
|
position: MyAvatar.position,
|
|
localOnly: true
|
|
}, null, makeInvisible);
|
|
|
|
} else {
|
|
makeInvisible();
|
|
}
|
|
}
|
|
|
|
var Zone = function () {
|
|
this.overlayID;
|
|
};
|
|
|
|
Zone.prototype = {
|
|
|
|
preload: function (id) {
|
|
entityID = id;
|
|
|
|
soundThump = new Sound(SOUND_THUMP_URL);
|
|
soundWhisper = new Sound(SOUND_WHISPER_URL);
|
|
soundFinalThump = new Sound(SOUND_FINAL_THUMP_URL);
|
|
soundScream = new Sound(SOUND_SCREAM_URL);
|
|
|
|
var properties = Entities.getEntityProperties(entityID, ["position"]);
|
|
|
|
startPosition = utils.position.getStart(properties.position);
|
|
overlayProperties.position = startPosition;
|
|
|
|
utils.createStatue();
|
|
},
|
|
|
|
enterEntity: function () {
|
|
start();
|
|
},
|
|
|
|
unload: scriptEnding
|
|
};
|
|
|
|
function scriptEnding() {
|
|
|
|
if (overlayID) {
|
|
Overlays.deleteOverlay(overlayID);
|
|
overlayID = null;
|
|
}
|
|
|
|
soundThump.unload();
|
|
soundWhisper.unload();
|
|
soundFinalThump.unload();
|
|
soundScream.unload();
|
|
|
|
var properties = Entities.getEntityProperties(entityID, ["position"]);
|
|
|
|
startPosition = utils.position.getStart(properties.position);
|
|
overlayProperties.position = startPosition;
|
|
endCondition = false;
|
|
count = 0;
|
|
|
|
utils.createStatue();
|
|
}
|
|
|
|
return new Zone();
|
|
}); |