375 lines
No EOL
14 KiB
JavaScript
375 lines
No EOL
14 KiB
JavaScript
//
|
|
// zombieSurvivorScript.js
|
|
//
|
|
// Created by David Back on 2/8/18.
|
|
// Copyright 2018 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
|
|
|
|
var CHANNEL_NAME = "ZOMBIE_BITE";
|
|
var DEAD_ZONE_POS = { x:-1.887, y:4.39989, z:-53.2878 };
|
|
var MINIMUM_STRIDE_MOVEMENT = 0.015;
|
|
var MINIMUM_FORWARD_STRIDES = 20;
|
|
var MINIMUM_BACKWARD_STRIDES = 20;
|
|
var MAX_VELOCITY = 10;
|
|
var VELOCITY_INCREASE_FACTOR = 60;
|
|
var VELOCITY_DECREASE = 0.2;
|
|
var VELOCITY_DECREASE_RUNNING = 0.05;
|
|
var RUN_CONTROLS = [Controller.Standard.LeftGrip, Controller.Standard.RightGrip];
|
|
var RUN_CONTROLS_THRESHOLD = 0.9;
|
|
var ADDITIONAL_BODY_OFFSET = {x:-0.097595, y:-0.018677, z:-0.088746};
|
|
var BITE_ANIMATION = "atp:/biteReaction.fbx";
|
|
var BITE_ANIMATION_START_FRAME = 0;
|
|
var BITE_ANIMATION_BLOOD_KEYFRAME = 10;
|
|
var BITE_ANIMATION_END_FRAME = 138;
|
|
var BITE_ANIMATION_FPS = 60;
|
|
var BLOOD_HAZE = "https://hifi-content.s3.amazonaws.com/davidback/development/zombies/BloodSphere2.fbx";
|
|
var MSEC_PER_SEC = 1000;
|
|
var BITES_REQUIRED = 3;
|
|
var BITES_SETTING_NAME = "ZombieBiteCount";
|
|
var HEALTH_VERTICAL_OFFSET_OVERHEAD = 1;
|
|
var HEALTH_VERTICAL_OFFSET_OVERLAY = -0.25;
|
|
var HEALTH_HORIZONTAL_OFFSET_OVERHEAD = 0.1;
|
|
var HEALTH_HORIZONTAL_OFFSET_OVERLAY = 0.1;
|
|
var HEALTH_OFFSET_WRIST = { x:0, y:0.03, z:0 };
|
|
var FORWARD_OFFSET = 0.2;
|
|
var HEART_MODEL_URL = "http://hifi-content.s3-us-west-1.amazonaws.com/rebecca/zombies/models/1410%20Heart.obj";
|
|
var DEBUG_BITE_KEY = "f";
|
|
var DEBUG_PRINT = true;
|
|
var FORCE_TEST_RUN = false;
|
|
|
|
var currentVelocity = 0;
|
|
var previousLeftDotProduct = 0;
|
|
var previousRightDotProduct = 0;
|
|
var bodyOffset;
|
|
var additionalBodyOffset;
|
|
var minLeftStride;
|
|
var maxLeftStride;
|
|
var minRightStride;
|
|
var maxRightStride;
|
|
var leftStrideForwardCount = 0;
|
|
var previousLeftStrideForwardCount = 0;
|
|
var leftStrideBackwardCount = 0;
|
|
var rightStrideForwardCount = 0;
|
|
var previousRightStrideForwardCount = 0;
|
|
var rightStrideBackwardCount = 0;
|
|
var biteAnimationPlaying = false;
|
|
var bloodHaze = undefined;
|
|
var healthOverhead = [];
|
|
var healthWrist = [];
|
|
var localPositionOverhead;
|
|
var localPositionOverlay;
|
|
var localPositionWrist;
|
|
|
|
function onMessageReceived(channel, message, senderID) {
|
|
var messageData = JSON.parse(message);
|
|
var type = messageData['type'];
|
|
var biterID = messageData['biterID'];
|
|
var victimID = messageData['victimID'];
|
|
if (type == "receiveBite") {
|
|
if (DEBUG_PRINT) {
|
|
print("receiveBite message received from biter " + biterID + " biting " + victimID);
|
|
}
|
|
if (victimID == MyAvatar.sessionUUID) {
|
|
if (DEBUG_PRINT) {
|
|
print("Bitten by zombie " + biterID);
|
|
}
|
|
biteReceived();
|
|
}
|
|
}
|
|
}
|
|
|
|
function keyPressEvent(event) {
|
|
if (DEBUG_BITE_KEY == event.text) {
|
|
if (DEBUG_PRINT) {
|
|
print("biteReceived called from debug key press");
|
|
}
|
|
biteReceived();
|
|
}
|
|
}
|
|
|
|
function biteReceived() {
|
|
var oldBites = Settings.getValue(BITES_SETTING_NAME, 0);
|
|
var newBites = oldBites;
|
|
newBites++;
|
|
Settings.setValue(BITES_SETTING_NAME, newBites);
|
|
if (DEBUG_PRINT) {
|
|
print(BITES_SETTING_NAME + " setting changed from " + oldBites + " to " + newBites);
|
|
}
|
|
|
|
var frameCount = BITE_ANIMATION_END_FRAME - BITE_ANIMATION_START_FRAME;
|
|
MyAvatar.overrideAnimation(BITE_ANIMATION, BITE_ANIMATION_FPS, false,
|
|
BITE_ANIMATION_START_FRAME, BITE_ANIMATION_END_FRAME);
|
|
biteAnimationPlaying = true;
|
|
|
|
var timeOut = MSEC_PER_SEC * frameCount / BITE_ANIMATION_FPS;
|
|
Script.setTimeout(function () {
|
|
biteAnimationPlaying = false;
|
|
MyAvatar.restoreAnimation();
|
|
Overlays.deleteOverlay(bloodHaze);
|
|
bloodHaze = undefined;
|
|
if (newBites >= BITES_REQUIRED) {
|
|
//MyAvatar.goToLocation(DEAD_ZONE_POS, true, MyAvatar.orientation);
|
|
Settings.setValue(BITES_SETTING_NAME, 0);
|
|
initHealth();
|
|
if (DEBUG_PRINT) {
|
|
print("DEAD - teleport to dead zone - " + BITES_SETTING_NAME + " reset back to 0");
|
|
}
|
|
}
|
|
}, timeOut);
|
|
|
|
var bloodTimeOut = MSEC_PER_SEC * BITE_ANIMATION_BLOOD_KEYFRAME / BITE_ANIMATION_FPS;
|
|
Script.setTimeout(function () {
|
|
var localPositionBlood = Vec3.subtract(MyAvatar.getJointPosition("Head"), MyAvatar.position);
|
|
var localRotationBlood = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
|
bloodHaze = Overlays.addOverlay("model", {
|
|
localPosition: localPositionBlood,
|
|
localRotation: Quat.fromPitchYawRollDegrees(0, 0, 0),
|
|
parentID: MyAvatar.sessionUUID,
|
|
dimensions: {
|
|
x: 1,
|
|
y: 1,
|
|
z: 1
|
|
},
|
|
url: BLOOD_HAZE
|
|
});
|
|
}, bloodTimeOut);
|
|
|
|
loseHealth();
|
|
}
|
|
|
|
function update() {
|
|
var runControlsHeld = true;
|
|
for (var i = 0; i < RUN_CONTROLS.length; i++) {
|
|
var controlValue = Controller.getValue(RUN_CONTROLS[i]);
|
|
if (controlValue < RUN_CONTROLS_THRESHOLD) {
|
|
runControlsHeld = false;
|
|
}
|
|
}
|
|
|
|
if (runControlsHeld && !biteAnimationPlaying) {
|
|
if (Vec3.equal(bodyOffset, Vec3.ZERO)) {
|
|
bodyOffset = Vec3.subtract(MyAvatar.getJointPosition("Body"), MyAvatar.position);
|
|
additionalBodyOffset = Vec3.sum(bodyOffset, ADDITIONAL_BODY_OFFSET);
|
|
}
|
|
|
|
var leftHandPosition = Vec3.sum(MyAvatar.position, MyAvatar.getLeftHandPosition());
|
|
var rightHandPosition = Vec3.sum(MyAvatar.position, MyAvatar.getRightHandPosition());
|
|
var leftPlaneNormal = Vec3.cross(bodyOffset, additionalBodyOffset);
|
|
var rightPlaneNormal = leftPlaneNormal;
|
|
var toLeftHand = Vec3.subtract(leftHandPosition, MyAvatar.position);
|
|
var leftDotProduct = Vec3.dot(leftPlaneNormal, toLeftHand);
|
|
var toRightHand = Vec3.subtract(rightHandPosition, MyAvatar.position);
|
|
var rightDotProduct = Vec3.dot(rightPlaneNormal, toRightHand);
|
|
|
|
var leftHandDifference = leftDotProduct - previousLeftDotProduct;
|
|
var leftHandDirection = leftHandDifference > 0 ? 1 : -1;
|
|
if (leftHandDirection === 1) {
|
|
leftStrideForwardCount++;
|
|
} else {
|
|
leftStrideBackwardCount++;
|
|
}
|
|
var rightHandDifference = rightDotProduct - previousRightDotProduct;
|
|
var rightHandDirection = rightHandDifference > 0 ? 1 : -1;
|
|
if (rightHandDirection === 1) {
|
|
rightStrideForwardCount++;
|
|
} else {
|
|
rightStrideBackwardCount++;
|
|
}
|
|
|
|
if (minLeftStride == undefined || leftDotProduct < minLeftStride) {
|
|
minLeftStride = leftDotProduct;
|
|
}
|
|
if (maxLeftStride == undefined || leftDotProduct > maxLeftStride) {
|
|
maxLeftStride = leftDotProduct;
|
|
}
|
|
if (minRightStride == undefined || rightDotProduct < minRightStride) {
|
|
minRightStride = rightDotProduct;
|
|
}
|
|
if (maxRightStride == undefined || rightDotProduct > maxRightStride) {
|
|
maxRightStride = rightDotProduct;
|
|
}
|
|
|
|
if (minLeftStride != undefined && maxLeftStride != undefined && maxLeftStride - minLeftStride > MINIMUM_STRIDE_MOVEMENT && leftStrideForwardCount >= MINIMUM_FORWARD_STRIDES && leftStrideBackwardCount >= MINIMUM_BACKWARD_STRIDES) {
|
|
var maxLeftStrideDifference = Math.abs(leftDotProduct - maxLeftStride);
|
|
if (maxLeftStrideDifference > 0 && maxLeftStrideDifference < 0.01) {
|
|
var strideLength = maxLeftStride - minLeftStride;
|
|
var velocityIncrease = strideLength * VELOCITY_INCREASE_FACTOR;
|
|
currentVelocity += velocityIncrease;
|
|
if (currentVelocity > MAX_VELOCITY) {
|
|
currentVelocity = MAX_VELOCITY;
|
|
}
|
|
maxLeftStride = undefined;
|
|
minLeftStride = undefined;
|
|
leftStrideForwardCount = 0;
|
|
leftStrideBackwardCount = 0;
|
|
}
|
|
}
|
|
|
|
if (minRightStride != undefined && maxRightStride != undefined && maxRightStride - minRightStride > MINIMUM_STRIDE_MOVEMENT && rightStrideForwardCount >= MINIMUM_FORWARD_STRIDES && rightStrideBackwardCount >= MINIMUM_BACKWARD_STRIDES) {
|
|
var maxRightStrideDifference = Math.abs(rightDotProduct - maxRightStride);
|
|
if (maxRightStrideDifference > 0 && maxRightStrideDifference < 0.01) {
|
|
var strideLength = maxRightStride - minRightStride;
|
|
var velocityIncrease = strideLength * VELOCITY_INCREASE_FACTOR;
|
|
currentVelocity += velocityIncrease;
|
|
if (currentVelocity > MAX_VELOCITY) {
|
|
currentVelocity = MAX_VELOCITY;
|
|
}
|
|
maxRightStride = undefined;
|
|
minRightStride = undefined;
|
|
rightStrideForwardCount = 0;
|
|
rightStrideBackwardCount = 0;
|
|
}
|
|
}
|
|
} else {
|
|
maxLeftStride = undefined;
|
|
minLeftStride = undefined;
|
|
maxRightStride = undefined;
|
|
minRightStride = undefined;
|
|
leftStrideForwardCount = 0;
|
|
leftStrideBackwardCount = 0;
|
|
rightStrideForwardCount = 0;
|
|
rightStrideBackwardCount = 0;
|
|
}
|
|
|
|
if (FORCE_TEST_RUN && !biteAnimationPlaying) {
|
|
currentVelocity = MAX_VELOCITY;
|
|
}
|
|
|
|
var velocityDecrease = runControlsHeld ? VELOCITY_DECREASE_RUNNING : VELOCITY_DECREASE;
|
|
if (currentVelocity > 0) {
|
|
currentVelocity -= velocityDecrease;
|
|
}
|
|
if (currentVelocity < 0) {
|
|
currentVelocity = 0;
|
|
}
|
|
|
|
var velocityForward = { x:0, y:0, z:1 };
|
|
var newVelocity = currentVelocity;
|
|
if (newVelocity < 0) {
|
|
newVelocity = 0;
|
|
}
|
|
var newVelocityVector = Vec3.multiply(velocityForward, newVelocity);
|
|
if (Vec3.length(newVelocityVector) > MAX_VELOCITY) {
|
|
newVelocityVector = Vec3.normalize(newVelocityVector);
|
|
newVelocityVector = Vec3.multiply(newVelocityVector, MAX_VELOCITY);
|
|
}
|
|
|
|
MyAvatar.motorVelocity = newVelocityVector;
|
|
|
|
previousLeftDotProduct = leftDotProduct;
|
|
previousRightDotProduct = rightDotProduct;
|
|
|
|
if (bloodHaze !== undefined) {
|
|
var localRotationBlood = Quat.multiply(Quat.inverse(MyAvatar.orientation), Quat.multiply(Camera.orientation, Quat.fromPitchYawRollDegrees(0, 0, 0)));
|
|
Overlays.editOverlay(bloodHaze, { localRotation: localRotationBlood });
|
|
}
|
|
}
|
|
|
|
function addHealth() {
|
|
var heart = Entities.addEntity({
|
|
"clientOnly": 1,
|
|
"dimensions": {
|
|
"x": 0.075,
|
|
"y": 0.075,
|
|
"z": 0.075
|
|
},
|
|
"localPosition": localPositionOverhead,
|
|
"modelURL": HEART_MODEL_URL,
|
|
"name": "Heart CC-BY Poly by Google",
|
|
"parentID": MyAvatar.sessionUUID,
|
|
"parentJointName": "Hips",
|
|
"owningAvatarID": MyAvatar.sessionUUID,
|
|
"type": "Model",
|
|
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
|
|
});
|
|
healthOverhead.push(heart);
|
|
//spawnOverlayReplica(heart);
|
|
spawnWristOverlay();
|
|
}
|
|
|
|
function loseHealth() {
|
|
if (healthOverhead[0]) {
|
|
Entities.deleteEntity(healthOverhead[0]);
|
|
healthOverhead.shift();
|
|
}
|
|
if (healthWrist[0]) {
|
|
Overlays.deleteOverlay(healthWrist[0]);
|
|
healthWrist.shift();
|
|
}
|
|
}
|
|
|
|
function spawnOverlayReplica(entity) {
|
|
var overlayProperties = {
|
|
url: HEART_MODEL_URL,
|
|
alpha: 0.9,
|
|
parentID: entity,
|
|
localPosition: {x: 0, y: HEALTH_VERTICAL_OFFSET_OVERLAY, z: 0.5},
|
|
dimensions: {
|
|
x: 0.04,
|
|
y: 0.04,
|
|
z: 0.04
|
|
}
|
|
};
|
|
Overlays.addOverlay("model", overlayProperties);
|
|
}
|
|
|
|
function spawnWristOverlay() {
|
|
// maybe change this to an image overlay
|
|
var overlayProperties = {
|
|
url: HEART_MODEL_URL,
|
|
alpha: 0.9,
|
|
parentID: MyAvatar.sessionUUID,
|
|
parentJointIndex: MyAvatar.getJointIndex("RightForeArm"),
|
|
localPosition: localPositionWrist,
|
|
dimensions: {
|
|
x: 0.02,
|
|
y: 0.02,
|
|
z: 0.02
|
|
}
|
|
};
|
|
healthWrist.push(Overlays.addOverlay("model", overlayProperties));
|
|
}
|
|
|
|
function initHealth() {
|
|
localPositionOverhead = {x:-HEALTH_HORIZONTAL_OFFSET_OVERHEAD, y:HEALTH_VERTICAL_OFFSET_OVERHEAD, z:0};
|
|
localPositionWrist = {x:0.06, y:0.15, z:0};
|
|
addHealth();
|
|
for (var i = 1; i < BITES_REQUIRED; i++) {
|
|
localPositionOverhead.x += HEALTH_HORIZONTAL_OFFSET_OVERHEAD;
|
|
localPositionWrist = Vec3.sum(localPositionWrist, HEALTH_OFFSET_WRIST);
|
|
addHealth();
|
|
}
|
|
}
|
|
|
|
function shutdown() {
|
|
Messages.unsubscribe(CHANNEL_NAME);
|
|
Messages.messageReceived.disconnect(onMessageReceived);
|
|
Controller.keyPressEvent.disconnect(keyPressEvent);
|
|
|
|
MyAvatar.motorVelocity = { x:0, y:0, z:0 };
|
|
MyAvatar.motorMode = "simple";
|
|
|
|
for (var i = 0; i < BITES_REQUIRED; i++) {
|
|
loseHealth();
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
Script.scriptEnding.connect(shutdown);
|
|
Messages.subscribe(CHANNEL_NAME);
|
|
Messages.messageReceived.connect(onMessageReceived);
|
|
Controller.keyPressEvent.connect(keyPressEvent);
|
|
|
|
MyAvatar.motorMode = "dynamic";
|
|
MyAvatar.motorReferenceFrame = "camera";
|
|
|
|
Script.update.connect(update);
|
|
|
|
Script.setTimeout(function () {
|
|
initHealth();
|
|
}, 500);
|
|
}
|
|
|
|
init(); |