This commit is contained in:
Luca Conte 2023-09-28 18:43:49 +02:00
parent c36be0428c
commit 7d068b8da6
84 changed files with 2299 additions and 0 deletions

6
CREDITS.txt Normal file
View File

@ -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

BIN
assets/Pixellari.ttf Normal file

Binary file not shown.

BIN
assets/Pixolletta8px.ttf Normal file

Binary file not shown.

BIN
assets/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
assets/cursor/cursor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

BIN
assets/cursor/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

BIN
assets/cursor/wrench.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

BIN
assets/floor_tile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

BIN
assets/items/backpack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

BIN
assets/items/cable_red.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

BIN
assets/items/coffee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

BIN
assets/items/server.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

BIN
assets/items/shoes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

BIN
assets/menu_background.pdn Normal file

Binary file not shown.

BIN
assets/menu_background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
assets/player/idle_d.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

BIN
assets/player/idle_l.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

BIN
assets/player/idle_r.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

BIN
assets/player/idle_u.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

BIN
assets/player/walk_d_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

BIN
assets/player/walk_d_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

BIN
assets/player/walk_l_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

BIN
assets/player/walk_l_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

BIN
assets/player/walk_r_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

BIN
assets/player/walk_r_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

BIN
assets/player/walk_u_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

BIN
assets/player/walk_u_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

BIN
assets/server.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

BIN
assets/server/server_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

BIN
assets/server/server_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

BIN
assets/server/server_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

BIN
assets/server/server_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

BIN
assets/shop_table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

BIN
assets/sounds/buy.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/click.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/spark.wav Normal file

Binary file not shown.

BIN
assets/sounds/step.wav Normal file

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/take_item.wav Normal file

Binary file not shown.

BIN
assets/sounds/trash.wav Normal file

Binary file not shown.

BIN
assets/table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

BIN
assets/table_cables.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

BIN
assets/table_coffee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

BIN
assets/trashbin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

BIN
assets/ui/close_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

BIN
assets/ui/connector.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

BIN
assets/ui/gauge.pdn Normal file

Binary file not shown.

BIN
assets/ui/gauge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

BIN
assets/ui/server_rack.pdn Normal file

Binary file not shown.

BIN
assets/ui/server_rack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
assets/ui/ui_frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

195
classes/Assets.js Normal file
View File

@ -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);
}
}

50
classes/Game.js Normal file
View File

@ -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;
}
}

57
classes/GameObject.js Normal file
View File

@ -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();
}
}
}

45
classes/Hitbox.js Normal file
View File

@ -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;
}
}

137
classes/Player.js Normal file
View File

@ -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;
}
}

107
classes/Server.js Normal file
View File

@ -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() {
}
}

55
classes/Tables.js Normal file
View File

@ -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();
}
}
}
}

21
classes/Trashbin.js Normal file
View File

@ -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;
}
}

389
empty.js Normal file
View File

@ -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; i<j; i++) {
arr.push(i);
}
return arr;
}
function minmax(num, min, max) {
if (num>max) return max;
if (num<min) return min;
return num;
}
function rgb(r,g,b) { //COLOR FUNCTIONS
return "rgb(" + r + "," + g + "," + b +")";
}
function rgba(r,g,b,a) {
return "rgba(" + r + "," + g + "," + b +"," + a + ")";
}
function hsl(h,s,l) {
return "hsl(" + h + "," + s + "%," + l +"%)";
}
function hsla(h,s,l,a) {
return "hsla(" + h + "," + s + "%," + l +"%," + a +")";
}
function closestPointOnLine(l1x, l1y, l2x, l2y, px, py) {
var bb = Math.atan2(py - l1y, px - l1x);
var ll = Math.atan2(l2y - l1y, l2x - l1x);
var ab=ll-bb;
var d=Math.cos(ab)*distance(l1x, l1y, px, py);
d = minmax(d, 0, distance(l1x, l1y, l2x, l2y));
var x=Math.cos(ll)*d + l1x;
var y=Math.sin(ll)*d + l1y;
return {x:x, y:y};
}
function lineCircleIntersect(l1x, l1y, l2x, l2y, cx, cy, cr) {
var p =closestPointOnLine(l1x, l1y, l2x, l2y, cx, cy)
return pointInCircle(p.x,p.y,cx,cy,cr);
}
function pointInRect(px,py,rx,ry,rw,rh) {
return px>=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;
}

1
frame.html Normal file
View File

@ -0,0 +1 @@
<iframe src="index.html"></iframe>

417
game_manager.js Normal file
View File

@ -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();
}

BIN
gmtk_2023.zip Normal file

Binary file not shown.

24
index.html Normal file
View File

@ -0,0 +1,24 @@
<script src="empty.js"></script>
<script src="classes/Game.js"></script>
<script src="classes/Assets.js"></script>
<script src="classes/GameObject.js"></script>
<script src="classes/Trashbin.js"></script>
<script src="classes/Tables.js"></script>
<script src="classes/Player.js"></script>
<script src="classes/Server.js"></script>
<script src="classes/Hitbox.js"></script>
<script src="ui.js"></script>
<script src="game_manager.js"></script>
<style>
body {
background-color: white;
display: flex;
align-items: center;
justify-content: center;
}
canvas {
image-rendering: pixelated;
image-rendering: crisp-edges;
cursor: url('assets/cursor/cursor.png'), auto;
}
</style>

BIN
sum.zip Normal file

Binary file not shown.

795
ui.js Normal file
View File

@ -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();
}
}