use the placeholder player model
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 95 B After Width: | Height: | Size: 95 B |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+56
-27
@@ -1,5 +1,6 @@
|
|||||||
import * as THREE from 'three'
|
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
|
||||||
import { Tween } from '@tweenjs/tween.js'
|
import { Tween } from '@tweenjs/tween.js'
|
||||||
|
import * as THREE from 'three'
|
||||||
import Stats from 'stats.js'
|
import Stats from 'stats.js'
|
||||||
|
|
||||||
const global = (0,eval)('this')
|
const global = (0,eval)('this')
|
||||||
@@ -22,6 +23,16 @@ camera.updateProjectionMatrix()
|
|||||||
camera.layers.enable(1)
|
camera.layers.enable(1)
|
||||||
camera.layers.enable(2)
|
camera.layers.enable(2)
|
||||||
|
|
||||||
|
const gltfLoader = new GLTFLoader()
|
||||||
|
const addTo = function addTo(scene) {
|
||||||
|
return function addToScene(gltf) {
|
||||||
|
const model = gltf.scene
|
||||||
|
const scale = 2
|
||||||
|
model.scale.set(scale, scale, scale)
|
||||||
|
scene.add(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const projectileMaterial = new THREE.MeshToonMaterial({ color: 0xcccccc })
|
const projectileMaterial = new THREE.MeshToonMaterial({ color: 0xcccccc })
|
||||||
const terrainMaterial = new THREE.MeshToonMaterial({ color: 0x5c4033 })
|
const terrainMaterial = new THREE.MeshToonMaterial({ color: 0x5c4033 })
|
||||||
const passableTerrainMaterial = new THREE.MeshToonMaterial({ color: 0x228822, transparent: true, opacity: 0.65 })
|
const passableTerrainMaterial = new THREE.MeshToonMaterial({ color: 0x228822, transparent: true, opacity: 0.65 })
|
||||||
@@ -50,6 +61,7 @@ minimapCamera.position.set(10, 10, 10)
|
|||||||
const entities = {}
|
const entities = {}
|
||||||
const projectiles = {}
|
const projectiles = {}
|
||||||
const positionTweens = {}
|
const positionTweens = {}
|
||||||
|
const rotationTweens = {}
|
||||||
const terrains = {}
|
const terrains = {}
|
||||||
var state = { abilities: [], entities: [], terrains: [], projectiles: [] }
|
var state = { abilities: [], entities: [], terrains: [], projectiles: [] }
|
||||||
|
|
||||||
@@ -88,7 +100,8 @@ function render() {
|
|||||||
stats.begin()
|
stats.begin()
|
||||||
delta = clock.getDelta()
|
delta = clock.getDelta()
|
||||||
cameraMovement()
|
cameraMovement()
|
||||||
Object.values(positionTweens).forEach((tween) => tween.update()) // TODO: clean up tweens
|
Object.values(positionTweens).forEach((tween) => tween.update())
|
||||||
|
Object.values(rotationTweens).forEach((tween) => tween.update())
|
||||||
renderer.render(scene, camera)
|
renderer.render(scene, camera)
|
||||||
stats.end()
|
stats.end()
|
||||||
}
|
}
|
||||||
@@ -296,17 +309,19 @@ function connectWebSocket() {
|
|||||||
entity = entities[e.id]
|
entity = entities[e.id]
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const entityMaterial = teamMaterials[e.team]
|
// 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.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.rotation.x = Math.PI / 2
|
||||||
|
entity.scale.set(e.visualRadius / 100, e.height / 100, e.visualRadius / 100)
|
||||||
entity.userData.type = 'entity'
|
entity.userData.type = 'entity'
|
||||||
entity.userData.id = e.id
|
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)
|
scene.add(entity)
|
||||||
|
|
||||||
const hpMargin = 0.4
|
const hpMargin = 0.4
|
||||||
const maxHp = new THREE.Sprite(new THREE.SpriteMaterial({ color: 0xd03333 }))
|
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.scale.set(1.5, 0.2, 1)
|
||||||
maxHp.layers.set(1)
|
maxHp.layers.set(1)
|
||||||
entity.add(maxHp)
|
entity.add(maxHp)
|
||||||
@@ -318,12 +333,8 @@ function connectWebSocket() {
|
|||||||
maxHp.add(hp)
|
maxHp.add(hp)
|
||||||
|
|
||||||
const teamMaterial = teamMaterials[`${e.team}Transparent`]
|
const teamMaterial = teamMaterials[`${e.team}Transparent`]
|
||||||
const teamMarker = new THREE.Mesh(new THREE.CylinderGeometry((e.radius) / 100, (e.radius) / 100, 1), teamMaterial)
|
const teamMarker = new THREE.Mesh(new THREE.CylinderGeometry(1, 0.00001, 1), teamMaterial)
|
||||||
const teamMarkerSize = 4000
|
teamMarker.position.y = -0.496
|
||||||
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)
|
|
||||||
entity.add(teamMarker)
|
entity.add(teamMarker)
|
||||||
|
|
||||||
const buffMaterial = new THREE.MeshToonMaterial({ color: 0xffff00, transparent: true, opacity: 0.4 })
|
const buffMaterial = new THREE.MeshToonMaterial({ color: 0xffff00, transparent: true, opacity: 0.4 })
|
||||||
@@ -334,30 +345,35 @@ function connectWebSocket() {
|
|||||||
buffMarker.visible = false
|
buffMarker.visible = false
|
||||||
entity.add(buffMarker)
|
entity.add(buffMarker)
|
||||||
|
|
||||||
const rotationBase = new THREE.Object3D()
|
const castingMarkerrotationBase = new THREE.Group()
|
||||||
entity.add(rotationBase)
|
entity.add(castingMarkerrotationBase)
|
||||||
|
|
||||||
const castingMaterial = new THREE.MeshToonMaterial({ color: 0x10dde0, transparent: true, opacity: 0.4 })
|
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 castingMarker = new THREE.Mesh(new THREE.CylinderGeometry((e.height * 0.9) / 100, (e.height * 0.9) / 100, 1), castingMaterial)
|
||||||
const castingMarkerSize = 800
|
const castingMarkerSize = 800
|
||||||
castingMarker.rotation.z = Math.PI / 2
|
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.scale.y = e.height / castingMarkerSize
|
||||||
castingMarker.layers.set(1)
|
castingMarker.layers.set(1)
|
||||||
buffMarker.visible = false
|
buffMarker.visible = false
|
||||||
rotationBase.add(castingMarker)
|
castingMarkerrotationBase.add(castingMarker)
|
||||||
|
|
||||||
const rangeMaterial = teamMaterials['range']
|
const rangeMaterial = teamMaterials['range']
|
||||||
// const rangeSize = e.visionRange ?? 0
|
// const rangeSize = e.visionRange ?? 0
|
||||||
const rangeSize = (state.abilities.find((it) => it.id == e.abilities?.a)?.range ?? 0) + e.radius
|
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 rangeMarker = new THREE.Mesh(new THREE.CylinderGeometry(rangeSize / e.visualRadius, rangeSize / e.visualRadius, 0.001), rangeMaterial)
|
||||||
const rangeMarkerSize = 5000
|
rangeMarker.position.y = 0.002
|
||||||
rangeMarker.scale.y = e.height / rangeMarkerSize
|
|
||||||
rangeMarker.position.y = (e.height / (rangeMarkerSize * 2)) - (e.height / 100)
|
|
||||||
rangeMarker.layers.set(1)
|
rangeMarker.layers.set(1)
|
||||||
rangeMarker.visible = false
|
rangeMarker.visible = false
|
||||||
entity.add(rangeMarker)
|
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
|
entities[e.id] = entity
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,28 +381,36 @@ function connectWebSocket() {
|
|||||||
entity.children.at(1).visible = !e.dead
|
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
|
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) {
|
if (e.dead) {
|
||||||
entity.rotation.x = 0
|
entity.rotation.x = 0
|
||||||
entity.position.z = 0
|
entity.children.at(5).children.at(0).rotation.y = Math.PI / 2
|
||||||
z = 0
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
entity.rotation.x = Math.PI / 2
|
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.userData.flaggedForRemoval = false
|
||||||
entity.children.at(3).rotation.y = e.rotation
|
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 hp = entity.children.at(0).children.at(0)
|
||||||
const percentageHp = e.health / e.maxHealth
|
const percentageHp = e.health / e.maxHealth
|
||||||
hp.scale.x = percentageHp
|
hp.scale.x = percentageHp
|
||||||
hp.position.x = -(1 - percentageHp) / 2
|
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
|
entity.children.at(3).children.at(0).visible = e.casting != null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +419,7 @@ function connectWebSocket() {
|
|||||||
scene.remove(e)
|
scene.remove(e)
|
||||||
delete entities[e.userData.id]
|
delete entities[e.userData.id]
|
||||||
delete positionTweens[e.userData.id]
|
delete positionTweens[e.userData.id]
|
||||||
|
delete rotationTweens[e.userData.id]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,6 +586,10 @@ window.addEventListener('load', () => {
|
|||||||
playerId = params.id
|
playerId = params.id
|
||||||
if (playerId == null) {
|
if (playerId == null) {
|
||||||
playerId = prompt('Player ID:')
|
playerId = prompt('Player ID:')
|
||||||
|
if (playerId == '') {
|
||||||
|
window.location.href = '/menu/'
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectWebSocket()
|
connectWebSocket()
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<style>
|
||||||
|
html {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
a:link, a:hover, a:active, a:visited {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<h1>Take control of a unit:</h1>
|
||||||
|
<ul id="links"></ul>
|
||||||
|
<script>
|
||||||
|
websocket = new WebSocket(`ws://${window.location.hostname}:1280/ws`)
|
||||||
|
websocket.onopen = () => { websocket.send(JSON.stringify({ action: 'entities' })) }
|
||||||
|
websocket.onmessage = (event) => {
|
||||||
|
const message = JSON.parse(event.data)
|
||||||
|
const entityIds = message?.entities
|
||||||
|
if (entityIds == null) { return }
|
||||||
|
|
||||||
|
websocket.close()
|
||||||
|
let links = ''
|
||||||
|
entityIds.forEach((entityId) => links += `<li><a href="/?id=${encodeURI(entityId)}">${entityId}</a></li>`)
|
||||||
|
document.getElementById('links').innerHTML = links
|
||||||
|
}
|
||||||
|
</script>
|
||||||
+4
-2
@@ -18,7 +18,7 @@ export default class Entity {
|
|||||||
dead = false
|
dead = false
|
||||||
ghosting = false
|
ghosting = false
|
||||||
health = null
|
health = null
|
||||||
height = 40
|
height = null
|
||||||
maxHealth = 1
|
maxHealth = 1
|
||||||
position = null
|
position = null
|
||||||
radius = 0
|
radius = 0
|
||||||
@@ -43,7 +43,6 @@ export default class Entity {
|
|||||||
#pathfindingCooldown = 0
|
#pathfindingCooldown = 0
|
||||||
#pathfindingObstacleLimit = null
|
#pathfindingObstacleLimit = null
|
||||||
#projectilesInVision = []
|
#projectilesInVision = []
|
||||||
#queuedAction = null
|
|
||||||
#spawnPosition = new Vector2()
|
#spawnPosition = new Vector2()
|
||||||
|
|
||||||
static bbox(x, y, radius) {
|
static bbox(x, y, radius) {
|
||||||
@@ -141,6 +140,9 @@ export default class Entity {
|
|||||||
if (this.visualRadius == null) {
|
if (this.visualRadius == null) {
|
||||||
this.visualRadius = this.radius
|
this.visualRadius = this.radius
|
||||||
}
|
}
|
||||||
|
if (this.height == null) {
|
||||||
|
this.height = this.visualRadius ?? this.radius
|
||||||
|
}
|
||||||
|
|
||||||
this.#calculateCollider()
|
this.#calculateCollider()
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -23,6 +23,8 @@ app.use('/@tweenjs/', express.static('node_modules/@tweenjs'))
|
|||||||
app.use('/stats.js/', express.static('node_modules/stats.js'))
|
app.use('/stats.js/', express.static('node_modules/stats.js'))
|
||||||
|
|
||||||
app.use('/', express.static('public'))
|
app.use('/', express.static('public'))
|
||||||
|
app.use('/models', express.static('models'))
|
||||||
|
|
||||||
app.use('/tools/', express.static('tools'))
|
app.use('/tools/', express.static('tools'))
|
||||||
|
|
||||||
app.ws('/ws', async (req, res) => {
|
app.ws('/ws', async (req, res) => {
|
||||||
@@ -31,6 +33,10 @@ app.ws('/ws', async (req, res) => {
|
|||||||
websocket.on('message', (rawData) => {
|
websocket.on('message', (rawData) => {
|
||||||
const message = JSON.parse(rawData)
|
const message = JSON.parse(rawData)
|
||||||
console.info(message)
|
console.info(message)
|
||||||
|
if (message.action == 'entities') {
|
||||||
|
websocket.send(JSON.stringify({ entities: game.entities.map((it) => it.id) }))
|
||||||
|
}
|
||||||
|
|
||||||
if (message.action == 'join') {
|
if (message.action == 'join') {
|
||||||
const id = message.id
|
const id = message.id
|
||||||
const connectionId = crypto.randomUUID()
|
const connectionId = crypto.randomUUID()
|
||||||
@@ -56,7 +62,7 @@ app.ws('/ws', async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.info({ event: 'startup', visit: `http://localhost:${port}` })
|
console.info({ event: 'startup', visit: `http://localhost:${port}/menu/` })
|
||||||
|
|
||||||
Dungeon.scenario(game)
|
Dungeon.scenario(game)
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-7
@@ -6,11 +6,9 @@ export default class Template {
|
|||||||
static basilisk(overrides) {
|
static basilisk(overrides) {
|
||||||
return {
|
return {
|
||||||
abilities: {},
|
abilities: {},
|
||||||
height: 100,
|
|
||||||
logic: this.#basiliskLogic(),
|
logic: this.#basiliskLogic(),
|
||||||
radius: 180,
|
radius: 180,
|
||||||
speed: 230,
|
speed: 230,
|
||||||
visualRadius: 170,
|
|
||||||
maxHealth: 3000,
|
maxHealth: 3000,
|
||||||
...overrides,
|
...overrides,
|
||||||
}
|
}
|
||||||
@@ -19,17 +17,15 @@ export default class Template {
|
|||||||
static minion(team, options = {}) {
|
static minion(team, options = {}) {
|
||||||
return {
|
return {
|
||||||
abilities: { a: options.ranged ? Ability.rangedAttack.id : Ability.meleeAttack.id },
|
abilities: { a: options.ranged ? Ability.rangedAttack.id : Ability.meleeAttack.id },
|
||||||
height: options.ranged ? 40 : 38,
|
|
||||||
logic: this.#minionLogic(options.route, (team != Team.blue)),
|
logic: this.#minionLogic(options.route, (team != Team.blue)),
|
||||||
maxHealth: options.ranged ? 300 : 450,
|
maxHealth: options.ranged ? 300 : 450,
|
||||||
pathfindingCooldown: 0.2,
|
pathfindingCooldown: 0.2,
|
||||||
pathfindingObstacleLimit: 0,
|
pathfindingObstacleLimit: 0,
|
||||||
position: options.route?.at(0) ?? options.position ?? new Vector2(0, 0),
|
position: options.route?.at(0) ?? options.position ?? new Vector2(0, 0),
|
||||||
radius: 48,
|
radius: options.ranged ? 36 : 38,
|
||||||
speed: 325,
|
speed: 325,
|
||||||
team,
|
team,
|
||||||
visionRange: 1200,
|
visionRange: 1200,
|
||||||
visualRadius: options.ranged ? 36 : 38,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,14 +40,12 @@ export default class Template {
|
|||||||
d: Ability.circleOfResurrection.id,
|
d: Ability.circleOfResurrection.id,
|
||||||
f: Ability.blink.id,
|
f: Ability.blink.id,
|
||||||
},
|
},
|
||||||
height: 80,
|
|
||||||
logic: this.#playerLogic,
|
logic: this.#playerLogic,
|
||||||
maxHealth: 600,
|
maxHealth: 600,
|
||||||
pathfindingObstacleLimit: 3,
|
pathfindingObstacleLimit: 3,
|
||||||
radius: 65,
|
radius: 65,
|
||||||
spawnPosition: new Vector2(500, 150),
|
spawnPosition: new Vector2(500, 150),
|
||||||
visionRange: 1350,
|
visionRange: 1350,
|
||||||
visualRadius: 40,
|
|
||||||
...overrides,
|
...overrides,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user