From 52a0da10fe663079a256b33b00c7bb528b2a194d Mon Sep 17 00:00:00 2001 From: Thayol Date: Thu, 23 Jan 2025 23:39:10 +0900 Subject: [PATCH] use the placeholder player model --- models/blue.png | Bin 95 -> 95 bytes models/generic-player-placeholder.bbmodel | 2 +- models/generic-player-placeholder.gltf | 2 +- public/client.js | 83 +++++++++++++++------- public/menu/index.html | 26 +++++++ src/entity.js | 6 +- src/index.js | 8 ++- src/template.js | 8 +-- 8 files changed, 96 insertions(+), 39 deletions(-) create mode 100644 public/menu/index.html diff --git a/models/blue.png b/models/blue.png index fc04b755d3a13db331d15a3c0e4c208405d20a19..94d13047063e1e4b1b1c8a9ec95fe190b4afe0ff 100644 GIT binary patch delta 43 zcma!#pJ1sba%IjT2d)Gq&#x=m0(qUim>E{QXLvVx_T5$n1_lOCS3j3^P6 tween.update()) // TODO: clean up tweens + Object.values(positionTweens).forEach((tween) => tween.update()) + Object.values(rotationTweens).forEach((tween) => tween.update()) renderer.render(scene, camera) stats.end() } @@ -296,17 +309,19 @@ function connectWebSocket() { entity = entities[e.id] } else { - const entityMaterial = teamMaterials[e.team] - entity = new THREE.Mesh(new THREE.CylinderGeometry(e.visualRadius / 100, e.visualRadius / 100, e.height / 50), entityMaterial) + // const entityMaterial = teamMaterials[e.team] + // entity = new THREE.Mesh(new THREE.CylinderGeometry(e.visualRadius / 100, e.visualRadius / 100, e.height / 50), entityMaterial) + entity = new THREE.Group() entity.rotation.x = Math.PI / 2 + entity.scale.set(e.visualRadius / 100, e.height / 100, e.visualRadius / 100) entity.userData.type = 'entity' entity.userData.id = e.id - entity.position.set(e.position.x / 100, e.position.y / 100, e.height / 100) + entity.position.set(e.position.x / 100, e.position.y / 100, 0) scene.add(entity) const hpMargin = 0.4 const maxHp = new THREE.Sprite(new THREE.SpriteMaterial({ color: 0xd03333 })) - maxHp.position.set(0, (e.height / 100) + hpMargin, 0) + maxHp.position.set(0, (e.visualRadius / 100) + hpMargin, 0) maxHp.scale.set(1.5, 0.2, 1) maxHp.layers.set(1) entity.add(maxHp) @@ -318,12 +333,8 @@ function connectWebSocket() { maxHp.add(hp) const teamMaterial = teamMaterials[`${e.team}Transparent`] - const teamMarker = new THREE.Mesh(new THREE.CylinderGeometry((e.radius) / 100, (e.radius) / 100, 1), teamMaterial) - const teamMarkerSize = 4000 - teamMarker.scale.y = e.height / teamMarkerSize - teamMarker.position.y = (e.height / (teamMarkerSize * 2)) - (e.height / 100) - teamMarker.position.y += 0.01 - teamMarker.layers.set(1) + const teamMarker = new THREE.Mesh(new THREE.CylinderGeometry(1, 0.00001, 1), teamMaterial) + teamMarker.position.y = -0.496 entity.add(teamMarker) const buffMaterial = new THREE.MeshToonMaterial({ color: 0xffff00, transparent: true, opacity: 0.4 }) @@ -334,30 +345,35 @@ function connectWebSocket() { buffMarker.visible = false entity.add(buffMarker) - const rotationBase = new THREE.Object3D() - entity.add(rotationBase) + const castingMarkerrotationBase = new THREE.Group() + entity.add(castingMarkerrotationBase) const castingMaterial = new THREE.MeshToonMaterial({ color: 0x10dde0, transparent: true, opacity: 0.4 }) const castingMarker = new THREE.Mesh(new THREE.CylinderGeometry((e.height * 0.9) / 100, (e.height * 0.9) / 100, 1), castingMaterial) const castingMarkerSize = 800 castingMarker.rotation.z = Math.PI / 2 - castingMarker.position.x = (e.radius) / 100 + castingMarker.position.x = 1 + castingMarker.position.y = 1 castingMarker.scale.y = e.height / castingMarkerSize castingMarker.layers.set(1) buffMarker.visible = false - rotationBase.add(castingMarker) + castingMarkerrotationBase.add(castingMarker) const rangeMaterial = teamMaterials['range'] // const rangeSize = e.visionRange ?? 0 const rangeSize = (state.abilities.find((it) => it.id == e.abilities?.a)?.range ?? 0) + e.radius - const rangeMarker = new THREE.Mesh(new THREE.CylinderGeometry((rangeSize) / 100, (rangeSize) / 100, 1), rangeMaterial) - const rangeMarkerSize = 5000 - rangeMarker.scale.y = e.height / rangeMarkerSize - rangeMarker.position.y = (e.height / (rangeMarkerSize * 2)) - (e.height / 100) + const rangeMarker = new THREE.Mesh(new THREE.CylinderGeometry(rangeSize / e.visualRadius, rangeSize / e.visualRadius, 0.001), rangeMaterial) + rangeMarker.position.y = 0.002 rangeMarker.layers.set(1) rangeMarker.visible = false entity.add(rangeMarker) + const modelRotationBase = new THREE.Object3D() + modelRotationBase.layers.set(1) + entity.add(modelRotationBase) + + gltfLoader.load('models/generic-player-placeholder.gltf', addTo(modelRotationBase)) + entities[e.id] = entity } @@ -365,28 +381,36 @@ function connectWebSocket() { entity.children.at(1).visible = !e.dead entity.children.at(2).visible = e.buffs.some((it) => it.id == 'exposed') // TODO: only works for Exposed now - let z = e.height / 100 - if (e.dead) { entity.rotation.x = 0 - entity.position.z = 0 - z = 0 + entity.children.at(5).children.at(0).rotation.y = Math.PI / 2 } else { entity.rotation.x = Math.PI / 2 - entity.position.z = e.height / 100 + entity.children.at(5).children.at(0).rotation.y = 0 } entity.userData.flaggedForRemoval = false entity.children.at(3).rotation.y = e.rotation - positionTweens[entity.id] = new Tween(entity.position).to({ x: e.position.x / 100, y: e.position.y / 100, z }, tweenDuration).start() + // entity.children.at(5).rotation.y = e.rotation - (Math.PI / 2) + const oldRotationY = entity.children.at(5).rotation.y + const newRotationY = e.rotation - (Math.PI / 2) + if (Math.abs((oldRotationY - (2 * Math.PI)) - newRotationY) < Math.abs(oldRotationY - newRotationY)) { + entity.children.at(5).rotation.y = oldRotationY - (2 * Math.PI) + } + if (Math.abs((oldRotationY + (2 * Math.PI)) - newRotationY) < Math.abs(oldRotationY - newRotationY)) { + entity.children.at(5).rotation.y = oldRotationY + (2 * Math.PI) + } + + positionTweens[entity.id] = new Tween(entity.position).to({ x: e.position.x / 100, y: e.position.y / 100, z: 0 }, tweenDuration).start() + rotationTweens[entity.id] = new Tween(entity.children.at(5).rotation).to({ x: 0, y: newRotationY, z: 0 }, tweenDuration).start() const hp = entity.children.at(0).children.at(0) const percentageHp = e.health / e.maxHealth hp.scale.x = percentageHp hp.position.x = -(1 - percentageHp) / 2 - // entity.children.at(4).visible = e.id == playerId + entity.children.at(4).visible = e.id == playerId entity.children.at(3).children.at(0).visible = e.casting != null } @@ -395,6 +419,7 @@ function connectWebSocket() { scene.remove(e) delete entities[e.userData.id] delete positionTweens[e.userData.id] + delete rotationTweens[e.userData.id] } } @@ -561,6 +586,10 @@ window.addEventListener('load', () => { playerId = params.id if (playerId == null) { playerId = prompt('Player ID:') + if (playerId == '') { + window.location.href = '/menu/' + return + } } connectWebSocket() diff --git a/public/menu/index.html b/public/menu/index.html new file mode 100644 index 0000000..7350230 --- /dev/null +++ b/public/menu/index.html @@ -0,0 +1,26 @@ + +

