diff --git a/public/client.js b/public/client.js index fcadfbe..b0687d0 100644 --- a/public/client.js +++ b/public/client.js @@ -10,18 +10,21 @@ const backgroundColor = new THREE.Color().setHex(0x112233) scene.background = backgroundColor renderer.setSize(window.innerWidth, window.innerHeight) renderer.setAnimationLoop(render) -camera.position.set(5, -12, 10) -camera.rotation.set((60 / 180) * Math.PI, 0, 0) +camera.position.set(5, -12, 20) +// camera.position.set(5, -12, 10) +camera.rotation.set((56 / 180) * Math.PI, 0, 0) camera.layers.enable(1) camera.layers.enable(2) const entityMaterial = new THREE.MeshToonMaterial({ color: 0xffffff }) -const projectileMaterial = new THREE.MeshToonMaterial({ color: 0xff00ff }) +const projectileMaterial = new THREE.MeshToonMaterial({ color: 0xcccccc }) const terrainMaterial = new THREE.MeshToonMaterial({ color: 0xffd700 }) +const opacity = 0.3 const teamMaterials = { - blue: new THREE.MeshToonMaterial({ color: 0x4444ff }), - neutral: new THREE.MeshToonMaterial({ color: 0x22dd22 }), - red: new THREE.MeshToonMaterial({ color: 0xff4444 }), + blue: new THREE.MeshToonMaterial({ color: 0x4444ff, transparent: true, opacity }), + neutral: new THREE.MeshToonMaterial({ color: 0x22dd22, transparent: true, opacity }), + red: new THREE.MeshToonMaterial({ color: 0xff4444, transparent: true, opacity }), + projectile: new THREE.MeshToonMaterial({ color: 0xff00ff, transparent: true, opacity }), } const minimapCamera = new THREE.OrthographicCamera(-10, 10, 10, -10) @@ -41,11 +44,11 @@ const material = new THREE.MeshToonMaterial({ color: 0x115011 }) const ground = new THREE.Mesh(geometry, material) scene.add(ground) -const ambientLight = new THREE.AmbientLight(0x404040) +const ambientLight = new THREE.AmbientLight(0x404040, 10) scene.add(ambientLight) const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5) -directionalLight.position.set(-0.3, 0.1, 1) +directionalLight.position.set(-0.5, -0.05, 1) directionalLight.power = 3000 scene.add(directionalLight) @@ -74,11 +77,12 @@ function followCamera() { if (entity == null) { return } const offsetX = 0 - const offsetY = -16.5 + const offsetY = -28 const distanceX = Math.abs((entity.position.x + offsetX) - camera.position.x) const distanceY = Math.abs((entity.position.y + offsetY) - camera.position.y) + camera.position.z = 20 if (distanceX > 0.01) { if (entity.position.x + offsetX > camera.position.x) { camera.position.x += cameraSpeed * distanceX @@ -118,7 +122,7 @@ function cameraMovement() { else if (keysDown.ArrowDown) { camera.position.y -= cameraSpeed } if (keysDown.Space) { - camera.position.set(entities['1'].position.x, entities['1'].position.y - 17, 10) + camera.position.set(entities[playerId].position.x, entities[playerId].position.y - 28, 20) } } @@ -177,7 +181,7 @@ function connectWebSocket() { entity = entities[e.id] } else { - entity = new THREE.Mesh(new THREE.CylinderGeometry(e.radius / 100, e.radius / 100, e.height / 50), entityMaterial) + entity = new THREE.Mesh(new THREE.CylinderGeometry(e.visualRadius / 100, e.visualRadius / 100, e.height / 50), entityMaterial) entity.rotation.x = Math.PI / 2 entity.userData.type = 'entity' entity.userData.id = e.id @@ -199,8 +203,9 @@ function connectWebSocket() { const teamMaterial = teamMaterials[e.team] const teamMarker = new THREE.Mesh(new THREE.CylinderGeometry((e.radius + 2) / 100, (e.radius + 2) / 100, 1), teamMaterial) - teamMarker.scale.y = e.height / 400 - teamMarker.position.y = (e.height / 800) - (e.height / 100) + const teamMarkerSize = 4000 + teamMarker.scale.y = e.height / teamMarkerSize + teamMarker.position.y = (e.height / (teamMarkerSize * 2)) - (e.height / 100) entity.rotation.x = Math.PI / 2 entity.add(teamMarker) @@ -234,13 +239,22 @@ function connectWebSocket() { projectile = projectiles[p.id] } else { - projectile = new THREE.Mesh(new THREE.SphereGeometry(p.radius / 100), projectileMaterial) + projectile = new THREE.Mesh(new THREE.SphereGeometry(p.visualRadius / 100), projectileMaterial) projectile.userData.type = 'projectile' projectile.userData.id = p.id projectile.position.set(p.position.x / 100, p.position.y / 100, p.height / 100) projectile.layers.set(2) scene.add(projectile) + const teamMaterial = teamMaterials['projectile'] + const teamMarker = new THREE.Mesh(new THREE.CylinderGeometry((p.radius) / 100, (p.radius) / 100, 1), teamMaterial) + const teamMarkerSize = 4000 + teamMarker.scale.y = p.height / teamMarkerSize + teamMarker.position.y = (p.height / (teamMarkerSize * 2)) - (p.height / 100) + projectile.rotation.x = Math.PI / 2 + projectile.layers.set(2) + projectile.add(teamMarker) + projectiles[p.id] = projectile } diff --git a/src/ability.js b/src/ability.js index fa07eab..9203d29 100644 --- a/src/ability.js +++ b/src/ability.js @@ -30,9 +30,10 @@ export default class Ability { name: 'Straight Shot', castTime: 0.25, cooldown: 1, - damage: 10, + damage: 83, radius: 60, range: 1200, + visualRadius: 20, speed: 2000, effect: function straightShotEffect(caster, cursor) { const ability = this @@ -47,6 +48,7 @@ export default class Ability { position: caster.position.clone(), radius: ability.radius, speed: ability.speed, + visualRadius: ability.visualRadius, }) projectile.destination = caster.position.clone().add(cursor.clone().sub(caster.position).normalize().multiplyScalar(ability.range + caster.radius)) @@ -58,13 +60,13 @@ export default class Ability { static rangedAttack = new Ability({ id: 'ranged_attack', name: 'Ranged Attack', - castTime: 0.25, - cooldown: 1.25, - damage: 25, + castTime: (1.6 * 0.18839), + cooldown: 1.6, + damage: 60, moveCancelable: true, radius: 5, range: 500, - speed: 600, + speed: 2000, effect: function rangedAttackEffect(caster, cursor) { const ability = this const target = caster.closestTargetTo(cursor, ability.range) @@ -110,11 +112,11 @@ export default class Ability { static shieldThrow = new Ability({ id: 'shield_throw', name: 'Shield Throw', - castTime: 0.15, + castTime: 0.25, cooldown: 5, - radius: 20, - range: 1000, - speed: 2000, + radius: 110, + range: 1025, + speed: 2400, effect: function shieldThrowEffect(caster, cursor) { const ability = this const shieldThrowReturn = function shieldThrowReturn(projectile, homingTarget) { @@ -146,9 +148,9 @@ export default class Ability { static blink = new Ability({ id: 'blink', name: 'Blink', - castTime: 1, + castTime: 0.25, cooldown: 2, - range: 400, + range: 475, effect: function blinkEffect(caster, cursor) { const ability = this const direction = cursor.clone().sub(caster.position) diff --git a/src/entity.js b/src/entity.js index d84860a..13d2ef4 100644 --- a/src/entity.js +++ b/src/entity.js @@ -13,11 +13,12 @@ export default class Entity { health = null height = 40 maxHealth = 1 - memory = {} // TODO: WARNING: currently only used for minions (code smell?) + memory = {} position = null radius = 0 speed = 400 team = Team.neutral + visualRadius = null #attacking = false #dest = null @@ -39,6 +40,9 @@ export default class Entity { if (this.health == null) { this.health = this.maxHealth } + if (this.visualRadius == null) { + this.visualRadius = this.radius + } } get destination() { return this.#dest } @@ -90,6 +94,7 @@ export default class Entity { castAction(slot, cursor, halt = false) { const ability = this.abilities[slot] + if (ability == null) { return } if (this.casting != null) { const abilityBeingCasted = this.casting.ability diff --git a/src/index.js b/src/index.js index 8b8e8fb..c56db62 100644 --- a/src/index.js +++ b/src/index.js @@ -37,24 +37,29 @@ app.ws('/ws', async (req, res) => { console.log(message) } + let delay = 0 + // if(message.id == '1') { + // delay = 45 + // } + if (message.action == 'attack') { - entity.attackAction(new Vector2(message.x, message.y)) + setTimeout(() => entity.attackAction(new Vector2(message.x, message.y)), delay) } if (message.action == 'cast') { - entity.castAction(message.slot, new Vector2(message.x, message.y)) + setTimeout(() => entity.castAction(message.slot, new Vector2(message.x, message.y)), delay) } if (message.action == 'halt') { - entity.haltAction() + setTimeout(() => entity.haltAction(), delay) } if (message.action == 'stop') { - entity.stopAction() + setTimeout(() => entity.stopAction(), delay) } if (message.action == 'move') { - entity.moveAction(new Vector2(message.x, message.y)) + setTimeout(() => entity.moveAction(new Vector2(message.x, message.y)), delay) } }) }) @@ -68,13 +73,13 @@ function laneScenario() { game.spawnEntity(player1) player1.attackAction(new Vector2(500, 150)) - // const player2 = new Entity(Template.player({ - // id: '2', - // spawnPosition: new Vector2(1600, 1800), - // team: Team.red, - // })) - // game.spawnEntity(player2) - // player2.attackAction(new Vector2(1600, 1800)) + const player2 = new Entity(Template.player({ + id: '2', + spawnPosition: new Vector2(1600, 1800), + team: Team.red, + })) + game.spawnEntity(player2) + player2.attackAction(new Vector2(1600, 1800)) const midWallStart = new Vector2(600, 600) const midWallEnd = new Vector2(1400, 1400) @@ -116,24 +121,7 @@ function laneScenario() { game.spawnEntity(new Entity(Template.minion(Team.red, { ranged: true, route: redRoute }))) } } - // game.logic = gameLogic - - // const uBottomPoints = [ - // midSouthWallPoints.at(0).clone().sub(midWallThickness), - // midSouthWallPoints.at(1).clone().sub(midWallThickness), - // midNorthWallPoints.at(-2).clone().add(midWallThickness), - // midNorthWallPoints.at(-1).clone().add(midWallThickness), - // ] - // const uBottom = new Terrain(uBottomPoints) - // uBottom.id = 'uBottom' - // game.addTerrain(uBottom) - - const minion = new Entity({...Template.minion(Team.red, { ranged: true }), logic: null}) - minion.teleport(new Vector2(850, 250)) - game.spawnEntity(minion) - - player1.stopAction() - player1.castAction(1, new Vector2(850, 250)) + game.logic = gameLogic } app.listen(port, () => { diff --git a/src/projectile.js b/src/projectile.js index fa0b913..864b734 100644 --- a/src/projectile.js +++ b/src/projectile.js @@ -6,12 +6,13 @@ export default class Projectile { id = crypto.randomUUID() after = null height = 50 - memory = {} // TODO: WARNING: currently only used for hit detection (code smell?) + memory = {} onCollide = null owner = null position = new Vector2() radius = 5 speed = 1000 + visualRadius = null #dest = null #homingTarget = null @@ -28,6 +29,9 @@ export default class Projectile { constructor(options = {}) { Object.entries(options).forEach(([key, value]) => this[key] = value) + if (this.visualRadius == null) { + this.visualRadius = this.radius + } } checkCollisions(collider) { @@ -39,7 +43,7 @@ export default class Projectile { } }) } - + collider() { return new SAT.Circle(new SAT.Vector(this.position.x, this.position.y), this.radius) } @@ -72,6 +76,8 @@ export default class Projectile { } #move() { + if (this.destination == null) { return } + const speed = (this.speed / (this.game?.tickBudget ?? 1000)) const prevPos = this.position.clone() if (this.position.distanceTo(this.destination) < speed) { diff --git a/src/template.js b/src/template.js index d73719d..7d8b64d 100644 --- a/src/template.js +++ b/src/template.js @@ -10,9 +10,10 @@ export default class Template { logic: this.#minionLogic(options.route), maxHealth: options.ranged ? 300 : 450, position: team == Team.blue ? new Vector2(200, 200) : new Vector2(1800, 1800), - radius: options.ranged ? 46 : 48, + radius: 48, speed: 325, team, + visualRadius: options.ranged ? 36 : 38, } } @@ -29,6 +30,7 @@ export default class Template { maxHealth: 600, spawnPosition: new Vector2(500, 150), radius: 65, + visualRadius: 40, ...overrides, } }