diff --git a/CREDITS.txt b/CREDITS.txt new file mode 100644 index 0000000..5ff6339 --- /dev/null +++ b/CREDITS.txt @@ -0,0 +1,6 @@ +Font: +Pixellari by Zacchary Dempsey-Plante +https://ztdp.ca/ + +Character: +https://humblecrow.itch.io/itty-bitty-rrg-3-10-characters-asset-pack \ No newline at end of file diff --git a/assets/Pixellari.ttf b/assets/Pixellari.ttf new file mode 100644 index 0000000..5a3a3c2 Binary files /dev/null and b/assets/Pixellari.ttf differ diff --git a/assets/Pixolletta8px.ttf b/assets/Pixolletta8px.ttf new file mode 100644 index 0000000..fa3e11b Binary files /dev/null and b/assets/Pixolletta8px.ttf differ diff --git a/assets/background.png b/assets/background.png new file mode 100644 index 0000000..5e73620 Binary files /dev/null and b/assets/background.png differ diff --git a/assets/cursor/cursor.png b/assets/cursor/cursor.png new file mode 100644 index 0000000..fdf950d Binary files /dev/null and b/assets/cursor/cursor.png differ diff --git a/assets/cursor/default.png b/assets/cursor/default.png new file mode 100644 index 0000000..68a3169 Binary files /dev/null and b/assets/cursor/default.png differ diff --git a/assets/cursor/wrench.png b/assets/cursor/wrench.png new file mode 100644 index 0000000..b5a7990 Binary files /dev/null and b/assets/cursor/wrench.png differ diff --git a/assets/floor_tile.png b/assets/floor_tile.png new file mode 100644 index 0000000..197ac70 Binary files /dev/null and b/assets/floor_tile.png differ diff --git a/assets/items/backpack.png b/assets/items/backpack.png new file mode 100644 index 0000000..a5cb0c0 Binary files /dev/null and b/assets/items/backpack.png differ diff --git a/assets/items/big_backpack.png b/assets/items/big_backpack.png new file mode 100644 index 0000000..f79418f Binary files /dev/null and b/assets/items/big_backpack.png differ diff --git a/assets/items/broken_cable.png b/assets/items/broken_cable.png new file mode 100644 index 0000000..f90da70 Binary files /dev/null and b/assets/items/broken_cable.png differ diff --git a/assets/items/cable_red.png b/assets/items/cable_red.png new file mode 100644 index 0000000..9452189 Binary files /dev/null and b/assets/items/cable_red.png differ diff --git a/assets/items/coffee.png b/assets/items/coffee.png new file mode 100644 index 0000000..1678532 Binary files /dev/null and b/assets/items/coffee.png differ diff --git a/assets/items/comfy_shoes.png b/assets/items/comfy_shoes.png new file mode 100644 index 0000000..332e426 Binary files /dev/null and b/assets/items/comfy_shoes.png differ diff --git a/assets/items/server.png b/assets/items/server.png new file mode 100644 index 0000000..65b13bf Binary files /dev/null and b/assets/items/server.png differ diff --git a/assets/items/shoes.png b/assets/items/shoes.png new file mode 100644 index 0000000..20dfaa3 Binary files /dev/null and b/assets/items/shoes.png differ diff --git a/assets/menu_background.pdn b/assets/menu_background.pdn new file mode 100644 index 0000000..ced8985 Binary files /dev/null and b/assets/menu_background.pdn differ diff --git a/assets/menu_background.png b/assets/menu_background.png new file mode 100644 index 0000000..fbd23d1 Binary files /dev/null and b/assets/menu_background.png differ diff --git a/assets/player/idle_d.png b/assets/player/idle_d.png new file mode 100644 index 0000000..961e4cc Binary files /dev/null and b/assets/player/idle_d.png differ diff --git a/assets/player/idle_l.png b/assets/player/idle_l.png new file mode 100644 index 0000000..24bf163 Binary files /dev/null and b/assets/player/idle_l.png differ diff --git a/assets/player/idle_r.png b/assets/player/idle_r.png new file mode 100644 index 0000000..a643145 Binary files /dev/null and b/assets/player/idle_r.png differ diff --git a/assets/player/idle_u.png b/assets/player/idle_u.png new file mode 100644 index 0000000..bc38635 Binary files /dev/null and b/assets/player/idle_u.png differ diff --git a/assets/player/walk_d_1.png b/assets/player/walk_d_1.png new file mode 100644 index 0000000..245d4c3 Binary files /dev/null and b/assets/player/walk_d_1.png differ diff --git a/assets/player/walk_d_2.png b/assets/player/walk_d_2.png new file mode 100644 index 0000000..f13e4e9 Binary files /dev/null and b/assets/player/walk_d_2.png differ diff --git a/assets/player/walk_l_1.png b/assets/player/walk_l_1.png new file mode 100644 index 0000000..116ca17 Binary files /dev/null and b/assets/player/walk_l_1.png differ diff --git a/assets/player/walk_l_2.png b/assets/player/walk_l_2.png new file mode 100644 index 0000000..116ca17 Binary files /dev/null and b/assets/player/walk_l_2.png differ diff --git a/assets/player/walk_r_1.png b/assets/player/walk_r_1.png new file mode 100644 index 0000000..340adf2 Binary files /dev/null and b/assets/player/walk_r_1.png differ diff --git a/assets/player/walk_r_2.png b/assets/player/walk_r_2.png new file mode 100644 index 0000000..340adf2 Binary files /dev/null and b/assets/player/walk_r_2.png differ diff --git a/assets/player/walk_u_1.png b/assets/player/walk_u_1.png new file mode 100644 index 0000000..c94f761 Binary files /dev/null and b/assets/player/walk_u_1.png differ diff --git a/assets/player/walk_u_2.png b/assets/player/walk_u_2.png new file mode 100644 index 0000000..cd0f167 Binary files /dev/null and b/assets/player/walk_u_2.png differ diff --git a/assets/server.png b/assets/server.png new file mode 100644 index 0000000..afa88a8 Binary files /dev/null and b/assets/server.png differ diff --git a/assets/server/server_1.png b/assets/server/server_1.png new file mode 100644 index 0000000..a0eb1bd Binary files /dev/null and b/assets/server/server_1.png differ diff --git a/assets/server/server_2.png b/assets/server/server_2.png new file mode 100644 index 0000000..f851082 Binary files /dev/null and b/assets/server/server_2.png differ diff --git a/assets/server/server_3.png b/assets/server/server_3.png new file mode 100644 index 0000000..d98de5d Binary files /dev/null and b/assets/server/server_3.png differ diff --git a/assets/server/server_4.png b/assets/server/server_4.png new file mode 100644 index 0000000..8570d25 Binary files /dev/null and b/assets/server/server_4.png differ diff --git a/assets/server/server_empty.png b/assets/server/server_empty.png new file mode 100644 index 0000000..ae49f9e Binary files /dev/null and b/assets/server/server_empty.png differ diff --git a/assets/server/server_failure.png b/assets/server/server_failure.png new file mode 100644 index 0000000..e58f7ef Binary files /dev/null and b/assets/server/server_failure.png differ diff --git a/assets/server/sparks/spark_1.png b/assets/server/sparks/spark_1.png new file mode 100644 index 0000000..495192f Binary files /dev/null and b/assets/server/sparks/spark_1.png differ diff --git a/assets/server/sparks/spark_2.png b/assets/server/sparks/spark_2.png new file mode 100644 index 0000000..dc53153 Binary files /dev/null and b/assets/server/sparks/spark_2.png differ diff --git a/assets/server/sparks/spark_3.png b/assets/server/sparks/spark_3.png new file mode 100644 index 0000000..8aa3b0e Binary files /dev/null and b/assets/server/sparks/spark_3.png differ diff --git a/assets/shop_table.png b/assets/shop_table.png new file mode 100644 index 0000000..28e175d Binary files /dev/null and b/assets/shop_table.png differ diff --git a/assets/sounds/buy.wav b/assets/sounds/buy.wav new file mode 100644 index 0000000..7ed596a Binary files /dev/null and b/assets/sounds/buy.wav differ diff --git a/assets/sounds/cable_connect.wav b/assets/sounds/cable_connect.wav new file mode 100644 index 0000000..f5320f1 Binary files /dev/null and b/assets/sounds/cable_connect.wav differ diff --git a/assets/sounds/cable_disconnect.wav b/assets/sounds/cable_disconnect.wav new file mode 100644 index 0000000..5e25430 Binary files /dev/null and b/assets/sounds/cable_disconnect.wav differ diff --git a/assets/sounds/click.wav b/assets/sounds/click.wav new file mode 100644 index 0000000..60a5d36 Binary files /dev/null and b/assets/sounds/click.wav differ diff --git a/assets/sounds/fix_server.wav b/assets/sounds/fix_server.wav new file mode 100644 index 0000000..38e1f93 Binary files /dev/null and b/assets/sounds/fix_server.wav differ diff --git a/assets/sounds/make_coffee.wav b/assets/sounds/make_coffee.wav new file mode 100644 index 0000000..5c6c007 Binary files /dev/null and b/assets/sounds/make_coffee.wav differ diff --git a/assets/sounds/open_dialogue.wav b/assets/sounds/open_dialogue.wav new file mode 100644 index 0000000..9187bdc Binary files /dev/null and b/assets/sounds/open_dialogue.wav differ diff --git a/assets/sounds/open_server.sfs b/assets/sounds/open_server.sfs new file mode 100644 index 0000000..f9e1a61 Binary files /dev/null and b/assets/sounds/open_server.sfs differ diff --git a/assets/sounds/open_server.wav b/assets/sounds/open_server.wav new file mode 100644 index 0000000..2c3d8b3 Binary files /dev/null and b/assets/sounds/open_server.wav differ diff --git a/assets/sounds/place_item.wav b/assets/sounds/place_item.wav new file mode 100644 index 0000000..6a9c8e1 Binary files /dev/null and b/assets/sounds/place_item.wav differ diff --git a/assets/sounds/restore_energy.wav b/assets/sounds/restore_energy.wav new file mode 100644 index 0000000..7cca057 Binary files /dev/null and b/assets/sounds/restore_energy.wav differ diff --git a/assets/sounds/spark.wav b/assets/sounds/spark.wav new file mode 100644 index 0000000..1cdabca Binary files /dev/null and b/assets/sounds/spark.wav differ diff --git a/assets/sounds/step.wav b/assets/sounds/step.wav new file mode 100644 index 0000000..c77c1d8 Binary files /dev/null and b/assets/sounds/step.wav differ diff --git a/assets/sounds/take_cable.wav b/assets/sounds/take_cable.wav new file mode 100644 index 0000000..3989434 Binary files /dev/null and b/assets/sounds/take_cable.wav differ diff --git a/assets/sounds/take_item.wav b/assets/sounds/take_item.wav new file mode 100644 index 0000000..b1485e0 Binary files /dev/null and b/assets/sounds/take_item.wav differ diff --git a/assets/sounds/trash.wav b/assets/sounds/trash.wav new file mode 100644 index 0000000..3e2a89d Binary files /dev/null and b/assets/sounds/trash.wav differ diff --git a/assets/table.png b/assets/table.png new file mode 100644 index 0000000..9fd3a9f Binary files /dev/null and b/assets/table.png differ diff --git a/assets/table_cables.png b/assets/table_cables.png new file mode 100644 index 0000000..a073901 Binary files /dev/null and b/assets/table_cables.png differ diff --git a/assets/table_coffee.png b/assets/table_coffee.png new file mode 100644 index 0000000..adcc96b Binary files /dev/null and b/assets/table_coffee.png differ diff --git a/assets/trashbin.png b/assets/trashbin.png new file mode 100644 index 0000000..36961b2 Binary files /dev/null and b/assets/trashbin.png differ diff --git a/assets/ui/cable_connector.png b/assets/ui/cable_connector.png new file mode 100644 index 0000000..038ccfa Binary files /dev/null and b/assets/ui/cable_connector.png differ diff --git a/assets/ui/close_button.png b/assets/ui/close_button.png new file mode 100644 index 0000000..99135d0 Binary files /dev/null and b/assets/ui/close_button.png differ diff --git a/assets/ui/connector.png b/assets/ui/connector.png new file mode 100644 index 0000000..a9e1a09 Binary files /dev/null and b/assets/ui/connector.png differ diff --git a/assets/ui/gauge.pdn b/assets/ui/gauge.pdn new file mode 100644 index 0000000..9032405 Binary files /dev/null and b/assets/ui/gauge.pdn differ diff --git a/assets/ui/gauge.png b/assets/ui/gauge.png new file mode 100644 index 0000000..7e43b8f Binary files /dev/null and b/assets/ui/gauge.png differ diff --git a/assets/ui/server_rack.pdn b/assets/ui/server_rack.pdn new file mode 100644 index 0000000..d392557 Binary files /dev/null and b/assets/ui/server_rack.pdn differ diff --git a/assets/ui/server_rack.png b/assets/ui/server_rack.png new file mode 100644 index 0000000..d2b1398 Binary files /dev/null and b/assets/ui/server_rack.png differ diff --git a/assets/ui/ui_frame.png b/assets/ui/ui_frame.png new file mode 100644 index 0000000..8fab5fb Binary files /dev/null and b/assets/ui/ui_frame.png differ diff --git a/classes/Assets.js b/classes/Assets.js new file mode 100644 index 0000000..7e9cc6f --- /dev/null +++ b/classes/Assets.js @@ -0,0 +1,195 @@ +class Assets { + static menuBackground = new Image(); + static background = new Image(); + static floorTile = new Image(); + + static player = { + idle: { + u: new Image(), + d: new Image(), + r: new Image(), + l: new Image() + }, + walking: { + u: [ + new Image(), + new Image(), + new Image() + ], + d: [ + new Image(), + new Image(), + new Image() + ], + r: [ + new Image(), + new Image(), + new Image() + ], + l: [ + new Image(), + new Image(), + new Image() + ] + } + } + + static trashbin = new Image(); + static coffeetable = new Image(); + static cabletable = new Image(); + static shoptable = new Image(); + + static server = [ + new Image(), + new Image(), + new Image(), + new Image() + ]; + + static serverFailure = new Image(); + + static spark = [ + new Image(), + new Image(), + new Image() + ]; + + static ui = { + cursor: { + default: new Image(), + wrench: new Image() + }, + server: { + background: new Image(), + connector: new Image(), + cableConnector: new Image() + }, + buttons: { + close: new Image() + }, + gauge: new Image() + }; + + static items = { + cable_red: new Image(), + broken_cable: new Image(), + coffee: new Image(), + backpack: new Image(), + big_backpack: new Image(), + shoes: new Image(), + comfy_shoes: new Image(), + server: new Image() + } + + static sounds = { + player: { + step: new Audio("assets/sounds/step.wav"), + drink: new Audio("assets/sounds/restore_energy.wav"), + makeCoffe: new Audio("assets/sounds/make_coffee.wav"), + takeCable: new Audio("assets/sounds/take_cable.wav"), + placeItem: new Audio("assets/sounds/place_item.wav"), + takeItem: new Audio("assets/sounds/take_item.wav") + }, + server: { + open: new Audio("assets/sounds/open_server.wav"), + spark: new Audio("assets/sounds/spark.wav"), + fix: new Audio("assets/sounds/fix_server.wav") + }, + cable: { + connect: new Audio("assets/sounds/cable_connect.wav"), + disconnect: new Audio("assets/sounds/cable_disconnect.wav") + }, + trash: new Audio("assets/sounds/trash.wav"), + click: new Audio("assets/sounds/click.wav"), + buy: new Audio("assets/sounds/buy.wav"), + openDialogue: new Audio("assets/sounds/open_dialogue.wav") + }; + + static fonts = { + small: { + size: 10, + name: "Pixolletta8px", + full: "10px Pixolletta8px" + }, + large: { + size: 16, + name: "Pixellari", + full: "16px Pixellari" + } + } + + static loadAssets() { + Assets.menuBackground.src = "assets/menu_background.png"; + Assets.background.src = "assets/background.png"; + Assets.floorTile.src = "assets/floor_tile.png"; + + Assets.player.idle.u.src = "assets/player/idle_u.png"; + Assets.player.idle.d.src = "assets/player/idle_d.png"; + Assets.player.idle.l.src = "assets/player/idle_l.png"; + Assets.player.idle.r.src = "assets/player/idle_r.png"; + + Assets.player.walking.u[0].src = "assets/player/walk_u_1.png"; + Assets.player.walking.u[1].src = "assets/player/idle_u.png"; + Assets.player.walking.u[2].src = "assets/player/walk_u_2.png"; + + Assets.player.walking.d[0].src = "assets/player/walk_d_1.png"; + Assets.player.walking.d[1].src = "assets/player/idle_d.png"; + Assets.player.walking.d[2].src = "assets/player/walk_d_2.png"; + + Assets.player.walking.l[0].src = "assets/player/walk_l_1.png"; + Assets.player.walking.l[1].src = "assets/player/idle_l.png"; + Assets.player.walking.l[2].src = "assets/player/walk_l_2.png"; + + Assets.player.walking.r[0].src = "assets/player/walk_r_1.png"; + Assets.player.walking.r[1].src = "assets/player/idle_r.png"; + Assets.player.walking.r[2].src = "assets/player/walk_r_2.png"; + + Assets.trashbin.src = "assets/trashbin.png"; + Assets.coffeetable.src = "assets/table_coffee.png"; + Assets.cabletable.src = "assets/table_cables.png"; + Assets.shoptable.src = "assets/shop_table.png"; + + Assets.server[0].src = "assets/server/server_1.png"; + Assets.server[1].src = "assets/server/server_2.png"; + Assets.server[2].src = "assets/server/server_3.png"; + Assets.server[3].src = "assets/server/server_4.png"; + + Assets.serverFailure.src = "assets/server/server_failure.png"; + + Assets.spark[0].src = "assets/server/sparks/spark_1.png"; + Assets.spark[1].src = "assets/server/sparks/spark_2.png"; + Assets.spark[2].src = "assets/server/sparks/spark_3.png"; + + Assets.ui.cursor.default.src = "assets/cursor/default.png"; + Assets.ui.cursor.wrench.src = "assets/cursor/wrench.png"; + + Assets.ui.server.background.src = "assets/ui/server_rack.png"; + Assets.ui.server.connector.src = "assets/ui/connector.png"; + Assets.ui.server.cableConnector.src = "assets/ui/cable_connector.png"; + + Assets.ui.buttons.close.src = "assets/ui/close_button.png"; + + Assets.ui.gauge.src = "assets/ui/gauge.png"; + + Assets.items.cable_red.src = "assets/items/cable_red.png"; + Assets.items.broken_cable.src = "assets/items/broken_cable.png"; + Assets.items.coffee.src = "assets/items/coffee.png"; + Assets.items.backpack.src = "assets/items/backpack.png"; + Assets.items.big_backpack.src = "assets/items/big_backpack.png"; + Assets.items.shoes.src = "assets/items/shoes.png"; + Assets.items.comfy_shoes.src = "assets/items/comfy_shoes.png"; + Assets.items.server.src = "assets/items/server.png"; + + + const small_font = new FontFace( + Assets.fonts.small.name, + "url(assets/Pixolletta8px.ttf)" + ); + const large_font = new FontFace( + Assets.fonts.large.name, + "url(assets/Pixellari.ttf)" + ); + document.fonts.add(small_font); + document.fonts.add(large_font); + } +} \ No newline at end of file diff --git a/classes/Game.js b/classes/Game.js new file mode 100644 index 0000000..0043d70 --- /dev/null +++ b/classes/Game.js @@ -0,0 +1,50 @@ +class Game { + static FPS = 30; + static TILESIZE = 16; + static TILES_X = 15; + static TILES_Y = 10; + static WIDTH = Game.TILESIZE * Game.TILES_X; + static HEIGHT = Game.TILESIZE * Game.TILES_Y; + + static MIN_TIME_UNTIL_FAIL = 20; + static RANDOM_TIME_UNTIL_FAIL = 40; + + static SERVER_ACCESS_DISTANCE = Game.TILESIZE + 3; + + static PLAYER_SPEED = 3; + static PLAYER_OOE_SPEED = 1; + static PLAYER_MAX_ENERGY = Game.FPS * 5; + + static INITIAL_SERVER_LOAD_GROWTH = 400; + static MAX_SERVER_LOAD = 1000000; + + static PASSIVE_INCOME_PER_PLAYER = 0.06 / 400; + static FIX_SERVER_INCOME = 10; + + static serverLoadGrowth = Game.INITIAL_SERVER_LOAD_GROWTH; + static serverLoad = 200; + static lastServerLoad = 200; + + static money = 0; + + static scale; + + static c; + static cv; + + static player; + + static gamestate = "menu"; + + static tutorialStep = 0; + + static tutorialServers = []; + + static set scale(scale) { + Game.scale = scale; + } + + static get scale() { + return Game.scale; + } +} \ No newline at end of file diff --git a/classes/GameObject.js b/classes/GameObject.js new file mode 100644 index 0000000..f7ac728 --- /dev/null +++ b/classes/GameObject.js @@ -0,0 +1,57 @@ +class GameObject { + static gameObjects = []; + + constructor(x, y) { + this.x = x; + this.y = y; + this.clickable = false; + GameObject.gameObjects.push(this); + } + + update() { + + } + + click() { + + } + + mouseUp() { + + } + + draw() { + Game.c.fillStyle = "red"; + Game.c.fillRect(this.x, this.y, Game.TILESIZE, Game.TILESIZE); + } + + + distance(o) { + return Math.sqrt(Math.pow(this.x - o.x, 2) + Math.pow(this.y - o.y, 2)); + } + + delete() { + if (this.hitbox != undefined) { + this.hitbox.delete() + } + for (let i = 0; i < GameObject.gameObjects.length; i++) { + if (GameObject.gameObjects[i] == this) { + GameObject.gameObjects.splice(i, 1); + return; + } + } + } + + static updateAll() { + for (let o in GameObject.gameObjects) { + GameObject.gameObjects[o].update(); + } + } + + static drawAll() { + GameObject.gameObjects.sort((a, b) => (a.y > b.y ? 1 : -1)); + for (let o in GameObject.gameObjects) { + GameObject.gameObjects[o].draw(); + } + } +} \ No newline at end of file diff --git a/classes/Hitbox.js b/classes/Hitbox.js new file mode 100644 index 0000000..fb8fd60 --- /dev/null +++ b/classes/Hitbox.js @@ -0,0 +1,45 @@ +class Hitbox { + + static hitboxes = []; + + constructor (x, y, w, h) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + + Hitbox.hitboxes.push(this); + } + + collides(hb) { + return rectsCollide(this.x, this.y, this.w, this.h, hb.x, hb.y, hb.w, hb.h); + } + + collidesAny() { + for (let i = 0; i < Hitbox.hitboxes.length; i++) { + if (Hitbox.hitboxes[i] != null && Hitbox.hitboxes[i] != this && this.collides(Hitbox.hitboxes[i])) + return true; + } + return false; + } + + pointInBound(x, y) { + return x >= this.x && y >= this.y && x < this.x + this.w && y < this.y + this.h; + } + + delete() { + for (let i = 0; i < Hitbox.hitboxes.length; i++) { + if (Hitbox.hitboxes[i] == this) { + Hitbox.hitboxes.splice(i, 1); + return; + } + } + } + + static tempCollidesAny(x, y, w, h) { + let hb = new Hitbox(x, y, w, h); + let collides = hb.collidesAny(); + hb.delete(); + return collides; + } +} \ No newline at end of file diff --git a/classes/Player.js b/classes/Player.js new file mode 100644 index 0000000..527155f --- /dev/null +++ b/classes/Player.js @@ -0,0 +1,137 @@ +class Player extends GameObject { + constructor(x, y) { + super(x, y); + this.walking = false; + this.facing = "d"; + + this.clickable = true; + this.acceptsItem = true; + + this.hitbox = new Hitbox(x + 4, y + 10, Game.TILESIZE - 8, Game.TILESIZE - 10); + + this.walkCycle = 0; + this.direction_keys = [false, false, false, false]; + this.inventory = []; + this.inventorySlots = 1; + + this.maxEnergy = Game.PLAYER_MAX_ENERGY; + this.energy = Game.PLAYER_MAX_ENERGY; + } + + get speed() { + return this.energy > 0 ? Game.PLAYER_SPEED : Game.PLAYER_OOE_SPEED; + } + + keydown(e) { + if (e.code == "KeyW" || e.code == "ArrowUp") { + this.facing = "u"; + this.walking = true; + this.direction_keys[0] = true; + } + if (e.code == "KeyS" || e.code == "ArrowDown") { + this.facing = "d"; + this.walking = true; + this.direction_keys[1] = true; + } + if (e.code == "KeyA" || e.code == "ArrowLeft") { + this.facing = "l"; + this.walking = true; + this.direction_keys[2] = true; + } + if (e.code == "KeyD" || e.code == "ArrowRight") { + this.facing = "r"; + this.walking = true; + this.direction_keys[3] = true; + } + } + + keyup(e) { + if (e.code == "KeyW" || e.code == "ArrowUp") { + this.direction_keys[0] = false; + } + if (e.code == "KeyS" || e.code == "ArrowDown") { + this.direction_keys[1] = false; + } + if (e.code == "KeyA" || e.code == "ArrowLeft") { + this.direction_keys[2] = false; + } + if (e.code == "KeyD" || e.code == "ArrowRight") { + this.direction_keys[3] = false; + } + this.walking = false; + for (let i = 0; i < this.direction_keys.length; i++) { + if (this.direction_keys[i] == true) { + this.walking = true; + this.facing = "udlr"[i]; + break; + } + } + } + + move(x, y) { + this.x += x; + this.y += y; + this.hitbox.x += x; + this.hitbox.y += y; + } + + update() { + if (this.walking) { + if (this.facing == "u") { + this.move(0, - this.speed); + } + if (this.facing == "d") { + this.move(0, this.speed); + } + if (this.facing == "l") { + this.move(- this.speed, 0); + } + if (this.facing == "r") { + this.move(this.speed, 0); + } + if (this.energy > 0 && (Game.gamestate != "tutorial" || Game.tutorialStep >= 12)) { + this.energy--; + } + } + while (this.hitbox.collidesAny()) { + if (this.facing == "u") { + this.move(0, 1); + } + if (this.facing == "d") { + this.move(0, -1); + } + if (this.facing == "l") { + this.move(1, 0); + } + if (this.facing == "r") { + this.move(-1, 0); + } + } + } + + draw() { + let sprite; + if (this.walking) { + sprite = Assets.player.walking[this.facing][this.walkCycle]; + this.walkCycle = (this.walkCycle + 1) % 3; + + Assets.sounds.player.step.play(); + } else { + sprite = Assets.player.idle[this.facing]; + this.walkCycle = 0; + } + c.drawImage(sprite, this.x, this.y, Game.TILESIZE, Game.TILESIZE); + } + + passItem(item) { + if (item == "coffee") { + this.energy = this.maxEnergy; + Assets.sounds.player.drink.play(); + if (Game.gamestate == "tutorial" && Game.tutorialStep == 9) { + tutorialStep(); + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/classes/Server.js b/classes/Server.js new file mode 100644 index 0000000..e504faf --- /dev/null +++ b/classes/Server.js @@ -0,0 +1,107 @@ +class Server extends GameObject { + constructor(x, y) { + super(x, y); + this.clickable = true; + this.hitbox = new Hitbox(this.x, this.y, Game.TILESIZE, Game.TILESIZE); + + this.sprite = Assets.server[Math.floor(Math.random() * Assets.server.length)]; + this.framesSinceSpriteChange = 0; + + this.failBlinkCycle = 0; + + this.framesUntilNextFailure = Game.FPS * Game.MIN_TIME_UNTIL_FAIL + Math.floor(Math.random() * Game.FPS * Game.RANDOM_TIME_UNTIL_FAIL); + + this.sparkFrames = -1; + + this.numConnectors = 5; + this.numLines = 2; + + this.ui = new ServerUI(this); + this.ui.visible = false; + + this.load = 100; + } + draw() { + if (!this.failure) { + Game.c.drawImage(this.sprite, this.x, this.y - Game.TILESIZE, Game.TILESIZE, Game.TILESIZE * 2); + if (Math.random() < Math.pow(1.001, this.framesSinceSpriteChange) - 1) { + this.sprite = Assets.server[Math.floor(Math.random() * Assets.server.length)]; + this.framesSinceSpriteChange = 0; + } else { + this.framesSinceSpriteChange++; + } + } else { + this.failBlinkCycle = (this.failBlinkCycle + 1) % (Game.FPS * 2); + + Game.c.drawImage(Assets.serverFailure, this.x, this.y - Game.TILESIZE, Game.TILESIZE, Game.TILESIZE * 2); + + Game.c.fillStyle = "red"; + Game.c.globalAlpha = Math.abs(this.failBlinkCycle - Game.FPS) / (Game.FPS * 3); + Game.c.fillRect(this.x, this.y - Game.TILESIZE * 0.5, Game.TILESIZE, Game.TILESIZE * 1.5); + Game.c.globalAlpha = 1; + + + if (this.sparkFrames >= 0) { + Game.c.drawImage(Assets.spark[this.sparkFrames], this.x, this.y); + this.sparkFrames++; + if (this.sparkFrames >= Assets.spark.length) { + this.sparkFrames = -1; + } + } else { + if (Math.random() < 0.01) { + this.sparkFrames = 0; + Assets.sounds.server.spark.play(); + } + } + } + } + + click() { + if (this.failure && this.distance(Game.player) < Game.SERVER_ACCESS_DISTANCE) { + this.ui.visible = true; + Assets.sounds.server.open.play(); + } + if ( + Game.gamestate == "tutorial" && ( + Game.tutorialStep == 3 || + Game.tutorialStep == 4 || + Game.tutorialStep == 13 || + Game.tutorialStep == 18 || + Game.tutorialStep == 27 + ) + ) { + tutorialStep(); + } + } + + fix() { + this.framesUntilNextFailure = Game.FPS * Game.MIN_TIME_UNTIL_FAIL + Math.floor(Math.random() * Game.FPS * Game.RANDOM_TIME_UNTIL_FAIL); + Assets.sounds.server.fix.play(); + Game.money += Game.FIX_SERVER_INCOME; + if (Game.gamestate == "tutorial") { + tutorialStep(); + } + } + + get failure() { + return this.framesUntilNextFailure <= 0; + } + + update() { + if (!this.failure) { + this.framesUntilNextFailure--; + if (this.failure) { + this.ui.createFailure(); + } + + Game.serverLoad -= this.load; + } + } +} + + +class TutorialServer extends Server { + update() { + + } +} \ No newline at end of file diff --git a/classes/Tables.js b/classes/Tables.js new file mode 100644 index 0000000..afbf96c --- /dev/null +++ b/classes/Tables.js @@ -0,0 +1,55 @@ +class CoffeeTable extends GameObject { + constructor(x, y) { + super(x, y); + this.clickable = true; + this.hitbox = new Hitbox(this.x, this.y + 5, Game.TILESIZE, Game.TILESIZE - 5); + } + draw() { + Game.c.drawImage(Assets.coffeetable, this.x, this.y); + } + click() { + if (Game.player.distance(this) < Game.TILESIZE) { + Assets.sounds.player.makeCoffe.play(); + Game.cursor.item = "coffee"; + } + } +} + +class CableTable extends GameObject { + constructor(x, y) { + super(x, y); + this.clickable = true; + this.hitbox = new Hitbox(this.x, this.y + 5, Game.TILESIZE, Game.TILESIZE - 5); + } + draw() { + Game.c.drawImage(Assets.cabletable, this.x, this.y); + } + click() { + if (Game.player.distance(this) < Game.TILESIZE) { + Assets.sounds.player.takeCable.play(); + Game.cursor.item = "cable_red"; + } + } +} + +class ShopTable extends GameObject { + constructor(x, y) { + super(x, y); + this.clickable = true; + this.hitbox = new Hitbox(this.x, this.y + 5, Game.TILESIZE, Game.TILESIZE - 5); + this.shop = new Shop(); + this.shop.visible = false; + } + draw() { + Game.c.drawImage(Assets.shoptable, this.x, this.y); + } + click() { + if (Game.player.distance(this) < Game.TILESIZE) { + Assets.sounds.click.play(); + this.shop.visible = true; + if (Game.gamestate == "tutorial" && Game.tutorialStep == 23) { + tutorialStep(); + } + } + } +} \ No newline at end of file diff --git a/classes/Trashbin.js b/classes/Trashbin.js new file mode 100644 index 0000000..a8bf07d --- /dev/null +++ b/classes/Trashbin.js @@ -0,0 +1,21 @@ +class Trashbin extends GameObject { + constructor(x, y) { + super(x, y); + this.clickable = true; + this.acceptsItem = true; + this.hitbox = new Hitbox(this.x, this.y + 5, Game.TILESIZE, Game.TILESIZE - 5); + } + draw() { + Game.c.drawImage(Assets.trashbin, this.x, this.y); + } + passItem(item) { + if (Game.player.distance(this) < Game.TILESIZE) { + Assets.sounds.trash.play(); + if (Game.gamestate == "tutorial" && Game.tutorialStep == 30) { + tutorialStep(); + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/empty.js b/empty.js new file mode 100644 index 0000000..7ad170b --- /dev/null +++ b/empty.js @@ -0,0 +1,389 @@ +/// CONFIG +var FPS=30; //How often tick() is called each second + +//Width and height of the canvas. -1 = maximum possible (whole window) +var WIDTH = -1; +var HEIGHT = -1; + +var HIDDEN = false; + +var LEFT = 0.5; +var TOP = 0.5; + +var BORDER = 0; +var BORDER_COLOR = "black"; +var FULLSCREEN = false; + +var PAGE_TITLE = "Empty.js"; + +var PAUSED = false; //set to true to pause calling tick(). + +var CURSOR = "default" //set cursor on canvas + +//EMPTYJS +var cv, c; +var empty_keys = []; +var empty_mouse_x = -1; +var empty_mouse_y = -1; +var empty_mouse_keys= [false, false, false]; + +var empty_new_mouse_x = -1; +var empty_new_mouse_y = -1; + +document.addEventListener("DOMContentLoaded",function() { //ONLOAD + cv = document.getElementById("cv"); + if (cv == null) { + cv = document.createElement("canvas"); + cv.id = "cv"; + document.body.appendChild(cv); + } + c = cv.getContext("2d"); + + document.body.style.margin="0"; + + window.addEventListener("resize",empty_resize); + + document.addEventListener("fullscreenchange",function() { + cv.focus(); + empty_fullscreen(); + }); + + document.addEventListener("mousemove",function(e) { //MOUSE MOVEMENT + var box = cv.getBoundingClientRect(); + var boxX = box.top; + var boxY = box.left; + var x = e.clientX - boxX; + var y = e.clientY - boxY; + empty_new_mouse_x = e.clientX; + empty_new_mouse_y = e.clientY; + if (typeof onMouseMove == "function") { + onMouseMove(x,y); + } + }); + + document.addEventListener("keydown", function(e) { //KEY DOWN + if (e.isComposing || e.keyCode === 229) { return; } + empty_setKey(e.code, true); + if (typeof onKeyDown == "function") { + onKeyDown(e); + } + empty_fullscreen(); + }); + + document.addEventListener("keyup", function(e) { //KEY UP + if (e.isComposing || e.keyCode === 229) {return;} + empty_setKey(e.code, false); + if (typeof onKeyUp == "function") { + onKeyUp(e); + } + }); + + document.addEventListener("click", function(e) { //CLICK + empty_fullscreen(); + if (!HIDDEN && !PAUSED) { + cv.focus(); + } + }); + + document.addEventListener("mousedown", function(e) { //MOUSEDOWN + if (e.button==0) { + empty_fullscreen(); + } + if (!HIDDEN && !PAUSED) { + cv.focus(); + } + empty_new_mouse_x = e.clientX; + empty_new_mouse_y = e.clientY; + empty_mouse_keys[e.button]=true; + if (typeof onMouseDown == "function") { + onMouseDown(getMousePos().x, getMousePos().y, e.button); + } + }); + + document.addEventListener("mouseup",function(e) { //MOUSEUP + if (e.button==0) { + empty_fullscreen(); + } + empty_new_mouse_x = e.clientX; + empty_new_mouse_y = e.clientY; + empty_mouse_keys[e.button]=false; + if (typeof onMouseUp == "function") { + onMouseUp(getMousePos().x, getMousePos().y, e.button); + } + }); + + document.addEventListener("contextmenu",function(e) { //CONTEXTMENU + if (!HIDDEN && !PAUSED) { + cv.focus(); + e.preventDefault(); + } + cv.focus(); + }); + + document.addEventListener("touchmove",function(e) { + empty_new_mouse_x = e.touches[0].clientX; + empty_new_mouse_y = e.touches[0].clientY; + var box = cv.getBoundingClientRect(); + var boxX = box.top; + var boxY = box.left; + var x = empty_new_mouse_x - boxX; + var y = empty_new_mouse_y - boxY; + if (typeof onMouseMove == "function") { + onMouseMove(x,y); + } + }) + + document.addEventListener("touchstart",function(e) { + empty_new_mouse_x = e.touches[0].clientX; + empty_new_mouse_y = e.touches[0].clientY; + var box = cv.getBoundingClientRect(); + var boxX = box.top; + var boxY = box.left; + var x = empty_new_mouse_x - boxX; + var y = empty_new_mouse_y - boxY; + if (typeof onMouseDown == "function") { + onMouseDown(x,y,0); + } + }) + + document.addEventListener("touchstart",function(e) { + empty_new_mouse_x = e.touches[0].clientX; + empty_new_mouse_y = e.touches[0].clientY; + var box = cv.getBoundingClientRect(); + var boxX = box.top; + var boxY = box.left; + var x = empty_new_mouse_x - boxX; + var y = empty_new_mouse_y - boxY; + empty_mouse_keys[0] = true; + if (typeof onMouseDown == "function") { + onMouseDown(x,y,0); + } + }) + document.addEventListener("touchend",function(e) { + var box = cv.getBoundingClientRect(); + var boxX = box.top; + var boxY = box.left; + var x = empty_new_mouse_x - boxX; + var y = empty_new_mouse_y - boxY; + empty_mouse_keys[0] = false; + if (typeof onMouseUp == "function") { + onMouseUp(x,y,0); + } + }) + + empty_resize(); + if (typeof init == "function") { + init(); + } + setTimeout(empty_tick,1000/FPS); +}); + +function empty_tick() { //TICK + window.document.title=PAGE_TITLE; + + if (HIDDEN && cv.style.display!="hidden") { + cv.style.display="none"; + } + if (!HIDDEN && cv.style.display!="inline-block") { + cv.style.display="inline-block"; + } + + if (BORDER>0) { + cv.style.borderStyle="solid"; + } else { + cv.style.borderStyle="none"; + } + cv.style.borderWidth=BORDER; + cv.style.borderColor=BORDER_COLOR; + if (typeof tick == "function" && !PAUSED) { + tick(); + } + document.body.style.cursor = CURSOR; + empty_mouse_x = empty_new_mouse_x; + empty_mouse_y = empty_new_mouse_y; + setTimeout(empty_tick,1000/FPS); +} + +function empty_fullscreen() { + if (FULLSCREEN) { + FULLSCREEN=false; + if(document.body.requestFullscreen) { + document.body.requestFullscreen(); + } else if(document.body.mozRequestFullScreen) { + document.body.mozRequestFullScreen(); + } else if(document.body.msRequestFullscreen) { + document.body.msRequestFullscreen(); + } else if(document.body.webkitRequestFullscreen) { + document.body.webkitRequestFullscreen(); + } else { + FULLSCREEN=true; + } + } +} + +function empty_resize() { //WINDOW RESIZE + if (typeof onresize=="function") { + onresize(window.innerWidth, window.innerHeight); + } +} + +function empty_setKey(code, value) { //SET KEYSTATE + empty_keys[code] = value; +} + + +// DATA FUNCTIONS + +function getMouseKey(key) { //GET MOUSE KEY + return empty_mouse_keys[key]; +} + +function getKey(code) { //GET KEYSTATE + if (code in empty_keys) return empty_keys[code]; + return false; +} + +function getMousePosGobal() { //MOUSE POSITION + return {x: empty_mouse_x, y: empty_mouse_y}; +} + +function getMousePos() { + var box = cv.getBoundingClientRect(); + var boxX = box.top; + var boxY = box.left; + var x = empty_new_mouse_x - box.top; + var y = empty_new_mouse_y - box.left; + return {x: x, y: y}; +} + +function getLastMousePos() { + var box = cv.getBoundingClientRect(); + var boxX = box.top; + var boxY = box.left; + var x = empty_mouse_x - box.top; + var y = empty_mouse_y - box.left; + return {x: x, y: y}; +} + +function getRelativeMouseMovement() { + var m1=getMousePos(); + var m2=getLastMousePos(); + var ret={x: m1.x - m2.x, y: m1.y - m2.y}; + return ret; +} + + +// DRAWING FUNCTIONS + +function fillBackground() { + c.fillRect(0,0,cv.width,cv.height); +} + +function fillCircle(x,y,r) { + c.beginPath(); + c.moveTo(x,y); + c.arc(x,y,r,0,Math.PI*2); + c.fill(); +} + +function strokeCircle(x,y,r) { + c.beginPath(); + c.moveTo(x+r,y); + c.arc(x,y,r,0,Math.PI*2); + c.stroke(); +} + +function line(x1,y1,x2,y2) { + c.beginPath(); + c.moveTo(x1,y1); + c.lineTo(x2,y2); + c.stroke(); +} + +function drawRotatedImage(image,x,y,angle,width,height) { + var origin = {x: width/2, y: height/2}; + c.save(); + c.translate(x,y); + c.rotate(angle); + c.drawImage(image,-origin.x,-origin.y,width,height); + c.restore(); +} + + +// CALCULATION FUNCTIONS + +function distance(x1,y1,x2,y2) { //DISTANCE + return Math.sqrt(Math.pow(x1-x2,2) + Math.pow(y1-y2,2)); +} + +function randomRange(min,max) { //RANDOM RANGE + return Math.floor(Math.random() * (max-min+1) + min); +} + +function range(j) { + var arr=[]; + for (var i=0; imax) return max; + if (num=rx && py>=ry && px<=rx+rw && py<=ry+rh; +} + +function pointInCircle(px,py,cx,cy,cr) { + return distance(px,py,cx,cy)<=cr; +} + +function rectsCollide(x1,y1,dx1,dy1,x2,y2,dx2,dy2) { + return (empty_1dcollide(x1,dx1,x2,dx2) && empty_1dcollide(y1,dy1,y2,dy2)) +} + +function empty_1dcollide(x1,dx1,x2,dx2) { + if (x2 >= x1 && x2 < x1+dx1) { + return true; + } + if (x1 >= x2 && x1 < x2+dx2) { + return true; + } + return false; +} diff --git a/frame.html b/frame.html new file mode 100644 index 0000000..d67837c --- /dev/null +++ b/frame.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/game_manager.js b/game_manager.js new file mode 100644 index 0000000..0e89c06 --- /dev/null +++ b/game_manager.js @@ -0,0 +1,417 @@ +PAGE_TITLE="Servers Under Maintenance"; + +FPS=Game.FPS; + + +function cleanSlate() { + GameObject.gameObjects = []; + UIElement.elements = []; + Hitbox.hitboxes = []; + + Game.tutorialServers = []; + + Game.serverLoadGrowth = Game.INITIAL_SERVER_LOAD_GROWTH; + Game.serverLoad = 200; + Game.lastServerLoad = 200; + + Game.money = 0; + + Game.cursor = new Cursor(0,0); +} + +function initMainMenu() { + cleanSlate(); + + Game.gamestate = "menu"; + + new UIText("Servers under Maintenance", 22, 42, "white", "large"); + new AnimatedTextButton(70, 70, "#BEA58C", "Play", "white", "large", () => { + initMainGame(); + }); + new AnimatedTextButton(130, 80, "#49B3BC", "Tutorial", "white", "small", () => { + initTutorial(); + }); +} + + +function initGameOver() { + cleanSlate(); + + Game.gamestate = "gameover"; + + let seconds_full = Math.floor(Game.playedFrames / Game.FPS); + let minutes = Math.floor(seconds_full / 60); + let seconds = (seconds_full % 60).toString().padStart(2, '0'); + + new UIText("Game Over", 90, 42, "white", "large"); + new UIText("Time survived: " + minutes + ":" + seconds, 90, 60, "white", "small"); + new AnimatedTextButton(90, 70, "#BEA58C", "Play Again", "white", "large", () => { + initMainGame(); + }); + new AnimatedTextButton(90, 100, "#49B3BC", "Main Menu", "white", "small", () => { + initMainMenu(); + }); +} + +function initMainGame() { + + cleanSlate(); + + Game.playedFrames = 0; + Game.gamestate = "playing"; + + new Hitbox(0, 0, Game.WIDTH, 32); + new Hitbox(0, 0, 0, Game.HEIGHT); + new Hitbox(0, Game.HEIGHT, Game.WIDTH, 0); + new Hitbox(Game.WIDTH, 0, 0, Game.HEIGHT); + + + Game.player = new Player(Game.WIDTH / 2, Game.HEIGHT / 2 + Game.TILESIZE * 2); + new Inventory(2, Game.HEIGHT - 18); + new EnergyBar(Game.WIDTH - 50, 2, 48, 6); + new ServerLoadBar(Game.WIDTH - 50, 10, 48, 6); + new PlayerCount(Game.WIDTH - 80, 30); + new MoneyLabel(16, 16); + new ServerLoadGauge(Game.WIDTH - 120, 1); + + new Server(5 * Game.TILESIZE, 3 * Game.TILESIZE); + new Server(7 * Game.TILESIZE, 3 * Game.TILESIZE); + new Server(9 * Game.TILESIZE, 3 * Game.TILESIZE); + new Server(5 * Game.TILESIZE, 5 * Game.TILESIZE); + new Server(7 * Game.TILESIZE, 5 * Game.TILESIZE); + new Server(9 * Game.TILESIZE, 5 * Game.TILESIZE); + + new Trashbin(Game.WIDTH - Game.TILESIZE, Game.HEIGHT - Game.TILESIZE * 5); + new CoffeeTable(Game.WIDTH - Game.TILESIZE * 3, Game.HEIGHT - Game.TILESIZE); + new CableTable(Game.WIDTH - Game.TILESIZE * 7, Game.HEIGHT - Game.TILESIZE); + new ShopTable(Game.WIDTH - Game.TILESIZE * 11, Game.HEIGHT - Game.TILESIZE); +} + +function initTutorial() { + cleanSlate(); + + Game.gamestate = "tutorial"; + Game.tutorialStep = 0; + + new Hitbox(0, 0, Game.WIDTH, 32); + new Hitbox(0, 0, 0, Game.HEIGHT); + new Hitbox(0, Game.HEIGHT, Game.WIDTH, 0); + new Hitbox(Game.WIDTH, 0, 0, Game.HEIGHT); + + + Game.player = new Player(Game.WIDTH / 2, Game.HEIGHT / 2); + Game.player.maxEnergy = Game.PLAYER_MAX_ENERGY * 3; + Game.player.inventorySlots = 3; + Game.money = 160; + + Game.tutorialServers.push(new TutorialServer(5 * Game.TILESIZE, 3 * Game.TILESIZE)); + Game.tutorialServers.push(new TutorialServer(7 * Game.TILESIZE, 3 * Game.TILESIZE)); + Game.tutorialServers.push(new TutorialServer(9 * Game.TILESIZE, 3 * Game.TILESIZE)); + + new AnimatedTextButton(0, 32, "#49B3BC", "Main Menu", "white", "small", () => { + initMainMenu(); + }); + + new Dialogue("Welcome to your first day", "in the [redacted] Games data center", tutorialStep); +} + +function tutorialStep() { + Game.tutorialStep++; + + for (let i = 0; i < UIElement.elements.length; i++) { + if (UIElement.elements[i] instanceof Dialogue) { + UIElement.elements[i].delete(); + } + } + + if (Game.tutorialStep == 1) { + new Dialogue("Your task will be to maintain the", "servers as the player count rises", tutorialStep); + } + if (Game.tutorialStep == 2) { + new Dialogue("Servers will inevitably fail and it is your ", "job to fix them before they overload", tutorialStep); + } + if (Game.tutorialStep == 3) { + Game.tutorialServers[0].ui.createMoveCableFailure(); + Game.tutorialServers[0].framesUntilNextFailure = 0; + new Dialogue("Talking of! Looks like that server has a", "problem. Go check it out.", () => {}); + } + if (Game.tutorialStep == 4) { + new Dialogue("Looks like this cable isn't plugged in", "the right connectors.", tutorialStep); + } + if (Game.tutorialStep == 5) { + new Dialogue("Drag and Drop the ends of the cable", "into the marked connectors.", () => { + if (!Game.tutorialServers[0].failure) { + tutorialStep(); + } + }); + } + if (Game.tutorialStep == 6) { + new Dialogue("Good Job! You have successfuly", "repaired your first server.", tutorialStep); + } + if (Game.tutorialStep == 7) { + Game.tutorialServers[0].ui.closeButton.click(); + new Dialogue("Wow that was tiring. It's", "about time for a coffee!", tutorialStep); + } + if (Game.tutorialStep == 8) { + new CoffeeTable(Game.WIDTH - Game.TILESIZE * 3, Game.HEIGHT - Game.TILESIZE); + new Dialogue("Click the Coffee Table in the bottom", "right to make a coffee", tutorialStep); + } + if (Game.tutorialStep == 9) { + new Dialogue("Then click on yourself to drink it", "", () => {}); + } + if (Game.tutorialStep == 10) { + new Dialogue("Wow, tasty!", "", tutorialStep); + } + if (Game.tutorialStep == 11) { + new Dialogue("Should you ever feel tired, you can", "have as much coffee as you'd like", tutorialStep); + } + if (Game.tutorialStep == 12) { + new EnergyBar(Game.WIDTH - 50, 2, 48, 6); + new Dialogue("Make sure you drink some before your", "energy runs out!", tutorialStep); + } + if (Game.tutorialStep == 13) { + setTimeout(() => { + Game.tutorialServers[1].ui.createNewCableFailure(); + Game.tutorialServers[1].framesUntilNextFailure = 0; + new Dialogue("Oh no! Another server just broke down.", "Go check it out!", () => {}); + }, 1000); + } + if (Game.tutorialStep == 14) { + new Dialogue("Hmm, looks like this one is missing", "a cable.", tutorialStep); + } + if (Game.tutorialStep == 15) { + Game.tutorialServers[1].ui.closeButton.click(); + new Dialogue("Go fetch a new one from the table", "at the bottom of the screen.", tutorialStep); + } + if (Game.tutorialStep == 16) { + new Dialogue("Click on the Table to grab a cable", "", tutorialStep); + } + if (Game.tutorialStep == 17) { + new CableTable(Game.WIDTH - Game.TILESIZE * 7, Game.HEIGHT - Game.TILESIZE); + new Inventory(2, Game.HEIGHT - 18); + new Dialogue("Then place it in your inventroy", "in the bottom left", () => {}); + } + if (Game.tutorialStep == 18) { + new Dialogue("Nice, now you have everything you", "need to repair the server", () => {}); + } + if (Game.tutorialStep == 19) { + new Dialogue("Click on the cable in your", "inventory to pick it up", tutorialStep); + } + if (Game.tutorialStep == 20) { + new Dialogue("Then drag it from one connector", "to the other to install it", () => { + if (!Game.tutorialServers[1].failure) { + tutorialStep(); + } + }); + } + if (Game.tutorialStep == 21) { + new MoneyLabel(16, 16); + Game.tutorialServers[1].ui.closeButton.click(); + new Dialogue("Great job. You've earned some", "money already.", tutorialStep); + } + if (Game.tutorialStep == 22) { + new Dialogue("Time to spend some of it in", "the online shop.", tutorialStep); + } + if (Game.tutorialStep == 23) { + new ShopTable(Game.WIDTH - Game.TILESIZE * 11, Game.HEIGHT - Game.TILESIZE); + new Dialogue("You can access it from the", "computer table in the bottom left", () => {}); + } + + if (Game.tutorialStep == 24) { + new Dialogue("Looks like you can afford some", "better shoes.", tutorialStep); + } + if (Game.tutorialStep == 25) { + new Dialogue("Buy them to be able to walk", "for longer without getting tired", () => { + if (Game.player.maxEnergy > Game.PLAYER_MAX_ENERGY * 3) { + tutorialStep(); + } + }); + } + if (Game.tutorialStep == 26) { + new Dialogue("Wow, those look really good", "on you :)", tutorialStep); + } + if (Game.tutorialStep == 27) { + for (let i = 0; i < UIElement.elements.length; i++) { + if (UIElement.elements[i] instanceof Shop) { + UIElement.elements[i].closeButton.click(); + } + } + Game.tutorialServers[2].ui.createBrokenCableFailure(); + Game.tutorialServers[2].framesUntilNextFailure = 0; + new Dialogue("Oh Bollocks! Now the last", "server broke down too.", () => {}); + } + if (Game.tutorialStep == 28) { + new Dialogue("This cable seems to be broken", "You will have to replace it", tutorialStep); + } + if (Game.tutorialStep == 29) { + new Dialogue("Drag the ends on top of one", "another to detach the cable", () => { + if (Game.tutorialServers[2].ui.cables.length == 0 || !Game.tutorialServers[2].failure) { + tutorialStep(); + } + }); + } + if (Game.tutorialStep == 30) { + new Trashbin(Game.WIDTH - Game.TILESIZE, Game.HEIGHT - Game.TILESIZE * 5); + new Dialogue("Go to the trash bin on the right", "to throw this cable out", () => {}); + } + if (Game.tutorialStep == 31) { + new Dialogue("Great, now grab a new cable", "and replace this old one", () => {}); + } + if (Game.tutorialStep == 32) { + Game.tutorialServers[2].ui.closeButton.click(); + new Dialogue("Well done. You're ready to repair", "any server now", tutorialStep); + } + if (Game.tutorialStep == 33) { + new PlayerCount(Game.WIDTH - 80, 30); + new Dialogue("Each server provides capacity for 100", "players to play simultaneausly", tutorialStep); + } + if (Game.tutorialStep == 34) { + new Dialogue("We will need to buy a new one before", "they overload.", tutorialStep); + } + if (Game.tutorialStep == 35) { + Game.money = 320; + new Dialogue("Head to the Computer table to order", "a new server.", () => {}); + } + if (Game.tutorialStep == 36) { + new Dialogue("Now our servers should be able to", "handle all the players.", tutorialStep); + } + if (Game.tutorialStep == 37) { + new Dialogue("As long as they are up and", "running of course", tutorialStep); + } + if (Game.tutorialStep == 38) { + new Dialogue("If the servers break down they can't", "provide any more capacity.", tutorialStep); + } + if (Game.tutorialStep == 39) { + new ServerLoadGauge(Game.WIDTH - 120, 1); + new Dialogue("This gauge tells you how well servers", "are keeping up with demand", tutorialStep); + } + if (Game.tutorialStep == 40) { + new Dialogue("If it goes to the right the servers", "are overloaded", tutorialStep); + } + if (Game.tutorialStep == 41) { + new ServerLoadBar(Game.WIDTH - 50, 10, 48, 6); + new Dialogue("If the servers are overloaded for", "too long, you will be fired.", tutorialStep); + } + if (Game.tutorialStep == 42) { + new Dialogue("Keep them under maximum capacity", "to make sure that doesn't happen", tutorialStep); + } + if (Game.tutorialStep == 43) { + new Dialogue("Good Luck", "", tutorialStep); + } + if (Game.tutorialStep == 44) { + initMainMenu(); + } + + + + + + + // if (Game.tutorialStep == 24) { + // new Dialogue("You are about ready for the", "real deal now.", tutorialStep); + // } + // if (Game.tutorialStep == 25) { + // new ServerLoadGauge(Game.WIDTH - 120, 1); + // new ServerLoadBar(Game.WIDTH - 50, 10, 48, 6); + // new Dialogue("The Server Load Bar and the", "Gauge show you how...", tutorialStep); + // } + // if (Game.tutorialStep == 26) { + // new Dialogue("busy the servers are.", "If the red bar fills up", tutorialStep); + // } + // if (Game.tutorialStep == 27) { + // new Dialogue("all the way, you're fired.", "Good luck.", tutorialStep); + // } +} + +function init() { + document.addEventListener("mousemove", mouseMoveHandler); + Game.c = c; + Game.cv = cv; + + c.imageSmoothingEnabled = false; + Assets.loadAssets(); + resize(); + + initMainMenu(); +} + +function onresize(x, y) { + resize(); +} + +function resize() { + let dpr = window.devicePixelRatio; + let xScale = window.innerWidth * dpr / Game.WIDTH; + let yScale = window.innerHeight * dpr / Game.HEIGHT; + + Game.scale = Math.min(xScale, yScale); + + cv.width = Game.WIDTH; + cv.height = Game.HEIGHT + + cv.style.width = Game.WIDTH * Game.scale; + cv.style.height = Game.HEIGHT * Game.scale; +} + +function drawBackground() { + Game.c.drawImage(Assets.background, 0, 0, Game.WIDTH, 32); + for (let x = 0; x < Game.TILES_X; x++) { + for (let y = 2; y < Game.TILES_Y; y++) { + Game.c.drawImage(Assets.floorTile, x * Game.TILESIZE, y * Game.TILESIZE, Game.TILESIZE, Game.TILESIZE); + } + } +} + +function onKeyDown(e) { + if (Game.gamestate != "menu") { + Game.player.keydown(e); + } +} + +function onKeyUp(e) { + if (Game.gamestate != "menu") { + Game.player.keyup(e); + } +} + +function mouseMoveHandler(e) { + let rect = Game.cv.getBoundingClientRect(); + Game.cursor.x = Math.floor((e.clientX - rect.x) / rect.width * Game.WIDTH); + Game.cursor.y = Math.floor((e.clientY - rect.y) / rect.height * Game.HEIGHT); +} + +function onMouseDown() { + Game.cursor.click(); +} + +function onMouseUp() { + Game.cursor.mouseUp(); +} + +function tick() { + if (Game.gamestate == "menu" || Game.gamestate == "gameover") { + Game.c.drawImage(Assets.menuBackground, 0, 0); + } else { + if (Game.gamestate == "playing") { + if (Game.serverLoad < 0) { + Game.serverLoad = 0; + } + if (Game.serverLoad > Game.MAX_SERVER_LOAD) { + initGameOver(); + } + Game.lastServerLoad = Game.serverLoad; + Game.serverLoad += Game.serverLoadGrowth; + Game.money += Game.PASSIVE_INCOME_PER_PLAYER * Game.serverLoadGrowth; + Game.serverLoadGrowth += 0.02; + + Game.playedFrames++; + } + + GameObject.updateAll(); + + drawBackground(); + } + + GameObject.drawAll(); + UIElement.drawAll(); +} diff --git a/gmtk_2023.zip b/gmtk_2023.zip new file mode 100644 index 0000000..c1b2274 Binary files /dev/null and b/gmtk_2023.zip differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..4e2ff59 --- /dev/null +++ b/index.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/sum.zip b/sum.zip new file mode 100644 index 0000000..c1b2274 Binary files /dev/null and b/sum.zip differ diff --git a/ui.js b/ui.js new file mode 100644 index 0000000..b768548 --- /dev/null +++ b/ui.js @@ -0,0 +1,795 @@ +class UIElement { + static elements = []; + constructor (x, y, w, h, layer = 0) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.layer = layer; + this.isVisible = true; + this.clickable = false; + this.acceptsItem = false; + + UIElement.elements.push(this); + } + + get visible() { + return this.isVisible; + } + + set visible(visible) { + this.isVisible = visible; + } + + delete() { + for (let i = 0; i < UIElement.elements.length; i++) { + if (UIElement.elements[i] == this) { + UIElement.elements.splice(i, 1); + break; + } + } + } + + draw() { + + } + + click() { + + } + + mouseUp() { + + } + + pointInBound(x, y) { + return x >= this.x && y >= this.y && x <= this.x + this.w && y <= this.y + this.h; + } + + static drawAll() { + UIElement.elements.sort((a, b) => (a.layer > b.layer ? 1 : -1)); + for (let i in UIElement.elements) { + if (UIElement.elements[i].visible) + UIElement.elements[i].draw(); + } + } +} + +class Cursor extends UIElement { + constructor(x, y) { + super(x, y, 16, 16, 9999999); + this.cursor = "default"; + this.item = null; + this.placingServer = false; + } + draw() { + this.updateCursor(); + if (this.item != null) { + Game.c.drawImage(Assets.items[this.item], this.x, this.y); + Game.c.globalAlpha = 0.5; + } + if (this.placingServer) { + Game.c.globalAlpha = 0.5; + let sx = Math.floor(this.x / Game.WIDTH * Game.TILES_X) * Game.TILESIZE; + let sy = Math.floor(this.y / Game.HEIGHT * Game.TILES_Y) * Game.TILESIZE; + Game.c.drawImage(Assets.server[0], sx, sy - 16); + Game.c.globalAlpha = 0.3; + if (Hitbox.tempCollidesAny(sx, sy, 16, 16)) { + Game.c.fillStyle = "red"; + } else { + Game.c.fillStyle = "green"; + } + Game.c.fillRect(sx, sy - 8, 16, 24); + Game.c.globalAlpha = 1; + } + Game.c.drawImage(Assets.ui.cursor[this.cursor], this.x, this.y); + Game.c.globalAlpha = 1; + } + updateCursor() { + this.cursor = "default"; + for (let i = UIElement.elements.length - 1; i >= 0; i--) { + if (UIElement.elements[i] != this && UIElement.elements[i].visible && UIElement.elements[i].pointInBound(this.x, this.y)) { + this.cursor = "default"; + return; + } + } + for (let i = GameObject.gameObjects.length -1; i >= 0; i--) { + if (GameObject.gameObjects[i].clickable && GameObject.gameObjects[i].hitbox != undefined && GameObject.gameObjects[i].hitbox.pointInBound(this.x, this.y)) { + if (GameObject.gameObjects[i] instanceof Server && GameObject.gameObjects[i].failure) { + this.cursor = "wrench"; + return; + } + } + } + } + click() { + if (this.placingServer) { + let sx = Math.floor(this.x / Game.WIDTH * Game.TILES_X) * Game.TILESIZE; + let sy = Math.floor(this.y / Game.HEIGHT * Game.TILES_Y) * Game.TILESIZE; + if (!Hitbox.tempCollidesAny(sx, sy, 16, 16)) { + if (Game.gamestate == "tutorial") { + Game.tutorialServers.push(new TutorialServer(sx, sy)); + tutorialStep(); + } else { + new Server(sx, sy); + } + this.placingServer = false; + } + return; + } + for (let i = UIElement.elements.length - 1; i >= 0; i--) { + if (UIElement.elements[i].clickable && UIElement.elements[i].visible) { + if (UIElement.elements[i].pointInBound(this.x, this.y)) { + if (this.item == null) { + UIElement.elements[i].click(); + return; + } else if (UIElement.elements[i].acceptsItem) { + if (UIElement.elements[i].passItem(this.item)) { + this.item = null; + return; + } + } + } + } + } + for (let i = GameObject.gameObjects.length -1; i >= 0; i--) { + if (GameObject.gameObjects[i].clickable && GameObject.gameObjects[i].hitbox != undefined) { + if ( + GameObject.gameObjects[i].hitbox.pointInBound(this.x, this.y) || + GameObject.gameObjects[i] == Game.player && pointInRect(this.x, this.y, Game.player.x, Game.player.y, Game.TILESIZE, Game.TILESIZE) + ) { + + if (this.item == null) { + GameObject.gameObjects[i].click(); + return; + } else if (GameObject.gameObjects[i].acceptsItem) { + if (GameObject.gameObjects[i].passItem(this.item)) { + this.item = null; + return; + } + } + } + } + } + } + mouseUp() { + if (this.item != null) return; + for (let i = UIElement.elements.length - 1; i >= 0; i--) { + if (UIElement.elements[i].clickable && UIElement.elements[i].visible) { + if (UIElement.elements[i].pointInBound(this.x, this.y)) { + UIElement.elements[i].mouseUp(); + return; + } + } + } + for (let i = GameObject.gameObjects.length -1; i >= 0; i--) { + if (GameObject.gameObjects[i].clickable && GameObject.gameObjects[i].hitbox != undefined) { + if (GameObject.gameObjects[i].hitbox.pointInBound(this.x, this.y)) { + GameObject.gameObjects[i].mouseUp(); + return; + } + } + } + } +} + +class UIText extends UIElement { + constructor (text, x, y, col, style = "small" , layer = 0) { + super(x, y, 0, 0, layer); + this.text = text; + this.style = style; + this.col = col; + } + draw() { + Game.c.font = Assets.fonts[this.style].full; + Game.c.fillStyle = this.col; + Game.c.fillText(this.text, this.x, this.y); + } +} + +class ServerUI extends UIElement { + constructor(server) { + super( + Math.floor(Game.WIDTH - 180) / 2, + Math.floor(Game.HEIGHT - 100) / 2, + 180, + 100 + ); + this.server = server; + this.closeButton = new CloseButton(this.x, this.y, () => { + this.visible = false; + Assets.sounds.click.play(); + }, 1); + this.closeButton.visible = false; + + this.connectors = []; + for (let i = 0; i < this.server.numLines; i++) { + for (let j = 0; j < this.server.numConnectors; j++) { + this.connectors.push(new ConnectorUI(this.x + 30 + 25*j, this.y - 7 + this.h / 2 + (i - 0.5) * 50, this)); + } + } + this.cables = []; + let conn1 = Math.floor(Math.random() * this.connectors.length / 2); + let conn2 = Math.floor(Math.random() * this.connectors.length / 2 + this.connectors.length / 2); + this.requiredConnectors = [conn1, conn2]; + this.cables.push(new CableUI( + this.connectors[conn1].x, + this.connectors[conn1].y, + this.connectors[conn2].x, + this.connectors[conn2].y, + "red" + )); + } + + createNewCableFailure() { + if (this.requiredConnectors.length >= 6) return; + for (let i = 0; i < 2; i++) { + let newConn; + do { + newConn = Math.floor(Math.random() * this.connectors.length); + } while (this.requiredConnectors.includes(newConn)); + this.requiredConnectors.push(newConn); + } + } + + createMoveCableFailure() { + let numChanges = Math.floor(Math.random() * 2) + 1; // 50% one cable, 50% two cables + for (let i = 0; i < numChanges; i++) { + let newConn; + do { + newConn = Math.floor(Math.random() * this.connectors.length); // find free spot + } while (this.requiredConnectors.includes(newConn)); + + let changeI = Math.floor(Math.random() * this.requiredConnectors.length); // find index to change + this.requiredConnectors[i] = newConn; + } + } + + createBrokenCableFailure() { + this.cables[Math.floor(Math.random() * this.cables.length)].broken = true; + } + + createFailure() { + if (this.requiredConnectors.length < 6 && Math.random() < 0.1) { // 10% chance new cable + this.createNewCableFailure(); + return; + } else if (Math.random() > 0.3) { // otherwise 30% replace cable, 70% move cable + this.createMoveCableFailure(); + return; + } else { + this.createBrokenCableFailure(); + return; + } + } + + set visible(visible) { + this.isVisible = visible; + this.closeButton.visible = visible; + for (let i = 0; i < this.connectors.length; i++) { + this.connectors[i].visible = visible; + } + for (let i = 0; i < this.cables.length; i++) { + this.cables[i].visible = visible; + } + } + + get visible() { + return this.isVisible; + } + + checkFixed() { + if (!this.server.failure) return; + for (let i = 0; i < this.cables.length; i++) { + if (this.cables[i].broken) { + return; + } + } + for (let i = 0; i < this.requiredConnectors.length; i++) { + let found = false; + for (let j = 0; j < this.cables.length; j++) { + if (this.cables[j].connectors == null) continue; + if ( + this.cables[j].connectors[0].x == this.connectors[this.requiredConnectors[i]].x && + this.cables[j].connectors[0].y == this.connectors[this.requiredConnectors[i]].y + || + this.cables[j].connectors[1].x == this.connectors[this.requiredConnectors[i]].x && + this.cables[j].connectors[1].y == this.connectors[this.requiredConnectors[i]].y + ) { + found = true; + break; + } + } + if (!found) { + return; + } + } + this.server.fix(); + } + + draw() { + this.w = Assets.ui.server.background.width + this.h = Assets.ui.server.background.height; + this.x = Math.floor(Game.WIDTH - this.w) / 2; + this.y = Math.floor(Game.HEIGHT - this.h) / 2; + + this.checkFixed(); + + for (let i = 0; i < this.connectors.length; i++) { + this.connectors[i].required = this.requiredConnectors.includes(i); + } + + this.closeButton.x = this.x + this.w - 15; + this.closeButton.y = this.y + 6; + + Game.c.drawImage(Assets.ui.server.background, this.x, this.y); + + for (let i = 0; i < this.cables.length; i++) { + if (this.cables[i].connectors == null) { + this.cables.splice(i, 1); + i--; + if (Game.player.inventory.length >= Game.player.inventorySlots) { + this.closeButton.click(); + } + } + } + } +} + +class ConnectorUI extends UIElement { + constructor(x, y, parent, layer = 1) { + super(x, y, 13, 13, layer); + this.acceptsItem = true; + this.clickable = true; + this.parent = parent; + this.required = false; + } + draw() { + this.w = Assets.ui.server.connector.width; + this.h = Assets.ui.server.connector.height; + if (this.required) { + c.fillStyle = "red"; + c.fillRect(this.x - 1, this.y - 1, this.w + 2, this.h + 2); + } + c.drawImage(Assets.ui.server.connector, this.x, this.y); + } + passItem(item) { + if (item.startsWith("cable_")) { + let cable = new CableUI(this.x, this.y, Game.cursor.x + 8, Game.cursor.y + 8, item.substr(6)); + cable.connectors[1].dragging = true; + this.parent.cables.push(cable); + Assets.sounds.cable.disconnect.play(); + return true; + } else { + return false; + } + } +} + +class CableConnectorUI extends UIElement { + constructor(x, y, parent, layer = 2) { + super(x, y, 13, 13, layer); + this.dragging = false; + this.clickable = true; + this.parent = parent; + this.partner = null; + } + draw() { + if (this.dragging) { + this.x = Game.cursor.x - 7; + this.y = Game.cursor.y - 7; + } + c.drawImage(Assets.ui.server.cableConnector, this.x, this.y); + } + click() { + this.dragging = true; + this.layer = 3; + Assets.sounds.cable.disconnect.play(); + } + mouseUp() { + if (this.dragging) { + this.layer = 2; + Assets.sounds.cable.connect.play(); + this.dragging = false; + if (this.partner.pointInBound(Game.cursor.x, Game.cursor.y)) { + this.delete(); + this.partner.delete(); + this.parent.delete(); + if (!this.parent.broken) { + Game.cursor.item = "cable_" + this.parent.color; + } else { + if (Game.gamestate == "tutorial") { + Game.player.inventory[0] = "broken_cable"; + tutorialStep(); + } else { + Game.cursor.item = "broken_cable"; + } + } + } + for (let i = 0; i < UIElement.elements.length; i++) { + if (UIElement.elements[i].visible && UIElement.elements[i] instanceof ConnectorUI) { + if (UIElement.elements[i].pointInBound(Game.cursor.x, Game.cursor.y)) { + this.x = UIElement.elements[i].x; + this.y = UIElement.elements[i].y; + return; + } + } + } + } + + } +} + +class CableUI extends UIElement { + constructor(x1, y1, x2, y2, color, layer=3) { + super(0,0,0,0, layer); + this.connectors = [ + new CableConnectorUI(x1, y1, this), + new CableConnectorUI(x2, y2, this) + ]; + this.color = color; + this.connectors[0].partner = this.connectors[1]; + this.connectors[1].partner = this.connectors[0]; + this.broken = false; + } + draw() { + Game.c.strokeStyle = "black"; + Game.c.lineWidth = 4.5; + line(this.connectors[0].x + 7, this.connectors[0].y + 7, this.connectors[1].x + 7, this.connectors[1].y + 7); + if (!this.broken) { + Game.c.strokeStyle = "red"; + Game.c.lineWidth = 3; + line(this.connectors[0].x + 7, this.connectors[0].y + 7, this.connectors[1].x + 7, this.connectors[1].y + 7); + } + } + set visible(visible) { + this.isVisible = visible; + for (let i = 0; i < this.connectors.length; i++) { + this.connectors[i].visible = visible; + } + } + get visible() { + return this.isVisible; + } + delete() { + super.delete(); + this.connectors = null; + } +} + +class Button extends UIElement { + constructor(x, y, w, h, onclick, layer = 1) { + super(x, y, w, h, layer); + this.clickable = true; + this.onclick = onclick; + } + click() { + this.onclick(); + } + draw() { + Game.c.fillStyle = "white"; + Game.c.fillRect(this.x, this.y, this.w, this.h); + } + +} + +class AnimatedTextButton extends Button { + constructor(x, y, color, text, textcolor, textstyle, onclick, layer = 1) { + super(x, y, 0, 0, onclick, layer); + this.text = new UIText(text, x + 3, y + 1 + Assets.fonts[textstyle].size, textcolor, textstyle, this.layer + 1); + this.color = color; + this.clicked = false; + } + draw() { + + Game.c.font = Assets.fonts[this.text.style].full; + this.w = c.measureText(this.text.text).width + 6; + this.h = Assets.fonts[this.text.style].size + 6; + + if (!this.clicked) { + Game.c.fillStyle = this.color; + Game.c.font = Assets.fonts[this.text.style].full; + Game.c.filter = "brightness(0.6)"; + Game.c.fillRect(this.x, this.y, this.w, this.h + 3); + Game.c.filter = "none"; + Game.c.fillRect(this.x, this.y, this.w, this.h); + } else { + Game.c.fillStyle = this.color; + Game.c.font = Assets.fonts[this.text.style].full; + Game.c.fillRect(this.x, this.y + 3, this.w, this.h); + } + } + click() { + this.clicked = true; + this.text.y += 3; + Assets.sounds.click.play(); + setTimeout(() => { + this.onclick(); + this.clicked = false; + this.text.y -= 3; + }, 100); + } + + set visible(visible) { + this.isVisible = visible; + this.text.visible = visible; + } + get visible() { + return this.isVisible; + } + + delete() { + this.text.delete(); + super.delete(); + } +} + +class CloseButton extends Button { + constructor(x, y, onclick, layer = 1) { + super(x, y, 9, 9, onclick, layer); + } + draw() { + c.drawImage(Assets.ui.buttons.close, this.x, this.y); + } +} + +class Inventory extends UIElement { + constructor(x, y, layer = 1) { + super(x, y, 18 * Game.player.inventorySlots, 16, layer); + this.acceptsItem = true; + this.clickable = true; + } + draw() { + this.w = 18 * Game.player.inventorySlots; + this.h = 16; + + Game.c.fillStyle = "white"; + Game.c.strokeStyle = "black" + Game.c.lineWidth = 1; + for (let i = 0; i < Game.player.inventorySlots; i++) { + Game.c.globalAlpha = 0.5; + Game.c.fillRect(this.x + 18 * i, this.y, 16, 16); + Game.c.strokeRect(this.x + 18 * i + 0.5, this.y + 0.5, 15.5, 15.5); + Game.c.globalAlpha = 1; + if (Game.player.inventory.length > i) { + Game.c.drawImage(Assets.items[Game.player.inventory[i]], this.x + 18 * i, this.y); + } + } + } + passItem(item) { + if (Game.player.inventory.length < Game.player.inventorySlots) { + Assets.sounds.player.placeItem.play(); + Game.player.inventory.push(item); + console.log(Game.gamestate); + if (Game.gamestate == "tutorial" && Game.tutorialStep == 17) { + tutorialStep(); + } + return true; + } else { + return false; + } + } + + click() { + for (let i = 0; i < Game.player.inventorySlots; i++) { + if (Game.cursor.x < this.x + 18 * (i + 1)) { + if (Game.player.inventory.length > i) { + Game.cursor.item = Game.player.inventory[i]; + Game.player.inventory.splice(i, 1); + Assets.sounds.player.takeItem.play(); + } + return; + } + } + } +} + +class ProgressBar extends UIElement { + constructor(x, y, w, h, bgcol, fgcol, align = "right", layer = 1) { + super(x, y, w, h, layer); + this.bgcol = bgcol; + this.fgcol = fgcol; + this.value = 0; + this.align = align; + } + draw() { + Game.c.fillStyle = this.bgcol; + Game.c.fillRect(this.x, this.y, this.w, this.h); + Game.c.fillStyle = this.fgcol; + if (this.align == "right") { + Game.c.filter = "brightness(0.6)"; + Game.c.fillRect(this.x + 1, this.y + 1, this.w - 2 - Math.floor((this.w - 2) * (1 - this.value)), this.h - 2); + Game.c.filter = "none"; + Game.c.fillRect(this.x + 1, this.y + 1, this.w - 2 - Math.floor((this.w - 2) * (1 - this.value)), this.h - 3); + } + if (this.align == "left") { + Game.c.filter = "brightness(0.6)"; + Game.c.fillRect(this.x + 1 + Math.floor((this.w - 2) * (1- this.value)), this.y + 1, this.w - 2 - Math.floor((this.w - 2) * (1 - this.value)), this.h - 2 ); + Game.c.filter = "none"; + Game.c.fillRect(this.x + 1 + Math.floor((this.w - 2) * (1- this.value)), this.y + 1, this.w - 2 - Math.floor((this.w - 2) * (1 - this.value)), this.h - 3 ); + } + } +} + +class EnergyBar extends ProgressBar { + constructor(x, y, w, h, layer = 1) { + super(x, y, w, h, "black", "yellow", "left", layer); + } + draw() { + this.value = Game.player.energy / Game.player.maxEnergy; + super.draw(); + } +} + +class ServerLoadBar extends ProgressBar { + constructor(x, y, w, h, layer = 1) { + super(x, y, w, h, "black", "red", "right", layer); + } + draw() { + this.value = Game.serverLoad / Game.MAX_SERVER_LOAD; + super.draw(); + } +} + +class PlayerCount extends UIText { + constructor(x, y) { + super("", x, y, "white", "small", 1); + } + draw() { + this.text = "Players: " + Math.floor(Game.serverLoadGrowth); + super.draw(); + } +} + +class MoneyLabel extends UIText { + constructor(x, y) { + super("$", x, y, "lime", "small", 1); + this.moneyLabel = new UIText(Game.money, x + 8, y, "white", "small", 1); + } + draw() { + super.draw(); + this.moneyLabel.text = Math.floor(Game.money); + } + set visible(visible) { + this.isVisible = visible; + this.moneyLabel.visible = visible; + } + get visible() { + return this.isVisible; + } +} + +class ShopItem extends UIText { + constructor (name, icon, price, x, y, buttonx, buyAction, layer, priceIncrease = 0) { + super(price + "$ - " + name, x + 20, y + 13, "white", "small", layer); + this.icon = icon; + this.price = price; + this.priceIncrease = priceIncrease; + this.buyButton = new AnimatedTextButton(buttonx, this.y - 13, "lime", "Buy", "black", "small", () => { + if (Game.money >= this.price) { + Game.money -= this.price; + if (this.priceIncrease == 0) { + this.buyButton.clickable = false; + this.buyButton.color = "gray"; + this.text = name; + } else { + this.price += this.priceIncrease; + this.text = this.price + "$ - " + name; + } + Assets.sounds.buy.play(); + buyAction(); + } + }, layer); + } + draw() { + super.draw(); + Game.c.drawImage(this.icon, this.x - 20, this.y - 13); + } + set visible(visible) { + this.isVisible = visible; + this.buyButton.visible = visible; + } + get visible() { + return this.isVisible; + } +} + +class Shop extends UIElement { + constructor() { + super(0, 0, 0, 0, 2); + + this.w = 195; + this.h = 5 * 23 + 10; + this.x = Math.floor((Game.WIDTH - this.w) / 2); + this.y = Math.floor((Game.HEIGHT - this.h) / 2); + + this.items = []; + + this.items.push(new ShopItem("Backpack", Assets.items.backpack, 200, this.x + 5, this.y + 5 + 23*1, this.x + 150, () => { Game.player.inventorySlots++; }, this.layer + 1)); + this.items.push(new ShopItem("Big Backpack", Assets.items.big_backpack, 750, this.x + 5, this.y + 5 + 23*3, this.x + 150, () => { Game.player.inventorySlots++; }, this.layer + 1)); + this.items.push(new ShopItem("Work Shoes", Assets.items.shoes, 150, this.x + 5, this.y + 5 + 23*0, this.x + 150, () => { + Game.player.maxEnergy *= 1.75; Game.player.energy = Game.player.maxEnergy; + if (Game.gamestate == "tutorial") { + tutorialStep(); + } + }, this.layer + 1)); + this.items.push(new ShopItem("Comfy Shoes", Assets.items.comfy_shoes, 600, this.x + 5, this.y + 5 + 23*2, this.x + 150, () => { Game.player.maxEnergy *= 1.75; Game.player.energy = Game.player.maxEnergy; }, this.layer + 1)); + + + this.items.push(new ShopItem("Server", Assets.items.server, 300, this.x + 5, this.y + 5 + 23*4, this.x + 150, () => { + this.closeButton.click(); + Game.cursor.placingServer = true; + }, this.layer + 1, 150)); + + this.closeButton = new CloseButton(this.x + 180, this.y + 6, () => { + this.visible = false; + Assets.sounds.click.play(); + }, this.layer + 1); + + } + + draw() { + Game.c.fillStyle = "#4C4C4C"; + Game.c.fillRect(this.x, this.y, this.w, this.h); + } + + set visible(visible) { + this.isVisible = visible; + for (let i = 0; i < this.items.length; i++) { + this.items[i].visible = visible; + } + this.closeButton.visible = visible; + } + get visible() { + return this.isVisible; + } +} + +class ServerLoadGauge extends UIElement { + constructor(x, y, layer = 1) { + super(x, y, 30, 30, layer); + } + + draw() { + this.value = (Game.serverLoad - Game.lastServerLoad) / 400; + if (this.value > 1) { + this.value = 1; + } + if (this.value < -1) { + this.value = -1; + } + let maxAngle = 3.5 * Math.PI / 4; + let angle = -Math.PI / 2 + maxAngle * this.value; + Game.c.strokeStyle = "black"; + Game.c.lineWidth = 3; + let cx = this.x + 14.5; + let cy = this.y + 14.5; + Game.c.drawImage(Assets.ui.gauge, this.x, this.y); + line(cx, cy, cx + Math.cos(angle) * 12, cy + Math.sin(angle) * 12); + } +} + +class Dialogue extends UIElement { + constructor(text1, text2, onclose) { + super(0, Game.HEIGHT - 32, Game.WIDTH, 32, 4); + this.onclose = onclose; + this.text1 = new UIText(text1, 5, this.y + 13, "white", "small", this.layer + 1); + this.text2 = new UIText(text2, 5, this.y + 26, "white", "small", this.layer + 1); + this.button = new AnimatedTextButton(Game.WIDTH - 26, this.y + 8, "#2E48DD", "OK", "white", "small", () => { + this.delete(); + this.onclose(); + }, this.layer + 1); + Assets.sounds.openDialogue.play(); + } + + draw() { + Game.c.fillStyle = "#242936"; + Game.c.fillRect(this.x, this.y, this.w, this.h); + } + + delete() { + this.text1.delete(); + this.text2.delete(); + this.button.delete(); + super.delete(); + } +} \ No newline at end of file