Take control of a unit:

+ + diff --git a/src/entity.js b/src/entity.js index 30e859d..2171799 100644 --- a/src/entity.js +++ b/src/entity.js @@ -18,7 +18,7 @@ export default class Entity { dead = false ghosting = false health = null - height = 40 + height = null maxHealth = 1 position = null radius = 0 @@ -43,7 +43,6 @@ export default class Entity { #pathfindingCooldown = 0 #pathfindingObstacleLimit = null #projectilesInVision = [] - #queuedAction = null #spawnPosition = new Vector2() static bbox(x, y, radius) { @@ -141,6 +140,9 @@ export default class Entity { if (this.visualRadius == null) { this.visualRadius = this.radius } + if (this.height == null) { + this.height = this.visualRadius ?? this.radius + } this.#calculateCollider() } diff --git a/src/index.js b/src/index.js index 1fcda57..eee13a1 100644 --- a/src/index.js +++ b/src/index.js @@ -23,6 +23,8 @@ app.use('/@tweenjs/', express.static('node_modules/@tweenjs')) app.use('/stats.js/', express.static('node_modules/stats.js')) app.use('/', express.static('public')) +app.use('/models', express.static('models')) + app.use('/tools/', express.static('tools')) app.ws('/ws', async (req, res) => { @@ -31,6 +33,10 @@ app.ws('/ws', async (req, res) => { websocket.on('message', (rawData) => { const message = JSON.parse(rawData) console.info(message) + if (message.action == 'entities') { + websocket.send(JSON.stringify({ entities: game.entities.map((it) => it.id) })) + } + if (message.action == 'join') { const id = message.id const connectionId = crypto.randomUUID() @@ -56,7 +62,7 @@ app.ws('/ws', async (req, res) => { }) app.listen(port, () => { - console.info({ event: 'startup', visit: `http://localhost:${port}` }) + console.info({ event: 'startup', visit: `http://localhost:${port}/menu/` }) Dungeon.scenario(game) }) diff --git a/src/template.js b/src/template.js index b35d2b9..af22e42 100644 --- a/src/template.js +++ b/src/template.js @@ -6,11 +6,9 @@ export default class Template { static basilisk(overrides) { return { abilities: {}, - height: 100, logic: this.#basiliskLogic(), radius: 180, speed: 230, - visualRadius: 170, maxHealth: 3000, ...overrides, } @@ -19,17 +17,15 @@ export default class Template { static minion(team, options = {}) { return { abilities: { a: options.ranged ? Ability.rangedAttack.id : Ability.meleeAttack.id }, - height: options.ranged ? 40 : 38, logic: this.#minionLogic(options.route, (team != Team.blue)), maxHealth: options.ranged ? 300 : 450, pathfindingCooldown: 0.2, pathfindingObstacleLimit: 0, position: options.route?.at(0) ?? options.position ?? new Vector2(0, 0), - radius: 48, + radius: options.ranged ? 36 : 38, speed: 325, team, visionRange: 1200, - visualRadius: options.ranged ? 36 : 38, } } @@ -44,14 +40,12 @@ export default class Template { d: Ability.circleOfResurrection.id, f: Ability.blink.id, }, - height: 80, logic: this.#playerLogic, maxHealth: 600, pathfindingObstacleLimit: 3, radius: 65, spawnPosition: new Vector2(500, 150), visionRange: 1350, - visualRadius: 40, ...overrides, } }