diff --git a/client/public/assets/game/player/knight.png b/client/public/assets/game/player/knight.png new file mode 100644 index 00000000..a3c51e71 Binary files /dev/null and b/client/public/assets/game/player/knight.png differ diff --git a/client/public/assets/game/player/rook.png b/client/public/assets/game/player/rook.png new file mode 100644 index 00000000..d333ef2c Binary files /dev/null and b/client/public/assets/game/player/rook.png differ diff --git a/client/public/assets/game/player/samurai.png b/client/public/assets/game/player/samurai.png new file mode 100644 index 00000000..1628a93d Binary files /dev/null and b/client/public/assets/game/player/samurai.png differ diff --git a/client/public/assets/game/player/stalker.png b/client/public/assets/game/player/stalker.png new file mode 100644 index 00000000..37411003 Binary files /dev/null and b/client/public/assets/game/player/stalker.png differ diff --git a/client/public/assets/game/player/vampire.png b/client/public/assets/game/player/vampire.png new file mode 100644 index 00000000..095232c3 Binary files /dev/null and b/client/public/assets/game/player/vampire.png differ diff --git a/client/src/ServerList.ts b/client/src/ServerList.ts index a3db8f5b..7b329215 100644 --- a/client/src/ServerList.ts +++ b/client/src/ServerList.ts @@ -8,6 +8,7 @@ interface Server { ping: number; offline?: boolean; playerCnt?: number; + realPlayersCnt?: number; } @@ -54,7 +55,7 @@ export async function updatePing() { if (cache[server.address]) { server.offline = cache[server.address].offline; server.ping = cache[server.address].ping; - server.playerCnt = cache[server.address].playerCnt; + server.playerCnt = cache[server.address].realPlayersCnt; } else { try { const data = await fetch(`${window.location.protocol}//${server.address}/serverinfo?${Date.now()}`, { @@ -67,7 +68,7 @@ export async function updatePing() { const json = await data.json() server.offline = false; server.ping = Date.now() - start; - server.playerCnt = json.playerCnt; + server.playerCnt = json.realPlayersCnt; cache[server.address] = server; } catch (e) { diff --git a/client/src/game/Controls.ts b/client/src/game/Controls.ts index cd35d85c..aa3a8e17 100644 --- a/client/src/game/Controls.ts +++ b/client/src/game/Controls.ts @@ -44,7 +44,15 @@ export class Controls { }); this.joystick.on('pointerup', () => { this.joystickPointer = null; + this.joystick.setVisible(false); }) + this.game.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => { + if (pointer.x < this.game.scale.width / 2 && !this.joystick.visible) { + this.joystick.setPosition(pointer.x, pointer.y); + this.joystick.setVisible(true); + } + }); + input.addPointer(2); } diff --git a/client/src/game/Evolutions.ts b/client/src/game/Evolutions.ts index 39dc7deb..36ba2653 100644 --- a/client/src/game/Evolutions.ts +++ b/client/src/game/Evolutions.ts @@ -4,4 +4,9 @@ import { EvolutionTypes } from './Types'; export const Evolutions: Record = { [EvolutionTypes.Tank]: ['Tank', 'tankOverlay', 1, [0.5, 0.55]], [EvolutionTypes.Berserker]: ['Berserker', 'berserkerOverlay', 1.18, [0.47, 0.6]], + [EvolutionTypes.Vampire]: ['Vampire', 'vampireOverlay', 1.09, [0.5, 0.53]], + [EvolutionTypes.Knight]: ['Knight', 'knightOverlay', 1.09, [0.5, 0.53]], + [EvolutionTypes.Samurai]: ['Samurai', 'samuraiOverlay', 1.09, [0.5, 0.53]], + [EvolutionTypes.Rook]: ['Rook', 'rookOverlay', 1.09, [0.5, 0.53]], + [EvolutionTypes.Stalker]: ['Stalker', 'stalkerOverlay', 1.09, [0.5, 0.53]], }; diff --git a/client/src/game/Types.ts b/client/src/game/Types.ts index 706e909f..0ab51265 100644 --- a/client/src/game/Types.ts +++ b/client/src/game/Types.ts @@ -42,6 +42,11 @@ export enum EvolutionTypes { Default = 0, Tank = 1, Berserker = 2, + Vampire = 3, + Knight = 4, + Samurai = 5, + Rook = 6, + Stalker = 7, } export enum BuffTypes { diff --git a/client/src/game/scenes/Game.ts b/client/src/game/scenes/Game.ts index bbfe23b0..f3a6516e 100644 --- a/client/src/game/scenes/Game.ts +++ b/client/src/game/scenes/Game.ts @@ -37,6 +37,7 @@ export default class Game extends Phaser.Scene { this.gameState.initialize(); this.game.canvas.oncontextmenu = (e) => e.preventDefault(); this.isMobile = this.game.device.os.android || this.game.device.os.iOS; + // this.isMobile = true } preload() { @@ -77,8 +78,15 @@ export default class Game extends Phaser.Scene { this.load.image('chest6', publicPath + '/assets/game/Chest6.png'); this.load.image('crown', publicPath + '/assets/game/player/crown.png'); + + // evols this.load.image('tankOverlay', publicPath + '/assets/game/player/tank.png'); this.load.image('berserkerOverlay', publicPath + '/assets/game/player/berserker.png'); + this.load.image('vampireOverlay', publicPath + '/assets/game/player/vampire.png'); + this.load.image('knightOverlay', publicPath + '/assets/game/player/knight.png'); + this.load.image('samuraiOverlay', publicPath + '/assets/game/player/samurai.png'); + this.load.image('rookOverlay', publicPath + '/assets/game/player/rook.png'); + this.load.image('stalkerOverlay', publicPath + '/assets/game/player/stalker.png'); this.load.image('hitParticle', publicPath + '/assets/game/particles/hit.png'); this.load.image('starParticle', publicPath + '/assets/game/particles/star.png'); diff --git a/client/src/ui/App.tsx b/client/src/ui/App.tsx index 7e731c82..47302e54 100644 --- a/client/src/ui/App.tsx +++ b/client/src/ui/App.tsx @@ -143,7 +143,7 @@ function App() { }, 10); if(!firstGame) return; - // setModal(); + setModal(); }, [gameStarted]); const [server, setServer] = useState(Settings.server); diff --git a/client/src/ui/modals/ChangelogModal.tsx b/client/src/ui/modals/ChangelogModal.tsx index f14451cc..bd13f91a 100644 --- a/client/src/ui/modals/ChangelogModal.tsx +++ b/client/src/ui/modals/ChangelogModal.tsx @@ -3,12 +3,13 @@ import './ChangelogModal.scss'; function ChangelogModal() { return (
-

Sunsetting swordbattle.io

-

We regret to announce that swordbattle.io will be discontinued soon. Thank you for your support and engagement over the years! The game servers will be shutdown within the next week.

-

In the meantime, we encourage our community to get involved one last time. Show your creativity by designing your own game skins:

- +

Evolution Revamp! (May 11th, 2024)

+
  • - 4 new evolutions added!
  • +
  • - Balancing and improvements to PvP
  • +
  • - Improved Mobile Controls
  • + {/*
    Create your own skins to be added in the game! - + */}

    Stay tuned for more updates and future projects from our team.

    diff --git a/server/src/game/Types.js b/server/src/game/Types.js index 2b8cc747..95994822 100644 --- a/server/src/game/Types.js +++ b/server/src/game/Types.js @@ -48,6 +48,11 @@ module.exports = { Basic: 0, Tank: 1, Berserker: 2, + Vampire: 3, + Knight: 4, + Samurai: 5, + Rook: 6, + Stalker: 7, }, Buff: { Speed: 1, diff --git a/server/src/game/components/Health.js b/server/src/game/components/Health.js index 4ecbd0df..baf3c089 100644 --- a/server/src/game/components/Health.js +++ b/server/src/game/components/Health.js @@ -22,6 +22,10 @@ class Health { } } + gain(amount) { + this.percent = Math.min(this.percent + amount / this.max.value, 1); + } + update(dt) { if(Date.now() - this.lastDamage < this.regenWait.value) return; const coef = this.regen.value / this.max.value * dt; diff --git a/server/src/game/entities/Player.js b/server/src/game/entities/Player.js index c351e6cf..6789f038 100644 --- a/server/src/game/entities/Player.js +++ b/server/src/game/entities/Player.js @@ -89,6 +89,8 @@ class Player extends Entity { this.evolutions = new EvolutionSystem(this); this.tamedWolves = new Set(); + this.modifiers = {}; + this.chatMessage = ''; this.chatMessageTimer = new Timer(0, 3); } @@ -221,6 +223,16 @@ class Player extends Entity { this.movementDirection = mouseAngle; dx = speed * Math.cos(this.movementDirection); dy = speed * Math.sin(this.movementDirection); + + if(this.modifiers.disableDiagonalMovement) { + if (Math.abs(dx) > Math.abs(dy)) { + dy = 0; + dx = dx > 0 ? speed : -speed; + } else { + dx = 0; + dy = dy > 0 ? speed : -speed; + } + } } else { let directionX = 0; let directionY = 0; @@ -241,6 +253,13 @@ class Player extends Entity { this.movementDirection = Math.atan2(directionY, directionX); dx = speed * Math.cos(this.movementDirection); dy = speed * Math.sin(this.movementDirection); + + if(this.modifiers.disableDiagonalMovement) { + if (directionX !== 0 && directionY !== 0) { + dy = directionY * speed; + dx = 0; + } + } } else { this.movementDirection = 0; } @@ -360,6 +379,7 @@ class Player extends Entity { super.cleanup(); this.sword.cleanup(); this.flags.clear(); + this.modifiers = {}; [this.speed, this.regeneration, this.friction, this.viewport.zoom, this.knockbackResistance, this.health.regenWait].forEach((property) => property.reset()); } diff --git a/server/src/game/entities/Sword.js b/server/src/game/entities/Sword.js index 5a195df8..dbd8be0b 100644 --- a/server/src/game/entities/Sword.js +++ b/server/src/game/entities/Sword.js @@ -186,6 +186,10 @@ class Sword extends Entity { entity.velocity.y = -1*yComp; entity.damaged(this.damage.value, this.player); + if(this.player.modifiers.leech) { + this.player.health.gain(this.damage.value * this.player.modifiers.leech); + } + this.collidedEntities.add(entity); this.player.flags.set(Types.Flags.EnemyHit, entity.id); diff --git a/server/src/game/evolutions/Berserker.js b/server/src/game/evolutions/Berserker.js index dbc14068..a4e52e79 100644 --- a/server/src/game/evolutions/Berserker.js +++ b/server/src/game/evolutions/Berserker.js @@ -3,12 +3,13 @@ const Types = require('../Types'); module.exports = class Berserker extends Evolution { static type = Types.Evolution.Berserker; - static level = 10; + static level = 13; + static previousEvol = Types.Evolution.Knight; static abilityDuration = 7; - static abilityCooldown = 50; + static abilityCooldown = 60; applyAbilityEffects() { - this.player.sword.damage.multiplier *= 1.25; + this.player.sword.damage.multiplier *= 1.15; this.player.sword.knockback.multiplier['ability'] = 1.8; this.player.speed.multiplier *= 1.5; this.player.sword.swingDuration.multiplier['ability'] = 0.6; @@ -16,10 +17,10 @@ module.exports = class Berserker extends Evolution { update(dt) { super.update(dt); - - this.player.sword.damage.multiplier *= 1.2; - this.player.knockbackResistance.multiplier *= 1.1; + this.player.sword.damage.multiplier *= 1.1; + this.player.knockbackResistance.multiplier *= 1.05; this.player.speed.multiplier *= 1.1; this.player.health.max.multiplier *= 0.9; } } + diff --git a/server/src/game/evolutions/Knight.js b/server/src/game/evolutions/Knight.js new file mode 100644 index 00000000..30fb90bb --- /dev/null +++ b/server/src/game/evolutions/Knight.js @@ -0,0 +1,25 @@ +const Evolution = require('./BasicEvolution'); +const Types = require('../Types'); + +module.exports = class Knight extends Evolution { + static type = Types.Evolution.Knight; + static level = 8; + static abilityDuration = 6; + static abilityCooldown = 90; + + applyAbilityEffects() { + this.player.sword.damage.multiplier *= 1.15; + this.player.sword.knockback.multiplier['ability'] = 1.8; + this.player.speed.multiplier *= 1.5; + this.player.sword.swingDuration.multiplier['ability'] = 0.6; + } + + update(dt) { + super.update(dt); + + this.player.sword.damage.multiplier *= 1.1; + // this.player.knockbackResistance.multiplier *= 1.05; + this.player.speed.multiplier *= 1.05; + this.player.health.max.multiplier *= 0.9; + } +} diff --git a/server/src/game/evolutions/Rook.js b/server/src/game/evolutions/Rook.js new file mode 100644 index 00000000..776cb813 --- /dev/null +++ b/server/src/game/evolutions/Rook.js @@ -0,0 +1,49 @@ +const Evolution = require('./BasicEvolution'); +const Types = require('../Types'); + +module.exports = class Rook extends Evolution { + static type = Types.Evolution.Rook; + static level = 13; + static previousEvol = Types.Evolution.Tank; + static abilityDuration = 0.1; + static abilityCooldown = 40; + + applyAbilityEffects() { + const downInputs = this.player.inputs?.downInputs; + + let angle = Math.PI / 2; // dwn + + if(downInputs && downInputs.length > 0) { + switch (downInputs[0]) { + case 1: + angle = -Math.PI / 2; + break; + case 2: + angle = 0; + break; + case 3: + angle = Math.PI / 2; + break; + case 4: + angle = Math.PI; + break; + } + } + + this.player.shape.x = this.player.shape.x + (10000000 * Math.cos(angle)); + this.player.shape.y = this.player.shape.y + (10000000 * Math.sin(angle)); + } + + update(dt) { + this.player.modifiers.disableDiagonalMovement = true; + + this.player.shape.setScale(1.25); + this.player.sword.damage.multiplier *= 1.2; + this.player.sword.knockback.multiplier['ability'] = 1.25; + this.player.knockbackResistance.multiplier *= 1.25; + this.player.health.max.multiplier *= 1.25; + this.player.health.regen.multiplier *= 1.25; + this.player.health.regenWait.multiplier *= 1.15; + super.update(dt); + } +} diff --git a/server/src/game/evolutions/Samurai.js b/server/src/game/evolutions/Samurai.js new file mode 100644 index 00000000..3c668eb6 --- /dev/null +++ b/server/src/game/evolutions/Samurai.js @@ -0,0 +1,35 @@ +const Evolution = require('./BasicEvolution'); +const Types = require('../Types'); + +module.exports = class Samurai extends Evolution { + static type = Types.Evolution.Samurai; + static level = 13; + static previousEvol = Types.Evolution.Tank; + static abilityDuration = 6; + static abilityCooldown = 60; + + applyAbilityEffects() { + this.player.sword.damage.multiplier *= 1.5; + this.player.sword.knockback.multiplier['ability'] = 2.5; + this.player.knockbackResistance.multiplier *= 1.5; + this.player.health.regen.multiplier *= 8; + this.player.speed.multiplier *= 1.25; + + + this.player.health.regenWait.multiplier = 0; + this.player.sword.swingDuration.multiplier['ability'] = 0.5; + } + + update(dt) { + super.update(dt); + this.player.speed.multiplier *= 0.85; + this.player.shape.setScale(1.05); + this.player.sword.damage.multiplier *= 1.15; + this.player.sword.knockback.multiplier['ability'] = 1.15; + this.player.knockbackResistance.multiplier *= 1.15; + this.player.health.max.multiplier *= 1.15; + this.player.health.regen.multiplier *= 1.15; + this.player.health.regenWait.multiplier *= 1; + //TODO: Damagecooldown: 1.1 + } +} diff --git a/server/src/game/evolutions/Stalker.txt b/server/src/game/evolutions/Stalker.txt new file mode 100644 index 00000000..c3443421 --- /dev/null +++ b/server/src/game/evolutions/Stalker.txt @@ -0,0 +1,17 @@ +// const Evolution = require('./BasicEvolution'); +// const Types = require('../Types'); + +// module.exports = class Stalker extends Evolution { +// static type = Types.Evolution.Stalker; +// static level = 3; +// static previousEvol = Types.Evolution.Vampire; +// static abilityDuration = 6; +// static abilityCooldown = 90; + +// applyAbilityEffects() { +// } + +// update(dt) { +// super.update(dt); +// } +// } diff --git a/server/src/game/evolutions/Tank.js b/server/src/game/evolutions/Tank.js index cb7d3ee0..4946b558 100644 --- a/server/src/game/evolutions/Tank.js +++ b/server/src/game/evolutions/Tank.js @@ -3,31 +3,30 @@ const Types = require('../Types'); module.exports = class Tank extends Evolution { static type = Types.Evolution.Tank; - static level = 10; + static level = 8; static abilityDuration = 6; static abilityCooldown = 90; applyAbilityEffects() { - this.player.sword.damage.multiplier *= 2; + this.player.sword.damage.multiplier *= 1.5; this.player.sword.knockback.multiplier['ability'] = 2.5; this.player.knockbackResistance.multiplier *= 1.5; this.player.shape.setScale(1.75); this.player.health.regen.multiplier *= 8; - // todo: fix bug why it cant be 0 (it doesnt recover then) this.player.health.regenWait.multiplier = 0; this.player.sword.swingDuration.multiplier['ability'] = 0.5; } update(dt) { super.update(dt); - this.player.speed.multiplier *= 0.75; - this.player.shape.setScale(1.25); - this.player.sword.damage.multiplier *= 1.25; - this.player.sword.knockback.multiplier['ability'] = 1.25; - this.player.knockbackResistance.multiplier *= 1.25; - this.player.health.max.multiplier *= 1.25; - this.player.health.regen.multiplier *= 1.25; + this.player.speed.multiplier *= 0.7; + this.player.shape.setScale(1.15); + this.player.sword.damage.multiplier *= 1.15; + this.player.sword.knockback.multiplier['ability'] = 1.15; + this.player.knockbackResistance.multiplier *= 1.15; + this.player.health.max.multiplier *= 1.15; + this.player.health.regen.multiplier *= 1.15; this.player.health.regenWait.multiplier *= 1; //TODO: Damagecooldown: 1.1 } diff --git a/server/src/game/evolutions/Vampire.js b/server/src/game/evolutions/Vampire.js new file mode 100644 index 00000000..9a776b55 --- /dev/null +++ b/server/src/game/evolutions/Vampire.js @@ -0,0 +1,22 @@ +const Evolution = require('./BasicEvolution'); +const Types = require('../Types'); + +module.exports = class Vampire extends Evolution { + static type = Types.Evolution.Vampire; + static level = 13; + static previousEvol = Types.Evolution.Knight; + // static level = 1; + static abilityDuration = 6; + static abilityCooldown = 90; + + applyAbilityEffects() { + this.player.modifiers.leech = 2; + this.player.sword.knockback.multiplier['ability'] = 1.8; + this.player.speed.multiplier *= 1.5; + } + + update(dt) { + this.player.modifiers.leech = 0.5; + super.update(dt); + } +} diff --git a/server/src/game/evolutions/index.js b/server/src/game/evolutions/index.js index 96c8ddf6..5de3b7d7 100644 --- a/server/src/game/evolutions/index.js +++ b/server/src/game/evolutions/index.js @@ -4,6 +4,7 @@ const evolutions = {}; fs.readdirSync(__dirname).forEach((file) => { if (file == 'index.js') return; + if (!file.endsWith('.js')) return; const EvolutionClass = require(`${__dirname}/${file}`); evolutions[EvolutionClass.type] = EvolutionClass; @@ -38,7 +39,8 @@ class EvolutionSystem { const Evol = evolutions[evolution]; return Evol && Evol.level <= this.player.levels.level && (Evol.biomes.length === 0 || Evol.biomes.includes(this.player.biome)) - && evolutions[this.evolution].level < Evol.level; + && evolutions[this.evolution].level < Evol.level + && (Evol.previousEvol === undefined || this.evolution === Evol.previousEvol); } upgrade(evol) {