waves/public/assets/g/burgerandfrights/js/entities/player.class.js
2025-04-09 17:11:14 -05:00

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();