diff --git a/index.html b/index.html index 6b78459..9cf28ae 100644 --- a/index.html +++ b/index.html @@ -8,9 +8,6 @@ Bastion App -
- -
diff --git a/public/Mission01.tmx b/public/Mission01.tmx deleted file mode 100644 index e0ab478..0000000 --- a/public/Mission01.tmx +++ /dev/null @@ -1,28 +0,0 @@ - - - - - -15,15,15,15,15,15,15,16,15,15,15,15,15,15,15,15,15,15,15,15, -15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, -15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,15,15,15, -4,4,4,4,4,4,4,4,4,4,4,5,15,15,15,15,15,15,15,15, -32,32,32,32,32,32,32,32,32,32,7,19,15,15,15,15,15,15,15,16, -15,15,15,15,15,15,16,15,15,15,17,19,15,15,15,15,15,15,15,15, -15,15,15,15,15,15,15,15,15,15,17,19,15,15,15,15,15,15,15,15, -15,16,15,15,15,15,15,15,15,15,17,19,15,15,16,15,15,15,15,15, -15,15,15,15,15,15,15,15,15,15,17,20,4,4,4,4,4,5,15,15, -15,15,15,15,16,15,15,15,15,15,31,32,32,32,32,32,7,19,15,15, -15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,17,19,15,15, -15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,17,19,15,15, -15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,17,19,15,15, -15,16,15,15,3,4,4,4,4,4,4,4,4,4,4,4,21,19,15,15, -15,15,15,15,17,6,32,32,32,32,32,32,32,32,32,32,32,33,15,15, -15,15,15,15,17,19,16,15,15,15,15,15,15,15,15,15,15,15,15,15, -15,15,15,15,17,19,15,15,15,15,15,15,15,15,15,15,15,15,15,15, -15,15,15,15,17,19,15,15,15,15,15,15,15,16,15,15,15,15,15,15, -15,15,15,15,17,19,15,15,15,15,15,15,15,15,15,15,15,15,15,15, -15,15,15,15,17,19,15,15,15,15,15,15,15,15,15,15,15,15,15,15 - - - diff --git a/public/Mission011 b/public/Mission011 new file mode 100644 index 0000000..5915250 --- /dev/null +++ b/public/Mission011 @@ -0,0 +1,48 @@ +{ "compressionlevel":-1, + "height":17, + "infinite":false, + "layers":[ + { + "data":[15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 15, 15, 15, 15, 15, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 7, 18, 19, 15, 15, 15, 15, 15, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 17, 18, 6, 32, 32, 32, 32, 32, 32, 32, 32, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 3, 4, 4, 4, 4, 4, 4, 4, 4, 21, 18, 19, 15, 15, 15, 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 15, 15, 15, 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 17, 18, 6, 32, 32, 32, 32, 32, 32, 32, 32, 33, 15, 15, 15, 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 17, 18, 19, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 17, 18, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 21, 18, 19, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], + "height":17, + "id":1, + "name":"Tile Layer 1", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":30, + "x":0, + "y":0 + }], + "nextlayerid":2, + "nextobjectid":1, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.11.0", + "tileheight":64, + "tilesets":[ + { + "firstgid":1, + "source":"Tileset.tsx" + }], + "tilewidth":64, + "type":"map", + "version":"1.10", + "width":30 +} \ No newline at end of file diff --git a/public/Mission011.tmx b/public/Mission011.tmx new file mode 100644 index 0000000..df76b7d --- /dev/null +++ b/public/Mission011.tmx @@ -0,0 +1,25 @@ + + + + + +15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +4,4,4,4,4,4,4,4,4,4,4,4,4,5,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +18,18,18,18,18,18,18,18,18,18,18,18,18,19,15,15,15,15,15,3,4,4,4,4,4,4,4,4,4,4, +32,32,32,32,32,32,32,32,32,32,32,7,18,19,15,15,15,15,15,17,18,18,18,18,18,18,18,18,18,18, +15,15,15,15,15,15,15,15,15,15,15,17,18,19,15,15,15,15,15,17,18,6,32,32,32,32,32,32,32,32, +15,15,15,15,15,15,15,15,15,15,15,17,18,19,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15, +15,15,15,15,15,15,15,15,15,15,15,17,18,19,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15, +15,15,3,4,4,4,4,4,4,4,4,21,18,19,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15, +15,15,17,18,18,18,18,18,18,18,18,18,18,19,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15, +15,15,17,18,6,32,32,32,32,32,32,32,32,33,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15, +15,15,17,18,19,15,15,15,15,15,15,15,15,15,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15, +15,15,17,18,20,4,4,4,4,4,4,4,4,4,4,4,4,4,4,21,18,19,15,15,15,15,15,15,15,15, +15,15,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,15,15,15,15,15,15,15,15, +15,15,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,33,15,15,15,15,15,15,15,15, +15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 + + + diff --git a/public/assets/CreepStats.json b/public/assets/CreepStats.json index 2d76ba3..f672c43 100644 --- a/public/assets/CreepStats.json +++ b/public/assets/CreepStats.json @@ -1,7 +1,7 @@ [ { "health": 2, - "speed": 0.0005, + "speed": 0.25, "special": null, "resistance": { "physical": 0, diff --git a/public/assets/maps/mission_01.png b/public/assets/maps/mission_01.png index 903df82..6d14258 100644 Binary files a/public/assets/maps/mission_01.png and b/public/assets/maps/mission_01.png differ diff --git a/public/assets/missions/mission_01.json b/public/assets/missions/mission_01.json index 46bae96..705acf2 100644 --- a/public/assets/missions/mission_01.json +++ b/public/assets/missions/mission_01.json @@ -2,64 +2,99 @@ "name": "Mission 1", "description": "This is the first mission", "mapImage": { - "url": "/assets/maps/mission_01.png", - "width": 1280, - "height": 1280 + "url": "/assets/maps/mission_01.png" }, "gameMap": { - "rows": 20, - "columns": 20, + "rows": 17, + "columns": 30, "cells": [ - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] ], "paths": [ [ - [8, 0], - [8, 1], - [9, 2], - [10, 2], - [11, 2], - [12, 3], - [12, 4], - [12, 5], - [12, 6], - [12, 7], - [12, 8], - [12, 9], - [11, 10], - [10, 10], - [9, 10], - [8, 11], + [4, 0], + [4, 1], + [4, 2], + [4, 3], + [4, 4], + [4, 5], + [4, 6], + [4, 7], + [4, 8], + [4, 9], + [4, 10], + [4, 11], + [4, 12], + [5, 12], + [6, 12], + [7, 12], [8, 12], - [8, 13], - [7, 14], - [6, 14], - [5, 14], - [4, 14], - [3, 14], - [2, 14], - [1, 14], - [0, 15] + [9, 12], + [10, 12], + [10, 11], + [10, 10], + [10, 9], + [10, 8], + [10, 7], + [10, 6], + [10, 5], + [10, 4], + [10, 3], + [11, 3], + [12, 3], + [13, 3], + [14, 3], + [14, 4], + [14, 5], + [14, 6], + [14, 7], + [14, 8], + [14, 9], + [14, 10], + [14, 11], + [14, 12], + [14, 13], + [14, 14], + [14, 15], + [14, 16], + [14, 17], + [14, 18], + [14, 19], + [14, 20], + [13, 20], + [12, 20], + [11, 20], + [10, 20], + [9, 20], + [8, 20], + [7, 20], + [6, 20], + [5, 20], + [5, 21], + [5, 22], + [5, 23], + [5, 24], + [5, 25], + [5, 26], + [5, 27], + [5, 28], + [5, 29] ] ] }, diff --git a/public/maps.tiled-session b/public/maps.tiled-session index 981a2b3..f22f5ad 100644 --- a/public/maps.tiled-session +++ b/public/maps.tiled-session @@ -24,19 +24,29 @@ "y": 3197.8680879413732 } }, + "Mission01.tmx": { + "scale": 0.6739062499999999, + "selectedLayer": 0, + "viewCenter": { + "x": 795.3628564804081, + "y": 639.5548342221192 + } + }, "Tileset.tsx": { "scaleInDock": 1 } }, "last.externalTilesetPath": "/home/koneko/Programing/js/towerdefense/public", + "map.height": 17, "map.lastUsedFormat": "tmx", "map.tileHeight": 64, "map.tileWidth": 64, - "map.width": 20, + "map.width": 30, "openFiles": [ ], "project": "maps.tiled-project", "recentFiles": [ + "Mission01.tmx", "/home/koneko/dumping/tiles/TiledTDThree64.tmx" ], "tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)" diff --git a/public/style.css b/public/style.css index 201df2f..316c7f7 100644 --- a/public/style.css +++ b/public/style.css @@ -1,15 +1,12 @@ -* { - margin: 0; - padding: 0; -} body, html { - width: 100vw; - height: 100vh; - overflow: hidden; - /* background-color: white; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; */ + margin: 0; + padding: 0; + overflow: hidden; /* Prevent scrollbars */ + width: 100%; + height: 100%; + background-color: black; /* Letterbox background color */ +} +canvas { + display: block; } diff --git a/src/classes/Bastion.ts b/src/classes/Bastion.ts index be6d587..5eee98c 100644 --- a/src/classes/Bastion.ts +++ b/src/classes/Bastion.ts @@ -3,6 +3,7 @@ import GameObject from './GameObject'; import GuiObject from './GuiObject'; import Scene from '../scenes/Scene'; import { Grid } from './game/Grid'; +import WaveManager from './game/WaveManager'; export class Globals { public static app: PIXI.Application; @@ -11,6 +12,7 @@ export class Globals { public static WindowWidth: number; public static AspectRatio: number = 16 / 9; public static Grid: Grid; + public static WaveManager: WaveManager; } export default class GameMaster { @@ -26,18 +28,6 @@ export default class GameMaster { this.ticker.add(() => this.update(this.ticker.elapsedMS)); } - public _CreateGameObject(object: GameObject) { - this.GameObjects.push(object); - } - - public _RemoveGameObject(object: GameObject) { - this.GameObjects.splice(this.GameObjects.indexOf(object), 1); - } - - public GetGameObjectByName(name: string) { - return this.GameObjects.filter((obj) => obj.name == name); - } - public _CreateGuiObject(object: GuiObject) { this.currentScene.gui.push(object); Globals.app.stage.addChild(object.container); diff --git a/src/classes/GameObject.ts b/src/classes/GameObject.ts index 3f0d318..6e822e6 100644 --- a/src/classes/GameObject.ts +++ b/src/classes/GameObject.ts @@ -1,18 +1,15 @@ import * as PIXI from 'pixi.js'; -import { Globals } from './Bastion'; export default abstract class GameObject { public readonly name: string = this.constructor.name; protected _container: PIXI.Container = new PIXI.Container(); - // bb = bounding box protected bb: PIXI.Rectangle = new PIXI.Rectangle(); protected _events: PIXI.EventEmitter = new PIXI.EventEmitter(); public destroy() { - Globals.GameMaster._RemoveGameObject(this); this._events.removeAllListeners(); if (this._container.parent) this._container.parent.removeChild(this._container); this._container.destroy(); diff --git a/src/classes/game/Creep.ts b/src/classes/game/Creep.ts new file mode 100644 index 0000000..ed8ae1c --- /dev/null +++ b/src/classes/game/Creep.ts @@ -0,0 +1,109 @@ +import Assets from '../Assets'; +import { Globals } from '../Bastion'; +import { CreepStatsDefinition, CreepType, PathDefinition } from '../Definitions'; +import GameObject from '../GameObject'; +import * as PIXI from 'pixi.js'; + +export enum CreepEvents { + Died = 'died', + TakenDamage = 'takenDamage', + Escaped = 'escaped', + Moved = 'moved', +} + +export default class Creep extends GameObject { + public creepType: CreepType; + private sprite: PIXI.Sprite; + private path: PathDefinition; + private stats: CreepStatsDefinition; + private pathIndex: number = 0; + private speed: number; + public health: number; + public escaped: boolean = false; + public died: boolean = false; + public x: number; // X and Y are local to the grid, not canvas + public y: number; + constructor(creepType: CreepType, path: PathDefinition) { + super(); + this.creepType = creepType; + this.stats = Assets.CreepStats[this.creepType]; + this.sprite = new PIXI.Sprite(Assets.BasicCreepTexture); + // because wave manager spawns all instantly and i dont want + // it to look like a shit game (they all spawn in top left corner) + // i want to hide minion - mario + this.container.x = -70; + this.container.y = -50; + this.sprite.width = 64; + this.sprite.height = 64; + this.speed = this.stats.speed; + this.health = this.stats.health; + this.path = path; + this.x = path[0][1] * 64 + 32; // centered + this.y = path[0][0] * 64 + 32; + Globals.Grid.container.addChild(this.container); + this.container.addChild(this.sprite); + } + public update(elapsedMS: number) { + if (this.pathIndex + 1 == this.path.length) { + if (this.escaped) return; + this.events.emit(CreepEvents.Escaped, this); + this.escaped = true; + return; + } + const currentCell = this.path[this.pathIndex]; + const targetCell = this.path[this.pathIndex + 1]; + + const targetX = targetCell[1] * 64 + 32; + const targetY = targetCell[0] * 64 + 32; + const directionX = targetCell[1] - currentCell[1]; + const directionY = targetCell[0] - currentCell[0]; + let deltaX = this.speed * elapsedMS * directionX; + let deltaY = this.speed * elapsedMS * directionY; + console.log(deltaX + ' DELTA X UPDATE\n' + deltaY + 'DELTA Y UPDATE '); + let increaseIndex = false; + + if (deltaX > 0 && this.x + deltaX > targetX) { + // limit to center of target cell + deltaX = targetX - this.x; + increaseIndex = true; + } + if (deltaX < 0 && this.x + deltaX < targetX) { + // limit to center of target cell + deltaX = targetX - this.x; + increaseIndex = true; + } + if (deltaY > 0 && this.y + deltaY > targetY) { + // limit to center of target cell + deltaY = targetY - this.y; + increaseIndex = true; + } + if (deltaY < 0 && this.y + deltaY < targetY) { + // limit to center of target cell + deltaY = targetY - this.y; + increaseIndex = true; + } + this.x += deltaX; + this.y += deltaY; + console.log(this.x + ' CREEP X UPDATE\n' + this.y + 'CREEP Y UPDATE '); + if (increaseIndex) this.pathIndex++; + this.draw(); + } + + public takeDamage(amount: number) { + this.health -= amount; + if (this.health < 0 && !this.died) { + this.died = true; + this.events.emit(CreepEvents.Died, this); + } + } + + public override destroy() { + super.destroy(); + this.container.removeChildren(); + } + protected draw() { + this.sprite.anchor.set(0.5, 0.5); + this.container.x = this.x; + this.container.y = this.y; + } +} diff --git a/src/classes/game/Grid.ts b/src/classes/game/Grid.ts index b86bd16..547f053 100644 --- a/src/classes/game/Grid.ts +++ b/src/classes/game/Grid.ts @@ -1,10 +1,9 @@ import * as PIXI from 'pixi.js'; import GameObject from '../GameObject'; import { GameMapDefinition, TerrainType } from '../Definitions'; -// import Creep, { CreepEvents } from './Creep'; import GameAssets from '../Assets'; -import { Viewport } from 'pixi-viewport'; import { Globals } from '../Bastion'; +import Creep, { CreepEvents } from './Creep'; export class Cell extends GameObject { public type: TerrainType; @@ -18,15 +17,11 @@ export class Cell extends GameObject { this.row = row; this.column = column; this.isPath = isPath; - // this.bb.x = parseFloat(environment.Grid.gridUnitsToPixels(this.column).toFixed(2)); - // this.bb.y = parseFloat(environment.Grid.gridUnitsToPixels(this.row).toFixed(2)); - if (column == 24 || column == 23) console.log(`col ${column} ` + Globals.Grid.gridUnitsToPixels(this.column)); - this.bb.x = Globals.Grid.gridUnitsToPixels(this.column); - this.bb.y = Globals.Grid.gridUnitsToPixels(this.row); - this.bb.width = Globals.Grid.gridUnitsToPixels(1); - this.bb.height = Globals.Grid.gridUnitsToPixels(1); + this.bb.x = this.column * 64; + this.bb.y = this.row * 64; + this.bb.width = 64; + this.bb.height = 64; Globals.Grid.container.addChild(this.container); - Globals.GameMaster._CreateGameObject(this); let g = new PIXI.Graphics(); g.rect(0, 0, this.bb.width, this.bb.height); switch (this.type) { @@ -64,42 +59,54 @@ export class Cell extends GameObject { export class Grid extends GameObject { private gameMap: GameMapDefinition; private cells: Cell[] = []; - // public creeps: Creep[] = []; + public creeps: Creep[] = []; constructor(map: GameMapDefinition, missionIndex) { super(); this.gameMap = map; Globals.Grid = this; - this.container.isRenderGroup = true; this.bb.x = 0; - this.bb.y = 110; - this.bb.width = Globals.WindowWidth - 360; - this.bb.height = Globals.WindowHeight - 110; + this.bb.y = 0; + this.bb.width = 64 * 30; + this.bb.height = 64 * 17; Globals.app.stage.addChild(this.container); - Globals.GameMaster._CreateGameObject(this); let background = new PIXI.Sprite(GameAssets.MissionBackgrounds[missionIndex]); background.x = 0; background.y = 0; - this.bb.width = this.gridUnitsToPixels(1) * this.gameMap.columns; - this.bb.height = this.gridUnitsToPixels(1) * this.gameMap.rows; background.width = this.bb.width; background.height = this.bb.height; this.container.addChild(background); - } - public update() {} - private getPixelScalingFactor() { - const pixelScaleX = this.bb.width / this.gameMap.columns; - const pixelScaleY = this.bb.height / this.gameMap.rows; - return pixelScaleX < pixelScaleY ? pixelScaleX : pixelScaleY; - } - public gridUnitsToPixels(amount: number): number { - return amount * this.getPixelScalingFactor(); + for (let y = 0; y < this.gameMap.columns; y++) { + for (let x = 0; x < this.gameMap.rows; x++) { + let type = this.gameMap.cells[x][y]; + if (!type) type = 1; + const isPath = this.gameMap.paths.some((path) => path.some((p) => p[0] === x && p[1] === y)); + if (isPath) type = TerrainType.Path; + let cell = new Cell(type, x, y, isPath); + this.cells.push(cell); + } + } } - - public pixelsToGridUnits(pixels: number): number { - return pixels / this.getPixelScalingFactor(); + public addCreep(creep: Creep) { + console.log('ADD CREEP'); + this.creeps.push(creep); + creep.events.on(CreepEvents.Died, (diedCreep) => { + this.onCreepDiedOrEscaped(diedCreep); + }); + creep.events.on(CreepEvents.Escaped, (escapedCreep) => { + this.onCreepDiedOrEscaped(escapedCreep); + }); + } + private onCreepDiedOrEscaped(creep: Creep) { + this.creeps.splice(this.creeps.indexOf(creep), 1); + creep.destroy(); + } + public update(elapsedMS) { + this.creeps.forEach((creep) => { + creep.update(elapsedMS); + }); } } diff --git a/src/classes/game/WaveManager.ts b/src/classes/game/WaveManager.ts new file mode 100644 index 0000000..9076b43 --- /dev/null +++ b/src/classes/game/WaveManager.ts @@ -0,0 +1,74 @@ +import { CreepType, MissionRoundDefinition, PathDefinition } from '../Definitions'; +import * as PIXI from 'pixi.js'; +import Creep, { CreepEvents } from './Creep'; +import { Globals } from '../Bastion'; + +export enum WaveManagerEvents { + CreepSpawned = 'creepSpawned', + Finished = 'finished', +} + +type CreepInstance = { + creep: Creep; + tickToSpawnAt: number; + spawned: boolean; +}; + +export default class WaveManager { + // Doesn't need to extend GameObject since it does not render + private creeps: CreepInstance[] = []; + private rounds: MissionRoundDefinition[]; + private paths: PathDefinition[]; + private ticks: number = 0; + private started: boolean = false; + public finished: boolean = false; + public events = new PIXI.EventEmitter(); + constructor(rounds: MissionRoundDefinition[], paths: PathDefinition[]) { + Globals.WaveManager = this; + this.rounds = rounds; + this.paths = paths; + } + public start(roundIndex) { + this.started = true; + this.ticks = 0; + this.creeps = []; + this.finished = false; + let tickToSpawnAt = 0; + this.rounds[roundIndex].waves.forEach((wave) => { + tickToSpawnAt += wave.firstCreepSpawnTick; + wave.creeps.forEach((creep) => { + const creepObj = new Creep(creep, this.paths[0]); + const creepInstance = { + creep: creepObj, + tickToSpawnAt, + spawned: false, + }; + console.log('CREAWTASEDASD'); + tickToSpawnAt += wave.spawnIntervalTicks; + this.creeps.push(creepInstance); + }); + }); + console.log(this.creeps); + } + public end() { + this.started = false; + } + public update(elapsedMS: number): void { + if (this.started == false) return; + this.ticks += elapsedMS; + this.creeps.forEach((creep) => { + if (!creep.spawned && creep.tickToSpawnAt <= this.ticks) { + creep.spawned = true; + this.events.emit(WaveManagerEvents.CreepSpawned, creep.creep); + console.log('Wave manager creep spawned, ', creep, this.ticks); + if (!this.finished && this.creeps.every((creep) => creep.spawned)) { + this.finished = true; + console.log('wave maanger finisehd'); + this.events.emit(WaveManagerEvents.Finished); + } + } else if (creep.spawned) { + creep.creep.update(elapsedMS); + } + }); + } +} diff --git a/src/main.ts b/src/main.ts index 22a153b..4248852 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,28 +6,48 @@ import { GameScene } from './scenes/Game'; (async () => { const app = new PIXI.Application(); - const aspectRatio = Globals.AspectRatio; - const maxWidth = window.innerWidth; - const maxHeight = window.innerHeight; - const width = Math.min(maxWidth * 0.75, maxHeight * aspectRatio); - const height = width / aspectRatio; Globals.app = app; - await app.init({ - width: maxWidth, - height: maxHeight, - backgroundColor: 'black', + width: 1920, // Base width + height: 1080, // Base height + resolution: window.devicePixelRatio || 1, + autoDensity: true, + backgroundColor: 0xffffff, sharedTicker: true, - preference: 'webgl', }); - const screenRatio = maxWidth / maxHeight; - const scale = screenRatio < aspectRatio ? maxWidth / width : maxHeight / height; - - app.stage.scale.x = scale; - app.stage.scale.y = scale; - Globals.WindowWidth = maxWidth / scale; - Globals.WindowHeight = maxHeight / scale; document.body.appendChild(app.canvas); + + function resize() { + const windowWidth = window.innerWidth; + const windowHeight = window.innerHeight; + Globals.WindowHeight = windowHeight; + Globals.WindowWidth = windowWidth; + + // Calculate scale factor to maintain aspect ratio + const scaleX = windowWidth / app.screen.width; + const scaleY = windowHeight / app.screen.height; + const scale = Math.min(scaleX, scaleY); // Use the smaller scale to fit + + // Calculate new canvas size + const gameWidth = Math.round(app.screen.width * scale); + const gameHeight = Math.round(app.screen.height * scale); + + // Center the canvas + const marginHorizontal = (windowWidth - gameWidth) / 2; + const marginVertical = (windowHeight - gameHeight) / 2; + + // Apply styles to canvas + app.canvas.style.width = `${gameWidth}px`; + app.canvas.style.height = `${gameHeight}px`; + app.canvas.style.marginLeft = `${marginHorizontal}px`; + app.canvas.style.marginTop = `${marginVertical}px`; + app.canvas.style.marginRight = `0`; // Prevent unnecessary margin + app.canvas.style.marginBottom = `0`; // Prevent unnecessary margin + app.canvas.style.display = 'block'; // Prevent inline-block spacing issues + } + + window.addEventListener('resize', resize); + resize(); await Assets.LoadAssets(); new GameMaster(); Globals.GameMaster.changeScene(new MainScene()); diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index bdb3365..b216c42 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -1,14 +1,26 @@ import GameAssets from '../classes/Assets'; import { Globals } from '../classes/Bastion'; import { MissionDefinition } from '../classes/Definitions'; +import Creep, { CreepEvents } from '../classes/game/Creep'; import { Grid } from '../classes/game/Grid'; +import WaveManager, { WaveManagerEvents } from '../classes/game/WaveManager'; import Sidebar from '../classes/gui/Sidebar'; +import Button, { ButtonTexture } from '../classes/gui/Button'; import Scene from './Scene'; import * as PIXI from 'pixi.js'; +enum RoundMode { + Purchase = 0, + Combat = 1, +} + export class GameScene extends Scene { public mission: MissionDefinition; public missionIndex: number; + public roundMode: RoundMode; + public ticker: PIXI.Ticker; + private currentRound: number = 0; + constructor(name: string) { super(); GameAssets.Missions.forEach((mission, index) => { @@ -19,9 +31,44 @@ export class GameScene extends Scene { }); } public init() { - const SidebarRect = new PIXI.Rectangle(Globals.WindowWidth - 400, 0, 400, Globals.WindowHeight); + this.ticker = new PIXI.Ticker(); + this.ticker.maxFPS = 60; + this.ticker.minFPS = 30; + this.ticker.add(() => this.update(this.ticker.elapsedMS)); // bruh + this.ticker.start(); + const SidebarRect = new PIXI.Rectangle(Globals.WindowWidth - 350, 0, 350, Globals.app.canvas.height); + const StartButtonRect = new PIXI.Rectangle(Globals.WindowWidth - 200, Globals.WindowHeight, 200, 100); - new Sidebar(SidebarRect); new Grid(this.mission.gameMap, this.missionIndex); + new WaveManager(this.mission.rounds, this.mission.gameMap.paths); + Globals.WaveManager.events.on(WaveManagerEvents.CreepSpawned, (creep: Creep) => { + Globals.Grid.addCreep(creep); + creep.events.on(CreepEvents.Escaped, () => { + this.onCreepEscaped(creep); + }); + }); + new Sidebar(SidebarRect); + // const changeRoundButton = new Button('Start', new PIXI.Color('white'), true); + const changeRoundButton = new Button(StartButtonRect, 'Start', ButtonTexture.Button01, true); + changeRoundButton.onClick = () => { + console.log('clicked'); + changeRoundButton.setEnabled(false); + changeRoundButton.setCaption('[X]'); + this.setRoundMode(RoundMode.Combat); + }; + } + public update(elapsedMS) { + Globals.WaveManager.update(elapsedMS); + Globals.Grid.update(elapsedMS); + } + public onCreepEscaped(creep: Creep) {} + + private setRoundMode(roundMode: RoundMode) { + this.roundMode = roundMode; + if (this.roundMode == RoundMode.Combat) { + Globals.WaveManager.start(this.currentRound); + } else { + Globals.WaveManager.end(); + } } } diff --git a/src/scenes/MissionPicker.ts b/src/scenes/MissionPicker.ts index bcf3da5..c54b2f4 100644 --- a/src/scenes/MissionPicker.ts +++ b/src/scenes/MissionPicker.ts @@ -9,7 +9,12 @@ export class MissionPickerScene extends Scene { public init() { Assets.Missions.forEach((mission, index) => { const button = new Button( - new PIXI.Rectangle(Globals.WindowWidth / 2 - 300 / 2, Globals.WindowHeight / 5 + index * 80, 300, 60), + new PIXI.Rectangle( + Globals.app.canvas.width / 2 - 300 / 2, + Globals.app.canvas.height / 5 + index * 80, + 300, + 60 + ), mission.name, ButtonTexture.Button01 );