content/hifi-content/DomainContent/Spot/Material Gun/materialSwapGun.js
2022-02-13 22:49:05 +01:00

508 lines
22 KiB
JavaScript

//
// materialSwapGun.js
//
// created by Rebecca Stankus on 03/27/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
//
/* global Pointers, Graphics */
function exponentialSmoothing(target, current) {
var smoothingConstant = 0.75;
return target * (1 - smoothingConstant) + current * smoothingConstant;
}
(function() {
var _this;
var TRIGGER_CONTROLS = [Controller.Standard.LT, Controller.Standard.RT];
var TRIGGER_THRESHOLD = 0.97;
var AUDIO_VOLUME_LEVEL = 0.1;
var BARREL_LOCAL_OFFSET = {x: 0.015, y: 0.065, z: -0.25};
var BARREL_LOCAL_DIRECTION = {x: 0, y: 0, z: -1000};
var SHOOT_SOUND = Script.resolvePath("sounds/shoot.wav");
var DESKTOP_HOW_TO_IMAGE_URL = Script.resolvePath("textures/desktopFireUnequip.png");
var DESKTOP_HOW_TO_IMAGE_WIDTH = 384;
var DESKTOP_HOW_TO_IMAGE_HEIGHT = 128;
var FIRE_KEY = "f";
var HAND = {LEFT: 0, RIGHT: 1};
var DESKTOP_HOW_TO_OVERLAY = true;
var CAN_FIRE_AGAIN_TIMEOUT_MS = 250;
var SWAP_SOUND = Script.resolvePath("sounds/swap.wav");
var MISS_SOUND = Script.resolvePath("sounds/miss.wav");
var SWAP_TIMEOUT_MS = 500;
var FIND_MATERIAL_RADIUS = 10;
var PRIORITY_DEFAULT =100;
var SUBMESH_DEFAULT = "0";
var STOP_EMITTING_MS = 100;
var DEFAULT_DISTANCE_M = 3;
var LIFETIME = 45;
var Y_OFFSET_FOR_WINDOW = 24;
var currentHand = null;
var canShoot = true;
var injector;
var canFire = true;
var mouseEquipAnimationHandler;
var desktopHowToOverlay = null;
var previousHMDActive;
var previousLeftYPosition = 0;
var previousLeftXRotation = 0;
var previousLeftZRotation = 0;
var previousRightYPosition = 0;
var previousRightXRotation = 0;
var previousRightZRotation = 0;
var nextMaterial;
var nextColor;
var swapSound;
var missSound;
var shootSound;
var doNotRayPick = [];
var offsetMultiplier = 0.8;
function Gun() {
_this = this;
}
Gun.prototype = {
particleEffect: null,
particleInterval: null,
colorSpray: null,
preload: function(entityID) {
_this.entityID = entityID;
previousHMDActive = HMD.active;
swapSound = SoundCache.getSound(SWAP_SOUND);
missSound = SoundCache.getSound(MISS_SOUND);
shootSound = SoundCache.getSound(SHOOT_SOUND);
Entities.getChildrenIDs(_this.entityID).forEach(function(element) {
var name = Entities.getEntityProperties(element, 'name').name;
if (name === "Gun Particle Effect") {
_this.particleEffect = element;
}
});
_this.getNext();
doNotRayPick = Entities.getChildrenIDs(_this.entityID);
doNotRayPick.push(_this.entityID, nextMaterial);
},
callToShootBall: function(distance) {
var params = [];
params[0] = JSON.stringify(_this.getBarrelPosition());
params[1] = JSON.stringify(_this.getBarrelDirection());
params[2] = JSON.stringify(distance);
Entities.callEntityServerMethod(_this.entityID, 'fire', params);
},
startEquip: function(id, params) {
currentHand = params[0] === "left" ? 0 : 1;
Entities.editEntity(_this.entityID, {
visible: true,
lifetime: -1
});
Controller.keyReleaseEvent.connect(_this.keyReleaseEvent);
if (!HMD.active) {
_this.addMouseEquipAnimation();
_this.addDesktopOverlay();
}
previousHMDActive = HMD.active;
},
startNearGrab: function() {
Entities.editEntity(_this.entityID, {
visible: true,
lifetime: -1
});
},
releaseNearGrab: function() {
var age = Entities.getEntityProperties(_this.entityID, "age").age;
Entities.editEntity(_this.entityID, {
lifetime: age + LIFETIME
});
},
continueEquip: function(id, params) {
if (currentHand === null) {
return;
}
if (HMD.active !== previousHMDActive) {
if (HMD.active) {
_this.removeDesktopOverlay();
_this.removeMouseEquipAnimation();
} else {
_this.addDesktopOverlay();
_this.addMouseEquipAnimation();
}
previousHMDActive = HMD.active;
}
_this.toggleWithTriggerPressure();
},
releaseEquip: function(id, params) {
currentHand = null;
var age = Entities.getEntityProperties(_this.entityID, "age").age;
Entities.editEntity(_this.entityID, {
lifetime: age + LIFETIME
});
Controller.keyReleaseEvent.disconnect(_this.keyReleaseEvent);
_this.removeMouseEquipAnimation();
_this.removeDesktopOverlay();
},
getNext: function() {
var gunUserDataString = Entities.getEntityProperties(_this.entityID, 'userData').userData;
var gunUserData;
if (gunUserDataString) {
gunUserData = JSON.parse(gunUserDataString);
}
if (gunUserData) {
nextMaterial = gunUserData.nextMaterial;
nextColor = gunUserData.nextColor;
// just getting sim ownership
Entities.editEntity(nextMaterial, {
parentID: _this.entityID,
priority: 2,
parentMaterialName: "0"
});
}
},
fire: function() {
Entities.findEntities(MyAvatar.position, FIND_MATERIAL_RADIUS).forEach(function(element) {
var type = Entities.getEntityProperties(element, 'type').type;
if (type === "Material") {
doNotRayPick.push(element);
}
});
var HAPTIC_STRENGTH = 1;
var HAPTIC_DURATION = 20;
Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, currentHand);
_this.playSound(Entities.getEntityProperties(_this.entityID, 'position').position, shootSound);
_this.addParticleEffect();
var fireStart = this.getBarrelPosition();
var barrelDirection = this.getBarrelDirection();
var barrelDirectionLength = Vec3.length(barrelDirection);
var barrelDirectionNormalized = Vec3.normalize(barrelDirection);
var fireRay = {
origin: fireStart,
direction: barrelDirectionNormalized
};
var entityIntersection = Entities.findRayIntersection(fireRay, true, [], doNotRayPick);
var entityIntersectionDistance = entityIntersection.intersects ? entityIntersection.distance : Number.MAX_VALUE;
var avatarIntersection = AvatarList.findRayIntersection(fireRay);
var avatarIntersectionDistance = avatarIntersection.intersects ? avatarIntersection.distance : Number.MAX_VALUE;
var intersectEntityID = null;
var intersection = null;
if (entityIntersection.intersects && entityIntersectionDistance < avatarIntersectionDistance &&
entityIntersectionDistance < barrelDirectionLength) {
intersectEntityID = entityIntersection.entityID;
intersection = entityIntersection;
} else if (avatarIntersection.intersects && avatarIntersectionDistance < entityIntersectionDistance &&
avatarIntersectionDistance < barrelDirectionLength) {
intersectEntityID = avatarIntersection.avatarID;
intersection = avatarIntersection;
}
_this.callToShootBall(intersection ? intersection.distance : DEFAULT_DISTANCE_M);
var intersectEntityProperties = Entities.getEntityProperties(intersectEntityID, ['position', 'rotation']);
if (intersectEntityID) {
Script.setTimeout(function() {
try {
var mesh = Graphics.getModel(intersectEntityID);
} catch (err) {
print("could not get mesh");
_this.playSound(intersectEntityProperties.position, missSound);
}
var priority;
var submesh;
var materialList;
if (mesh) {
materialList = mesh.materialLayers;
if (intersection && intersection.extraInfo) {
submesh = intersection.extraInfo.subMeshIndex;
} else {
submesh = SUBMESH_DEFAULT;
}
} else {
submesh = SUBMESH_DEFAULT;
}
if (materialList) {
if (materialList[submesh]) {
priority = _this.getTopMaterialPriority(materialList[submesh]);
}
} else {
priority = PRIORITY_DEFAULT;
}
Entities.getChildrenIDs(intersectEntityID).forEach(function(element) {
var name = Entities.getEntityProperties(element, 'name').name;
if (name === "Gun Material" && (element !== nextMaterial)) {
var otherSubmesh =
Entities.getEntityProperties(element, 'parentMaterialName').parentMaterialName;
if (submesh == otherSubmesh) {
Entities.deleteEntity(element);
}
}
});
priority += 1;
var params = [];
params[0] = JSON.stringify(intersection.intersection);
Entities.callEntityServerMethod(_this.entityID, 'createSplat', params);
Entities.editEntity(nextMaterial, {
parentID: intersectEntityID,
priority: priority,
parentMaterialName: submesh
});
_this.playSound(intersectEntityProperties.position, swapSound);
_this.getNext();
}, SWAP_TIMEOUT_MS);
} else {
_this.playSound(intersectEntityProperties.position, missSound);
}
},
getTopMaterialPriority: function(multiMaterial) {
if (multiMaterial.length > 1) {
if (multiMaterial[1].priority > multiMaterial[0].priority) {
return multiMaterial[1].priority;
}
}
return multiMaterial[0].priority;
},
playSound: function(position, sound) {
if (sound.downloaded) {
if (injector) {
injector.stop();
}
injector = Audio.playSound(sound, {
position: Entities.getEntityProperties(_this.entityID, 'position').position,
volume: AUDIO_VOLUME_LEVEL
});
}
},
addParticleEffect: function() {
if (_this.particleInterval) {
Script.clearInterval(_this.particleInterval);
_this.particleInterval = null;
} else {
Entities.editEntity(_this.particleEffect, {
color: nextColor,
colorSpread: nextColor,
colorStart: nextColor,
colorFinish: nextColor,
isEmitting: true
});
}
Script.setTimeout(function() {
Entities.editEntity(_this.particleEffect, { isEmitting: false });
}, STOP_EMITTING_MS);
},
getBarrelPosition: function() {
var properties = Entities.getEntityProperties(_this.entityID, ['position', 'rotation']);
var barrelLocalPosition = Vec3.multiplyQbyV(properties.rotation, BARREL_LOCAL_OFFSET);
var barrelWorldPosition = Vec3.sum(properties.position, barrelLocalPosition);
return barrelWorldPosition;
},
getBarrelDirection: function() {
var rotation = Entities.getEntityProperties(_this.entityID, ['rotation']).rotation;
var barrelAdjustedDirection = Vec3.multiplyQbyV(rotation, BARREL_LOCAL_DIRECTION);
return barrelAdjustedDirection;
},
toggleWithTriggerPressure: function() {
var triggerValue = Controller.getValue(TRIGGER_CONTROLS[currentHand]);
if (triggerValue >= TRIGGER_THRESHOLD) {
if (canShoot === true) {
_this.fire();
canShoot = false;
}
} else {
canShoot = true;
}
},
addDesktopOverlay: function() {
_this.removeDesktopOverlay();
var userDataProperties = JSON.parse(Entities.getEntityProperties(_this.entityID, 'userData').userData);
if (currentHand === null || !DESKTOP_HOW_TO_OVERLAY) {
return;
}
var showOverlay = true;
var otherHandDesktopOverlay = _this.getOtherHandDesktopOverlay();
if (otherHandDesktopOverlay !== null) {
desktopHowToOverlay = userDataProperties.desktopHowToOverlay;
showOverlay = false;
}
if (showOverlay) {
var viewport = Controller.getViewportDimensions();
var windowHeight = viewport.y;
desktopHowToOverlay = Overlays.addOverlay("image", {
imageURL: DESKTOP_HOW_TO_IMAGE_URL,
x: 0,
y: windowHeight - DESKTOP_HOW_TO_IMAGE_HEIGHT - Y_OFFSET_FOR_WINDOW,
width: DESKTOP_HOW_TO_IMAGE_WIDTH,
height: DESKTOP_HOW_TO_IMAGE_HEIGHT,
alpha: 1.0,
visible: true
});
userDataProperties.desktopHowToOverlay = desktopHowToOverlay;
Entities.editEntity(_this.entityID, {
userData: JSON.stringify(userDataProperties)
});
}
},
getOtherHandDesktopOverlay: function() {
var otherHandDesktopOverlay = null;
if (currentHand !== null) {
var handJointIndex = MyAvatar.getJointIndex(currentHand === HAND.LEFT ? "RightHand" : "LeftHand");
var children = Entities.getChildrenIDsOfJoint(MyAvatar.SELF_ID, handJointIndex);
children.forEach(function(childID) {
var userDataProperties = JSON.parse(Entities.getEntityProperties(childID, 'userData').userData);
if (userDataProperties.desktopHowToOverlay) {
otherHandDesktopOverlay = userDataProperties.desktopHowToOverlay;
}
});
}
return otherHandDesktopOverlay;
},
removeDesktopOverlay: function() {
var otherHandDesktopOverlay = _this.getOtherHandDesktopOverlay();
if (desktopHowToOverlay !== null && otherHandDesktopOverlay === null) {
Overlays.deleteOverlay(desktopHowToOverlay);
desktopHowToOverlay = null;
}
},
addMouseEquipAnimation: function() {
_this.removeMouseEquipAnimation();
if (currentHand === HAND.LEFT) {
mouseEquipAnimationHandler = MyAvatar.addAnimationStateHandler(_this.leftHandMouseEquipAnimation, []);
} else if (currentHand === HAND.RIGHT) {
mouseEquipAnimationHandler = MyAvatar.addAnimationStateHandler(_this.rightHandMouseEquipAnimation, []);
}
},
removeMouseEquipAnimation: function() {
if (mouseEquipAnimationHandler) {
mouseEquipAnimationHandler = MyAvatar.removeAnimationStateHandler(mouseEquipAnimationHandler);
}
},
leftHandMouseEquipAnimation: function() {
var result = {};
result.leftHandType = 0;
var leftHandPosition = MyAvatar.getJointPosition("LeftHand");
var leftShoulderPosition = MyAvatar.getJointPosition("LeftShoulder");
var shoulderToHandDistance = Vec3.distance(leftHandPosition, leftShoulderPosition);
var cameraForward = Quat.getForward(Camera.orientation);
var newForward = Vec3.multiply(cameraForward, shoulderToHandDistance);
var newLeftHandPosition = Vec3.sum(leftShoulderPosition, newForward);
var newLeftHandPositionAvatarFrame = Vec3.subtract(newLeftHandPosition, MyAvatar.position);
var headIndex = MyAvatar.getJointIndex("Head");
var offset = 0.5;
if (headIndex) {
offset = offsetMultiplier* MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y;
}
result.leftHandPosition = Vec3.multiply(offset, {x: 0.25, y: 0.6, z: 1.3});
var yPosition = exponentialSmoothing(newLeftHandPositionAvatarFrame.y, previousLeftYPosition);
result.leftHandPosition.y = yPosition;
previousLeftYPosition = yPosition;
var leftHandPositionNew = Vec3.sum(MyAvatar.position, result.leftHandPosition);
var rotation = Quat.lookAtSimple(leftHandPositionNew, leftShoulderPosition);
var rotationAngles = Quat.safeEulerAngles(rotation);
var xRotation = exponentialSmoothing(rotationAngles.x, previousLeftXRotation);
var zRotation = exponentialSmoothing(rotationAngles.z, previousLeftZRotation);
var newRotation = Quat.fromPitchYawRollDegrees(rotationAngles.x, 0, rotationAngles.z);
previousLeftXRotation = xRotation;
previousLeftZRotation = zRotation;
result.leftHandRotation = Quat.multiply(newRotation, Quat.fromPitchYawRollDegrees(80, -20, -90));
return result;
},
rightHandMouseEquipAnimation: function() {
var result = {};
result.rightHandType = 0;
var rightHandPosition = MyAvatar.getJointPosition("RightHand");
var rightShoulderPosition = MyAvatar.getJointPosition("RightShoulder");
var shoulderToHandDistance = Vec3.distance(rightHandPosition, rightShoulderPosition);
var cameraForward = Quat.getForward(Camera.orientation);
var newForward = Vec3.multiply(cameraForward, shoulderToHandDistance);
var newRightHandPosition = Vec3.sum(rightShoulderPosition, newForward);
var newRightHandPositionAvatarFrame = Vec3.subtract(newRightHandPosition, MyAvatar.position);
var headIndex = MyAvatar.getJointIndex("Head");
var offset = 0.5;
if (headIndex) {
offset = offsetMultiplier * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y;
}
result.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.6, z: 1.3});
var yPosition = exponentialSmoothing(newRightHandPositionAvatarFrame.y, previousRightYPosition);
result.rightHandPosition.y = yPosition;
previousRightYPosition = yPosition;
var rightHandPositionNew = Vec3.sum(MyAvatar.position, result.rightHandPosition);
var rotation = Quat.lookAtSimple(rightHandPositionNew, rightShoulderPosition);
var rotationAngles = Quat.safeEulerAngles(rotation);
var xRotation = exponentialSmoothing(rotationAngles.x, previousRightXRotation);
var zRotation = exponentialSmoothing(rotationAngles.z, previousRightZRotation);
var newRotation = Quat.fromPitchYawRollDegrees(rotationAngles.x, 0, rotationAngles.z);
previousRightXRotation = xRotation;
previousRightZRotation = zRotation;
result.rightHandRotation = Quat.multiply(newRotation, Quat.fromPitchYawRollDegrees(80, 00, 90));
return result;
},
keyReleaseEvent: function(event) {
if ((event.text).toLowerCase() === FIRE_KEY) {
if (canFire) {
canFire = false;
_this.fire();
Script.setTimeout(function() {
canFire = true;
}, CAN_FIRE_AGAIN_TIMEOUT_MS);
}
}
},
unload: function() {
this.removeMouseEquipAnimation();
this.removeDesktopOverlay();
}
};
return new Gun();
});