diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2df297a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,48 @@
+<<<<<<< HEAD
+node_modules/
+.env
+# Compiled source #
+###################
+.breakpoints
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+out/
+config.json
+dist/
+# Packages #
+############
+# it's better to unpack these files and commit the raw source
+# git has its own built in compression methods
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+
+# Logs and databases #
+######################
+*.log
+*.sql
+*.sqlite
+
+# OS generated files #
+######################
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+node_modules/
+.env
+.DS_Store
+package-lock.json
\ No newline at end of file
diff --git a/Procfile b/Procfile
new file mode 100644
index 0000000..4154ce2
--- /dev/null
+++ b/Procfile
@@ -0,0 +1 @@
+web: node .
\ No newline at end of file
diff --git a/assets/html/name.html b/assets/html/name.html
new file mode 100644
index 0000000..b004c9a
--- /dev/null
+++ b/assets/html/name.html
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Waterwar.io
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/images/bg.jpeg b/assets/images/bg.jpeg
new file mode 100644
index 0000000..5dbf214
Binary files /dev/null and b/assets/images/bg.jpeg differ
diff --git a/assets/images/redo.png b/assets/images/redo.png
new file mode 100644
index 0000000..70563c7
Binary files /dev/null and b/assets/images/redo.png differ
diff --git a/assets/images/star-grey.png b/assets/images/star-grey.png
new file mode 100644
index 0000000..65138e1
Binary files /dev/null and b/assets/images/star-grey.png differ
diff --git a/assets/images/star-medium.png b/assets/images/star-medium.png
new file mode 100644
index 0000000..69ee668
Binary files /dev/null and b/assets/images/star-medium.png differ
diff --git a/assets/images/star-yellow.png b/assets/images/star-yellow.png
new file mode 100644
index 0000000..99b6e2d
Binary files /dev/null and b/assets/images/star-yellow.png differ
diff --git a/classes/Bridge.js b/classes/Bridge.js
new file mode 100644
index 0000000..f230ce1
--- /dev/null
+++ b/classes/Bridge.js
@@ -0,0 +1,57 @@
+const intersect = require("intersects");
+module.exports = class Bridge {
+ constructor(island1, island2, width) {
+ this.island1 = island1;
+ this.island2 = island2;
+ this.width = width;
+ this.length = Math.sqrt(Math.pow(this.island1.pos.x - this.island2.pos.x, 2) + Math.pow(this.island1.pos.y - this.island2.pos.y, 2));
+
+ // calculate angle
+ this.angle = Math.atan2(island2.pos.y - island1.pos.y, island2.pos.x - island1.pos.x)+Math.PI/2;
+ //to degrees
+ console.log( this.angle * 180 / Math.PI);
+ }
+ getSendObject() {
+ return {
+ width: this.width,
+ length: this.length,
+ angle: this.angle,
+ pos: this.getCorners()[2],
+ corners: [this.getCorners()[2]],
+ };
+ }
+ getCorners(extraDiff = 1) {
+ // find 4 corners of the bridge, make sure to use the angle and width
+ var newWidth = this.width * extraDiff;
+ var corners = [];
+ corners.push({
+ x: this.island1.pos.x + Math.cos(this.angle) * newWidth,
+ y: this.island1.pos.y + Math.sin(this.angle) * newWidth,
+ });
+ corners.push({
+ x: this.island1.pos.x - Math.cos(this.angle) * newWidth,
+ y: this.island1.pos.y - Math.sin(this.angle) * newWidth,
+ });
+ corners.push({
+ x: this.island2.pos.x - Math.cos(this.angle) * newWidth,
+ y: this.island2.pos.y - Math.sin(this.angle) * newWidth,
+ });
+ corners.push({
+ x: this.island2.pos.x + Math.cos(this.angle) * newWidth,
+ y: this.island2.pos.y + Math.sin(this.angle) * newWidth,
+ });
+ return corners;
+ }
+ isIn(player) {
+ // check if pos is in the bridge
+ var corners = [];
+ var u = this.getCorners()
+ u.forEach(corner => {
+ corners.push(corner.x);
+ corners.push(corner.y);
+ });
+ var center = player.getCenterPoint();
+
+ return intersect.pointPolygon(center.x, center.y, corners);
+ }
+}
\ No newline at end of file
diff --git a/classes/Bullet.js b/classes/Bullet.js
new file mode 100644
index 0000000..b5df46b
--- /dev/null
+++ b/classes/Bullet.js
@@ -0,0 +1,63 @@
+const idgen = require("../helpers/idgen");
+const intersect = require("intersects");
+const io = require("../helpers/io");
+
+function getRandomInt(min, max) {
+ min = Math.ceil(min);
+ max = Math.floor(max);
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+}
+
+class Bullet {
+ constructor(player, offset) {
+ this.speed = player.speed / 2;
+ this.angle = player.lookAngle;
+ //cloning the object is necessary because the object is changed in the tick function
+ this.pos = JSON.parse(JSON.stringify(player.pos));
+ this.pos.x += (Math.cos(this.angle) * this.speed * (50+offset));
+ this.pos.y += (Math.sin(this.angle) * this.speed * (50+offset));
+ this.createdAt = Date.now();
+ this.owner = player.id;
+ this.roomId = player.roomId;
+ this.id = idgen();
+
+ this.length = 10;
+ io.getio().to(this.roomId).emit("addBullet", this.getSendObject());
+ }
+ tick(tickDiff) {
+ this.pos.x += Math.cos(this.angle) * this.speed * 50 * (tickDiff / 50);
+ this.pos.y += Math.sin(this.angle) * this.speed * 50 * (tickDiff / 50);
+ }
+ collidingPlayer(entity) {
+ if(entity.id == this.owner) return false;
+
+
+
+ var corners = entity.getCorners();
+ var arr = [];
+ corners.forEach(function(corner) {
+ arr.push(corner.x);
+ arr.push(corner.y);
+ });
+
+ //check is inside corners
+
+
+ return intersect.circlePolygon(this.pos.x, this.pos.y, this.length, arr);
+
+ }
+ collidingBullet(bullet) {
+ return intersect.circleCircle(this.pos.x, this.pos.y, this.length, bullet.pos.x, bullet.pos.y, bullet.length);
+ }
+ getSendObject() {
+ return {
+ pos: this.pos,
+ id: this.id,
+ angle: this.angle,
+ speed: this.speed,
+ owner: this.owner,
+ }
+ }
+}
+
+module.exports = Bullet;
\ No newline at end of file
diff --git a/classes/Island.js b/classes/Island.js
new file mode 100644
index 0000000..2f33d54
--- /dev/null
+++ b/classes/Island.js
@@ -0,0 +1,90 @@
+const intersect = require("intersects");
+const io = require("../helpers/io");
+const idgen = require("../helpers/idgen");
+module.exports = class Island {
+ constructor(shape, size, position, canBeCaptured = true) {
+ this.shape = shape;
+ this.size = size;
+ this.pos = position;
+ this.canBeCaptured = canBeCaptured;
+ this.capturedBy = "none";
+ this.captureState = 0;
+ this.capturingBy = 0.1;
+ this.id = idgen();
+ this.capturedPercentage = 0;
+ }
+ getSendObject() {
+ return {
+ shape: this.shape,
+ size: this.size,
+ pos: this.pos,
+ capturedBy: this.capturedBy,
+ id: this.id,
+ capturedPercentage: this.capturedPercentage,
+ capturingBy: this.capturingBy
+ }
+ }
+ tick(diff, room) {
+ if(!this.canBeCaptured) return;
+ var players = Array.from(room.players.values()).filter(player => this.isIn(player.pos));
+ if(players.length < 1 && this.captureState == 1) {
+ this.capturedPercentage -= (diff / 50) * 0.5;
+ if(this.capturedPercentage <= 0) {
+ this.capturedPercentage = 0;
+ this.captureState = 0;
+ }
+ io.getio().to(room.id).emit("islandCapturing", this.id, this.capturingBy, this.capturedPercentage);
+
+ }
+ if(players.length < 1 && this.captureState == 2) {
+ if(this.capturedPercentage >= 100 && this.capturingBy == this.capturedBy) return;
+ this.capturedPercentage += (diff / 50) * 0.5;
+ if(this.capturedPercentage >= 100) {
+ this.capturedBy = this.capturingBy;
+ this.captureState = 0;
+ io.getio().to(room.id).emit("islandCaptured", this.id, this.capturedBy);
+ } else io.getio().to(room.id).emit("islandCapturing", this.id, this.capturingBy, this.capturedPercentage);
+
+ console.log(this.capturedPercentage);
+
+ }
+ if(players.length < 1) return;
+ //make sure all players are in the same team
+ var team = players[0].team;
+ for(var i = 1; i < players.length; i++) {
+ if(players[i].team != team) return;
+ }
+ if(this.capturedBy == team) return;
+ console.log(this.captureState);
+ if(this.captureState == 0) {
+ this.captureState = 1;
+ this.capturedPercentage = 0;
+ this.capturingBy = players[0].team;
+ console.log(this.capturingBy+ " is capturing island");
+ } else if(this.captureState == 1 && this.capturingBy == team) {
+ this.capturedPercentage += (diff / 50) * players.length;
+
+ if(this.capturedPercentage >= 100) {
+ this.capturedBy = team;
+ this.captureState = 2;
+ io.getio().to(room.id).emit("islandCaptured", this.id, this.capturedBy);
+ } else {
+ io.getio().to(room.id).emit("islandCapturing", this.id, this.capturingBy, this.capturedPercentage);
+ }
+
+ } else if(this.captureState == 2) {
+ this.capturedPercentage -= (diff / 50) * players.length;
+ this.capturedBy = "none";
+ if(this.capturedPercentage <= 0) {
+ this.capturedPercentage = 0;
+ this.captureState = 0;
+ }
+ io.getio().to(room.id).emit("islandCaptured", this.id, this.capturedBy);
+ io.getio().to(room.id).emit("islandCapturing", this.id, this.capturingBy, this.capturedPercentage);
+ }
+
+ }
+ isIn(pos) {
+ return intersect.pointCircle(pos.x, pos.y, this.pos.x, this.pos.y, this.size/2);
+ }
+}
\ No newline at end of file
diff --git a/classes/Player.js b/classes/Player.js
new file mode 100644
index 0000000..ab75b07
--- /dev/null
+++ b/classes/Player.js
@@ -0,0 +1,146 @@
+const idgen = require("../helpers/idgen");
+const roomlist = require("../helpers/roomlist");
+
+const Bullet = require("./Bullet");
+
+class Player {
+ constructor(name, id=idgen(), socket=undefined) {
+ this.name = name;
+ this.id = id;
+ this.roomId = null;
+ this.socket = socket;
+ this.pos = {
+ x: 0,
+ y: 0,
+ };
+ this.speed = 1;
+ this.down = false;
+
+ this.bodySize = 50;
+
+ this.team = Math.random() > 0.5 ? "red" : "blue";
+
+ this.hit = false;
+ this.lookAngle = 0;
+ this.bulletIds = new Set();
+ this.lastFullSendTo = new Set();
+
+ this.controller = {
+ left: false,
+ right: false,
+ up: false,
+ down: false,
+ };
+ }
+ joinRoom(room) {
+ this.roomId = room.id;
+ this.socket.emit("joinRoom", room.id);
+ }
+ updateController(controller) {
+ //check if controller valid
+ if(controller.left === undefined || controller.right === undefined || controller.up === undefined || controller.down === undefined) {
+ return;
+ }
+ //check if any extra properties are set
+ if(Object.keys(controller).length > 4) {
+ return;
+ }
+
+ this.controller = controller;
+ }
+ updateMouse(mouseAngle) {
+ this.lookAngle = mouseAngle;
+ }
+ getFirstSendObject() {
+ return {
+ name: this.name,
+ id: this.id,
+ speed: this.speed,
+ team: this.team,
+
+ pos: this.pos,
+ lookAngle: this.lookAngle
+ }
+ }
+ getSendObject() {
+ return {
+ id: this.id,
+ pos: this.pos,
+ lookAngle: this.lookAngle,
+ };
+ }
+ getCorners(extraDiff = 1) {
+ // get each corner of the player's body
+ // make sure it's rotated correctly
+
+ var corners = [];
+ var angle = this.lookAngle+0.785398;
+ var x = this.pos.x;
+ var y = this.pos.y;
+ var length = this.bodySize * extraDiff;
+
+ var cos = Math.cos(angle);
+ var sin = Math.sin(angle);
+
+ corners.push({
+ x: x + cos * length,
+ y: y + sin * length,
+ });
+ corners.push({
+ x: x - cos * length,
+ y: y - sin * length,
+ });
+ corners.push({
+ x: x + sin * length,
+ y: y - cos * length,
+ });
+ corners.push({
+ x: x - sin * length,
+ y: y + cos * length,
+ });
+ // console.log(corners);
+ return corners;
+
+ }
+ getCenterPoint() {
+ var corners = this.getCorners();
+ //find center point
+ var x = 0;
+ var y = 0;
+ for(var i = 0; i < corners.length; i++) {
+ x += corners[i].x;
+ y += corners[i].y;
+ }
+ x /= corners.length;
+ y /= corners.length;
+ return {
+ x,
+ y,
+ }
+ }
+
+ tick(tickDiff) {
+ //move
+ if(this.controller.left) {
+ this.pos.x -= tickDiff * 0.2 * this.speed;
+ }
+ if(this.controller.right) {
+ this.pos.x += tickDiff * 0.2 * this.speed;
+ }
+ if(this.controller.up) {
+ this.pos.y -= tickDiff * 0.2 * this.speed;
+ }
+ if(this.controller.down) {
+ this.pos.y += tickDiff * 0.2* this.speed;
+ }
+
+ var corners = this.getCorners(0.5);
+ // this.socket.emit("corners", [this.pos])
+ //shoot
+ if(!this.down) return;
+ var room = roomlist.getRoom(this.roomId);
+ this.down = false;
+ room.bullets.push(new Bullet(this, 0));
+ }
+}
+module.exports = Player;
\ No newline at end of file
diff --git a/classes/Room.js b/classes/Room.js
new file mode 100644
index 0000000..d31188a
--- /dev/null
+++ b/classes/Room.js
@@ -0,0 +1,174 @@
+const idgen = require("../helpers/idgen");
+const io = require("../helpers/io");
+const intersect = require("intersects");
+const Island = require("./Island");
+const Bridge = require("./Bridge");
+
+class Room {
+ constructor() {
+ this.id = idgen();
+ this.players = new Map();
+ this.maxPlayers = 50;
+ this.lastTick = Date.now();
+ this.bullets = [];
+ this.islands = [];
+ this.bridges = [];
+
+ this.islands.push(new Island(
+ "circle",
+ 1500,
+ { x: 0, y: 0 },
+ false
+ ));
+
+ this.islands.push(new Island(
+ "circle",
+ 500,
+ { x: 1200, y: 1200 },
+ ));
+
+ this.islands.push(new Island(
+ "circle",
+ 500,
+ { x: -1100, y: 1200 },
+ ));
+
+ this.islands.push(new Island(
+ "circle",
+ 500,
+ { x: 1500, y: -100 },
+ ));
+ this.islands.push(new Island(
+ "circle",
+ 500,
+ { x: -1400, y: -100 },
+ ));
+ this.islands.push(new Island(
+ "circle",
+ 500,
+ { x: 0, y: -1400 },
+ ));
+ this.bridges.push(new Bridge(this.islands[0], this.islands[1], 100));
+ this.bridges.push(new Bridge(this.islands[0], this.islands[2], 100));
+ this.bridges.push(new Bridge(this.islands[0], this.islands[3], 100));
+ this.bridges.push(new Bridge(this.islands[0], this.islands[4], 100));
+ this.bridges.push(new Bridge(this.islands[0], this.islands[5], 100));
+
+ this.bridges.push(new Bridge(this.islands[1], this.islands[2], 100));
+ this.bridges.push(new Bridge(this.islands[2], this.islands[4], 100));
+ this.bridges.push(new Bridge(this.islands[4], this.islands[5], 100));
+ this.bridges.push(new Bridge(this.islands[5], this.islands[3], 100));
+ this.bridges.push(new Bridge(this.islands[3], this.islands[1], 100));
+
+
+
+ }
+ addPlayer(player) {
+ var ioinstance = io.getio();
+ player.joinRoom(this);
+ this.players.set(player.id, player);
+ player.socket.join(this.id);
+
+ //TODO: only send players in range
+ ioinstance.to(this.id).emit("playerJoined", player.getFirstSendObject());
+ player.socket.emit("players", [...this.players.values()].map((player) => player.getFirstSendObject()));
+ player.socket.emit("bullets", this.bullets.map((bullet) => bullet.getSendObject()));
+ player.socket.emit("islands", this.islands.map((island) => island.getSendObject()));
+ player.socket.emit("bridges", this.bridges.map((bridge) => bridge.getSendObject()));
+ }
+ removePlayer(id) {
+ var ioinstance = io.getio();
+ var player = this.players.get(id);
+ if(player) {
+ player.socket.leave(this.id);
+ this.players.delete(id);
+ ioinstance.to(this.id).emit("playerLeft", id);
+ }
+ }
+ playerControllerUpdate(id, controller) {
+ var player = this.players.get(id);
+ if(player) {
+ player.updateController(controller);
+ }
+ }
+ playerMouseUpdate(id, mouseAngle) {
+ var player = this.players.get(id);
+ if(player) {
+ if(typeof mouseAngle == "number" && !isNaN(mouseAngle)) {
+ player.updateMouse(mouseAngle);
+ }
+ }
+ }
+ playerDown(id, down) {
+ if(!typeof down == "boolean") return;
+ var player = this.players.get(id);
+ if(player) {
+ player.down = down;
+ }
+ }
+ tick() {
+ var tickDiff = Date.now() - this.lastTick;
+ this.lastTick = Date.now();
+ var ioinstance = io.getio();
+ this.players.forEach((player) => {
+
+ player.tick(tickDiff);
+ ioinstance.to(this.id).emit("playerUpdate", player.getSendObject(), {hit: player.hit});
+ if(player.hit) player.hit = false;
+
+ //make sure player is on island
+ var inisland = this.islands.some((island) => {
+ if(island.isIn(player.pos)) {
+ return true;
+ } else return false;
+ });
+ if(!inisland) {
+ var isinbridge = this.bridges.some((bridge) => {
+ if(bridge.isIn(player)) {
+ return true;
+ } else return false;
+ });
+ if(!isinbridge) {
+ player.socket.emit("youDied", {reason: "drown"});
+ ioinstance.to(this.id).emit("playerLeft", player.id);
+ this.players.delete(player.id);
+ }
+ }
+ });
+ this.bullets.forEach((bullet) => {
+ bullet.tick(tickDiff);
+ });
+ this.bullets = this.bullets.filter((bullet) => {
+ if(Date.now() - bullet.createdAt > 1000) {
+ ioinstance.to(this.id).emit("removeBullet", bullet.id);
+ return false;
+ } else return true;
+ });
+ this.bullets = this.bullets.filter((bullet) => {
+ for (var player of Array.from(this.players.values())) {
+ if(bullet.collidingPlayer(player)) {
+ player.pos.x += bullet.speed * Math.cos(bullet.angle) * tickDiff * 5;
+ player.pos.y += bullet.speed * Math.sin(bullet.angle) * tickDiff * 5;
+ player.hit = true;
+ ioinstance.to(this.id).emit("removeBullet", bullet.id);
+ return false;
+ }
+ };
+ for (var bullet1 of Array.from(this.bullets)) {
+ if(bullet1.id == bullet.id || bullet.owner == bullet1.owner) continue;
+ if(bullet.collidingBullet(bullet1)) {
+ ioinstance.to(this.id).emit("removeBullet", bullet.id);
+ return false;
+ }
+ }
+ return true;
+ });
+ this.islands.forEach((island) => {
+ island.tick(tickDiff, this);
+ });
+
+ }
+
+}
+
+module.exports = Room;
\ No newline at end of file
diff --git a/helpers/idgen.js b/helpers/idgen.js
new file mode 100644
index 0000000..52ae130
--- /dev/null
+++ b/helpers/idgen.js
@@ -0,0 +1,9 @@
+function gen() {
+ let id = 0;
+ return () => {
+ id += 1;
+ return id;
+ };
+}
+const curGen = gen();
+module.exports = curGen;
diff --git a/helpers/io.js b/helpers/io.js
new file mode 100644
index 0000000..c36c9da
--- /dev/null
+++ b/helpers/io.js
@@ -0,0 +1,19 @@
+const { Server } = require("socket.io");
+let io;
+
+module.exports = {
+ init: function(server) {
+ // start socket.io server and cache io value
+ io = new Server(server, {
+ cors: { origin: "*" },
+ });
+ return io;
+ },
+ getio: function() {
+ // return previously cached value
+ if (!io) {
+ throw new Error("must call .init(server) before you can call .getio()");
+ }
+ return io;
+ }
+};
\ No newline at end of file
diff --git a/helpers/roomlist.js b/helpers/roomlist.js
new file mode 100644
index 0000000..a9408e3
--- /dev/null
+++ b/helpers/roomlist.js
@@ -0,0 +1,40 @@
+class RoomList {
+ constructor() {
+ this.rooms = {};
+ }
+
+ getAllRooms() {
+ return Object.values(this.rooms);
+ }
+
+ getRoom(id) {
+ return this.rooms[id];
+ }
+
+ setRoom(room) {
+ this.rooms[room.id] = room;
+ }
+
+ removeRoom(id) {
+ delete this.rooms[id];
+ }
+
+ getRoomByPlayerId(id) {
+ for (var room in this.rooms) {
+ if (this.rooms[room].players.has(id)) {
+ return this.rooms[room];
+ }
+ }
+ return null;
+ }
+
+ tickAll() {
+ for (var room in this.rooms) {
+ this.rooms[room].tick();
+ }
+ }
+}
+
+const list = new RoomList();
+
+module.exports = list;
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..707c312
--- /dev/null
+++ b/index.js
@@ -0,0 +1,74 @@
+const express = require("express");
+var http = require("http");
+require("dotenv").config();
+
+const app = express();
+var process = require("process");
+
+app.use(express.json());
+
+var server = http.createServer(app);
+const io = require("./helpers/io").init(server);
+
+const Player = require("./classes/Player");
+const Room = require("./classes/Room");
+
+const roomlist = require("./helpers/roomlist");
+
+app.use("/", express.static(__dirname + "/dist"));
+app.use("/", express.static(__dirname+"/public"));
+app.use("/assets", express.static(__dirname+"/assets"));
+
+roomlist.setRoom(new Room())
+
+io.on("connection", async (socket) => {
+ socket.on("go", (name) => {
+ if(!name || typeof name != "string") return;
+ name = name.trim();
+ if(name.length == 0) return socket.disconnect();
+ name = name.substring(0,16);
+ var player = new Player(name, socket.id, socket);
+ roomlist.getAllRooms()[0].addPlayer(player);
+ });
+ socket.on("controller", (controller) => {
+ var room = roomlist.getRoomByPlayerId(socket.id);
+ if(!room) return;
+ room.playerControllerUpdate(socket.id, controller);
+ });
+ socket.on("mouse", (mouseAngle) => {
+ var room = roomlist.getRoomByPlayerId(socket.id);
+ if(!room) return;
+ room.playerMouseUpdate(socket.id, mouseAngle);
+ });
+ socket.on("down", (down) => {
+ var room = roomlist.getRoomByPlayerId(socket.id);
+ if(!room) return;
+ room.playerDown(socket.id, down);
+ })
+ socket.on("ping", (fn) => {
+ fn(); // Simply execute the callback on the client
+ })
+ socket.on("disconnect", async () => {
+ var room = roomlist.getRoomByPlayerId(socket.id);
+ if(room) {
+ room.removePlayer(socket.id);
+ }
+ });
+});
+
+//tick rooms
+var tps = 0;
+var secondStart = Date.now();
+setInterval(() => {
+ roomlist.tickAll();
+ tps++;
+ if(Date.now() - secondStart > 1000) {
+ console.log("tps: " + tps);
+ tps = 0;
+ secondStart = Date.now();
+ }
+}, 1000/20);
+
+server.listen(process.env.PORT || 3000, () => {
+ console.log("server started");
+});
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 0000000..2670155
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,5 @@
+{
+ "include": [
+ "src/**/*.js"
+ ]
+}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..14d716f
--- /dev/null
+++ b/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "waterwar.io",
+ "version": "1.0.0",
+ "description": "splash splash splash",
+ "main": "index.js",
+ "scripts": {
+ "build": "webpack --config webpack.config.js",
+ "prod": "webpack --config webpackprod.config.js",
+ "watch": "webpack --config webpack.config.js --watch --progress",
+ "test": "node index.js --unhandled-rejections=strict",
+ "start": "node index.js --unhandled-rejections=strict"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "dotenv": "^10.0.0",
+ "express": "^4.17.1",
+ "intersects": "^2.7.2",
+ "phaser": "^3.55.2",
+ "point-in-polygon": "^1.1.0",
+ "socket.io": "^4.2.0",
+ "socket.io-client": "^4.5.1",
+ "ts-loader": "^9.3.0",
+ "typescript": "^4.6.4",
+ "uuid": "^8.3.2"
+ },
+ "devDependencies": {
+ "copy-webpack-plugin": "^10.0.0",
+ "eslint": "^8.3.0",
+ "webpack": "^5.61.0",
+ "webpack-cli": "^4.9.1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/codergautam/swordbattle.io.git"
+ }
+}
diff --git a/src/GameScene.ts b/src/GameScene.ts
new file mode 100644
index 0000000..8b19794
--- /dev/null
+++ b/src/GameScene.ts
@@ -0,0 +1,279 @@
+import Phaser from "phaser";
+import { io, Socket } from "socket.io-client";
+import Bullet from "./components/Bullet";
+import Island from "./components/Island";
+import GameMap from "./components/Map";
+import Player from "./components/Player";
+import TeamPicker from "./components/TeamPicker";
+import { Data, PlayerData, FirstPlayerData, BulletData, IslandData, BridgeData } from "./helpers/Packets";
+
+interface Keys {
+ up: Phaser.Input.Keyboard.Key;
+ down: Phaser.Input.Keyboard.Key;
+ left: Phaser.Input.Keyboard.Key;
+ right: Phaser.Input.Keyboard.Key;
+
+ w: Phaser.Input.Keyboard.Key;
+ s: Phaser.Input.Keyboard.Key;
+ a: Phaser.Input.Keyboard.Key;
+ d: Phaser.Input.Keyboard.Key;
+}
+
+
+class GameScene extends Phaser.Scene {
+ mobile: boolean;
+ canvas: { width: number, height: number };
+ name: string;
+ socket: Socket;
+ loadingText: Phaser.GameObjects.Text;
+ ready: boolean;
+ players: Map;
+ bullets: Map
+ mouseAngle: number;
+ map: GameMap;
+ fpsCounter: Phaser.GameObjects.Text;
+ controller: {left: boolean, right: boolean, up: boolean, down: boolean};
+ islands: Island[];
+ uiCam: Phaser.Cameras.Scene2D.Camera;
+ teamPicker: TeamPicker;
+
+ constructor() {
+ super("game");
+
+ }
+ preload() {
+ this.loadingText = this.add.text(
+ this.canvas.width / 2,
+ this.canvas.height / 2,
+ "Connecting...",
+ ).setOrigin(0.5);
+ this.ready = false;
+ this.players = new Map();
+ this.bullets = new Map();
+ this.islands = [];
+ }
+
+ create() {
+ this.socket = io();
+ this.socket.emit("go", this.name);
+
+ this.map = new GameMap(this);
+
+ this.uiCam = this.cameras.add(0, 0, this.canvas.width, this.canvas.height);
+
+ this.teamPicker = new TeamPicker(this);
+
+ const playerJoined = (data: FirstPlayerData) =>{
+ this.players.set(data.id, new Player(this, data.pos.x, data.pos.y, data.id, data.name, data.team).setDepth(2));
+ if(this.socket.id === data.id) this.cameras.main.startFollow(this.players.get(data.id));
+ }
+
+ this.socket.on("playerJoined", (data: FirstPlayerData) => {
+ console.log("playerJoined", data);
+ playerJoined(data);
+ });
+ this.socket.on("playerLeft", (id: string) => {
+ if(this.players.has(id)){
+ this.players.get(id).destroy();
+ this.players.delete(id);
+ }
+ });
+ this.socket.on("players", (data: FirstPlayerData[]) => {
+ this.ready = true;
+ this.loadingText.destroy();
+
+ for (const player of data) {
+ if(!this.players.has(player.id)) playerJoined(player);
+ else this.players.get(player.id).tick(player, false);
+ }
+ });
+ this.socket.on("addBullet", (data: BulletData) => {
+ if(!this.bullets.has(data.id)) this.bullets.set(data.id, new Bullet(this, data).setDepth(1));
+ });
+ this.socket.on("removeBullet", (id: string) => {
+ if(this.bullets.has(id)) {
+ setTimeout(() => {
+ this.bullets.get(id).end();
+ this.bullets.delete(id);
+ }, (1000/20)*2);
+ }
+ });
+ this.socket.on("bullets", (data: BulletData[]) => {
+ for (const bullet of data) {
+ if(!this.bullets.has(bullet.id)) this.bullets.set(bullet.id, new Bullet(this, bullet).setDepth(1));
+ }
+
+ for (const bullet of this.bullets.values()) {
+ if(!data.find(b => b.id === bullet.id)) {
+ setTimeout(() => {
+ bullet.end();
+ this.bullets.delete(bullet.id);
+ }, (1000/20)*2);
+ }
+ }
+ });
+ this.socket.on("islands", (data: IslandData[]) => {
+ console.log("islands", data);
+ this.islands = data.map(d => new Island(this, d).setDepth(1));
+ });
+
+ this.socket.on("bridges", (data: BridgeData[]) => {
+ data.forEach(d => {
+ var r = this.add.rectangle(d.pos.x, d.pos.y, d.width*2, d.length, 0xffffff).setDepth(0).setRotation(d.angle).setOrigin(0,0);
+ this.uiCam.ignore(r);
+ });
+ });
+ this.socket.on("islandCaptured", (id: number, team: string) => {
+ console.log("islandCaptured", id, team);
+ this.islands.find(i => i.id === id).setTeam(team);
+
+ });
+ this.socket.on("islandCapturing", (id: number, team: string, percent: number) => {
+ console.log("islandCapturing", id, team, percent);
+ this.islands.find(i => i.id === id).setPercent(percent, team);
+
+ })
+ // this.socket.on("corners", (data: {x: number, y: number}[]) => {
+ // data.forEach(d => {
+ // var el = this.add.ellipse(d.x, d.y, 5, 5, 0x00ff00).setDepth(10);
+ // setTimeout(() => {
+ // el.destroy();
+ // }, 1000/20);
+ // });
+ // });
+ this.socket.on("playerUpdate", (data: PlayerData, object: {hit: boolean}) => {
+ if(!this.ready) return;
+ if(!this.players.has(data.id)) return;
+ this.players.get(data.id).tick(data, object.hit);
+ });
+ // this.socket.on("test", (pos, corners) => {
+ // console.log(pos);
+ // this.add.circle(pos.x, pos.y, 5, 0x00FF0F).setOrigin(0.5).setDepth(0);
+ // this.add.circle(corners[0].x, corners[0].y, 10, 0x00FF00).setOrigin(0.5);
+ // this.add.circle(corners[1].x, corners[1].y, 10, 0x00FF00).setOrigin(0.5);
+ // this.add.circle(corners[2].x, corners[2].y, 10, 0x00FF00).setOrigin(0.5);
+ // this.add.circle(corners[3].x, corners[3].y, 10, 0x00FF00).setOrigin(0.5);
+ // })
+
+ this.socket.on("youDied", () => {
+ this.players.get(this.socket.id).destroy();
+ this.players.delete(this.socket.id);
+
+ })
+ var keys = (this.input.keyboard.addKeys({
+ up: 'up',
+ down: 'down',
+ left: 'left',
+ right: 'right',
+ w: 'W',
+ s: 'S',
+ a: 'A',
+ d: 'D',
+ }) as Keys);
+
+ this.controller = {
+ up: false,
+ down: false,
+ left: false,
+ right: false,
+ }
+
+ keys.up.on('down', () => {
+ this.controller.up = true;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.down.on('down', () => {
+ this.controller.down = true;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.left.on('down', () => {
+ this.controller.left = true;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.right.on('down', () => {
+ this.controller.right = true;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.w.on('down', () => {
+ this.controller.up = true;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.s.on('down', () => {
+ this.controller.down = true;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.a.on('down', () => {
+ this.controller.left = true;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.d.on('down', () => {
+ this.controller.right = true;
+ this.socket.emit("controller", this.controller);
+ });
+
+ keys.up.on('up', () => {
+ this.controller.up = false;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.down.on('up', () => {
+ this.controller.down = false;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.left.on('up', () => {
+ this.controller.left = false;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.right.on('up', () => {
+ this.controller.right = false;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.w.on('up', () => {
+ this.controller.up = false;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.s.on('up', () => {
+ this.controller.down = false;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.a.on('up', () => {
+ this.controller.left = false;
+ this.socket.emit("controller", this.controller);
+ });
+ keys.d.on('up', () => {
+ this.controller.right = false;
+ this.socket.emit("controller", this.controller);
+ });
+
+ this.mouseAngle = Math.atan2(this.game.input.mousePointer.y - (this.canvas.height /2), this.game.input.mousePointer.x - (this.canvas.width / 2));
+ //on mouse move
+ this.input.on("pointermove", () => {
+ this.mouseAngle = Math.atan2(this.game.input.mousePointer.y - (this.canvas.height /2), this.game.input.mousePointer.x - (this.canvas.width / 2))
+ this.socket.emit("mouse", this.mouseAngle);
+ });
+
+ this.input.on("pointerdown", () => {
+ this.socket.emit("down", true);
+ })
+ this.input.on("pointerup", () => {
+ this.socket.emit("down", false);
+ });
+
+ setInterval(() => {
+ var start = Date.now();
+ this.socket.emit( 'ping', function clientCallback() {
+ console.log( 'Websocket RTT: ' + (Date.now() - start) + ' ms' );
+ });
+ }, 2000);
+
+ }
+ update(time: number, delta: number): void {
+ Array.from(this.players.values()).forEach(player => player.updateObject());
+ Array.from(this.bullets.values()).forEach(bullet => bullet.updateObject());
+
+ if(!this.fpsCounter) this.fpsCounter = this.add.text(10, 10, "FPS: 0", {color: "white"}).setDepth(0);
+ this.cameras.main.ignore(this.fpsCounter);
+ this.fpsCounter.setText("FPS: " + this.game.loop.actualFps.toFixed(2));
+ }
+}
+
+export default GameScene;
\ No newline at end of file
diff --git a/src/OpenScene.ts b/src/OpenScene.ts
new file mode 100644
index 0000000..cc7d307
--- /dev/null
+++ b/src/OpenScene.ts
@@ -0,0 +1,44 @@
+import Phaser from "phaser";
+
+class OpenScene extends Phaser.Scene {
+ callback: any;
+ e: boolean;
+ background: Phaser.GameObjects.Rectangle;
+ loadText: Phaser.GameObjects.Text;
+ canvas: any;
+ mobile: boolean;
+ constructor() {
+ super("open");
+ }
+ preload() {
+ console.time("load");
+ this.e = true;
+ this.background = this.add.rectangle(0, 0, document.documentElement.clientWidth, document.documentElement.clientHeight, 0x2e74e6).setOrigin(0).setScrollFactor(0, 0).setScale(2);
+ this.loadText = this.add.text(0,0,"Loading").setOrigin(0.5,0.5);
+
+ this.loadText.setFontSize(this.canvas.width/20);
+ this.loadText.x = this.canvas.width/2;
+ this.loadText.y = this.canvas.height/2;
+
+ //load images
+
+ this.load.image("title", "/assets/images/bg.jpeg");
+
+ this.load.html("namebox", "/assets/html/name.html");
+
+ this.scale.fullscreenTarget = document.getElementById("game");
+ console.timeEnd("load");
+
+ }
+
+ create() {
+
+ this.scene.stop();
+ this.scene.start("title");
+ }
+ update() {
+
+ }
+}
+
+export default OpenScene;
\ No newline at end of file
diff --git a/src/TitleScene.ts b/src/TitleScene.ts
new file mode 100644
index 0000000..9f2c34b
--- /dev/null
+++ b/src/TitleScene.ts
@@ -0,0 +1,69 @@
+import Phaser from "phaser";
+
+class TitleScene extends Phaser.Scene {
+ callback: Function;
+ nameBox: Phaser.GameObjects.DOMElement;
+ canvas: { width: number, height: number };
+ background: Phaser.GameObjects.Image;
+ text: Phaser.GameObjects.Text;
+ mobile: boolean;
+ constructor(callback: Function) {
+ super("title");
+ this.callback = callback;
+ }
+ preload() {
+ }
+ create() {
+ this.nameBox = this.add.dom(0,0).createFromCache("namebox");
+ this.background = this.add.image(0, 0, "title").setOrigin(0).setScrollFactor(0, 0).setScale(2);
+
+ this.text = this.add.text(this.canvas.width / 2, 0, "Waterwar.io", {
+ fontSize: "64px",
+ color: "#000000",
+ }).setOrigin(0.5);
+
+ this.nameBox.getChildByName("btn").addEventListener("click", () => {
+ var box = this.nameBox.getChildByName("name") as HTMLInputElement | null;
+ var name = box.value.trim();
+ if(!name || name.length == 0) return;
+
+ this.callback(name);
+ });
+
+
+ //this.stats.y -= this.stats.height
+
+ const resize = () => {
+
+ this.game.scale.resize(this.canvas.width, this.canvas.height);
+
+ try {
+ const cameraWidth = this.cameras.main.width;
+ const cameraHeight = this.cameras.main.height;
+ this.background.setScale(Math.max(cameraWidth / this.background.width, cameraHeight / this.background.height));
+
+ this.background.x = 0 - ((this.background.displayWidth - cameraWidth) / 2);
+ } catch (e) {
+
+ }
+ this.text.x = this.canvas.width / 2;
+ this.text.y = this.canvas.height / 4;
+
+ this.nameBox.x = this.canvas.width / 2;
+ this.nameBox.y = this.canvas.height / 2.3;
+ };
+ var doit: string | number | NodeJS.Timeout;
+
+ window.addEventListener("resize", function() {
+ clearTimeout(doit);
+ doit = setTimeout(resize, 100);
+ });
+
+ resize();
+ }
+ update(time: number, delta: number): void {
+ this.text.setFontSize(Math.min(this.canvas.width/12, this.canvas.height/5));
+ }
+}
+
+export default TitleScene;
diff --git a/src/components/Bullet.ts b/src/components/Bullet.ts
new file mode 100644
index 0000000..fc281eb
--- /dev/null
+++ b/src/components/Bullet.ts
@@ -0,0 +1,49 @@
+import Phaser from "phaser";
+import GameScene from "../GameScene";
+import { BulletData, Data } from "../helpers/Packets";
+
+export default class Bullet extends Phaser.GameObjects.Container {
+ id: string;
+ bullet: Phaser.GameObjects.Rectangle;
+ lastRecievedData: number;
+ goTo: { x: number; y: number; };
+ speed: number;
+ mAngle: number;
+ lastUpdate: number;
+ constructor(scene: Phaser.Scene, data: BulletData) {
+ super(scene);
+ this.id = data.id;
+ this.x = data.pos.x;
+ this.y = data.pos.y;
+
+ this.speed = data.speed;
+ this.mAngle = data.angle;
+
+ this.goTo = {
+ x: data.pos.x,
+ y: data.pos.y
+ }
+
+
+ this.bullet = new Phaser.GameObjects.Ellipse(scene, 0, 0, 20, 20, 0x00FFFF).setOrigin(0.5);
+ this.bullet.setRotation(data.angle+(Math.PI/2));
+ this.lastRecievedData = Date.now();
+ this.add(this.bullet);
+ (this.scene as GameScene).uiCam.ignore(this);
+ this.scene.add.existing(this);
+
+ this.lastUpdate = Date.now();
+
+ // this.visible = false;
+ }
+ end() {
+ this.destroy();
+ }
+ updateObject() {
+
+ this.x += Math.cos(this.mAngle) * this.speed * 50 * ((Date.now() - this.lastUpdate) / 50);
+ this.y += Math.sin(this.mAngle) * this.speed * 50 * ((Date.now() - this.lastUpdate) / 50);
+ this.lastUpdate = Date.now();
+
+ }
+}
\ No newline at end of file
diff --git a/src/components/Island.ts b/src/components/Island.ts
new file mode 100644
index 0000000..0c17ab1
--- /dev/null
+++ b/src/components/Island.ts
@@ -0,0 +1,44 @@
+import GameScene from "../GameScene";
+import { IslandData } from "../helpers/Packets";
+export default class Island extends Phaser.GameObjects.Container {
+ island: Phaser.GameObjects.Ellipse;
+ shape: string;
+ capturedBy: string;
+ id: number;
+ capturingCircle: Phaser.GameObjects.Ellipse;
+ constructor(scene: Phaser.Scene, data: IslandData) {
+ super(scene);
+ this.x = data.pos.x;
+ this.y = data.pos.y;
+ this.shape = data.shape;
+ this.capturedBy = data.capturedBy;
+ this.capturingCircle = new Phaser.GameObjects.Ellipse(scene, 0, 0, data.size, data.size, 0x00FFFF).setOrigin(0.5).setVisible(false).setDepth(1);
+ this.id = data.id;
+
+ if(this.capturedBy == "red") console.log(this.id + " is captured by red");
+ this.island = new Phaser.GameObjects.Ellipse(scene, 0, 0, data.size, data.size, data.capturedBy == "none" ? 0x838579: data.capturedBy == "red" ? 0xFF0000 : 0x0000FF).setOrigin(0.5).setDepth(1);
+
+ if(data.capturedPercentage < 100) {
+ this.setPercent(data.capturedPercentage, data.capturingBy);
+ }
+
+ this.add(this.island);
+ this.add(this.capturingCircle);
+ this.scene.add.existing(this);
+ (this.scene as GameScene).uiCam.ignore(this);
+ }
+
+ setTeam(team: string) {
+ this.capturingCircle.setVisible(false);
+ this.island.setFillStyle(team == "red" ? 0xFF0000 : team == "none" ? 0x838579 : 0x0000FF);
+ this.capturedBy = team;
+ }
+ setPercent(percent: number, team: string) {
+ console.log(team)
+ this.capturingCircle.setFillStyle(team == "red" ? 0xFF0000 : team == "none" ? 0x838579 : 0x0000FF);
+ this.capturingCircle.setVisible(true);
+ this.capturingCircle.setScale(percent/100);
+ }
+
+}
+
diff --git a/src/components/Map.ts b/src/components/Map.ts
new file mode 100644
index 0000000..07cd811
--- /dev/null
+++ b/src/components/Map.ts
@@ -0,0 +1,16 @@
+import Phaser from "phaser";
+
+export default class GameMap extends Phaser.GameObjects.Container {
+ islands: Phaser.GameObjects.Rectangle[];
+ constructor(scene: Phaser.Scene) {
+ super(scene);
+
+ this.scene.add.existing(this);
+ }
+ end() {
+
+ }
+ updateObject() {
+
+ }
+}
\ No newline at end of file
diff --git a/src/components/Player.ts b/src/components/Player.ts
new file mode 100644
index 0000000..0ad6504
--- /dev/null
+++ b/src/components/Player.ts
@@ -0,0 +1,94 @@
+import Phaser from "phaser";
+import GameScene from "../GameScene";
+import { PlayerData } from "../helpers/Packets";
+export default class Player extends Phaser.GameObjects.Container {
+ square: Phaser.GameObjects.Rectangle;
+ gun: Phaser.GameObjects.Rectangle;
+ bodySize: number;
+ lastTick: number;
+ toAngle: number;
+ lastUpdate: number;
+ id: string;
+ name: string;
+ speed: number;
+ team: string;
+ nameTag: Phaser.GameObjects.Text;
+ constructor(scene: Phaser.Scene, x: number, y: number, id: string, name: string, team: string, speed: number = 1, size: number = 50) {
+ super(scene);
+ this.x = x;
+ this.y = y;
+ this.bodySize = size;
+ this.lastTick = Date.now();
+ this.lastUpdate = Date.now();
+
+ this.id = id;
+ this.name = name;
+ this.speed = speed;
+
+ this.team = team;
+ // alert(size)/
+ this.square = new Phaser.GameObjects.Rectangle(scene, 0, 0, this.bodySize, this.bodySize, team == "red" ? 0x013220: 0x0000FF).setOrigin(0.5);
+ this.gun = new Phaser.GameObjects.Rectangle(scene, 0, 0, this.bodySize/2, this.bodySize/2, team == "red" ? 0xff0000 : 0x00FFFF).setOrigin(0.5);
+ this.nameTag = new Phaser.GameObjects.Text(scene, 0, -1 * this.square.displayHeight, name, {
+ fontSize: "20px",
+ color: team == "red" ? "#ff0000" : "#00FFFF",
+ align: "center"
+ }).setDepth(5).setOrigin(0.5);
+
+ this.toAngle =this.square.rotation;
+
+ this.add(this.square);
+ this.add(this.gun);
+ if(this.id != (this.scene as GameScene).socket.id) this.add(this.nameTag);
+ this.scene.add.existing(this);
+ (this.scene as GameScene).uiCam.ignore(this);
+ }
+ tick(data: PlayerData, hit: boolean) {
+ this.toAngle = data.lookAngle;
+ this.scene.tweens.add({
+ targets: this,
+ x: data.pos.x,
+ y: data.pos.y,
+ duration: hit ? 500 : 100,
+ ease: "Power2",
+ repeat: 0,
+ yoyo: false,
+ });
+
+
+ this.lastTick = Date.now();
+
+ }
+ updateObject() {
+ var tickDiff = Date.now() - this.lastUpdate;
+ function rLerp (A: number, B: number, w: number){
+ let CS = (1-w)*Math.cos(A) + w*Math.cos(B);
+ let SN = (1-w)*Math.sin(A) + w*Math.sin(B);
+ return Math.atan2(SN,CS);
+ }
+
+ if((this.scene as GameScene).socket.id != this.id) this.square.setRotation(rLerp(this.square.rotation, this.toAngle, 0.5));
+ else this.square.setRotation((this.scene as GameScene).mouseAngle);
+
+ this.gun.setRotation(this.square.rotation);
+ this.gun.x = Math.cos(this.square.rotation) * this.bodySize/2;
+ this.gun.y = Math.sin(this.square.rotation) * this.bodySize/2;
+
+ if(this.id == (this.scene as GameScene).socket.id) {
+ var controller = (this.scene as GameScene).controller;
+ if(controller.left) {
+ this.x -= tickDiff * 0.2 * this.speed;
+ }
+ if(controller.right) {
+ this.x += tickDiff * 0.2 * this.speed;
+ }
+ if(controller.up) {
+ this.y -= tickDiff * 0.2 * this.speed;
+ }
+ if(controller.down) {
+ this.y += tickDiff * 0.2* this.speed;
+ }
+ this.lastUpdate = Date.now();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/TeamPicker.ts b/src/components/TeamPicker.ts
new file mode 100644
index 0000000..f348331
--- /dev/null
+++ b/src/components/TeamPicker.ts
@@ -0,0 +1,18 @@
+import Phaser, { Game, Scene } from "phaser";
+import GameScene from "../GameScene";
+export default class ClassPicker extends Phaser.GameObjects.Container {
+ rect1: Phaser.GameObjects.Rectangle;
+ constructor(scene: GameScene) {
+ super(scene as Scene);
+
+
+this.rect1 = new Phaser.GameObjects.Rectangle(scene, scene.canvas.width / 2, (scene.canvas.height / 4), 100, 100, 0x00FFFF);
+this.add(this.rect1);
+this.scene.add.existing(this);
+this.scene.cameras.main.ignore(this);
+ }
+ update() {
+ // console.log("ClassPicker update");
+
+ }
+}
\ No newline at end of file
diff --git a/src/helpers/Packets.ts b/src/helpers/Packets.ts
new file mode 100644
index 0000000..1be390d
--- /dev/null
+++ b/src/helpers/Packets.ts
@@ -0,0 +1,35 @@
+interface Data {
+ id: string;
+ pos: {x: number, y: number};
+}
+interface PlayerData extends Data {
+ lookAngle: number;
+}
+interface FirstPlayerData extends PlayerData {
+ name: string;
+ team: string;
+}
+interface BulletData extends Data {
+ angle: number;
+ speed: number;
+ owner: string;
+}
+interface IslandData {
+ pos: {x: number, y: number};
+ id: number;
+ shape: string;
+ capturedBy: string;
+ size: number;
+ capturedPercentage: number;
+ capturingBy: string;
+}
+interface BridgeData {
+ width: number,
+ length: number,
+ angle: number,
+ pos: {x: number, y: number},
+ corners: {x: number, y: number}[],
+};
+
+
+export { Data, BridgeData, IslandData, BulletData, PlayerData, FirstPlayerData };
\ No newline at end of file
diff --git a/src/helpers/msToSeconds.ts b/src/helpers/msToSeconds.ts
new file mode 100644
index 0000000..edbf4a4
--- /dev/null
+++ b/src/helpers/msToSeconds.ts
@@ -0,0 +1,3 @@
+export default function msToSeconds(ms: number) {
+ return Math.ceil(ms / 1000).toString();
+}
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..71268b8
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+ Waterwar.io
+
+
+
+
+
+
+
+
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..cc41352
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,79 @@
+import TitleScene from "./TitleScene";
+import OpenScene from "./OpenScene";
+import GameScene from "./GameScene";
+import Phaser from "phaser";
+
+window.addEventListener("load", () => {
+var config = {
+ width: document.documentElement.clientWidth,
+ height: document.documentElement.clientHeight,
+ parent: "game",
+ dom: {
+ createContainer: true,
+ autoCenter: Phaser.Scale.CENTER_BOTH,
+ },
+ scale: {
+ mode:Phaser.Scale.RESIZE,
+ }
+
+};
+var mobile = window.matchMedia("(pointer: coarse)").matches;
+var game = new Phaser.Game(config);
+
+var openScene = new OpenScene();
+var gameScene = new GameScene();
+
+
+//alert(lastAd)
+var scale = "scale(1)"; // IE 9
+ document.body.style.transform = scale; // General
+
+
+var titleScene = new TitleScene((name: string) => {
+ gameScene.name = name;
+ game.scene.switch("title", "game");
+ return name;
+});
+
+titleScene.mobile = mobile;
+openScene.mobile = mobile;
+gameScene.mobile = mobile;
+//titleScene.showPromo = false;
+
+function canvas() {
+ return {
+ width: document.documentElement.clientWidth,
+ height: document.documentElement.clientHeight
+ };
+}
+
+Object.defineProperty(titleScene, "canvas", {
+ get: canvas
+});
+Object.defineProperty(openScene, "canvas", {
+ get: canvas
+});
+Object.defineProperty(gameScene, "canvas", {
+ get: canvas
+});
+
+
+
+game.scene.add("title", titleScene);
+game.scene.add("open", openScene);
+game.scene.add("game", gameScene);
+
+game.scene.start("open");
+
+document.addEventListener("contextmenu",function(e) {
+ e.preventDefault();
+});
+
+
+//for debugging on the school chromebooks they fricking banned dev console
+/*window.onerror = function(msg, url, line) {
+ document.write("Error : " + msg + "
");
+ document.write("Line number : " + line + "
");
+ document.write("File : " + url);
+};*/
+});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..76b973e
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "compilerOptions": {
+ "sourceMap": true,
+ "esModuleInterop": true,
+ "target": "es5",
+ "downlevelIteration": true
+ },
+}
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..bc24500
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,42 @@
+
+//const webpack = require("webpack");
+const path = require("path");
+const CopyPlugin = require("copy-webpack-plugin");
+function uuidv4() {
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
+ var r = Math.random() * 16 | 0, v = c == "x" ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+}
+const config = {
+ entry: "./src/index.ts",
+ plugins: [
+ new CopyPlugin({
+ patterns: [
+ { from: "src/index.html", to: "", transform(content) {
+ return content
+ .toString()
+ .replace("RANDOM_UUID", uuidv4());
+ }},
+ ],
+ }),
+ ],
+ resolve: {
+ // Add `.ts` and `.tsx` as a resolvable extension.
+ extensions: [".ts", ".tsx", ".js"]
+ },
+ module: {
+ rules: [
+ // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
+ { test: /\.tsx?$/, loader: "ts-loader" }
+ ]
+ },
+ output: {
+ path: path.resolve(__dirname, "dist"),
+ filename: "main.js",
+ },
+ devtool: "source-map",
+ mode: "development"
+};
+
+module.exports = config;
diff --git a/webpackprod.config.js b/webpackprod.config.js
new file mode 100644
index 0000000..1c0d64e
--- /dev/null
+++ b/webpackprod.config.js
@@ -0,0 +1,44 @@
+
+//const webpack = require("webpack");
+const path = require("path");
+const CopyPlugin = require("copy-webpack-plugin");
+const {CAPTCHASITE} = require("./config.json");
+function uuidv4() {
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
+ var r = Math.random() * 16 | 0, v = c == "x" ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+}
+const config = {
+ entry: "./src/index.ts",
+ plugins: [
+ new CopyPlugin({
+ patterns: [
+ { from: "src/index.html", to: "", transform(content) {
+ return content
+ .toString()
+ .replace("INSERT_RECAPTCHA_SITE_KEY", CAPTCHASITE)
+ .replace("RANDOM_UUID", uuidv4());
+ }},
+ ],
+ }),
+ ],
+ resolve: {
+ // Add `.ts` and `.tsx` as a resolvable extension.
+ extensions: [".ts", ".tsx", ".js"]
+ },
+ module: {
+ rules: [
+ // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
+ { test: /\.tsx?$/, loader: "ts-loader" }
+ ]
+ },
+ output: {
+ path: path.resolve(__dirname, "dist"),
+ filename: "main.js",
+ },
+ devtool: "source-map",
+ mode: "production"
+};
+
+module.exports = config;