forked from sent/waves
953 lines
34 KiB
JavaScript
953 lines
34 KiB
JavaScript
/**
|
|
* @file player.class.js
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
import AudioSystem from '../framework/audiosystem.class.js';
|
|
import Entity from '../framework/entity.class.js';
|
|
import Input from '../framework/input.class.js';
|
|
import ModelManager from '../framework/modelmanager.class.js';
|
|
import Animator from '../framework/three.js/animator.class.js';
|
|
|
|
import Utility from '../game/utility.class.js';
|
|
|
|
import { SkeletonUtils } from '../lib/three.js/SkeletonUtils.js';
|
|
import * as THREE from '../lib/three.js/three.module.js';
|
|
|
|
import config from '../resources/config.js';
|
|
|
|
export default class Player extends Entity {
|
|
static _init() {
|
|
Player._speedSteer = 2.3;
|
|
Player._speedTurn = 0.1;
|
|
Player._speedTurnMax = 2.5;
|
|
Player._speedAccelerate = 1;
|
|
Player._speedBrake = 1;
|
|
Player._speedStopThreshold = 0.4;
|
|
|
|
Player._velocityMax = 40;
|
|
Player._velocitySlowFactor = 0.6;
|
|
|
|
Player._lookResetTime = 0.5;
|
|
Player._lookResetSmoothing = 0.01;
|
|
Player._lookSmoothing = 0.3;
|
|
|
|
Player._tmpV20 = new THREE.Vector2();
|
|
Player._tmpV30 = new THREE.Vector3();
|
|
Player._tmpV31 = new THREE.Vector3();
|
|
}
|
|
|
|
constructor(context, scene, options = Object.create(null)) {
|
|
options.updatePriority = 10;
|
|
|
|
super(context, scene, options);
|
|
|
|
const tScene = scene.getThreeScene();
|
|
|
|
const gltf = ModelManager.getModel('characters');
|
|
|
|
const animationsMan = [].concat(
|
|
Animator.filterAnimations(gltf.animations, [
|
|
'PlayerCrash', 'PlayerEatenByCar', 'PlayerEatenByGhost', 'PlayerStrangled1',
|
|
'PlayerStrangled2', 'PlayerEnding', 'PlayerTrapped', 'PlayerTrappedIdle', 'PlayerTrappedEscape',
|
|
], null, null, true, false, true),
|
|
Animator.filterAnimations(gltf.animations, [
|
|
'PlayerLowerBodyPaddle', 'PlayerLowerBodyPause',
|
|
], [
|
|
'UpperLegLM', 'LowerLegLM', 'UpperFootLM', 'MiddleFootLM', 'LowerFootLM',
|
|
'UpperLegRM', 'LowerLegRM', 'UpperFootRM', 'MiddleFootRM', 'LowerFootRM',
|
|
], null, true, false, true),
|
|
Animator.filterAnimations(gltf.animations, [
|
|
'PlayerUpperBodySteer', 'PlayerUpperBodyLookLeft', 'PlayerUpperBodyLookRight',
|
|
], [
|
|
'RootM', 'LowerBodyM', 'MiddleBodyM', 'ChestM', 'NeckM', 'HeadM', 'CameraM',
|
|
'ShoulderLM', 'UpperArmLM', 'LowerArmLM', 'Hand2LM', 'Finger2LM', 'Hand1LM', 'Finger1LM',
|
|
'ShoulderRM', 'UpperArmRM', 'LowerArmRM', 'Hand2RM', 'Finger2RM', 'Hand1RM', 'Finger1RM',
|
|
], null, true, false, true));
|
|
|
|
const animationsBike = Animator.filterAnimations(gltf.animations, [
|
|
'BikeCrash', 'BikeSteer'
|
|
], null, null, true, false, true);
|
|
|
|
this._rigMan = SkeletonUtils.clone(gltf.scene.getObjectByName('Man'));
|
|
this._rigBike = gltf.scene.getObjectByName('Bike');
|
|
|
|
this._rigMan.getObjectByName('DriverScreamingMesh').removeFromParent();
|
|
this._rigMan.getObjectByName('DriverDeadMesh').removeFromParent();
|
|
this._rigMan.getObjectByName('HeadM').scale.set(0, 0, 0);
|
|
|
|
const handlebars = this._rigBike.getObjectByName('Handlebars');
|
|
|
|
this._bag = gltf.scene.getObjectByName('Bag');
|
|
this._bag.position.set(0, 0.08, 0.15);
|
|
handlebars.add(this._bag);
|
|
|
|
this._animatorMan = new Animator(this._rigMan, animationsMan);
|
|
this._animatorBike = new Animator(this._rigBike, animationsBike);
|
|
|
|
this._actionLowerBody = null;
|
|
this._actionUpperBody = null;
|
|
this._actionBike = null;
|
|
|
|
this._group = new THREE.Group();
|
|
this._group.name = 'Player';
|
|
this._group.add(this._rigMan);
|
|
this._group.add(this._rigBike);
|
|
tScene.add(this._group);
|
|
|
|
this._spotLight = new THREE.PointLight(0xf1a134, 0.6, 2);
|
|
this._spotLight.name = 'AreaLight';
|
|
this._spotLight.position.set(0, 0.13, 0.05);
|
|
handlebars.add(this._spotLight);
|
|
|
|
this._bikeLight = new THREE.SpotLight(0xffd37b, 0, 5, Math.PI * 0.25, 0.2, 0.4);
|
|
|
|
this._bikeLight.name = 'BikeLight';
|
|
this._bikeLight.position.set(0, 0.13, 0.05);
|
|
this._bikeLight.target = this._rigBike.getObjectByName('Light');
|
|
handlebars.add(this._bikeLight);
|
|
|
|
this._paddles = this._rigBike.getObjectByName('Paddles');
|
|
this._wheels = [
|
|
this._rigBike.getObjectByName('FrontWheel'),
|
|
this._rigBike.getObjectByName('BackWheel'),
|
|
];
|
|
|
|
this._group.traverse(obj => {
|
|
obj.matrixAutoUpdate = true;
|
|
obj.frustumCulled = false;
|
|
|
|
obj.material = Utility.replaceMaterial(obj.material);
|
|
});
|
|
|
|
this._camera = new THREE.PerspectiveCamera(70, 1, 0.06, 35);
|
|
this._camera.name = 'PlayerCamera';
|
|
this._camera.matrixAutoUpdate = true;
|
|
|
|
this._listener = new THREE.AudioListener();
|
|
this._camera.add(this._listener);
|
|
|
|
this._cameraShakeAmount = 0;
|
|
this._cameraShakeSeconds = 0;
|
|
this._cameraShakeAmountMax = 0;
|
|
|
|
this._rigMan.getObjectByName('CameraM').add(this._camera);
|
|
|
|
this._camera.position.set(0, 0.14, 0.1);
|
|
|
|
this._overlay = scene.findEntityOfType('Overlay');
|
|
this._road = scene.findEntityOfType('Road');
|
|
|
|
this._lastCursorPosition = new THREE.Vector2();
|
|
|
|
this._lookRotation = new THREE.Vector2();
|
|
this._lookTarget = null;
|
|
this._lookSeconds = null;
|
|
this._stopDuringLook = null;
|
|
|
|
this._endingSeconds = null;
|
|
|
|
this._resetHeight = null;
|
|
|
|
this._killed = null;
|
|
this._fallen = null;
|
|
this._biking = null;
|
|
|
|
this._secondsSinceReset = null;
|
|
this._hurtSeconds = null;
|
|
this._pauseSeconds = null;
|
|
this._slowSeconds = null;
|
|
this._slowFactor = 1;
|
|
|
|
this._deadHandsSeconds = null;
|
|
|
|
this._bagMode = null;
|
|
this._bagShakeSeconds = null;
|
|
|
|
this._speed = null;
|
|
|
|
this._steerAngle = null;
|
|
this._bikeAngle = null;
|
|
|
|
this._paddleAngle = null;
|
|
this._wheelAngle = null;
|
|
|
|
this._playingChainSound = false;
|
|
|
|
this.setBagMode(0);
|
|
}
|
|
|
|
reset(context, position, angle, fullReset = true) {
|
|
this._lookRotation.set(0, 0);
|
|
this._lookTarget = null;
|
|
this._lookSeconds = 0;
|
|
this._stopDuringLook = null;
|
|
|
|
this._resetHeight = true;
|
|
|
|
this._killed = false;
|
|
this._fallen = false;
|
|
this._biking = false;
|
|
|
|
if (fullReset) {
|
|
this._hurtSeconds = 0;
|
|
this._secondsSinceReset = 0;
|
|
} else if (this._hurtSeconds > 0) {
|
|
context.time.setTimeout(() => {
|
|
this._overlay.playScene(context, 'pulse_red', this._hurtSeconds);
|
|
}, 0);
|
|
}
|
|
|
|
this._pauseSeconds = 0;
|
|
this._slowSeconds = 0;
|
|
this._slowFactor = 1;
|
|
|
|
this._deadHandsSeconds = null;
|
|
|
|
this._bagShakeSeconds = 0;
|
|
|
|
this._speed = 0;
|
|
|
|
this._steerAngle = 0;
|
|
this._bikeAngle = angle;
|
|
|
|
this._paddleAngle = 0;
|
|
this._wheelAngle = 0;
|
|
|
|
this._group.position.copy(position);
|
|
this._group.updateWorldMatrix(false, true);
|
|
|
|
this.playScene(context, 'biking');
|
|
}
|
|
|
|
handleResize(context) {
|
|
super.handleResize(context);
|
|
|
|
const size = context.renderer.getSize(Player._tmpV20);
|
|
|
|
this._camera.aspect = size.x / size.y;
|
|
this._camera.updateProjectionMatrix();
|
|
}
|
|
|
|
update(context) {
|
|
super.update(context);
|
|
|
|
if (this._resetHeight === null) {
|
|
return;
|
|
}
|
|
|
|
const dt = context.time.elapsedSeconds;
|
|
|
|
// Ending
|
|
if (this._endingSeconds !== null) {
|
|
this._endingSeconds += dt;
|
|
|
|
this._camera.position.y += dt * 0.4;
|
|
|
|
return;
|
|
}
|
|
|
|
// Update timers
|
|
this._secondsSinceReset += dt;
|
|
this._hurtSeconds -= dt;
|
|
this._pauseSeconds -= dt;
|
|
this._slowSeconds -= dt;
|
|
this._lookSeconds -= dt;
|
|
|
|
const resettingLook = this._lookSeconds >= 0 && this._lookSeconds < Player._lookResetTime;
|
|
|
|
// Camera rotation fix
|
|
if (resettingLook) {
|
|
this._lookRotation.x = (this._lookRotation.x + Math.PI * 8) % (Math.PI * 2);
|
|
if (this._lookRotation.x > Math.PI) {
|
|
this._lookRotation.x -= Math.PI * 2;
|
|
}
|
|
}
|
|
|
|
// Get bike height and slope
|
|
const positionCenter = this._group.getWorldPosition(Player._tmpV30);
|
|
const positionFront = this._group.localToWorld(Player._tmpV31.set(0, 0, 0.4));
|
|
|
|
const heightCenter = this._road.getHeightAtPosition(positionCenter, true);
|
|
const heightFront = this._road.getHeightAtPosition(positionFront, false);
|
|
|
|
const slopeAngle = this._road.getForwardSlopeAngle(this._group, 1, 4);
|
|
|
|
// If player is in control
|
|
if (this._biking && !this._fallen && !this._killed) {
|
|
const upHeld = config.input.keys.up.some(Input.getKey);
|
|
const downHeld = config.input.keys.down.some(Input.getKey);
|
|
const leftHeld = config.input.keys.left.some(Input.getKey);
|
|
const rightHeld = config.input.keys.right.some(Input.getKey);
|
|
const autopilot = this._lookTarget !== null && !this._stopDuringLook;
|
|
|
|
// Show dead hands
|
|
if (this._deadHandsSeconds !== null) {
|
|
if (this._deadHandsSeconds === Infinity) {
|
|
if (this._lookRotation.y > -0.3) {
|
|
this._rigMan.getObjectByName('CyclistMesh').visible = false;
|
|
this._rigMan.getObjectByName('CyclistBloodyMesh').visible = true;
|
|
|
|
this._deadHandsSeconds = 1;
|
|
}
|
|
} else if (this._deadHandsSeconds < 0) {
|
|
this._deadHandsSeconds = null;
|
|
|
|
this._overlay.playScene(context, 'fade_in_noise', 0.5);
|
|
|
|
this._rigMan.getObjectByName('CyclistMesh').visible = true;
|
|
this._rigMan.getObjectByName('CyclistBloodyMesh').visible = false;
|
|
} else if (this._deadHandsSeconds < 1) {
|
|
this._deadHandsSeconds -= dt;
|
|
} else if (this._lookRotation.y < -0.55 + 0.2 * Math.abs(this._lookRotation.x) && Math.abs(this._lookRotation.x) < Math.PI * 0.4) {
|
|
this._deadHandsSeconds = 0.6;
|
|
|
|
AudioSystem.play('effect__stinger_1')
|
|
}
|
|
}
|
|
|
|
// Control camera
|
|
if (!Input.isPointerLocked()) {
|
|
this._lastCursorPosition.set(null, null);
|
|
} else {
|
|
const cursorPosition = Input.getCursorPosition(Player._tmpV20);
|
|
|
|
if (this._lastCursorPosition.x !== null &&
|
|
(this._lookTarget === null || resettingLook)) {
|
|
const dx = cursorPosition.x - this._lastCursorPosition.x;
|
|
const dy = cursorPosition.y - this._lastCursorPosition.y;
|
|
|
|
const maxX = Math.PI;
|
|
const x = THREE.MathUtils.clamp(this._lookRotation.x - dx * config.input.mouseSensitivity.x, -maxX, maxX);
|
|
const maxY = Math.PI * 0.4 - Math.max(0, Math.abs(x) * 0.5 - 0.4);
|
|
const y = THREE.MathUtils.clamp(this._lookRotation.y - dy * config.input.mouseSensitivity.y, -maxY, maxY);
|
|
|
|
this._lookRotation.set(x, y);
|
|
|
|
// Ring bell
|
|
if (Input.getMouseButtonDown() && this._secondsSinceReset > 1.5) {
|
|
AudioSystem.play('effect__bike_bell');
|
|
|
|
if (this._bagShakeSeconds > 0.6) {
|
|
this._bagShakeSeconds = Math.max(0.6, this._bagShakeSeconds - 3);
|
|
}
|
|
}
|
|
}
|
|
|
|
this._lastCursorPosition.copy(cursorPosition);
|
|
}
|
|
|
|
// Turn handle
|
|
if (autopilot) {
|
|
this._bikeAngle = this._road.getRoadAngle(this._road.getPlayerRoadPosition().y);
|
|
} else {
|
|
this._steerAngle = (
|
|
leftHeld ? Math.max(-1, this._steerAngle - Player._speedSteer * dt * (this._steerAngle > 0 ? 2 : 1)) :
|
|
rightHeld ? Math.min(1, this._steerAngle + Player._speedSteer * dt * (this._steerAngle < 0 ? 2 : 1)) :
|
|
this._steerAngle < 0 ?
|
|
Math.min(0, this._steerAngle + Player._speedSteer * dt) :
|
|
Math.max(0, this._steerAngle - Player._speedSteer * dt));
|
|
}
|
|
|
|
// Accelerate and brake
|
|
const stop = this._secondsSinceReset < 1 || this._pauseSeconds > 0 ||
|
|
this._lookTarget !== null && this._stopDuringLook && !resettingLook;
|
|
|
|
if (upHeld && !stop || autopilot) {
|
|
this._speed = this._speed + Player._speedAccelerate * dt;
|
|
} else if (downHeld || this._speed < Player._speedStopThreshold || stop) {
|
|
this._speed = this._speed - Player._speedBrake * dt;
|
|
}
|
|
|
|
// Limit speed
|
|
this._speed = THREE.MathUtils.clamp(this._speed, 0, 1);
|
|
|
|
// Fall down
|
|
const drop = heightCenter === null ? 0 : this._group.position.y - heightCenter;
|
|
const outsideMap = heightFront === null || heightCenter === null;
|
|
|
|
if (this._pauseSeconds <= 0 && this._lookTarget === null &&
|
|
(drop > 3 && !this._resetHeight || outsideMap)) {
|
|
this.playScene(context, 'fall');
|
|
|
|
return;
|
|
}
|
|
|
|
// Crash into wall
|
|
if (heightFront > heightCenter + 3) {
|
|
const forwardAngle = this._road.getRoadAngle(this._road.getPlayerRoadPosition().y);
|
|
const closestAngle = Utility.getClosestAngle(forwardAngle, this._bikeAngle);
|
|
|
|
if (closestAngle < -Math.PI / 2) {
|
|
this._bikeAngle = forwardAngle + Math.PI * 0.95;
|
|
} else if (closestAngle < 0) {
|
|
this._bikeAngle = forwardAngle + Math.PI * 0.05;
|
|
} else if (closestAngle > Math.PI / 2) {
|
|
this._bikeAngle = forwardAngle - Math.PI * 0.95;
|
|
} else {
|
|
this._bikeAngle = forwardAngle - Math.PI * 0.05;
|
|
}
|
|
|
|
this._group.position.x += Math.sin(-this._bikeAngle) * 0.3;
|
|
this._group.position.z += Math.cos(-this._bikeAngle) * 0.3;
|
|
|
|
if (!autopilot && this._pauseSeconds <= 0) {
|
|
this.stun(context);
|
|
}
|
|
}
|
|
|
|
// Calculate velocity
|
|
if (this._slowSeconds > 0) {
|
|
this._slowFactor = Math.max(Player._velocitySlowFactor, this._slowFactor - dt);
|
|
} else {
|
|
this._slowFactor = Math.min(1, this._slowFactor + dt * 2);
|
|
}
|
|
const velocity = Utility.easeInOutCubic(this._speed) * Player._velocityMax * this._slowFactor;
|
|
|
|
// Turn bike
|
|
this._bikeAngle += this._steerAngle * Math.min(Player._speedTurn * velocity, Player._speedTurnMax) * dt;
|
|
|
|
// Rotate and move player
|
|
this._group.rotation.set(0, 0, 0);
|
|
this._group.rotateY(-this._bikeAngle);
|
|
this._group.rotateX(-slopeAngle);
|
|
this._group.rotateZ((this._steerAngle + Math.sin(this._paddleAngle) * 0.5) * Math.min(velocity * 0.1, 1) * 0.02);
|
|
|
|
if (heightCenter !== null) {
|
|
this._group.position.y = heightCenter;
|
|
|
|
this._resetHeight = false;
|
|
}
|
|
|
|
this._group.position.x += Math.sin(-this._bikeAngle) * velocity * dt * 0.1;
|
|
this._group.position.z += Math.cos(-this._bikeAngle) * velocity * dt * 0.1;
|
|
|
|
// Pull towards center if in autopilot
|
|
if (autopilot) {
|
|
const roadCenter = this._road.roadToWorldPosition(Player._tmpV20.set(0, this._road.getPlayerRoadPosition().y), Player._tmpV30);
|
|
const centerT = 1 - 0.7 ** dt;
|
|
this._group.position.lerp(roadCenter, centerT);
|
|
}
|
|
|
|
// Update animations
|
|
const lookingRight = this._lookRotation.x < -Math.PI * 0.4;
|
|
const lookingLeft = this._lookRotation.x > Math.PI * 0.4;
|
|
|
|
// Update bike animations
|
|
if (this._actionLowerBody !== null) {
|
|
switch (this._actionLowerBody.getClip().name) {
|
|
case 'PlayerLowerBodyPaddle':
|
|
this._actionLowerBody.timeScale = this._speed * 1;
|
|
this._paddleAngle = this._actionLowerBody.time / this._actionLowerBody.getClip().duration * Math.PI * 2;
|
|
|
|
if (this._speed === 0) {
|
|
this._actionLowerBody.fadeOut(0.5);
|
|
this._actionLowerBody = this._animatorMan.playAction('PlayerLowerBodyPause', 1, 0.5);
|
|
}
|
|
|
|
break;
|
|
|
|
case 'PlayerLowerBodyPause':
|
|
this._paddleAngle = 0;
|
|
|
|
if (this._speed > 0) {
|
|
this._actionLowerBody.fadeOut(0.5);
|
|
this._actionLowerBody = this._animatorMan.playAction('PlayerLowerBodyPaddle', Infinity, 0.5, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (this._actionUpperBody.getClip().name) {
|
|
case 'PlayerUpperBodySteer':
|
|
this._actionUpperBody.time = ((this._steerAngle + 1) / 2) * this._actionUpperBody.getClip().duration;
|
|
|
|
if (lookingLeft || lookingRight) {
|
|
this._actionUpperBody.fadeOut(0.5);
|
|
this._actionUpperBody = this._animatorMan.playAction(lookingLeft ? 'PlayerUpperBodyLookLeft' : 'PlayerUpperBodyLookRight', 1);
|
|
}
|
|
|
|
break;
|
|
|
|
case 'PlayerUpperBodyLookLeft':
|
|
if (!lookingLeft) {
|
|
this._actionUpperBody.fadeOut(0.2);
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerUpperBodySteer', 1, 0.2, 0);
|
|
}
|
|
|
|
break;
|
|
|
|
case 'PlayerUpperBodyLookRight':
|
|
if (!lookingRight) {
|
|
this._actionUpperBody.fadeOut(0.2);
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerUpperBodySteer', 1, 0.2, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
switch (this._actionBike.getClip().name) {
|
|
case 'BikeSteer':
|
|
this._actionBike.time = ((this._steerAngle + 1) / 2) * this._actionBike.getClip().duration;
|
|
|
|
break;
|
|
}
|
|
|
|
// Rotate wheels
|
|
this._wheelAngle += velocity * dt * 2;
|
|
|
|
this._paddles.rotation.x = this._paddleAngle - Math.PI * 0.5;
|
|
|
|
this._wheels.forEach(wheel => {
|
|
wheel.rotation.x = this._wheelAngle;
|
|
});
|
|
}
|
|
|
|
// Play bike chain sound
|
|
if (this._speed > 0.5 && !this._playingChainSound) {
|
|
AudioSystem.play('effect__bike_chain', 1, true, 'chain');
|
|
|
|
this._playingChainSound = true;
|
|
} else if (this._speed <= 0.5 && this._playingChainSound) {
|
|
AudioSystem.stop('chain');
|
|
|
|
this._playingChainSound = false;
|
|
}
|
|
|
|
// Update bike light
|
|
this._bikeLight.intensity = THREE.MathUtils.clamp((this._speed - 0.3) * (2 - Math.random() * 0.1) * 4, 0.2, 2);
|
|
|
|
// Update animators
|
|
this._animatorMan.update(dt);
|
|
this._animatorBike.update(dt);
|
|
|
|
// Shake bag
|
|
this._bagShakeSeconds -= dt;
|
|
if (this._bagShakeSeconds <= 0) {
|
|
this._bagShakeSeconds = 5 + Math.random() * 20;
|
|
}
|
|
|
|
const bagShake = this._bagMode < 2 ? 0 :
|
|
Math.sin(context.time.totalSeconds * 30) * (0.3 - Math.abs(0.3 - Math.min(0.6, this._bagShakeSeconds)));
|
|
|
|
this._bag.rotation.set(Math.PI - 0.3, 0, -this._steerAngle * 0.2 + bagShake * 0.35);
|
|
|
|
// Update camera
|
|
if (this._lookTarget !== null && !resettingLook) {
|
|
const lerpT = 1 - Player._lookSmoothing ** dt;
|
|
|
|
const world = this._lookTarget.getWorldPosition(Player._tmpV30);
|
|
const local = this._group.worldToLocal(world);
|
|
const angle = Math.atan2(local.x, local.z);
|
|
const closestAngle = Utility.getClosestAngle(this._lookRotation.x, angle);
|
|
|
|
this._lookRotation.lerp(Player._tmpV20.set(this._lookRotation.x + closestAngle, 0), lerpT);
|
|
}
|
|
|
|
if (this._lookSeconds <= 0) {
|
|
this._lookTarget = null;
|
|
}
|
|
|
|
if (!this._biking || resettingLook) {
|
|
const lerpT = 1 - Player._lookResetSmoothing ** dt;
|
|
this._lookRotation.lerp(Player._tmpV20.set(0, 0), lerpT);
|
|
}
|
|
|
|
this._cameraShakeSeconds = Math.max(0, this._cameraShakeSeconds - dt);
|
|
const shakeAmount = Math.min(this._cameraShakeAmountMax, this._cameraShakeSeconds * this._cameraShakeAmount);
|
|
|
|
this._camera.rotation.set(
|
|
this._lookRotation.y + shakeAmount * Math.random(),
|
|
this._lookRotation.x + Math.PI + shakeAmount * Math.random(), 0, 'ZYX');
|
|
}
|
|
|
|
playScene(context, sceneName) {
|
|
this._speed = 0;
|
|
|
|
this._rigMan.getObjectByName('CyclistMesh').visible = true;
|
|
this._rigMan.getObjectByName('CyclistBloodyMesh').visible = false;
|
|
this._rigMan.getObjectByName('DriverCalmMesh').visible = false;
|
|
|
|
this._rigBike.visible = true;
|
|
|
|
this._animatorMan.fadeOutActions(0.1);
|
|
this._animatorBike.fadeOutActions(0.1);
|
|
|
|
this._biking = false;
|
|
this._killed = false;
|
|
this._fallen = false;
|
|
|
|
switch (sceneName) {
|
|
case 'biking':
|
|
this._biking = true;
|
|
|
|
this._actionLowerBody = this._animatorMan.playAction('PlayerLowerBodyPaddle', Infinity, 0, 0);
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerUpperBodySteer', 1, 0, 0);
|
|
this._actionBike = this._animatorBike.playAction('BikeSteer', 1, 0, 0);
|
|
|
|
break;
|
|
|
|
case 'crash':
|
|
AudioSystem.play('effect__bike_crash');
|
|
|
|
this._actionLowerBody = null;
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerCrash', 1, 0.1);
|
|
this._actionBike = this._animatorBike.playAction('BikeCrash', 1, 0.1);
|
|
|
|
this.shakeCamera(1);
|
|
|
|
this._killed = true;
|
|
|
|
break;
|
|
|
|
case 'fall':
|
|
AudioSystem.play('effect__bike_crash');
|
|
|
|
this._actionLowerBody = null;
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerCrash', 1, 0.1);
|
|
this._actionBike = this._animatorBike.playAction('BikeCrash', 1, 0.1);
|
|
|
|
this._fallen = true;
|
|
|
|
break;
|
|
|
|
case 'eaten_by_car':
|
|
AudioSystem.play('effect__bike_crash');
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__game_over_stinger');
|
|
}, 3500);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__game_over_scream');
|
|
}, 5000);
|
|
|
|
this._actionLowerBody = null;
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerEatenByCar', 1, 0.1, 1, () => {
|
|
this._killed = true;
|
|
});
|
|
this._actionBike = this._animatorBike.playAction('BikeCrash', 1, 0.1);
|
|
|
|
this._bikeAngle = this._road.getRoadAngle(this._road.getPlayerRoadPosition().y);
|
|
|
|
this._group.rotation.set(0, 0, 0);
|
|
this._group.rotateY(-this._bikeAngle);
|
|
|
|
break;
|
|
|
|
case 'eaten_by_ghost':
|
|
AudioSystem.play('effect__bike_crash');
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__game_over_stinger');
|
|
}, 3000);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__game_over_scream');
|
|
}, 4500);
|
|
|
|
this._actionLowerBody = null;
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerEatenByGhost', 1, 0.1, 1, () => {
|
|
this._killed = true;
|
|
});
|
|
this._actionBike = this._animatorBike.playAction('BikeCrash', 1, 0.1);
|
|
|
|
this._bikeAngle = this._road.getRoadAngle(this._road.getPlayerRoadPosition().y);
|
|
|
|
this._group.rotation.set(0, 0, 0);
|
|
this._group.rotateY(-this._bikeAngle);
|
|
|
|
break;
|
|
|
|
case 'strangled':
|
|
const killed = this._hurtSeconds > 0;
|
|
|
|
this._hurtSeconds = 50;
|
|
|
|
this._overlay.playScene(context, 'pulse_red', killed ? 3 : this._hurtSeconds);
|
|
|
|
this._actionLowerBody = null;
|
|
this._actionUpperBody = this._animatorMan.playAction(killed ? 'PlayerStrangled2' : 'PlayerStrangled1', 1, 0.1, 1, () => {
|
|
if (killed) {
|
|
this._killed = true;
|
|
} else {
|
|
this.playScene(context, 'biking');
|
|
}
|
|
});
|
|
|
|
AudioSystem.play('effect__ghost_catch');
|
|
|
|
if (killed) {
|
|
this._actionBike = this._animatorBike.playAction('BikeCrash', 1, 0, 0.3);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__game_over_stinger');
|
|
}, 2500);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__game_over_scream');
|
|
}, 4000);
|
|
} else {
|
|
this._actionBike = this._animatorBike.playAction('BikeSteer', 1, 0, 0);
|
|
}
|
|
|
|
this.shakeCamera(2, 0.5, 0.05);
|
|
|
|
break;
|
|
|
|
case 'trapped':
|
|
AudioSystem.stop('ambiance');
|
|
|
|
AudioSystem.play('effect__bike_crash');
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__trap_chain');
|
|
}, 1000);
|
|
|
|
this._actionBike = this._animatorBike.playAction('BikeCrash');
|
|
|
|
this.shakeCamera(0.1);
|
|
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerTrapped', 1, 0.1, 1, () => {
|
|
AudioSystem.play('effect__ghost_laugh');
|
|
AudioSystem.play('ambiance__creepy_violin', 1, true, 'ambiance', 0, 3);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__car_reveal');
|
|
}, 12000);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.stop('ambiance', 1);
|
|
AudioSystem.play('ambiance__forest_after_hunt', 1, true, 'ambiance', 0, 0.5);
|
|
|
|
this._overlay.playScene(context, 'enough', 2);
|
|
}, 12500);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__game_over_stinger');
|
|
}, 21400);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__bike_stun');
|
|
}, 16800);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__game_over_splatter');
|
|
}, 22500);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__car_reveal');
|
|
}, 24000);
|
|
|
|
context.time.setTimeout(() => {
|
|
this._overlay.playScene(context, 'belong', 4);
|
|
}, 25000);
|
|
|
|
this._animatorMan.fadeOutActions(0.1);
|
|
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerTrappedIdle', 9, 0.1, 1, () => {
|
|
this._animatorMan.fadeOutActions(0.1);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__bike_stun');
|
|
|
|
this.shakeCamera(0.2);
|
|
}, 1100);
|
|
|
|
for (let i = 0; i < 4; i++) {
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.play('effect__step');
|
|
|
|
this.shakeCamera(0.05);
|
|
}, 4500 + i * 500);
|
|
}
|
|
|
|
AudioSystem.play('effect__trap_chain');
|
|
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerTrappedEscape', 1, 0.1, 1, () => {
|
|
AudioSystem.stop('ambiance', 0.5);
|
|
AudioSystem.play('effect__road_loop');
|
|
|
|
this._overlay.playScene(context, 'spiral', 10);
|
|
|
|
context.time.setTimeout(() => {
|
|
const scene = this.getScene();
|
|
|
|
scene.findEntityOfType('Environment').setEnvironment('forest');
|
|
scene.findEntityOfType('Director').resetInEnding(context);
|
|
|
|
this.playScene(context, 'ending', 1, 0, 0.7);
|
|
}, 5000);
|
|
});
|
|
});
|
|
});
|
|
|
|
const throneSegment = this._road.getSegmentInstance('TuonelaThrone');
|
|
const chains = throneSegment.mesh.getObjectByName('Chains');
|
|
|
|
chains.getWorldPosition(this._group.position);
|
|
chains.getWorldQuaternion(this._group.quaternion);
|
|
|
|
context.time.setTimeout(() => {
|
|
this._rigMan.getObjectByName('CyclistMesh').visible = false;
|
|
this._rigMan.getObjectByName('DriverCalmMesh').visible = true;
|
|
|
|
chains.visible = true;
|
|
}, 3000);
|
|
|
|
break;
|
|
|
|
case 'ending':
|
|
this._rigMan.getObjectByName('CyclistMesh').visible = false;
|
|
this._rigMan.getObjectByName('DriverCalmMesh').visible = true;
|
|
|
|
this._rigBike.visible = false;
|
|
|
|
const segment = this._road.getSegmentInstance('ForestBike');
|
|
const car = segment.mesh.getObjectByName('Car');
|
|
|
|
car.getWorldPosition(this._group.position);
|
|
car.getWorldQuaternion(this._group.quaternion);
|
|
|
|
this._actionUpperBody = this._animatorMan.playAction('PlayerEnding', 1, 0, 1);
|
|
|
|
this.getScene().findEntityOfType('Environment').getPointLight(0xffffff, 0.5, 2, 2,
|
|
this._group.position.x,
|
|
this._group.position.y + 1,
|
|
this._group.position.z);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.stop('ambiance', 3);
|
|
AudioSystem.play('music__outro', 1, true, 'music');
|
|
}, 16000);
|
|
|
|
context.time.setTimeout(() => {
|
|
this._camera.removeFromParent();
|
|
this.getScene().getThreeScene().add(this._camera);
|
|
|
|
car.getWorldPosition(this._camera.position).y += 0.7;
|
|
this._camera.rotation.set(-Math.PI / 2, 0, 0);
|
|
|
|
this._endingSeconds = 0;
|
|
}, 17000);
|
|
|
|
context.time.setTimeout(() => {
|
|
this._overlay.playScene(context, 'outro', 6, () => {
|
|
this.getScene().stop(context);
|
|
|
|
context.app.constructor.setCreditsVisibility(true, false);
|
|
|
|
Input.exitPointerLock();
|
|
Input.setPointerLockElement();
|
|
});
|
|
}, 38000);
|
|
|
|
context.time.setTimeout(() => {
|
|
AudioSystem.stop('music', 10);
|
|
}, 50000);
|
|
|
|
break;
|
|
|
|
default:
|
|
throw new Error(`Player scene "${sceneName}" does not exist`);
|
|
}
|
|
}
|
|
|
|
getCamera() {
|
|
return this._camera;
|
|
}
|
|
|
|
getListener() {
|
|
return this._listener;
|
|
}
|
|
|
|
getGroup() {
|
|
return this._group;
|
|
}
|
|
|
|
getHurt() {
|
|
return this._hurtSeconds > 0;
|
|
}
|
|
|
|
getKilled() {
|
|
return this._killed;
|
|
}
|
|
|
|
getFallen() {
|
|
return this._fallen;
|
|
}
|
|
|
|
getBiking() {
|
|
return this._biking;
|
|
}
|
|
|
|
getSpeed() {
|
|
return this._speed;
|
|
}
|
|
|
|
getLookAngleTowards(object3d) {
|
|
const world = object3d.getWorldPosition(Player._tmpV30);
|
|
const local = this._camera.worldToLocal(world);
|
|
const forward = Player._tmpV31.set(0, 0, -1);
|
|
|
|
return local.angleTo(forward);
|
|
}
|
|
|
|
getLookRotation() {
|
|
return this._lookRotation;
|
|
}
|
|
|
|
setBagMode(mode) {
|
|
this._bagMode = mode;
|
|
|
|
this._bag.getObjectByName('BagNormal').visible = this._bagMode === 0;
|
|
this._bag.getObjectByName('BagLittleBlood').visible = this._bagMode === 1;
|
|
this._bag.getObjectByName('BagMuchBlood').visible = this._bagMode === 2;
|
|
}
|
|
|
|
shakeCamera(duration, amount = 0.5, amountMax = 0.2) {
|
|
this._cameraShakeSeconds = duration;
|
|
this._cameraShakeAmount = amount;
|
|
this._cameraShakeAmountMax = amountMax;
|
|
}
|
|
|
|
lookAt(object3d, seconds, stopDuringLook = true) {
|
|
this._lookTarget = object3d;
|
|
this._lookSeconds = seconds;
|
|
this._stopDuringLook = stopDuringLook;
|
|
}
|
|
|
|
pause(seconds) {
|
|
this._pauseSeconds = seconds;
|
|
}
|
|
|
|
slowDown(seconds) {
|
|
this._slowSeconds = seconds;
|
|
}
|
|
|
|
stun(context) {
|
|
if (this._lookSeconds > 0) {
|
|
return;
|
|
}
|
|
|
|
AudioSystem.play('effect__bike_stun');
|
|
|
|
if (!this._overlay.isPlayingScene()) {
|
|
this._overlay.playScene(context, 'hurt', 0.5);
|
|
}
|
|
|
|
this.shakeCamera(0.3);
|
|
|
|
this._pauseSeconds = 0.3;
|
|
this._speed = 0;
|
|
}
|
|
|
|
showDeadHands() {
|
|
this._deadHandsSeconds = Infinity;
|
|
}
|
|
}
|
|
|
|
Player._init();
|
|
Player.p_register();
|