From 2edd620a2ecc338c0e9425768548bf01ded35289 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 1 Aug 2025 18:47:22 +0000 Subject: [PATCH 01/58] #31 - Adding WIP Hangar class --- .../scenograph/objects/structures/hangar.js | 106 ++++++++++++++++++ .../src/app/scenograph/overlays/scanners.js | 1 + client/src/app/scenograph/scenes/overworld.js | 14 +++ 3 files changed, 121 insertions(+) create mode 100644 client/src/app/scenograph/objects/structures/hangar.js diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js new file mode 100644 index 00000000..ad17fd09 --- /dev/null +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -0,0 +1,106 @@ +/** + * Union Extractor + */ + +/** + * Vendor libs + */ +import * as THREE from 'three'; +import { HOLLOW_SUBTRACTION, Brush, Evaluator } from 'three-bvh-csg'; + +/** + * Internal libs and helpers. + */ +import l from '@/helpers/l.js'; +import { proceduralBuilding, proceduralMetalMaterial2 } from '@/scenograph/materials.js'; + +export default class Hangar { + + // THREE.Mesh + mesh; + + // The scale of the mesh. + size; + + constructor() { + this.ready = false; + this.size = 150; + + } + + async load() { + + //const material = new THREE.MeshBasicMaterial( {color: 0xff0000, transparent: true, opacity: 1.0, side: THREE.DoubleSide} ); + + const material = proceduralBuilding( { + uniforms: { + time : { value: 0.0 }, + scale : { value: .005 }, // Scale + lacunarity : { value: 2.0 }, // Lacunarity + randomness : { value: 1.0 }, // Randomness + emitColour1 : { value: new THREE.Vector4( 0.0, 0.0, 0.0, 0.25 ) }, // Emission gradient colour 1 + emitColour2 : { value: new THREE.Vector4( 0.158, 1., 1., .9 ) }, // Emission gradient colour 2 + shadowFactor: { value: 0.03 }, + shadowOffset: { value: 0.1 }, + } + } ); + + material.transparent = true; + material.side = THREE.DoubleSide; + + window.extractor = {}; + window.extractor.outer = material; + + let outerMesh = new Brush( new THREE.CylinderGeometry( 8, 8, 100, 8, 1, false ), material ); + outerMesh.scale.setScalar( this.size ); + outerMesh.updateMatrixWorld(); + + // let innerMesh = new Brush( new THREE.CylinderGeometry( 7.5, 7.5, 99, 8, 1, false ), material ); + // innerMesh.position.y = 1000; + // innerMesh.scale.setScalar( this.size ); + // innerMesh.updateMatrixWorld(); + + // let result = new THREE.Mesh( + // new THREE.BufferGeometry(), + // new THREE.MeshBasicMaterial() + // ); + + // // Constructive Solid Geometry (csg) Evaluator. + // let csgEvaluator; + // csgEvaluator = new Evaluator(); + // csgEvaluator.useGroups = true; + // csgEvaluator.evaluate( outerMesh, innerMesh, HOLLOW_SUBTRACTION, result ); + // result.name = 'outer'; + + // let innerMesh2 = new THREE.Mesh( new THREE.CylinderGeometry( 7.5, 7.5, 100, 8, 1, true ), material.clone() ); + // window.extractor.inner = innerMesh2.material; + // innerMesh2.material.uniforms.scale.value = 0.8; + // innerMesh2.position.y = 0; + // innerMesh2.scale.setScalar( this.size ); + // innerMesh2.updateMatrixWorld(); + // innerMesh2.name = 'inner'; + + this.mesh = new THREE.Object3D(); + this.mesh.add( outerMesh ); + // this.mesh.add( innerMesh2 ); + this.mesh.userData.targetable = true; + this.mesh.userData.objectClass = 'hangar'; + + } + + /** + * Animate hook. + * + * This method is called within the main animation loop and + * therefore must only reference global objects or properties. + * + * @method animate + * @memberof Hangar + * @global + * @note All references within this method should be globally accessible. + **/ + animate( currentTime ) { + + } + +} diff --git a/client/src/app/scenograph/overlays/scanners.js b/client/src/app/scenograph/overlays/scanners.js index e9965e2d..4a58ca94 100644 --- a/client/src/app/scenograph/overlays/scanners.js +++ b/client/src/app/scenograph/overlays/scanners.js @@ -206,6 +206,7 @@ export default class Scanners { 'cargoShip': 'ship', 'city': 'structure', 'extractors': 'structure', + 'hangar': 'structure', 'missiles': 'aircraft', 'player': 'aircraft', 'refinery': 'structure', diff --git a/client/src/app/scenograph/scenes/overworld.js b/client/src/app/scenograph/scenes/overworld.js index bf7d7b12..a91b09f1 100644 --- a/client/src/app/scenograph/scenes/overworld.js +++ b/client/src/app/scenograph/scenes/overworld.js @@ -20,6 +20,7 @@ import Sky2 from "@/scenograph/objects/environment/sky2"; // Structures import Extractors from "@/scenograph/objects/structures/extractors"; +import Hangar from "@/scenograph/objects/structures/hangar"; import Platform from "@/scenograph/objects/structures/platform"; import Refineries from "@/scenograph/objects/structures/refineries"; @@ -172,6 +173,19 @@ export default class Overworld extends SceneBase { l.current_scene.objects.bot.animate ); + // Setup hangar + // @todo: #31 Implement first person mode and a way to go between being in the hangar and being in the aircraft + l.current_scene.objects.hangar = new Hangar(); + await l.current_scene.objects.hangar.load(); + // l.current_scene.objects.hangar.mesh.position.z = 0; + // l.current_scene.objects.hangar.mesh.position.x = 0; + l.current_scene.scene.add( + l.current_scene.objects.hangar.mesh + ); + l.current_scene.animation_queue.push( + l.current_scene.objects.hangar.animate + ); + // Setup Platform l.current_scene.objects.platform = new Platform(); From 78538db3951c17f64a7f5d5882ace5bd5c194988 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sat, 2 Aug 2025 14:23:41 +0000 Subject: [PATCH 02/58] #31 - Tweaking hangar appearance --- .../scenograph/objects/structures/hangar.js | 69 +++++++++++-------- client/src/app/scenograph/scenes/overworld.js | 2 +- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index ad17fd09..d52a9c13 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -24,18 +24,18 @@ export default class Hangar { constructor() { this.ready = false; - this.size = 150; + this.size = 5; } async load() { - //const material = new THREE.MeshBasicMaterial( {color: 0xff0000, transparent: true, opacity: 1.0, side: THREE.DoubleSide} ); + const clearMaterial = new THREE.MeshBasicMaterial( {color: 0xffFFFF, transparent: true, visible: false, side: THREE.DoubleSide} ); const material = proceduralBuilding( { uniforms: { time : { value: 0.0 }, - scale : { value: .005 }, // Scale + scale : { value: .05 }, // Scale lacunarity : { value: 2.0 }, // Lacunarity randomness : { value: 1.0 }, // Randomness emitColour1 : { value: new THREE.Vector4( 0.0, 0.0, 0.0, 0.25 ) }, // Emission gradient colour 1 @@ -51,38 +51,49 @@ export default class Hangar { window.extractor = {}; window.extractor.outer = material; - let outerMesh = new Brush( new THREE.CylinderGeometry( 8, 8, 100, 8, 1, false ), material ); + let outerMesh = new Brush( new THREE.BoxGeometry( 10, 5, 10, 2, 2, 2 ), material ); outerMesh.scale.setScalar( this.size ); outerMesh.updateMatrixWorld(); - // let innerMesh = new Brush( new THREE.CylinderGeometry( 7.5, 7.5, 99, 8, 1, false ), material ); - // innerMesh.position.y = 1000; - // innerMesh.scale.setScalar( this.size ); - // innerMesh.updateMatrixWorld(); - - // let result = new THREE.Mesh( - // new THREE.BufferGeometry(), - // new THREE.MeshBasicMaterial() - // ); - - // // Constructive Solid Geometry (csg) Evaluator. - // let csgEvaluator; - // csgEvaluator = new Evaluator(); - // csgEvaluator.useGroups = true; - // csgEvaluator.evaluate( outerMesh, innerMesh, HOLLOW_SUBTRACTION, result ); - // result.name = 'outer'; - - // let innerMesh2 = new THREE.Mesh( new THREE.CylinderGeometry( 7.5, 7.5, 100, 8, 1, true ), material.clone() ); - // window.extractor.inner = innerMesh2.material; + let innerMesh = new Brush( new THREE.BoxGeometry( 9, 4, 9, 2, 2, 2 ), material ); + innerMesh.position.z = -10; + innerMesh.scale.setScalar( this.size ); + innerMesh.updateMatrixWorld(); + + let result = new THREE.Mesh( + new THREE.BufferGeometry(), + new THREE.MeshBasicMaterial() + ); + + // Constructive Solid Geometry (csg) Evaluator. + let csgEvaluator; + csgEvaluator = new Evaluator(); + csgEvaluator.useGroups = true; + csgEvaluator.evaluate( outerMesh, innerMesh, HOLLOW_SUBTRACTION, result ); + result.name = 'outer'; + + let innerGeo = new THREE.BoxGeometry( 9, 4, 9, 2, 2, 2 ); + let innerMeshMaterial = material.clone(); + innerMeshMaterial.uniforms.scale.value = 0.25; + + let innerMesh2 = new THREE.Mesh( innerGeo, [ + innerMeshMaterial, + innerMeshMaterial, + innerMeshMaterial, + innerMeshMaterial, + innerMeshMaterial, + clearMaterial.clone() + ] ); + window.extractor.inner = innerMesh2.material; // innerMesh2.material.uniforms.scale.value = 0.8; - // innerMesh2.position.y = 0; - // innerMesh2.scale.setScalar( this.size ); - // innerMesh2.updateMatrixWorld(); - // innerMesh2.name = 'inner'; + innerMesh2.position.y = 0; + innerMesh2.scale.setScalar( this.size ); + innerMesh2.updateMatrixWorld(); + innerMesh2.name = 'inner'; this.mesh = new THREE.Object3D(); - this.mesh.add( outerMesh ); - // this.mesh.add( innerMesh2 ); + this.mesh.add( result ); + this.mesh.add( innerMesh2 ); this.mesh.userData.targetable = true; this.mesh.userData.objectClass = 'hangar'; diff --git a/client/src/app/scenograph/scenes/overworld.js b/client/src/app/scenograph/scenes/overworld.js index a91b09f1..99a985bd 100644 --- a/client/src/app/scenograph/scenes/overworld.js +++ b/client/src/app/scenograph/scenes/overworld.js @@ -177,7 +177,7 @@ export default class Overworld extends SceneBase { // @todo: #31 Implement first person mode and a way to go between being in the hangar and being in the aircraft l.current_scene.objects.hangar = new Hangar(); await l.current_scene.objects.hangar.load(); - // l.current_scene.objects.hangar.mesh.position.z = 0; + l.current_scene.objects.hangar.mesh.position.y = 15; // l.current_scene.objects.hangar.mesh.position.x = 0; l.current_scene.scene.add( l.current_scene.objects.hangar.mesh From 685b4000040176412414cd9cc051e7eab6b559b9 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 3 Aug 2025 09:13:32 +0000 Subject: [PATCH 03/58] #31 - Working on player hangar --- .../scenograph/objects/structures/hangar.js | 87 ++++++++++++++----- client/src/app/scenograph/overlays/map.js | 1 + 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index d52a9c13..ba58a46a 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -6,7 +6,7 @@ * Vendor libs */ import * as THREE from 'three'; -import { HOLLOW_SUBTRACTION, Brush, Evaluator } from 'three-bvh-csg'; +import { ADDITION, HOLLOW_SUBTRACTION, Brush, Evaluator } from 'three-bvh-csg'; /** * Internal libs and helpers. @@ -16,6 +16,14 @@ import { proceduralBuilding, proceduralMetalMaterial2 } from '@/scenograph/mater export default class Hangar { + // Dimensions of the hangar + // @todo: Dynamic hangars with different dimensions + hangarSize; + + // Dimensions of player living quarters + // @todo: Dynamic hangars which don't all have one. + quartersSize; + // THREE.Mesh mesh; @@ -23,6 +31,19 @@ export default class Hangar { size; constructor() { + + this.hangarSize = { + width: 10, + height: 5, + depth: 10 + }; + + this.quartersSize = { + width: 5, + height: 2.5, + depth: 5 + }; + this.ready = false; this.size = 5; @@ -51,15 +72,36 @@ export default class Hangar { window.extractor = {}; window.extractor.outer = material; - let outerMesh = new Brush( new THREE.BoxGeometry( 10, 5, 10, 2, 2, 2 ), material ); + let innerMeshMaterial = material.clone(); + innerMeshMaterial.uniforms.scale.value = 0.25; + + + let outerMesh = new Brush( new THREE.BoxGeometry( this.hangarSize.width, this.hangarSize.height, this.hangarSize.depth, 2, 2, 2 ), [ + innerMeshMaterial, + innerMeshMaterial, + innerMeshMaterial, + innerMeshMaterial, + innerMeshMaterial, + clearMaterial.clone() + ] ); outerMesh.scale.setScalar( this.size ); outerMesh.updateMatrixWorld(); - let innerMesh = new Brush( new THREE.BoxGeometry( 9, 4, 9, 2, 2, 2 ), material ); - innerMesh.position.z = -10; - innerMesh.scale.setScalar( this.size ); + // Create door geometry + var doorWidth = 8.2; + var doorHeight = 20.4; + var doorDepth = 20; + var doorGeometry = new THREE.BoxGeometry( doorWidth, doorHeight, doorDepth ); + + let innerMesh = new Brush( doorGeometry, material ); + innerMesh.position.x = - (this.hangarSize.width * this.size) / 2 ; + innerMesh.position.y = ( - (this.hangarSize.height * this.size) / 2 ) + doorHeight * 0.25; + innerMesh.rotation.y = Math.PI / 2; + innerMesh.scale.setScalar( this.size / 10 ); innerMesh.updateMatrixWorld(); + window.hangarDoor = innerMesh; + let result = new THREE.Mesh( new THREE.BufferGeometry(), new THREE.MeshBasicMaterial() @@ -69,31 +111,34 @@ export default class Hangar { let csgEvaluator; csgEvaluator = new Evaluator(); csgEvaluator.useGroups = true; - csgEvaluator.evaluate( outerMesh, innerMesh, HOLLOW_SUBTRACTION, result ); + csgEvaluator.evaluate( innerMesh, outerMesh, ADDITION, result ); result.name = 'outer'; - let innerGeo = new THREE.BoxGeometry( 9, 4, 9, 2, 2, 2 ); - let innerMeshMaterial = material.clone(); - innerMeshMaterial.uniforms.scale.value = 0.25; + let innerGeo = new THREE.BoxGeometry( this.quartersSize.width, this.quartersSize.height, this.quartersSize.depth, 2, 2, 2 ); + + let innerMesh2 = new Brush( innerGeo, material ); + window.quarters = innerMesh2; + innerMesh2.material.uniforms.scale.value = 0.8; + // Offset player quarters by the hangars half width + innerMesh2.position.x = (- (this.hangarSize.width * this.size) / 2); + // Offset player quarters by the access corridor + innerMesh2.position.x += - doorDepth * 0.25; + // Offset player quarters by half the quarters width + innerMesh2.position.x += (- (this.quartersSize.width * this.size) / 2); + // Offset for intersection + innerMesh2.position.x += 1; + + innerMesh2.position.y = ( - (this.quartersSize.height * this.size) / 2 ); - let innerMesh2 = new THREE.Mesh( innerGeo, [ - innerMeshMaterial, - innerMeshMaterial, - innerMeshMaterial, - innerMeshMaterial, - innerMeshMaterial, - clearMaterial.clone() - ] ); - window.extractor.inner = innerMesh2.material; - // innerMesh2.material.uniforms.scale.value = 0.8; - innerMesh2.position.y = 0; innerMesh2.scale.setScalar( this.size ); innerMesh2.updateMatrixWorld(); innerMesh2.name = 'inner'; + csgEvaluator.evaluate( result, innerMesh2, ADDITION, result ); + this.mesh = new THREE.Object3D(); this.mesh.add( result ); - this.mesh.add( innerMesh2 ); + // this.mesh.add( innerMesh2 ); this.mesh.userData.targetable = true; this.mesh.userData.objectClass = 'hangar'; diff --git a/client/src/app/scenograph/overlays/map.js b/client/src/app/scenograph/overlays/map.js index 2bbaac6e..b7932e6e 100644 --- a/client/src/app/scenograph/overlays/map.js +++ b/client/src/app/scenograph/overlays/map.js @@ -68,6 +68,7 @@ export default class Map { 'cargoShip': 'ship', 'city': 'structure', 'extractors': 'structure', + 'hangar': 'structure', 'missiles': 'aircraft', 'player': 'aircraft', 'refinery': 'structure', From 5a53344c46e4e77e2892c0437089f1fea063aeeb Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 3 Aug 2025 09:28:46 +0000 Subject: [PATCH 04/58] #31 - Refactoring hangar class and tidying up cruft --- .../scenograph/objects/structures/hangar.js | 164 +++++++++++------- 1 file changed, 100 insertions(+), 64 deletions(-) diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index ba58a46a..a3cba3ce 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -20,6 +20,8 @@ export default class Hangar { // @todo: Dynamic hangars with different dimensions hangarSize; + materials; + // Dimensions of player living quarters // @todo: Dynamic hangars which don't all have one. quartersSize; @@ -32,7 +34,16 @@ export default class Hangar { constructor() { - this.hangarSize = { + this.materials = {}; + + // Based on office door dimensions. + this.corridorSize = { + width: 8.2, + height: 20.4, + depth: 20, + } + + this.hangarSize = { width: 10, height: 5, depth: 10 @@ -51,99 +62,124 @@ export default class Hangar { async load() { - const clearMaterial = new THREE.MeshBasicMaterial( {color: 0xffFFFF, transparent: true, visible: false, side: THREE.DoubleSide} ); - - const material = proceduralBuilding( { + /** + * Setup Hangar materials + */ + + this.materials.clear = new THREE.MeshBasicMaterial( { color: 0xffFFFF, transparent: true, visible: false, side: THREE.DoubleSide } ); + this.materials.metal = proceduralBuilding( { uniforms: { - time : { value: 0.0 }, - scale : { value: .05 }, // Scale - lacunarity : { value: 2.0 }, // Lacunarity - randomness : { value: 1.0 }, // Randomness - emitColour1 : { value: new THREE.Vector4( 0.0, 0.0, 0.0, 0.25 ) }, // Emission gradient colour 1 - emitColour2 : { value: new THREE.Vector4( 0.158, 1., 1., .9 ) }, // Emission gradient colour 2 - shadowFactor: { value: 0.03 }, - shadowOffset: { value: 0.1 }, + time: { value: 0.0 }, + scale: { value: .05 }, // Scale + lacunarity: { value: 2.0 }, // Lacunarity + randomness: { value: 1.0 }, // Randomness + emitColour1: { value: new THREE.Vector4( 0.0, 0.0, 0.0, 0.25 ) }, // Emission gradient colour 1 + emitColour2: { value: new THREE.Vector4( 0.158, 1., 1., .9 ) }, // Emission gradient colour 2 + shadowFactor: { value: 0.03 }, + shadowOffset: { value: 0.1 }, } } ); + this.materials.metal.transparent = true; + this.materials.metal.side = THREE.DoubleSide; + // Clone of the material to customize scaling. + this.materials.hangar = this.materials.metal.clone(); + this.materials.hangar.uniforms.scale.value = 0.25; - material.transparent = true; - material.side = THREE.DoubleSide; - - window.extractor = {}; - window.extractor.outer = material; - - let innerMeshMaterial = material.clone(); - innerMeshMaterial.uniforms.scale.value = 0.25; + let hangarMesh = await this.loadHangarMesh(); + + /** + * Corridor mesh + */ + let corridorMesh = await this.loadCorridorMesh(); - let outerMesh = new Brush( new THREE.BoxGeometry( this.hangarSize.width, this.hangarSize.height, this.hangarSize.depth, 2, 2, 2 ), [ - innerMeshMaterial, - innerMeshMaterial, - innerMeshMaterial, - innerMeshMaterial, - innerMeshMaterial, - clearMaterial.clone() - ] ); - outerMesh.scale.setScalar( this.size ); - outerMesh.updateMatrixWorld(); - - // Create door geometry - var doorWidth = 8.2; - var doorHeight = 20.4; - var doorDepth = 20; - var doorGeometry = new THREE.BoxGeometry( doorWidth, doorHeight, doorDepth ); - - let innerMesh = new Brush( doorGeometry, material ); - innerMesh.position.x = - (this.hangarSize.width * this.size) / 2 ; - innerMesh.position.y = ( - (this.hangarSize.height * this.size) / 2 ) + doorHeight * 0.25; - innerMesh.rotation.y = Math.PI / 2; - innerMesh.scale.setScalar( this.size / 10 ); - innerMesh.updateMatrixWorld(); - - window.hangarDoor = innerMesh; - - let result = new THREE.Mesh( - new THREE.BufferGeometry(), - new THREE.MeshBasicMaterial() - ); + // Setup the result mesh. + let result = new THREE.Mesh( new THREE.BufferGeometry(), new THREE.MeshBasicMaterial() ); // Constructive Solid Geometry (csg) Evaluator. let csgEvaluator; csgEvaluator = new Evaluator(); csgEvaluator.useGroups = true; - csgEvaluator.evaluate( innerMesh, outerMesh, ADDITION, result ); + csgEvaluator.evaluate( corridorMesh, hangarMesh, ADDITION, result ); result.name = 'outer'; let innerGeo = new THREE.BoxGeometry( this.quartersSize.width, this.quartersSize.height, this.quartersSize.depth, 2, 2, 2 ); - let innerMesh2 = new Brush( innerGeo, material ); - window.quarters = innerMesh2; - innerMesh2.material.uniforms.scale.value = 0.8; + /** + * Player quarters mesh. + */ + let quartersMesh = new Brush( innerGeo, this.materials.metal ); + window.quarters = quartersMesh; + quartersMesh.material.uniforms.scale.value = 0.8; // Offset player quarters by the hangars half width - innerMesh2.position.x = (- (this.hangarSize.width * this.size) / 2); + quartersMesh.position.x = ( - ( this.hangarSize.width * this.size ) / 2 ); // Offset player quarters by the access corridor - innerMesh2.position.x += - doorDepth * 0.25; + quartersMesh.position.x += - this.corridorSize.depth * 0.25; // Offset player quarters by half the quarters width - innerMesh2.position.x += (- (this.quartersSize.width * this.size) / 2); + quartersMesh.position.x += ( - ( this.quartersSize.width * this.size ) / 2 ); // Offset for intersection - innerMesh2.position.x += 1; + quartersMesh.position.x += 1; - innerMesh2.position.y = ( - (this.quartersSize.height * this.size) / 2 ); + quartersMesh.position.y = ( - ( this.quartersSize.height * this.size ) / 2 ); - innerMesh2.scale.setScalar( this.size ); - innerMesh2.updateMatrixWorld(); - innerMesh2.name = 'inner'; + quartersMesh.scale.setScalar( this.size ); + quartersMesh.updateMatrixWorld(); + quartersMesh.name = 'inner'; - csgEvaluator.evaluate( result, innerMesh2, ADDITION, result ); + // @todo: #31 - Implement hierarchical operations + // csgEvaluator.evaluate( result, innerMesh2, ADDITION, result ); this.mesh = new THREE.Object3D(); this.mesh.add( result ); // this.mesh.add( innerMesh2 ); - this.mesh.userData.targetable = true; + this.mesh.userData.targetable = false; this.mesh.userData.objectClass = 'hangar'; } + /** + * Creates the Hangar's main mesh. + */ + async loadHangarMesh() { + /** + * Hangar mesh + */ + let hangarMesh = new Brush( new THREE.BoxGeometry( this.hangarSize.width, this.hangarSize.height, this.hangarSize.depth, 2, 2, 2 ), [ + this.materials.hangar, + this.materials.hangar, + this.materials.hangar, + this.materials.hangar, + this.materials.hangar, + this.materials.clear.clone() + ] ); + + hangarMesh.scale.setScalar( this.size ); + + hangarMesh.updateMatrixWorld(); + + return hangarMesh; + } + + /** + * Creates the corridor mesh. + */ + async loadCorridorMesh() { + + var doorGeometry = new THREE.BoxGeometry( this.corridorSize.width, this.corridorSize.height, this.corridorSize.depth ); + + let corridorMesh = new Brush( doorGeometry, this.materials.metal ); + + corridorMesh.position.x = - ( this.hangarSize.width * this.size ) / 2; + corridorMesh.position.y = ( - ( this.hangarSize.height * this.size ) / 2 ) + this.corridorSize.height * 0.25; + corridorMesh.rotation.y = Math.PI / 2; + + corridorMesh.scale.setScalar( this.size / 10 ); + + corridorMesh.updateMatrixWorld(); + + return corridorMesh; + } + /** * Animate hook. * From 1af40ee89050a780ddfb440a819160728254dff4 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Thu, 7 Aug 2025 16:58:19 +0000 Subject: [PATCH 05/58] #31 - Extracting hangar quarters mesh and materials setup to separate functions --- .../scenograph/objects/structures/hangar.js | 97 +++++++++++-------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index a3cba3ce..e46a5e24 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -62,31 +62,10 @@ export default class Hangar { async load() { - /** - * Setup Hangar materials - */ - - this.materials.clear = new THREE.MeshBasicMaterial( { color: 0xffFFFF, transparent: true, visible: false, side: THREE.DoubleSide } ); - this.materials.metal = proceduralBuilding( { - uniforms: { - time: { value: 0.0 }, - scale: { value: .05 }, // Scale - lacunarity: { value: 2.0 }, // Lacunarity - randomness: { value: 1.0 }, // Randomness - emitColour1: { value: new THREE.Vector4( 0.0, 0.0, 0.0, 0.25 ) }, // Emission gradient colour 1 - emitColour2: { value: new THREE.Vector4( 0.158, 1., 1., .9 ) }, // Emission gradient colour 2 - shadowFactor: { value: 0.03 }, - shadowOffset: { value: 0.1 }, - } - } ); - this.materials.metal.transparent = true; - this.materials.metal.side = THREE.DoubleSide; - // Clone of the material to customize scaling. - this.materials.hangar = this.materials.metal.clone(); - this.materials.hangar.uniforms.scale.value = 0.25; + // Setup materials. + await this.loadMaterials(); let hangarMesh = await this.loadHangarMesh(); - /** * Corridor mesh @@ -103,28 +82,10 @@ export default class Hangar { csgEvaluator.evaluate( corridorMesh, hangarMesh, ADDITION, result ); result.name = 'outer'; - let innerGeo = new THREE.BoxGeometry( this.quartersSize.width, this.quartersSize.height, this.quartersSize.depth, 2, 2, 2 ); - /** * Player quarters mesh. */ - let quartersMesh = new Brush( innerGeo, this.materials.metal ); - window.quarters = quartersMesh; - quartersMesh.material.uniforms.scale.value = 0.8; - // Offset player quarters by the hangars half width - quartersMesh.position.x = ( - ( this.hangarSize.width * this.size ) / 2 ); - // Offset player quarters by the access corridor - quartersMesh.position.x += - this.corridorSize.depth * 0.25; - // Offset player quarters by half the quarters width - quartersMesh.position.x += ( - ( this.quartersSize.width * this.size ) / 2 ); - // Offset for intersection - quartersMesh.position.x += 1; - - quartersMesh.position.y = ( - ( this.quartersSize.height * this.size ) / 2 ); - - quartersMesh.scale.setScalar( this.size ); - quartersMesh.updateMatrixWorld(); - quartersMesh.name = 'inner'; + let quartersMesh = await this.loadQuartersMesh(); // @todo: #31 - Implement hierarchical operations // csgEvaluator.evaluate( result, innerMesh2, ADDITION, result ); @@ -137,6 +98,30 @@ export default class Hangar { } + /** + * Setup Hangar materials + */ + async loadMaterials() { + this.materials.clear = new THREE.MeshBasicMaterial( { color: 0xffFFFF, transparent: true, visible: false, side: THREE.DoubleSide } ); + this.materials.metal = proceduralBuilding( { + uniforms: { + time: { value: 0.0 }, + scale: { value: .05 }, // Scale + lacunarity: { value: 2.0 }, // Lacunarity + randomness: { value: 1.0 }, // Randomness + emitColour1: { value: new THREE.Vector4( 0.0, 0.0, 0.0, 0.25 ) }, // Emission gradient colour 1 + emitColour2: { value: new THREE.Vector4( 0.158, 1., 1., .9 ) }, // Emission gradient colour 2 + shadowFactor: { value: 0.03 }, + shadowOffset: { value: 0.1 }, + } + } ); + this.materials.metal.transparent = true; + this.materials.metal.side = THREE.DoubleSide; + // Clone of the material to customize scaling. + this.materials.hangar = this.materials.metal.clone(); + this.materials.hangar.uniforms.scale.value = 0.25; + } + /** * Creates the Hangar's main mesh. */ @@ -180,6 +165,34 @@ export default class Hangar { return corridorMesh; } + /** + * Creates the player quarters mesh. + */ + async loadQuartersMesh() { + let innerGeo = new THREE.BoxGeometry( this.quartersSize.width, this.quartersSize.height, this.quartersSize.depth, 2, 2, 2 ); + let quartersMesh = new Brush( innerGeo, this.materials.metal ); + + window.quarters = quartersMesh; + + quartersMesh.material.uniforms.scale.value = 0.8; + // Offset player quarters by the hangars half width + quartersMesh.position.x = ( - ( this.hangarSize.width * this.size ) / 2 ); + // Offset player quarters by the access corridor + quartersMesh.position.x += - this.corridorSize.depth * 0.25; + // Offset player quarters by half the quarters width + quartersMesh.position.x += ( - ( this.quartersSize.width * this.size ) / 2 ); + // Offset for intersection + quartersMesh.position.x += 1; + + quartersMesh.position.y = ( - ( this.quartersSize.height * this.size ) / 2 ); + + quartersMesh.scale.setScalar( this.size ); + quartersMesh.updateMatrixWorld(); + quartersMesh.name = 'inner'; + + return quartersMesh; + } + /** * Animate hook. * From e86f40e18b4d34a56d56268b5322333451f0a397 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Thu, 7 Aug 2025 17:24:08 +0000 Subject: [PATCH 06/58] #31 - Switching to using evaluateHierarchy() --- .../scenograph/objects/structures/hangar.js | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index e46a5e24..4b1aa753 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -6,7 +6,7 @@ * Vendor libs */ import * as THREE from 'three'; -import { ADDITION, HOLLOW_SUBTRACTION, Brush, Evaluator } from 'three-bvh-csg'; +import { ADDITION, HOLLOW_SUBTRACTION, Operation, Evaluator } from 'three-bvh-csg'; /** * Internal libs and helpers. @@ -16,6 +16,9 @@ import { proceduralBuilding, proceduralMetalMaterial2 } from '@/scenograph/mater export default class Hangar { + // The hangar mesh and root of the CSG BVH hierarchy. + hangar; + // Dimensions of the hangar // @todo: Dynamic hangars with different dimensions hangarSize; @@ -34,13 +37,16 @@ export default class Hangar { constructor() { + this.size = 5; + this.materials = {}; + let corridorScale = 0.125; // Based on office door dimensions. this.corridorSize = { - width: 8.2, - height: 20.4, - depth: 20, + width: 8.2 * corridorScale, + height: 20.4 * corridorScale, + depth: 20 * corridorScale, } this.hangarSize = { @@ -56,7 +62,7 @@ export default class Hangar { }; this.ready = false; - this.size = 5; + } @@ -65,22 +71,21 @@ export default class Hangar { // Setup materials. await this.loadMaterials(); - let hangarMesh = await this.loadHangarMesh(); + this.hangar = await this.loadHangarMesh(); /** * Corridor mesh */ let corridorMesh = await this.loadCorridorMesh(); - - // Setup the result mesh. - let result = new THREE.Mesh( new THREE.BufferGeometry(), new THREE.MeshBasicMaterial() ); + corridorMesh.operation = ADDITION; + this.hangar.add( corridorMesh ); // Constructive Solid Geometry (csg) Evaluator. let csgEvaluator; csgEvaluator = new Evaluator(); csgEvaluator.useGroups = true; - csgEvaluator.evaluate( corridorMesh, hangarMesh, ADDITION, result ); - result.name = 'outer'; + let result = csgEvaluator.evaluateHierarchy( this.hangar ); + // result.name = 'outer'; /** * Player quarters mesh. @@ -129,7 +134,7 @@ export default class Hangar { /** * Hangar mesh */ - let hangarMesh = new Brush( new THREE.BoxGeometry( this.hangarSize.width, this.hangarSize.height, this.hangarSize.depth, 2, 2, 2 ), [ + let hangarMesh = new Operation( new THREE.BoxGeometry( this.hangarSize.width, this.hangarSize.height, this.hangarSize.depth, 2, 2, 2 ), [ this.materials.hangar, this.materials.hangar, this.materials.hangar, @@ -152,14 +157,12 @@ export default class Hangar { var doorGeometry = new THREE.BoxGeometry( this.corridorSize.width, this.corridorSize.height, this.corridorSize.depth ); - let corridorMesh = new Brush( doorGeometry, this.materials.metal ); + let corridorMesh = new Operation( doorGeometry, this.materials.metal ); - corridorMesh.position.x = - ( this.hangarSize.width * this.size ) / 2; - corridorMesh.position.y = ( - ( this.hangarSize.height * this.size ) / 2 ) + this.corridorSize.height * 0.25; + corridorMesh.position.x = - ( this.hangarSize.width * this.size ) / 10; + corridorMesh.position.y = ( - ( this.hangarSize.height * this.size ) / 10 ) + this.corridorSize.height * 0.5; corridorMesh.rotation.y = Math.PI / 2; - corridorMesh.scale.setScalar( this.size / 10 ); - corridorMesh.updateMatrixWorld(); return corridorMesh; @@ -170,7 +173,7 @@ export default class Hangar { */ async loadQuartersMesh() { let innerGeo = new THREE.BoxGeometry( this.quartersSize.width, this.quartersSize.height, this.quartersSize.depth, 2, 2, 2 ); - let quartersMesh = new Brush( innerGeo, this.materials.metal ); + let quartersMesh = new Operation( innerGeo, this.materials.metal ); window.quarters = quartersMesh; From 39cf2f84eaebba6893644114c0763280b8f0bb24 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Thu, 7 Aug 2025 17:28:04 +0000 Subject: [PATCH 07/58] #31 - Adding player quarters to hangar --- .../scenograph/objects/structures/hangar.js | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index 4b1aa753..2ccdea0f 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -80,24 +80,21 @@ export default class Hangar { corridorMesh.operation = ADDITION; this.hangar.add( corridorMesh ); - // Constructive Solid Geometry (csg) Evaluator. - let csgEvaluator; - csgEvaluator = new Evaluator(); - csgEvaluator.useGroups = true; - let result = csgEvaluator.evaluateHierarchy( this.hangar ); - // result.name = 'outer'; - /** * Player quarters mesh. */ let quartersMesh = await this.loadQuartersMesh(); + quartersMesh.operation = ADDITION; + this.hangar.add( quartersMesh ); - // @todo: #31 - Implement hierarchical operations - // csgEvaluator.evaluate( result, innerMesh2, ADDITION, result ); + // Constructive Solid Geometry (csg) Evaluator. + let csgEvaluator; + csgEvaluator = new Evaluator(); + csgEvaluator.useGroups = true; + let result = csgEvaluator.evaluateHierarchy( this.hangar ); this.mesh = new THREE.Object3D(); this.mesh.add( result ); - // this.mesh.add( innerMesh2 ); this.mesh.userData.targetable = false; this.mesh.userData.objectClass = 'hangar'; @@ -179,17 +176,16 @@ export default class Hangar { quartersMesh.material.uniforms.scale.value = 0.8; // Offset player quarters by the hangars half width - quartersMesh.position.x = ( - ( this.hangarSize.width * this.size ) / 2 ); + quartersMesh.position.x = ( - ( this.hangarSize.width * this.size ) / 10 ); // Offset player quarters by the access corridor - quartersMesh.position.x += - this.corridorSize.depth * 0.25; + quartersMesh.position.x += - this.corridorSize.depth * 0.75; // Offset player quarters by half the quarters width - quartersMesh.position.x += ( - ( this.quartersSize.width * this.size ) / 2 ); + quartersMesh.position.x += ( - ( this.quartersSize.width * this.size ) / 10 ); // Offset for intersection quartersMesh.position.x += 1; - quartersMesh.position.y = ( - ( this.quartersSize.height * this.size ) / 2 ); + quartersMesh.position.y = ( - ( this.quartersSize.height * this.size ) / 10 ); - quartersMesh.scale.setScalar( this.size ); quartersMesh.updateMatrixWorld(); quartersMesh.name = 'inner'; From 4280695a74dac6754dea9f72a2400c783516c274 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Thu, 7 Aug 2025 17:31:58 +0000 Subject: [PATCH 08/58] #31 - Tweaking hangar scale --- client/src/app/scenograph/objects/structures/hangar.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index 2ccdea0f..f421d6b2 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -97,6 +97,7 @@ export default class Hangar { this.mesh.add( result ); this.mesh.userData.targetable = false; this.mesh.userData.objectClass = 'hangar'; + this.mesh.scale.setScalar( 2.5 ); } @@ -140,8 +141,6 @@ export default class Hangar { this.materials.clear.clone() ] ); - hangarMesh.scale.setScalar( this.size ); - hangarMesh.updateMatrixWorld(); return hangarMesh; From a478c19b02d76a07c82e1f24bbcfe0cf61b27536 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 8 Aug 2025 16:50:55 +0000 Subject: [PATCH 09/58] #31 - Tweaking hangar layout --- client/src/app/scenograph/objects/structures/hangar.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index f421d6b2..b7dbfa3a 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -1,5 +1,5 @@ /** - * Union Extractor + * Hangar (Player quarters) */ /** @@ -109,7 +109,7 @@ export default class Hangar { this.materials.metal = proceduralBuilding( { uniforms: { time: { value: 0.0 }, - scale: { value: .05 }, // Scale + scale: { value: .025 }, // Scale lacunarity: { value: 2.0 }, // Lacunarity randomness: { value: 1.0 }, // Randomness emitColour1: { value: new THREE.Vector4( 0.0, 0.0, 0.0, 0.25 ) }, // Emission gradient colour 1 @@ -159,6 +159,8 @@ export default class Hangar { corridorMesh.position.y = ( - ( this.hangarSize.height * this.size ) / 10 ) + this.corridorSize.height * 0.5; corridorMesh.rotation.y = Math.PI / 2; + corridorMesh.position.z = ( - ( this.hangarSize.depth * this.size ) / 10 ) / 4; + corridorMesh.updateMatrixWorld(); return corridorMesh; @@ -185,6 +187,8 @@ export default class Hangar { quartersMesh.position.y = ( - ( this.quartersSize.height * this.size ) / 10 ); + quartersMesh.position.z = ( - ( this.hangarSize.depth * this.size ) / 10 ) / 2; + quartersMesh.updateMatrixWorld(); quartersMesh.name = 'inner'; @@ -203,7 +207,7 @@ export default class Hangar { * @note All references within this method should be globally accessible. **/ animate( currentTime ) { - + l.current_scene.objects.hangar.materials.metal.uniforms.time.value += 0.0000025; } } From 615923804684971233b2c6e55d25fec12965a159 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sat, 9 Aug 2025 17:40:12 +0000 Subject: [PATCH 10/58] #31 - Setting up routes class and moving single player init logic --- client/src/app/main.js | 8 +++++- client/src/app/routes.js | 25 +++++++++++++++++ client/src/app/routes/singleplayer.js | 39 +++++++++++++++++++++++++++ client/src/app/ui/menus/main_menu.js | 13 ++------- 4 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 client/src/app/routes.js create mode 100644 client/src/app/routes/singleplayer.js diff --git a/client/src/app/main.js b/client/src/app/main.js index 9c3053e1..2d5ddd6b 100644 --- a/client/src/app/main.js +++ b/client/src/app/main.js @@ -3,6 +3,7 @@ */ import l from '@/helpers/l.js'; import Config from "@/config.js"; +import Routes from "@/routes.js"; import Scenograph from "@/scenograph.js"; import UI from "@/ui.js"; @@ -12,7 +13,12 @@ import UI from "@/ui.js"; l.config = new Config(); /** - * Scenograph controls the current scene + * Routes activate game modes and screens. + */ +l.routes = new Routes(); + +/** + * Scenograph controls the current 3D scene */ l.scenograph = new Scenograph(); diff --git a/client/src/app/routes.js b/client/src/app/routes.js new file mode 100644 index 00000000..93f78099 --- /dev/null +++ b/client/src/app/routes.js @@ -0,0 +1,25 @@ +/** + * @name Routes + * @description Provides an interface to load and unload game modes and screens. + * @namespace l.routes + * @memberof l + * @global + */ + +/** + * Internal libs and helpers. + */ +import l from '@/helpers/l.js'; + +import singlePlayerRoute from '@/routes/singleplayer.js'; + + +export default class routes { + + singlePlayer; + + constructor() { + this.singlePlayer = new singlePlayerRoute(); + } + +} diff --git a/client/src/app/routes/singleplayer.js b/client/src/app/routes/singleplayer.js new file mode 100644 index 00000000..051b3a99 --- /dev/null +++ b/client/src/app/routes/singleplayer.js @@ -0,0 +1,39 @@ +/** + * @name Single Player + * @description Managees the game client's single player mode. + * @namespace l.routes.singleplayer + * @memberof l.routes + * @global + */ + +/** + * Internal libs and helpers. + */ +import l from '@/helpers/l.js'; + +export default class singlePlayerRoute { + + /** + * Start single player mode. + */ + start() { + console.log( 'Single player launched' ); + + // Start controls. + l.scenograph.controls.activate(); + + // Start overlays. + l.scenograph.overlays.activate(); + + // Set client mode. + l.mode = 'single_player'; + } + + /** + * Stop single player mode. + */ + stop() { + + } + +} diff --git a/client/src/app/ui/menus/main_menu.js b/client/src/app/ui/menus/main_menu.js index 70bd95d8..d70a03de 100644 --- a/client/src/app/ui/menus/main_menu.js +++ b/client/src/app/ui/menus/main_menu.js @@ -74,15 +74,7 @@ export default class Main_Menu { title: 'Single Player', } ); this.buttons.single_player.on( 'click', () => { - console.log( 'Single player launched' ); - - // Start controls. - l.scenograph.controls.activate(); - - // Start overlays. - l.scenograph.overlays.activate(); - - //l.ui.show_flight_instruments(); + l.routes.singlePlayer.start(); // Hide game mode buttons. this.buttons.single_player.hidden = true; @@ -98,8 +90,7 @@ export default class Main_Menu { // Show game scores button this.buttons.scores.hidden = false; - // Set client mode. - l.mode = 'single_player'; + } ); this.buttons.multi_player = this.pane.addButton( { From 894855ffa9aca314beb2bc6b67ce208dd1629333 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sat, 9 Aug 2025 20:13:05 +0000 Subject: [PATCH 11/58] #31 - Adding wip hangar route and menu button, tidying up routes --- client/src/app/routes.js | 7 ++++-- client/src/app/routes/hangar.js | 26 +++++++++++++++++++ client/src/app/routes/multiplayer.js | 34 +++++++++++++++++++++++++ client/src/app/routes/singleplayer.js | 12 +-------- client/src/app/ui/menus/main_menu.js | 36 ++++++++++++++++++--------- 5 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 client/src/app/routes/hangar.js create mode 100644 client/src/app/routes/multiplayer.js diff --git a/client/src/app/routes.js b/client/src/app/routes.js index 93f78099..05d35ce4 100644 --- a/client/src/app/routes.js +++ b/client/src/app/routes.js @@ -11,15 +11,18 @@ */ import l from '@/helpers/l.js'; +import hangarRoute from '@/routes/hangar.js'; +import multiPlayerRoute from '@/routes/multiplayer.js'; import singlePlayerRoute from '@/routes/singleplayer.js'; - export default class routes { singlePlayer; constructor() { - this.singlePlayer = new singlePlayerRoute(); + this.hangar = hangarRoute; + this.multiPlayer = multiPlayerRoute; + this.singlePlayer = singlePlayerRoute; } } diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js new file mode 100644 index 00000000..7bcfec18 --- /dev/null +++ b/client/src/app/routes/hangar.js @@ -0,0 +1,26 @@ +/** + * @name Hangar scene + * @description Displays a player hangar + * @namespace l.routes.hangar + * @memberof l.routes + * @global + */ + +/** + * Internal libs and helpers. + */ +import l from '@/helpers/l.js'; + +export default class hangarRoute { + + constructor() { + console.log( 'Hangar launched' ); + + // Start controls. + l.scenograph.controls.activate(); + + // Set client mode. + l.mode = 'hangar'; + } + +} diff --git a/client/src/app/routes/multiplayer.js b/client/src/app/routes/multiplayer.js new file mode 100644 index 00000000..e0e76d27 --- /dev/null +++ b/client/src/app/routes/multiplayer.js @@ -0,0 +1,34 @@ +/** + * @name Multi Player + * @description Managees the game client's multi player mode. + * @namespace l.routes.multiplayer + * @memberof l.routes + * @global + */ + +/** + * Internal libs and helpers. + */ +import l from '@/helpers/l.js'; + +export default class multiPlayerRoute { + + constructor() { + console.log( 'Multi player launched' ); + + // Start controls. + l.scenograph.controls.activate(); + + // Start overlays. + l.scenograph.overlays.activate(); + + let serverLocation = l.env == 'Dev' ? 'lcl.langenium.com:8090' : 'test.langenium.com:42069'; + + l.scenograph.modes.multiplayer.connect( '//' + serverLocation ); + + // Set client mode. + l.mode = 'multi_player'; + + } + +} diff --git a/client/src/app/routes/singleplayer.js b/client/src/app/routes/singleplayer.js index 051b3a99..28d158a8 100644 --- a/client/src/app/routes/singleplayer.js +++ b/client/src/app/routes/singleplayer.js @@ -13,10 +13,7 @@ import l from '@/helpers/l.js'; export default class singlePlayerRoute { - /** - * Start single player mode. - */ - start() { + constructor() { console.log( 'Single player launched' ); // Start controls. @@ -29,11 +26,4 @@ export default class singlePlayerRoute { l.mode = 'single_player'; } - /** - * Stop single player mode. - */ - stop() { - - } - } diff --git a/client/src/app/ui/menus/main_menu.js b/client/src/app/ui/menus/main_menu.js index d70a03de..2b2ab675 100644 --- a/client/src/app/ui/menus/main_menu.js +++ b/client/src/app/ui/menus/main_menu.js @@ -70,11 +70,33 @@ export default class Main_Menu { l.ui.score_table.show(); }); + this.buttons.test = this.pane.addButton( { + title: 'Test - Hangar', + } ); + this.buttons.test.on( 'click', () => { + new l.routes.hangar(); + + // Hide game mode buttons. + this.buttons.single_player.hidden = true; + this.buttons.multi_player.hidden = true; + + // Hide main menu and change it's title + this.pane.expanded = false; + this.pane.title = "Menu"; + + // Show game exit button to return to main menu. + this.buttons.exit_game.hidden = false; + + // Show game scores button + this.buttons.scores.hidden = false; + + } ); + this.buttons.single_player = this.pane.addButton( { title: 'Single Player', } ); this.buttons.single_player.on( 'click', () => { - l.routes.singlePlayer.start(); + new l.routes.singlePlayer(); // Hide game mode buttons. this.buttons.single_player.hidden = true; @@ -89,7 +111,6 @@ export default class Main_Menu { // Show game scores button this.buttons.scores.hidden = false; - } ); @@ -98,11 +119,7 @@ export default class Main_Menu { disabled: true // @todo: v7 Restore multiplayer and server tracking of scene objects. } ); this.buttons.multi_player.on( 'click', () => { - console.log( 'Multi player launched' ); - - l.scenograph.controls.activate(); - - //l.ui.show_flight_instruments(); + new l.routes.multiPlayer(); // Hide game mode buttons. this.buttons.single_player.hidden = true; @@ -118,12 +135,7 @@ export default class Main_Menu { // Show game scores button this.buttons.scores.hidden = false; - let serverLocation = l.env == 'Dev' ? 'lcl.langenium.com:8090' : 'test.langenium.com:42069'; - l.scenograph.modes.multiplayer.connect( '//' + serverLocation ); - - // Set client mode. - l.mode = 'multi_player'; } ); this.buttons.settings = this.pane.addButton( { From 4bf95148d99c447d72de0783f14a1b5900d0be96 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 10 Aug 2025 20:55:19 +0000 Subject: [PATCH 12/58] #31 - Implementing hangar mode switch and hiding union platform --- client/src/app/routes/hangar.js | 15 +++++++++++++++ client/src/app/scenograph/scenes/overworld.js | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index 7bcfec18..9ea7d55a 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -19,8 +19,23 @@ export default class hangarRoute { // Start controls. l.scenograph.controls.activate(); + // Start overlays. + // @todo: #31 Fix the need for this, only included so that damage calcs can trigger their overlay updates + l.scenograph.overlays.activate(); + // Set client mode. l.mode = 'hangar'; + + l.current_scene.objects.platform.mesh.visible = false; + l.current_scene.objects.hangar.mesh.visible = true; + + l.current_scene.objects.hangar.mesh.position.y = 15; + + // @todo #31 + // - Update player and hangar position to platform + // - Implement first person controls + // - Separate player from aircraft + } } diff --git a/client/src/app/scenograph/scenes/overworld.js b/client/src/app/scenograph/scenes/overworld.js index 99a985bd..af9b71a0 100644 --- a/client/src/app/scenograph/scenes/overworld.js +++ b/client/src/app/scenograph/scenes/overworld.js @@ -177,8 +177,8 @@ export default class Overworld extends SceneBase { // @todo: #31 Implement first person mode and a way to go between being in the hangar and being in the aircraft l.current_scene.objects.hangar = new Hangar(); await l.current_scene.objects.hangar.load(); - l.current_scene.objects.hangar.mesh.position.y = 15; - // l.current_scene.objects.hangar.mesh.position.x = 0; + // Hide the hangar so we can load it when needed. + l.current_scene.objects.hangar.mesh.visible = false; l.current_scene.scene.add( l.current_scene.objects.hangar.mesh ); From 5cac20adca79fe2a246fddc8d066b08cfa6390f0 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 11 Aug 2025 05:35:22 +0000 Subject: [PATCH 13/58] #31 - Update player and hangar position to platform --- client/src/app/routes/hangar.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index 9ea7d55a..82883941 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -29,10 +29,21 @@ export default class hangarRoute { l.current_scene.objects.platform.mesh.visible = false; l.current_scene.objects.hangar.mesh.visible = true; - l.current_scene.objects.hangar.mesh.position.y = 15; + l.current_scene.objects.hangar.mesh.position.copy( l.current_scene.objects.platform.mesh.position ); + l.current_scene.objects.hangar.mesh.position.y = 505; + + l.current_scene.objects.player.position.x = l.current_scene.objects.platform.mesh.position.x; + l.current_scene.objects.player.position.z = l.current_scene.objects.platform.mesh.position.z; + l.current_scene.objects.player.position.y = 500; + + + l.scenograph.controls.orbitTarget.copy(l.current_scene.objects.player.position); + l.scenograph.cameras.active.position.copy(l.current_scene.objects.player.position); + l.scenograph.cameras.active.translateZ(-15); + l.scenograph.cameras.orbit.updateProjectionMatrix(); + l.scenograph.controls.orbit.update(); // @todo #31 - // - Update player and hangar position to platform // - Implement first person controls // - Separate player from aircraft From c3ec8f4e4c08dee85b94e498cb0f6aec9489d054 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 11 Aug 2025 18:47:13 +0000 Subject: [PATCH 14/58] #31 - Moving trail animate --- .../scenograph/objects/vehicles/valiant.js | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/client/src/app/scenograph/objects/vehicles/valiant.js b/client/src/app/scenograph/objects/vehicles/valiant.js index c735e83d..8edbfeb0 100644 --- a/client/src/app/scenograph/objects/vehicles/valiant.js +++ b/client/src/app/scenograph/objects/vehicles/valiant.js @@ -457,49 +457,53 @@ export default class Valiant extends ValiantBase { l.scenograph.cameras.updatePlayer( rY, tY, tZ ); - if ( l.current_scene.objects.player.trail ) { + l.current_scene.objects.player.animateTrail( rY ); - // Fix the trail being too far behind. - let trailOffset = 0; + + } + } - // Only offset the trail effect if we are going forward which is (z-1) in numerical terms - if ( l.current_scene.objects.player.airSpeed < 0 ) { + animateTrail( rY ) { + if ( l.current_scene.objects.player.trail ) { - // Update ship thruster - l.current_scene.objects.player.animateThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.centralConeBurner, .5 ); - l.current_scene.objects.player.animateThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.outerCylBurner, .5 ); + // Fix the trail being too far behind. + let trailOffset = 0; - l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.rearConeBurner, -1 ); - l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.centralConeBurner, 1 ); - l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.outerCylBurner, -1 ); - l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.innerCylBurner, 1 ); + // Only offset the trail effect if we are going forward which is (z-1) in numerical terms + if ( l.current_scene.objects.player.airSpeed < 0 ) { - // Limit playback rate to 5x as large values freak out the browser. - l.current_scene.objects.player.thruster.videoElement.playbackRate = Math.min( 5, 0.25 + Math.abs( l.current_scene.objects.player.airSpeed ) ); + // Update ship thruster + l.current_scene.objects.player.animateThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.centralConeBurner, .5 ); + l.current_scene.objects.player.animateThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.outerCylBurner, .5 ); - trailOffset += l.current_scene.objects.player.trail_position_z - Math.abs( l.current_scene.objects.player.airSpeed ); + l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.rearConeBurner, -1 ); + l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.centralConeBurner, 1 ); + l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.outerCylBurner, -1 ); + l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.innerCylBurner, 1 ); - l.current_scene.objects.player.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, .8 ); // RGBA. - } - else { - l.current_scene.objects.player.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, 0 ); // RGBA. - } + // Limit playback rate to 5x as large values freak out the browser. + l.current_scene.objects.player.thruster.videoElement.playbackRate = Math.min( 5, 0.25 + Math.abs( l.current_scene.objects.player.airSpeed ) ); - // Update the trail position based on above calculations. - l.current_scene.objects.player.trail.targetObject.position.y = l.current_scene.objects.player.trail_position_y + l.current_scene.objects.player.verticalSpeed; - l.current_scene.objects.player.trail.targetObject.position.z = trailOffset; + trailOffset += l.current_scene.objects.player.trail_position_z - Math.abs( l.current_scene.objects.player.airSpeed ); - if ( rY != 0 ) { - l.current_scene.objects.player.trail.targetObject.position.x = rY * l.current_scene.objects.player.airSpeed; - l.current_scene.objects.player.trail.targetObject.position.y += Math.abs( l.current_scene.objects.player.trail.targetObject.position.x ) / 4; - } - else { - l.current_scene.objects.player.trail.targetObject.position.x = 0; - } - l.current_scene.objects.player.trail.update(); + l.current_scene.objects.player.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, .8 ); // RGBA. + } + else { + l.current_scene.objects.player.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, 0 ); // RGBA. } - + // Update the trail position based on above calculations. + l.current_scene.objects.player.trail.targetObject.position.y = l.current_scene.objects.player.trail_position_y + l.current_scene.objects.player.verticalSpeed; + l.current_scene.objects.player.trail.targetObject.position.z = trailOffset; + + if ( rY != 0 ) { + l.current_scene.objects.player.trail.targetObject.position.x = rY * l.current_scene.objects.player.airSpeed; + l.current_scene.objects.player.trail.targetObject.position.y += Math.abs( l.current_scene.objects.player.trail.targetObject.position.x ) / 4; + } + else { + l.current_scene.objects.player.trail.targetObject.position.x = 0; + } + l.current_scene.objects.player.trail.update(); } } From 9695c6272c0c5328a2e5c7120dabce0512719da6 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 11 Aug 2025 20:33:34 +0000 Subject: [PATCH 15/58] #31 - Implementing first person controls within the game client --- client/src/app/routes/hangar.js | 28 ++- client/src/app/routes/singleplayer.js | 3 + client/src/app/scenograph.js | 8 + client/src/app/scenograph/actors.js | 23 +++ client/src/app/scenograph/actors/player.js | 52 +++++ client/src/app/scenograph/cameras.js | 56 ------ client/src/app/scenograph/controls.js | 2 +- .../app/scenograph/controls/touch/weapons.js | 6 +- .../src/app/scenograph/modes/multiplayer.js | 24 +-- .../app/scenograph/objects/vehicles/person.js | 186 ++++++++++++++++++ .../scenograph/objects/vehicles/valiant.js | 180 +++++++++++------ .../scenograph/overlays/heads-up-display.js | 8 +- client/src/app/scenograph/overlays/map.js | 10 +- .../src/app/scenograph/overlays/scanners.js | 2 +- client/src/app/scenograph/scenes/overworld.js | 20 +- client/src/app/scenograph/tweens.js | 4 +- client/src/app/ui/flight_instruments.js | 4 +- client/src/app/ui/menus/debugging_tools.js | 8 +- client/src/app/ui/targeting/list.js | 4 +- client/src/app/ui/targeting/locked.js | 2 +- game/src/actors/pirate.ts | 4 +- game/src/objects/person.ts | 184 +++++++++++++++++ 22 files changed, 649 insertions(+), 169 deletions(-) create mode 100644 client/src/app/scenograph/actors.js create mode 100644 client/src/app/scenograph/actors/player.js create mode 100644 client/src/app/scenograph/objects/vehicles/person.js create mode 100644 game/src/objects/person.ts diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index 82883941..07f5a627 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -31,22 +31,34 @@ export default class hangarRoute { l.current_scene.objects.hangar.mesh.position.copy( l.current_scene.objects.platform.mesh.position ); l.current_scene.objects.hangar.mesh.position.y = 505; + + l.scenograph.actors.player.vehicle.position.x = l.current_scene.objects.platform.mesh.position.x; + l.scenograph.actors.player.vehicle.position.z = l.current_scene.objects.platform.mesh.position.z; + l.scenograph.actors.player.vehicle.position.y = 500; + + l.scenograph.actors.player.person.position.x = l.current_scene.objects.platform.mesh.position.x; + l.scenograph.actors.player.person.position.z = l.current_scene.objects.platform.mesh.position.z - 5; + l.scenograph.actors.player.person.position.y = 495; - l.current_scene.objects.player.position.x = l.current_scene.objects.platform.mesh.position.x; - l.current_scene.objects.player.position.z = l.current_scene.objects.platform.mesh.position.z; - l.current_scene.objects.player.position.y = 500; + l.scenograph.cameras.active.position.copy(l.scenograph.actors.player.vehicle.position); + l.scenograph.cameras.active.translateZ(15); + l.scenograph.cameras.active.translateY(7.5); - l.scenograph.controls.orbitTarget.copy(l.current_scene.objects.player.position); - l.scenograph.cameras.active.position.copy(l.current_scene.objects.player.position); - l.scenograph.cameras.active.translateZ(-15); - l.scenograph.cameras.orbit.updateProjectionMatrix(); - l.scenograph.controls.orbit.update(); + if ( l.scenograph.controls.orbit ) { + l.scenograph.cameras.orbit.updateProjectionMatrix(); + l.scenograph.controls.orbit.update(); + l.scenograph.controls.orbitTarget.x = l.scenograph.actors.player.vehicle.position.x; + l.scenograph.controls.orbitTarget.y = l.scenograph.actors.player.vehicle.position.y; + l.scenograph.controls.orbitTarget.z = l.scenograph.actors.player.vehicle.position.z; + } // @todo #31 // - Implement first person controls // - Separate player from aircraft + l.scenograph.actors.player.setMode('person'); + } } diff --git a/client/src/app/routes/singleplayer.js b/client/src/app/routes/singleplayer.js index 28d158a8..e2d7b985 100644 --- a/client/src/app/routes/singleplayer.js +++ b/client/src/app/routes/singleplayer.js @@ -24,6 +24,9 @@ export default class singlePlayerRoute { // Set client mode. l.mode = 'single_player'; + + l.scenograph.actors.player.setMode('vehicle'); + } } diff --git a/client/src/app/scenograph.js b/client/src/app/scenograph.js index a7090dd1..41a414f2 100644 --- a/client/src/app/scenograph.js +++ b/client/src/app/scenograph.js @@ -20,6 +20,7 @@ import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; import l from "@/helpers/l.js"; import { calculateAdjustedGapSize } from '@/helpers/math.js'; +import Actors from "@/scenograph/actors.js"; import Cameras from "@/scenograph/cameras.js"; import Controls from "@/scenograph/controls.js"; import Effects from "@/scenograph/effects"; @@ -46,6 +47,8 @@ import { export default class Scenograph { + actors; + cameras; /** @@ -79,6 +82,11 @@ export default class Scenograph { this.modes = {}; + /** + * Cameras. + */ + this.actors = new Actors(); + /** * Cameras. */ diff --git a/client/src/app/scenograph/actors.js b/client/src/app/scenograph/actors.js new file mode 100644 index 00000000..414afb86 --- /dev/null +++ b/client/src/app/scenograph/actors.js @@ -0,0 +1,23 @@ +/** + * @name Actors + * @description Provides an interface to manage actors. + * @namespace l.scenograph.actors + * @memberof l.scenograph + * @global + */ + +/** + * Internal libs and helpers. + */ +import l from '@/helpers/l.js'; +import Player from '@/scenograph/actors/player.js'; + +export default class Actors { + + player; + + constructor() { + this.player = new Player(); + } + +} diff --git a/client/src/app/scenograph/actors/player.js b/client/src/app/scenograph/actors/player.js new file mode 100644 index 00000000..968e8360 --- /dev/null +++ b/client/src/app/scenograph/actors/player.js @@ -0,0 +1,52 @@ +/** + * @name Player + * @description Provides an interface to the player actor in the game. + * @namespace l.scenograph.actors.player + * @memberof l.scenograph.actors + * @global + */ + +/** + * Vendor libs and base class. + */ +import * as THREE from "three"; + +/** + * Internal libs and helpers. + */ +import l from '@/helpers/l.js'; + +export default class Player { + + // Whether the player is in their vehicle or not. + mode; + + ready; + + // Character model. + person; + + // Vehicle model. + vehicle; + + constructor() { + + this.ready = false; + + this.person = false; + + this.vehicle = false; + + this.setMode(); + } + + setMode( mode = 'vehicle' ) { + if ( mode === 'vehicle' ) { + this.mode = 'vehicle'; + } + else { + this.mode = 'person'; + } + } + +} diff --git a/client/src/app/scenograph/cameras.js b/client/src/app/scenograph/cameras.js index 75230374..37914f8f 100644 --- a/client/src/app/scenograph/cameras.js +++ b/client/src/app/scenograph/cameras.js @@ -99,60 +99,4 @@ export default class Cameras { return camera; } - updatePlayer( rY, tY, tZ ) { - var radian = ( Math.PI / 180 ); - - l.current_scene.objects.player.camera_distance = l.current_scene.objects.player.default_camera_distance + ( l.current_scene.room_depth / 2 ); - if ( l.current_scene.objects.player.airSpeed < 0 ) { - l.current_scene.objects.player.camera_distance -= l.current_scene.objects.player.airSpeed * 4; - } - - let xDiff = l.current_scene.objects.player.mesh.position.x; - let zDiff = l.current_scene.objects.player.mesh.position.z; - - l.scenograph.cameras.player.position.x = xDiff + l.current_scene.objects.player.camera_distance * Math.sin( l.current_scene.objects.player.mesh.rotation.y ); - l.scenograph.cameras.player.position.z = zDiff + l.current_scene.objects.player.camera_distance * Math.cos( l.current_scene.objects.player.mesh.rotation.y ); - - if ( rY != 0 ) { - - l.scenograph.cameras.player.rotation.y += rY; - } - else { - // Check there is y difference and the rotation pad isn't being pressed. - if ( - l.scenograph.cameras.player.rotation.y != l.current_scene.objects.player.mesh.rotation.y && - ( l.scenograph.controls.touch && !l.scenograph.controls.touch.controls.rotationPad.mouseDown ) - ) { - - // Get the difference in y rotation betwen the camera and ship - let yDiff = l.current_scene.objects.player.mesh.rotation.y - l.scenograph.cameras.player.rotation.y; - - // Check the y difference is larger than 1/100th of a radian - if ( - Math.abs( yDiff ) > radian / 100 - ) { - // Add 1/60th of the difference in rotation, as FPS currently capped to 60. - l.scenograph.cameras.player.rotation.y += ( l.current_scene.objects.player.mesh.rotation.y - l.scenograph.cameras.player.rotation.y ) * 1 / 60; - } - else { - l.scenograph.cameras.player.rotation.y = l.current_scene.objects.player.mesh.rotation.y; - } - - } - - } - - let xDiff2 = tZ * Math.sin( l.current_scene.objects.player.mesh.rotation.y ), - zDiff2 = tZ * Math.cos( l.current_scene.objects.player.mesh.rotation.y ); - - if ( l.current_scene.objects.player.mesh.position.y + tY >= 1 ) { - l.scenograph.cameras.player.position.y += tY; - } - - l.scenograph.cameras.player.position.x += xDiff2; - l.scenograph.cameras.player.position.z += zDiff2; - - l.scenograph.cameras.player.updateProjectionMatrix(); - } - } diff --git a/client/src/app/scenograph/controls.js b/client/src/app/scenograph/controls.js index b6d1f26c..b25b1a77 100644 --- a/client/src/app/scenograph/controls.js +++ b/client/src/app/scenograph/controls.js @@ -137,7 +137,7 @@ export default class Controls { this.orbit = false; // Reset camera y position after disengaging orbit controls. - // l.scenograph.cameras.player.position.copy( l.current_scene.objects.player.mesh.position ); + // l.scenograph.cameras.player.position.copy( l.scenograph.actors.player.vehicle.mesh.position ); // l.scenograph.cameras.player.position.y += 10.775 / 4; } } diff --git a/client/src/app/scenograph/controls/touch/weapons.js b/client/src/app/scenograph/controls/touch/weapons.js index 8701765e..3147dc6c 100644 --- a/client/src/app/scenograph/controls/touch/weapons.js +++ b/client/src/app/scenograph/controls/touch/weapons.js @@ -67,9 +67,9 @@ export default class WeaponControls { let timeRemaining = 0; - if ( parseInt(l.current_scene.stats.currentTime) < parseInt(l.current_scene.objects.player.mesh.userData.actor.weapons.last) + parseInt(l.current_scene.objects.player.mesh.userData.actor.weapons.timeout) ) { - timeRemaining = parseInt(l.current_scene.objects.player.mesh.userData.actor.weapons.timeout) - ( - parseInt(l.current_scene.stats.currentTime) - parseInt(l.current_scene.objects.player.mesh.userData.actor.weapons.last) + if ( parseInt(l.current_scene.stats.currentTime) < parseInt(l.scenograph.actors.player.vehicle.mesh.userData.actor.weapons.last) + parseInt(l.scenograph.actors.player.vehicle.mesh.userData.actor.weapons.timeout) ) { + timeRemaining = parseInt(l.scenograph.actors.player.vehicle.mesh.userData.actor.weapons.timeout) - ( + parseInt(l.current_scene.stats.currentTime) - parseInt(l.scenograph.actors.player.vehicle.mesh.userData.actor.weapons.last) ); } diff --git a/client/src/app/scenograph/modes/multiplayer.js b/client/src/app/scenograph/modes/multiplayer.js index 0adacb9b..932c0b5c 100644 --- a/client/src/app/scenograph/modes/multiplayer.js +++ b/client/src/app/scenograph/modes/multiplayer.js @@ -94,7 +94,7 @@ export default class Multiplayer { // l.current_scene.animation_queue.push( // newPlayer.animate // ); - l.current_scene.objects.players.push( newPlayer ); + l.scenograph.actors.player.vehicles.push( newPlayer ); } @@ -102,7 +102,7 @@ export default class Multiplayer { * Remove a disconnected remote player from the client session. */ async remove_player( data ) { - l.current_scene.objects.players.forEach( ( ship ) => { + l.scenograph.actors.player.vehicles.forEach( ( ship ) => { if ( ship.socket_id == data.socket_id ) { l.current_scene.scene.remove( ship.mesh ); } @@ -115,18 +115,18 @@ export default class Multiplayer { move_ship( data ) { if ( data.socket_id == l.scenograph.modes.multiplayer.socket.id ) { // Update stored ship state, don't punch out as functions aren't transmitted. - l.current_scene.objects.player.airSpeed = data.airSpeed; - l.current_scene.objects.player.altitude = data.altitude; - l.current_scene.objects.player.heading = data.heading; - l.current_scene.objects.player.horizon = data.horizon; - l.current_scene.objects.player.position.x = data.position.x; - l.current_scene.objects.player.position.y = data.position.y; - l.current_scene.objects.player.position.z = data.position.z; - l.current_scene.objects.player.rotation = data.rotation; - l.current_scene.objects.player.verticalSpeed = data.verticalSpeed; + l.scenograph.actors.player.vehicle.airSpeed = data.airSpeed; + l.scenograph.actors.player.vehicle.altitude = data.altitude; + l.scenograph.actors.player.vehicle.heading = data.heading; + l.scenograph.actors.player.vehicle.horizon = data.horizon; + l.scenograph.actors.player.vehicle.position.x = data.position.x; + l.scenograph.actors.player.vehicle.position.y = data.position.y; + l.scenograph.actors.player.vehicle.position.z = data.position.z; + l.scenograph.actors.player.vehicle.rotation = data.rotation; + l.scenograph.actors.player.vehicle.verticalSpeed = data.verticalSpeed; } else { - l.current_scene.objects.players.forEach( ( ship ) => { + l.scenograph.actors.player.vehicles.forEach( ( ship ) => { if ( ship.socket_id == data.socket_id ) { // Update stored ship state, don't punch out as functions aren't transmitted. diff --git a/client/src/app/scenograph/objects/vehicles/person.js b/client/src/app/scenograph/objects/vehicles/person.js new file mode 100644 index 00000000..467abd94 --- /dev/null +++ b/client/src/app/scenograph/objects/vehicles/person.js @@ -0,0 +1,186 @@ +/** + * Person object. + * + * Rig to be used like a vehicle when in first person mode. + * + * @todo: Add some limbs or a body model to see. + */ +import * as THREE from 'three'; + +/** + * Internal libs and helpers. + */ +import l from '@/helpers/l.js'; + +import PersonBase from '#/game/src/objects/person'; + +export default class Person extends PersonBase { + + constructor() { + super(); + this.default_camera_distance = l.scenograph.width < l.scenograph.height ? -5 : -2.5; + + this.camera_distance = 0; + + this.mesh = new THREE.Object3D(); + + + } + + // Internal helper to manage state changes to the person's character model. + updateControls() { + let mappings = { + forward : 'W', + back : 'S', + jump : ' ', + crouch : 'shift', + turnLeft : 'A', + turnRight : 'D', + } + let changing = false; + for ( const [ controlName, keyMapping ] of Object.entries( mappings ) ) { + if ( l.scenograph.controls.keyboard.pressed( keyMapping ) ) { + l.scenograph.actors.player.person.controls[ controlName ] = true; + changing = true; + } + else { + + l.scenograph.actors.player.person.controls[ controlName ] = false; + + if ( l.scenograph.controls.touch ) { + // Check if any touchpad controls are being pressed + if ( + l.scenograph.controls.touch.controls.moveUp || + l.scenograph.controls.touch.controls.moveDown || + l.scenograph.controls.touch.controls.moveForward || + l.scenograph.controls.touch.controls.moveBackward || + l.scenograph.controls.touch.controls.moveLeft || + l.scenograph.controls.touch.controls.moveRight + ) { + changing = true; + if ( l.scenograph.controls.touch.controls.moveForward ) { + l.scenograph.actors.player.person.controls.forward = true; + } + if ( l.scenograph.controls.touch.controls.moveBackward ) { + l.scenograph.actors.player.person.controls.back = true; + } + if ( l.scenograph.controls.touch.controls.moveUp ) { + l.scenograph.actors.player.person.controls.jump = true; + } + if ( l.scenograph.controls.touch.controls.moveDown ) { + l.scenograph.actors.player.person.controls.crouch = true; + } + if ( l.scenograph.controls.touch.controls.moveLeft ) { + l.scenograph.actors.player.person.controls.turnLeft = true; + } + if ( l.scenograph.controls.touch.controls.moveRight ) { + l.scenograph.actors.player.person.controls.turnRight = true; + } + } + + } + } + } + l.scenograph.actors.player.person.controls.changing = changing; + + } + + // Update the position of the aircraft to spot determined by game logic. + updateMesh() { + l.scenograph.actors.player.person.mesh.position.x = l.scenograph.actors.player.person.position.x; + l.scenograph.actors.player.person.mesh.position.y = l.scenograph.actors.player.person.position.y; + l.scenograph.actors.player.person.mesh.position.z = l.scenograph.actors.player.person.position.z; + + l.scenograph.actors.player.person.mesh.rotation.x = l.scenograph.actors.player.person.rotation.x; + l.scenograph.actors.player.person.mesh.rotation.y = l.scenograph.actors.player.person.rotation.y; + l.scenograph.actors.player.person.mesh.rotation.z = l.scenograph.actors.player.person.rotation.z; + } + + updateCamera( rY, tY, tZ ) { + var radian = ( Math.PI / 180 ); + + l.scenograph.actors.player.person.camera_distance = l.scenograph.actors.player.person.default_camera_distance + ( l.current_scene.room_depth / 2 ); + if ( l.scenograph.actors.player.person.airSpeed < 0 ) { + l.scenograph.actors.player.person.camera_distance -= l.scenograph.actors.player.person.airSpeed * 4; + } + + let xDiff = l.scenograph.actors.player.person.mesh.position.x; + let zDiff = l.scenograph.actors.player.person.mesh.position.z; + + l.scenograph.cameras.player.position.x = xDiff + l.scenograph.actors.player.person.camera_distance * Math.sin( l.scenograph.actors.player.person.mesh.rotation.y ); + l.scenograph.cameras.player.position.z = zDiff + l.scenograph.actors.player.person.camera_distance * Math.cos( l.scenograph.actors.player.person.mesh.rotation.y ); + + if ( rY != 0 ) { + + l.scenograph.cameras.player.rotation.y += rY; + } + else { + // Check there is y difference and the rotation pad isn't being pressed. + if ( + l.scenograph.cameras.player.rotation.y != l.scenograph.actors.player.person.mesh.rotation.y && + ( l.scenograph.controls.touch && !l.scenograph.controls.touch.controls.rotationPad.mouseDown ) + ) { + + // Get the difference in y rotation betwen the camera and ship + let yDiff = l.scenograph.actors.player.person.mesh.rotation.y - l.scenograph.cameras.player.rotation.y; + + // Check the y difference is larger than 1/100th of a radian + if ( + Math.abs( yDiff ) > radian / 100 + ) { + // Add 1/60th of the difference in rotation, as FPS currently capped to 60. + l.scenograph.cameras.player.rotation.y += ( l.scenograph.actors.player.person.mesh.rotation.y - l.scenograph.cameras.player.rotation.y ) * 1 / 60; + } + else { + l.scenograph.cameras.player.rotation.y = l.scenograph.actors.player.person.mesh.rotation.y; + } + + } + + } + + let xDiff2 = tZ * Math.sin( l.scenograph.actors.player.person.mesh.rotation.y ), + zDiff2 = tZ * Math.cos( l.scenograph.actors.player.person.mesh.rotation.y ); + + if ( l.scenograph.actors.player.person.mesh.position.y + tY >= 1 ) { + l.scenograph.cameras.player.position.y += tY; + } + + l.scenograph.cameras.player.position.x += xDiff2; + l.scenograph.cameras.player.position.z += zDiff2; + + l.scenograph.cameras.player.updateProjectionMatrix(); + } + + + /** + * Animate hook. + * + * This method is called within the main animation loop and + * therefore must only reference global objects or properties. + * + * @method animate + * @memberof Raven + * @global + * @note All references within this method should be globally accessible. + **/ + animate( delta ) { + if ( l.current_scene.settings.game_controls && l.scenograph.actors.player.mode == 'person' ) { + // Detect keyboard input and pass it to the ship state model. + l.scenograph.actors.player.person.updateControls(); + + // Update the persons state model. + let [ rY, tY, tZ ] = l.scenograph.actors.player.person.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); + + + // Update the persons mesh + l.scenograph.actors.player.person.updateMesh(); + + // Update the persons camera + l.scenograph.actors.player.person.updateCamera(rY, tY, tZ); + + } + } + + +} diff --git a/client/src/app/scenograph/objects/vehicles/valiant.js b/client/src/app/scenograph/objects/vehicles/valiant.js index 8edbfeb0..58f0bf47 100644 --- a/client/src/app/scenograph/objects/vehicles/valiant.js +++ b/client/src/app/scenograph/objects/vehicles/valiant.js @@ -272,7 +272,7 @@ export default class Valiant extends ValiantBase { .to( target, l.config.settings.skipintro ? 0 : 2000 ) // Move to (300, 200) in 1 second. .easing( TWEEN.Easing.Circular.Out ) // Use an easing function to make the animation smooth. .onUpdate( () => { - l.current_scene.objects.player.mesh.position.y = coords.y; + l.scenograph.actors.player.vehicle.mesh.position.y = coords.y; } ) .onComplete( () => { //console.log('ready'); @@ -290,7 +290,7 @@ export default class Valiant extends ValiantBase { // Called after tween.js updates 'coords'. // Move 'box' to the position described by 'coords' with a CSS translation. - l.current_scene.objects.player.mesh.position.z = coords.x; + l.scenograph.actors.player.vehicle.mesh.position.z = coords.x; } ) .onComplete( () => { @@ -310,11 +310,11 @@ export default class Valiant extends ValiantBase { } // Set the ship as ready. - l.current_scene.objects.player.ready = true; - l.current_scene.objects.player.camera_distance = l.current_scene.objects.player.default_camera_distance + ( l.current_scene.room_depth / 2 ); - l.current_scene.objects.player.position.x = l.current_scene.objects.player.mesh.position.x; - l.current_scene.objects.player.position.y = l.current_scene.objects.player.mesh.position.y; - l.current_scene.objects.player.position.z = l.current_scene.objects.player.mesh.position.z; + l.scenograph.actors.player.vehicle.ready = true; + l.scenograph.actors.player.vehicle.camera_distance = l.scenograph.actors.player.vehicle.default_camera_distance + ( l.current_scene.room_depth / 2 ); + l.scenograph.actors.player.vehicle.position.x = l.scenograph.actors.player.vehicle.mesh.position.x; + l.scenograph.actors.player.vehicle.position.y = l.scenograph.actors.player.vehicle.mesh.position.y; + l.scenograph.actors.player.vehicle.position.z = l.scenograph.actors.player.vehicle.mesh.position.z; } ); } @@ -331,12 +331,12 @@ export default class Valiant extends ValiantBase { let changing = false; for ( const [ controlName, keyMapping ] of Object.entries( mappings ) ) { if ( l.scenograph.controls.keyboard.pressed( keyMapping ) ) { - l.current_scene.objects.player.controls[ controlName ] = true; + l.scenograph.actors.player.vehicle.controls[ controlName ] = true; changing = true; } else { - l.current_scene.objects.player.controls[ controlName ] = false; + l.scenograph.actors.player.vehicle.controls[ controlName ] = false; if ( l.scenograph.controls.touch ) { // Check if any touchpad controls are being pressed @@ -350,54 +350,54 @@ export default class Valiant extends ValiantBase { ) { changing = true; if ( l.scenograph.controls.touch.controls.moveUp ) { - l.current_scene.objects.player.controls.moveUp = true; + l.scenograph.actors.player.vehicle.controls.moveUp = true; } if ( l.scenograph.controls.touch.controls.moveDown ) { - l.current_scene.objects.player.controls.moveDown = true; + l.scenograph.actors.player.vehicle.controls.moveDown = true; } if ( l.scenograph.controls.touch.controls.moveForward ) { - l.current_scene.objects.player.controls.throttleUp = true; + l.scenograph.actors.player.vehicle.controls.throttleUp = true; } if ( l.scenograph.controls.touch.controls.moveBackward ) { - l.current_scene.objects.player.controls.throttleDown = true; + l.scenograph.actors.player.vehicle.controls.throttleDown = true; } if ( l.scenograph.controls.touch.controls.moveLeft ) { - l.current_scene.objects.player.controls.moveLeft = true; + l.scenograph.actors.player.vehicle.controls.moveLeft = true; } if ( l.scenograph.controls.touch.controls.moveRight ) { - l.current_scene.objects.player.controls.moveRight = true; + l.scenograph.actors.player.vehicle.controls.moveRight = true; } } } } } - l.current_scene.objects.player.controls.changing = changing; + l.scenograph.actors.player.vehicle.controls.changing = changing; } updateAnimation( delta ) { - if ( l.current_scene.objects.player.mixer ) { - l.current_scene.objects.player.mixer.update( delta ); + if ( l.scenograph.actors.player.vehicle.mixer ) { + l.scenograph.actors.player.vehicle.mixer.update( delta ); } // Rock the ship forward and back when moving horizontally - if ( l.current_scene.objects.player.controls.throttleDown || l.current_scene.objects.player.controls.throttleUp ) { - let pitchChange = l.current_scene.objects.player.controls.throttleUp ? -1 : 1; - if ( Math.abs( l.current_scene.objects.player.mesh.rotation.x ) < 1 / 4 ) { - l.current_scene.objects.player.mesh.rotation.x += pitchChange / 10 / 180; + if ( l.scenograph.actors.player.vehicle.controls.throttleDown || l.scenograph.actors.player.vehicle.controls.throttleUp ) { + let pitchChange = l.scenograph.actors.player.vehicle.controls.throttleUp ? -1 : 1; + if ( Math.abs( l.scenograph.actors.player.vehicle.mesh.rotation.x ) < 1 / 4 ) { + l.scenograph.actors.player.vehicle.mesh.rotation.x += pitchChange / 10 / 180; } } // Rock the ship forward and back when moving vertically if ( - l.current_scene.objects.player.controls.moveDown + l.scenograph.actors.player.vehicle.controls.moveDown || - l.current_scene.objects.player.controls.moveUp + l.scenograph.actors.player.vehicle.controls.moveUp ) { - let elevationChange = l.current_scene.objects.player.controls.moveDown ? -1 : 1; - if ( Math.abs( l.current_scene.objects.player.mesh.rotation.x ) < 1 / 8 ) { - l.current_scene.objects.player.mesh.rotation.x += elevationChange / 10 / 180; + let elevationChange = l.scenograph.actors.player.vehicle.controls.moveDown ? -1 : 1; + if ( Math.abs( l.scenograph.actors.player.vehicle.mesh.rotation.x ) < 1 / 8 ) { + l.scenograph.actors.player.vehicle.mesh.rotation.x += elevationChange / 10 / 180; } if ( Math.abs( l.scenograph.cameras.player.rotation.x ) < 1 / 8 ) { @@ -413,15 +413,70 @@ export default class Valiant extends ValiantBase { // Update the position of the aircraft to spot determined by game logic. updateMesh() { - l.current_scene.objects.player.mesh.position.x = l.current_scene.objects.player.position.x; - l.current_scene.objects.player.mesh.position.y = l.current_scene.objects.player.position.y; - l.current_scene.objects.player.mesh.position.z = l.current_scene.objects.player.position.z; + l.scenograph.actors.player.vehicle.mesh.position.x = l.scenograph.actors.player.vehicle.position.x; + l.scenograph.actors.player.vehicle.mesh.position.y = l.scenograph.actors.player.vehicle.position.y; + l.scenograph.actors.player.vehicle.mesh.position.z = l.scenograph.actors.player.vehicle.position.z; - l.current_scene.objects.player.mesh.rotation.x = l.current_scene.objects.player.rotation.x; - l.current_scene.objects.player.mesh.rotation.y = l.current_scene.objects.player.rotation.y; - l.current_scene.objects.player.mesh.rotation.z = l.current_scene.objects.player.rotation.z; + l.scenograph.actors.player.vehicle.mesh.rotation.x = l.scenograph.actors.player.vehicle.rotation.x; + l.scenograph.actors.player.vehicle.mesh.rotation.y = l.scenograph.actors.player.vehicle.rotation.y; + l.scenograph.actors.player.vehicle.mesh.rotation.z = l.scenograph.actors.player.vehicle.rotation.z; } + updateCamera( rY, tY, tZ ) { + var radian = ( Math.PI / 180 ); + + l.scenograph.actors.player.vehicle.camera_distance = l.scenograph.actors.player.vehicle.default_camera_distance + ( l.current_scene.room_depth / 2 ); + if ( l.scenograph.actors.player.vehicle.airSpeed < 0 ) { + l.scenograph.actors.player.vehicle.camera_distance -= l.scenograph.actors.player.vehicle.airSpeed * 4; + } + + let xDiff = l.scenograph.actors.player.vehicle.mesh.position.x; + let zDiff = l.scenograph.actors.player.vehicle.mesh.position.z; + + l.scenograph.cameras.player.position.x = xDiff + l.scenograph.actors.player.vehicle.camera_distance * Math.sin( l.scenograph.actors.player.vehicle.mesh.rotation.y ); + l.scenograph.cameras.player.position.z = zDiff + l.scenograph.actors.player.vehicle.camera_distance * Math.cos( l.scenograph.actors.player.vehicle.mesh.rotation.y ); + + if ( rY != 0 ) { + + l.scenograph.cameras.player.rotation.y += rY; + } + else { + // Check there is y difference and the rotation pad isn't being pressed. + if ( + l.scenograph.cameras.player.rotation.y != l.scenograph.actors.player.vehicle.mesh.rotation.y && + ( l.scenograph.controls.touch && !l.scenograph.controls.touch.controls.rotationPad.mouseDown ) + ) { + + // Get the difference in y rotation betwen the camera and ship + let yDiff = l.scenograph.actors.player.vehicle.mesh.rotation.y - l.scenograph.cameras.player.rotation.y; + + // Check the y difference is larger than 1/100th of a radian + if ( + Math.abs( yDiff ) > radian / 100 + ) { + // Add 1/60th of the difference in rotation, as FPS currently capped to 60. + l.scenograph.cameras.player.rotation.y += ( l.scenograph.actors.player.vehicle.mesh.rotation.y - l.scenograph.cameras.player.rotation.y ) * 1 / 60; + } + else { + l.scenograph.cameras.player.rotation.y = l.scenograph.actors.player.vehicle.mesh.rotation.y; + } + + } + + } + + let xDiff2 = tZ * Math.sin( l.scenograph.actors.player.vehicle.mesh.rotation.y ), + zDiff2 = tZ * Math.cos( l.scenograph.actors.player.vehicle.mesh.rotation.y ); + + if ( l.scenograph.actors.player.vehicle.mesh.position.y + tY >= 1 ) { + l.scenograph.cameras.player.position.y += tY; + } + + l.scenograph.cameras.player.position.x += xDiff2; + l.scenograph.cameras.player.position.z += zDiff2; + + l.scenograph.cameras.player.updateProjectionMatrix(); + } /** * Animate hook. * @@ -435,75 +490,78 @@ export default class Valiant extends ValiantBase { **/ animate( delta ) { - if ( l.current_scene.objects.player.ready ) { + if ( l.scenograph.actors.player.vehicle.ready ) { if ( l.current_scene.settings.game_controls ) { - // Detect keyboard input and pass it to the ship state model. - l.current_scene.objects.player.updateControls(); + + if ( l.scenograph.actors.player.mode == 'vehicle' ) { + // Detect keyboard input and pass it to the ship state model. + l.scenograph.actors.player.vehicle.updateControls(); + } if ( l.scenograph.modes.multiplayer.connected ) { - l.scenograph.modes.multiplayer.socket.emit( 'input', l.current_scene.objects.player.controls ); + l.scenograph.modes.multiplayer.socket.emit( 'input', l.scenograph.actors.player.vehicle.controls ); } - l.current_scene.objects.player.mesh.userData.actor.animate( delta ); + l.scenograph.actors.player.vehicle.mesh.userData.actor.animate( delta ); } - l.current_scene.objects.player.updateAnimation( delta ); + l.scenograph.actors.player.vehicle.updateAnimation( delta ); // Update the ships state model. - let [ rY, tY, tZ ] = l.current_scene.objects.player.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); - l.current_scene.objects.player.updateMesh(); + let [ rY, tY, tZ ] = l.scenograph.actors.player.vehicle.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); + l.scenograph.actors.player.vehicle.updateMesh(); - l.scenograph.cameras.updatePlayer( rY, tY, tZ ); + l.scenograph.actors.player.vehicle.updateCamera( rY, tY, tZ ); - l.current_scene.objects.player.animateTrail( rY ); + l.scenograph.actors.player.vehicle.animateTrail( rY ); } } animateTrail( rY ) { - if ( l.current_scene.objects.player.trail ) { + if ( l.scenograph.actors.player.vehicle.trail ) { // Fix the trail being too far behind. let trailOffset = 0; // Only offset the trail effect if we are going forward which is (z-1) in numerical terms - if ( l.current_scene.objects.player.airSpeed < 0 ) { + if ( l.scenograph.actors.player.vehicle.airSpeed < 0 ) { // Update ship thruster - l.current_scene.objects.player.animateThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.centralConeBurner, .5 ); - l.current_scene.objects.player.animateThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.outerCylBurner, .5 ); + l.scenograph.actors.player.vehicle.animateThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.centralConeBurner, .5 ); + l.scenograph.actors.player.vehicle.animateThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.outerCylBurner, .5 ); - l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.rearConeBurner, -1 ); - l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.centralConeBurner, 1 ); - l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.outerCylBurner, -1 ); - l.current_scene.objects.player.spinThruster( l.current_scene.objects.player.airSpeed, l.current_scene.objects.player.thruster.innerCylBurner, 1 ); + l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.rearConeBurner, -1 ); + l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.centralConeBurner, 1 ); + l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.outerCylBurner, -1 ); + l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.innerCylBurner, 1 ); // Limit playback rate to 5x as large values freak out the browser. - l.current_scene.objects.player.thruster.videoElement.playbackRate = Math.min( 5, 0.25 + Math.abs( l.current_scene.objects.player.airSpeed ) ); + l.scenograph.actors.player.vehicle.thruster.videoElement.playbackRate = Math.min( 5, 0.25 + Math.abs( l.scenograph.actors.player.vehicle.airSpeed ) ); - trailOffset += l.current_scene.objects.player.trail_position_z - Math.abs( l.current_scene.objects.player.airSpeed ); + trailOffset += l.scenograph.actors.player.vehicle.trail_position_z - Math.abs( l.scenograph.actors.player.vehicle.airSpeed ); - l.current_scene.objects.player.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, .8 ); // RGBA. + l.scenograph.actors.player.vehicle.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, .8 ); // RGBA. } else { - l.current_scene.objects.player.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, 0 ); // RGBA. + l.scenograph.actors.player.vehicle.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, 0 ); // RGBA. } // Update the trail position based on above calculations. - l.current_scene.objects.player.trail.targetObject.position.y = l.current_scene.objects.player.trail_position_y + l.current_scene.objects.player.verticalSpeed; - l.current_scene.objects.player.trail.targetObject.position.z = trailOffset; + l.scenograph.actors.player.vehicle.trail.targetObject.position.y = l.scenograph.actors.player.vehicle.trail_position_y + l.scenograph.actors.player.vehicle.verticalSpeed; + l.scenograph.actors.player.vehicle.trail.targetObject.position.z = trailOffset; if ( rY != 0 ) { - l.current_scene.objects.player.trail.targetObject.position.x = rY * l.current_scene.objects.player.airSpeed; - l.current_scene.objects.player.trail.targetObject.position.y += Math.abs( l.current_scene.objects.player.trail.targetObject.position.x ) / 4; + l.scenograph.actors.player.vehicle.trail.targetObject.position.x = rY * l.scenograph.actors.player.vehicle.airSpeed; + l.scenograph.actors.player.vehicle.trail.targetObject.position.y += Math.abs( l.scenograph.actors.player.vehicle.trail.targetObject.position.x ) / 4; } else { - l.current_scene.objects.player.trail.targetObject.position.x = 0; + l.scenograph.actors.player.vehicle.trail.targetObject.position.x = 0; } - l.current_scene.objects.player.trail.update(); + l.scenograph.actors.player.vehicle.trail.update(); } } diff --git a/client/src/app/scenograph/overlays/heads-up-display.js b/client/src/app/scenograph/overlays/heads-up-display.js index 573a83c7..5293ad69 100644 --- a/client/src/app/scenograph/overlays/heads-up-display.js +++ b/client/src/app/scenograph/overlays/heads-up-display.js @@ -77,13 +77,13 @@ export default class HeadsUpDisplay { l.scenograph.overlays.hud.container.classList.remove('portrait'); } - let aspd = -l.scenograph.overlays.hud.frameToSecond(l.current_scene.objects.player.airSpeed); + let aspd = -l.scenograph.overlays.hud.frameToSecond(l.scenograph.actors.player.vehicle.airSpeed); l.scenograph.overlays.hud.aspdElement.innerHTML = `AIRSPEED: ${aspd}km/h`; - let vspd = l.scenograph.overlays.hud.frameToSecond(l.current_scene.objects.player.verticalSpeed); + let vspd = l.scenograph.overlays.hud.frameToSecond(l.scenograph.actors.player.vehicle.verticalSpeed); l.scenograph.overlays.hud.vspdElement.innerHTML = `VERT. SPD: ${vspd}km/h`; - let heading = THREE.MathUtils.radToDeg( l.current_scene.objects.player.rotation.y ); + let heading = THREE.MathUtils.radToDeg( l.scenograph.actors.player.vehicle.rotation.y ); heading = heading % 360; if (heading < 0) { heading += 360; @@ -91,7 +91,7 @@ export default class HeadsUpDisplay { heading = Math.round(360 - heading); l.scenograph.overlays.hud.headingElement.innerHTML = `HEADING: ${heading}°`; - let elevation = Math.round( l.current_scene.objects.player.position.y * 100 ) / 100; + let elevation = Math.round( l.scenograph.actors.player.vehicle.position.y * 100 ) / 100; l.scenograph.overlays.hud.elevationElement.innerHTML = `ELEVATION: ${elevation}m`; } diff --git a/client/src/app/scenograph/overlays/map.js b/client/src/app/scenograph/overlays/map.js index b7932e6e..af96f6bc 100644 --- a/client/src/app/scenograph/overlays/map.js +++ b/client/src/app/scenograph/overlays/map.js @@ -106,12 +106,12 @@ export default class Map { let offset = mapSize / l.scenograph.overlays.map.distance; // Pixels per world unit let halfMapSize = (mapSize / 2) - 2.5; // Half the map size to center objects - let leftEdge = l.current_scene.objects.player.mesh.position.x - l.scenograph.overlays.map.distance / 2; - let topEdge = l.current_scene.objects.player.mesh.position.z - l.scenograph.overlays.map.distance / 2; + let leftEdge = l.scenograph.actors.player.vehicle.mesh.position.x - l.scenograph.overlays.map.distance / 2; + let topEdge = l.scenograph.actors.player.vehicle.mesh.position.z - l.scenograph.overlays.map.distance / 2; - l.current_scene.objects.player.mesh.userData.actor.scanners.targets.forEach( target => { - let distance = target.mesh.position.distanceTo( l.current_scene.objects.player.mesh.position ); + l.scenograph.actors.player.vehicle.mesh.userData.actor.scanners.targets.forEach( target => { + let distance = target.mesh.position.distanceTo( l.scenograph.actors.player.vehicle.mesh.position ); // Check if the object is within the mapping distance. if ( distance <= l.scenograph.overlays.map.distance * 100 ) { @@ -171,7 +171,7 @@ export default class Map { * @note All references within this method should be globally accessible. **/ animate() { - let heading = THREE.MathUtils.radToDeg( l.current_scene.objects.player.rotation.y ); + let heading = THREE.MathUtils.radToDeg( l.scenograph.actors.player.vehicle.rotation.y ); heading = heading % 360; if (heading < 0) { heading += 360; diff --git a/client/src/app/scenograph/overlays/scanners.js b/client/src/app/scenograph/overlays/scanners.js index 4a58ca94..4a526bfc 100644 --- a/client/src/app/scenograph/overlays/scanners.js +++ b/client/src/app/scenograph/overlays/scanners.js @@ -129,7 +129,7 @@ export default class Scanners { l.scenograph.cameras.active.updateProjectionMatrix(); // Use the players scanners to update the overlays. - l.current_scene.objects.player.mesh.userData.actor.scanners.targets.forEach( target => l.scenograph.overlays.scanners.animateTarget( delta, target, frustum ) ); + l.scenograph.actors.player.vehicle.mesh.userData.actor.scanners.targets.forEach( target => l.scenograph.overlays.scanners.animateTarget( delta, target, frustum ) ); l.scenograph.overlays.scanners.removeOldTargets(); diff --git a/client/src/app/scenograph/scenes/overworld.js b/client/src/app/scenograph/scenes/overworld.js index af9b71a0..a1dc8c80 100644 --- a/client/src/app/scenograph/scenes/overworld.js +++ b/client/src/app/scenograph/scenes/overworld.js @@ -26,6 +26,7 @@ import Refineries from "@/scenograph/objects/structures/refineries"; // Vehicles import CargoShips from "@/scenograph/objects/vehicles/cargo_ships"; +import Person from "@/scenograph/objects/vehicles/person"; import Raven from "@/scenograph/objects/vehicles/raven"; import Valiant from "@/scenograph/objects/vehicles/valiant"; @@ -105,14 +106,23 @@ export default class Overworld extends SceneBase { // l.current_scene.objects.sky.animate // ); - // Setup Player, currently hardcoded to Valiant aircraft - l.current_scene.objects.player = new Valiant(); - await l.current_scene.objects.player.load(); + // Setup Player aircraft, used for the intro sequence. + l.scenograph.actors.player.vehicle = new Valiant(); + await l.scenograph.actors.player.vehicle.load(); l.current_scene.scene.add( - l.current_scene.objects.player.mesh + l.scenograph.actors.player.vehicle.mesh ); l.current_scene.animation_queue.push( - l.current_scene.objects.player.animate + l.scenograph.actors.player.vehicle.animate + ); + + // Setup Player person, used for the hangar scene. + l.scenograph.actors.player.person = new Person(); + l.current_scene.scene.add( + l.scenograph.actors.player.person.mesh + ); + l.current_scene.animation_queue.push( + l.scenograph.actors.player.person.animate ); // let scale = 500; diff --git a/client/src/app/scenograph/tweens.js b/client/src/app/scenograph/tweens.js index 38c3445e..d71a239d 100644 --- a/client/src/app/scenograph/tweens.js +++ b/client/src/app/scenograph/tweens.js @@ -218,7 +218,7 @@ function flickerEffect() { */ function enterTheOffice() { let coords = { x: 15 + l.current_scene.room_depth / 2 }; // Start at (0, 0) - let targetZ = l.current_scene.objects.player.default_camera_distance + l.current_scene.room_depth / 2; + let targetZ = l.scenograph.actors.player.vehicle.default_camera_distance + l.current_scene.room_depth / 2; return new TWEEN.Tween( coords, false ) // Create a new tween that modifies 'coords'. .to( { x: targetZ }, l.config.settings.skipintro ? 0 : 1000 ) // Move to (300, 200) in 1 second. .easing( TWEEN.Easing.Quadratic.InOut ) // Use an easing function to make the animation smooth. @@ -311,7 +311,7 @@ function dollyUp() { return new TWEEN.Tween( l.scenograph.cameras.player.position ) .to( { y: l.scenograph.cameras.playerY }, l.config.settings.skipintro ? 0 : 500 ) // Set the duration of the animation .onUpdate( () => { - //l.scenograph.cameras.player.lookAt(l.current_scene.objects.player.mesh.position); + //l.scenograph.cameras.player.lookAt(l.scenograph.actors.player.vehicle.mesh.position); l.scenograph.cameras.player.updateProjectionMatrix(); } ) .onComplete( () => { diff --git a/client/src/app/ui/flight_instruments.js b/client/src/app/ui/flight_instruments.js index 941e35ce..e72f700e 100644 --- a/client/src/app/ui/flight_instruments.js +++ b/client/src/app/ui/flight_instruments.js @@ -38,7 +38,7 @@ export default class Flight_Instruments { update() { // Check if the main aircraft is loaded and ready - if (l.current_scene.objects.player && l.current_scene.objects.player.ready) { + if (l.scenograph.actors.player.vehicle && l.scenograph.actors.player.vehicle.ready) { if ( l.current_scene.settings.game_controls ) { if ( !l.ui.flight_instruments.activated ) { @@ -47,7 +47,7 @@ export default class Flight_Instruments { } // Update the angle of the needle - const angle = (Math.abs(l.current_scene.objects.player.airSpeed) * 1.94384) * 45; + const angle = (Math.abs(l.scenograph.actors.player.vehicle.airSpeed) * 1.94384) * 45; // Update the needle rotation document.querySelector(l.ui.flight_instruments.containerSelector + ' #Airspeed #Needle').style.transform = `rotate(${angle}deg)`; } diff --git a/client/src/app/ui/menus/debugging_tools.js b/client/src/app/ui/menus/debugging_tools.js index 865dc475..176aa054 100644 --- a/client/src/app/ui/menus/debugging_tools.js +++ b/client/src/app/ui/menus/debugging_tools.js @@ -73,19 +73,19 @@ export default class Debugging_Tools { expanded: false, } ); - shipState.addBinding( l.current_scene.objects.player.controls, 'throttleUp', { + shipState.addBinding( l.scenograph.actors.player.vehicle.controls, 'throttleUp', { readonly: true, interval: 200 } ) - shipState.addBinding( l.current_scene.objects.player.controls, 'throttleDown', { + shipState.addBinding( l.scenograph.actors.player.vehicle.controls, 'throttleDown', { readonly: true, interval: 200 } ) - shipState.addBinding( l.current_scene.objects.player.controls, 'moveLeft', { + shipState.addBinding( l.scenograph.actors.player.vehicle.controls, 'moveLeft', { readonly: true, interval: 200 } ) - shipState.addBinding( l.current_scene.objects.player.controls, 'moveRight', { + shipState.addBinding( l.scenograph.actors.player.vehicle.controls, 'moveRight', { readonly: true, interval: 200 } ) diff --git a/client/src/app/ui/targeting/list.js b/client/src/app/ui/targeting/list.js index 7ca0e501..2b1c1d5c 100644 --- a/client/src/app/ui/targeting/list.js +++ b/client/src/app/ui/targeting/list.js @@ -58,7 +58,7 @@ export default class List { // Check if targetable and not the current player. let targetable = mesh.userData && mesh.userData.targetable ? true : false; - if ( targetable && mesh.uuid != l.current_scene.objects.player.mesh.uuid ) { + if ( targetable && mesh.uuid != l.scenograph.actors.player.vehicle.mesh.uuid ) { let item = JSON.parse( JSON.stringify( l.ui.targeting.list.item_template ) ); let icon_class = ''; @@ -143,7 +143,7 @@ export default class List { let targetObject = l.current_scene.scene.getObjectByProperty( 'uuid', targetIcon.dataset.uuid ); // Update the distance to target. - let distance = targetObject.position.distanceTo( l.current_scene.objects.player.mesh.position ); + let distance = targetObject.position.distanceTo( l.scenograph.actors.player.vehicle.mesh.position ); if ( distance > 1000 ) { distance = Math.round( Math.round( distance ) / 10 ) / 100; targetIcon.querySelector( '.distance' ).innerHTML = distance + 'km'; diff --git a/client/src/app/ui/targeting/locked.js b/client/src/app/ui/targeting/locked.js index 89692ea0..5947b04c 100644 --- a/client/src/app/ui/targeting/locked.js +++ b/client/src/app/ui/targeting/locked.js @@ -80,7 +80,7 @@ export default class Locked { let targetObject = l.current_scene.scene.getObjectByProperty( 'uuid', targetIcon.dataset.uuid); // Update the distance to target. - let distance = targetObject.position.distanceTo( l.current_scene.objects.player.mesh.position ); + let distance = targetObject.position.distanceTo( l.scenograph.actors.player.vehicle.mesh.position ); if ( distance > 1000 ) { distance = Math.round(Math.round(distance) / 10) / 100; targetIcon.querySelector('.distance').innerHTML = distance + 'km'; diff --git a/game/src/actors/pirate.ts b/game/src/actors/pirate.ts index a2d429f8..e2b8bb19 100644 --- a/game/src/actors/pirate.ts +++ b/game/src/actors/pirate.ts @@ -45,7 +45,7 @@ export default class Pirate extends BaseActor { this.entity.steering.add( this.follow ); // @todo: v7 Figure out a way to signal this to happen without l. global object access - this.pursue = new YUKA.PursuitBehavior( l.current_scene.objects.player.mesh.userData.actor.entity, 1 ); + this.pursue = new YUKA.PursuitBehavior( l.scenograph.actors.player.vehicle.mesh.userData.actor.entity, 1 ); this.pursue.active = false; this.entity.steering.add( this.pursue ); @@ -60,7 +60,7 @@ export default class Pirate extends BaseActor { } // @todo: v7 Figure out a way to signal this to happen without l. global object access - if ( this.entity.vision.visible( l.current_scene.objects.player.position ) === true ) { + if ( this.entity.vision.visible( l.scenograph.actors.player.vehicle.mesh.position ) === true ) { this.pursue.active = true; this.follow.active = false; diff --git a/game/src/objects/person.ts b/game/src/objects/person.ts new file mode 100644 index 00000000..56fe5685 --- /dev/null +++ b/game/src/objects/person.ts @@ -0,0 +1,184 @@ +/** + * Base Aircraft class + * + * @todo: + * - Add weight and wind resistance + */ + +import { normaliseSpeedDelta, easeOutExpo, easeInQuad, easeInOutExpo } from '../helpers'; + +export default class BaseAircraft { + public score: { kills: number; deaths: number } = { kills: 0, deaths: 0 }; + public standing: number = 0; + public hitPoints: number = 100; + public airSpeed: number = 0; + public verticalSpeed: number = 0; + public maxForward: number = 3.7 * 5; // Reading as 200 knots on the airspeed instrument, may not be correct. + public maxBackward: number = 2.0; + public maxUp: number = 3.7 * 2.5; + public maxDown: number = 3.7 * 5; // gravity? + + public position: { x: number; y: number; z: number } = { x: 0, y: 8.5, z: 0 }; + public startPosition: { x: number; y: number; z: number } = { x: 0, y: 8.5, z: 0 }; + public rotation: { x: number; y: number; z: number } = { x: 0, y: 0, z: 0 }; + + public controls: { + changing: boolean, + forward: boolean; + back: boolean; + jump: boolean; + crouch: boolean; + turnLeft: boolean; + turnRight: boolean; + } = { + changing: false, + forward: false, + back: false, + jump: false, + crouch: false, + turnLeft: false, + turnRight: false + }; + + constructor() { + } + + /** + * Change aircraft velocity based on current and what buttons are pushed by the player. + * + * @param currentVelocity + * @param increasePushed + * @param decreasePushed + */ + private _changeVelocity(stepIncrease, stepDecrease, currentVelocity, increasePushed, decreasePushed, increaseMax, decreaseMax, dragFactor): number { + let newVelocity = currentVelocity; + + if (increasePushed) { + + // Check if the change puts us over the max. + let tempVelocity = newVelocity - stepIncrease; + if (Math.abs(increaseMax) >= Math.abs(tempVelocity)) + newVelocity = tempVelocity; + } + else { + if (decreasePushed) { + + // Check if the change puts us over the max. + let tempVelocity = newVelocity + stepDecrease; + if (Math.abs(decreaseMax) >= tempVelocity) + newVelocity = tempVelocity; + } + else { + if (newVelocity != 0) { + + if (Math.abs(newVelocity) > 0.1) { + // Ease out the velocity exponentially to simulate drag + newVelocity *= dragFactor; + } + else { + newVelocity = 0; + } + } + + } + } + + return newVelocity; + } + + /** + * Move the aircraft based on velocity, direction and time delta between frames. + * + * @param time_delta + */ + public move( time_delta: number ): object { + let stepSize: number = .05 * normaliseSpeedDelta( time_delta ), + rY: number = 0, + tZ: number = 0, + tY: number = 0, + radian: number = (Math.PI / 180); + + // Update Airspeed (horizontal velocity) + this.airSpeed = this._changeVelocity( + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.airSpeed ) / this.maxForward ) ), + stepSize, + this.airSpeed, + this.controls.forward, + this.controls.back, + this.maxForward, + this.maxBackward, + easeOutExpo( 0.987 ) + ); + + // Update Vertical Speed (velocity) + this.verticalSpeed = this._changeVelocity( + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxUp ) ), + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxDown ) ), + this.verticalSpeed, + this.controls.jump, // Note: Move Down/Up is reversed by design. + this.controls.crouch, + this.maxDown, + this.maxUp, + easeInQuad( 0.321 ) + ); + + // Check the vertical speed exceeds minimum threshold for change in vertical position + if (Math.abs(this.verticalSpeed) > 0.01) { + tY = this.verticalSpeed; + } + + // Turning + if (this.controls.turnLeft) { + rY += radian; + } + else { + if (this.controls.turnRight) { + rY -= radian; + } + } + + // Check if we have significant airspeed + if (Math.abs(this.airSpeed) > 0.01) { + + // Set change in Z position based on airspeed + tZ = this.airSpeed; + + } + + // Animate the ship's rotation in the game client based on controls. + if ( + !(this.controls.forward || this.controls.back) && + !(this.controls.jump || this.controls.crouch) + ) { + this.rotation.x *= .9; + } + + if (rY != 0) { + if (Math.abs(this.rotation.z) < Math.PI / 4) { + this.rotation.z += rY / Math.PI; + } + + this.rotation.y += rY; + } + else { + this.rotation.z *= .9; + } + + let xDiff = tZ * Math.sin(this.rotation.y), + zDiff = tZ * Math.cos(this.rotation.y); + + // "1" is the floor limit as it's the ocean surface and the camera clips through the water any lower. + if (this.position.y + tY >= 1 ) { + this.position.y += tY; + } else { + this.verticalSpeed = 0; + } + + this.position.x += xDiff; + this.position.z += zDiff; + + return [ rY, tY, tZ ]; + + } + +} From 498b45d7687da21d5f000595f15ebf6de3b58b55 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 11 Aug 2025 20:47:17 +0000 Subject: [PATCH 16/58] #31 - Fixing issues with new Person vehicle --- .../app/scenograph/objects/vehicles/person.js | 56 +++++++++---------- game/src/objects/person.ts | 12 ++-- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/client/src/app/scenograph/objects/vehicles/person.js b/client/src/app/scenograph/objects/vehicles/person.js index 467abd94..24c98eb6 100644 --- a/client/src/app/scenograph/objects/vehicles/person.js +++ b/client/src/app/scenograph/objects/vehicles/person.js @@ -99,7 +99,7 @@ export default class Person extends PersonBase { updateCamera( rY, tY, tZ ) { var radian = ( Math.PI / 180 ); - l.scenograph.actors.player.person.camera_distance = l.scenograph.actors.player.person.default_camera_distance + ( l.current_scene.room_depth / 2 ); + l.scenograph.actors.player.person.camera_distance = l.scenograph.actors.player.person.default_camera_distance + ( l.current_scene.room_depth / 20 ); if ( l.scenograph.actors.player.person.airSpeed < 0 ) { l.scenograph.actors.player.person.camera_distance -= l.scenograph.actors.player.person.airSpeed * 4; } @@ -114,40 +114,40 @@ export default class Person extends PersonBase { l.scenograph.cameras.player.rotation.y += rY; } - else { - // Check there is y difference and the rotation pad isn't being pressed. - if ( - l.scenograph.cameras.player.rotation.y != l.scenograph.actors.player.person.mesh.rotation.y && - ( l.scenograph.controls.touch && !l.scenograph.controls.touch.controls.rotationPad.mouseDown ) - ) { + // else { + // // Check there is y difference and the rotation pad isn't being pressed. + // if ( + // l.scenograph.cameras.player.rotation.y != l.scenograph.actors.player.person.mesh.rotation.y && + // ( l.scenograph.controls.touch && !l.scenograph.controls.touch.controls.rotationPad.mouseDown ) + // ) { - // Get the difference in y rotation betwen the camera and ship - let yDiff = l.scenograph.actors.player.person.mesh.rotation.y - l.scenograph.cameras.player.rotation.y; + // // Get the difference in y rotation betwen the camera and ship + // let yDiff = l.scenograph.actors.player.person.mesh.rotation.y - l.scenograph.cameras.player.rotation.y; - // Check the y difference is larger than 1/100th of a radian - if ( - Math.abs( yDiff ) > radian / 100 - ) { - // Add 1/60th of the difference in rotation, as FPS currently capped to 60. - l.scenograph.cameras.player.rotation.y += ( l.scenograph.actors.player.person.mesh.rotation.y - l.scenograph.cameras.player.rotation.y ) * 1 / 60; - } - else { - l.scenograph.cameras.player.rotation.y = l.scenograph.actors.player.person.mesh.rotation.y; - } + // // Check the y difference is larger than 1/100th of a radian + // if ( + // Math.abs( yDiff ) > radian / 100 + // ) { + // // Add 1/60th of the difference in rotation, as FPS currently capped to 60. + // l.scenograph.cameras.player.rotation.y += ( l.scenograph.actors.player.person.mesh.rotation.y - l.scenograph.cameras.player.rotation.y ) * 1 / 60; + // } + // else { + // l.scenograph.cameras.player.rotation.y = l.scenograph.actors.player.person.mesh.rotation.y; + // } - } + // } - } + // } - let xDiff2 = tZ * Math.sin( l.scenograph.actors.player.person.mesh.rotation.y ), - zDiff2 = tZ * Math.cos( l.scenograph.actors.player.person.mesh.rotation.y ); + // let xDiff2 = tZ * Math.sin( l.scenograph.actors.player.person.mesh.rotation.y ), + // zDiff2 = tZ * Math.cos( l.scenograph.actors.player.person.mesh.rotation.y ); - if ( l.scenograph.actors.player.person.mesh.position.y + tY >= 1 ) { - l.scenograph.cameras.player.position.y += tY; - } + // if ( l.scenograph.actors.player.person.mesh.position.y + tY >= 1 ) { + // l.scenograph.cameras.player.position.y += tY; + // } - l.scenograph.cameras.player.position.x += xDiff2; - l.scenograph.cameras.player.position.z += zDiff2; + // l.scenograph.cameras.player.position.x += xDiff2; + // l.scenograph.cameras.player.position.z += zDiff2; l.scenograph.cameras.player.updateProjectionMatrix(); } diff --git a/game/src/objects/person.ts b/game/src/objects/person.ts index 56fe5685..1d2739a4 100644 --- a/game/src/objects/person.ts +++ b/game/src/objects/person.ts @@ -7,16 +7,16 @@ import { normaliseSpeedDelta, easeOutExpo, easeInQuad, easeInOutExpo } from '../helpers'; -export default class BaseAircraft { +export default class Person { public score: { kills: number; deaths: number } = { kills: 0, deaths: 0 }; public standing: number = 0; public hitPoints: number = 100; public airSpeed: number = 0; public verticalSpeed: number = 0; - public maxForward: number = 3.7 * 5; // Reading as 200 knots on the airspeed instrument, may not be correct. - public maxBackward: number = 2.0; - public maxUp: number = 3.7 * 2.5; - public maxDown: number = 3.7 * 5; // gravity? + public maxForward: number = 8 / 60; // 8 km/h @ 60 FPS + public maxBackward: number = 8 / 120; + public maxUp: number = 4 / 60; + public maxDown: number = 16 / 60; // gravity? public position: { x: number; y: number; z: number } = { x: 0, y: 8.5, z: 0 }; public startPosition: { x: number; y: number; z: number } = { x: 0, y: 8.5, z: 0 }; @@ -96,7 +96,7 @@ export default class BaseAircraft { rY: number = 0, tZ: number = 0, tY: number = 0, - radian: number = (Math.PI / 180); + radian: number = - (Math.PI / 180) * stepSize; // Update Airspeed (horizontal velocity) this.airSpeed = this._changeVelocity( From ecab2d5247f6762a25065b901e47a5464ef5af4f Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 11 Aug 2025 21:43:18 +0000 Subject: [PATCH 17/58] #31 - Tweaking rotation --- game/src/objects/person.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/game/src/objects/person.ts b/game/src/objects/person.ts index 1d2739a4..25bc0c05 100644 --- a/game/src/objects/person.ts +++ b/game/src/objects/person.ts @@ -96,7 +96,7 @@ export default class Person { rY: number = 0, tZ: number = 0, tY: number = 0, - radian: number = - (Math.PI / 180) * stepSize; + radian: number = - (Math.PI / 180) * stepSize * 10; // Update Airspeed (horizontal velocity) this.airSpeed = this._changeVelocity( @@ -144,14 +144,6 @@ export default class Person { tZ = this.airSpeed; } - - // Animate the ship's rotation in the game client based on controls. - if ( - !(this.controls.forward || this.controls.back) && - !(this.controls.jump || this.controls.crouch) - ) { - this.rotation.x *= .9; - } if (rY != 0) { if (Math.abs(this.rotation.z) < Math.PI / 4) { @@ -160,9 +152,6 @@ export default class Person { this.rotation.y += rY; } - else { - this.rotation.z *= .9; - } let xDiff = tZ * Math.sin(this.rotation.y), zDiff = tZ * Math.cos(this.rotation.y); From 94c5df70c288bfdf0db4d2264e0f9de3c5767c3f Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Tue, 12 Aug 2025 16:17:50 +0000 Subject: [PATCH 18/58] #31 - Correcting FPS controls --- client/src/app/routes/hangar.js | 8 ++-- .../app/scenograph/objects/vehicles/person.js | 8 ++-- game/src/objects/person.ts | 44 +++++++++++-------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index 07f5a627..09b8eb46 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -37,12 +37,10 @@ export default class hangarRoute { l.scenograph.actors.player.vehicle.position.y = 500; l.scenograph.actors.player.person.position.x = l.current_scene.objects.platform.mesh.position.x; - l.scenograph.actors.player.person.position.z = l.current_scene.objects.platform.mesh.position.z - 5; - l.scenograph.actors.player.person.position.y = 495; + l.scenograph.actors.player.person.position.z = l.current_scene.objects.platform.mesh.position.z + 5; + l.scenograph.actors.player.person.position.y = 502.5; - l.scenograph.cameras.active.position.copy(l.scenograph.actors.player.vehicle.position); - l.scenograph.cameras.active.translateZ(15); - l.scenograph.cameras.active.translateY(7.5); + l.scenograph.cameras.active.position.copy(l.scenograph.actors.player.person.position); if ( l.scenograph.controls.orbit ) { diff --git a/client/src/app/scenograph/objects/vehicles/person.js b/client/src/app/scenograph/objects/vehicles/person.js index 24c98eb6..47e87c2e 100644 --- a/client/src/app/scenograph/objects/vehicles/person.js +++ b/client/src/app/scenograph/objects/vehicles/person.js @@ -110,10 +110,10 @@ export default class Person extends PersonBase { l.scenograph.cameras.player.position.x = xDiff + l.scenograph.actors.player.person.camera_distance * Math.sin( l.scenograph.actors.player.person.mesh.rotation.y ); l.scenograph.cameras.player.position.z = zDiff + l.scenograph.actors.player.person.camera_distance * Math.cos( l.scenograph.actors.player.person.mesh.rotation.y ); - if ( rY != 0 ) { + // if ( rY != 0 ) { - l.scenograph.cameras.player.rotation.y += rY; - } + // l.scenograph.cameras.player.rotation.y += rY; + // } // else { // // Check there is y difference and the rotation pad isn't being pressed. // if ( @@ -179,6 +179,8 @@ export default class Person extends PersonBase { // Update the persons camera l.scenograph.actors.player.person.updateCamera(rY, tY, tZ); + l.scenograph.cameras.player.rotation.y = l.scenograph.actors.player.person.rotation.y; + } } diff --git a/game/src/objects/person.ts b/game/src/objects/person.ts index 25bc0c05..8043ab6c 100644 --- a/game/src/objects/person.ts +++ b/game/src/objects/person.ts @@ -13,7 +13,7 @@ export default class Person { public hitPoints: number = 100; public airSpeed: number = 0; public verticalSpeed: number = 0; - public maxForward: number = 8 / 60; // 8 km/h @ 60 FPS + public maxForward: number = 12 / 60; // 8 km/h @ 60 FPS public maxBackward: number = 8 / 120; public maxUp: number = 4 / 60; public maxDown: number = 16 / 60; // gravity? @@ -92,31 +92,37 @@ export default class Person { * @param time_delta */ public move( time_delta: number ): object { - let stepSize: number = .05 * normaliseSpeedDelta( time_delta ), + let stepSize: number = .025 * normaliseSpeedDelta( time_delta ), rY: number = 0, tZ: number = 0, tY: number = 0, - radian: number = - (Math.PI / 180) * stepSize * 10; - - // Update Airspeed (horizontal velocity) - this.airSpeed = this._changeVelocity( - stepSize * easeInOutExpo( 1 - ( Math.abs ( this.airSpeed ) / this.maxForward ) ), - stepSize, - this.airSpeed, - this.controls.forward, - this.controls.back, - this.maxForward, - this.maxBackward, - easeOutExpo( 0.987 ) - ); + radian: number = - (Math.PI / 180) * stepSize * 50; + + if ( this.controls.forward || this.controls.back ){ + // Update Airspeed (horizontal velocity) + this.airSpeed = this._changeVelocity( + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.airSpeed ) / this.maxForward ) ), + stepSize, + this.airSpeed, + this.controls.forward, + this.controls.back, + this.maxForward, + this.maxBackward, + easeOutExpo( 0.987 ) + ); + } + else { + this.airSpeed = 0; + } + // Update Vertical Speed (velocity) this.verticalSpeed = this._changeVelocity( stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxUp ) ), stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxDown ) ), this.verticalSpeed, - this.controls.jump, // Note: Move Down/Up is reversed by design. - this.controls.crouch, + this.controls.crouch, // Note: Move Down/Up is reversed by design. + this.controls.jump, this.maxDown, this.maxUp, easeInQuad( 0.321 ) @@ -128,11 +134,11 @@ export default class Person { } // Turning - if (this.controls.turnLeft) { + if (this.controls.turnRight) { rY += radian; } else { - if (this.controls.turnRight) { + if (this.controls.turnLeft) { rY -= radian; } } From 6fb370e02811238368bd1b43c05652b5b2a9baf3 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Tue, 12 Aug 2025 16:19:13 +0000 Subject: [PATCH 19/58] #31 - Aligning walking backward speed to forward --- game/src/objects/person.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/src/objects/person.ts b/game/src/objects/person.ts index 8043ab6c..70e313ac 100644 --- a/game/src/objects/person.ts +++ b/game/src/objects/person.ts @@ -14,7 +14,7 @@ export default class Person { public airSpeed: number = 0; public verticalSpeed: number = 0; public maxForward: number = 12 / 60; // 8 km/h @ 60 FPS - public maxBackward: number = 8 / 120; + public maxBackward: number = 12 / 60; public maxUp: number = 4 / 60; public maxDown: number = 16 / 60; // gravity? From 72913b973c210dfa04f5a1042cc4d93297a8c0c8 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Tue, 12 Aug 2025 16:36:27 +0000 Subject: [PATCH 20/58] #31 - Tweaking hangar appearance --- client/src/app/routes/hangar.js | 6 +++--- client/src/app/scenograph/objects/vehicles/valiant.js | 5 +++-- game/src/objects/person.ts | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index 09b8eb46..6754f12c 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -33,11 +33,11 @@ export default class hangarRoute { l.current_scene.objects.hangar.mesh.position.y = 505; l.scenograph.actors.player.vehicle.position.x = l.current_scene.objects.platform.mesh.position.x; - l.scenograph.actors.player.vehicle.position.z = l.current_scene.objects.platform.mesh.position.z; - l.scenograph.actors.player.vehicle.position.y = 500; + l.scenograph.actors.player.vehicle.position.z = l.current_scene.objects.platform.mesh.position.z - 2.5; + l.scenograph.actors.player.vehicle.position.y = 497.5; l.scenograph.actors.player.person.position.x = l.current_scene.objects.platform.mesh.position.x; - l.scenograph.actors.player.person.position.z = l.current_scene.objects.platform.mesh.position.z + 5; + l.scenograph.actors.player.person.position.z = l.current_scene.objects.platform.mesh.position.z + 10; l.scenograph.actors.player.person.position.y = 502.5; l.scenograph.cameras.active.position.copy(l.scenograph.actors.player.person.position); diff --git a/client/src/app/scenograph/objects/vehicles/valiant.js b/client/src/app/scenograph/objects/vehicles/valiant.js index 58f0bf47..fb41368d 100644 --- a/client/src/app/scenograph/objects/vehicles/valiant.js +++ b/client/src/app/scenograph/objects/vehicles/valiant.js @@ -106,6 +106,7 @@ export default class Valiant extends ValiantBase { this.mesh.name = 'Player Ship'; this.mesh.position.z = l.current_scene.room_depth; this.mesh.rotation.order = 'YXZ'; + this.mesh.scale.setScalar(2); this.mesh.userData.targetable = true; this.mesh.userData.objectClass = 'player'; @@ -507,7 +508,8 @@ export default class Valiant extends ValiantBase { } - l.scenograph.actors.player.vehicle.updateAnimation( delta ); + if ( l.mode != 'hangar') + l.scenograph.actors.player.vehicle.updateAnimation( delta ); // Update the ships state model. let [ rY, tY, tZ ] = l.scenograph.actors.player.vehicle.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); @@ -517,7 +519,6 @@ export default class Valiant extends ValiantBase { l.scenograph.actors.player.vehicle.animateTrail( rY ); - } } diff --git a/game/src/objects/person.ts b/game/src/objects/person.ts index 70e313ac..c1eb3f8d 100644 --- a/game/src/objects/person.ts +++ b/game/src/objects/person.ts @@ -13,8 +13,8 @@ export default class Person { public hitPoints: number = 100; public airSpeed: number = 0; public verticalSpeed: number = 0; - public maxForward: number = 12 / 60; // 8 km/h @ 60 FPS - public maxBackward: number = 12 / 60; + public maxForward: number = 16 / 60; // 8 km/h @ 60 FPS + public maxBackward: number = 16 / 60; public maxUp: number = 4 / 60; public maxDown: number = 16 / 60; // gravity? From c22d4a45b676d1c1ecd87fedb4eaadd947c1e3f9 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 12 Sep 2025 18:38:11 +0000 Subject: [PATCH 21/58] #31 - Moving overlay scanner update from game folder to client --- .../src/app/scenograph/overlays/scanners.js | 22 ++++++++++++++++++- game/src/objects/aircraft/base.ts | 9 -------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/client/src/app/scenograph/overlays/scanners.js b/client/src/app/scenograph/overlays/scanners.js index 4a526bfc..8fda3c40 100644 --- a/client/src/app/scenograph/overlays/scanners.js +++ b/client/src/app/scenograph/overlays/scanners.js @@ -133,6 +133,8 @@ export default class Scanners { l.scenograph.overlays.scanners.removeOldTargets(); + l.scenograph.overlays.scanners.toggleRespawningTargets(); + } /** @@ -170,10 +172,28 @@ export default class Scanners { domElement.classList.remove('locking'); } + domElement.style.display = `block`; domElement.style.left = `${x-10}px`; domElement.style.top = `${y-10}px`; } + + /** + * Show/hide markers of objects that are respawning. + */ + toggleRespawningTargets() { + + const overlayKeys = Object.keys(l.scenograph.overlays.scanners.trackedObjects); + const scannerKeys = l.scenograph.actors.player.vehicle.mesh.userData.actor.scanners.targets.map(t => t.mesh.uuid); + + // Hide targets missing from player scanners, theoretically those are ones being relocated by the engine / respawning. + const respawningTargets = overlayKeys.filter(k => !scannerKeys.includes(k)); + respawningTargets.map( uuid => { + l.scenograph.overlays.scanners.trackedObjects[ uuid ].style.display = 'none'; + } ); + + } + /** * Remove markers for objects no longer in the scene. */ @@ -187,7 +207,7 @@ export default class Scanners { // Delete the marker domElement from memory. delete l.scenograph.overlays.scanners.trackedObjects[ uuid ]; - } + } } } diff --git a/game/src/objects/aircraft/base.ts b/game/src/objects/aircraft/base.ts index 13741f22..45a74175 100644 --- a/game/src/objects/aircraft/base.ts +++ b/game/src/objects/aircraft/base.ts @@ -111,12 +111,6 @@ export default class BaseAircraft { this.score.deaths += 1; originMesh.userData.object.score.kills += 1; - // Hide the scanner marker during respawn. - const scannerMarker = l.scenograph.overlays.scanners.trackedObjects[ this.mesh.uuid ]; - if ( scannerMarker ) - scannerMarker.style.display = 'none'; - - // Wait 3 seconds before 'respawn'. setTimeout( () => { // Reset hitpoints @@ -136,9 +130,6 @@ export default class BaseAircraft { this.mesh.userData.targetable = true; this.mesh.visible = true; - // Restore the scanner marker after respawn. - if ( scannerMarker ) - scannerMarker.style.display = 'block'; }, 3000 ); } From cbc443833046ab0dab4427ec9240d4379cf47273 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 12 Sep 2025 19:44:00 +0000 Subject: [PATCH 22/58] #31 - Disabling overlays in hangar mode --- client/src/app/routes/hangar.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index 6754f12c..97f066cc 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -19,10 +19,6 @@ export default class hangarRoute { // Start controls. l.scenograph.controls.activate(); - // Start overlays. - // @todo: #31 Fix the need for this, only included so that damage calcs can trigger their overlay updates - l.scenograph.overlays.activate(); - // Set client mode. l.mode = 'hangar'; From 4c16ca82761fda5b308ce9a8159d52bff71683ae Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Thu, 2 Oct 2025 18:31:06 +0000 Subject: [PATCH 23/58] #31 - Beginning to implement a share game instance --- client/src/app/scenograph.js | 6 +- client/src/app/scenograph/scenes/base.js | 16 ---- game/src/scenes/overworld.yml | 93 ++++++++++++++++++++++++ game/src/world.ts | 28 +++++++ 4 files changed, 122 insertions(+), 21 deletions(-) create mode 100644 game/src/scenes/overworld.yml create mode 100644 game/src/world.ts diff --git a/client/src/app/scenograph.js b/client/src/app/scenograph.js index 41a414f2..289b8b81 100644 --- a/client/src/app/scenograph.js +++ b/client/src/app/scenograph.js @@ -145,11 +145,7 @@ export default class Scenograph { } load( sceneName ) { - let scene = false; - - if ( sceneName == 'Overworld' ) { - scene = new Overworld(); - } + let scene = new World( sceneName ); return scene; } diff --git a/client/src/app/scenograph/scenes/base.js b/client/src/app/scenograph/scenes/base.js index ca0cac0e..bf5d0eab 100644 --- a/client/src/app/scenograph/scenes/base.js +++ b/client/src/app/scenograph/scenes/base.js @@ -17,13 +17,6 @@ export default class SceneBase { */ this.camera = false; - /** - * Debug mode. - * - * @memberof Boolean - */ - this.debug = false; - /** * Effects composers and their layers. * @@ -34,15 +27,6 @@ export default class SceneBase { postprocessing: false }; - /** - * Exit sign. - * - * @todo: Consolidate scene rigs. - * - * @memberOf function - */ - this.exitSignClick = false; - /** * Fast mode (bloom off, no shadows) * diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml new file mode 100644 index 00000000..a0302d18 --- /dev/null +++ b/game/src/scenes/overworld.yml @@ -0,0 +1,93 @@ +# Dynamic Objects +actors: + - name: Capy One + model: cargoShip + class: cargoShip + position: + x: -35000 + y: -2000 + z: 10000 + - name: Capy Two + model: cargoShip + class: cargoShip + position: + x: -36000 + y: -1500 + z: 10000 + - name: Capy Three + model: cargoShip + class: cargoShip + position: + x: -34000 + y: -1500 + z: 10000 + - name: Player One + model: valiant + class: player + position: + x: -65000 + y: -35000 + z: 1500 + rotation: + x: 0 + y: -12.56 + z: 0 + - name: Pirate One + model: raven + class: pirate + position: + x: -65000 + y: -35000 + z: 1500 + rotation: + x: 0 + y: -12.56 + z: 0 +# Static Objects +objects: + - name: Einstein Well + model: extractor + position: + x: 0 + y: -70000 + z: 1500 + - name: Newton Well + model: extractor + position: + x: 0 + y: 70000 + z: 1500 + - name: Galileo Well + model: extractor + position: + x: -70000 + y: 0 + z: 1500 + - name: Planck Well + model: extractor + position: + x: 70000 + y: 0 + z: 1500 + - name: Lambda + model: platform + position: + x: -65000 + y: -35000 + z: 1500 + rotation: + x: 0 + y: -12.56 + z: 0 + - name: Refinery 91 + model: refinery + position: + x: 20000 + y: -20000 + z: 10000 + - name: Refinery 92 + model: refinery + position: + x: 20000 + y: -20000 + z: 10000 diff --git a/game/src/world.ts b/game/src/world.ts new file mode 100644 index 00000000..85845095 --- /dev/null +++ b/game/src/world.ts @@ -0,0 +1,28 @@ +/** + * Game World class + * + * Loads and runs simulations of game scenes + * + */ + + +export default class World { + + + constructor( sceneName ) { + if ( sceneName == 'Overworld' ) { + // + /** + * @todo: + * - load abstract scene definition file overworld.yml and parse it + * - loop over config to load game world simulation in here and scenograph in the client + */ + + } + } + + update() { + + } + +} From be94bc2ee58c42808ea41216f11c96a59618b45b Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sat, 4 Oct 2025 07:32:47 +0000 Subject: [PATCH 24/58] #31 - Begun refactoring overworld into a loadable config --- client/build.mjs | 5 +++++ client/package-lock.json | 40 +++++++++++++++++++++++++++++++----- client/package.json | 3 ++- client/src/app/scenograph.js | 6 ++++-- client/watch.mjs | 4 ++++ game/src/world.ts | 39 +++++++++++++++++++++++++++-------- package-lock.json | 6 ++++++ 7 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 package-lock.json diff --git a/client/build.mjs b/client/build.mjs index 524b2e30..ea9132d6 100644 --- a/client/build.mjs +++ b/client/build.mjs @@ -2,6 +2,8 @@ import { fileURLToPath } from 'url'; import { dirname, resolve } from 'path'; import esbuild from 'esbuild'; +import { YAMLPlugin } from 'esbuild-yaml'; + const __filename = fileURLToPath( import.meta.url ); const __dirname = dirname( __filename ); @@ -12,6 +14,9 @@ esbuild bundle: true, minify: false, outdir: '../docs', + plugins: [ + YAMLPlugin() + ], target: 'es2018', alias: { '@': resolve( __dirname, 'src/app' ), diff --git a/client/package-lock.json b/client/package-lock.json index 796af0a8..0a04d75b 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14,8 +14,9 @@ "cross-env": "^7.0.3", "detect-gpu": "^5.0.57", "esbuild": "0.24.0", + "esbuild-yaml": "^3.0.4", "npm-run-all": "^4.1.5", - "postprocessing": "^6.36.5", + "postprocessing": "^6.37.8", "pug": "^3.0.3", "pug-stylus": "^0.0.5", "stylus": "^0.64.0", @@ -1825,6 +1826,22 @@ "@esbuild/win32-x64": "0.24.0" } }, + "node_modules/esbuild-yaml": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/esbuild-yaml/-/esbuild-yaml-3.0.4.tgz", + "integrity": "sha512-PQFVI+CL7gTEmvJk5jjMcltqGEDPSQVrTM2wCz9HC44pdRBZWmNjkLR44N8IO8XZeoxoSk2/r7B2H1LMM3E8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.8.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "esbuild": ">=0.19.0" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -3673,13 +3690,13 @@ } }, "node_modules/postprocessing": { - "version": "6.36.5", - "resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.36.5.tgz", - "integrity": "sha512-/KiVAZaEpmigRofZf2pXOTwtzli3GdzvvdrDjU8wU79ScXQfzwuLoPDglatNGsBctMuJ29SCEZBe0LUZBT3s+A==", + "version": "6.37.8", + "resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.37.8.tgz", + "integrity": "sha512-qTFUKS51z/fuw2U+irz4/TiKJ/0oI70cNtvQG1WxlPKvBdJUfS1CcFswJd5ATY3slotWfvkDDZAsj1X0fU8BOQ==", "dev": true, "license": "Zlib", "peerDependencies": { - "three": ">= 0.157.0 < 0.172.0" + "three": ">= 0.157.0 < 0.181.0" } }, "node_modules/promise": { @@ -4976,6 +4993,19 @@ } } }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/yuka": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/yuka/-/yuka-0.7.8.tgz", diff --git a/client/package.json b/client/package.json index 18167d45..3c06eee9 100644 --- a/client/package.json +++ b/client/package.json @@ -26,8 +26,9 @@ "cross-env": "^7.0.3", "detect-gpu": "^5.0.57", "esbuild": "0.24.0", + "esbuild-yaml": "^3.0.4", "npm-run-all": "^4.1.5", - "postprocessing": "^6.36.5", + "postprocessing": "^6.37.8", "pug": "^3.0.3", "pug-stylus": "^0.0.5", "stylus": "^0.64.0", diff --git a/client/src/app/scenograph.js b/client/src/app/scenograph.js index 289b8b81..93920c57 100644 --- a/client/src/app/scenograph.js +++ b/client/src/app/scenograph.js @@ -33,9 +33,9 @@ import Fast from '@/scenograph/modes/fast.js'; import Multiplayer from "@/scenograph/modes/multiplayer.js"; /** - * Scenes + * World Simulation */ -import Overworld from '@/scenograph/scenes/overworld.js'; +import World from '#/game/src/world'; /** * Scene controllers @@ -146,6 +146,8 @@ export default class Scenograph { load( sceneName ) { let scene = new World( sceneName ); + console.log(this.instance); + return scene; } diff --git a/client/watch.mjs b/client/watch.mjs index 029a4075..2acadbe9 100644 --- a/client/watch.mjs +++ b/client/watch.mjs @@ -2,6 +2,7 @@ import { fileURLToPath } from 'url'; import { dirname, resolve } from 'path'; import esbuild from 'esbuild'; +import { YAMLPlugin } from 'esbuild-yaml'; const __filename = fileURLToPath( import.meta.url ); const __dirname = dirname( __filename ); @@ -13,6 +14,9 @@ const context = await esbuild minify: false, outdir: '../docs', target: 'es2018', + plugins: [ + YAMLPlugin() + ], alias: { '@': resolve( __dirname, 'src/app' ), '#': resolve( __dirname, '..' ), diff --git a/game/src/world.ts b/game/src/world.ts index 85845095..fb56c4f2 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -5,24 +5,45 @@ * */ +import Overworld from "./scenes/overworld.yml"; +interface WorldInstance { + actors: Record; + objects: Record; +} + export default class World { + public instance: WorldInstance; - constructor( sceneName ) { - if ( sceneName == 'Overworld' ) { - // - /** - * @todo: - * - load abstract scene definition file overworld.yml and parse it - * - loop over config to load game world simulation in here and scenograph in the client - */ - + constructor( sceneName: string ) { + if ( sceneName === 'Overworld' ) { + this.initialise( Overworld ); } } + /** + * Initialise game world instance. + * + * Parses config and builds a self updating virtual world simulation. + * + * @todo: + * - load abstract scene definition file overworld.yml and parse it + * - loop over config to load game world simulation in here and scenograph in the client + */ + initialise( config ) { + this.instance = { + actors: config.actors, + objects: config.objects + }; + + // console.log(this.instance); + // debugger; + } + update() { } } + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..c9333b9d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "langenium.com", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} From bdf1aa8d687ae54e68a60ab3f09f365ac3059f5a Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sat, 4 Oct 2025 08:38:15 +0000 Subject: [PATCH 25/58] #31 - Recreating director scene manager class --- client/src/app/main.js | 2 +- client/src/app/scenograph.js | 20 +- client/src/app/scenograph/director.js | 377 +++++++++++++++++++++++ client/src/app/scenograph/scenes/base.js | 209 ------------- 4 files changed, 387 insertions(+), 221 deletions(-) create mode 100644 client/src/app/scenograph/director.js delete mode 100644 client/src/app/scenograph/scenes/base.js diff --git a/client/src/app/main.js b/client/src/app/main.js index 2d5ddd6b..7e0cc293 100644 --- a/client/src/app/main.js +++ b/client/src/app/main.js @@ -31,7 +31,7 @@ l.init = function () { /** * Load up the overworld by default. */ - l.current_scene = l.scenograph.load( "Overworld" ); + l.current_scene = l.scenograph.director.load( "Overworld" ); l.scenograph.init(); } diff --git a/client/src/app/scenograph.js b/client/src/app/scenograph.js index 93920c57..137b765c 100644 --- a/client/src/app/scenograph.js +++ b/client/src/app/scenograph.js @@ -23,20 +23,17 @@ import { calculateAdjustedGapSize } from '@/helpers/math.js'; import Actors from "@/scenograph/actors.js"; import Cameras from "@/scenograph/cameras.js"; import Controls from "@/scenograph/controls.js"; +import Director from "@/scenograph/director.js"; import Effects from "@/scenograph/effects"; import Events from "./scenograph/events"; import Materials from "@/scenograph/materials.js"; import Overlays from "@/scenograph/overlays.js"; + import Debugging from '@/scenograph/modes/debugging.js'; import Fast from '@/scenograph/modes/fast.js'; import Multiplayer from "@/scenograph/modes/multiplayer.js"; -/** - * World Simulation - */ -import World from '#/game/src/world'; - /** * Scene controllers */ @@ -64,6 +61,8 @@ export default class Scenograph { overlays; + sceneManager; + /** * @instance YUKA.EntityManager; */ @@ -117,6 +116,11 @@ export default class Scenograph { */ this.overlays = new Overlays(); + /** + * Scene Manager. + */ + this.director = new Director(); + /** * Setup the different game modes (controllers) */ @@ -144,12 +148,6 @@ export default class Scenograph { } - load( sceneName ) { - let scene = new World( sceneName ); - console.log(this.instance); - - return scene; - } /** * Game 3D initialiser, called by l when it's finished loading. diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js new file mode 100644 index 00000000..c2bc147a --- /dev/null +++ b/client/src/app/scenograph/director.js @@ -0,0 +1,377 @@ +/** + * Director. + * + * Scene Management class. + */ + +/** + * Vendor libs and base class. + */ +import * as THREE from "three"; + +/** + * Internal libs and helpers + */ +import l from '@/helpers/l.js'; + +/** + * World Simulation + */ +import World from '#/game/src/world'; + + +/** + * Scene controllers + */ +import { setupTriggers, updateTriggers } from "@/scenograph/triggers"; +import { + setupTweens, + updateTweens, + startTweening, +} from "@/scenograph/tweens"; + +/** + * Objects + */ + +// Environment +import Ocean from "@/scenograph/objects/environment/ocean"; +import Sky from "@/scenograph/objects/environment/sky"; +import Sky2 from "@/scenograph/objects/environment/sky2"; + +// Structures +import Extractors from "@/scenograph/objects/structures/extractors"; +import Hangar from "@/scenograph/objects/structures/hangar"; +import Platform from "@/scenograph/objects/structures/platform"; +import Refineries from "@/scenograph/objects/structures/refineries"; + +// Vehicles +import CargoShips from "@/scenograph/objects/vehicles/cargo_ships"; +import Person from "@/scenograph/objects/vehicles/person"; +import Raven from "@/scenograph/objects/vehicles/raven"; +import Valiant from "@/scenograph/objects/vehicles/valiant"; + +// Projectiles +import Missile from "@/scenograph/objects/projectiles/missile"; + +/** + * Preloader objects + */ +import { + createDoor, + createOfficeRoom, + doorHeight, + doorWidth, +} from "@/scenograph/objects/structures/office_room"; + + +export default class Director { + constructor() { + + /** + * Animation queue. + */ + this.animation_queue = []; + + /** + * Primary scene camera + * + * @memberof THREE.Camera + */ + this.camera = false; + + /** + * Effects composers and their layers. + * + * @memberof Object { postprocessing.EffectComposer } + */ + this.effects = { + particles: false, + postprocessing: false + }; + + /** + * Fast mode (bloom off, no shadows) + * + * @memberof Boolean + */ + this.fast = true; + + + /** + * World instance. + */ + this.instance = false; + + /** + * Reusable loaders for assets. + */ + this.loaders = { + gltf: false, + object: false, + texture: false, + stats: { + fonts: { + target: 0, // @todo: Check if this affects double loads, shouldn't with caching. + loaded: 0 + }, + gtlf: { + target: 0, // @todo: Check if this affects double loads, shouldn't with caching. + loaded: 0 + }, + screens: { + target: 0, + loaded: 0 + }, + svg: { + target: 1, + loaded: 0 + }, + textures: { + target: 9, + loaded: 0 + } + }, + + /** + * UI controller + */ + ui: false + }; + + /** + * Reusable materials for assets + */ + this.materials = false; + + /** + * Camera is being moved by tweening. + * + * @memberof Boolean + */ + this.moving = false; + + /** + * Current position of the users pointer. + * + * @memberof THREE.Vector2 + */ + this.pointer = false; + + /** + * Raycaster that projects into the scene from the users pointer and picks up collisions for interaction. + * + * @memberof THREE.Raycaster + */ + this.raycaster = false; + + /** + * Ready to begin. + */ + + this.ready = false; + + /** + * Renderers that create the scene. + * + * @memberof Object { THREE.Renderer , ... } + */ + this.renderers = { + webgl: false + }; + + /** + * Settings that controls the scene. + * + * @memberof Object + */ + this.settings = { + adjusted_gap: false, // calculated value + game_controls: false, // show in-game control overlays. + gap: 1.3, // depth(z axis) gap between desks + light: { + fast: { + desk: { + normal: 0.015, active: 0.05 + }, + neonSign: { + normal: 0.35, active: 0.05 + } + }, + highP: { + desk: { + normal: 0.015, active: 0.035 + }, + neonSign: { + normal: 0.1, active: 0.05 + } + } + + }, + room_depth: false, // calculated value + scale: 11, // do not change, braeks css screen sizes + startPosZ: - 10 // updated responsive eugene levy + }; + + + /** + * Currently selected object. + * + * @memberof THREE.Object3d + */ + this.selected = false; + + /** + * Skip the intro sequence for this scene. + */ + this.skipintro = false; + + /** + * If the main sequence has begun. + * + * @memberof Boolean + */ + this.started = false; + + /** + * Custom array of game performance stats. + */ + this.stats = { + /** + * Frames Per Second (FPS) + * + * @memberof Integer + */ + currentTime: performance.now(), + fps: 0, + frameCount: 0, + lastTime: performance.now(), + }; + + /** + * All scene triggers. + * + * @memberof Object + */ + this.triggers = {}; + + /** + * All scene tweens. + * + * @memberof Object + */ + this.tweens = {}; + } + + // Load world instance from game classes. + load( sceneName ) { + this.instance = new World( sceneName ); + + return this; + } + + // Load the objects in world instance to the current scene. + async setup() { + this.setupSceneDefaults(); + + this.finishSetup(); + } + + async setupSceneDefaults() { + + /** + * Tracked meshes and mesh groups that compose the scene. + * + * @memberof Object + */ + l.current_scene.objects = {}; + + /** + * The main scene container. + * + * @memberof THREE.Scene + */ + l.current_scene.scene = new THREE.Scene(); + l.current_scene.scene.visible = false; + + l.scenograph.effects.init(); + + l.current_scene.objects.projectiles = { + missile: new Missile() + }; + await l.current_scene.objects.projectiles.missile.load(); + l.current_scene.animation_queue.push( + l.current_scene.objects.projectiles.missile.animate + ); + + l.current_scene.objects.door = await createDoor(); + l.current_scene.objects.door.position.set( + -doorWidth / 2, + -5 + doorHeight / 2, + -15 + l.current_scene.room_depth / 2 + ); + l.current_scene.scene.add( l.current_scene.objects.door ); + + // Setup skybox + l.current_scene.objects.sky = new Sky(); + l.current_scene.scene.add( + l.current_scene.objects.sky.mesh + ); + l.current_scene.animation_queue.push( + l.current_scene.objects.sky.animate + ); + + // Setup ocean + //l.current_scene.objects.ocean = new Ocean( extractors.extractorLocations ); + l.current_scene.objects.ocean = new Ocean( [] ); + l.current_scene.scene.add( + l.current_scene.objects.ocean.water + ); + l.current_scene.animation_queue.push( + l.current_scene.objects.ocean.animate + ); + + // Adjust ambient light intensity + l.current_scene.objects.ambientLight = new THREE.AmbientLight( + l.config.settings.fast ? 0x555555 : 0x444444 + ); // Dim ambient light color + l.current_scene.objects.ambientLight.name = 'Main Light'; + l.current_scene.objects.ambientLight.intensity = Math.PI; + l.current_scene.scene.add( + l.current_scene.objects.ambientLight + ); + + l.current_scene.objects.screens_loaded = 0; + l.current_scene.objects.room = await createOfficeRoom(); + l.current_scene.scene.add( l.current_scene.objects.room ); + + + // Setup triggers + setupTriggers(); + + // Setup Tweens. + setupTweens(); + + + } + + finishSetup() { + // Check if we've finished loading. + let bootWaiter = setInterval( () => { + if ( + // Check door sign is loaded up. + l.current_scene.objects.door_sign + ) { + l.current_scene.ready = true; + clearTimeout( bootWaiter ); + + // Start tweens. + startTweening(); + + requestAnimationFrame( l.scenograph.animate ); + } + }, 100 ); + } + + +} diff --git a/client/src/app/scenograph/scenes/base.js b/client/src/app/scenograph/scenes/base.js deleted file mode 100644 index bf5d0eab..00000000 --- a/client/src/app/scenograph/scenes/base.js +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Scene base - */ - -export default class SceneBase { - constructor() { - - /** - * Animation queue. - */ - this.animation_queue = []; - - /** - * Primary scene camera - * - * @memberof THREE.Camera - */ - this.camera = false; - - /** - * Effects composers and their layers. - * - * @memberof Object { postprocessing.EffectComposer } - */ - this.effects = { - particles: false, - postprocessing: false - }; - - /** - * Fast mode (bloom off, no shadows) - * - * @memberof Boolean - */ - this.fast = true; - - /** - * Reusable loaders for assets. - */ - this.loaders = { - gltf: false, - object: false, - texture: false, - stats: { - fonts: { - target: 0, // @todo: Check if this affects double loads, shouldn't with caching. - loaded: 0 - }, - gtlf: { - target: 0, // @todo: Check if this affects double loads, shouldn't with caching. - loaded: 0 - }, - screens: { - target: 0, - loaded: 0 - }, - svg: { - target: 1, - loaded: 0 - }, - textures: { - target: 9, - loaded: 0 - } - }, - - /** - * UI controller - */ - ui: false - }; - - /** - * Reusable materials for assets - */ - this.materials = false; - - /** - * Camera is being moved by tweening. - * - * @memberof Boolean - */ - this.moving = false; - - /** - * Current position of the users pointer. - * - * @memberof THREE.Vector2 - */ - this.pointer = false; - - /** - * Raycaster that projects into the scene from the users pointer and picks up collisions for interaction. - * - * @memberof THREE.Raycaster - */ - this.raycaster = false; - - /** - * Ready to begin. - */ - - this.ready = false; - - /** - * Renderers that create the scene. - * - * @memberof Object { THREE.Renderer , ... } - */ - this.renderers = { - webgl: false - }; - - /** - * Settings that controls the scene. - * - * @memberof Object - */ - this.settings = { - adjusted_gap: false, // calculated value - game_controls: false, // show in-game control overlays. - gap: 1.3, // depth(z axis) gap between desks - light: { - fast: { - desk: { - normal: 0.015, active: 0.05 - }, - neonSign: { - normal: 0.35, active: 0.05 - } - }, - highP: { - desk: { - normal: 0.015, active: 0.035 - }, - neonSign: { - normal: 0.1, active: 0.05 - } - } - - }, - room_depth: false, // calculated value - scale: 11, // do not change, braeks css screen sizes - startPosZ: - 10 // updated responsive eugene levy - }; - - /** - * The main scene container. - * - * @memberof THREE.Scene - */ - this.scene = false; - - /** - * Tracked meshes and mesh groups that compose the scene. - * - * @memberof Object - */ - this.objects = { }; - - /** - * Currently selected object. - * - * @memberof THREE.Object3d - */ - this.selected = false; - - /** - * Skip the intro sequence for this scene. - */ - this.skipintro = false; - - /** - * If the main sequence has begun. - * - * @memberof Boolean - */ - this.started = false; - - /** - * Custom array of game performance stats. - */ - this.stats = { - /** - * Frames Per Second (FPS) - * - * @memberof Integer - */ - currentTime: performance.now(), - fps: 0, - frameCount: 0, - lastTime: performance.now(), - }; - - /** - * All scene triggers. - * - * @memberof Object - */ - this.triggers = {}; - - /** - * All scene tweens. - * - * @memberof Object - */ - this.tweens = {}; - } -} From fa7cbee88344f2fb5ff982dfed44af834e3305b1 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 5 Oct 2025 18:20:37 +0000 Subject: [PATCH 26/58] #31 - Moving object factories to new global scenograph objects class --- client/src/app/scenograph.js | 8 ++ client/src/app/scenograph/director.js | 82 +++++++++--------- client/src/app/scenograph/objects.js | 84 +++++++++++++++++++ .../{extractors.js => extractor.js} | 0 .../{cargo_ships.js => cargo_ship.js} | 2 +- 5 files changed, 136 insertions(+), 40 deletions(-) create mode 100644 client/src/app/scenograph/objects.js rename client/src/app/scenograph/objects/structures/{extractors.js => extractor.js} (100%) rename client/src/app/scenograph/objects/vehicles/{cargo_ships.js => cargo_ship.js} (99%) diff --git a/client/src/app/scenograph.js b/client/src/app/scenograph.js index 137b765c..bf6a1bd2 100644 --- a/client/src/app/scenograph.js +++ b/client/src/app/scenograph.js @@ -27,6 +27,7 @@ import Director from "@/scenograph/director.js"; import Effects from "@/scenograph/effects"; import Events from "./scenograph/events"; import Materials from "@/scenograph/materials.js"; +import Objects from "@/scenograph/objects"; import Overlays from "@/scenograph/overlays.js"; @@ -59,6 +60,8 @@ export default class Scenograph { modes; + objects; + overlays; sceneManager; @@ -111,6 +114,11 @@ export default class Scenograph { */ this.materials = new Materials(); + /** + * Objects. + */ + this.objects = new Objects(); + /** * Overlays. */ diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index c2bc147a..b6bb634e 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -19,7 +19,6 @@ import l from '@/helpers/l.js'; */ import World from '#/game/src/world'; - /** * Scene controllers */ @@ -30,39 +29,6 @@ import { startTweening, } from "@/scenograph/tweens"; -/** - * Objects - */ - -// Environment -import Ocean from "@/scenograph/objects/environment/ocean"; -import Sky from "@/scenograph/objects/environment/sky"; -import Sky2 from "@/scenograph/objects/environment/sky2"; - -// Structures -import Extractors from "@/scenograph/objects/structures/extractors"; -import Hangar from "@/scenograph/objects/structures/hangar"; -import Platform from "@/scenograph/objects/structures/platform"; -import Refineries from "@/scenograph/objects/structures/refineries"; - -// Vehicles -import CargoShips from "@/scenograph/objects/vehicles/cargo_ships"; -import Person from "@/scenograph/objects/vehicles/person"; -import Raven from "@/scenograph/objects/vehicles/raven"; -import Valiant from "@/scenograph/objects/vehicles/valiant"; - -// Projectiles -import Missile from "@/scenograph/objects/projectiles/missile"; - -/** - * Preloader objects - */ -import { - createDoor, - createOfficeRoom, - doorHeight, - doorWidth, -} from "@/scenograph/objects/structures/office_room"; export default class Director { @@ -99,9 +65,9 @@ export default class Director { /** - * World instance. + * Game world simulation. */ - this.instance = false; + this.world = false; /** * Reusable loaders for assets. @@ -265,7 +231,7 @@ export default class Director { // Load world instance from game classes. load( sceneName ) { - this.instance = new World( sceneName ); + this.world = new World( sceneName ); return this; } @@ -273,10 +239,43 @@ export default class Director { // Load the objects in world instance to the current scene. async setup() { this.setupSceneDefaults(); + + this.loadInstance(); this.finishSetup(); } + async loadInstance() { + this.world.instance.actors.forEach( actor => { + if ( actor.class == 'cargoShip' ) { + + } + console.log(actor); + } ); + + debugger; + + // Setup Player aircraft, used for the intro sequence. + l.scenograph.actors.player.vehicle = new Valiant(); + await l.scenograph.actors.player.vehicle.load(); + l.current_scene.scene.add( + l.scenograph.actors.player.vehicle.mesh + ); + l.current_scene.animation_queue.push( + l.scenograph.actors.player.vehicle.animate + ); + + // Setup Player person, used for the hangar scene. + l.scenograph.actors.player.person = new Person(); + l.current_scene.scene.add( + l.scenograph.actors.player.person.mesh + ); + l.current_scene.animation_queue.push( + l.scenograph.actors.player.person.animate + ); + + } + async setupSceneDefaults() { /** @@ -313,7 +312,7 @@ export default class Director { l.current_scene.scene.add( l.current_scene.objects.door ); // Setup skybox - l.current_scene.objects.sky = new Sky(); + l.current_scene.objects.sky = new l.scenograph.objects.environment.sky(); l.current_scene.scene.add( l.current_scene.objects.sky.mesh ); @@ -323,7 +322,12 @@ export default class Director { // Setup ocean //l.current_scene.objects.ocean = new Ocean( extractors.extractorLocations ); - l.current_scene.objects.ocean = new Ocean( [] ); + l.current_scene.objects.ocean = new l.scenograph.objects.environment.ocean( [ + //new THREE.Vector3( 0, -500, this.size * 10 ), // Test ship + new THREE.Vector3( -35000, -2000, 10000 ), + new THREE.Vector3( -36000, -1500, 10000 ), + new THREE.Vector3( -34000, -1500, 10000 ), + ] ); l.current_scene.scene.add( l.current_scene.objects.ocean.water ); diff --git a/client/src/app/scenograph/objects.js b/client/src/app/scenograph/objects.js new file mode 100644 index 00000000..03654078 --- /dev/null +++ b/client/src/app/scenograph/objects.js @@ -0,0 +1,84 @@ +/** + * Objects. + * + * Composes scene meshes from gltf and procedural code. + */ + +/** + * Vendor libs and base class. + */ +import * as THREE from "three"; + +/** + * Internal libs and helpers + */ +import l from '@/helpers/l.js'; + + +/** + * Objects + */ + +// Environment +import Ocean from "@/scenograph/objects/environment/ocean"; +import Sky from "@/scenograph/objects/environment/sky"; +import Sky2 from "@/scenograph/objects/environment/sky2"; + +/** + * Preloader objects + */ +import { createDoor, createOfficeRoom, doorHeight, doorWidth } from "@/scenograph/objects/structures/office_room"; + +// Projectiles +import Missile from "@/scenograph/objects/projectiles/missile"; + +// Structures +import Extractor from "@/scenograph/objects/structures/extractor"; +import Hangar from "@/scenograph/objects/structures/hangar"; +import Platform from "@/scenograph/objects/structures/platform"; +import Refineries from "@/scenograph/objects/structures/refineries"; + +// Vehicles +import CargoShip from "@/scenograph/objects/vehicles/cargo_ship"; +import Person from "@/scenograph/objects/vehicles/person"; +import Raven from "@/scenograph/objects/vehicles/raven"; +import Valiant from "@/scenograph/objects/vehicles/valiant"; + +export default class Objects { + + this.environment = false; + this.preloader = false; + this.projectiles = false; + this.structures = false; + this.vehicles = false; + + constructor() { + this.environment = { + ocean: Ocean, + sky: Sky, + }; + this.preloader = { + createDoor: createDoor, + createOfficeRoom: createOfficeRoom, + doorHeight: doorHeight, + doorWidth: doorWidth, + }; + this.projectiles = { + missile: Missile + }; + this.structures = { + extractor: Extractor, + hangar: Hangar, + platform: Platform, + refineries: Refineries, + }; + this.vehicles = { + cargoShip: CargoShip, + person: Person, + raven: Raven, + valiant: Valiant, + }; + + } + +} diff --git a/client/src/app/scenograph/objects/structures/extractors.js b/client/src/app/scenograph/objects/structures/extractor.js similarity index 100% rename from client/src/app/scenograph/objects/structures/extractors.js rename to client/src/app/scenograph/objects/structures/extractor.js diff --git a/client/src/app/scenograph/objects/vehicles/cargo_ships.js b/client/src/app/scenograph/objects/vehicles/cargo_ship.js similarity index 99% rename from client/src/app/scenograph/objects/vehicles/cargo_ships.js rename to client/src/app/scenograph/objects/vehicles/cargo_ship.js index d5029756..de3458b0 100644 --- a/client/src/app/scenograph/objects/vehicles/cargo_ships.js +++ b/client/src/app/scenograph/objects/vehicles/cargo_ship.js @@ -16,7 +16,7 @@ import { proceduralMetalMaterial } from '@/scenograph/materials.js'; import { SUBTRACTION, Brush, Evaluator } from 'three-bvh-csg'; import cargoShip from '../../../../../../game/src/actors/cargoShip'; -export default class CargoShips { +export default class CargoShip { // THREE.Mesh clones instances; From 849534fe2732312fbf50c2f11549a63b1838c6da Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 6 Oct 2025 19:01:19 +0000 Subject: [PATCH 27/58] #31 - Implementing extractors in dynamic scene loader --- client/src/app/scenograph.js | 4 ++ client/src/app/scenograph/director.js | 59 ++++++++++++----- client/src/app/scenograph/objects.js | 40 +++++++++-- .../objects/structures/extractor.js | 66 ++++++++++++------- .../scenograph/objects/vehicles/cargo_ship.js | 29 ++++++++ game/src/scenes/overworld.yml | 16 ++--- 6 files changed, 158 insertions(+), 56 deletions(-) diff --git a/client/src/app/scenograph.js b/client/src/app/scenograph.js index bf6a1bd2..80ec2df6 100644 --- a/client/src/app/scenograph.js +++ b/client/src/app/scenograph.js @@ -186,6 +186,9 @@ export default class Scenograph { // Reusable raycaster for tracking what the user tried to hit. l.current_scene.raycaster = new THREE.Raycaster(); + // Load all object classes. + await this.objects.init(); + // Scene Setup. l.current_scene.setup(); @@ -198,6 +201,7 @@ export default class Scenograph { // Activate Event listeners. this.events.init(); + }; async checkGPUTier() { diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index b6bb634e..a87bbb25 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -246,17 +246,26 @@ export default class Director { } async loadInstance() { - this.world.instance.actors.forEach( actor => { - if ( actor.class == 'cargoShip' ) { - + this.world.instance.objects.forEach( async object => { + if ( object.model == 'extractor' ) { + l.scenograph.director.loadObject( + object, + await l.scenograph.objects.structures.extractor.get() + ); } - console.log(actor); } ); - debugger; + // this.world.instance.actors.forEach( actor => { + // if ( actor.class == 'cargoShip' ) { + + // } + // console.log(actor); + // } ); + + //debugger; // Setup Player aircraft, used for the intro sequence. - l.scenograph.actors.player.vehicle = new Valiant(); + l.scenograph.actors.player.vehicle = new l.scenograph.objects.vehicles.valiant(); await l.scenograph.actors.player.vehicle.load(); l.current_scene.scene.add( l.scenograph.actors.player.vehicle.mesh @@ -266,7 +275,7 @@ export default class Director { ); // Setup Player person, used for the hangar scene. - l.scenograph.actors.player.person = new Person(); + l.scenograph.actors.player.person = new l.scenograph.objects.vehicles.person(); l.current_scene.scene.add( l.scenograph.actors.player.person.mesh ); @@ -276,6 +285,27 @@ export default class Director { } + async loadObject( config, object ) { + object.position.x = config.position.x; + object.position.y = config.position.y; + object.position.z = config.position.z; + + object.name = config.name; + + // @todo: add to current_scene array relevant to object class. + + l.current_scene.scene.add( + object + ); + } + + /** + * @todo: Make this dynamic and not hard codo + */ + async temp_addPlayer() { + + } + async setupSceneDefaults() { /** @@ -295,18 +325,11 @@ export default class Director { l.scenograph.effects.init(); - l.current_scene.objects.projectiles = { - missile: new Missile() - }; - await l.current_scene.objects.projectiles.missile.load(); - l.current_scene.animation_queue.push( - l.current_scene.objects.projectiles.missile.animate - ); - l.current_scene.objects.door = await createDoor(); + l.current_scene.objects.door = await l.scenograph.objects.preloader.createDoor(); l.current_scene.objects.door.position.set( - -doorWidth / 2, - -5 + doorHeight / 2, + -l.scenograph.objects.preloader.doorWidth / 2, + -5 + l.scenograph.objects.preloader.doorHeight / 2, -15 + l.current_scene.room_depth / 2 ); l.current_scene.scene.add( l.current_scene.objects.door ); @@ -346,7 +369,7 @@ export default class Director { ); l.current_scene.objects.screens_loaded = 0; - l.current_scene.objects.room = await createOfficeRoom(); + l.current_scene.objects.room = await l.scenograph.objects.preloader.createOfficeRoom(); l.current_scene.scene.add( l.current_scene.objects.room ); diff --git a/client/src/app/scenograph/objects.js b/client/src/app/scenograph/objects.js index 03654078..8af58461 100644 --- a/client/src/app/scenograph/objects.js +++ b/client/src/app/scenograph/objects.js @@ -46,11 +46,11 @@ import Valiant from "@/scenograph/objects/vehicles/valiant"; export default class Objects { - this.environment = false; - this.preloader = false; - this.projectiles = false; - this.structures = false; - this.vehicles = false; + environment = false; + preloader = false; + projectiles = false; + structures = false; + vehicles = false; constructor() { this.environment = { @@ -64,10 +64,10 @@ export default class Objects { doorWidth: doorWidth, }; this.projectiles = { - missile: Missile + missile: new Missile() }; this.structures = { - extractor: Extractor, + extractor: new Extractor(), hangar: Hangar, platform: Platform, refineries: Refineries, @@ -81,4 +81,30 @@ export default class Objects { } + /** + * @todo: Conditionally switching off loading some objects on scenes where not needed. + */ + async init () { + await this.structures.extractor.load(); + await this.projectiles.missile.load(); + //await this.vehicles.cargoShip.load(); + console.log("Objects loaded"); + } + + /** + * Animate hook. + * + * This method is called within the main animation loop and + * therefore must only reference global objects or properties. + * + * @method animate + * @memberof Objects + * @global + * @note All references within this method should be globally accessible. + **/ + animate( currentTime ) { + l.scenograph.objects.structures.extractors.animate( currentTime ); + l.scenograph.objects.projectiles.missile.animate( currentTime ); + } + } diff --git a/client/src/app/scenograph/objects/structures/extractor.js b/client/src/app/scenograph/objects/structures/extractor.js index 8fb0695c..9bba1cf4 100644 --- a/client/src/app/scenograph/objects/structures/extractor.js +++ b/client/src/app/scenograph/objects/structures/extractor.js @@ -16,6 +16,10 @@ import { proceduralBuilding, proceduralMetalMaterial2 } from '@/scenograph/mater export default class Extractor { + instances; + + locations; + // Array of three.vector3's defining X/Z coordinates and radius of where extractors are in the ocean. extractorLocations; @@ -26,39 +30,41 @@ export default class Extractor { size; constructor() { + this.instances = []; + this.locations = []; this.ready = false; this.size = 150; - // Setup extractors - this.extractorLocations = [ - //new THREE.Vector3( 0, 0, this.size * 10 ), // Test extractor. - new THREE.Vector3( 0, -70000, this.size * 10 ), - new THREE.Vector3( 0, 70000, this.size * 10 ), - new THREE.Vector3( -70000, 0, this.size * 10 ), - new THREE.Vector3( 70000, 0, this.size * 10 ), - ]; + // // Setup extractors + // this.extractorLocations = [ + // //new THREE.Vector3( 0, 0, this.size * 10 ), // Test extractor. + // new THREE.Vector3( 0, -70000, this.size * 10 ), + // new THREE.Vector3( 0, 70000, this.size * 10 ), + // new THREE.Vector3( -70000, 0, this.size * 10 ), + // new THREE.Vector3( 70000, 0, this.size * 10 ), + // ]; } - async getAll() { - let extractors = []; + // async getAll() { + // let extractors = []; - await this.load(); + // await this.load(); - this.extractorLocations.forEach( async ( extractor_location, i ) => { - let extractor = this.mesh.clone(); + // this.extractorLocations.forEach( async ( extractor_location, i ) => { + // let extractor = this.mesh.clone(); - extractor.rotation.y = Math.PI / 8; - extractor.position.x = extractor_location.x; - extractor.position.y = -7450; - extractor.position.z = extractor_location.y; + // extractor.rotation.y = Math.PI / 8; + // extractor.position.x = extractor_location.x; + // extractor.position.y = -7450; + // extractor.position.z = extractor_location.y; - extractor.name = 'Extractor #' + ( i + 1 ); + // extractor.name = 'Extractor #' + ( i + 1 ); - extractors.push( extractor ); - } ); + // extractors.push( extractor ); + // } ); - return extractors; - } + // return extractors; + // } async load() { @@ -128,6 +134,20 @@ export default class Extractor { } + async get() { + let extractor = this.mesh.clone(); + + extractor.rotation.y = Math.PI / 8; + + let i = this.instances.length; + + extractor.name = 'Extractor #' + ( i + 1 ); + + this.instances.push( extractor ); + + return extractor; + } + getPhatTank() { const geometry = new THREE.SphereGeometry( 3, 32, 16 ); // const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); @@ -199,7 +219,7 @@ export default class Extractor { **/ animate( currentTime ) { - l.current_scene.objects.extractors.forEach( ( extractor, i ) => { + l.scenograph.objects.extractor.instances.forEach( ( extractor, i ) => { let inner = extractor.getObjectByName( 'inner' ); let outer = extractor.getObjectByName( 'outer' ); inner.material.uniforms.time.value += 0.0000025; diff --git a/client/src/app/scenograph/objects/vehicles/cargo_ship.js b/client/src/app/scenograph/objects/vehicles/cargo_ship.js index de3458b0..27218ffb 100644 --- a/client/src/app/scenograph/objects/vehicles/cargo_ship.js +++ b/client/src/app/scenograph/objects/vehicles/cargo_ship.js @@ -136,6 +136,35 @@ export default class CargoShip { } + async get() { + let mesh = this.mesh.clone(); + mesh.userData.path = this.getPath(); + + let i = this.instances.length; + + // Bump each starting point for the cargo ships + for ( let j = 0; j < i; j++) { + mesh.userData.path.advance(); + } + + mesh.position.copy( mesh.userData.path.current() ); + mesh.name = 'Cargo Ship #' + ( i + 1 ); + + mesh.userData.objectClass = 'cargoShip'; + mesh.userData.targetable = true; + mesh.userData.size = this.size; + mesh.userData.actor = new cargoShip( mesh, l.current_scene.scene ); + + l.scenograph.entityManager.add( mesh.userData.actor.entity ); + + mesh.matrixAutoUpdate = false; + + this.instances.push( mesh ); + + return mesh; + } + + async load() { //const material = new THREE.MeshBasicMaterial( {color: 0xff0000, transparent: true, opacity: 1.0, side: THREE.DoubleSide} ); diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml index a0302d18..b43b601e 100644 --- a/game/src/scenes/overworld.yml +++ b/game/src/scenes/overworld.yml @@ -49,26 +49,26 @@ objects: model: extractor position: x: 0 - y: -70000 - z: 1500 + y: -7450 + z: -70000 - name: Newton Well model: extractor position: x: 0 - y: 70000 - z: 1500 + y: -7450 + z: 70000 - name: Galileo Well model: extractor position: x: -70000 - y: 0 - z: 1500 + y: -7450 + z: 0 - name: Planck Well model: extractor position: x: 70000 - y: 0 - z: 1500 + y: -7450 + z: 0 - name: Lambda model: platform position: From ecfefd8aa1a4e0ab036c96c77eed93fd92d6e7a7 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 6 Oct 2025 19:35:41 +0000 Subject: [PATCH 28/58] #31 - Adding Lamda city platform to dynamic loader --- client/src/app/scenograph/director.js | 14 ++++++++++---- client/src/app/scenograph/objects.js | 8 +++++--- .../app/scenograph/objects/structures/platform.js | 6 ++++++ game/src/scenes/overworld.yml | 4 ++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index a87bbb25..d56f755a 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -248,10 +248,16 @@ export default class Director { async loadInstance() { this.world.instance.objects.forEach( async object => { if ( object.model == 'extractor' ) { - l.scenograph.director.loadObject( - object, - await l.scenograph.objects.structures.extractor.get() - ); + l.scenograph.director.loadObject( + object, + await l.scenograph.objects.structures.extractor.get() + ); + } + if ( object.model == 'platform' ) { + l.scenograph.director.loadObject( + object, + await l.scenograph.objects.structures.platform.get() + ); } } ); diff --git a/client/src/app/scenograph/objects.js b/client/src/app/scenograph/objects.js index 8af58461..d4e0b243 100644 --- a/client/src/app/scenograph/objects.js +++ b/client/src/app/scenograph/objects.js @@ -68,8 +68,8 @@ export default class Objects { }; this.structures = { extractor: new Extractor(), - hangar: Hangar, - platform: Platform, + hangar: new Hangar(), + platform: new Platform(), refineries: Refineries, }; this.vehicles = { @@ -86,6 +86,7 @@ export default class Objects { */ async init () { await this.structures.extractor.load(); + await this.structures.platform.load(); await this.projectiles.missile.load(); //await this.vehicles.cargoShip.load(); console.log("Objects loaded"); @@ -103,7 +104,8 @@ export default class Objects { * @note All references within this method should be globally accessible. **/ animate( currentTime ) { - l.scenograph.objects.structures.extractors.animate( currentTime ); + l.scenograph.objects.structures.extractor.animate( currentTime ); + l.scenograph.objects.structures.platform.animate( currentTime ); l.scenograph.objects.projectiles.missile.animate( currentTime ); } diff --git a/client/src/app/scenograph/objects/structures/platform.js b/client/src/app/scenograph/objects/structures/platform.js index 01ad77e1..a0622203 100644 --- a/client/src/app/scenograph/objects/structures/platform.js +++ b/client/src/app/scenograph/objects/structures/platform.js @@ -111,6 +111,12 @@ export default class Platform { this.ready = true; } + async get() { + let platform = this.mesh.clone(); + + return platform; + } + /** * Animate hook. * diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml index b43b601e..625eee02 100644 --- a/game/src/scenes/overworld.yml +++ b/game/src/scenes/overworld.yml @@ -73,8 +73,8 @@ objects: model: platform position: x: -65000 - y: -35000 - z: 1500 + y: 1500 + z: -35000 rotation: x: 0 y: -12.56 From b8457c453861d2aee96856170486d86479760fb2 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 6 Oct 2025 20:58:18 +0000 Subject: [PATCH 29/58] #31 - Object rotation in loader --- client/src/app/scenograph/director.js | 6 ++++++ game/src/scenes/overworld.yml | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index d56f755a..e1755fa9 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -296,6 +296,12 @@ export default class Director { object.position.y = config.position.y; object.position.z = config.position.z; + if ( config.rotation ) { + object.rotation.x = config.rotation.x; + object.rotation.y = config.rotation.y; + object.rotation.z = config.rotation.z; + } + object.name = config.name; // @todo: add to current_scene array relevant to object class. diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml index 625eee02..17b073a4 100644 --- a/game/src/scenes/overworld.yml +++ b/game/src/scenes/overworld.yml @@ -72,9 +72,9 @@ objects: - name: Lambda model: platform position: - x: -65000 + x: -35000 y: 1500 - z: -35000 + z: -65000 rotation: x: 0 y: -12.56 From 23b987bea19b9de6f77da7903dcd3931fdac8233 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 6 Oct 2025 21:09:15 +0000 Subject: [PATCH 30/58] #31 - Correcting manual extractor locations --- client/src/app/scenograph/director.js | 9 +++------ .../app/scenograph/objects/structures/extractor.js | 14 +++++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index e1755fa9..c4a0ad94 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -357,12 +357,9 @@ export default class Director { // Setup ocean //l.current_scene.objects.ocean = new Ocean( extractors.extractorLocations ); - l.current_scene.objects.ocean = new l.scenograph.objects.environment.ocean( [ - //new THREE.Vector3( 0, -500, this.size * 10 ), // Test ship - new THREE.Vector3( -35000, -2000, 10000 ), - new THREE.Vector3( -36000, -1500, 10000 ), - new THREE.Vector3( -34000, -1500, 10000 ), - ] ); + l.current_scene.objects.ocean = new l.scenograph.objects.environment.ocean( + l.scenograph.objects.structures.extractor.extractorLocations + ); l.current_scene.scene.add( l.current_scene.objects.ocean.water ); diff --git a/client/src/app/scenograph/objects/structures/extractor.js b/client/src/app/scenograph/objects/structures/extractor.js index 9bba1cf4..aab91ce5 100644 --- a/client/src/app/scenograph/objects/structures/extractor.js +++ b/client/src/app/scenograph/objects/structures/extractor.js @@ -36,13 +36,13 @@ export default class Extractor { this.size = 150; // // Setup extractors - // this.extractorLocations = [ - // //new THREE.Vector3( 0, 0, this.size * 10 ), // Test extractor. - // new THREE.Vector3( 0, -70000, this.size * 10 ), - // new THREE.Vector3( 0, 70000, this.size * 10 ), - // new THREE.Vector3( -70000, 0, this.size * 10 ), - // new THREE.Vector3( 70000, 0, this.size * 10 ), - // ]; + this.extractorLocations = [ + //new THREE.Vector3( 0, 0, this.size * 10 ), // Test extractor. + new THREE.Vector3( 0, -70000, this.size * 10 ), + new THREE.Vector3( 0, 70000, this.size * 10 ), + new THREE.Vector3( -70000, 0, this.size * 10 ), + new THREE.Vector3( 70000, 0, this.size * 10 ), + ]; } // async getAll() { From df844494d825270af1bb46eb96fd0f4a20117723 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Thu, 9 Oct 2025 12:55:07 +0000 Subject: [PATCH 31/58] #31 - Adding refineries from scene config --- client/src/app/scenograph/director.js | 6 ++++++ client/src/app/scenograph/objects.js | 6 ++++-- .../structures/{refineries.js => refinery.js} | 18 ++++++++++++++++-- game/src/scenes/overworld.yml | 10 +++++----- 4 files changed, 31 insertions(+), 9 deletions(-) rename client/src/app/scenograph/objects/structures/{refineries.js => refinery.js} (94%) diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index c4a0ad94..27c3ff2b 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -259,6 +259,12 @@ export default class Director { await l.scenograph.objects.structures.platform.get() ); } + if ( object.model == 'refinery' ) { + l.scenograph.director.loadObject( + object, + await l.scenograph.objects.structures.refinery.get() + ); + } } ); // this.world.instance.actors.forEach( actor => { diff --git a/client/src/app/scenograph/objects.js b/client/src/app/scenograph/objects.js index d4e0b243..6ca92268 100644 --- a/client/src/app/scenograph/objects.js +++ b/client/src/app/scenograph/objects.js @@ -36,7 +36,7 @@ import Missile from "@/scenograph/objects/projectiles/missile"; import Extractor from "@/scenograph/objects/structures/extractor"; import Hangar from "@/scenograph/objects/structures/hangar"; import Platform from "@/scenograph/objects/structures/platform"; -import Refineries from "@/scenograph/objects/structures/refineries"; +import Refinery from "@/scenograph/objects/structures/refinery"; // Vehicles import CargoShip from "@/scenograph/objects/vehicles/cargo_ship"; @@ -70,7 +70,7 @@ export default class Objects { extractor: new Extractor(), hangar: new Hangar(), platform: new Platform(), - refineries: Refineries, + refinery: new Refinery(), }; this.vehicles = { cargoShip: CargoShip, @@ -87,6 +87,7 @@ export default class Objects { async init () { await this.structures.extractor.load(); await this.structures.platform.load(); + await this.structures.refinery.load(); await this.projectiles.missile.load(); //await this.vehicles.cargoShip.load(); console.log("Objects loaded"); @@ -106,6 +107,7 @@ export default class Objects { animate( currentTime ) { l.scenograph.objects.structures.extractor.animate( currentTime ); l.scenograph.objects.structures.platform.animate( currentTime ); + l.scenograph.objects.structures.refinery.animate( currentTime ); l.scenograph.objects.projectiles.missile.animate( currentTime ); } diff --git a/client/src/app/scenograph/objects/structures/refineries.js b/client/src/app/scenograph/objects/structures/refinery.js similarity index 94% rename from client/src/app/scenograph/objects/structures/refineries.js rename to client/src/app/scenograph/objects/structures/refinery.js index 16082e1c..6eb2765b 100644 --- a/client/src/app/scenograph/objects/structures/refineries.js +++ b/client/src/app/scenograph/objects/structures/refinery.js @@ -10,7 +10,7 @@ import l from '@/helpers/l.js'; import { proceduralMetalMaterial2, proceduralBuilding } from '@/scenograph/materials.js'; import { Brush, Evaluator, INTERSECTION } from 'three-bvh-csg'; -export default class Refineries { +export default class Refinery { // Array of three.vector3's defining X/Z coordinates and radius of where extractors are in the ocean. locations; @@ -22,6 +22,7 @@ export default class Refineries { size; constructor() { + this.instances = []; this.ready = false; this.size = 1000; @@ -42,7 +43,7 @@ export default class Refineries { let mesh = this.mesh.clone(); mesh.position.x = location.x; - mesh.position.y = this.size / 6.5; + mesh.position.z = location.y; mesh.name = 'Refinery #' + ( i + 1 ); mesh.userData.targetable = true; @@ -54,6 +55,19 @@ export default class Refineries { return meshes; } + + async get() { + let refinery = this.mesh.clone(); + + let i = this.instances.length; + + refinery.name = 'Extractor #' + ( i + 1 ); + + this.instances.push( refinery ); + + return refinery; + } + async load() { //const material = new THREE.MeshBasicMaterial( {color: 0xff0000, transparent: true, opacity: 1.0, side: THREE.DoubleSide} ); diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml index 17b073a4..f59ccb80 100644 --- a/game/src/scenes/overworld.yml +++ b/game/src/scenes/overworld.yml @@ -83,11 +83,11 @@ objects: model: refinery position: x: 20000 - y: -20000 - z: 10000 + y: 153 + z: -20000 - name: Refinery 92 model: refinery position: - x: 20000 - y: -20000 - z: 10000 + x: -20000 + y: 153 + z: 20000 From 86be0da976f51f8ecf2c97811a92465a3949c934 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Thu, 9 Oct 2025 20:29:50 +0000 Subject: [PATCH 32/58] #31 - Implementing cargo ships in dynamic loader --- client/src/app/scenograph/director.js | 30 ++++++++-------- client/src/app/scenograph/objects.js | 5 +-- .../objects/structures/extractor.js | 21 ----------- .../scenograph/objects/structures/platform.js | 5 +++ .../scenograph/objects/structures/refinery.js | 22 ------------ .../scenograph/objects/vehicles/cargo_ship.js | 36 +++++-------------- game/src/scenes/overworld.yml | 2 +- 7 files changed, 32 insertions(+), 89 deletions(-) diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index 27c3ff2b..14501e7c 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -246,35 +246,35 @@ export default class Director { } async loadInstance() { - this.world.instance.objects.forEach( async object => { - if ( object.model == 'extractor' ) { + await this.world.instance.objects.forEach( async object_config => { + if ( object_config.model == 'extractor' ) { l.scenograph.director.loadObject( - object, + object_config, await l.scenograph.objects.structures.extractor.get() ); } - if ( object.model == 'platform' ) { + if ( object_config.model == 'platform' ) { l.scenograph.director.loadObject( - object, + object_config, await l.scenograph.objects.structures.platform.get() ); } - if ( object.model == 'refinery' ) { + if ( object_config.model == 'refinery' ) { l.scenograph.director.loadObject( - object, + object_config, await l.scenograph.objects.structures.refinery.get() ); } } ); - // this.world.instance.actors.forEach( actor => { - // if ( actor.class == 'cargoShip' ) { - - // } - // console.log(actor); - // } ); - - //debugger; + this.world.instance.actors.forEach( async object_config => { + if ( object_config.class == 'cargoShip' ) { + l.scenograph.director.loadObject( + object_config, + await l.scenograph.objects.vehicles.cargoShip.get() + ); + } + } ); // Setup Player aircraft, used for the intro sequence. l.scenograph.actors.player.vehicle = new l.scenograph.objects.vehicles.valiant(); diff --git a/client/src/app/scenograph/objects.js b/client/src/app/scenograph/objects.js index 6ca92268..27855397 100644 --- a/client/src/app/scenograph/objects.js +++ b/client/src/app/scenograph/objects.js @@ -73,7 +73,7 @@ export default class Objects { refinery: new Refinery(), }; this.vehicles = { - cargoShip: CargoShip, + cargoShip: new CargoShip(), person: Person, raven: Raven, valiant: Valiant, @@ -89,7 +89,7 @@ export default class Objects { await this.structures.platform.load(); await this.structures.refinery.load(); await this.projectiles.missile.load(); - //await this.vehicles.cargoShip.load(); + await this.vehicles.cargoShip.load(); console.log("Objects loaded"); } @@ -109,6 +109,7 @@ export default class Objects { l.scenograph.objects.structures.platform.animate( currentTime ); l.scenograph.objects.structures.refinery.animate( currentTime ); l.scenograph.objects.projectiles.missile.animate( currentTime ); + l.scenograph.objects.vehicles.cargoShip.animate( currentTime ); } } diff --git a/client/src/app/scenograph/objects/structures/extractor.js b/client/src/app/scenograph/objects/structures/extractor.js index aab91ce5..916bdfc5 100644 --- a/client/src/app/scenograph/objects/structures/extractor.js +++ b/client/src/app/scenograph/objects/structures/extractor.js @@ -45,27 +45,6 @@ export default class Extractor { ]; } - // async getAll() { - // let extractors = []; - - // await this.load(); - - // this.extractorLocations.forEach( async ( extractor_location, i ) => { - // let extractor = this.mesh.clone(); - - // extractor.rotation.y = Math.PI / 8; - // extractor.position.x = extractor_location.x; - // extractor.position.y = -7450; - // extractor.position.z = extractor_location.y; - - // extractor.name = 'Extractor #' + ( i + 1 ); - - // extractors.push( extractor ); - // } ); - - // return extractors; - // } - async load() { //const material = new THREE.MeshBasicMaterial( {color: 0xff0000, transparent: true, opacity: 1.0, side: THREE.DoubleSide} ); diff --git a/client/src/app/scenograph/objects/structures/platform.js b/client/src/app/scenograph/objects/structures/platform.js index a0622203..77f5cdf3 100644 --- a/client/src/app/scenograph/objects/structures/platform.js +++ b/client/src/app/scenograph/objects/structures/platform.js @@ -13,6 +13,8 @@ import { proceduralBuilding, proceduralMetalMaterial2, proceduralSolarPanel } fr export default class Platform { + instances; + // THREE.Mesh mesh; @@ -20,6 +22,7 @@ export default class Platform { model; constructor() { + this.instances = []; this.ready = false; } @@ -114,6 +117,8 @@ export default class Platform { async get() { let platform = this.mesh.clone(); + this.instances.push( platform ); + return platform; } diff --git a/client/src/app/scenograph/objects/structures/refinery.js b/client/src/app/scenograph/objects/structures/refinery.js index 6eb2765b..37f7185a 100644 --- a/client/src/app/scenograph/objects/structures/refinery.js +++ b/client/src/app/scenograph/objects/structures/refinery.js @@ -34,28 +34,6 @@ export default class Refinery { ]; } - async getAll() { - let meshes = []; - - await this.load(); - - this.locations.forEach( async ( location, i ) => { - let mesh = this.mesh.clone(); - - mesh.position.x = location.x; - - mesh.position.z = location.y; - mesh.name = 'Refinery #' + ( i + 1 ); - mesh.userData.targetable = true; - mesh.userData.objectClass = 'refinery'; - - meshes.push( mesh ); - } ); - - return meshes; - } - - async get() { let refinery = this.mesh.clone(); diff --git a/client/src/app/scenograph/objects/vehicles/cargo_ship.js b/client/src/app/scenograph/objects/vehicles/cargo_ship.js index 27218ffb..d3155972 100644 --- a/client/src/app/scenograph/objects/vehicles/cargo_ship.js +++ b/client/src/app/scenograph/objects/vehicles/cargo_ship.js @@ -21,9 +21,6 @@ export default class CargoShip { // THREE.Mesh clones instances; - // Array of three.vector3's defining X/Z coordinates and radius of where extractors are in the ocean. - locations; - // THREE.Mesh mesh; @@ -36,28 +33,10 @@ export default class CargoShip { // The scale of the mesh. size; - // Locations the cargo ships will randomly select and travel to. - targets; - constructor() { this.instances = []; this.ready = false; this.size = 1000; - - // Cargo ship start locations - this.locations = [ - //new THREE.Vector3( 0, -500, this.size * 10 ), // Test ship - new THREE.Vector3( -35000, -2000, this.size * 10 ), - new THREE.Vector3( -36000, -1500, this.size * 10 ), - new THREE.Vector3( -34000, -1500, this.size * 10 ), - ]; - - // Cargo ship destinations, use the current scene's extractor positions. - this.destinations = []; - l.current_scene.objects.extractors.forEach( (extractor) => { - this.destinations.push( new THREE.Vector3( extractor.position.x, 0, extractor.position.z ) ); - }); - } /** @@ -69,14 +48,15 @@ export default class CargoShip { let path = new YUKA.Path(); path.loop = true; - // Add the union platform - const platform_location = l.current_scene.objects.platform.mesh.position; - path.add( new YUKA.Vector3( platform_location.x, 0, platform_location.z ) ); + // Add the union platforms + l.scenograph.objects.structures.platform.instances.forEach( (platform) => { + path.add( new YUKA.Vector3( platform.position.x, 0, platform.position.z ) ); + }); - this.destinations.forEach( ( destination ) => { - path.add( new YUKA.Vector3( destination.x, 0, destination.z ) ); + // Add the extractors. + l.scenograph.objects.structures.extractor.instances.forEach( (extractor) => { + path.add( new YUKA.Vector3( extractor.position.x, 0, extractor.position.z ) ); }); - path.add( new YUKA.Vector3( platform_location.x, 0, platform_location.z ) ); return path; } @@ -164,7 +144,7 @@ export default class CargoShip { return mesh; } - + // Note: has to be loaded after extractors! async load() { //const material = new THREE.MeshBasicMaterial( {color: 0xff0000, transparent: true, opacity: 1.0, side: THREE.DoubleSide} ); diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml index f59ccb80..fc7201a6 100644 --- a/game/src/scenes/overworld.yml +++ b/game/src/scenes/overworld.yml @@ -69,7 +69,7 @@ objects: x: 70000 y: -7450 z: 0 - - name: Lambda + - name: Lambda City model: platform position: x: -35000 From 9657a7d4145b4d6729421e23fcc66c4696dce9e4 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sat, 11 Oct 2025 05:56:33 +0000 Subject: [PATCH 33/58] #31 - WIP addition of Raven --- client/src/app/scenograph.js | 2 +- client/src/app/scenograph/director.js | 47 ++++++++++++------- client/src/app/scenograph/objects.js | 4 +- .../scenograph/objects/projectiles/missile.js | 28 +++++------ .../objects/structures/extractor.js | 2 +- .../scenograph/objects/structures/refinery.js | 2 +- .../app/scenograph/objects/vehicles/raven.js | 37 +++++++++++---- 7 files changed, 77 insertions(+), 45 deletions(-) diff --git a/client/src/app/scenograph.js b/client/src/app/scenograph.js index 80ec2df6..dd4fe024 100644 --- a/client/src/app/scenograph.js +++ b/client/src/app/scenograph.js @@ -201,7 +201,6 @@ export default class Scenograph { // Activate Event listeners. this.events.init(); - }; async checkGPUTier() { @@ -238,6 +237,7 @@ export default class Scenograph { const delta = l.scenograph.time.update().getDelta(); if ( l.current_scene.started ) { + if ( l.current_scene.animation_queue.length > 0 ) { for ( var i = 0; diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index 14501e7c..4c05fdaf 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -246,6 +246,25 @@ export default class Director { } async loadInstance() { + // Setup Player aircraft, used for the intro sequence. + l.scenograph.actors.player.vehicle = new l.scenograph.objects.vehicles.valiant(); + await l.scenograph.actors.player.vehicle.load(); + l.current_scene.scene.add( + l.scenograph.actors.player.vehicle.mesh + ); + l.current_scene.animation_queue.push( + l.scenograph.actors.player.vehicle.animate + ); + + // Setup Player person, used for the hangar scene. + l.scenograph.actors.player.person = new l.scenograph.objects.vehicles.person(); + l.current_scene.scene.add( + l.scenograph.actors.player.person.mesh + ); + l.current_scene.animation_queue.push( + l.scenograph.actors.player.person.animate + ); + await this.world.instance.objects.forEach( async object_config => { if ( object_config.model == 'extractor' ) { l.scenograph.director.loadObject( @@ -274,27 +293,15 @@ export default class Director { await l.scenograph.objects.vehicles.cargoShip.get() ); } + if ( object_config.class == 'pirate' ) { + l.scenograph.director.loadObject( + object_config, + await l.scenograph.objects.vehicles.raven.get() + ); + } } ); - // Setup Player aircraft, used for the intro sequence. - l.scenograph.actors.player.vehicle = new l.scenograph.objects.vehicles.valiant(); - await l.scenograph.actors.player.vehicle.load(); - l.current_scene.scene.add( - l.scenograph.actors.player.vehicle.mesh - ); - l.current_scene.animation_queue.push( - l.scenograph.actors.player.vehicle.animate - ); - // Setup Player person, used for the hangar scene. - l.scenograph.actors.player.person = new l.scenograph.objects.vehicles.person(); - l.current_scene.scene.add( - l.scenograph.actors.player.person.mesh - ); - l.current_scene.animation_queue.push( - l.scenograph.actors.player.person.animate - ); - } async loadObject( config, object ) { @@ -398,6 +405,10 @@ export default class Director { } finishSetup() { + // Add objects to animation queue. + l.current_scene.animation_queue.push( + l.scenograph.objects.animate + ); // Check if we've finished loading. let bootWaiter = setInterval( () => { if ( diff --git a/client/src/app/scenograph/objects.js b/client/src/app/scenograph/objects.js index 27855397..9034adee 100644 --- a/client/src/app/scenograph/objects.js +++ b/client/src/app/scenograph/objects.js @@ -75,7 +75,7 @@ export default class Objects { this.vehicles = { cargoShip: new CargoShip(), person: Person, - raven: Raven, + raven: new Raven(), valiant: Valiant, }; @@ -90,6 +90,7 @@ export default class Objects { await this.structures.refinery.load(); await this.projectiles.missile.load(); await this.vehicles.cargoShip.load(); + await this.vehicles.raven.load(); console.log("Objects loaded"); } @@ -110,6 +111,7 @@ export default class Objects { l.scenograph.objects.structures.refinery.animate( currentTime ); l.scenograph.objects.projectiles.missile.animate( currentTime ); l.scenograph.objects.vehicles.cargoShip.animate( currentTime ); + l.scenograph.objects.vehicles.raven.animate( currentTime ); } } diff --git a/client/src/app/scenograph/objects/projectiles/missile.js b/client/src/app/scenograph/objects/projectiles/missile.js index 03062430..2cb9ae0b 100644 --- a/client/src/app/scenograph/objects/projectiles/missile.js +++ b/client/src/app/scenograph/objects/projectiles/missile.js @@ -74,10 +74,10 @@ export default class Missile { async targetLost( targetMeshUuid ) { - l.current_scene.objects.projectiles.missile.active.forEach( ( missile, index ) => { + l.scenograph.objects.projectiles.missile.active.forEach( ( missile, index ) => { // Detonate early if missile target was lost. if ( missile.userData.destMesh.uuid == targetMeshUuid ) { - l.current_scene.objects.projectiles.missile.animateMissileEnd( missile, index ); + l.scenograph.objects.projectiles.missile.animateMissileEnd( missile, index ); } } ); } @@ -159,7 +159,7 @@ export default class Missile { l.current_scene.scene.add( mesh ); - l.current_scene.objects.projectiles.missile.explosions.push( mesh ); + l.scenograph.objects.projectiles.missile.explosions.push( mesh ); } /** @@ -171,7 +171,7 @@ export default class Missile { * @param {*} destCoords Destination coordinates for flight path */ async fireMissile( originMesh, originCoords, destMesh, destCoords ) { - let newMissile = l.current_scene.objects.projectiles.missile.mesh.clone(); + let newMissile = l.scenograph.objects.projectiles.missile.mesh.clone(); // Attach metas. newMissile.userData.created = l.current_scene.stats.currentTime; @@ -192,7 +192,7 @@ export default class Missile { l.current_scene.scene.add(newMissile); // Add missile to the active missiles array (for animation etc) - l.current_scene.objects.projectiles.missile.active.push( newMissile ); + l.scenograph.objects.projectiles.missile.active.push( newMissile ); // Add a trail to the missile. newMissile.userData.trail = l.current_scene.effects.trail.createTrail( newMissile, 0, 0, -1.5 ); @@ -233,7 +233,7 @@ export default class Missile { const destroyedTargets = new Set(); // @todo: v7: This has to be simulated on the server somehow.. - l.current_scene.objects.projectiles.missile.active.forEach( ( missile, index ) => { + l.scenograph.objects.projectiles.missile.active.forEach( ( missile, index ) => { const [ damage, targetDestroyed ] = missile.userData.object.hitCalculation(); if ( targetDestroyed ) { @@ -247,22 +247,22 @@ export default class Missile { ( parseFloat( l.current_scene.stats.currentTime ) >= parseFloat( missile.userData.created ) + 10000 ) || ( damage ) ) { - l.current_scene.objects.projectiles.missile.animateMissileEnd( missile, index ); + l.scenograph.objects.projectiles.missile.animateMissileEnd( missile, index ); } // Otherwise keep flying forward. else { - l.current_scene.objects.projectiles.missile.animateMissileFlight( missile ); + l.scenograph.objects.projectiles.missile.animateMissileFlight( missile ); } } ); // Destroy all missiles headed toward the target. - l.current_scene.objects.projectiles.missile.active = l.current_scene.objects.projectiles.missile.active.filter( missile => { + l.scenograph.objects.projectiles.missile.active = l.scenograph.objects.projectiles.missile.active.filter( missile => { const remove = destroyedTargets.has( missile.userData.destMesh.uuid ); - if (remove) l.current_scene.objects.projectiles.missile.targetLost( missile.userData.destMesh.uuid ); + if (remove) l.scenograph.objects.projectiles.missile.targetLost( missile.userData.destMesh.uuid ); return !remove; }); - l.current_scene.objects.projectiles.missile.explosions.forEach( ( explosion, index ) => { + l.scenograph.objects.projectiles.missile.explosions.forEach( ( explosion, index ) => { // Check if it's been 2 seconds since the explosion started, remove if so if ( parseFloat( l.current_scene.stats.currentTime ) >= parseFloat( explosion.userData.created ) + 2000 ) { // Remove the explosion from the scene. @@ -284,7 +284,7 @@ export default class Missile { explosion = null; // Remove from the tracking array. - l.current_scene.objects.projectiles.missile.explosions.splice( index, 1 ); + l.scenograph.objects.projectiles.missile.explosions.splice( index, 1 ); } else { explosion.lookAt( l.scenograph.cameras.active.position ); @@ -310,11 +310,11 @@ export default class Missile { } animateMissileEnd( missile, activeIndex ) { - l.current_scene.objects.projectiles.missile.active.splice( activeIndex, 1 ); + l.scenograph.objects.projectiles.missile.active.splice( activeIndex, 1 ); l.current_scene.scene.remove( missile ); missile.userData.trail.destroyMesh(); missile.userData.trail.deactivate(); - l.current_scene.objects.projectiles.missile.loadExplosion( missile.userData.destMesh.position ); + l.scenograph.objects.projectiles.missile.loadExplosion( missile.userData.destMesh.position ); } } diff --git a/client/src/app/scenograph/objects/structures/extractor.js b/client/src/app/scenograph/objects/structures/extractor.js index 916bdfc5..ebe3d070 100644 --- a/client/src/app/scenograph/objects/structures/extractor.js +++ b/client/src/app/scenograph/objects/structures/extractor.js @@ -198,7 +198,7 @@ export default class Extractor { **/ animate( currentTime ) { - l.scenograph.objects.extractor.instances.forEach( ( extractor, i ) => { + l.scenograph.objects.structures.extractor.instances.forEach( ( extractor, i ) => { let inner = extractor.getObjectByName( 'inner' ); let outer = extractor.getObjectByName( 'outer' ); inner.material.uniforms.time.value += 0.0000025; diff --git a/client/src/app/scenograph/objects/structures/refinery.js b/client/src/app/scenograph/objects/structures/refinery.js index 37f7185a..d33fe24b 100644 --- a/client/src/app/scenograph/objects/structures/refinery.js +++ b/client/src/app/scenograph/objects/structures/refinery.js @@ -133,7 +133,7 @@ export default class Refinery { **/ animate( delta ) { - l.current_scene.objects.refineries.forEach( ( refinery, i ) => { + l.scenograph.objects.structures.refinery.instances.forEach( ( refinery, i ) => { let inner = refinery.getObjectByName( 'inner' ); inner.material.uniforms.time.value += 0.00005; } ); diff --git a/client/src/app/scenograph/objects/vehicles/raven.js b/client/src/app/scenograph/objects/vehicles/raven.js index 892db1a7..d50e7705 100644 --- a/client/src/app/scenograph/objects/vehicles/raven.js +++ b/client/src/app/scenograph/objects/vehicles/raven.js @@ -19,6 +19,9 @@ export default class Raven extends RavenBase { // An actor containing AI behaviours. actor; + // THREE.Mesh clones + instances; + // Ship Model (gltf) model; @@ -33,6 +36,7 @@ export default class Raven extends RavenBase { constructor() { super(); + this.instances = []; this.default_camera_distance = l.scenograph.width < l.scenograph.height ? -70 : -35; this.trail_position_y = 1.2; this.trail_position_z = 1.5; @@ -111,19 +115,29 @@ export default class Raven extends RavenBase { // this.mesh.scale.set(100,100,100); this.mesh.matrixAutoUpdate = false; - // @todo: Uncouple from the pirate actor when vehicle selection is introduced. - this.mesh.userData.actor = new Pirate( this.mesh, l.current_scene.scene ); - l.scenograph.entityManager.add( this.mesh.userData.actor.entity ); - this.mesh.userData.object = this; - this.mesh.userData.object.standing = -1; + } + + async get() { + let mesh = this.mesh.clone(); + + let i = this.instances.length; + + this.instances.push( mesh ); + mesh.userData.object = this; + mesh.userData.object.standing = -1; // Set the object start position based on the path. // @todo: pluck it dynamically from path. - this.mesh.userData.object.startPosition.x = -2000; - this.mesh.userData.object.startPosition.y = this.mesh.position.y; - this.mesh.userData.object.startPosition.z = -1000; + mesh.userData.object.startPosition.x = -2000; + mesh.userData.object.startPosition.y = this.mesh.position.y; + mesh.userData.object.startPosition.z = -1000; + + mesh.userData.actor = new Pirate( this.mesh, l.current_scene.scene ); + + l.scenograph.entityManager.add( mesh.userData.actor.entity ); + return mesh; } /** @@ -138,8 +152,13 @@ export default class Raven extends RavenBase { * @note All references within this method should be globally accessible. **/ animate( delta ) { + console.log('bonjour hi 1!'); if ( l.current_scene.settings.game_controls ) { - l.current_scene.objects.bot.mesh.userData.actor.animate( delta ); + console.log('bonjour hi 2!'); + l.scenograph.objects.vehicles.raven.instances.forEach( raven => { + raven.userData.actor.animate( delta ); + console.log('bonjour hi 3!'); + } ); } } From 6cae408cc401fcd260dbf514f3310767d293cfb7 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sat, 11 Oct 2025 17:40:08 +0000 Subject: [PATCH 34/58] #31 - Fixing Raven activation --- client/src/app/scenograph/objects/vehicles/cargo_ship.js | 2 +- client/src/app/scenograph/objects/vehicles/raven.js | 6 ++---- game/src/actors/pirate.ts | 3 +-- game/src/systems/weapons.ts | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/client/src/app/scenograph/objects/vehicles/cargo_ship.js b/client/src/app/scenograph/objects/vehicles/cargo_ship.js index d3155972..e2bcaf4c 100644 --- a/client/src/app/scenograph/objects/vehicles/cargo_ship.js +++ b/client/src/app/scenograph/objects/vehicles/cargo_ship.js @@ -214,7 +214,7 @@ export default class CargoShip { animate( delta ) { if ( l.current_scene.settings.game_controls ) { - l.current_scene.objects.cargo_ships.forEach( ( cargo_ship ) => { + l.scenograph.objects.vehicles.cargoShip.instances.forEach( ( cargo_ship ) => { cargo_ship.userData.actor.animate( delta ); diff --git a/client/src/app/scenograph/objects/vehicles/raven.js b/client/src/app/scenograph/objects/vehicles/raven.js index d50e7705..ceb3bb3d 100644 --- a/client/src/app/scenograph/objects/vehicles/raven.js +++ b/client/src/app/scenograph/objects/vehicles/raven.js @@ -133,7 +133,7 @@ export default class Raven extends RavenBase { mesh.userData.object.startPosition.y = this.mesh.position.y; mesh.userData.object.startPosition.z = -1000; - mesh.userData.actor = new Pirate( this.mesh, l.current_scene.scene ); + mesh.userData.actor = new Pirate( mesh, l.current_scene.scene ); l.scenograph.entityManager.add( mesh.userData.actor.entity ); @@ -152,12 +152,10 @@ export default class Raven extends RavenBase { * @note All references within this method should be globally accessible. **/ animate( delta ) { - console.log('bonjour hi 1!'); + if ( l.current_scene.settings.game_controls ) { - console.log('bonjour hi 2!'); l.scenograph.objects.vehicles.raven.instances.forEach( raven => { raven.userData.actor.animate( delta ); - console.log('bonjour hi 3!'); } ); } } diff --git a/game/src/actors/pirate.ts b/game/src/actors/pirate.ts index e2b8bb19..a9c42f15 100644 --- a/game/src/actors/pirate.ts +++ b/game/src/actors/pirate.ts @@ -47,8 +47,7 @@ export default class Pirate extends BaseActor { // @todo: v7 Figure out a way to signal this to happen without l. global object access this.pursue = new YUKA.PursuitBehavior( l.scenograph.actors.player.vehicle.mesh.userData.actor.entity, 1 ); this.pursue.active = false; - this.entity.steering.add( this.pursue ); - + this.entity.steering.add( this.pursue ); } } diff --git a/game/src/systems/weapons.ts b/game/src/systems/weapons.ts index 2aa7d83c..a0af9edc 100644 --- a/game/src/systems/weapons.ts +++ b/game/src/systems/weapons.ts @@ -63,7 +63,7 @@ export default class Weapons extends BaseSystem { // @todo: v7 Figure out a way to signal this to happen without l. global object access this.last = l.current_scene.stats.currentTime; - l.current_scene.objects.projectiles.missile.fireMissile( + l.scenograph.objects.projectiles.missile.fireMissile( this.mesh, this.mesh.position, target.mesh, From a13d97b380bcee929b8fc686721520b7d9d0e5b0 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sat, 11 Oct 2025 21:55:08 +0000 Subject: [PATCH 35/58] #31 - Modifying scope of Raven so instances refer to instances, not to core references --- .../app/scenograph/objects/vehicles/raven.js | 18 ++++++++---------- game/src/objects/aircraft/base.ts | 4 +++- game/src/objects/aircraft/raven.ts | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/client/src/app/scenograph/objects/vehicles/raven.js b/client/src/app/scenograph/objects/vehicles/raven.js index ceb3bb3d..e1d12771 100644 --- a/client/src/app/scenograph/objects/vehicles/raven.js +++ b/client/src/app/scenograph/objects/vehicles/raven.js @@ -10,10 +10,10 @@ import * as THREE from 'three'; */ import l from '@/helpers/l.js'; import { brightenMaterial, proceduralMetalMaterial } from '@/scenograph/materials.js'; -import Pirate from '#/game/src/actors/pirate'; -import RavenBase from '#/game/src/objects/aircraft/raven'; +import PirateActor from '#/game/src/actors/pirate'; +import RavenObject from '#/game/src/objects/aircraft/raven'; -export default class Raven extends RavenBase { +export default class Raven { // An actor containing AI behaviours. @@ -35,7 +35,7 @@ export default class Raven extends RavenBase { state; constructor() { - super(); + this.instances = []; this.default_camera_distance = l.scenograph.width < l.scenograph.height ? -70 : -35; this.trail_position_y = 1.2; @@ -122,10 +122,7 @@ export default class Raven extends RavenBase { async get() { let mesh = this.mesh.clone(); - let i = this.instances.length; - - this.instances.push( mesh ); - mesh.userData.object = this; + mesh.userData.object = new RavenObject( mesh ); mesh.userData.object.standing = -1; // Set the object start position based on the path. // @todo: pluck it dynamically from path. @@ -133,10 +130,11 @@ export default class Raven extends RavenBase { mesh.userData.object.startPosition.y = this.mesh.position.y; mesh.userData.object.startPosition.z = -1000; - mesh.userData.actor = new Pirate( mesh, l.current_scene.scene ); - + mesh.userData.actor = new PirateActor( mesh, l.current_scene.scene ); l.scenograph.entityManager.add( mesh.userData.actor.entity ); + this.instances.push( mesh ); + return mesh; } diff --git a/game/src/objects/aircraft/base.ts b/game/src/objects/aircraft/base.ts index 45a74175..86d143fb 100644 --- a/game/src/objects/aircraft/base.ts +++ b/game/src/objects/aircraft/base.ts @@ -8,6 +8,7 @@ import { normaliseSpeedDelta, easeOutExpo, easeInQuad, easeInOutExpo } from '../../helpers'; export default class BaseAircraft { + public mesh; public score: { kills: number; deaths: number } = { kills: 0, deaths: 0 }; public standing: number = 0; public hitPoints: number = 100; @@ -43,7 +44,8 @@ export default class BaseAircraft { moveRight: false }; - constructor() { + constructor( mesh ) { + this.mesh = mesh; } public blowUp( meshPosition ) { diff --git a/game/src/objects/aircraft/raven.ts b/game/src/objects/aircraft/raven.ts index c7c4555c..cb20fe78 100644 --- a/game/src/objects/aircraft/raven.ts +++ b/game/src/objects/aircraft/raven.ts @@ -11,8 +11,8 @@ class Raven extends BaseAircraft { public maxUp: number = 3.7 * 2.5; public maxDown: number = 3.7 * 5; // gravity? - constructor() { - super(); // Call the constructor of the base class + constructor( mesh ) { + super( mesh ); // Call the constructor of the base class } } From 8bc4fdd2958690c8bc2e8301c9764f63ec77623c Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sat, 11 Oct 2025 21:59:16 +0000 Subject: [PATCH 36/58] #31 - Refactoring Player/Valiant aircraft to be killable --- client/src/app/scenograph/objects/vehicles/valiant.js | 6 +++--- game/src/objects/aircraft/base.ts | 2 +- game/src/objects/aircraft/valiant.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/app/scenograph/objects/vehicles/valiant.js b/client/src/app/scenograph/objects/vehicles/valiant.js index fb41368d..adbbb7ea 100644 --- a/client/src/app/scenograph/objects/vehicles/valiant.js +++ b/client/src/app/scenograph/objects/vehicles/valiant.js @@ -14,9 +14,9 @@ import * as THREE from 'three'; import l from '@/helpers/l.js'; import { brightenMaterial, proceduralMetalMaterial } from '@/scenograph/materials.js'; import Player from '#/game/src/actors/player'; -import ValiantBase from '#/game/src/objects/aircraft/valiant'; +import ValiantObject from '#/game/src/objects/aircraft/valiant'; -export default class Valiant extends ValiantBase { +export default class Valiant { // Camera distance. camera_distance; @@ -126,7 +126,7 @@ export default class Valiant extends ValiantBase { this.trail = l.current_scene.effects.trail.createTrail( this.mesh, 0, this.trail_position_y, this.trail_position_z ); - this.mesh.userData.object = this; + this.mesh.userData.object = new ValiantObject( this.mesh ); } createThrusterMesh( options ) { diff --git a/game/src/objects/aircraft/base.ts b/game/src/objects/aircraft/base.ts index 86d143fb..dbb96b57 100644 --- a/game/src/objects/aircraft/base.ts +++ b/game/src/objects/aircraft/base.ts @@ -62,7 +62,7 @@ export default class BaseAircraft { explosionPosition.z += zOffset; setTimeout( () => { - l.current_scene.objects.projectiles.missile.loadExplosion( explosionPosition ); + l.scenograph.objects.projectiles.missile.loadExplosion( explosionPosition ); }, 250 * Math.random() ) } diff --git a/game/src/objects/aircraft/valiant.ts b/game/src/objects/aircraft/valiant.ts index d4d608ad..ad75b337 100644 --- a/game/src/objects/aircraft/valiant.ts +++ b/game/src/objects/aircraft/valiant.ts @@ -6,8 +6,8 @@ import BaseAircraft from './base'; class Valiant extends BaseAircraft { - constructor() { - super(); // Call the constructor of the base class + constructor( mesh ) { + super( mesh ); // Call the constructor of the base class } } From 0d3f83c63cbfd65255cc267831f4d4f13d79109a Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Tue, 9 Dec 2025 17:10:45 +0000 Subject: [PATCH 37/58] #31 - Continuing refactor --- client/src/app/routes/hangar.js | 30 ++++---- client/src/app/scenograph/objects.js | 9 +-- .../scenograph/objects/vehicles/valiant.js | 68 +++++++++---------- .../scenograph/overlays/heads-up-display.js | 10 +-- client/src/app/scenograph/overlays/map.js | 8 +-- 5 files changed, 62 insertions(+), 63 deletions(-) diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index 97f066cc..c04bfd1b 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -22,29 +22,29 @@ export default class hangarRoute { // Set client mode. l.mode = 'hangar'; - l.current_scene.objects.platform.mesh.visible = false; - l.current_scene.objects.hangar.mesh.visible = true; + l.scenograph.objects.structures.platform.mesh.visible = false; + l.scenograph.objects.structures.hangar.mesh.visible = true; - l.current_scene.objects.hangar.mesh.position.copy( l.current_scene.objects.platform.mesh.position ); - l.current_scene.objects.hangar.mesh.position.y = 505; + l.scenograph.objects.structures.hangar.mesh.position.copy( l.scenograph.objects.structures.platform.mesh.position ); + l.scenograph.objects.structures.hangar.mesh.position.y = 505; - l.scenograph.actors.player.vehicle.position.x = l.current_scene.objects.platform.mesh.position.x; - l.scenograph.actors.player.vehicle.position.z = l.current_scene.objects.platform.mesh.position.z - 2.5; - l.scenograph.actors.player.vehicle.position.y = 497.5; + l.scenograph.actors.player.vehicle.mesh.position.x = l.scenograph.objects.structures.platform.mesh.position.x; + l.scenograph.actors.player.vehicle.mesh.position.z = l.scenograph.objects.structures.platform.mesh.position.z - 2.5; + l.scenograph.actors.player.vehicle.mesh.position.y = 497.5; - l.scenograph.actors.player.person.position.x = l.current_scene.objects.platform.mesh.position.x; - l.scenograph.actors.player.person.position.z = l.current_scene.objects.platform.mesh.position.z + 10; + l.scenograph.actors.player.person.position.x = l.scenograph.objects.structures.platform.mesh.position.x; + l.scenograph.actors.player.person.position.z = l.scenograph.objects.structures.platform.mesh.position.z + 10; l.scenograph.actors.player.person.position.y = 502.5; - + l.scenograph.cameras.active.position.copy(l.scenograph.actors.player.person.position); - - + + if ( l.scenograph.controls.orbit ) { l.scenograph.cameras.orbit.updateProjectionMatrix(); l.scenograph.controls.orbit.update(); - l.scenograph.controls.orbitTarget.x = l.scenograph.actors.player.vehicle.position.x; - l.scenograph.controls.orbitTarget.y = l.scenograph.actors.player.vehicle.position.y; - l.scenograph.controls.orbitTarget.z = l.scenograph.actors.player.vehicle.position.z; + l.scenograph.controls.orbitTarget.x = l.scenograph.actors.player.vehicle.mesh.position.x; + l.scenograph.controls.orbitTarget.y = l.scenograph.actors.player.vehicle.mesh.position.y; + l.scenograph.controls.orbitTarget.z = l.scenograph.actors.player.vehicle.mesh.position.z; } // @todo #31 diff --git a/client/src/app/scenograph/objects.js b/client/src/app/scenograph/objects.js index 9034adee..2e462af5 100644 --- a/client/src/app/scenograph/objects.js +++ b/client/src/app/scenograph/objects.js @@ -1,6 +1,6 @@ /** * Objects. - * + * * Composes scene meshes from gltf and procedural code. */ @@ -78,7 +78,7 @@ export default class Objects { raven: new Raven(), valiant: Valiant, }; - + } /** @@ -86,6 +86,7 @@ export default class Objects { */ async init () { await this.structures.extractor.load(); + await this.structures.hangar.load(); await this.structures.platform.load(); await this.structures.refinery.load(); await this.projectiles.missile.load(); @@ -96,10 +97,10 @@ export default class Objects { /** * Animate hook. - * + * * This method is called within the main animation loop and * therefore must only reference global objects or properties. - * + * * @method animate * @memberof Objects * @global diff --git a/client/src/app/scenograph/objects/vehicles/valiant.js b/client/src/app/scenograph/objects/vehicles/valiant.js index adbbb7ea..c139d418 100644 --- a/client/src/app/scenograph/objects/vehicles/valiant.js +++ b/client/src/app/scenograph/objects/vehicles/valiant.js @@ -1,8 +1,8 @@ /** * Valiant Aircraft - * + * * Provides a Valiant aircraft that can be added and updated in the game world. - * + * * @todo: Uncouple player actor from this ship class. */ @@ -46,7 +46,6 @@ export default class Valiant { trail; constructor() { - super(); this.default_camera_distance = -35; this.trail_position_y = 1.2; this.trail_position_z = 1.5; @@ -313,9 +312,9 @@ export default class Valiant { // Set the ship as ready. l.scenograph.actors.player.vehicle.ready = true; l.scenograph.actors.player.vehicle.camera_distance = l.scenograph.actors.player.vehicle.default_camera_distance + ( l.current_scene.room_depth / 2 ); - l.scenograph.actors.player.vehicle.position.x = l.scenograph.actors.player.vehicle.mesh.position.x; - l.scenograph.actors.player.vehicle.position.y = l.scenograph.actors.player.vehicle.mesh.position.y; - l.scenograph.actors.player.vehicle.position.z = l.scenograph.actors.player.vehicle.mesh.position.z; + l.scenograph.actors.player.vehicle.mesh.userData.object.position.x = l.scenograph.actors.player.vehicle.mesh.position.x; + l.scenograph.actors.player.vehicle.mesh.userData.object.position.y = l.scenograph.actors.player.vehicle.mesh.position.y; + l.scenograph.actors.player.vehicle.mesh.userData.object.position.z = l.scenograph.actors.player.vehicle.mesh.position.z; } ); } @@ -332,12 +331,12 @@ export default class Valiant { let changing = false; for ( const [ controlName, keyMapping ] of Object.entries( mappings ) ) { if ( l.scenograph.controls.keyboard.pressed( keyMapping ) ) { - l.scenograph.actors.player.vehicle.controls[ controlName ] = true; + l.scenograph.actors.player.vehicle.mesh.userData.object.controls[ controlName ] = true; changing = true; } else { - l.scenograph.actors.player.vehicle.controls[ controlName ] = false; + l.scenograph.actors.player.vehicle.mesh.userData.object.controls[ controlName ] = false; if ( l.scenograph.controls.touch ) { // Check if any touchpad controls are being pressed @@ -351,29 +350,29 @@ export default class Valiant { ) { changing = true; if ( l.scenograph.controls.touch.controls.moveUp ) { - l.scenograph.actors.player.vehicle.controls.moveUp = true; + l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveUp = true; } if ( l.scenograph.controls.touch.controls.moveDown ) { - l.scenograph.actors.player.vehicle.controls.moveDown = true; + l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveDown = true; } if ( l.scenograph.controls.touch.controls.moveForward ) { - l.scenograph.actors.player.vehicle.controls.throttleUp = true; + l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleUp = true; } if ( l.scenograph.controls.touch.controls.moveBackward ) { - l.scenograph.actors.player.vehicle.controls.throttleDown = true; + l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleDown = true; } if ( l.scenograph.controls.touch.controls.moveLeft ) { - l.scenograph.actors.player.vehicle.controls.moveLeft = true; + l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveLeft = true; } if ( l.scenograph.controls.touch.controls.moveRight ) { - l.scenograph.actors.player.vehicle.controls.moveRight = true; + l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveRight = true; } } } } } - l.scenograph.actors.player.vehicle.controls.changing = changing; + l.scenograph.actors.player.vehicle.mesh.userData.object.controls.changing = changing; } @@ -383,8 +382,8 @@ export default class Valiant { } // Rock the ship forward and back when moving horizontally - if ( l.scenograph.actors.player.vehicle.controls.throttleDown || l.scenograph.actors.player.vehicle.controls.throttleUp ) { - let pitchChange = l.scenograph.actors.player.vehicle.controls.throttleUp ? -1 : 1; + if ( l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleDown || l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleUp ) { + let pitchChange = l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleUp ? -1 : 1; if ( Math.abs( l.scenograph.actors.player.vehicle.mesh.rotation.x ) < 1 / 4 ) { l.scenograph.actors.player.vehicle.mesh.rotation.x += pitchChange / 10 / 180; } @@ -392,11 +391,11 @@ export default class Valiant { // Rock the ship forward and back when moving vertically if ( - l.scenograph.actors.player.vehicle.controls.moveDown + l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveDown || - l.scenograph.actors.player.vehicle.controls.moveUp + l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveUp ) { - let elevationChange = l.scenograph.actors.player.vehicle.controls.moveDown ? -1 : 1; + let elevationChange = l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveDown ? -1 : 1; if ( Math.abs( l.scenograph.actors.player.vehicle.mesh.rotation.x ) < 1 / 8 ) { l.scenograph.actors.player.vehicle.mesh.rotation.x += elevationChange / 10 / 180; } @@ -414,13 +413,12 @@ export default class Valiant { // Update the position of the aircraft to spot determined by game logic. updateMesh() { - l.scenograph.actors.player.vehicle.mesh.position.x = l.scenograph.actors.player.vehicle.position.x; - l.scenograph.actors.player.vehicle.mesh.position.y = l.scenograph.actors.player.vehicle.position.y; - l.scenograph.actors.player.vehicle.mesh.position.z = l.scenograph.actors.player.vehicle.position.z; - - l.scenograph.actors.player.vehicle.mesh.rotation.x = l.scenograph.actors.player.vehicle.rotation.x; - l.scenograph.actors.player.vehicle.mesh.rotation.y = l.scenograph.actors.player.vehicle.rotation.y; - l.scenograph.actors.player.vehicle.mesh.rotation.z = l.scenograph.actors.player.vehicle.rotation.z; + l.scenograph.actors.player.vehicle.mesh.position.x = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.position.x; + l.scenograph.actors.player.vehicle.mesh.position.y = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.position.y; + l.scenograph.actors.player.vehicle.mesh.position.z = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.position.z; + l.scenograph.actors.player.vehicle.mesh.rotation.x = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.rotation.x; + l.scenograph.actors.player.vehicle.mesh.rotation.y = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.rotation.y; + l.scenograph.actors.player.vehicle.mesh.rotation.z = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.rotation.z; } updateCamera( rY, tY, tZ ) { @@ -442,7 +440,7 @@ export default class Valiant { l.scenograph.cameras.player.rotation.y += rY; } else { - // Check there is y difference and the rotation pad isn't being pressed. + // Check there is y difference and the rotation pad isn't being pressed. if ( l.scenograph.cameras.player.rotation.y != l.scenograph.actors.player.vehicle.mesh.rotation.y && ( l.scenograph.controls.touch && !l.scenograph.controls.touch.controls.rotationPad.mouseDown ) @@ -480,10 +478,10 @@ export default class Valiant { } /** * Animate hook. - * + * * This method is called within the main animation loop and * therefore must only reference global objects or properties. - * + * * @method animate * @memberof Valiant * @global @@ -494,14 +492,14 @@ export default class Valiant { if ( l.scenograph.actors.player.vehicle.ready ) { if ( l.current_scene.settings.game_controls ) { - + if ( l.scenograph.actors.player.mode == 'vehicle' ) { // Detect keyboard input and pass it to the ship state model. l.scenograph.actors.player.vehicle.updateControls(); } if ( l.scenograph.modes.multiplayer.connected ) { - l.scenograph.modes.multiplayer.socket.emit( 'input', l.scenograph.actors.player.vehicle.controls ); + l.scenograph.modes.multiplayer.socket.emit( 'input', l.scenograph.actors.player.vehicle.mesh.userData.object.controls ); } l.scenograph.actors.player.vehicle.mesh.userData.actor.animate( delta ); @@ -512,7 +510,7 @@ export default class Valiant { l.scenograph.actors.player.vehicle.updateAnimation( delta ); // Update the ships state model. - let [ rY, tY, tZ ] = l.scenograph.actors.player.vehicle.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); + let [ rY, tY, tZ ] = l.scenograph.actors.player.vehicle.mesh.userData.object.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); l.scenograph.actors.player.vehicle.updateMesh(); l.scenograph.actors.player.vehicle.updateCamera( rY, tY, tZ ); @@ -545,7 +543,7 @@ export default class Valiant { trailOffset += l.scenograph.actors.player.vehicle.trail_position_z - Math.abs( l.scenograph.actors.player.vehicle.airSpeed ); - l.scenograph.actors.player.vehicle.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, .8 ); // RGBA. + l.scenograph.actors.player.vehicle.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, .8 ); // RGBA. } else { l.scenograph.actors.player.vehicle.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, 0 ); // RGBA. @@ -562,7 +560,7 @@ export default class Valiant { else { l.scenograph.actors.player.vehicle.trail.targetObject.position.x = 0; } - l.scenograph.actors.player.vehicle.trail.update(); + //l.scenograph.actors.player.vehicle.trail.update(); } } diff --git a/client/src/app/scenograph/overlays/heads-up-display.js b/client/src/app/scenograph/overlays/heads-up-display.js index 5293ad69..ca04cff0 100644 --- a/client/src/app/scenograph/overlays/heads-up-display.js +++ b/client/src/app/scenograph/overlays/heads-up-display.js @@ -57,10 +57,10 @@ export default class HeadsUpDisplay { /** * Animate hook. - * + * * This method is called within the main animation loop and * therefore must only reference global objects or properties. - * + * * @method animate * @memberof HeadsUpDisplay * @global @@ -83,7 +83,7 @@ export default class HeadsUpDisplay { let vspd = l.scenograph.overlays.hud.frameToSecond(l.scenograph.actors.player.vehicle.verticalSpeed); l.scenograph.overlays.hud.vspdElement.innerHTML = `VERT. SPD: ${vspd}km/h`; - let heading = THREE.MathUtils.radToDeg( l.scenograph.actors.player.vehicle.rotation.y ); + let heading = THREE.MathUtils.radToDeg( l.scenograph.actors.player.vehicle.mesh.rotation.y ); heading = heading % 360; if (heading < 0) { heading += 360; @@ -91,7 +91,7 @@ export default class HeadsUpDisplay { heading = Math.round(360 - heading); l.scenograph.overlays.hud.headingElement.innerHTML = `HEADING: ${heading}°`; - let elevation = Math.round( l.scenograph.actors.player.vehicle.position.y * 100 ) / 100; + let elevation = Math.round( l.scenograph.actors.player.vehicle.mesh.position.y * 100 ) / 100; l.scenograph.overlays.hud.elevationElement.innerHTML = `ELEVATION: ${elevation}m`; } @@ -104,4 +104,4 @@ export default class HeadsUpDisplay { return metersPerSecond; } -} \ No newline at end of file +} diff --git a/client/src/app/scenograph/overlays/map.js b/client/src/app/scenograph/overlays/map.js index af96f6bc..a9eca0ff 100644 --- a/client/src/app/scenograph/overlays/map.js +++ b/client/src/app/scenograph/overlays/map.js @@ -115,7 +115,7 @@ export default class Map { // Check if the object is within the mapping distance. if ( distance <= l.scenograph.overlays.map.distance * 100 ) { - + // Check if the object is already present on the map, move it if so if ( target.mesh.uuid in l.scenograph.overlays.map.markers ) { @@ -161,17 +161,17 @@ export default class Map { /** * Animate hook. - * + * * This method is called within the main animation loop andw * therefore must only reference global objects or properties. - * + * * @method animate * @memberof Map * @global * @note All references within this method should be globally accessible. **/ animate() { - let heading = THREE.MathUtils.radToDeg( l.scenograph.actors.player.vehicle.rotation.y ); + let heading = THREE.MathUtils.radToDeg( l.scenograph.actors.player.vehicle.mesh.rotation.y ); heading = heading % 360; if (heading < 0) { heading += 360; From b74f05ac1fe894557335bd34f2513af58462acde Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Tue, 9 Dec 2025 17:19:53 +0000 Subject: [PATCH 38/58] #31 - Fixing player aircraft position and rotation updates --- .../src/app/scenograph/objects/vehicles/valiant.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/app/scenograph/objects/vehicles/valiant.js b/client/src/app/scenograph/objects/vehicles/valiant.js index c139d418..a7aaffb4 100644 --- a/client/src/app/scenograph/objects/vehicles/valiant.js +++ b/client/src/app/scenograph/objects/vehicles/valiant.js @@ -413,12 +413,12 @@ export default class Valiant { // Update the position of the aircraft to spot determined by game logic. updateMesh() { - l.scenograph.actors.player.vehicle.mesh.position.x = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.position.x; - l.scenograph.actors.player.vehicle.mesh.position.y = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.position.y; - l.scenograph.actors.player.vehicle.mesh.position.z = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.position.z; - l.scenograph.actors.player.vehicle.mesh.rotation.x = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.rotation.x; - l.scenograph.actors.player.vehicle.mesh.rotation.y = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.rotation.y; - l.scenograph.actors.player.vehicle.mesh.rotation.z = l.scenograph.actors.player.vehicle.mesh.userData.actor.entity.rotation.z; + l.scenograph.actors.player.vehicle.mesh.position.x = l.scenograph.actors.player.vehicle.mesh.userData.object.position.x; + l.scenograph.actors.player.vehicle.mesh.position.y = l.scenograph.actors.player.vehicle.mesh.userData.object.position.y; + l.scenograph.actors.player.vehicle.mesh.position.z = l.scenograph.actors.player.vehicle.mesh.userData.object.position.z; + l.scenograph.actors.player.vehicle.mesh.rotation.x = l.scenograph.actors.player.vehicle.mesh.userData.object.rotation.x; + l.scenograph.actors.player.vehicle.mesh.rotation.y = l.scenograph.actors.player.vehicle.mesh.userData.object.rotation.y; + l.scenograph.actors.player.vehicle.mesh.rotation.z = l.scenograph.actors.player.vehicle.mesh.userData.object.rotation.z; } updateCamera( rY, tY, tZ ) { From b66c4a5719ec74e1253ad58685b807d143e0b788 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Tue, 9 Dec 2025 17:33:52 +0000 Subject: [PATCH 39/58] #31 - Fixing player aircraft airspeed and vertical speed tracking --- .../src/app/scenograph/modes/multiplayer.js | 6 ++--- .../scenograph/objects/vehicles/valiant.js | 26 +++++++++---------- .../scenograph/overlays/heads-up-display.js | 4 +-- client/src/app/ui/flight_instruments.js | 10 +++---- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/client/src/app/scenograph/modes/multiplayer.js b/client/src/app/scenograph/modes/multiplayer.js index 932c0b5c..6706ed1f 100644 --- a/client/src/app/scenograph/modes/multiplayer.js +++ b/client/src/app/scenograph/modes/multiplayer.js @@ -115,15 +115,15 @@ export default class Multiplayer { move_ship( data ) { if ( data.socket_id == l.scenograph.modes.multiplayer.socket.id ) { // Update stored ship state, don't punch out as functions aren't transmitted. - l.scenograph.actors.player.vehicle.airSpeed = data.airSpeed; - l.scenograph.actors.player.vehicle.altitude = data.altitude; + l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed = data.airSpeed; + l.scenograph.actors.player.vehicle.mesh.userData.object.altitude = data.altitude; l.scenograph.actors.player.vehicle.heading = data.heading; l.scenograph.actors.player.vehicle.horizon = data.horizon; l.scenograph.actors.player.vehicle.position.x = data.position.x; l.scenograph.actors.player.vehicle.position.y = data.position.y; l.scenograph.actors.player.vehicle.position.z = data.position.z; l.scenograph.actors.player.vehicle.rotation = data.rotation; - l.scenograph.actors.player.vehicle.verticalSpeed = data.verticalSpeed; + l.scenograph.actors.player.vehicle.mesh.userData.object.verticalSpeed = data.verticalSpeed; } else { l.scenograph.actors.player.vehicles.forEach( ( ship ) => { diff --git a/client/src/app/scenograph/objects/vehicles/valiant.js b/client/src/app/scenograph/objects/vehicles/valiant.js index a7aaffb4..d0a56376 100644 --- a/client/src/app/scenograph/objects/vehicles/valiant.js +++ b/client/src/app/scenograph/objects/vehicles/valiant.js @@ -425,8 +425,8 @@ export default class Valiant { var radian = ( Math.PI / 180 ); l.scenograph.actors.player.vehicle.camera_distance = l.scenograph.actors.player.vehicle.default_camera_distance + ( l.current_scene.room_depth / 2 ); - if ( l.scenograph.actors.player.vehicle.airSpeed < 0 ) { - l.scenograph.actors.player.vehicle.camera_distance -= l.scenograph.actors.player.vehicle.airSpeed * 4; + if ( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed < 0 ) { + l.scenograph.actors.player.vehicle.camera_distance -= l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed * 4; } let xDiff = l.scenograph.actors.player.vehicle.mesh.position.x; @@ -527,21 +527,21 @@ export default class Valiant { let trailOffset = 0; // Only offset the trail effect if we are going forward which is (z-1) in numerical terms - if ( l.scenograph.actors.player.vehicle.airSpeed < 0 ) { + if ( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed < 0 ) { // Update ship thruster - l.scenograph.actors.player.vehicle.animateThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.centralConeBurner, .5 ); - l.scenograph.actors.player.vehicle.animateThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.outerCylBurner, .5 ); + l.scenograph.actors.player.vehicle.animateThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.centralConeBurner, .5 ); + l.scenograph.actors.player.vehicle.animateThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.outerCylBurner, .5 ); - l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.rearConeBurner, -1 ); - l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.centralConeBurner, 1 ); - l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.outerCylBurner, -1 ); - l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.airSpeed, l.scenograph.actors.player.vehicle.thruster.innerCylBurner, 1 ); + l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.rearConeBurner, -1 ); + l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.centralConeBurner, 1 ); + l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.outerCylBurner, -1 ); + l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.innerCylBurner, 1 ); // Limit playback rate to 5x as large values freak out the browser. - l.scenograph.actors.player.vehicle.thruster.videoElement.playbackRate = Math.min( 5, 0.25 + Math.abs( l.scenograph.actors.player.vehicle.airSpeed ) ); + l.scenograph.actors.player.vehicle.thruster.videoElement.playbackRate = Math.min( 5, 0.25 + Math.abs( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed ) ); - trailOffset += l.scenograph.actors.player.vehicle.trail_position_z - Math.abs( l.scenograph.actors.player.vehicle.airSpeed ); + trailOffset += l.scenograph.actors.player.vehicle.trail_position_z - Math.abs( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed ); l.scenograph.actors.player.vehicle.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, .8 ); // RGBA. } @@ -550,11 +550,11 @@ export default class Valiant { } // Update the trail position based on above calculations. - l.scenograph.actors.player.vehicle.trail.targetObject.position.y = l.scenograph.actors.player.vehicle.trail_position_y + l.scenograph.actors.player.vehicle.verticalSpeed; + l.scenograph.actors.player.vehicle.trail.targetObject.position.y = l.scenograph.actors.player.vehicle.trail_position_y + l.scenograph.actors.player.vehicle.mesh.userData.object.verticalSpeed; l.scenograph.actors.player.vehicle.trail.targetObject.position.z = trailOffset; if ( rY != 0 ) { - l.scenograph.actors.player.vehicle.trail.targetObject.position.x = rY * l.scenograph.actors.player.vehicle.airSpeed; + l.scenograph.actors.player.vehicle.trail.targetObject.position.x = rY * l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed; l.scenograph.actors.player.vehicle.trail.targetObject.position.y += Math.abs( l.scenograph.actors.player.vehicle.trail.targetObject.position.x ) / 4; } else { diff --git a/client/src/app/scenograph/overlays/heads-up-display.js b/client/src/app/scenograph/overlays/heads-up-display.js index ca04cff0..4d282ebc 100644 --- a/client/src/app/scenograph/overlays/heads-up-display.js +++ b/client/src/app/scenograph/overlays/heads-up-display.js @@ -77,10 +77,10 @@ export default class HeadsUpDisplay { l.scenograph.overlays.hud.container.classList.remove('portrait'); } - let aspd = -l.scenograph.overlays.hud.frameToSecond(l.scenograph.actors.player.vehicle.airSpeed); + let aspd = -l.scenograph.overlays.hud.frameToSecond(l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed); l.scenograph.overlays.hud.aspdElement.innerHTML = `AIRSPEED: ${aspd}km/h`; - let vspd = l.scenograph.overlays.hud.frameToSecond(l.scenograph.actors.player.vehicle.verticalSpeed); + let vspd = l.scenograph.overlays.hud.frameToSecond(l.scenograph.actors.player.vehicle.mesh.userData.object.verticalSpeed); l.scenograph.overlays.hud.vspdElement.innerHTML = `VERT. SPD: ${vspd}km/h`; let heading = THREE.MathUtils.radToDeg( l.scenograph.actors.player.vehicle.mesh.rotation.y ); diff --git a/client/src/app/ui/flight_instruments.js b/client/src/app/ui/flight_instruments.js index e72f700e..f5e25f0c 100644 --- a/client/src/app/ui/flight_instruments.js +++ b/client/src/app/ui/flight_instruments.js @@ -1,6 +1,6 @@ /** * Controls flight instrument UI elements - * + * * @todo: v7: Remove if not used. */ @@ -26,10 +26,10 @@ export default class Flight_Instruments { /** * Update hook. - * + * * This method is called within the UI setInterval updater, allowing * HTML content to be updated at different rate than the 3D frame rate. - * + * * @method update * @memberof Flight_Instruments * @global @@ -47,11 +47,11 @@ export default class Flight_Instruments { } // Update the angle of the needle - const angle = (Math.abs(l.scenograph.actors.player.vehicle.airSpeed) * 1.94384) * 45; + const angle = (Math.abs(l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed) * 1.94384) * 45; // Update the needle rotation document.querySelector(l.ui.flight_instruments.containerSelector + ' #Airspeed #Needle').style.transform = `rotate(${angle}deg)`; } - + } } From 5aa41c936b3de1619474fc26f1a72cc8bfb563f6 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Tue, 9 Dec 2025 18:27:12 +0000 Subject: [PATCH 40/58] #31 - Reenabled aircraft trail --- client/src/app/scenograph/objects/vehicles/valiant.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/app/scenograph/objects/vehicles/valiant.js b/client/src/app/scenograph/objects/vehicles/valiant.js index d0a56376..d6051720 100644 --- a/client/src/app/scenograph/objects/vehicles/valiant.js +++ b/client/src/app/scenograph/objects/vehicles/valiant.js @@ -560,7 +560,7 @@ export default class Valiant { else { l.scenograph.actors.player.vehicle.trail.targetObject.position.x = 0; } - //l.scenograph.actors.player.vehicle.trail.update(); + l.scenograph.actors.player.vehicle.trail.update(); } } From 7b9286be4596e102e8533b456e430635543b10f1 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 14 Dec 2025 16:13:20 +0000 Subject: [PATCH 41/58] #31 - Temporarily adding hangar to scene by hand, doubling person turning speed, adding hangar configs to platform structure --- client/src/app/routes/hangar.js | 2 ++ game/src/objects/person.ts | 30 +++++++++++++++--------------- game/src/scenes/overworld.yml | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index c04bfd1b..6b60c365 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -22,6 +22,8 @@ export default class hangarRoute { // Set client mode. l.mode = 'hangar'; + l.current_scene.scene.add(l.scenograph.objects.structures.hangar.mesh); + l.scenograph.objects.structures.platform.mesh.visible = false; l.scenograph.objects.structures.hangar.mesh.visible = true; diff --git a/game/src/objects/person.ts b/game/src/objects/person.ts index c1eb3f8d..6e8fc02f 100644 --- a/game/src/objects/person.ts +++ b/game/src/objects/person.ts @@ -1,6 +1,6 @@ /** * Base Aircraft class - * + * * @todo: * - Add weight and wind resistance */ @@ -41,14 +41,14 @@ export default class Person { }; constructor() { - } + } /** * Change aircraft velocity based on current and what buttons are pushed by the player. - * + * * @param currentVelocity - * @param increasePushed - * @param decreasePushed + * @param increasePushed + * @param decreasePushed */ private _changeVelocity(stepIncrease, stepDecrease, currentVelocity, increasePushed, decreasePushed, increaseMax, decreaseMax, dragFactor): number { let newVelocity = currentVelocity; @@ -79,7 +79,7 @@ export default class Person { newVelocity = 0; } } - + } } @@ -88,15 +88,15 @@ export default class Person { /** * Move the aircraft based on velocity, direction and time delta between frames. - * - * @param time_delta + * + * @param time_delta */ public move( time_delta: number ): object { let stepSize: number = .025 * normaliseSpeedDelta( time_delta ), - rY: number = 0, - tZ: number = 0, + rY: number = 0, + tZ: number = 0, tY: number = 0, - radian: number = - (Math.PI / 180) * stepSize * 50; + radian: number = - (Math.PI / 180) * stepSize * 100; if ( this.controls.forward || this.controls.back ){ // Update Airspeed (horizontal velocity) @@ -114,7 +114,7 @@ export default class Person { else { this.airSpeed = 0; } - + // Update Vertical Speed (velocity) this.verticalSpeed = this._changeVelocity( @@ -150,7 +150,7 @@ export default class Person { tZ = this.airSpeed; } - + if (rY != 0) { if (Math.abs(this.rotation.z) < Math.PI / 4) { this.rotation.z += rY / Math.PI; @@ -161,7 +161,7 @@ export default class Person { let xDiff = tZ * Math.sin(this.rotation.y), zDiff = tZ * Math.cos(this.rotation.y); - + // "1" is the floor limit as it's the ocean surface and the camera clips through the water any lower. if (this.position.y + tY >= 1 ) { this.position.y += tY; @@ -173,7 +173,7 @@ export default class Person { this.position.z += zDiff; return [ rY, tY, tZ ]; - + } } diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml index fc7201a6..0c8c12db 100644 --- a/game/src/scenes/overworld.yml +++ b/game/src/scenes/overworld.yml @@ -79,6 +79,25 @@ objects: x: 0 y: -12.56 z: 0 + hangars: + - name: Hangar 1 + position: + x: 0 + y: 505 + z: 0 + rotation: + x: 0 + y: 0 + z: 0 + - name: Hangar 2 + position: + x: 0 + y: 505 + z: 0 + rotation: + x: 0 + y: 3.14159 + z: 0 - name: Refinery 91 model: refinery position: From f0aea9c6bc08935c6b64645c7846db63e8b38151 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 14 Dec 2025 17:14:52 +0000 Subject: [PATCH 42/58] #31 - Setting hangar to load from config --- client/src/app/routes/hangar.js | 51 ++++++++++++---------- client/src/app/scenograph/director.js | 63 ++++++++++++++------------- 2 files changed, 59 insertions(+), 55 deletions(-) diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index 6b60c365..ceaf6302 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -13,6 +13,9 @@ import l from '@/helpers/l.js'; export default class hangarRoute { + // Scenograph instance of a structure that contains a hangar. + targetStructure = false; + constructor() { console.log( 'Hangar launched' ); @@ -22,39 +25,39 @@ export default class hangarRoute { // Set client mode. l.mode = 'hangar'; - l.current_scene.scene.add(l.scenograph.objects.structures.hangar.mesh); - - l.scenograph.objects.structures.platform.mesh.visible = false; - l.scenograph.objects.structures.hangar.mesh.visible = true; + this.targetStructure = l.scenograph.objects.structures.platform.instances[0]; - l.scenograph.objects.structures.hangar.mesh.position.copy( l.scenograph.objects.structures.platform.mesh.position ); - l.scenograph.objects.structures.hangar.mesh.position.y = 505; + this.loadHangar(); - l.scenograph.actors.player.vehicle.mesh.position.x = l.scenograph.objects.structures.platform.mesh.position.x; - l.scenograph.actors.player.vehicle.mesh.position.z = l.scenograph.objects.structures.platform.mesh.position.z - 2.5; - l.scenograph.actors.player.vehicle.mesh.position.y = 497.5; + l.scenograph.actors.player.setMode('person'); - l.scenograph.actors.player.person.position.x = l.scenograph.objects.structures.platform.mesh.position.x; - l.scenograph.actors.player.person.position.z = l.scenograph.objects.structures.platform.mesh.position.z + 10; - l.scenograph.actors.player.person.position.y = 502.5; + } - l.scenograph.cameras.active.position.copy(l.scenograph.actors.player.person.position); + loadHangar() { + l.current_scene.scene.add(l.scenograph.objects.structures.hangar.mesh); + l.scenograph.objects.structures.platform.mesh.visible = false; + l.scenograph.objects.structures.hangar.mesh.visible = true; + l.scenograph.objects.structures.hangar.mesh.position.copy( this.targetStructure.position ); + l.scenograph.objects.structures.hangar.mesh.position.y = this.targetStructure.userData.config.hangars[0].position.y; - if ( l.scenograph.controls.orbit ) { - l.scenograph.cameras.orbit.updateProjectionMatrix(); - l.scenograph.controls.orbit.update(); - l.scenograph.controls.orbitTarget.x = l.scenograph.actors.player.vehicle.mesh.position.x; - l.scenograph.controls.orbitTarget.y = l.scenograph.actors.player.vehicle.mesh.position.y; - l.scenograph.controls.orbitTarget.z = l.scenograph.actors.player.vehicle.mesh.position.z; - } + l.scenograph.actors.player.vehicle.mesh.userData.object.position.x = this.targetStructure.position.x; + l.scenograph.actors.player.vehicle.mesh.userData.object.position.z = this.targetStructure.position.z - 2.5; + l.scenograph.actors.player.vehicle.mesh.userData.object.position.y = l.scenograph.objects.structures.hangar.mesh.position.y - 7.5; - // @todo #31 - // - Implement first person controls - // - Separate player from aircraft + l.scenograph.actors.player.person.position.x = this.targetStructure.position.x; + l.scenograph.actors.player.person.position.z = this.targetStructure.position.z + 10; + l.scenograph.actors.player.person.position.y = l.scenograph.objects.structures.hangar.mesh.position.y - 2.75; - l.scenograph.actors.player.setMode('person'); + l.scenograph.cameras.active.position.copy(l.scenograph.actors.player.person.position); + if ( l.scenograph.controls.orbit ) { + l.scenograph.cameras.orbit.updateProjectionMatrix(); + l.scenograph.controls.orbit.update(); + l.scenograph.controls.orbitTarget.x = l.scenograph.objects.structures.hangar.mesh.position.x; + l.scenograph.controls.orbitTarget.y = l.scenograph.objects.structures.hangar.mesh.position.y; + l.scenograph.controls.orbitTarget.z = l.scenograph.objects.structures.hangar.mesh.position.z; + } } } diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index 4c05fdaf..21c5608b 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -1,6 +1,6 @@ /** * Director. - * + * * Scene Management class. */ @@ -15,7 +15,7 @@ import * as THREE from "three"; import l from '@/helpers/l.js'; /** - * World Simulation + * World Simulation */ import World from '#/game/src/world'; @@ -33,7 +33,7 @@ import { export default class Director { constructor() { - + /** * Animation queue. */ @@ -41,14 +41,14 @@ export default class Director { /** * Primary scene camera - * + * * @memberof THREE.Camera */ this.camera = false; /** * Effects composers and their layers. - * + * * @memberof Object { postprocessing.EffectComposer } */ this.effects = { @@ -58,7 +58,7 @@ export default class Director { /** * Fast mode (bloom off, no shadows) - * + * * @memberof Boolean */ this.fast = true; @@ -112,21 +112,21 @@ export default class Director { /** * Camera is being moved by tweening. - * + * * @memberof Boolean */ this.moving = false; /** * Current position of the users pointer. - * + * * @memberof THREE.Vector2 */ this.pointer = false; /** * Raycaster that projects into the scene from the users pointer and picks up collisions for interaction. - * + * * @memberof THREE.Raycaster */ this.raycaster = false; @@ -139,7 +139,7 @@ export default class Director { /** * Renderers that create the scene. - * + * * @memberof Object { THREE.Renderer , ... } */ this.renderers = { @@ -148,7 +148,7 @@ export default class Director { /** * Settings that controls the scene. - * + * * @memberof Object */ this.settings = { @@ -172,17 +172,17 @@ export default class Director { normal: 0.1, active: 0.05 } } - + }, room_depth: false, // calculated value scale: 11, // do not change, braeks css screen sizes startPosZ: - 10 // updated responsive eugene levy }; - + /** * Currently selected object. - * + * * @memberof THREE.Object3d */ this.selected = false; @@ -194,7 +194,7 @@ export default class Director { /** * If the main sequence has begun. - * + * * @memberof Boolean */ this.started = false; @@ -205,7 +205,7 @@ export default class Director { this.stats = { /** * Frames Per Second (FPS) - * + * * @memberof Integer */ currentTime: performance.now(), @@ -216,14 +216,14 @@ export default class Director { /** * All scene triggers. - * + * * @memberof Object */ this.triggers = {}; - + /** * All scene tweens. - * + * * @memberof Object */ this.tweens = {}; @@ -232,7 +232,7 @@ export default class Director { // Load world instance from game classes. load( sceneName ) { this.world = new World( sceneName ); - + return this; } @@ -241,7 +241,7 @@ export default class Director { this.setupSceneDefaults(); this.loadInstance(); - + this.finishSetup(); } @@ -264,7 +264,7 @@ export default class Director { l.current_scene.animation_queue.push( l.scenograph.actors.player.person.animate ); - + await this.world.instance.objects.forEach( async object_config => { if ( object_config.model == 'extractor' ) { l.scenograph.director.loadObject( @@ -316,6 +316,7 @@ export default class Director { } object.name = config.name; + object.userData.config = config; // @todo: add to current_scene array relevant to object class. @@ -335,14 +336,14 @@ export default class Director { /** * Tracked meshes and mesh groups that compose the scene. - * + * * @memberof Object */ l.current_scene.objects = {}; /** * The main scene container. - * + * * @memberof THREE.Scene */ l.current_scene.scene = new THREE.Scene(); @@ -350,7 +351,7 @@ export default class Director { l.scenograph.effects.init(); - + l.current_scene.objects.door = await l.scenograph.objects.preloader.createDoor(); l.current_scene.objects.door.position.set( -l.scenograph.objects.preloader.doorWidth / 2, @@ -358,7 +359,7 @@ export default class Director { -15 + l.current_scene.room_depth / 2 ); l.current_scene.scene.add( l.current_scene.objects.door ); - + // Setup skybox l.current_scene.objects.sky = new l.scenograph.objects.environment.sky(); l.current_scene.scene.add( @@ -367,10 +368,10 @@ export default class Director { l.current_scene.animation_queue.push( l.current_scene.objects.sky.animate ); - + // Setup ocean //l.current_scene.objects.ocean = new Ocean( extractors.extractorLocations ); - l.current_scene.objects.ocean = new l.scenograph.objects.environment.ocean( + l.current_scene.objects.ocean = new l.scenograph.objects.environment.ocean( l.scenograph.objects.structures.extractor.extractorLocations ); l.current_scene.scene.add( @@ -379,7 +380,7 @@ export default class Director { l.current_scene.animation_queue.push( l.current_scene.objects.ocean.animate ); - + // Adjust ambient light intensity l.current_scene.objects.ambientLight = new THREE.AmbientLight( l.config.settings.fast ? 0x555555 : 0x444444 @@ -389,11 +390,11 @@ export default class Director { l.current_scene.scene.add( l.current_scene.objects.ambientLight ); - + l.current_scene.objects.screens_loaded = 0; l.current_scene.objects.room = await l.scenograph.objects.preloader.createOfficeRoom(); l.current_scene.scene.add( l.current_scene.objects.room ); - + // Setup triggers setupTriggers(); From 0fdddb2801cbf08c9dd45e995b341f0baeee2764 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Thu, 18 Dec 2025 21:42:07 +0000 Subject: [PATCH 43/58] #31 - Created hangar game object and beginning to setup as source of layout config --- .../scenograph/objects/structures/hangar.js | 44 +++++++++-- game/src/objects/aircraft/base.ts | 63 ++++++++------- game/src/objects/structures/base.ts | 18 +++++ game/src/objects/structures/hangar.ts | 78 +++++++++++++++++++ 4 files changed, 169 insertions(+), 34 deletions(-) create mode 100644 game/src/objects/structures/base.ts create mode 100644 game/src/objects/structures/hangar.ts diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index b7dbfa3a..7699f05b 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -14,8 +14,12 @@ import { ADDITION, HOLLOW_SUBTRACTION, Operation, Evaluator } from 'three-bvh-cs import l from '@/helpers/l.js'; import { proceduralBuilding, proceduralMetalMaterial2 } from '@/scenograph/materials.js'; +import HangarObject from '#/game/src/objects/structures/hangar'; + export default class Hangar { + designs; + // The hangar mesh and root of the CSG BVH hierarchy. hangar; @@ -25,6 +29,8 @@ export default class Hangar { materials; + objectClass; + // Dimensions of player living quarters // @todo: Dynamic hangars which don't all have one. quartersSize; @@ -62,8 +68,25 @@ export default class Hangar { }; this.ready = false; - + this.objectClass = new HangarObject(); + + } + + /** + * Loads hangar room layouts from the game object class. + */ + async loadDesigns() { + let designs = {}; + + this.objectClass.designs.forEach(design => { + // Auto load the "'Bay with quarters'" as default + if (design.name == 'Bay with quarters') { + designs.default = design; + } + }); + + return designs; } async load() { @@ -71,14 +94,21 @@ export default class Hangar { // Setup materials. await this.loadMaterials(); + this.designs = await this.loadDesigns(); + console.log(this.designs); + this.hangar = await this.loadHangarMesh(); - + console.log('hangar.position', this.hangar.position); + console.log('hangar.rotation', this.hangar.rotation); + /** * Corridor mesh */ let corridorMesh = await this.loadCorridorMesh(); corridorMesh.operation = ADDITION; this.hangar.add( corridorMesh ); + console.log('corridorMesh.position', corridorMesh.position); + console.log('corridorMesh.rotation', corridorMesh.rotation); /** * Player quarters mesh. @@ -86,6 +116,8 @@ export default class Hangar { let quartersMesh = await this.loadQuartersMesh(); quartersMesh.operation = ADDITION; this.hangar.add( quartersMesh ); + console.log('quartersMesh.position', quartersMesh.position); + console.log('quartersMesh.rotation', quartersMesh.rotation); // Constructive Solid Geometry (csg) Evaluator. let csgEvaluator; @@ -104,7 +136,7 @@ export default class Hangar { /** * Setup Hangar materials */ - async loadMaterials() { + async loadMaterials() { this.materials.clear = new THREE.MeshBasicMaterial( { color: 0xffFFFF, transparent: true, visible: false, side: THREE.DoubleSide } ); this.materials.metal = proceduralBuilding( { uniforms: { @@ -150,7 +182,7 @@ export default class Hangar { * Creates the corridor mesh. */ async loadCorridorMesh() { - + var doorGeometry = new THREE.BoxGeometry( this.corridorSize.width, this.corridorSize.height, this.corridorSize.depth ); let corridorMesh = new Operation( doorGeometry, this.materials.metal ); @@ -197,10 +229,10 @@ export default class Hangar { /** * Animate hook. - * + * * This method is called within the main animation loop and * therefore must only reference global objects or properties. - * + * * @method animate * @memberof Hangar * @global diff --git a/game/src/objects/aircraft/base.ts b/game/src/objects/aircraft/base.ts index dbb96b57..c8e617c3 100644 --- a/game/src/objects/aircraft/base.ts +++ b/game/src/objects/aircraft/base.ts @@ -1,6 +1,6 @@ /** * Base Aircraft class - * + * * @todo: * - Add weight and wind resistance */ @@ -51,32 +51,39 @@ export default class BaseAircraft { public blowUp( meshPosition ) { let seed = Math.round(Math.random() * 10); - for ( var i = 0; i < seed; i++ ) { - let xOffset = 10 - Math.random() * 20; - let yOffset = 10 - Math.random() * 20; - let zOffset = 10 - Math.random() * 20; + if ( window.location.pathname == 'https://langenium.com' && l.config.settings.fast == false ) { + for ( var i = 0; i < seed; i++ ) { + let xOffset = 10 - Math.random() * 20; + let yOffset = 10 - Math.random() * 20; + let zOffset = 10 - Math.random() * 20; - let explosionPosition = meshPosition.clone(); - explosionPosition.x += xOffset; - explosionPosition.y += yOffset; - explosionPosition.z += zOffset; + let explosionPosition = meshPosition.clone(); + explosionPosition.x += xOffset; + explosionPosition.y += yOffset; + explosionPosition.z += zOffset; - setTimeout( () => { - l.scenograph.objects.projectiles.missile.loadExplosion( explosionPosition ); - }, 250 * Math.random() ) - + setTimeout( () => { + l.scenograph.objects.projectiles.missile.loadExplosion( explosionPosition ); + }, 250 * Math.random() ) + + } + } + else { + setTimeout( () => { + l.scenograph.objects.projectiles.missile.loadExplosion( meshPosition ); + }, 250 * Math.random() ) } }; /** * Damages the aircraft based on the incoming damage. - * + * * Returns the calculated final damage amount. - * - * @param damagePoints + * + * @param damagePoints * @param originMesh - * @returns + * @returns */ public damage( damagePoints, originMesh ): number { let targetDestroyed = false; @@ -144,10 +151,10 @@ export default class BaseAircraft { /** * Change aircraft velocity based on current and what buttons are pushed by the player. - * + * * @param currentVelocity - * @param increasePushed - * @param decreasePushed + * @param increasePushed + * @param decreasePushed */ private _changeVelocity(stepIncrease, stepDecrease, currentVelocity, increasePushed, decreasePushed, increaseMax, decreaseMax, dragFactor): number { let newVelocity = currentVelocity; @@ -178,7 +185,7 @@ export default class BaseAircraft { newVelocity = 0; } } - + } } @@ -187,13 +194,13 @@ export default class BaseAircraft { /** * Move the aircraft based on velocity, direction and time delta between frames. - * - * @param time_delta + * + * @param time_delta */ public move( time_delta: number ): object { let stepSize: number = .05 * normaliseSpeedDelta( time_delta ), - rY: number = 0, - tZ: number = 0, + rY: number = 0, + tZ: number = 0, tY: number = 0, radian: number = (Math.PI / 180); @@ -251,7 +258,7 @@ export default class BaseAircraft { ) { this.rotation.x *= .9; } - + if (rY != 0) { if (Math.abs(this.rotation.z) < Math.PI / 4) { this.rotation.z += rY / Math.PI; @@ -265,7 +272,7 @@ export default class BaseAircraft { let xDiff = tZ * Math.sin(this.rotation.y), zDiff = tZ * Math.cos(this.rotation.y); - + // "1" is the floor limit as it's the ocean surface and the camera clips through the water any lower. if (this.position.y + tY >= 1 ) { this.position.y += tY; @@ -277,7 +284,7 @@ export default class BaseAircraft { this.position.z += zDiff; return [ rY, tY, tZ ]; - + } } diff --git a/game/src/objects/structures/base.ts b/game/src/objects/structures/base.ts new file mode 100644 index 00000000..0626f2da --- /dev/null +++ b/game/src/objects/structures/base.ts @@ -0,0 +1,18 @@ +/** + * Base Structure class + * + */ + + +export default class BaseStructure { + + + constructor() { + + } + + animate() { + + } + +} diff --git a/game/src/objects/structures/hangar.ts b/game/src/objects/structures/hangar.ts new file mode 100644 index 00000000..4cd58e23 --- /dev/null +++ b/game/src/objects/structures/hangar.ts @@ -0,0 +1,78 @@ +/** + * Aircraft hangar. + */ + +import BaseStructure from './base'; + +class Hangar extends BaseStructure { + + designs; + + constructor() { + super(); // Call the constructor of the base class + + let corridorScale = 0.125; + this.designs = [ + { + name: 'Bay with quarters', + size: 5, + components: [ + { + name: 'Main Bay', + width: 10, + height: 5, + depth: 10, + position: { + x: 0, + y: 0, + z: 0 + }, + rotation: { + x: 0, + y: 0, + z: 0 + } + }, + { + name: 'Quarters', + width: 5, + height: 2.5, + depth: 5, + position: { + x: -8.375, + y: -1.25, + z: -2.5 + }, + rotation: { + x: 0, + y: 0, + z: 0 + } + }, + { + name: 'Corridor', + width: 1.025, + height: 2.55, + depth: 2.5, + position: { + x: -5, + y: -1.225, + z: -1.25 + }, + rotation: { + x: 0, + y: 1.5708, // 90 degrees or half pi + z: 0 + } + }, + ] + } + ]; + } + + detectCollision () { + } + +} + +module.exports = Hangar; From 8bf8971c681b3fa780232a24db179ef909330deb Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Thu, 18 Dec 2025 22:20:46 +0000 Subject: [PATCH 44/58] #31 - Finished refactoring hangars to load from config --- .../scenograph/objects/structures/hangar.js | 147 ++++++++---------- 1 file changed, 67 insertions(+), 80 deletions(-) diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index 7699f05b..984924d7 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -18,23 +18,18 @@ import HangarObject from '#/game/src/objects/structures/hangar'; export default class Hangar { + // Preset layouts for hangars as defined by the game object class. designs; // The hangar mesh and root of the CSG BVH hierarchy. hangar; - // Dimensions of the hangar - // @todo: Dynamic hangars with different dimensions - hangarSize; - + // Modified materials for indoor scenes materials; + // Game object containing layout definition and collision detection handling. objectClass; - // Dimensions of player living quarters - // @todo: Dynamic hangars which don't all have one. - quartersSize; - // THREE.Mesh mesh; @@ -47,26 +42,6 @@ export default class Hangar { this.materials = {}; - let corridorScale = 0.125; - // Based on office door dimensions. - this.corridorSize = { - width: 8.2 * corridorScale, - height: 20.4 * corridorScale, - depth: 20 * corridorScale, - } - - this.hangarSize = { - width: 10, - height: 5, - depth: 10 - }; - - this.quartersSize = { - width: 5, - height: 2.5, - depth: 5 - }; - this.ready = false; this.objectClass = new HangarObject(); @@ -89,47 +64,69 @@ export default class Hangar { return designs; } - async load() { - - // Setup materials. - await this.loadMaterials(); + /** + * Load hangar based on design name, default if not set. + * + * @param {string} designName + * @returns + */ + async loadMesh( designName = 'default' ) { + let hangarConfig, corridorConfig, quartersConfig; - this.designs = await this.loadDesigns(); - console.log(this.designs); + this.designs[designName].components.forEach(component => { + if ( component.name === 'Main Bay' ) { + hangarConfig = component; + } + if ( component.name === 'Quarters' ) { + quartersConfig = component; + } + if ( component.name === 'Corridor' ) { + corridorConfig = component; + } + }); - this.hangar = await this.loadHangarMesh(); - console.log('hangar.position', this.hangar.position); - console.log('hangar.rotation', this.hangar.rotation); + /** + * Main hangar mesh + */ + let hangarMesh = await this.loadHangarMesh( hangarConfig ); /** * Corridor mesh */ - let corridorMesh = await this.loadCorridorMesh(); + let corridorMesh = await this.loadCorridorMesh( corridorConfig ); corridorMesh.operation = ADDITION; - this.hangar.add( corridorMesh ); - console.log('corridorMesh.position', corridorMesh.position); - console.log('corridorMesh.rotation', corridorMesh.rotation); + hangarMesh.add( corridorMesh ); /** * Player quarters mesh. */ - let quartersMesh = await this.loadQuartersMesh(); + let quartersMesh = await this.loadQuartersMesh( quartersConfig ); quartersMesh.operation = ADDITION; - this.hangar.add( quartersMesh ); - console.log('quartersMesh.position', quartersMesh.position); - console.log('quartersMesh.rotation', quartersMesh.rotation); + hangarMesh.add( quartersMesh ); // Constructive Solid Geometry (csg) Evaluator. let csgEvaluator; csgEvaluator = new Evaluator(); csgEvaluator.useGroups = true; - let result = csgEvaluator.evaluateHierarchy( this.hangar ); + let result = csgEvaluator.evaluateHierarchy( hangarMesh ); - this.mesh = new THREE.Object3D(); - this.mesh.add( result ); - this.mesh.userData.targetable = false; - this.mesh.userData.objectClass = 'hangar'; - this.mesh.scale.setScalar( 2.5 ); + let mesh = new THREE.Object3D(); + mesh.add( result ); + mesh.userData.targetable = false; + mesh.userData.objectClass = 'hangar'; + mesh.scale.setScalar( 2.5 ); + + return mesh; + } + + async load() { + + // Setup materials. + await this.loadMaterials(); + + this.designs = await this.loadDesigns(); + + this.mesh = await this.loadMesh(); } @@ -160,11 +157,11 @@ export default class Hangar { /** * Creates the Hangar's main mesh. */ - async loadHangarMesh() { + async loadHangarMesh( config ) { /** * Hangar mesh */ - let hangarMesh = new Operation( new THREE.BoxGeometry( this.hangarSize.width, this.hangarSize.height, this.hangarSize.depth, 2, 2, 2 ), [ + let hangarMesh = new Operation( new THREE.BoxGeometry( config.width, config.height, config.depth, 2, 2, 2 ), [ this.materials.hangar, this.materials.hangar, this.materials.hangar, @@ -181,17 +178,16 @@ export default class Hangar { /** * Creates the corridor mesh. */ - async loadCorridorMesh() { - - var doorGeometry = new THREE.BoxGeometry( this.corridorSize.width, this.corridorSize.height, this.corridorSize.depth ); + async loadCorridorMesh( config ) { - let corridorMesh = new Operation( doorGeometry, this.materials.metal ); + let corridorMesh = new Operation( new THREE.BoxGeometry( config.width, config.height, config.depth ), this.materials.metal ); - corridorMesh.position.x = - ( this.hangarSize.width * this.size ) / 10; - corridorMesh.position.y = ( - ( this.hangarSize.height * this.size ) / 10 ) + this.corridorSize.height * 0.5; - corridorMesh.rotation.y = Math.PI / 2; - - corridorMesh.position.z = ( - ( this.hangarSize.depth * this.size ) / 10 ) / 4; + corridorMesh.position.x = config.position.x; + corridorMesh.position.y = config.position.y; + corridorMesh.position.z = config.position.z; + corridorMesh.rotation.x = config.rotation.x; + corridorMesh.rotation.y = config.rotation.y; + corridorMesh.rotation.z = config.rotation.z; corridorMesh.updateMatrixWorld(); @@ -201,25 +197,16 @@ export default class Hangar { /** * Creates the player quarters mesh. */ - async loadQuartersMesh() { - let innerGeo = new THREE.BoxGeometry( this.quartersSize.width, this.quartersSize.height, this.quartersSize.depth, 2, 2, 2 ); - let quartersMesh = new Operation( innerGeo, this.materials.metal ); - - window.quarters = quartersMesh; - + async loadQuartersMesh( config ) { + let quartersMesh = new Operation( new THREE.BoxGeometry( config.width, config.height, config.depth, 2, 2, 2 ), this.materials.metal ); + + quartersMesh.position.x = config.position.x; + quartersMesh.position.y = config.position.y; + quartersMesh.position.z = config.position.z; + quartersMesh.rotation.x = config.rotation.x; + quartersMesh.rotation.y = config.rotation.y; + quartersMesh.rotation.z = config.rotation.z; quartersMesh.material.uniforms.scale.value = 0.8; - // Offset player quarters by the hangars half width - quartersMesh.position.x = ( - ( this.hangarSize.width * this.size ) / 10 ); - // Offset player quarters by the access corridor - quartersMesh.position.x += - this.corridorSize.depth * 0.75; - // Offset player quarters by half the quarters width - quartersMesh.position.x += ( - ( this.quartersSize.width * this.size ) / 10 ); - // Offset for intersection - quartersMesh.position.x += 1; - - quartersMesh.position.y = ( - ( this.quartersSize.height * this.size ) / 10 ); - - quartersMesh.position.z = ( - ( this.hangarSize.depth * this.size ) / 10 ) / 2; quartersMesh.updateMatrixWorld(); quartersMesh.name = 'inner'; From 3bf5d5f208a02ecb3ac746fa745c1b7515e3c25a Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 19 Dec 2025 03:48:28 +0000 Subject: [PATCH 45/58] #31 - Added second player who starts in the hangar and begun refactoring to load players from config --- client/src/app/scenograph/actors.js | 19 ++- client/src/app/scenograph/actors/player.js | 28 +++- client/src/app/scenograph/director.js | 35 ++-- .../scenograph/objects/vehicles/valiant.js | 151 +++++++++--------- client/src/app/scenograph/tweens.js | 6 +- game/src/actors/pirate.ts | 49 +++--- game/src/scenes/overworld.yml | 14 ++ 7 files changed, 180 insertions(+), 122 deletions(-) diff --git a/client/src/app/scenograph/actors.js b/client/src/app/scenograph/actors.js index 414afb86..7a6bc21c 100644 --- a/client/src/app/scenograph/actors.js +++ b/client/src/app/scenograph/actors.js @@ -14,10 +14,25 @@ import Player from '@/scenograph/actors/player.js'; export default class Actors { - player; + map; constructor() { - this.player = new Player(); + this.map = new Map(); + } + + registerActor(actor) { + if ( actor.class == 'player' ) { + let player = new Player( actor ); + this.map.set(actor.name, player); + } + } + + get(name) { + return this.map.get(name); + } + + getAll() { + return this.map.values(); } } diff --git a/client/src/app/scenograph/actors/player.js b/client/src/app/scenograph/actors/player.js index 968e8360..60257052 100644 --- a/client/src/app/scenograph/actors/player.js +++ b/client/src/app/scenograph/actors/player.js @@ -1,7 +1,6 @@ /** * @name Player * @description Provides an interface to the player actor in the game. - * @namespace l.scenograph.actors.player * @memberof l.scenograph.actors * @global */ @@ -29,7 +28,9 @@ export default class Player { // Vehicle model. vehicle; - constructor() { + constructor( actorConfig ) { + + this.actorConfig = actorConfig; this.ready = false; @@ -49,4 +50,27 @@ export default class Player { } } + async load() { + + // Setup aircraft, used for the intro sequence. + this.vehicle = new l.scenograph.objects.vehicles.valiant(); + await this.vehicle.load(); + l.current_scene.scene.add( + this.vehicle.mesh + ); + l.current_scene.animation_queue.push( + delta => this.vehicle.animate(delta) + ); + + // Setup person, used for the hangar scene. + this.person = new l.scenograph.objects.vehicles.person(); + l.current_scene.scene.add( + this.person.mesh + ); + l.current_scene.animation_queue.push( + this.person.animate + ); + + } + } diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index 21c5608b..c9c577c6 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -246,24 +246,6 @@ export default class Director { } async loadInstance() { - // Setup Player aircraft, used for the intro sequence. - l.scenograph.actors.player.vehicle = new l.scenograph.objects.vehicles.valiant(); - await l.scenograph.actors.player.vehicle.load(); - l.current_scene.scene.add( - l.scenograph.actors.player.vehicle.mesh - ); - l.current_scene.animation_queue.push( - l.scenograph.actors.player.vehicle.animate - ); - - // Setup Player person, used for the hangar scene. - l.scenograph.actors.player.person = new l.scenograph.objects.vehicles.person(); - l.current_scene.scene.add( - l.scenograph.actors.player.person.mesh - ); - l.current_scene.animation_queue.push( - l.scenograph.actors.player.person.animate - ); await this.world.instance.objects.forEach( async object_config => { if ( object_config.model == 'extractor' ) { @@ -299,6 +281,13 @@ export default class Director { await l.scenograph.objects.vehicles.raven.get() ); } + if ( object_config.class == 'player' ) { + l.scenograph.actors.registerActor( + object_config.name, + object_config + ); + + } } ); @@ -351,6 +340,16 @@ export default class Director { l.scenograph.effects.init(); + l.current_scene.objects.demoShip = new l.scenograph.objects.vehicles.valiant(); + await l.current_scene.objects.demoShip.load(); + l.current_scene.scene.add( + l.current_scene.objects.demoShip.mesh + ); + l.current_scene.animation_queue.push( + delta => l.current_scene.objects.demoShip.animate(delta) + ); + l.current_scene.tweens.shipEnterY = l.current_scene.objects.demoShip.shipEnterY(); + l.current_scene.tweens.shipEnterZ = l.current_scene.objects.demoShip.shipEnterZ(); l.current_scene.objects.door = await l.scenograph.objects.preloader.createDoor(); l.current_scene.objects.door.position.set( diff --git a/client/src/app/scenograph/objects/vehicles/valiant.js b/client/src/app/scenograph/objects/vehicles/valiant.js index d6051720..4179b4a2 100644 --- a/client/src/app/scenograph/objects/vehicles/valiant.js +++ b/client/src/app/scenograph/objects/vehicles/valiant.js @@ -118,9 +118,6 @@ export default class Valiant { this.mixer.clipAction( this.model.animations[ 0 ] ).play(); this.mixer.clipAction( this.model.animations[ 1 ] ).play(); - l.current_scene.tweens.shipEnterY = this.shipEnterY(); - l.current_scene.tweens.shipEnterZ = this.shipEnterZ(); - //l.current_scene.effects.particles.createShipThruster(this, 1.5, { x: 0, y: 1.2, z: 1.5 }); this.trail = l.current_scene.effects.trail.createTrail( this.mesh, 0, this.trail_position_y, this.trail_position_z ); @@ -272,7 +269,7 @@ export default class Valiant { .to( target, l.config.settings.skipintro ? 0 : 2000 ) // Move to (300, 200) in 1 second. .easing( TWEEN.Easing.Circular.Out ) // Use an easing function to make the animation smooth. .onUpdate( () => { - l.scenograph.actors.player.vehicle.mesh.position.y = coords.y; + l.current_scene.objects.demoShip.mesh.position.y = coords.y; } ) .onComplete( () => { //console.log('ready'); @@ -290,7 +287,7 @@ export default class Valiant { // Called after tween.js updates 'coords'. // Move 'box' to the position described by 'coords' with a CSS translation. - l.scenograph.actors.player.vehicle.mesh.position.z = coords.x; + l.current_scene.objects.demoShip.mesh.position.z = coords.x; } ) .onComplete( () => { @@ -310,11 +307,11 @@ export default class Valiant { } // Set the ship as ready. - l.scenograph.actors.player.vehicle.ready = true; - l.scenograph.actors.player.vehicle.camera_distance = l.scenograph.actors.player.vehicle.default_camera_distance + ( l.current_scene.room_depth / 2 ); - l.scenograph.actors.player.vehicle.mesh.userData.object.position.x = l.scenograph.actors.player.vehicle.mesh.position.x; - l.scenograph.actors.player.vehicle.mesh.userData.object.position.y = l.scenograph.actors.player.vehicle.mesh.position.y; - l.scenograph.actors.player.vehicle.mesh.userData.object.position.z = l.scenograph.actors.player.vehicle.mesh.position.z; + l.current_scene.objects.demoShip.ready = true; + l.current_scene.objects.demoShip.camera_distance = l.current_scene.objects.demoShip.default_camera_distance + ( l.current_scene.room_depth / 2 ); + l.current_scene.objects.demoShip.mesh.userData.object.position.x = l.current_scene.objects.demoShip.mesh.position.x; + l.current_scene.objects.demoShip.mesh.userData.object.position.y = l.current_scene.objects.demoShip.mesh.position.y; + l.current_scene.objects.demoShip.mesh.userData.object.position.z = l.current_scene.objects.demoShip.mesh.position.z; } ); } @@ -331,12 +328,12 @@ export default class Valiant { let changing = false; for ( const [ controlName, keyMapping ] of Object.entries( mappings ) ) { if ( l.scenograph.controls.keyboard.pressed( keyMapping ) ) { - l.scenograph.actors.player.vehicle.mesh.userData.object.controls[ controlName ] = true; + this.mesh.userData.object.controls[ controlName ] = true; changing = true; } else { - l.scenograph.actors.player.vehicle.mesh.userData.object.controls[ controlName ] = false; + this.mesh.userData.object.controls[ controlName ] = false; if ( l.scenograph.controls.touch ) { // Check if any touchpad controls are being pressed @@ -350,54 +347,54 @@ export default class Valiant { ) { changing = true; if ( l.scenograph.controls.touch.controls.moveUp ) { - l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveUp = true; + this.mesh.userData.object.controls.moveUp = true; } if ( l.scenograph.controls.touch.controls.moveDown ) { - l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveDown = true; + this.mesh.userData.object.controls.moveDown = true; } if ( l.scenograph.controls.touch.controls.moveForward ) { - l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleUp = true; + this.mesh.userData.object.controls.throttleUp = true; } if ( l.scenograph.controls.touch.controls.moveBackward ) { - l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleDown = true; + this.mesh.userData.object.controls.throttleDown = true; } if ( l.scenograph.controls.touch.controls.moveLeft ) { - l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveLeft = true; + this.mesh.userData.object.controls.moveLeft = true; } if ( l.scenograph.controls.touch.controls.moveRight ) { - l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveRight = true; + this.mesh.userData.object.controls.moveRight = true; } } } } } - l.scenograph.actors.player.vehicle.mesh.userData.object.controls.changing = changing; + this.mesh.userData.object.controls.changing = changing; } updateAnimation( delta ) { - if ( l.scenograph.actors.player.vehicle.mixer ) { - l.scenograph.actors.player.vehicle.mixer.update( delta ); + if ( this.mixer ) { + this.mixer.update( delta ); } // Rock the ship forward and back when moving horizontally - if ( l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleDown || l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleUp ) { - let pitchChange = l.scenograph.actors.player.vehicle.mesh.userData.object.controls.throttleUp ? -1 : 1; - if ( Math.abs( l.scenograph.actors.player.vehicle.mesh.rotation.x ) < 1 / 4 ) { - l.scenograph.actors.player.vehicle.mesh.rotation.x += pitchChange / 10 / 180; + if ( this.mesh.userData.object.controls.throttleDown || this.mesh.userData.object.controls.throttleUp ) { + let pitchChange = this.mesh.userData.object.controls.throttleUp ? -1 : 1; + if ( Math.abs( this.mesh.rotation.x ) < 1 / 4 ) { + this.mesh.rotation.x += pitchChange / 10 / 180; } } // Rock the ship forward and back when moving vertically if ( - l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveDown + this.mesh.userData.object.controls.moveDown || - l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveUp + this.mesh.userData.object.controls.moveUp ) { - let elevationChange = l.scenograph.actors.player.vehicle.mesh.userData.object.controls.moveDown ? -1 : 1; - if ( Math.abs( l.scenograph.actors.player.vehicle.mesh.rotation.x ) < 1 / 8 ) { - l.scenograph.actors.player.vehicle.mesh.rotation.x += elevationChange / 10 / 180; + let elevationChange = this.mesh.userData.object.controls.moveDown ? -1 : 1; + if ( Math.abs( this.mesh.rotation.x ) < 1 / 8 ) { + this.mesh.rotation.x += elevationChange / 10 / 180; } if ( Math.abs( l.scenograph.cameras.player.rotation.x ) < 1 / 8 ) { @@ -413,27 +410,27 @@ export default class Valiant { // Update the position of the aircraft to spot determined by game logic. updateMesh() { - l.scenograph.actors.player.vehicle.mesh.position.x = l.scenograph.actors.player.vehicle.mesh.userData.object.position.x; - l.scenograph.actors.player.vehicle.mesh.position.y = l.scenograph.actors.player.vehicle.mesh.userData.object.position.y; - l.scenograph.actors.player.vehicle.mesh.position.z = l.scenograph.actors.player.vehicle.mesh.userData.object.position.z; - l.scenograph.actors.player.vehicle.mesh.rotation.x = l.scenograph.actors.player.vehicle.mesh.userData.object.rotation.x; - l.scenograph.actors.player.vehicle.mesh.rotation.y = l.scenograph.actors.player.vehicle.mesh.userData.object.rotation.y; - l.scenograph.actors.player.vehicle.mesh.rotation.z = l.scenograph.actors.player.vehicle.mesh.userData.object.rotation.z; + this.mesh.position.x = this.mesh.userData.object.position.x; + this.mesh.position.y = this.mesh.userData.object.position.y; + this.mesh.position.z = this.mesh.userData.object.position.z; + this.mesh.rotation.x = this.mesh.userData.object.rotation.x; + this.mesh.rotation.y = this.mesh.userData.object.rotation.y; + this.mesh.rotation.z = this.mesh.userData.object.rotation.z; } updateCamera( rY, tY, tZ ) { var radian = ( Math.PI / 180 ); - l.scenograph.actors.player.vehicle.camera_distance = l.scenograph.actors.player.vehicle.default_camera_distance + ( l.current_scene.room_depth / 2 ); - if ( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed < 0 ) { - l.scenograph.actors.player.vehicle.camera_distance -= l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed * 4; + this.camera_distance = this.default_camera_distance + ( l.current_scene.room_depth / 2 ); + if ( this.mesh.userData.object.airSpeed < 0 ) { + this.camera_distance -= this.mesh.userData.object.airSpeed * 4; } - let xDiff = l.scenograph.actors.player.vehicle.mesh.position.x; - let zDiff = l.scenograph.actors.player.vehicle.mesh.position.z; + let xDiff = this.mesh.position.x; + let zDiff = this.mesh.position.z; - l.scenograph.cameras.player.position.x = xDiff + l.scenograph.actors.player.vehicle.camera_distance * Math.sin( l.scenograph.actors.player.vehicle.mesh.rotation.y ); - l.scenograph.cameras.player.position.z = zDiff + l.scenograph.actors.player.vehicle.camera_distance * Math.cos( l.scenograph.actors.player.vehicle.mesh.rotation.y ); + l.scenograph.cameras.player.position.x = xDiff + this.camera_distance * Math.sin( this.mesh.rotation.y ); + l.scenograph.cameras.player.position.z = zDiff + this.camera_distance * Math.cos( this.mesh.rotation.y ); if ( rY != 0 ) { @@ -442,32 +439,32 @@ export default class Valiant { else { // Check there is y difference and the rotation pad isn't being pressed. if ( - l.scenograph.cameras.player.rotation.y != l.scenograph.actors.player.vehicle.mesh.rotation.y && + l.scenograph.cameras.player.rotation.y != this.mesh.rotation.y && ( l.scenograph.controls.touch && !l.scenograph.controls.touch.controls.rotationPad.mouseDown ) ) { // Get the difference in y rotation betwen the camera and ship - let yDiff = l.scenograph.actors.player.vehicle.mesh.rotation.y - l.scenograph.cameras.player.rotation.y; + let yDiff = this.mesh.rotation.y - l.scenograph.cameras.player.rotation.y; // Check the y difference is larger than 1/100th of a radian if ( Math.abs( yDiff ) > radian / 100 ) { // Add 1/60th of the difference in rotation, as FPS currently capped to 60. - l.scenograph.cameras.player.rotation.y += ( l.scenograph.actors.player.vehicle.mesh.rotation.y - l.scenograph.cameras.player.rotation.y ) * 1 / 60; + l.scenograph.cameras.player.rotation.y += ( this.mesh.rotation.y - l.scenograph.cameras.player.rotation.y ) * 1 / 60; } else { - l.scenograph.cameras.player.rotation.y = l.scenograph.actors.player.vehicle.mesh.rotation.y; + l.scenograph.cameras.player.rotation.y = this.mesh.rotation.y; } } } - let xDiff2 = tZ * Math.sin( l.scenograph.actors.player.vehicle.mesh.rotation.y ), - zDiff2 = tZ * Math.cos( l.scenograph.actors.player.vehicle.mesh.rotation.y ); + let xDiff2 = tZ * Math.sin( this.mesh.rotation.y ), + zDiff2 = tZ * Math.cos( this.mesh.rotation.y ); - if ( l.scenograph.actors.player.vehicle.mesh.position.y + tY >= 1 ) { + if ( this.mesh.position.y + tY >= 1 ) { l.scenograph.cameras.player.position.y += tY; } @@ -489,78 +486,78 @@ export default class Valiant { **/ animate( delta ) { - if ( l.scenograph.actors.player.vehicle.ready ) { + if ( l.current_scene.objects.demoShip.ready ) { if ( l.current_scene.settings.game_controls ) { if ( l.scenograph.actors.player.mode == 'vehicle' ) { // Detect keyboard input and pass it to the ship state model. - l.scenograph.actors.player.vehicle.updateControls(); + this.updateControls(); } if ( l.scenograph.modes.multiplayer.connected ) { - l.scenograph.modes.multiplayer.socket.emit( 'input', l.scenograph.actors.player.vehicle.mesh.userData.object.controls ); + l.scenograph.modes.multiplayer.socket.emit( 'input', this.mesh.userData.object.controls ); } - l.scenograph.actors.player.vehicle.mesh.userData.actor.animate( delta ); + this.mesh.userData.actor.animate( delta ); } if ( l.mode != 'hangar') - l.scenograph.actors.player.vehicle.updateAnimation( delta ); + this.updateAnimation( delta ); // Update the ships state model. - let [ rY, tY, tZ ] = l.scenograph.actors.player.vehicle.mesh.userData.object.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); - l.scenograph.actors.player.vehicle.updateMesh(); + let [ rY, tY, tZ ] = this.mesh.userData.object.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); + this.updateMesh(); - l.scenograph.actors.player.vehicle.updateCamera( rY, tY, tZ ); + this.updateCamera( rY, tY, tZ ); - l.scenograph.actors.player.vehicle.animateTrail( rY ); + this.animateTrail( rY ); } } animateTrail( rY ) { - if ( l.scenograph.actors.player.vehicle.trail ) { + if ( this.trail ) { // Fix the trail being too far behind. let trailOffset = 0; // Only offset the trail effect if we are going forward which is (z-1) in numerical terms - if ( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed < 0 ) { + if ( this.mesh.userData.object.airSpeed < 0 ) { // Update ship thruster - l.scenograph.actors.player.vehicle.animateThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.centralConeBurner, .5 ); - l.scenograph.actors.player.vehicle.animateThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.outerCylBurner, .5 ); + this.animateThruster( this.mesh.userData.object.airSpeed, this.thruster.centralConeBurner, .5 ); + this.animateThruster( this.mesh.userData.object.airSpeed, this.thruster.outerCylBurner, .5 ); - l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.rearConeBurner, -1 ); - l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.centralConeBurner, 1 ); - l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.outerCylBurner, -1 ); - l.scenograph.actors.player.vehicle.spinThruster( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed, l.scenograph.actors.player.vehicle.thruster.innerCylBurner, 1 ); + this.spinThruster( this.mesh.userData.object.airSpeed, this.thruster.rearConeBurner, -1 ); + this.spinThruster( this.mesh.userData.object.airSpeed, this.thruster.centralConeBurner, 1 ); + this.spinThruster( this.mesh.userData.object.airSpeed, this.thruster.outerCylBurner, -1 ); + this.spinThruster( this.mesh.userData.object.airSpeed, this.thruster.innerCylBurner, 1 ); // Limit playback rate to 5x as large values freak out the browser. - l.scenograph.actors.player.vehicle.thruster.videoElement.playbackRate = Math.min( 5, 0.25 + Math.abs( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed ) ); + this.thruster.videoElement.playbackRate = Math.min( 5, 0.25 + Math.abs( this.mesh.userData.object.airSpeed ) ); - trailOffset += l.scenograph.actors.player.vehicle.trail_position_z - Math.abs( l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed ); + trailOffset += this.trail_position_z - Math.abs( this.mesh.userData.object.airSpeed ); - l.scenograph.actors.player.vehicle.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, .8 ); // RGBA. + this.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, .8 ); // RGBA. } else { - l.scenograph.actors.player.vehicle.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, 0 ); // RGBA. + this.trail.mesh.material.uniforms.headColor.value.set( 255 / 255, 212 / 255, 148 / 255, 0 ); // RGBA. } // Update the trail position based on above calculations. - l.scenograph.actors.player.vehicle.trail.targetObject.position.y = l.scenograph.actors.player.vehicle.trail_position_y + l.scenograph.actors.player.vehicle.mesh.userData.object.verticalSpeed; - l.scenograph.actors.player.vehicle.trail.targetObject.position.z = trailOffset; + this.trail.targetObject.position.y = this.trail_position_y + this.mesh.userData.object.verticalSpeed; + this.trail.targetObject.position.z = trailOffset; if ( rY != 0 ) { - l.scenograph.actors.player.vehicle.trail.targetObject.position.x = rY * l.scenograph.actors.player.vehicle.mesh.userData.object.airSpeed; - l.scenograph.actors.player.vehicle.trail.targetObject.position.y += Math.abs( l.scenograph.actors.player.vehicle.trail.targetObject.position.x ) / 4; + this.trail.targetObject.position.x = rY * this.mesh.userData.object.airSpeed; + this.trail.targetObject.position.y += Math.abs( this.trail.targetObject.position.x ) / 4; } else { - l.scenograph.actors.player.vehicle.trail.targetObject.position.x = 0; + this.trail.targetObject.position.x = 0; } - l.scenograph.actors.player.vehicle.trail.update(); + this.trail.update(); } } diff --git a/client/src/app/scenograph/tweens.js b/client/src/app/scenograph/tweens.js index d71a239d..289ae665 100644 --- a/client/src/app/scenograph/tweens.js +++ b/client/src/app/scenograph/tweens.js @@ -2,7 +2,7 @@ * Tweens * * Helpers for managing scene tweens. - * + * * @todo: Move overworld specific tweens into the scenes/overworld folder */ @@ -218,7 +218,7 @@ function flickerEffect() { */ function enterTheOffice() { let coords = { x: 15 + l.current_scene.room_depth / 2 }; // Start at (0, 0) - let targetZ = l.scenograph.actors.player.vehicle.default_camera_distance + l.current_scene.room_depth / 2; + let targetZ = l.current_scene.objects.demoShip.default_camera_distance + l.current_scene.room_depth / 2; return new TWEEN.Tween( coords, false ) // Create a new tween that modifies 'coords'. .to( { x: targetZ }, l.config.settings.skipintro ? 0 : 1000 ) // Move to (300, 200) in 1 second. .easing( TWEEN.Easing.Quadratic.InOut ) // Use an easing function to make the animation smooth. @@ -311,7 +311,7 @@ function dollyUp() { return new TWEEN.Tween( l.scenograph.cameras.player.position ) .to( { y: l.scenograph.cameras.playerY }, l.config.settings.skipintro ? 0 : 500 ) // Set the duration of the animation .onUpdate( () => { - //l.scenograph.cameras.player.lookAt(l.scenograph.actors.player.vehicle.mesh.position); + //l.scenograph.cameras.player.lookAt(l.current_scene.objects.demoShip.mesh.position); l.scenograph.cameras.player.updateProjectionMatrix(); } ) .onComplete( () => { diff --git a/game/src/actors/pirate.ts b/game/src/actors/pirate.ts index a9c42f15..4e2b2d71 100644 --- a/game/src/actors/pirate.ts +++ b/game/src/actors/pirate.ts @@ -1,6 +1,6 @@ /** * Pirate NPC - * + * * Defines an aggressive pirate NPC that attacks nearby aircraft. */ @@ -16,11 +16,11 @@ export default class Pirate extends BaseActor { path; - pursue; + targets; constructor( mesh, scene ) { super( mesh, scene ); - + this.marker = document.querySelector('#map .marker-bot svg path'); if ( this.type == 'vehicle' ) { @@ -40,14 +40,11 @@ export default class Pirate extends BaseActor { this.path.add( new YUKA.Vector3( loopDistance, this.mesh.position.y, - loopDistance ) ); this.path.add( new YUKA.Vector3( - loopDistance, this.mesh.position.y, - loopDistance ) ); this.path.add( new YUKA.Vector3( - loopDistance, this.mesh.position.y, loopDistance ) ); - + this.follow = new YUKA.FollowPathBehavior( this.path ); this.entity.steering.add( this.follow ); - // @todo: v7 Figure out a way to signal this to happen without l. global object access - this.pursue = new YUKA.PursuitBehavior( l.scenograph.actors.player.vehicle.mesh.userData.actor.entity, 1 ); - this.pursue.active = false; - this.entity.steering.add( this.pursue ); + this.targets = new Map(); } } @@ -59,18 +56,30 @@ export default class Pirate extends BaseActor { } // @todo: v7 Figure out a way to signal this to happen without l. global object access - if ( this.entity.vision.visible( l.scenograph.actors.player.vehicle.mesh.position ) === true ) { - this.pursue.active = true; - this.follow.active = false; - - if ( this.marker ) - this.marker.style.stroke = 'rgb( 255, 0, 0 )'; - } else { - this.pursue.active = false; - this.follow.active = true; - - if ( this.marker ) - this.marker.style.stroke = 'rgb( 255, 255, 0 )'; + for (let actor in l.scenograph.actors.getAll()) { + if ( this.targets.size < l.scenograph.actors.size ) { + this.targets.set(actor.name, actor); + let pursue = new YUKA.PursuitBehavior( actor.vehicle.mesh.userData.actor.entity, 1 ); + pursue.active = false; + this.entity.steering.add( pursue ); + } + + // @todo: v7 Figure out a way to signal this to happen without l. global object access + if ( this.entity.vision.visible( actor.vehicle.mesh.position ) === true ) { + this.pursue.active = true; + this.follow.active = false; + + if ( this.marker ) + this.marker.style.stroke = 'rgb( 255, 0, 0 )'; + } else { + this.pursue.active = false; + this.follow.active = true; + + if ( this.marker ) + this.marker.style.stroke = 'rgb( 255, 255, 0 )'; + } } + + } } diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml index 0c8c12db..8564d596 100644 --- a/game/src/scenes/overworld.yml +++ b/game/src/scenes/overworld.yml @@ -32,6 +32,20 @@ actors: x: 0 y: -12.56 z: 0 + - name: Player TWo + model: person + class: player + hangar: + structure: Lambda City + hangarName: Hangar 1 + position: + x: -65000 + y: -35000 + z: 1500 + rotation: + x: 0 + y: -12.56 + z: 0 - name: Pirate One model: raven class: pirate From b028633ef4cd4a98f5b96ca776d186018f63eb62 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 19 Dec 2025 04:17:08 +0000 Subject: [PATCH 46/58] #31 - Hardcoding initial player to be player one --- client/src/app/routes/singleplayer.js | 2 +- client/src/app/scenograph/actors.js | 10 +++++++++- client/src/app/scenograph/director.js | 7 +------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/client/src/app/routes/singleplayer.js b/client/src/app/routes/singleplayer.js index e2d7b985..ef3e383f 100644 --- a/client/src/app/routes/singleplayer.js +++ b/client/src/app/routes/singleplayer.js @@ -25,7 +25,7 @@ export default class singlePlayerRoute { // Set client mode. l.mode = 'single_player'; - l.scenograph.actors.player.setMode('vehicle'); + l.scenograph.actors.map.get('Player One').setMode('vehicle'); } diff --git a/client/src/app/scenograph/actors.js b/client/src/app/scenograph/actors.js index 7a6bc21c..c6444dab 100644 --- a/client/src/app/scenograph/actors.js +++ b/client/src/app/scenograph/actors.js @@ -15,15 +15,23 @@ import Player from '@/scenograph/actors/player.js'; export default class Actors { map; + player; constructor() { this.map = new Map(); } - registerActor(actor) { + async registerActor(actor) { + console.log(actor); if ( actor.class == 'player' ) { let player = new Player( actor ); + await player.load(); this.map.set(actor.name, player); + + // @todo: Add player to the game world more dynamically. + if ( actor.name == 'Player One' ) { + this.player = player; + } } } diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index c9c577c6..acab0156 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -282,15 +282,10 @@ export default class Director { ); } if ( object_config.class == 'player' ) { - l.scenograph.actors.registerActor( - object_config.name, - object_config - ); - + await l.scenograph.actors.registerActor( object_config ); } } ); - } async loadObject( config, object ) { From ce16c178e56b42f30c03c495bef654349a32a13a Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 26 Dec 2025 20:13:48 +0000 Subject: [PATCH 47/58] #31 - Adding secondary game classes to be part of new world loop and began implementing world update loop --- client/src/app/scenograph/actors.js | 21 ++- .../app/scenograph/objects/vehicles/person.js | 40 +++--- game/src/actors/base2.ts | 41 ++++++ game/src/actors/player.ts | 6 +- game/src/actors/player2.ts | 32 +++++ game/src/helpers.ts | 75 ++++++++--- game/src/objects/person2.ts | 122 ++++++++++++++++++ game/src/scenes/overworld.yml | 2 +- game/src/world.ts | 61 +++++++-- 9 files changed, 341 insertions(+), 59 deletions(-) create mode 100644 game/src/actors/base2.ts create mode 100644 game/src/actors/player2.ts create mode 100644 game/src/objects/person2.ts diff --git a/client/src/app/scenograph/actors.js b/client/src/app/scenograph/actors.js index c6444dab..d360e140 100644 --- a/client/src/app/scenograph/actors.js +++ b/client/src/app/scenograph/actors.js @@ -22,17 +22,16 @@ export default class Actors { } async registerActor(actor) { - console.log(actor); - if ( actor.class == 'player' ) { - let player = new Player( actor ); - await player.load(); - this.map.set(actor.name, player); - - // @todo: Add player to the game world more dynamically. - if ( actor.name == 'Player One' ) { - this.player = player; - } - } + if ( actor.class == 'player' ) { + let player = new Player( actor ); + await player.load(); + this.map.set(actor.name, player); + + // @todo: Add player to the game world more dynamically. + if ( actor.name == 'Player One' ) { + this.player = player; + } + } } get(name) { diff --git a/client/src/app/scenograph/objects/vehicles/person.js b/client/src/app/scenograph/objects/vehicles/person.js index 47e87c2e..497f19bf 100644 --- a/client/src/app/scenograph/objects/vehicles/person.js +++ b/client/src/app/scenograph/objects/vehicles/person.js @@ -1,8 +1,8 @@ /** * Person object. - * + * * Rig to be used like a vehicle when in first person mode. - * + * * @todo: Add some limbs or a body model to see. */ import * as THREE from 'three'; @@ -98,32 +98,32 @@ export default class Person extends PersonBase { updateCamera( rY, tY, tZ ) { var radian = ( Math.PI / 180 ); - + l.scenograph.actors.player.person.camera_distance = l.scenograph.actors.player.person.default_camera_distance + ( l.current_scene.room_depth / 20 ); if ( l.scenograph.actors.player.person.airSpeed < 0 ) { l.scenograph.actors.player.person.camera_distance -= l.scenograph.actors.player.person.airSpeed * 4; } - + let xDiff = l.scenograph.actors.player.person.mesh.position.x; let zDiff = l.scenograph.actors.player.person.mesh.position.z; - + l.scenograph.cameras.player.position.x = xDiff + l.scenograph.actors.player.person.camera_distance * Math.sin( l.scenograph.actors.player.person.mesh.rotation.y ); l.scenograph.cameras.player.position.z = zDiff + l.scenograph.actors.player.person.camera_distance * Math.cos( l.scenograph.actors.player.person.mesh.rotation.y ); - + // if ( rY != 0 ) { - + // l.scenograph.cameras.player.rotation.y += rY; // } // else { - // // Check there is y difference and the rotation pad isn't being pressed. + // // Check there is y difference and the rotation pad isn't being pressed. // if ( // l.scenograph.cameras.player.rotation.y != l.scenograph.actors.player.person.mesh.rotation.y && // ( l.scenograph.controls.touch && !l.scenograph.controls.touch.controls.rotationPad.mouseDown ) // ) { - + // // Get the difference in y rotation betwen the camera and ship // let yDiff = l.scenograph.actors.player.person.mesh.rotation.y - l.scenograph.cameras.player.rotation.y; - + // // Check the y difference is larger than 1/100th of a radian // if ( // Math.abs( yDiff ) > radian / 100 @@ -134,31 +134,31 @@ export default class Person extends PersonBase { // else { // l.scenograph.cameras.player.rotation.y = l.scenograph.actors.player.person.mesh.rotation.y; // } - + // } - + // } - + // let xDiff2 = tZ * Math.sin( l.scenograph.actors.player.person.mesh.rotation.y ), // zDiff2 = tZ * Math.cos( l.scenograph.actors.player.person.mesh.rotation.y ); - + // if ( l.scenograph.actors.player.person.mesh.position.y + tY >= 1 ) { // l.scenograph.cameras.player.position.y += tY; // } - + // l.scenograph.cameras.player.position.x += xDiff2; // l.scenograph.cameras.player.position.z += zDiff2; - + l.scenograph.cameras.player.updateProjectionMatrix(); } /** * Animate hook. - * + * * This method is called within the main animation loop and * therefore must only reference global objects or properties. - * + * * @method animate * @memberof Raven * @global @@ -172,6 +172,8 @@ export default class Person extends PersonBase { // Update the persons state model. let [ rY, tY, tZ ] = l.scenograph.actors.player.person.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); +// console.log(l.scenograph.actors.player.person.position); + // Update the persons mesh l.scenograph.actors.player.person.updateMesh(); @@ -184,5 +186,5 @@ export default class Person extends PersonBase { } } - + } diff --git a/game/src/actors/base2.ts b/game/src/actors/base2.ts new file mode 100644 index 00000000..d57e09e1 --- /dev/null +++ b/game/src/actors/base2.ts @@ -0,0 +1,41 @@ +/** + * Actor Base + * + * Provides an instance of an actor that can be attached to game objects. + * + * Behaviours such as bot AI, path finding and combat are attached to the entity of actors. + */ + +import * as YUKA from 'yuka'; + +export default class BaseActor { + + public entity: YUKA.GameEntity; // set by instantiator. + + public score: { kills: number; deaths: number } = { kills: 0, deaths: 0 }; + public faction: string = 'winthrom'; + public standing: { union: number, winthrom: number, zaar: number } = { union: 0.5, winthrom: 1.0, zaar: 0.0 } + + public controls: { + changing: boolean, + forward: boolean; + back: boolean; + jump: boolean; + crouch: boolean; + turnLeft: boolean; + turnRight: boolean; + } = { + changing: false, + forward: false, + back: false, + jump: false, + crouch: false, + turnLeft: false, + turnRight: false + }; + + constructor( ) { + + } + +} diff --git a/game/src/actors/player.ts b/game/src/actors/player.ts index 5cbaafa7..71a3d48e 100644 --- a/game/src/actors/player.ts +++ b/game/src/actors/player.ts @@ -1,8 +1,8 @@ /** * Player Agent - * + * * Defines a player entity in the game world. - * + * * @todo: Remove this file if not used. */ @@ -14,7 +14,7 @@ export default class Player extends BaseActor { constructor( mesh, scene ) { super( mesh, scene ); - + if ( this.type == 'vehicle' ) { this.entity.position.z = this.mesh.position.z; this.entity.position.y = this.mesh.position.y; diff --git a/game/src/actors/player2.ts b/game/src/actors/player2.ts new file mode 100644 index 00000000..4c111a23 --- /dev/null +++ b/game/src/actors/player2.ts @@ -0,0 +1,32 @@ +/** + * Player Agent + * + * Defines a player entity in the game world. + */ + +import * as YUKA from 'yuka'; + +import ActorBase from './base2'; + +export default class ActorPlayer extends ActorBase { + + constructor( ) { + super( ); + + this.entity = new YUKA.GameEntity(); + + } + + /** + * Update hook. + * + * This method is called within the main game world update loop. + * + * @method update + * @memberof BaseActor + * @global + **/ + update(delta) { + this.entity.update(delta); + } +} diff --git a/game/src/helpers.ts b/game/src/helpers.ts index 6202a774..58e3ecfc 100644 --- a/game/src/helpers.ts +++ b/game/src/helpers.ts @@ -2,14 +2,57 @@ * Helpers */ + /** + * Change aircraft velocity based on current and what buttons are pushed by the player. + * + * @param currentVelocity + * @param increasePushed + * @param decreasePushed + */ + export function changeVelocity(stepIncrease: number, stepDecrease: number, currentVelocity: number, increasePushed: boolean, decreasePushed: boolean, increaseMax: number, decreaseMax: number, dragFactor: number): number { + let newVelocity = currentVelocity; + + if (increasePushed) { + + // Check if the change puts us over the max. + let tempVelocity = newVelocity - stepIncrease; + if (Math.abs(increaseMax) >= Math.abs(tempVelocity)) + newVelocity = tempVelocity; + } + else { + if (decreasePushed) { + + // Check if the change puts us over the max. + let tempVelocity = newVelocity + stepDecrease; + if (Math.abs(decreaseMax) >= tempVelocity) + newVelocity = tempVelocity; + } + else { + if (newVelocity != 0) { + + if (Math.abs(newVelocity) > 0.1) { + // Ease out the velocity exponentially to simulate drag + newVelocity *= dragFactor; + } + else { + newVelocity = 0; + } + } + + } + } + + return newVelocity; +} + /** * Get speed delta from ideal of 60FPS - * - * All in game speeds are calculated to 60 FPS, this function checks what the current delta is - * - * @param - * - * @returns + * + * All in game speeds are calculated to 60 FPS, this function checks what the current delta is + * + * @param + * + * @returns */ export function normaliseSpeedDelta(time_delta: number): number { return 1.; @@ -18,11 +61,11 @@ export function normaliseSpeedDelta(time_delta: number): number { /** * Ease out exponentially - * + * * @see https://easings.net/#easeOutExpo - * + * * @param x - * + * * @returns number eased out number */ export function easeOutExpo(x: number): number { @@ -31,11 +74,11 @@ export function easeOutExpo(x: number): number { /** * Ease in quadratrically - * + * * @see https://easings.net/#easeOutExpo - * + * * @param x - * + * * @returns number eased out number */ export function easeInQuad(x: number): number { @@ -44,11 +87,11 @@ export function easeInQuad(x: number): number { /** * Ease in and out expotentially - * + * * @see https://easings.net/#easeOutExpo - * + * * @param x - * + * * @returns number eased out number */ export function easeInOutExpo(x: number): number { @@ -58,4 +101,4 @@ export function easeInOutExpo(x: number): number { ? 1 : x < 0.5 ? Math.pow(2, 20 * x - 10) / 2 : (2 - Math.pow(2, -20 * x + 10)) / 2; -} \ No newline at end of file +} diff --git a/game/src/objects/person2.ts b/game/src/objects/person2.ts new file mode 100644 index 00000000..79ad8d93 --- /dev/null +++ b/game/src/objects/person2.ts @@ -0,0 +1,122 @@ +/** + * Base Aircraft class + * + * @todo: + * - Add weight and wind resistance + */ + +import BaseActor from '../actors/base2'; +import { changeVelocity, normaliseSpeedDelta, easeOutExpo, easeInQuad, easeInOutExpo } from '../helpers'; + +export default class Person { + + public actor?: BaseActor; + + public hitPoints: number = 100; + public airSpeed: number = 0; + public verticalSpeed: number = 0; + public maxForward: number = 16 / 60; // 8 km/h @ 60 FPS + public maxBackward: number = 16 / 60; + public maxUp: number = 4 / 60; + public maxDown: number = 16 / 60; // gravity? + + public position: { x: number; y: number; z: number } = { x: 0, y: 0, z: 0 }; + public rotation: { x: number; y: number; z: number } = { x: 0, y: 0, z: 0 }; + + public rY: number = 0; + public tY: number = 0; + public tZ: number = 0; + + constructor() { + } + + update( time_delta: number ) { + if (this.actor) { + let stepSize: number = .025 * normaliseSpeedDelta( time_delta ), + rY: number = 0, + tZ: number = 0, + tY: number = 0, + radian: number = - (Math.PI / 180) * stepSize * 100; + + if ( this.actor.controls.forward || this.actor.controls.back ){ + // Update Airspeed (horizontal velocity) + this.airSpeed = changeVelocity( + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.airSpeed ) / this.maxForward ) ), + stepSize, + this.airSpeed, + this.actor.controls.forward, + this.actor.controls.back, + this.maxForward, + this.maxBackward, + easeOutExpo( 0.987 ) + ); + } + else { + this.airSpeed = 0; + } + + // Update Vertical Speed (velocity) + this.verticalSpeed = changeVelocity( + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxUp ) ), + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxDown ) ), + this.verticalSpeed, + this.actor.controls.crouch, // Note: Move Down/Up is reversed by design. + this.actor.controls.jump, + this.maxDown, + this.maxUp, + easeInQuad( 0.321 ) + ); + + // Check the vertical speed exceeds minimum threshold for change in vertical position + if (Math.abs(this.verticalSpeed) > 0.01) { + tY = this.verticalSpeed; + } + + // Turning + if (this.actor.controls.turnRight) { + rY += radian; + } + else { + if (this.actor.controls.turnLeft) { + rY -= radian; + } + } + + // Check if we have significant airspeed + if (Math.abs(this.airSpeed) > 0.01) { + + // Set change in Z position based on airspeed + tZ = this.airSpeed; + + } + + if (rY != 0) { + if (Math.abs(this.rotation.z) < Math.PI / 4) { + this.rotation.z += rY / Math.PI; + } + + this.rotation.y += rY; + } + + let xDiff = tZ * Math.sin(this.rotation.y), + zDiff = tZ * Math.cos(this.rotation.y); + + // "1" is the floor limit as it's the ocean surface and the camera clips through the water any lower. + if (this.position.y + tY >= 1 ) { + this.position.y += tY; + } else { + this.verticalSpeed = 0; + } + + this.position.x += xDiff; + this.position.z += zDiff; + + this.rY = rY; + this.tY = tY; + this.tZ = tZ; + + } + + } + +} diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml index 8564d596..1d90aa3f 100644 --- a/game/src/scenes/overworld.yml +++ b/game/src/scenes/overworld.yml @@ -32,7 +32,7 @@ actors: x: 0 y: -12.56 z: 0 - - name: Player TWo + - name: Player Two model: person class: player hangar: diff --git a/game/src/world.ts b/game/src/world.ts index fb56c4f2..bfab4619 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -1,24 +1,27 @@ /** * Game World class - * + * * Loads and runs simulations of game scenes - * + * */ - +// @todo: allow dynamic loading of other scenes. import Overworld from "./scenes/overworld.yml"; +import ActorPlayer from './actors/player2'; +import ObjectPerson from './objects/person2'; + interface WorldInstance { actors: Record; objects: Record; } - + export default class World { public instance: WorldInstance; constructor( sceneName: string ) { if ( sceneName === 'Overworld' ) { - this.initialise( Overworld ); + this.initialise( Overworld ); } } @@ -37,13 +40,53 @@ export default class World { objects: config.objects }; - // console.log(this.instance); - // debugger; + this.lastUpdateTime = performance.now(); + this.fixedDelta = 16; // ~60 FPS for logic + + + this.load(); + this.start(); } - update() { + load() { + for (const actor of this.instance.actors.values()) { + if (actor.name =='Player Two') { + // Set actors first. + if (actor.class == 'player') { + actor.actor = new ActorPlayer(); + } + + // Set objects. + if (actor.model == 'person') { + actor.object = new ObjectPerson(); + } + // Set objects actor properties to actor. + if ( actor.actor && actor.object ) { + actor.object.actor = actor.actor; + } + } + } } -} + start() { + this.updateLoop = setInterval(() => this.update(), this.fixedDelta); + } + + stop() { + clearInterval(this.updateLoop); + } + + update() { + for (const actor of this.instance.actors.values()) { + if (actor.name =='Player Two') { + actor.actor.update(this.fixedDelta); + actor.object.update(this.fixedDelta); + } + console.log(this, actor); + //actor.updateState(this.fixedDelta); + } + debugger; + } +} From 40729ccc17b2e80f0c2ff562d8af7362662334cc Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 26 Dec 2025 20:38:34 +0000 Subject: [PATCH 48/58] #31 - Updating game world loader to separate config from instance --- game/src/world.ts | 63 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/game/src/world.ts b/game/src/world.ts index bfab4619..bcb7093d 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -10,6 +10,11 @@ import Overworld from "./scenes/overworld.yml"; import ActorPlayer from './actors/player2'; import ObjectPerson from './objects/person2'; +interface WorldConfig { + actors: Record; + objects: Record; +} + interface WorldInstance { actors: Record; objects: Record; @@ -17,6 +22,7 @@ interface WorldInstance { export default class World { + public config: WorldConfig; public instance: WorldInstance; constructor( sceneName: string ) { @@ -35,9 +41,13 @@ export default class World { * - loop over config to load game world simulation in here and scenograph in the client */ initialise( config ) { - this.instance = { + this.config = { actors: config.actors, objects: config.objects + } + this.instance = { + actors: new Map(), + objects: new Map() }; this.lastUpdateTime = performance.now(); @@ -46,26 +56,42 @@ export default class World { this.load(); this.start(); + } load() { - for (const actor of this.instance.actors.values()) { - if (actor.name =='Player Two') { - // Set actors first. - if (actor.class == 'player') { - actor.actor = new ActorPlayer(); - } - - // Set objects. - if (actor.model == 'person') { - actor.object = new ObjectPerson(); - } - - // Set objects actor properties to actor. - if ( actor.actor && actor.object ) { - actor.object.actor = actor.actor; - } + for (const actorConfig of this.config.actors.values()) { + const actorInstance: any = { config: actorConfig }; + // Set actors first. + actorInstance.actor = this.loadActor(actorConfig.class); + + // Set objects. + actorInstance.object = this.loadObject(actorConfig.model); + + // Set objects actor properties to actor. + if ( actorInstance.actor && actorInstance.object ) { + actorInstance.object.actor = actorInstance.actor; } + + this.instance.actors.set(actorConfig.name, actorInstance); + } + } + + loadActor(actorClass: string) { + if (actorClass == 'player') { + return new ActorPlayer(); + } + else { + return false; + } + } + + loadObject(actorModel: string) { + if (actorModel == 'person') { + return new ObjectPerson(); + } + else { + return false; } } @@ -83,10 +109,7 @@ export default class World { actor.actor.update(this.fixedDelta); actor.object.update(this.fixedDelta); } - console.log(this, actor); - //actor.updateState(this.fixedDelta); } - debugger; } } From 4274c5818191c229f4121e81676a768642032bd5 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 26 Dec 2025 20:39:23 +0000 Subject: [PATCH 49/58] #31 - Updating game world loader to separate config from instance --- client/src/app/scenograph/director.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index acab0156..6163f07c 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -247,7 +247,7 @@ export default class Director { async loadInstance() { - await this.world.instance.objects.forEach( async object_config => { + await this.world.config.objects.forEach( async object_config => { if ( object_config.model == 'extractor' ) { l.scenograph.director.loadObject( object_config, @@ -268,7 +268,7 @@ export default class Director { } } ); - this.world.instance.actors.forEach( async object_config => { + this.world.config.actors.forEach( async object_config => { if ( object_config.class == 'cargoShip' ) { l.scenograph.director.loadObject( object_config, From 5f988f1bd239a1e73c01860771858dbeb94cd581 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Fri, 26 Dec 2025 23:19:13 +0000 Subject: [PATCH 50/58] #31 - Changed person actor and object to update via world instance --- client/src/app/routes/hangar.js | 17 ++-- client/src/app/scenograph/actors.js | 15 ++-- client/src/app/scenograph/actors/player.js | 10 +-- client/src/app/scenograph/director.js | 80 ++++++++++--------- .../app/scenograph/objects/vehicles/person.js | 72 ++++++++--------- game/src/objects/person2.ts | 1 + game/src/world.ts | 4 +- 7 files changed, 98 insertions(+), 101 deletions(-) diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index ceaf6302..8c74dfbc 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -27,17 +27,17 @@ export default class hangarRoute { this.targetStructure = l.scenograph.objects.structures.platform.instances[0]; - this.loadHangar(); - + l.scenograph.actors.player = l.scenograph.actors.get('Player Two'); l.scenograph.actors.player.setMode('person'); + this.loadHangar(); + } loadHangar() { l.current_scene.scene.add(l.scenograph.objects.structures.hangar.mesh); - l.scenograph.objects.structures.platform.mesh.visible = false; + this.targetStructure.visible = false; l.scenograph.objects.structures.hangar.mesh.visible = true; - l.scenograph.objects.structures.hangar.mesh.position.copy( this.targetStructure.position ); l.scenograph.objects.structures.hangar.mesh.position.y = this.targetStructure.userData.config.hangars[0].position.y; @@ -45,11 +45,12 @@ export default class hangarRoute { l.scenograph.actors.player.vehicle.mesh.userData.object.position.z = this.targetStructure.position.z - 2.5; l.scenograph.actors.player.vehicle.mesh.userData.object.position.y = l.scenograph.objects.structures.hangar.mesh.position.y - 7.5; - l.scenograph.actors.player.person.position.x = this.targetStructure.position.x; - l.scenograph.actors.player.person.position.z = this.targetStructure.position.z + 10; - l.scenograph.actors.player.person.position.y = l.scenograph.objects.structures.hangar.mesh.position.y - 2.75; + l.scenograph.actors.player.actorInstance.object.position.x = this.targetStructure.position.x; + l.scenograph.actors.player.actorInstance.object.position.z = this.targetStructure.position.z + 10; + l.scenograph.actors.player.actorInstance.object.position.y = l.scenograph.objects.structures.hangar.mesh.position.y - 2.5 + ; - l.scenograph.cameras.active.position.copy(l.scenograph.actors.player.person.position); + l.scenograph.cameras.active.position.copy(l.scenograph.actors.player.actorInstance.object.position); if ( l.scenograph.controls.orbit ) { l.scenograph.cameras.orbit.updateProjectionMatrix(); diff --git a/client/src/app/scenograph/actors.js b/client/src/app/scenograph/actors.js index d360e140..20bca219 100644 --- a/client/src/app/scenograph/actors.js +++ b/client/src/app/scenograph/actors.js @@ -4,6 +4,8 @@ * @namespace l.scenograph.actors * @memberof l.scenograph * @global + * + * @todo: #31 consider removal now that world instance manages and updates actors. */ /** @@ -21,16 +23,11 @@ export default class Actors { this.map = new Map(); } - async registerActor(actor) { - if ( actor.class == 'player' ) { - let player = new Player( actor ); + async registerActor(actorInstance) { + if ( actorInstance.config.class == 'player' ) { + let player = new Player( actorInstance ); await player.load(); - this.map.set(actor.name, player); - - // @todo: Add player to the game world more dynamically. - if ( actor.name == 'Player One' ) { - this.player = player; - } + this.map.set(actorInstance.config.name, player); } } diff --git a/client/src/app/scenograph/actors/player.js b/client/src/app/scenograph/actors/player.js index 60257052..f3a8e113 100644 --- a/client/src/app/scenograph/actors/player.js +++ b/client/src/app/scenograph/actors/player.js @@ -28,9 +28,9 @@ export default class Player { // Vehicle model. vehicle; - constructor( actorConfig ) { + constructor( actorInstance ) { - this.actorConfig = actorConfig; + this.actorInstance = actorInstance; this.ready = false; @@ -63,12 +63,12 @@ export default class Player { ); // Setup person, used for the hangar scene. - this.person = new l.scenograph.objects.vehicles.person(); + this.person = new l.scenograph.objects.vehicles.person(this.actorInstance); l.current_scene.scene.add( - this.person.mesh + this.person.mesh ); l.current_scene.animation_queue.push( - this.person.animate + delta => this.person.animate(delta) ); } diff --git a/client/src/app/scenograph/director.js b/client/src/app/scenograph/director.js index 6163f07c..73a01759 100644 --- a/client/src/app/scenograph/director.js +++ b/client/src/app/scenograph/director.js @@ -247,44 +247,48 @@ export default class Director { async loadInstance() { - await this.world.config.objects.forEach( async object_config => { - if ( object_config.model == 'extractor' ) { - l.scenograph.director.loadObject( - object_config, - await l.scenograph.objects.structures.extractor.get() - ); - } - if ( object_config.model == 'platform' ) { - l.scenograph.director.loadObject( - object_config, - await l.scenograph.objects.structures.platform.get() - ); - } - if ( object_config.model == 'refinery' ) { - l.scenograph.director.loadObject( - object_config, - await l.scenograph.objects.structures.refinery.get() - ); - } - } ); - - this.world.config.actors.forEach( async object_config => { - if ( object_config.class == 'cargoShip' ) { - l.scenograph.director.loadObject( - object_config, - await l.scenograph.objects.vehicles.cargoShip.get() - ); - } - if ( object_config.class == 'pirate' ) { - l.scenograph.director.loadObject( - object_config, - await l.scenograph.objects.vehicles.raven.get() - ); - } - if ( object_config.class == 'player' ) { - await l.scenograph.actors.registerActor( object_config ); - } - } ); + await this.world.config.objects.forEach( async object_config => { + if ( object_config.model == 'extractor' ) { + l.scenograph.director.loadObject( + object_config, + await l.scenograph.objects.structures.extractor.get() + ); + } + if ( object_config.model == 'platform' ) { + l.scenograph.director.loadObject( + object_config, + await l.scenograph.objects.structures.platform.get() + ); + } + if ( object_config.model == 'refinery' ) { + l.scenograph.director.loadObject( + object_config, + await l.scenograph.objects.structures.refinery.get() + ); + } + } ); + + this.world.config.actors.forEach( async object_config => { + if ( object_config.class == 'cargoShip' ) { + l.scenograph.director.loadObject( + object_config, + await l.scenograph.objects.vehicles.cargoShip.get() + ); + } + if ( object_config.class == 'pirate' ) { + l.scenograph.director.loadObject( + object_config, + await l.scenograph.objects.vehicles.raven.get() + ); + } + + } ); + + this.world.instance.actors.forEach(async actorInstance => { + if ( actorInstance.config.class == 'player' ) { + await l.scenograph.actors.registerActor( actorInstance ); + } + }); } diff --git a/client/src/app/scenograph/objects/vehicles/person.js b/client/src/app/scenograph/objects/vehicles/person.js index 497f19bf..d013233e 100644 --- a/client/src/app/scenograph/objects/vehicles/person.js +++ b/client/src/app/scenograph/objects/vehicles/person.js @@ -12,19 +12,17 @@ import * as THREE from 'three'; */ import l from '@/helpers/l.js'; -import PersonBase from '#/game/src/objects/person'; +export default class Person { -export default class Person extends PersonBase { + constructor(actorInstance) { + // Set internal game accessor to the game world actor instance. + this.game = actorInstance; - constructor() { - super(); this.default_camera_distance = l.scenograph.width < l.scenograph.height ? -5 : -2.5; this.camera_distance = 0; this.mesh = new THREE.Object3D(); - - } // Internal helper to manage state changes to the person's character model. @@ -40,12 +38,12 @@ export default class Person extends PersonBase { let changing = false; for ( const [ controlName, keyMapping ] of Object.entries( mappings ) ) { if ( l.scenograph.controls.keyboard.pressed( keyMapping ) ) { - l.scenograph.actors.player.person.controls[ controlName ] = true; + this.game.actor.controls[ controlName ] = true; changing = true; } else { - l.scenograph.actors.player.person.controls[ controlName ] = false; + this.game.actor.controls[ controlName ] = false; if ( l.scenograph.controls.touch ) { // Check if any touchpad controls are being pressed @@ -59,41 +57,41 @@ export default class Person extends PersonBase { ) { changing = true; if ( l.scenograph.controls.touch.controls.moveForward ) { - l.scenograph.actors.player.person.controls.forward = true; + this.game.actor.controls.forward = true; } if ( l.scenograph.controls.touch.controls.moveBackward ) { - l.scenograph.actors.player.person.controls.back = true; + this.game.actor.controls.back = true; } if ( l.scenograph.controls.touch.controls.moveUp ) { - l.scenograph.actors.player.person.controls.jump = true; + this.game.actor.controls.jump = true; } if ( l.scenograph.controls.touch.controls.moveDown ) { - l.scenograph.actors.player.person.controls.crouch = true; + this.game.actor.controls.crouch = true; } if ( l.scenograph.controls.touch.controls.moveLeft ) { - l.scenograph.actors.player.person.controls.turnLeft = true; + this.game.actor.controls.turnLeft = true; } if ( l.scenograph.controls.touch.controls.moveRight ) { - l.scenograph.actors.player.person.controls.turnRight = true; + this.game.actor.controls.turnRight = true; } } } } } - l.scenograph.actors.player.person.controls.changing = changing; + this.game.actor.controls.changing = changing; } - // Update the position of the aircraft to spot determined by game logic. - updateMesh() { - l.scenograph.actors.player.person.mesh.position.x = l.scenograph.actors.player.person.position.x; - l.scenograph.actors.player.person.mesh.position.y = l.scenograph.actors.player.person.position.y; - l.scenograph.actors.player.person.mesh.position.z = l.scenograph.actors.player.person.position.z; + // Synchronise mesh with game world object. + sync() { + this.mesh.position.x = this.game.object.position.x; + this.mesh.position.y = this.game.object.position.y; + this.mesh.position.z = this.game.object.position.z; - l.scenograph.actors.player.person.mesh.rotation.x = l.scenograph.actors.player.person.rotation.x; - l.scenograph.actors.player.person.mesh.rotation.y = l.scenograph.actors.player.person.rotation.y; - l.scenograph.actors.player.person.mesh.rotation.z = l.scenograph.actors.player.person.rotation.z; + this.mesh.rotation.x = this.game.object.rotation.x; + this.mesh.rotation.y = this.game.object.rotation.y; + this.mesh.rotation.z = this.game.object.rotation.z; } updateCamera( rY, tY, tZ ) { @@ -156,32 +154,26 @@ export default class Person extends PersonBase { /** * Animate hook. * - * This method is called within the main animation loop and - * therefore must only reference global objects or properties. + * This method is called within a delta in the main animation + * which means it supports "this" references to itself. * * @method animate - * @memberof Raven - * @global + * @memberof Person + * @local * @note All references within this method should be globally accessible. **/ animate( delta ) { - if ( l.current_scene.settings.game_controls && l.scenograph.actors.player.mode == 'person' ) { - // Detect keyboard input and pass it to the ship state model. - l.scenograph.actors.player.person.updateControls(); - - // Update the persons state model. - let [ rY, tY, tZ ] = l.scenograph.actors.player.person.move( l.current_scene.stats.currentTime - l.current_scene.stats.lastTime ); - -// console.log(l.scenograph.actors.player.person.position); + if ( l.current_scene.settings.game_controls && l.scenograph.actors.player.mode == 'person' && this.game.object) { + // Detect keyboard input and pass it to the ship state model. + this.updateControls(); - // Update the persons mesh - l.scenograph.actors.player.person.updateMesh(); + // Sync the mesh to game world state. + this.sync(); // Update the persons camera - l.scenograph.actors.player.person.updateCamera(rY, tY, tZ); - - l.scenograph.cameras.player.rotation.y = l.scenograph.actors.player.person.rotation.y; + this.updateCamera(this.game.object.rY, this.game.object.tY, this.game.object.tZ); + l.scenograph.cameras.player.rotation.y = this.game.object.rotation.y; } } diff --git a/game/src/objects/person2.ts b/game/src/objects/person2.ts index 79ad8d93..e8f53606 100644 --- a/game/src/objects/person2.ts +++ b/game/src/objects/person2.ts @@ -31,6 +31,7 @@ export default class Person { } update( time_delta: number ) { + if (this.actor) { let stepSize: number = .025 * normaliseSpeedDelta( time_delta ), rY: number = 0, diff --git a/game/src/world.ts b/game/src/world.ts index bcb7093d..c6f0da80 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -105,8 +105,10 @@ export default class World { update() { for (const actor of this.instance.actors.values()) { - if (actor.name =='Player Two') { + if (actor.actor) { actor.actor.update(this.fixedDelta); + } + if (actor.object) { actor.object.update(this.fixedDelta); } } From 12a4f65ba6b0c0d3cf9df86966fd77c120f8a96b Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 28 Dec 2025 01:29:19 +0000 Subject: [PATCH 51/58] #31 - Implementing structures for game world collision --- game/src/objects/base.ts | 29 +++++++++++++++++++++++++++++ game/src/objects/person2.ts | 21 +++++++++------------ game/src/types.ts | 5 +++++ game/src/world.ts | 20 ++++++++++++++++++-- 4 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 game/src/objects/base.ts create mode 100644 game/src/types.ts diff --git a/game/src/objects/base.ts b/game/src/objects/base.ts new file mode 100644 index 00000000..c0b242b9 --- /dev/null +++ b/game/src/objects/base.ts @@ -0,0 +1,29 @@ +/** + * Base Object class + */ + +import { AABB, Vec3 } from '../types'; + +export default class ObjectBase { + + // World position and rotation. + public position: Vec3 = { x: 0, y: 0, z: 0 }; + public rotation: Vec3 = { x: 0, y: 0, z: 0 }; + + // Movement deltas. + public rY: number = 0; + public tY: number = 0; + public tZ: number = 0; + + // Axis-aligned bounding box + public aabb: AABB = { + halfSize: { x: 0.5, y: 0.5, z: 0.5 } + }; + + // Whether to do a collision check. + public solid: boolean = false; + + constructor() { + } + +} diff --git a/game/src/objects/person2.ts b/game/src/objects/person2.ts index e8f53606..87ab2ee2 100644 --- a/game/src/objects/person2.ts +++ b/game/src/objects/person2.ts @@ -1,17 +1,17 @@ /** - * Base Aircraft class - * - * @todo: - * - Add weight and wind resistance + * Person class */ +import ObjectBase from './base'; import BaseActor from '../actors/base2'; import { changeVelocity, normaliseSpeedDelta, easeOutExpo, easeInQuad, easeInOutExpo } from '../helpers'; -export default class Person { +export default class Person extends ObjectBase { + // Actor that controls this object. public actor?: BaseActor; + // Object world parameters public hitPoints: number = 100; public airSpeed: number = 0; public verticalSpeed: number = 0; @@ -20,14 +20,11 @@ export default class Person { public maxUp: number = 4 / 60; public maxDown: number = 16 / 60; // gravity? - public position: { x: number; y: number; z: number } = { x: 0, y: 0, z: 0 }; - public rotation: { x: number; y: number; z: number } = { x: 0, y: 0, z: 0 }; - - public rY: number = 0; - public tY: number = 0; - public tZ: number = 0; - constructor() { + super(); + this.aabb = { + halfSize: { x: 0.3, y: 0.9, z: 0.3 } + }; } update( time_delta: number ) { diff --git a/game/src/types.ts b/game/src/types.ts new file mode 100644 index 00000000..835b7154 --- /dev/null +++ b/game/src/types.ts @@ -0,0 +1,5 @@ +export type Vec3 = { x: number; y: number; z: number }; + +export interface AABB { + halfSize: Vec3; +} diff --git a/game/src/world.ts b/game/src/world.ts index c6f0da80..594a256c 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -60,12 +60,13 @@ export default class World { } load() { + // Load actors and their attached objects. for (const actorConfig of this.config.actors.values()) { const actorInstance: any = { config: actorConfig }; - // Set actors first. + // Set actor class first. actorInstance.actor = this.loadActor(actorConfig.class); - // Set objects. + // Set object class. actorInstance.object = this.loadObject(actorConfig.model); // Set objects actor properties to actor. @@ -75,6 +76,21 @@ export default class World { this.instance.actors.set(actorConfig.name, actorInstance); } + + // Load static objects into the world. + for (const objectConfig of this.config.objects.values()) { + const objectInstance: any = { config: objectConfig }; + + // Set object class. + objectInstance.object = this.loadObject(objectConfig.model); + + // Set objects actor properties to actor. + if ( objectInstance.actor && objectInstance.object ) { + objectInstance.object.actor = objectInstance.actor; + } + + this.instance.objects.set(objectConfig.name, objectInstance); + } } loadActor(actorClass: string) { From 84a8dc334aa1ad1ef70aa6b8601c74b9e3cee0f7 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 28 Dec 2025 07:55:12 +0000 Subject: [PATCH 52/58] #31 - Instantiating hangars on structures that possess them for collission detection --- .../scenograph/objects/structures/hangar.js | 32 +--- game/src/objects/base.ts | 36 ++++- game/src/objects/person2.ts | 149 +++++++++--------- game/src/objects/structures/base.ts | 18 --- game/src/objects/structures/hangar.ts | 136 +++++++++------- game/src/world.ts | 58 ++++++- 6 files changed, 243 insertions(+), 186 deletions(-) delete mode 100644 game/src/objects/structures/base.ts diff --git a/client/src/app/scenograph/objects/structures/hangar.js b/client/src/app/scenograph/objects/structures/hangar.js index 984924d7..82a698ce 100644 --- a/client/src/app/scenograph/objects/structures/hangar.js +++ b/client/src/app/scenograph/objects/structures/hangar.js @@ -18,12 +18,6 @@ import HangarObject from '#/game/src/objects/structures/hangar'; export default class Hangar { - // Preset layouts for hangars as defined by the game object class. - designs; - - // The hangar mesh and root of the CSG BVH hierarchy. - hangar; - // Modified materials for indoor scenes materials; @@ -49,31 +43,13 @@ export default class Hangar { } /** - * Loads hangar room layouts from the game object class. - */ - async loadDesigns() { - let designs = {}; - - this.objectClass.designs.forEach(design => { - // Auto load the "'Bay with quarters'" as default - if (design.name == 'Bay with quarters') { - designs.default = design; - } - }); - - return designs; - } - - /** - * Load hangar based on design name, default if not set. - * - * @param {string} designName + * Load hangar mesh * @returns */ - async loadMesh( designName = 'default' ) { + async loadMesh( ) { let hangarConfig, corridorConfig, quartersConfig; - this.designs[designName].components.forEach(component => { + this.objectClass.design.components.forEach(component => { if ( component.name === 'Main Bay' ) { hangarConfig = component; } @@ -124,8 +100,6 @@ export default class Hangar { // Setup materials. await this.loadMaterials(); - this.designs = await this.loadDesigns(); - this.mesh = await this.loadMesh(); } diff --git a/game/src/objects/base.ts b/game/src/objects/base.ts index c0b242b9..d52fd044 100644 --- a/game/src/objects/base.ts +++ b/game/src/objects/base.ts @@ -10,12 +10,15 @@ export default class ObjectBase { public position: Vec3 = { x: 0, y: 0, z: 0 }; public rotation: Vec3 = { x: 0, y: 0, z: 0 }; + // Track next position (before collision check). + public nextPosition: Vec3 = { x: 0, y: 0, z: 0 }; + // Movement deltas. public rY: number = 0; public tY: number = 0; public tZ: number = 0; - // Axis-aligned bounding box + // Axis-Aligned Bounding Box. public aabb: AABB = { halfSize: { x: 0.5, y: 0.5, z: 0.5 } }; @@ -26,4 +29,35 @@ export default class ObjectBase { constructor() { } + public getAABB() { + return { + min: { + x: this.position.x - this.aabb.halfSize.x, + y: this.position.y - this.aabb.halfSize.y, + z: this.position.z - this.aabb.halfSize.z + }, + max: { + x: this.position.x + this.aabb.halfSize.x, + y: this.position.y + this.aabb.halfSize.y, + z: this.position.z + this.aabb.halfSize.z + } + }; + } + + public getAABBNext() { + return { + min: { + x: this.nextPosition.x - this.aabb.halfSize.x, + y: this.nextPosition.y - this.aabb.halfSize.y, + z: this.nextPosition.z - this.aabb.halfSize.z + }, + max: { + x: this.nextPosition.x + this.aabb.halfSize.x, + y: this.nextPosition.y + this.aabb.halfSize.y, + z: this.nextPosition.z + this.aabb.halfSize.z + } + }; + } + + } diff --git a/game/src/objects/person2.ts b/game/src/objects/person2.ts index 87ab2ee2..c18d9503 100644 --- a/game/src/objects/person2.ts +++ b/game/src/objects/person2.ts @@ -5,6 +5,8 @@ import ObjectBase from './base'; import BaseActor from '../actors/base2'; import { changeVelocity, normaliseSpeedDelta, easeOutExpo, easeInQuad, easeInOutExpo } from '../helpers'; +import { Vec3 } from '../types'; + export default class Person extends ObjectBase { @@ -28,93 +30,96 @@ export default class Person extends ObjectBase { } update( time_delta: number ) { - - if (this.actor) { - let stepSize: number = .025 * normaliseSpeedDelta( time_delta ), - rY: number = 0, - tZ: number = 0, - tY: number = 0, - radian: number = - (Math.PI / 180) * stepSize * 100; - - if ( this.actor.controls.forward || this.actor.controls.back ){ - // Update Airspeed (horizontal velocity) - this.airSpeed = changeVelocity( - stepSize * easeInOutExpo( 1 - ( Math.abs ( this.airSpeed ) / this.maxForward ) ), - stepSize, - this.airSpeed, - this.actor.controls.forward, - this.actor.controls.back, - this.maxForward, - this.maxBackward, - easeOutExpo( 0.987 ) - ); - } - else { - this.airSpeed = 0; - } - - // Update Vertical Speed (velocity) - this.verticalSpeed = changeVelocity( - stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxUp ) ), - stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxDown ) ), - this.verticalSpeed, - this.actor.controls.crouch, // Note: Move Down/Up is reversed by design. - this.actor.controls.jump, - this.maxDown, - this.maxUp, - easeInQuad( 0.321 ) + if (!this.actor) return; + this.nextPosition = { ...this.position }; // start with current position + + let stepSize: number = .025 * normaliseSpeedDelta( time_delta ), + rY: number = 0, + tZ: number = 0, + tY: number = 0, + radian: number = - (Math.PI / 180) * stepSize * 100; + + if ( this.actor.controls.forward || this.actor.controls.back ){ + // Update Airspeed (horizontal velocity) + this.airSpeed = changeVelocity( + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.airSpeed ) / this.maxForward ) ), + stepSize, + this.airSpeed, + this.actor.controls.forward, + this.actor.controls.back, + this.maxForward, + this.maxBackward, + easeOutExpo( 0.987 ) ); + } + else { + this.airSpeed = 0; + } - // Check the vertical speed exceeds minimum threshold for change in vertical position - if (Math.abs(this.verticalSpeed) > 0.01) { - tY = this.verticalSpeed; - } + // Update Vertical Speed (velocity) + this.verticalSpeed = changeVelocity( + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxUp ) ), + stepSize * easeInOutExpo( 1 - ( Math.abs ( this.verticalSpeed ) / this.maxDown ) ), + this.verticalSpeed, + this.actor.controls.crouch, // Note: Move Down/Up is reversed by design. + this.actor.controls.jump, + this.maxDown, + this.maxUp, + easeInQuad( 0.321 ) + ); + + // Check the vertical speed exceeds minimum threshold for change in vertical position + if (Math.abs(this.verticalSpeed) > 0.01) { + tY = this.verticalSpeed; + } - // Turning - if (this.actor.controls.turnRight) { - rY += radian; - } - else { - if (this.actor.controls.turnLeft) { - rY -= radian; - } + // Turning + if (this.actor.controls.turnRight) { + rY += radian; + } + else { + if (this.actor.controls.turnLeft) { + rY -= radian; } + } - // Check if we have significant airspeed - if (Math.abs(this.airSpeed) > 0.01) { - - // Set change in Z position based on airspeed - tZ = this.airSpeed; + // Check if we have significant airspeed + if (Math.abs(this.airSpeed) > 0.01) { - } + // Set change in Z position based on airspeed + tZ = this.airSpeed; - if (rY != 0) { - if (Math.abs(this.rotation.z) < Math.PI / 4) { - this.rotation.z += rY / Math.PI; - } + } - this.rotation.y += rY; + if (rY != 0) { + if (Math.abs(this.rotation.z) < Math.PI / 4) { + this.rotation.z += rY / Math.PI; } - let xDiff = tZ * Math.sin(this.rotation.y), - zDiff = tZ * Math.cos(this.rotation.y); + this.rotation.y += rY; + } + + let xDiff = tZ * Math.sin(this.rotation.y), + zDiff = tZ * Math.cos(this.rotation.y); - // "1" is the floor limit as it's the ocean surface and the camera clips through the water any lower. - if (this.position.y + tY >= 1 ) { - this.position.y += tY; - } else { - this.verticalSpeed = 0; - } + // "1" is the floor limit as it's the ocean surface and the camera clips through the water any lower. + if (this.nextPosition.y + tY >= 1 ) { + this.nextPosition.y += tY; + } else { + this.verticalSpeed = 0; + } - this.position.x += xDiff; - this.position.z += zDiff; + this.nextPosition.x += xDiff; + this.nextPosition.z += zDiff; - this.rY = rY; - this.tY = tY; - this.tZ = tZ; + this.rY = rY; + this.tY = tY; + this.tZ = tZ; - } + } + commitNextPosition() { + this.position = this.nextPosition; } } diff --git a/game/src/objects/structures/base.ts b/game/src/objects/structures/base.ts deleted file mode 100644 index 0626f2da..00000000 --- a/game/src/objects/structures/base.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Base Structure class - * - */ - - -export default class BaseStructure { - - - constructor() { - - } - - animate() { - - } - -} diff --git a/game/src/objects/structures/hangar.ts b/game/src/objects/structures/hangar.ts index 4cd58e23..f3635df6 100644 --- a/game/src/objects/structures/hangar.ts +++ b/game/src/objects/structures/hangar.ts @@ -2,75 +2,95 @@ * Aircraft hangar. */ -import BaseStructure from './base'; +import ObjectBase from '../base'; -class Hangar extends BaseStructure { +class Hangar extends ObjectBase { - designs; + // Hangar component configuration. + design; - constructor() { - super(); // Call the constructor of the base class + constructor(config = {}) { + super(); // Call the constructor of the base class - let corridorScale = 0.125; - this.designs = [ - { - name: 'Bay with quarters', - size: 5, - components: [ - { - name: 'Main Bay', - width: 10, - height: 5, - depth: 10, - position: { - x: 0, - y: 0, - z: 0 - }, - rotation: { - x: 0, - y: 0, - z: 0 - } - }, - { - name: 'Quarters', - width: 5, - height: 2.5, - depth: 5, + let corridorScale = 0.125; + if (config.design) { + this.design = this.getDesign(config.design); + } else { + this.design = this.getDesign(); + } + if (config.position) { + this.position.x = config.position.x; + this.position.y = config.position.y; + this.position.z = config.position.z; + } + if (config.rotation) { + this.rotation.x = config.rotation.x; + this.rotation.y = config.rotation.y; + this.rotation.z = config.rotation.z; + } + } + + detectCollision () { + } + + getDesign( designIndex = 0 ) { + const designs = [ + { + name: 'Bay with quarters', + size: 5, + components: [ + { + name: 'Main Bay', + width: 10, + height: 5, + depth: 10, + position: { + x: 0, + y: 0, + z: 0 + }, + rotation: { + x: 0, + y: 0, + z: 0 + } + }, + { + name: 'Quarters', + width: 5, + height: 2.5, + depth: 5, + position: { + x: -8.375, + y: -1.25, + z: -2.5 + }, + rotation: { + x: 0, + y: 0, + z: 0 + } + }, + { + name: 'Corridor', + width: 1.025, + height: 2.55, + depth: 2.5, position: { - x: -8.375, - y: -1.25, - z: -2.5 + x: -5, + y: -1.225, + z: -1.25 }, rotation: { x: 0, - y: 0, + y: 1.5708, // 90 degrees or half pi z: 0 } - }, - { - name: 'Corridor', - width: 1.025, - height: 2.55, - depth: 2.5, - position: { - x: -5, - y: -1.225, - z: -1.25 }, - rotation: { - x: 0, - y: 1.5708, // 90 degrees or half pi - z: 0 - } - }, - ] - } - ]; - } - - detectCollision () { + ] + } + ]; + return designs[designIndex]; } } diff --git a/game/src/world.ts b/game/src/world.ts index 594a256c..f40b5071 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -8,6 +8,8 @@ import Overworld from "./scenes/overworld.yml"; import ActorPlayer from './actors/player2'; + +import ObjectHangar from './objects/structures/hangar'; import ObjectPerson from './objects/person2'; interface WorldConfig { @@ -67,7 +69,7 @@ export default class World { actorInstance.actor = this.loadActor(actorConfig.class); // Set object class. - actorInstance.object = this.loadObject(actorConfig.model); + actorInstance.object = this.loadObject(actorConfig); // Set objects actor properties to actor. if ( actorInstance.actor && actorInstance.object ) { @@ -82,11 +84,18 @@ export default class World { const objectInstance: any = { config: objectConfig }; // Set object class. - objectInstance.object = this.loadObject(objectConfig.model); - - // Set objects actor properties to actor. - if ( objectInstance.actor && objectInstance.object ) { - objectInstance.object.actor = objectInstance.actor; + objectInstance.object = this.loadObject(objectConfig); + + if (objectConfig.hangars){ + objectInstance.hangars = []; + objectConfig.hangars.forEach(hangarConfig => { + const hangarInstance: any = { config: hangarConfig }; + hangarInstance.object = this.loadObject({ + ...hangarConfig, + model: 'hangar' + }); + objectInstance.hangars.push(hangarInstance); + }); } this.instance.objects.set(objectConfig.name, objectInstance); @@ -102,10 +111,13 @@ export default class World { } } - loadObject(actorModel: string) { - if (actorModel == 'person') { + loadObject(config: any) { + if (config.model == 'person') { return new ObjectPerson(); } + else if (config.model == 'hangar') { + return new ObjectHangar(config); + } else { return false; } @@ -126,8 +138,38 @@ export default class World { } if (actor.object) { actor.object.update(this.fixedDelta); + this.checkCollisions(actor.object); } } } + checkCollisions(object: ObjectPerson) { + // Get object's proposed AABB at next position + const objAABB = object.getAABBNext(); // you’ll need a method that returns AABB at nextPosition + + for (const other of this.instance.objects.values()) { + + if (!other.object || other.object === object) continue; + + const otherAABB = other.object.getAABB(); + + // AABB collision check + const overlapX = objAABB.min.x <= otherAABB.max.x && objAABB.max.x >= otherAABB.min.x; + const overlapY = objAABB.min.y <= otherAABB.max.y && objAABB.max.y >= otherAABB.min.y; + const overlapZ = objAABB.min.z <= otherAABB.max.z && objAABB.max.z >= otherAABB.min.z; + + if (overlapX && overlapY && overlapZ) { + console.log('Collision detected!'); + return true; // stop at first collision or handle multiple collisions + } + console.log(overlapX, overlapY, overlapZ); + debugger; + } + + + // No collision: commit next position + object.commitNextPosition(); + return false; + } + } From 4d34e7920d2ee3ee4d0c8c8ebd89cba492476bae Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 28 Dec 2025 09:32:28 +0000 Subject: [PATCH 53/58] #31 - Adding basic hangar collision detect --- game/src/objects/structures/hangar.ts | 35 ++++++++++++++- game/src/world.ts | 64 ++++++++++++++++----------- 2 files changed, 73 insertions(+), 26 deletions(-) diff --git a/game/src/objects/structures/hangar.ts b/game/src/objects/structures/hangar.ts index f3635df6..5da650d2 100644 --- a/game/src/objects/structures/hangar.ts +++ b/game/src/objects/structures/hangar.ts @@ -4,6 +4,8 @@ import ObjectBase from '../base'; +import { AABB } from '../types'; + class Hangar extends ObjectBase { // Hangar component configuration. @@ -12,7 +14,6 @@ class Hangar extends ObjectBase { constructor(config = {}) { super(); // Call the constructor of the base class - let corridorScale = 0.125; if (config.design) { this.design = this.getDesign(config.design); } else { @@ -93,6 +94,38 @@ class Hangar extends ObjectBase { return designs[designIndex]; } + /** + * Returns world-space AABBs for all solid components + */ + public getComponentAABBs(): AABB[] { + return this.design.components.map(component => { + const halfSize = { + x: component.width, + y: component.height, + z: component.depth + }; + + const worldPos = { + x: this.position.x + component.position.x, + y: this.position.y + component.position.y, + z: this.position.z + component.position.z + }; + + return { + min: { + x: worldPos.x - halfSize.x, + y: worldPos.y - halfSize.y, + z: worldPos.z - halfSize.z + }, + max: { + x: worldPos.x + halfSize.x, + y: worldPos.y + halfSize.y, + z: worldPos.z + halfSize.z + } + }; + }); + } + } module.exports = Hangar; diff --git a/game/src/world.ts b/game/src/world.ts index f40b5071..95724dd4 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -89,6 +89,12 @@ export default class World { if (objectConfig.hangars){ objectInstance.hangars = []; objectConfig.hangars.forEach(hangarConfig => { + // Merge hangar position with world position; + hangarConfig.position = { + x: hangarConfig.position.x + objectConfig.position.x, + y: hangarConfig.position.y, // skip this one as the y offset is model specific. + z: hangarConfig.position.z + objectConfig.position.z + }; const hangarInstance: any = { config: hangarConfig }; hangarInstance.object = this.loadObject({ ...hangarConfig, @@ -132,44 +138,52 @@ export default class World { } update() { - for (const actor of this.instance.actors.values()) { - if (actor.actor) { - actor.actor.update(this.fixedDelta); + for (const actorInstance of this.instance.actors.values()) { + if (actorInstance.actor) { + actorInstance.actor.update(this.fixedDelta); } - if (actor.object) { - actor.object.update(this.fixedDelta); - this.checkCollisions(actor.object); + if (actorInstance.object) { + actorInstance.object.update(this.fixedDelta); + this.checkHangarCollisions(actorInstance); } } } - checkCollisions(object: ObjectPerson) { + checkHangarCollisions(actorInstance: any) { // Get object's proposed AABB at next position - const objAABB = object.getAABBNext(); // you’ll need a method that returns AABB at nextPosition + const objAABB = actorInstance.object.getAABBNext(); // you’ll need a method that returns AABB at nextPosition - for (const other of this.instance.objects.values()) { + if (actorInstance.config.hangar) { + this.instance.objects.forEach((objectInstance, objectName) => { + if (objectName == actorInstance.config.hangar.structure) { + if (objectInstance.hangars) { + objectInstance.hangars.forEach((hangarInstance) => { + if (hangarInstance.config.name == actorInstance.config.hangar.hangarName) { + const componentAABBs = hangarInstance.object.getComponentAABBs(); + //const aabb = componentAABBs[0]; - if (!other.object || other.object === object) continue; + for (const aabb of componentAABBs ) { + const overlapX = objAABB.min.x <= aabb.max.x && objAABB.max.x >= aabb.min.x; + const overlapY = objAABB.min.y <= aabb.max.y && objAABB.max.y >= aabb.min.y; + const overlapZ = objAABB.min.z <= aabb.max.z && objAABB.max.z >= aabb.min.z; - const otherAABB = other.object.getAABB(); + if (overlapX && overlapY && overlapZ) { + actorInstance.object.commitNextPosition(); + return false; + } - // AABB collision check - const overlapX = objAABB.min.x <= otherAABB.max.x && objAABB.max.x >= otherAABB.min.x; - const overlapY = objAABB.min.y <= otherAABB.max.y && objAABB.max.y >= otherAABB.min.y; - const overlapZ = objAABB.min.z <= otherAABB.max.z && objAABB.max.z >= otherAABB.min.z; + } - if (overlapX && overlapY && overlapZ) { - console.log('Collision detected!'); - return true; // stop at first collision or handle multiple collisions - } - console.log(overlapX, overlapY, overlapZ); - debugger; - } + } + }); + } + } + }); + + } - // No collision: commit next position - object.commitNextPosition(); - return false; + return true; } } From 0a2d56854dd69f429f5bbb81e743c6f3be6f8f32 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 28 Dec 2025 11:05:46 +0000 Subject: [PATCH 54/58] #31 - Adding multi component hangar collisions --- game/src/objects/person2.ts | 2 +- game/src/objects/structures/hangar.ts | 12 ++++----- game/src/world.ts | 35 ++++++++++++++++++--------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/game/src/objects/person2.ts b/game/src/objects/person2.ts index c18d9503..fc99fc24 100644 --- a/game/src/objects/person2.ts +++ b/game/src/objects/person2.ts @@ -25,7 +25,7 @@ export default class Person extends ObjectBase { constructor() { super(); this.aabb = { - halfSize: { x: 0.3, y: 0.9, z: 0.3 } + halfSize: { x: 0.1, y: 0.1, z: 0.1 } }; } diff --git a/game/src/objects/structures/hangar.ts b/game/src/objects/structures/hangar.ts index 5da650d2..1fb4dc85 100644 --- a/game/src/objects/structures/hangar.ts +++ b/game/src/objects/structures/hangar.ts @@ -74,9 +74,9 @@ class Hangar extends ObjectBase { }, { name: 'Corridor', - width: 1.025, + width: 2.5, height: 2.55, - depth: 2.5, + depth: 1.25, position: { x: -5, y: -1.225, @@ -84,7 +84,7 @@ class Hangar extends ObjectBase { }, rotation: { x: 0, - y: 1.5708, // 90 degrees or half pi + y: 0, // 90 degrees or half pi z: 0 } }, @@ -106,9 +106,9 @@ class Hangar extends ObjectBase { }; const worldPos = { - x: this.position.x + component.position.x, - y: this.position.y + component.position.y, - z: this.position.z + component.position.z + x: this.position.x + component.position.x * 2.5, + y: this.position.y + component.position.y * 2.5, + z: this.position.z + component.position.z * 2.5 }; return { diff --git a/game/src/world.ts b/game/src/world.ts index 95724dd4..3ec6da33 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -151,8 +151,10 @@ export default class World { checkHangarCollisions(actorInstance: any) { // Get object's proposed AABB at next position - const objAABB = actorInstance.object.getAABBNext(); // you’ll need a method that returns AABB at nextPosition + const actorAABB = actorInstance.object.getAABBNext(); + // Ensure the user is still inside the hangar area. + let inside = false; if (actorInstance.config.hangar) { this.instance.objects.forEach((objectInstance, objectName) => { if (objectName == actorInstance.config.hangar.structure) { @@ -160,18 +162,12 @@ export default class World { objectInstance.hangars.forEach((hangarInstance) => { if (hangarInstance.config.name == actorInstance.config.hangar.hangarName) { const componentAABBs = hangarInstance.object.getComponentAABBs(); - //const aabb = componentAABBs[0]; - for (const aabb of componentAABBs ) { - const overlapX = objAABB.min.x <= aabb.max.x && objAABB.max.x >= aabb.min.x; - const overlapY = objAABB.min.y <= aabb.max.y && objAABB.max.y >= aabb.min.y; - const overlapZ = objAABB.min.z <= aabb.max.z && objAABB.max.z >= aabb.min.z; - - if (overlapX && overlapY && overlapZ) { - actorInstance.object.commitNextPosition(); - return false; + for (const componentAABB of componentAABBs ) { + if (this.aabbContained(actorAABB, componentAABB)) { + inside = true; + break; } - } } @@ -182,8 +178,23 @@ export default class World { }); } + if (inside) { + actorInstance.object.commitNextPosition(); + } + + } - return true; + // Checks if actor Bounding Box is within the component Bounding Box + aabbContained(actorBounds, componentBounds) { + let offset = 0.5; + return ( + actorBounds.min.x >= componentBounds.min.x - offset && + actorBounds.max.x <= componentBounds.max.x + offset && + actorBounds.min.y >= componentBounds.min.y - offset && + actorBounds.max.y <= componentBounds.max.y + offset && + actorBounds.min.z >= componentBounds.min.z - offset && + actorBounds.max.z <= componentBounds.max.z + offset + ); } } From 7edaa8c1e8cd2244a8437135cb55d576c0cc15a1 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 28 Dec 2025 11:09:36 +0000 Subject: [PATCH 55/58] #31 - Tweaking container offset tolerance --- game/src/world.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/src/world.ts b/game/src/world.ts index 3ec6da33..27b70474 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -186,7 +186,7 @@ export default class World { // Checks if actor Bounding Box is within the component Bounding Box aabbContained(actorBounds, componentBounds) { - let offset = 0.5; + let offset = 1.25; return ( actorBounds.min.x >= componentBounds.min.x - offset && actorBounds.max.x <= componentBounds.max.x + offset && From b5d0a7ad6d6ae31d6e0c6385d9cb847d76dd2d34 Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 28 Dec 2025 17:57:33 +0000 Subject: [PATCH 56/58] #31- Adding accumulator to smooth physics and tweaking person dimensions --- game/src/objects/person2.ts | 2 +- game/src/world.ts | 32 +++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/game/src/objects/person2.ts b/game/src/objects/person2.ts index fc99fc24..a804f7b0 100644 --- a/game/src/objects/person2.ts +++ b/game/src/objects/person2.ts @@ -25,7 +25,7 @@ export default class Person extends ObjectBase { constructor() { super(); this.aabb = { - halfSize: { x: 0.1, y: 0.1, z: 0.1 } + halfSize: { x: 0.5, y: 0.75, z: 0.3 } }; } diff --git a/game/src/world.ts b/game/src/world.ts index 27b70474..944b8a44 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -27,6 +27,9 @@ export default class World { public config: WorldConfig; public instance: WorldInstance; + private accumulator = 0; + private running = false; + constructor( sceneName: string ) { if ( sceneName === 'Overworld' ) { this.initialise( Overworld ); @@ -130,11 +133,32 @@ export default class World { } start() { - this.updateLoop = setInterval(() => this.update(), this.fixedDelta); + this.running = true; + this.lastUpdateTime = performance.now(); + + const loop = () => { + if (!this.running) return; + + const now = performance.now(); + const frameTime = now - this.lastUpdateTime; + this.lastUpdateTime = now; + + // Prevent spiral of death + this.accumulator += Math.min(frameTime, 250); + + while (this.accumulator >= this.fixedDelta) { + this.update(); + this.accumulator -= this.fixedDelta; + } + + setTimeout(loop, 0); + }; + + loop(); } stop() { - clearInterval(this.updateLoop); + this.running = false; } update() { @@ -144,7 +168,9 @@ export default class World { } if (actorInstance.object) { actorInstance.object.update(this.fixedDelta); - this.checkHangarCollisions(actorInstance); + if (actorInstance.actor.controls.forward || actorInstance.actor.controls.back) { + this.checkHangarCollisions(actorInstance); + } } } } From 00579d414f7bf5c78453486e8f6a2d1445574afa Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Sun, 28 Dec 2025 18:15:02 +0000 Subject: [PATCH 57/58] #31 - Presetting hangar aabb on load --- game/src/objects/structures/hangar.ts | 7 ++++++- game/src/world.ts | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/game/src/objects/structures/hangar.ts b/game/src/objects/structures/hangar.ts index 1fb4dc85..bdb702bd 100644 --- a/game/src/objects/structures/hangar.ts +++ b/game/src/objects/structures/hangar.ts @@ -8,8 +8,11 @@ import { AABB } from '../types'; class Hangar extends ObjectBase { + // axis aligned bounding box + public aabb: AABB; + // Hangar component configuration. - design; + public design: any; constructor(config = {}) { super(); // Call the constructor of the base class @@ -29,6 +32,8 @@ class Hangar extends ObjectBase { this.rotation.y = config.rotation.y; this.rotation.z = config.rotation.z; } + + this.aabb = this.getComponentAABBs(); } detectCollision () { diff --git a/game/src/world.ts b/game/src/world.ts index 944b8a44..3c329cb9 100644 --- a/game/src/world.ts +++ b/game/src/world.ts @@ -187,9 +187,8 @@ export default class World { if (objectInstance.hangars) { objectInstance.hangars.forEach((hangarInstance) => { if (hangarInstance.config.name == actorInstance.config.hangar.hangarName) { - const componentAABBs = hangarInstance.object.getComponentAABBs(); - for (const componentAABB of componentAABBs ) { + for (const componentAABB of hangarInstance.object.aabb ) { if (this.aabbContained(actorAABB, componentAABB)) { inside = true; break; From 1187e9541a0201b8a660b4d3ef7025804a3c915d Mon Sep 17 00:00:00 2001 From: Paul Brzeski Date: Mon, 29 Dec 2025 16:36:31 +0000 Subject: [PATCH 58/58] #31 - Moved hangar position to side of platform --- client/dev.js | 2 +- client/src/app/routes/hangar.js | 14 ++++++++------ game/src/scenes/overworld.yml | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/client/dev.js b/client/dev.js index fdb5d187..62eca6aa 100644 --- a/client/dev.js +++ b/client/dev.js @@ -13,7 +13,7 @@ run('build:esbuild'); chokidar.watch(['src', '../game'], { }).on('all', (event, path) => { if (event === 'change') { console.log(event, path); - if (path.endsWith('.js') || path.endsWith('.ts')) { + if (path.endsWith('.js') || path.endsWith('.ts') || path.endsWith('.yml')) { run('build:esbuild'); } } diff --git a/client/src/app/routes/hangar.js b/client/src/app/routes/hangar.js index 8c74dfbc..9bf37e1f 100644 --- a/client/src/app/routes/hangar.js +++ b/client/src/app/routes/hangar.js @@ -36,17 +36,19 @@ export default class hangarRoute { loadHangar() { l.current_scene.scene.add(l.scenograph.objects.structures.hangar.mesh); - this.targetStructure.visible = false; + //this.targetStructure.visible = false; l.scenograph.objects.structures.hangar.mesh.visible = true; - l.scenograph.objects.structures.hangar.mesh.position.copy( this.targetStructure.position ); + console.log(this.targetStructure.userData.config.hangars[0].position); + l.scenograph.objects.structures.hangar.mesh.position.x = this.targetStructure.userData.config.hangars[0].position.x; l.scenograph.objects.structures.hangar.mesh.position.y = this.targetStructure.userData.config.hangars[0].position.y; + l.scenograph.objects.structures.hangar.mesh.position.z = this.targetStructure.userData.config.hangars[0].position.z; - l.scenograph.actors.player.vehicle.mesh.userData.object.position.x = this.targetStructure.position.x; - l.scenograph.actors.player.vehicle.mesh.userData.object.position.z = this.targetStructure.position.z - 2.5; + l.scenograph.actors.player.vehicle.mesh.userData.object.position.x = this.targetStructure.userData.config.hangars[0].position.x; + l.scenograph.actors.player.vehicle.mesh.userData.object.position.z = - 2.5 + this.targetStructure.userData.config.hangars[0].position.z; l.scenograph.actors.player.vehicle.mesh.userData.object.position.y = l.scenograph.objects.structures.hangar.mesh.position.y - 7.5; - l.scenograph.actors.player.actorInstance.object.position.x = this.targetStructure.position.x; - l.scenograph.actors.player.actorInstance.object.position.z = this.targetStructure.position.z + 10; + l.scenograph.actors.player.actorInstance.object.position.x = this.targetStructure.userData.config.hangars[0].position.x; + l.scenograph.actors.player.actorInstance.object.position.z = 10 + this.targetStructure.userData.config.hangars[0].position.z; l.scenograph.actors.player.actorInstance.object.position.y = l.scenograph.objects.structures.hangar.mesh.position.y - 2.5 ; diff --git a/game/src/scenes/overworld.yml b/game/src/scenes/overworld.yml index 1d90aa3f..e7382a6d 100644 --- a/game/src/scenes/overworld.yml +++ b/game/src/scenes/overworld.yml @@ -97,8 +97,8 @@ objects: - name: Hangar 1 position: x: 0 - y: 505 - z: 0 + y: 6100 + z: -8620 rotation: x: 0 y: 0