diff --git a/public_html/ServerEventsHandlers.js b/public_html/ServerEventsHandlers.js
new file mode 100644
index 0000000..63fccf2
--- /dev/null
+++ b/public_html/ServerEventsHandlers.js
@@ -0,0 +1,348 @@
+class ServerEventsHandlers {
+ constructor(game) {
+ this.game = game;
+ }
+
+ version(data) {
+ const servVersion = data[0];
+ if(servVersion == REVISION) {
+ return;
+ }
+
+ alert(strings["bad_version"]);
+ this.game.conn.close();
+ this.game.conn = null;
+ }
+
+ err(data) {
+ alert(strings[data[0]]);
+ this.game.conn.close();
+ this.game.conn = null;
+ }
+
+ exploded(data) {
+ const nickname = data[0];
+ this.game.console("" + nickname + " drew a Detonating Cat!");
+
+ cardHUD("exploding", 1000);
+ }
+
+ // Legacy ??
+ card_left(data) {
+ $("#remaining-card-count").text(data[0]);
+ }
+
+ now_playing(data) {
+ this.game.nowPlaying = data[0];
+ this.game.console("It is " + this.game.nowPlaying + "'s turn.");
+
+ this.game.drawPlayerList();
+
+ // cmp with raw data because name isn't stored encoded
+ if (data[0] == this.game.name) {
+ this.game.ourTurn = true;
+ this.game.assets["atomic.ogg"].play();
+ document.title = strings["title_alert"];
+ $(".bottom-notify-container").html(strings["your_turn"]);
+ } else {
+ this.game.ourTurn = false;
+ document.title = strings["title_normal"]; // a bit fragile, needs to be same as
+ $(".bottom-notify-container").html("");
+ }
+ }
+
+ players(data) {
+ // Update players list
+ this.game.players = data;
+ this.game.spectators = this.game.spectators.filter(i => !this.game.players.includes(i));
+ this.game.drawPlayerList();
+ this.game.drawSpectatorsList();
+ }
+
+ spectators(data) {
+ // Update spectators list
+ this.game.spectators = data.filter(i => !this.game.players.includes(i));
+ this.game.drawSpectatorsList();
+ }
+
+ joins(data) {
+ const nickname = data[0];
+ if(nickname == this.game.name && !this.game.started) {
+ return this.game.start();
+ }
+
+ this.game.console("" + nickname + " joined as a spectator.");
+ this.game.addSpectator(nickname);
+ }
+
+ upgrades(data) {
+ const nickname = data[0];
+ this.game.console("" + nickname + " is now spectating.");
+ this.game.removeSpectator(nickname);
+ }
+
+ downgrades(data) {
+ const nickname = data[0];
+ this.game.console("" + nickname + " is now playing.");
+ this.game.addSpectator(nickname);
+ }
+
+ parts(data) {
+ const nickname = data[0];
+ this.game.console("" + nickname + " left the game.");
+
+ this.game.removeSpectator(nickname);
+ }
+
+ chat(data) {
+ const nickname = data[0];
+ const msg = data.join(" ").substring(nickname.length);
+ this.game.console(`${nickname}: ${msg}`);
+ }
+
+ message(data) {
+ // This refers to the 'message' container in the middle of the board
+ // that can be used to display useful game info
+ const msg = strings["message_" + data[0]];
+ $("#message").html(msg);
+ $("#message-container").removeClass("reveal");
+ }
+
+ clear_message(data) {
+ $("#message").html("");
+ $("#message-container").addClass("reveal");
+ }
+
+ bcast(data) {
+ let msg = strings["bcast_" + data[0]];
+ this.game.console(msg);
+ }
+
+ hand(data) {
+ this.game.hand = data;
+ $("#card-deck").empty();
+
+ for (let i=0; i < this.game.hand.length; i++) {
+ let card = $("");
+
+ (function (gameState, cardNo, cardName) {
+ card.on("click", function() {
+ if (gameState.locked) {
+ return;
+ }
+
+ if (gameState.favouring) {
+ // Favour NOPE-logic is handled server-side :)
+ gameState.send("a favour_what "+cardNo.toString());
+ gameState.favouring = false;
+ return;
+ }
+
+ if (gameState.combo > 1) {
+ // Do we have enough cards?
+ let cards = 0;
+ for (var j=1; j < parts.length; j++) {
+ if (parts[j] == cardName) {
+ cards++;
+ if (cards == gameState.combo) {
+ break;
+ }
+ }
+ }
+ if (cards < gameState.combo) {
+ gameState.console("You don't have enough " + strings["card_"+cardName] +
+ " cards to do that!");
+ return;
+ }
+
+ if (gameState.ourTurn) {
+ gameState.send("play_multiple "+gameState.combo.toString()+" "+
+ cardName);
+ gameState.resetButtons();
+ return;
+ }
+ }
+
+ if ((gameState.ourTurn || cardName === "nope") && !cardName.startsWith("random")
+ && ( (!gameState.defusing && cardName !== "defuse")
+ || (gameState.defusing && cardName === "defuse") )) {
+ gameState.send("play "+cardNo.toString());
+ gameState.defusing = false;
+ }
+ });
+ })(this.game, i, this.game.hand[i]);
+
+ $("#card-deck").append(card);
+ }
+ }
+
+ draw_pile(data) {
+ const flag = data[0] == "yes" ? true : false;
+
+ if(flag) {
+ $("#draw-pile").html("");
+ $("#draw-pile-counter").removeClass("reveal");
+ } else {
+ $("#draw-pile").html("");
+ $("#draw-pile-counter").addClass("reveal");
+ }
+ }
+
+ drew(data) {
+ this.game.console("You drew " + strings["card_" + data[0]] + ".");
+ cardHUD(parts[1], 2000);
+ }
+
+ drew_other(data) {
+ const nickname = data[0];
+ this.game.console("" + nickname + " drew a card.");
+
+ // Animation
+ $("#draw-pile-animation").html("");
+ animate("#draw-pile-animation", "right", 125, -150, -15, "px");
+ }
+
+ wins(data) {
+ const nickname = data[0];
+ this.game.ourTurn = false;
+ this.game.console("" + nickname + " won!");
+ this.game.nowPlaying = "";
+ }
+
+ defusing(data) {
+ this.game.console(strings["must_defuse"]);
+ this.game.defusing = true;
+ }
+
+ played(data) {
+ const [ nickname, card ] = data;
+ this.game.console(nickname + " played " + strings["card_" + card] + ".");
+
+ $("#discard-pile").html("");
+
+ if (card != "see3") {
+ cardHUD(card, 1000);
+ }
+ }
+
+ played_multiple(data) {
+ const [nickname, x, card] = data;
+ this.game.console(nickname + " played " + x + "x " + strings["card_" + card] + ".");
+
+ $("#discard-pile").html("");
+
+ if (x == 2) {
+ cardHUD3([card, card], 1000);
+ } else {
+ cardHUD3([card, card, card], 1000);
+ }
+ }
+
+ no_discard(data) {
+ $("#discard-pile").html("");
+ }
+
+ q(data) {
+ const type = data[0];
+ if (type == "favour_what") {
+ this.game.favouring = true;
+ let perpetrator = data[1];
+ this.game.console("" + perpetrator +
+ " is asking you for a favour.");
+ } else if (type == "favour_who" || type == "random_who" || type == "steal_who") {
+ (function (gameState) {
+ modalChoice(function(player) {
+ gameState.send("a " + type + " " + player);
+ }, strings["question_" + type], gameState.players, entities(gameState.name), null);
+ })(this.game);
+ } else if (type == "steal_what") {
+ (function (gameState) {
+ modalChoice(function(card) {
+ gameState.send("a " + type + " " + card);
+ }, strings["question_" + type], cards, null, x => strings["card_" + x]);
+ })(this.game);
+ } else {
+ ans = prompt(strings["question_" + type]);
+ this.game.send("a " + type + " " + ans);
+ }
+ }
+
+ q_cancel(data) {
+ this.game.favouring = false;
+ }
+
+ seen(data) {
+ cardHUD3(data, 2000);
+ this.game.console("You saw " + strings["card_" + data[0]] + ", " + strings["card_" + data[1]] + " and " +
+ strings["card_" + data[2]] + ".");
+ }
+
+ favoured(data) {
+ const [ perpetrator, victim ] = data;
+ this.game.console(perpetrator + " is asking " + victim + " for a favour.");
+ }
+
+ favour_complete(data) {
+ const [ perpetrator, victim ] = data;
+ this.game.console(victim + " gave " + perpetrator + " a favour.");
+ }
+
+ favour_recv(data) {
+ const remotePlayer = data[0];
+ const card = strings["card_" + data[1]];
+ this.game.console(remotePlayer + " gave you " + card + ".");
+ cardHUD(data[1], 2000);
+ }
+
+ favour_gave(data) {
+ const remotePlayer = data[0];
+ const card = strings["card_" + data[1]];
+ this.game.console("You gave " + remotePlayer + " " + card + ".");
+ }
+
+ randomed(data) {
+ const [ perpetrator, victim ] = data;
+ this.game.console(perpetrator + " took a random card from " + victim + ".");
+ }
+
+ random_n(data) {
+ const [ perpetrator, victim ] = data;
+ this.game.console(perpetrator + " asked " + victim + " for a random card, but they had nothing to give away!");
+ }
+
+ random_recv(data) {
+ const remotePlayer = data[0];
+ const card = strings["card_" + data[1]];
+
+ this.game.console("You randomly took " + card + " from " + remotePlayer + ".");
+ cardHUD(data[1], 2000);
+ }
+
+ random_gave(data) {
+ const remotePlayer = data[0];
+ const card = strings["card_" + data[1]];
+ this.game.console(remotePlayer + " randomly took " + card + " from you.");
+ cardHUD(data[1], 2000);
+ }
+
+ steal_n(data) {
+ const [ perpetrator, victim, card ] = data;
+ this.game.console(perpetrator + " asked " + victim + " for "
+ + strings["card_" + card] + ", but ended up empty-handed!");
+ }
+
+ steal_y(data) {
+ const [ perpetrator, victim, card ] = data;
+ this.game.console(perpetrator + " stole " +
+ strings["card_" + card]+" from " + victim + "!");
+ cardHUD(card, 2000);
+ }
+
+ lock(data) {
+ this.game.locked = true;
+ }
+
+ unlock(data) {
+ this.game.locked = false;
+ }
+}
\ No newline at end of file
diff --git a/public_html/game.js b/public_html/game.js
index e5f86fb..a8266e0 100644
--- a/public_html/game.js
+++ b/public_html/game.js
@@ -17,6 +17,8 @@
"#89A666", "#7365A6", "#8C1F1F"
];
+ this.servEvHandlers = new ServerEventsHandlers(this);
+
// Player and lobby name
this.name = "";
@@ -25,6 +27,7 @@
this.nowPlaying = "";
this.players = [];
this.spectators = [];
+ this.hand = [];
// Assets
@@ -216,410 +219,19 @@
this.readFromServer = function(ev) {
var parts = ev.data.split(" ");
- // TODO: cleanup and refactor
-
- if (parts[0] == "err") {
- alert(strings[parts[1]]);
- this.conn.close();
- this.conn = null;
- return;
- }
- if (parts[0] == "version" && parts[1] != REVISION) {
- alert(strings["bad_version"]);
- this.conn.close();
- this.conn = null;
- return;
- }
-
- if (parts[0] == "joins" && parts[1] == this.name) {
- // We're in!
- if (!this.started) {
- this.start();
- }
- return;
- }
-
- if (parts[0] == "players") {
- // Update players list
- this.players = parts.slice(1).map(x => entities(x));
- this.spectators = this.spectators.filter(i => !this.players.includes(i));
- this.drawPlayerList();
- this.drawSpectatorsList();
-
- return;
- }
-
- if (parts[0] == "spectators") {
- // Update spectators list
- this.spectators = parts.slice(1).map(x => entities(x));
- this.drawSpectatorsList();
-
- return;
- }
-
- if (parts[0] == "joins") {
- // Announce in console
- let encoded = entities(parts[1]);
- this.console(""+encoded+" joined as a spectator.");
-
- // Update spectators list
- $("#spectator-list").append(""+encoded+"");
-
- return;
- }
-
- if (parts[0] == "parts") {
- // Announce in console
- let encoded = entities(parts[1]);
- this.console(""+encoded+" left the game.");
-
- // Update spectators list if appropriate
- $("#spectator-list > li").each( function() {
- if ($( this ).html() == encoded) {
- $( this ).remove();
- }
- } );
-
- return;
- }
-
- if (parts[0] == "upgrades") {
- // Announce in console
- let encoded = entities(parts[1]);
- this.console(""+encoded+" is now playing.");
-
- // Update spectators list
- $("#spectator-list > li").each( function() {
- if ($( this ).html() == encoded) {
- $( this ).remove();
- }
- } );
-
- // Server will send players list separately, no worries
-
- return;
- }
-
- if (parts[0] == "downgrades") {
- // Announce in console
- let encoded = entities(parts[1]);
- this.console(""+encoded+" is now spectating.");
-
- // Update spectators list
- $("#spectator-list").append(""+encoded+"");
-
- // Server will send players list separately, no worries
-
- return;
- }
-
- if (parts[0] == "chat") {
- let encodedName = entities(parts[1]);
- let encodedMsg = entities(ev.data.substring(parts[1].length + 5));
- this.console(encodedName+": "+encodedMsg);
-
- return;
- }
-
- if (parts[0] == "message") {
- // This refers to the 'message' container in the middle of the board
- // that can be used to display useful game info
- let msg = strings["message_"+ev.data.substring(8)];
- $("#message").html(msg);
- $("#message-container").removeClass("reveal");
-
- return;
- }
-
- if (parts[0] == "clear_message") {
- $("#message").html("");
- $("#message-container").addClass("reveal");
-
- return;
- }
-
- if (parts[0] == "bcast") {
- let msg = strings["bcast_"+ev.data.substring(6)];
- this.console(msg);
-
- return;
- }
-
- if (parts[0] == "hand") {
- $("#card-deck").empty();
-
- for (var i=1; i < parts.length; i++) {
- var card = $("");
-
- (function (gameState, cardNo, cardName) {
- card.on("click", function() {
- if (gameState.locked) {
- return;
- }
-
- if (gameState.favouring) {
- // Favour NOPE-logic is handled server-side :)
- gameState.send("a favour_what "+cardNo.toString());
- gameState.favouring = false;
- return;
- }
-
- if (gameState.combo > 1) {
- // Do we have enough cards?
- let cards = 0;
- for (var j=1; j < parts.length; j++) {
- if (parts[j] == cardName) {
- cards++;
- if (cards == gameState.combo) {
- break;
- }
- }
- }
- if (cards < gameState.combo) {
- gameState.console("You don't have enough " + strings["card_"+cardName] +
- " cards to do that!");
- return;
- }
-
- if (gameState.ourTurn) {
- gameState.send("play_multiple "+gameState.combo.toString()+" "+
- cardName);
- gameState.resetButtons();
- return;
- }
- }
-
- if ((gameState.ourTurn || cardName === "nope") && !cardName.startsWith("random")
- && ( (!gameState.defusing && cardName !== "defuse")
- || (gameState.defusing && cardName === "defuse") )) {
- gameState.send("play "+cardNo.toString());
- gameState.defusing = false;
- }
- });
- })(this, i-1, parts[i]);
-
- $("#card-deck").append(card);
- }
-
- return;
- }
-
- if (ev.data == "draw_pile yes") {
- $("#draw-pile").html("");
- $("#draw-pile-counter").removeClass("reveal");
- return;
- }
- if (ev.data == "draw_pile no") {
- $("#draw-pile").html("");
- $("#draw-pile-counter").addClass("reveal");
- return;
- }
- if (parts[0] == "cards_left") {
- $("#remaining-card-count").text(parts[1]);
- return;
- }
-
- if (parts[0] == "now_playing") {
- this.nowPlaying = entities(parts[1]);
- this.console("It is "+this.nowPlaying+"'s turn.");
-
- this.drawPlayerList();
-
- // cmp with raw data because name isn't stored encoded
- if (parts[1] == this.name) {
- this.ourTurn = true;
- this.assets["atomic.ogg"].play();
- document.title = strings["title_alert"];
- $(".bottom-notify-container").html(strings["your_turn"]);
- } else {
- this.ourTurn = false;
- document.title = strings["title_normal"]; // a bit fragile, needs to be same as
- $(".bottom-notify-container").html("");
- }
-
- return;
- }
-
- if (parts[0] == "drew") {
- this.console("You drew "+strings["card_"+parts[1]]+".");
-
- // Animation
- cardHUD(parts[1], 2000);
-
- return;
- }
- if (parts[0] == "drew_other") {
- let encoded = entities(parts[1]);
- this.console(""+encoded+" drew a card.");
-
- // Animation
- $("#draw-pile-animation").html("");
- animate("#draw-pile-animation", "right", 125, -150, -15, "px");
-
- return;
- }
-
- if (parts[0] == "exploded") {
- let encoded = entities(parts[1]);
- this.console(""+encoded+" drew a Detonating Cat!");
-
- cardHUD("exploding", 1000);
-
- return;
- }
-
- if (parts[0] == "wins") {
- this.ourTurn = false;
- let encoded = entities(parts[1]);
- this.console(""+encoded+" won!");
- this.nowPlaying = "";
- return;
- }
-
- if (parts[0] == "defusing") {
- this.console(strings["must_defuse"]);
- this.defusing = true;
- return;
- }
-
- if (parts[0] == "played") {
- let encoded = entities(parts[1]);
- this.console(encoded+" played "+strings["card_"+parts[2]]+".");
-
- $("#discard-pile").html("");
-
- if (parts[2] != "see3") {
- cardHUD(parts[2], 1000);
- }
-
- return;
- }
-
- if (parts[0] == "played_multiple") {
- let encoded = entities(parts[1]);
- this.console(encoded+" played "+parts[2]+"x "+strings["card_"+parts[3]]+".");
-
- $("#discard-pile").html("");
-
- if (parts[2] == 2) {
- cardHUD3([parts[3], parts[3]], 1000);
- } else {
- cardHUD3([parts[3], parts[3], parts[3]], 1000);
- }
-
- return;
- }
-
- if (parts[0] == "no_discard") {
- $("#discard-pile").html("");
- return;
- }
-
- if (parts[0] == "q") {
- if (parts[1] == "favour_what") {
- this.favouring = true;
- let perpetrator = entities(parts[2]);
- this.console("" + perpetrator +
- " is asking you for a favour.");
- } else if (parts[1] == "favour_who" || parts[1] == "random_who" || parts[1] == "steal_who") {
- (function (gameState) {
- modalChoice(function(player) {
- gameState.send("a "+parts[1]+" "+player);
- }, strings["question_"+parts[1]], gameState.players, entities(gameState.name), null);
- })(this);
- } else if (parts[1] == "steal_what") {
- (function (gameState) {
- modalChoice(function(card) {
- gameState.send("a "+parts[1]+" "+card);
- }, strings["question_"+parts[1]], cards, null, x => strings["card_"+x]);
- })(this);
- } else {
- ans = prompt(strings["question_"+parts[1]]);
- this.send("a "+parts[1]+" "+ans);
- }
-
- return;
- }
- if (parts[0] == "q_cancel") {
- this.favouring = false;
- }
-
- if (parts[0] == "seen") {
- cardHUD3(parts.slice(1), 2000);
- this.console("You saw "+strings["card_"+parts[1]]+", "+strings["card_"+parts[2]]+" and "+
- strings["card_"+parts[3]]+".");
- return;
- }
-
- if (parts[0] == "favoured" || parts[0] == "favour_complete") {
- let perpetrator = entities(parts[1]);
- let victim = entities(parts[2]);
- if (parts[0] == "favoured") {
- this.console(perpetrator+" is asking "+victim+" for a favour.");
- } else {
- this.console(victim+" gave "+perpetrator+" a favour.");
- }
- return;
- }
- if (parts[0] == "favour_recv" || parts[0] == "favour_gave") {
- let remotePlayer = entities(parts[1]);
- let card = strings["card_"+parts[2]];
- if (parts[0] == "favour_recv") {
- this.console(remotePlayer + " gave you " + card + ".");
- cardHUD(parts[2], 2000);
- } else {
- this.console("You gave " + remotePlayer + " " + card + ".");
- }
- return;
- }
-
- if (parts[0] == "randomed" || parts[0] == "random_n") {
- let perpetrator = entities(parts[1]);
- let victim = entities(parts[2]);
- if (parts[0] == "randomed") {
- this.console(perpetrator+" took a random card from "+victim+".");
- } else {
- this.console(perpetrator+" asked "+victim+
- " for a random card, but they had nothing to give away!");
- }
-
- return;
- }
- if (parts[0] == "random_recv" || parts[0] == "random_gave") {
- let remotePlayer = entities(parts[1]);
- let card = strings["card_"+parts[2]];
- if (parts[0] == "random_recv") {
- this.console("You randomly took "+card+" from "+
- remotePlayer+".");
- } else {
- this.console(remotePlayer+" randomly took "+card+
- " from you.");
- }
- cardHUD(parts[2], 2000);
- return;
- }
-
- if (parts[0] == "steal_n" || parts[0] == "steal_y") {
- let perpetrator = entities(parts[1]);
- let victim = entities(parts[2]);
- if (parts[0] == "steal_y") {
- this.console(perpetrator+" stole "+
- strings["card_"+parts[3]]+" from "+victim+"!");
- cardHUD(parts[3], 2000);
- } else {
- this.console(perpetrator+" asked "+victim+" for "
- +strings["card_"+parts[3]]+", but ended up empty-handed!");
- }
- return;
- }
-
- if (parts[0] == "lock") {
- this.locked = true;
- return;
- }
- if (parts[0] == "unlock") {
- this.locked = false;
- return;
+ const eventName = parts[0];
+ console.log('Server Event', parts);
+
+ if(
+ parts.length
+ && typeof eventName == "string"
+ && eventName.length
+ && eventName in this.servEvHandlers
+ && typeof this.servEvHandlers[eventName] == "function"
+ ) {
+ parts.splice(0, 1).map(i => entities(i));
+ const eventData = parts;
+ return this.servEvHandlers[eventName](eventData);
}
console.warn("WARN: received unknown data from server: "+ev.data);
@@ -628,4 +240,27 @@
this.send = function(msg) {
this.conn.send(msg);
}
+
+ this.addSpectator = function(nickname) {
+ this.freePlayerColor(nickname);
+ this.spectators.push(nickname);
+ this.drawSpectatorsList();
+ }
+
+ this.removeSpectator = function(nickname) {
+ this.spectators.splice(this.spectators.indexOf(nickname), 1);
+ this.drawSpectatorsList();
+ }
+
+ this.addPlayer = function(nickname) { }
+
+ this.removePlayer = function(nickname) { }
+
+ this.freePlayerColor = function(nickname) {
+ if(nickname in this.playerColorMap) {
+ const color = this.playerColorMap[nickname];
+ delete this.playerColorMap[nickname];
+ this.playerColors.push(color);
+ }
+ }
}
diff --git a/public_html/index.html b/public_html/index.html
index 775dc9e..0921b76 100644
--- a/public_html/index.html
+++ b/public_html/index.html
@@ -2,6 +2,7 @@
+
Detonating Cats
@@ -139,6 +140,7 @@
+