Merge branch 'spectator' of https://github.com/uecasm/PretendYoureXyzzy into uecasm-spectator
This commit is contained in:
commit
4c686010c2
|
@ -137,7 +137,17 @@ h2,h3,h4 {
|
||||||
|
|
||||||
.gamelist_lobby_join {
|
.gamelist_lobby_join {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 75%;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
background: linear-gradient(#fff, #ddd) #ddd;
|
||||||
|
border-radius: .25em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamelist_lobby_spectate {
|
||||||
|
width: 100%;
|
||||||
|
height: 25%;
|
||||||
border: 1px solid #aaa;
|
border: 1px solid #aaa;
|
||||||
background: linear-gradient(#fff, #ddd) #ddd;
|
background: linear-gradient(#fff, #ddd) #ddd;
|
||||||
border-radius: .25em;
|
border-radius: .25em;
|
||||||
|
|
|
@ -183,13 +183,18 @@ HttpSession hSession = request.getSession(true);
|
||||||
<div class="gamelist_lobby_left">
|
<div class="gamelist_lobby_left">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="gamelist_lobby_host">host</span>'s Game
|
<span class="gamelist_lobby_host">host</span>'s Game
|
||||||
(<span class="gamelist_lobby_player_count"></span>/<span class="gamelist_lobby_max_players"></span>)
|
(<span class="gamelist_lobby_player_count"></span>/<span class="gamelist_lobby_max_players"></span>,
|
||||||
|
<span class="gamelist_lobby_spectator_count"></span>/<span class="gamelist_lobby_max_spectators"></span>)
|
||||||
<span class="gamelist_lobby_status">status</span>
|
<span class="gamelist_lobby_status">status</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
<strong>Players:</strong>
|
<strong>Players:</strong>
|
||||||
<span class="gamelist_lobby_players">host, player1, player2</span>
|
<span class="gamelist_lobby_players">host, player1, player2</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Spectators:</strong>
|
||||||
|
<span class="gamelist_lobby_spectators">spectator1</span>
|
||||||
|
</div>
|
||||||
<div><strong>Goal:</strong> <span class="gamelist_lobby_goal"></span></div>
|
<div><strong>Goal:</strong> <span class="gamelist_lobby_goal"></span></div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Cards:</strong> <span class="gamelist_lobby_cardset"></span>
|
<strong>Cards:</strong> <span class="gamelist_lobby_cardset"></span>
|
||||||
|
@ -198,6 +203,7 @@ HttpSession hSession = request.getSession(true);
|
||||||
</div>
|
</div>
|
||||||
<div class="gamelist_lobby_right">
|
<div class="gamelist_lobby_right">
|
||||||
<input type="button" class="gamelist_lobby_join" value="Join" />
|
<input type="button" class="gamelist_lobby_join" value="Join" />
|
||||||
|
<input type="button" class="gamelist_lobby_spectate" value="View" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -317,7 +323,7 @@ HttpSession hSession = request.getSession(true);
|
||||||
<div id="scorecard_template" class="scorecard" tabindex="0">
|
<div id="scorecard_template" class="scorecard" tabindex="0">
|
||||||
<span class="scorecard_player">PlayerName</span>
|
<span class="scorecard_player">PlayerName</span>
|
||||||
<div class="clear"></div>
|
<div class="clear"></div>
|
||||||
<span class="scorecard_score">0</span> <span class="scorecard_point_title">Awesome Point<span class="scorecard_s">s</span></span>
|
<span class="scorecard_points"><span class="scorecard_score">0</span> <span class="scorecard_point_title">Awesome Point<span class="scorecard_s">s</span></span></span>
|
||||||
<span class="scorecard_status">Status</span>
|
<span class="scorecard_status">Status</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -360,6 +366,15 @@ HttpSession hSession = request.getSession(true);
|
||||||
</select>
|
</select>
|
||||||
Having more than 10 players may get cramped!
|
Having more than 10 players may get cramped!
|
||||||
<br/>
|
<br/>
|
||||||
|
<label id="spectator_limit_template_label" for="spectator_limit_template">Spectator limit:</label>
|
||||||
|
<select id="spectator_limit_template" class="spectator_limit"
|
||||||
|
aria-label="Spectator limit.">
|
||||||
|
<% int defaultSpectatorLimit = 10; for (int i = 0; i <= 20; i++) { %>
|
||||||
|
<option <%= i == defaultSpectatorLimit ? "selected='selected' " : "" %>value="<%= i %>"><%= i %></option>
|
||||||
|
<% } %>
|
||||||
|
</select>
|
||||||
|
Spectators can watch and chat, but not actually play. Not even as Czar.
|
||||||
|
<br/>
|
||||||
<input type="checkbox" checked="checked" id="use_timer_template" class="use_timer"
|
<input type="checkbox" checked="checked" id="use_timer_template" class="use_timer"
|
||||||
title="Players will be skipped if they have not played within a reasonable amount of time."
|
title="Players will be skipped if they have not played within a reasonable amount of time."
|
||||||
aria-label="Use idle timer. Players will be skipped if they have not played within a reasonable amount of time."/>
|
aria-label="Use idle timer. Players will be skipped if they have not played within a reasonable amount of time."/>
|
||||||
|
|
|
@ -155,6 +155,17 @@ cah.ajax.Builder.prototype.withPlayerLimit = function(playerLimit) {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number}
|
||||||
|
* spectatorLimit Spectator limit field to use in the request.
|
||||||
|
* @returns {cah.ajax.Builder} This object.
|
||||||
|
*/
|
||||||
|
cah.ajax.Builder.prototype.withSpectatorLimit = function(spectatorLimit) {
|
||||||
|
this.assertNotExecuted_();
|
||||||
|
this.data[cah.$.AjaxRequest.SPECTATOR_LIMIT] = spectatorLimit;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number}
|
* @param {number}
|
||||||
* scoreLimit Score limit field to use in the request.
|
* scoreLimit Score limit field to use in the request.
|
||||||
|
|
|
@ -151,6 +151,10 @@ cah.ajax.SuccessHandlers[cah.$.AjaxOperation.JOIN_GAME] = function(data, req) {
|
||||||
cah.Game.joinGame(req[cah.$.AjaxRequest.GAME_ID]);
|
cah.Game.joinGame(req[cah.$.AjaxRequest.GAME_ID]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
cah.ajax.SuccessHandlers[cah.$.AjaxOperation.SPECTATE_GAME] = function(data, req) {
|
||||||
|
cah.Game.joinGame(req[cah.$.AjaxRequest.GAME_ID]);
|
||||||
|
};
|
||||||
|
|
||||||
cah.ajax.SuccessHandlers[cah.$.AjaxOperation.CREATE_GAME] = function(data) {
|
cah.ajax.SuccessHandlers[cah.$.AjaxOperation.CREATE_GAME] = function(data) {
|
||||||
cah.Game.joinGame(data[cah.$.AjaxResponse.GAME_ID]);
|
cah.Game.joinGame(data[cah.$.AjaxResponse.GAME_ID]);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,9 +8,10 @@ cah.$.AjaxOperation = function() {
|
||||||
cah.$.AjaxOperation.prototype.dummyForAutocomplete = undefined;
|
cah.$.AjaxOperation.prototype.dummyForAutocomplete = undefined;
|
||||||
cah.$.AjaxOperation.START_GAME = "sg";
|
cah.$.AjaxOperation.START_GAME = "sg";
|
||||||
cah.$.AjaxOperation.FIRST_LOAD = "fl";
|
cah.$.AjaxOperation.FIRST_LOAD = "fl";
|
||||||
|
cah.$.AjaxOperation.SPECTATE_GAME = "vg";
|
||||||
cah.$.AjaxOperation.LOG_OUT = "lo";
|
cah.$.AjaxOperation.LOG_OUT = "lo";
|
||||||
cah.$.AjaxOperation.BAN = "b";
|
|
||||||
cah.$.AjaxOperation.JUDGE_SELECT = "js";
|
cah.$.AjaxOperation.JUDGE_SELECT = "js";
|
||||||
|
cah.$.AjaxOperation.BAN = "b";
|
||||||
cah.$.AjaxOperation.GAME_LIST = "ggl";
|
cah.$.AjaxOperation.GAME_LIST = "ggl";
|
||||||
cah.$.AjaxOperation.CHANGE_GAME_OPTIONS = "cgo";
|
cah.$.AjaxOperation.CHANGE_GAME_OPTIONS = "cgo";
|
||||||
cah.$.AjaxOperation.GET_GAME_INFO = "ggi";
|
cah.$.AjaxOperation.GET_GAME_INFO = "ggi";
|
||||||
|
@ -31,17 +32,18 @@ cah.$.AjaxRequest = function() {
|
||||||
};
|
};
|
||||||
cah.$.AjaxRequest.prototype.dummyForAutocomplete = undefined;
|
cah.$.AjaxRequest.prototype.dummyForAutocomplete = undefined;
|
||||||
cah.$.AjaxRequest.WALL = "wall";
|
cah.$.AjaxRequest.WALL = "wall";
|
||||||
cah.$.AjaxRequest.MESSAGE = "m";
|
|
||||||
cah.$.AjaxRequest.CARD_ID = "cid";
|
|
||||||
cah.$.AjaxRequest.USE_TIMER = "ut";
|
cah.$.AjaxRequest.USE_TIMER = "ut";
|
||||||
cah.$.AjaxRequest.GAME_ID = "gid";
|
|
||||||
cah.$.AjaxRequest.CARD_SETS = "css";
|
cah.$.AjaxRequest.CARD_SETS = "css";
|
||||||
cah.$.AjaxRequest.SERIAL = "s";
|
cah.$.AjaxRequest.GAME_ID = "gid";
|
||||||
cah.$.AjaxRequest.PLAYER_LIMIT = "pL";
|
|
||||||
cah.$.AjaxRequest.PASSWORD = "pw";
|
|
||||||
cah.$.AjaxRequest.OP = "o";
|
cah.$.AjaxRequest.OP = "o";
|
||||||
cah.$.AjaxRequest.SCORE_LIMIT = "sl";
|
cah.$.AjaxRequest.PLAYER_LIMIT = "pL";
|
||||||
cah.$.AjaxRequest.NICKNAME = "n";
|
cah.$.AjaxRequest.NICKNAME = "n";
|
||||||
|
cah.$.AjaxRequest.SCORE_LIMIT = "sl";
|
||||||
|
cah.$.AjaxRequest.CARD_ID = "cid";
|
||||||
|
cah.$.AjaxRequest.MESSAGE = "m";
|
||||||
|
cah.$.AjaxRequest.SPECTATOR_LIMIT = "vL";
|
||||||
|
cah.$.AjaxRequest.SERIAL = "s";
|
||||||
|
cah.$.AjaxRequest.PASSWORD = "pw";
|
||||||
|
|
||||||
cah.$.AjaxResponse = function() {
|
cah.$.AjaxResponse = function() {
|
||||||
// Dummy constructor to make Eclipse auto-complete.
|
// Dummy constructor to make Eclipse auto-complete.
|
||||||
|
@ -179,6 +181,8 @@ cah.$.GameInfo.STATE = "S";
|
||||||
cah.$.GameInfo.PLAYERS = "P";
|
cah.$.GameInfo.PLAYERS = "P";
|
||||||
cah.$.GameInfo.USE_TIMER = "ut";
|
cah.$.GameInfo.USE_TIMER = "ut";
|
||||||
cah.$.GameInfo.CARD_SETS = "css";
|
cah.$.GameInfo.CARD_SETS = "css";
|
||||||
|
cah.$.GameInfo.SPECTATORS = "V";
|
||||||
|
cah.$.GameInfo.SPECTATOR_LIMIT = "vL";
|
||||||
cah.$.GameInfo.ID = "gid";
|
cah.$.GameInfo.ID = "gid";
|
||||||
cah.$.GameInfo.PLAYER_LIMIT = "pL";
|
cah.$.GameInfo.PLAYER_LIMIT = "pL";
|
||||||
cah.$.GameInfo.PASSWORD = "pw";
|
cah.$.GameInfo.PASSWORD = "pw";
|
||||||
|
@ -197,6 +201,7 @@ cah.$.GamePlayerStatus = function() {
|
||||||
// Dummy constructor to make Eclipse auto-complete.
|
// Dummy constructor to make Eclipse auto-complete.
|
||||||
};
|
};
|
||||||
cah.$.GamePlayerStatus.prototype.dummyForAutocomplete = undefined;
|
cah.$.GamePlayerStatus.prototype.dummyForAutocomplete = undefined;
|
||||||
|
cah.$.GamePlayerStatus.SPECTATOR = "sv";
|
||||||
cah.$.GamePlayerStatus.HOST = "sh";
|
cah.$.GamePlayerStatus.HOST = "sh";
|
||||||
cah.$.GamePlayerStatus.IDLE = "si";
|
cah.$.GamePlayerStatus.IDLE = "si";
|
||||||
cah.$.GamePlayerStatus.WINNER = "sw";
|
cah.$.GamePlayerStatus.WINNER = "sw";
|
||||||
|
@ -205,6 +210,7 @@ cah.$.GamePlayerStatus.JUDGE = "sj";
|
||||||
cah.$.GamePlayerStatus.JUDGING = "sjj";
|
cah.$.GamePlayerStatus.JUDGING = "sjj";
|
||||||
cah.$.GamePlayerStatus_msg = {};
|
cah.$.GamePlayerStatus_msg = {};
|
||||||
cah.$.GamePlayerStatus_msg['sp'] = "Playing";
|
cah.$.GamePlayerStatus_msg['sp'] = "Playing";
|
||||||
|
cah.$.GamePlayerStatus_msg['sv'] = "Spectator";
|
||||||
cah.$.GamePlayerStatus_msg['sh'] = "Host";
|
cah.$.GamePlayerStatus_msg['sh'] = "Host";
|
||||||
cah.$.GamePlayerStatus_msg['sw'] = "Winner!";
|
cah.$.GamePlayerStatus_msg['sw'] = "Winner!";
|
||||||
cah.$.GamePlayerStatus_msg['sj'] = "Card Czar";
|
cah.$.GamePlayerStatus_msg['sj'] = "Card Czar";
|
||||||
|
@ -212,6 +218,7 @@ cah.$.GamePlayerStatus_msg['sjj'] = "Selecting";
|
||||||
cah.$.GamePlayerStatus_msg['si'] = "";
|
cah.$.GamePlayerStatus_msg['si'] = "";
|
||||||
cah.$.GamePlayerStatus_msg_2 = {};
|
cah.$.GamePlayerStatus_msg_2 = {};
|
||||||
cah.$.GamePlayerStatus_msg_2['sp'] = "Select a card to play.";
|
cah.$.GamePlayerStatus_msg_2['sp'] = "Select a card to play.";
|
||||||
|
cah.$.GamePlayerStatus_msg_2['sv'] = "You are just spectating.";
|
||||||
cah.$.GamePlayerStatus_msg_2['sh'] = "Wait for players then click Start Game.";
|
cah.$.GamePlayerStatus_msg_2['sh'] = "Wait for players then click Start Game.";
|
||||||
cah.$.GamePlayerStatus_msg_2['sw'] = "You have won!";
|
cah.$.GamePlayerStatus_msg_2['sw'] = "You have won!";
|
||||||
cah.$.GamePlayerStatus_msg_2['sj'] = "You are the Card Czar.";
|
cah.$.GamePlayerStatus_msg_2['sj'] = "You are the Card Czar.";
|
||||||
|
@ -241,10 +248,12 @@ cah.$.LongPollEvent.prototype.dummyForAutocomplete = undefined;
|
||||||
cah.$.LongPollEvent.BANNED = "B&";
|
cah.$.LongPollEvent.BANNED = "B&";
|
||||||
cah.$.LongPollEvent.KICKED = "k";
|
cah.$.LongPollEvent.KICKED = "k";
|
||||||
cah.$.LongPollEvent.HURRY_UP = "hu";
|
cah.$.LongPollEvent.HURRY_UP = "hu";
|
||||||
|
cah.$.LongPollEvent.GAME_SPECTATOR_LEAVE = "gvl";
|
||||||
cah.$.LongPollEvent.KICKED_FROM_GAME_IDLE = "kfgi";
|
cah.$.LongPollEvent.KICKED_FROM_GAME_IDLE = "kfgi";
|
||||||
cah.$.LongPollEvent.GAME_JUDGE_SKIPPED = "gjs";
|
cah.$.LongPollEvent.GAME_JUDGE_SKIPPED = "gjs";
|
||||||
cah.$.LongPollEvent.GAME_PLAYER_LEAVE = "gpl";
|
cah.$.LongPollEvent.GAME_PLAYER_LEAVE = "gpl";
|
||||||
cah.$.LongPollEvent.NEW_PLAYER = "np";
|
cah.$.LongPollEvent.NEW_PLAYER = "np";
|
||||||
|
cah.$.LongPollEvent.GAME_SPECTATOR_JOIN = "gvj";
|
||||||
cah.$.LongPollEvent.GAME_PLAYER_JOIN = "gpj";
|
cah.$.LongPollEvent.GAME_PLAYER_JOIN = "gpj";
|
||||||
cah.$.LongPollEvent.GAME_LIST_REFRESH = "glr";
|
cah.$.LongPollEvent.GAME_LIST_REFRESH = "glr";
|
||||||
cah.$.LongPollEvent.GAME_ROUND_COMPLETE = "grc";
|
cah.$.LongPollEvent.GAME_ROUND_COMPLETE = "grc";
|
||||||
|
|
|
@ -58,6 +58,14 @@ cah.Game = function(id) {
|
||||||
this.scoreboardElement_.id = "scoreboard_" + id;
|
this.scoreboardElement_.id = "scoreboard_" + id;
|
||||||
$(this.scoreboardElement_).removeClass("hide");
|
$(this.scoreboardElement_).removeClass("hide");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The first spectator element within the scoreboard.
|
||||||
|
*
|
||||||
|
* @type {HTMLDivElement}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.firstSpectatorElement_ = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The element for the chat room for this game
|
* The element for the chat room for this game
|
||||||
*
|
*
|
||||||
|
@ -82,6 +90,7 @@ cah.Game = function(id) {
|
||||||
// TODO: It looks like I'm not changing the id on the label elements...
|
// TODO: It looks like I'm not changing the id on the label elements...
|
||||||
$("#score_limit_template_label", this.optionsElement_).attr("for", "score_limit_" + id);
|
$("#score_limit_template_label", this.optionsElement_).attr("for", "score_limit_" + id);
|
||||||
$("#player_limit_template_label", this.optionsElement_).attr("for", "player_limit_" + id);
|
$("#player_limit_template_label", this.optionsElement_).attr("for", "player_limit_" + id);
|
||||||
|
$("#spectator_limit_template_label", this.optionsElement_).attr("for", "spectator_limit_" + id);
|
||||||
$("#card_set_template_label", this.optionsElement_).attr("for", "card_set_" + id);
|
$("#card_set_template_label", this.optionsElement_).attr("for", "card_set_" + id);
|
||||||
$("#game_password_template_label", this.optionsElement_).attr("for", "game_password_" + id);
|
$("#game_password_template_label", this.optionsElement_).attr("for", "game_password_" + id);
|
||||||
$("#game_hide_password_template_label", this.optionsElement_).attr("for",
|
$("#game_hide_password_template_label", this.optionsElement_).attr("for",
|
||||||
|
@ -90,6 +99,7 @@ cah.Game = function(id) {
|
||||||
|
|
||||||
$("#score_limit_template", this.optionsElement_).attr("id", "score_limit_" + id);
|
$("#score_limit_template", this.optionsElement_).attr("id", "score_limit_" + id);
|
||||||
$("#player_limit_template", this.optionsElement_).attr("id", "player_limit_" + id);
|
$("#player_limit_template", this.optionsElement_).attr("id", "player_limit_" + id);
|
||||||
|
$("#spectator_limit_template", this.optionsElement_).attr("id", "spectator_limit_" + id);
|
||||||
$("#card_set_template", this.optionsElement_).attr("id", "card_set_" + id);
|
$("#card_set_template", this.optionsElement_).attr("id", "card_set_" + id);
|
||||||
$("#game_password_template", this.optionsElement_).attr("id", "game_password_" + id);
|
$("#game_password_template", this.optionsElement_).attr("id", "game_password_" + id);
|
||||||
$("#game_fake_password_template", this.optionsElement_).attr("id", "game_fake_password_" + id);
|
$("#game_fake_password_template", this.optionsElement_).attr("id", "game_fake_password_" + id);
|
||||||
|
@ -748,6 +758,7 @@ cah.Game.prototype.updateGameStatus = function(data) {
|
||||||
|
|
||||||
$(".score_limit", this.optionsElement_).val(gameInfo[cah.$.GameInfo.SCORE_LIMIT]);
|
$(".score_limit", this.optionsElement_).val(gameInfo[cah.$.GameInfo.SCORE_LIMIT]);
|
||||||
$(".player_limit", this.optionsElement_).val(gameInfo[cah.$.GameInfo.PLAYER_LIMIT]);
|
$(".player_limit", this.optionsElement_).val(gameInfo[cah.$.GameInfo.PLAYER_LIMIT]);
|
||||||
|
$(".spectator_limit", this.optionsElement_).val(gameInfo[cah.$.GameInfo.SPECTATOR_LIMIT]);
|
||||||
$(".game_password", this.optionsElement_).val(gameInfo[cah.$.GameInfo.PASSWORD]);
|
$(".game_password", this.optionsElement_).val(gameInfo[cah.$.GameInfo.PASSWORD]);
|
||||||
if (gameInfo[cah.$.GameInfo.USE_TIMER]) {
|
if (gameInfo[cah.$.GameInfo.USE_TIMER]) {
|
||||||
$(".use_timer", this.optionsElement_).attr("checked", "checked");
|
$(".use_timer", this.optionsElement_).attr("checked", "checked");
|
||||||
|
@ -765,6 +776,11 @@ cah.Game.prototype.updateGameStatus = function(data) {
|
||||||
for ( var index in playerInfos) {
|
for ( var index in playerInfos) {
|
||||||
this.updateUserStatus(playerInfos[index]);
|
this.updateUserStatus(playerInfos[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var spectators = gameInfo[cah.$.GameInfo.SPECTATORS];
|
||||||
|
for ( var index in spectators) {
|
||||||
|
this.updateSpectator(spectators[index]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -780,7 +796,11 @@ cah.Game.prototype.updateUserStatus = function(playerInfo) {
|
||||||
if (!panel) {
|
if (!panel) {
|
||||||
// new score panel
|
// new score panel
|
||||||
panel = new cah.GameScorePanel(playerName);
|
panel = new cah.GameScorePanel(playerName);
|
||||||
|
if (this.firstSpectatorElement_) {
|
||||||
|
$(this.firstSpectatorElement_).before(panel.getElement());
|
||||||
|
} else {
|
||||||
$(this.scoreboardElement_).append(panel.getElement());
|
$(this.scoreboardElement_).append(panel.getElement());
|
||||||
|
}
|
||||||
this.scoreCards_[playerName] = panel;
|
this.scoreCards_[playerName] = panel;
|
||||||
}
|
}
|
||||||
var oldStatus = panel.getStatus();
|
var oldStatus = panel.getStatus();
|
||||||
|
@ -838,6 +858,32 @@ cah.Game.prototype.updateUserStatus = function(playerInfo) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a single spectator's info.
|
||||||
|
*
|
||||||
|
* @param {String}
|
||||||
|
* spectator The spectator name.
|
||||||
|
*/
|
||||||
|
cah.Game.prototype.updateSpectator = function(spectator) {
|
||||||
|
var panel = this.scoreCards_[spectator];
|
||||||
|
if (!panel) {
|
||||||
|
// new score panel
|
||||||
|
panel = new cah.GameScorePanel(spectator);
|
||||||
|
$(this.scoreboardElement_).append(panel.getElement());
|
||||||
|
this.scoreCards_[spectator] = panel;
|
||||||
|
if (!this.firstSpectatorElement_) {
|
||||||
|
this.firstSpectatorElement_ = panel.getElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panel.update(-1, cah.$.GamePlayerStatus.SPECTATOR);
|
||||||
|
|
||||||
|
if (spectator == cah.nickname) {
|
||||||
|
$(".game_message", this.element_).text(cah.$.GamePlayerStatus_msg_2[cah.$.GamePlayerStatus.SPECTATOR]);
|
||||||
|
$(".confirm_card", this.element_).attr("disabled", "disabled");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Round has completed. Update display of round cards to show winner.
|
* Round has completed. Update display of round cards to show winner.
|
||||||
*
|
*
|
||||||
|
@ -1140,6 +1186,45 @@ cah.Game.prototype.playerLeave = function(player) {
|
||||||
delete this.scoreCards_[player];
|
delete this.scoreCards_[player];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A spectator has joined the game.
|
||||||
|
*
|
||||||
|
* @param {String}
|
||||||
|
* spectator Spectator that joined.
|
||||||
|
*/
|
||||||
|
cah.Game.prototype.spectatorJoin = function(spectator) {
|
||||||
|
if (spectator != cah.nickname) {
|
||||||
|
cah.log.status_with_game(this, spectator + " has started spectating the game.");
|
||||||
|
this.refreshGameStatus();
|
||||||
|
} else {
|
||||||
|
cah.log.status_with_game(this, "You have started spectating the game.");
|
||||||
|
}
|
||||||
|
this.updateSpectator(spectator);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A spectator has left the game.
|
||||||
|
*
|
||||||
|
* @param {String}
|
||||||
|
* spectator Spectator that left.
|
||||||
|
*/
|
||||||
|
cah.Game.prototype.spectatorLeave = function(spectator) {
|
||||||
|
if (spectator != cah.nickname) {
|
||||||
|
cah.log.status_with_game(this, spectator + " has stopped spectating the game.");
|
||||||
|
this.refreshGameStatus();
|
||||||
|
} else {
|
||||||
|
cah.log.status_with_game(this, "You have stopped spectating the game.");
|
||||||
|
}
|
||||||
|
var scorecard = this.scoreCards_[spectator];
|
||||||
|
if (scorecard) {
|
||||||
|
if (this.firstSpectatorElement_ == scorecard.getElement()) {
|
||||||
|
this.firstSpectatorElement_ = this.firstSpectatorElement_.nextSibling;
|
||||||
|
}
|
||||||
|
$(scorecard.getElement()).remove();
|
||||||
|
}
|
||||||
|
delete this.scoreCards_[spectator];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh game scoreboard, etc.
|
* Refresh game scoreboard, etc.
|
||||||
*/
|
*/
|
||||||
|
@ -1249,7 +1334,8 @@ cah.Game.prototype.optionChanged_ = function(e) {
|
||||||
}
|
}
|
||||||
cah.Ajax.build(cah.$.AjaxOperation.CHANGE_GAME_OPTIONS).withGameId(this.id_).withScoreLimit(
|
cah.Ajax.build(cah.$.AjaxOperation.CHANGE_GAME_OPTIONS).withGameId(this.id_).withScoreLimit(
|
||||||
$(".score_limit", this.optionsElement_).val()).withPlayerLimit(
|
$(".score_limit", this.optionsElement_).val()).withPlayerLimit(
|
||||||
$(".player_limit", this.optionsElement_).val()).withCardSets(cardSetIds).withPassword(
|
$(".player_limit", this.optionsElement_).val()).withSpectatorLimit(
|
||||||
|
$(".spectator_limit", this.optionsElement_).val()).withCardSets(cardSetIds).withPassword(
|
||||||
$(".game_password", this.optionsElement_).val()).withUseTimer(
|
$(".game_password", this.optionsElement_).val()).withUseTimer(
|
||||||
!!$('.use_timer', this.optionsElement_).attr('checked')).run();
|
!!$('.use_timer', this.optionsElement_).attr('checked')).run();
|
||||||
};
|
};
|
||||||
|
@ -1337,10 +1423,17 @@ cah.GameScorePanel.prototype.update = function(score, status) {
|
||||||
$(".scorecard_score", this.element_).text(score);
|
$(".scorecard_score", this.element_).text(score);
|
||||||
$(".scorecard_status", this.element_).text(cah.$.GamePlayerStatus_msg[status]);
|
$(".scorecard_status", this.element_).text(cah.$.GamePlayerStatus_msg[status]);
|
||||||
$(".scorecard_s", this.element_).text(score == 1 ? "" : "s");
|
$(".scorecard_s", this.element_).text(score == 1 ? "" : "s");
|
||||||
|
if (score < 0) {
|
||||||
|
$(".scorecard_points", this.element_).addClass("hide");
|
||||||
|
$(this.element_).attr("aria-label", this.player_ + ". "
|
||||||
|
+ cah.$.GamePlayerStatus_msg[status]);
|
||||||
|
} else {
|
||||||
|
$(".scorecard_points", this.element_).removeClass("hide");
|
||||||
$(this.element_).attr(
|
$(this.element_).attr(
|
||||||
"aria-label",
|
"aria-label",
|
||||||
this.player_ + " has " + score + " Awesome Point" + (score == 1 ? "" : "s") + ". "
|
this.player_ + " has " + score + " Awesome Point" + (score == 1 ? "" : "s") + ". "
|
||||||
+ cah.$.GamePlayerStatus_msg[status]);
|
+ cah.$.GamePlayerStatus_msg[status]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -211,11 +211,15 @@ cah.GameListLobby = function(parentElem, data) {
|
||||||
$(".gamelist_lobby_id", this.element_).text(this.id_);
|
$(".gamelist_lobby_id", this.element_).text(this.id_);
|
||||||
$(".gamelist_lobby_host", this.element_).text(data[cah.$.GameInfo.HOST]);
|
$(".gamelist_lobby_host", this.element_).text(data[cah.$.GameInfo.HOST]);
|
||||||
$(".gamelist_lobby_players", this.element_).text(data[cah.$.GameInfo.PLAYERS].join(", "));
|
$(".gamelist_lobby_players", this.element_).text(data[cah.$.GameInfo.PLAYERS].join(", "));
|
||||||
|
$(".gamelist_lobby_spectators", this.element_).text(data[cah.$.GameInfo.SPECTATORS].join(", "));
|
||||||
var statusMessage = cah.$.GameState_msg[data[cah.$.GameInfo.STATE]];
|
var statusMessage = cah.$.GameState_msg[data[cah.$.GameInfo.STATE]];
|
||||||
$(".gamelist_lobby_status", this.element_).text(statusMessage);
|
$(".gamelist_lobby_status", this.element_).text(statusMessage);
|
||||||
$(".gamelist_lobby_join", this.element_).click(cah.bind(this, this.joinClick));
|
$(".gamelist_lobby_join", this.element_).click(cah.bind(this, this.joinClick));
|
||||||
|
$(".gamelist_lobby_spectate", this.element_).click(cah.bind(this, this.spectateClick));
|
||||||
$(".gamelist_lobby_player_count", this.element_).text(data[cah.$.GameInfo.PLAYERS].length);
|
$(".gamelist_lobby_player_count", this.element_).text(data[cah.$.GameInfo.PLAYERS].length);
|
||||||
$(".gamelist_lobby_max_players", this.element_).text(data[cah.$.GameInfo.PLAYER_LIMIT]);
|
$(".gamelist_lobby_max_players", this.element_).text(data[cah.$.GameInfo.PLAYER_LIMIT]);
|
||||||
|
$(".gamelist_lobby_spectator_count", this.element_).text(data[cah.$.GameInfo.SPECTATORS].length);
|
||||||
|
$(".gamelist_lobby_max_spectators", this.element_).text(data[cah.$.GameInfo.SPECTATOR_LIMIT]);
|
||||||
$(".gamelist_lobby_goal", this.element_).text(data[cah.$.GameInfo.SCORE_LIMIT]);
|
$(".gamelist_lobby_goal", this.element_).text(data[cah.$.GameInfo.SCORE_LIMIT]);
|
||||||
var cardSetNames = [];
|
var cardSetNames = [];
|
||||||
data[cah.$.GameInfo.CARD_SETS].sort();
|
data[cah.$.GameInfo.CARD_SETS].sort();
|
||||||
|
@ -232,7 +236,8 @@ cah.GameListLobby = function(parentElem, data) {
|
||||||
$(this.element_).attr(
|
$(this.element_).attr(
|
||||||
"aria-label",
|
"aria-label",
|
||||||
data[cah.$.GameInfo.HOST] + "'s game, with " + data[cah.$.GameInfo.PLAYERS].length + " of "
|
data[cah.$.GameInfo.HOST] + "'s game, with " + data[cah.$.GameInfo.PLAYERS].length + " of "
|
||||||
+ data[cah.$.GameInfo.PLAYER_LIMIT] + " players. " + statusMessage + ". Goal is "
|
+ data[cah.$.GameInfo.PLAYER_LIMIT] + " players, and " + data[cah.$.GameInfo.SPECTATORS].length
|
||||||
|
+ " of " + data[cah.$.GameInfo.SPECTATOR_LIMIT] + "spectators. " + statusMessage + ". Goal is "
|
||||||
+ data[cah.$.GameInfo.SCORE_LIMIT] + " Awesome Points. Using " + cardSetNames.length
|
+ data[cah.$.GameInfo.SCORE_LIMIT] + " Awesome Points. Using " + cardSetNames.length
|
||||||
+ " card set" + (cardSetNames.length == 1 ? "" : "s") + ". "
|
+ " card set" + (cardSetNames.length == 1 ? "" : "s") + ". "
|
||||||
+ (data[cah.$.GameInfo.HAS_PASSWORD] ? "Has" : "Does not have") + " a password.");
|
+ (data[cah.$.GameInfo.HAS_PASSWORD] ? "Has" : "Does not have") + " a password.");
|
||||||
|
@ -259,10 +264,25 @@ cah.GameListLobby.prototype.join = function() {
|
||||||
$('.gamelist_lobby_join', this.element_).click();
|
$('.gamelist_lobby_join', this.element_).click();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler for clicking the View button in a game lobby.
|
||||||
|
*/
|
||||||
|
cah.GameListLobby.prototype.spectateClick = function() {
|
||||||
|
var password = "";
|
||||||
|
if (this.data_[cah.$.GameInfo.HAS_PASSWORD]) {
|
||||||
|
password = prompt("Enter the game's password.");
|
||||||
|
if (password == null) {
|
||||||
|
password = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cah.Ajax.build(cah.$.AjaxOperation.SPECTATE_GAME).withGameId(this.id_).withPassword(password).run();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the game lobby from the document and free up resources.
|
* Remove the game lobby from the document and free up resources.
|
||||||
*/
|
*/
|
||||||
cah.GameListLobby.prototype.dispose = function() {
|
cah.GameListLobby.prototype.dispose = function() {
|
||||||
this.parentElem_.removeChild(this.element_);
|
this.parentElem_.removeChild(this.element_);
|
||||||
$(".gamelist_lobby_join", this.element_).unbind();
|
$(".gamelist_lobby_join", this.element_).unbind();
|
||||||
|
$(".gamelist_lobby_spectate", this.element_).unbind();
|
||||||
};
|
};
|
||||||
|
|
|
@ -128,6 +128,17 @@ cah.longpoll.EventHandlers[cah.$.LongPollEvent.GAME_PLAYER_LEAVE] = function(dat
|
||||||
data[cah.$.LongPollResponse.NICKNAME], "player leave");
|
data[cah.$.LongPollResponse.NICKNAME], "player leave");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
cah.longpoll.EventHandlers[cah.$.LongPollEvent.GAME_SPECTATOR_JOIN] = function(data) {
|
||||||
|
cah.longpoll.EventHandlers.__gameEvent(data, cah.Game.prototype.spectatorJoin,
|
||||||
|
data[cah.$.LongPollResponse.NICKNAME],
|
||||||
|
"spectator join (if you just joined a game this may be OK)");
|
||||||
|
};
|
||||||
|
|
||||||
|
cah.longpoll.EventHandlers[cah.$.LongPollEvent.GAME_SPECTATOR_LEAVE] = function(data) {
|
||||||
|
cah.longpoll.EventHandlers.__gameEvent(data, cah.Game.prototype.spectatorLeave,
|
||||||
|
data[cah.$.LongPollResponse.NICKNAME], "spectator leave");
|
||||||
|
};
|
||||||
|
|
||||||
cah.longpoll.EventHandlers[cah.$.LongPollEvent.HAND_DEAL] = function(data) {
|
cah.longpoll.EventHandlers[cah.$.LongPollEvent.HAND_DEAL] = function(data) {
|
||||||
cah.longpoll.EventHandlers.__gameEvent(data, cah.Game.prototype.dealtCards,
|
cah.longpoll.EventHandlers.__gameEvent(data, cah.Game.prototype.dealtCards,
|
||||||
data[cah.$.LongPollResponse.HAND], "dealt cards");
|
data[cah.$.LongPollResponse.HAND], "dealt cards");
|
||||||
|
|
|
@ -172,6 +172,7 @@ public class Constants {
|
||||||
GET_CARDS("gc"),
|
GET_CARDS("gc"),
|
||||||
GET_GAME_INFO("ggi"),
|
GET_GAME_INFO("ggi"),
|
||||||
JOIN_GAME("jg"),
|
JOIN_GAME("jg"),
|
||||||
|
SPECTATE_GAME("vg"),
|
||||||
JUDGE_SELECT("js"),
|
JUDGE_SELECT("js"),
|
||||||
KICK("K"),
|
KICK("K"),
|
||||||
LEAVE_GAME("lg"),
|
LEAVE_GAME("lg"),
|
||||||
|
@ -208,6 +209,7 @@ public class Constants {
|
||||||
OP("o"),
|
OP("o"),
|
||||||
PASSWORD("pw"),
|
PASSWORD("pw"),
|
||||||
PLAYER_LIMIT("pL"),
|
PLAYER_LIMIT("pL"),
|
||||||
|
SPECTATOR_LIMIT("vL"),
|
||||||
SCORE_LIMIT("sl"),
|
SCORE_LIMIT("sl"),
|
||||||
SERIAL("s"),
|
SERIAL("s"),
|
||||||
USE_TIMER("ut"),
|
USE_TIMER("ut"),
|
||||||
|
@ -371,6 +373,8 @@ public class Constants {
|
||||||
GAME_PLAYER_KICKED_IDLE("gpki"),
|
GAME_PLAYER_KICKED_IDLE("gpki"),
|
||||||
GAME_PLAYER_LEAVE("gpl"),
|
GAME_PLAYER_LEAVE("gpl"),
|
||||||
GAME_PLAYER_SKIPPED("gps"),
|
GAME_PLAYER_SKIPPED("gps"),
|
||||||
|
GAME_SPECTATOR_JOIN("gvj"),
|
||||||
|
GAME_SPECTATOR_LEAVE("gvl"),
|
||||||
GAME_ROUND_COMPLETE("grc"),
|
GAME_ROUND_COMPLETE("grc"),
|
||||||
GAME_STATE_CHANGE("gsc"),
|
GAME_STATE_CHANGE("gsc"),
|
||||||
GAME_WHITE_RESHUFFLE("gwr"),
|
GAME_WHITE_RESHUFFLE("gwr"),
|
||||||
|
@ -596,6 +600,9 @@ public class Constants {
|
||||||
PLAYER_LIMIT(AjaxRequest.PLAYER_LIMIT),
|
PLAYER_LIMIT(AjaxRequest.PLAYER_LIMIT),
|
||||||
PLAYERS("P"),
|
PLAYERS("P"),
|
||||||
@DuplicationAllowed
|
@DuplicationAllowed
|
||||||
|
SPECTATOR_LIMIT(AjaxRequest.SPECTATOR_LIMIT),
|
||||||
|
SPECTATORS("V"),
|
||||||
|
@DuplicationAllowed
|
||||||
SCORE_LIMIT(AjaxRequest.SCORE_LIMIT),
|
SCORE_LIMIT(AjaxRequest.SCORE_LIMIT),
|
||||||
STATE("S"),
|
STATE("S"),
|
||||||
@DuplicationAllowed
|
@DuplicationAllowed
|
||||||
|
@ -648,7 +655,8 @@ public class Constants {
|
||||||
JUDGE("sj", "Card Czar", "You are the Card Czar."),
|
JUDGE("sj", "Card Czar", "You are the Card Czar."),
|
||||||
JUDGING("sjj", "Selecting", "Select a winning card."),
|
JUDGING("sjj", "Selecting", "Select a winning card."),
|
||||||
PLAYING("sp", "Playing", "Select a card to play."),
|
PLAYING("sp", "Playing", "Select a card to play."),
|
||||||
WINNER("sw", "Winner!", "You have won!");
|
WINNER("sw", "Winner!", "You have won!"),
|
||||||
|
SPECTATOR("sv", "Spectator", "You are just spectating.");
|
||||||
|
|
||||||
private final String status;
|
private final String status;
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|
|
@ -92,6 +92,7 @@ public class Game {
|
||||||
*/
|
*/
|
||||||
private final List<Player> roundPlayers = Collections.synchronizedList(new ArrayList<Player>(9));
|
private final List<Player> roundPlayers = Collections.synchronizedList(new ArrayList<Player>(9));
|
||||||
private final PlayerPlayedCardsTracker playedCards = new PlayerPlayedCardsTracker();
|
private final PlayerPlayedCardsTracker playedCards = new PlayerPlayedCardsTracker();
|
||||||
|
private final List<User> spectators = Collections.synchronizedList(new ArrayList<User>(10));
|
||||||
private final ConnectedUsers connectedUsers;
|
private final ConnectedUsers connectedUsers;
|
||||||
private final GameManager gameManager;
|
private final GameManager gameManager;
|
||||||
private Player host;
|
private Player host;
|
||||||
|
@ -101,6 +102,7 @@ public class Game {
|
||||||
private WhiteDeck whiteDeck;
|
private WhiteDeck whiteDeck;
|
||||||
private GameState state;
|
private GameState state;
|
||||||
private int maxPlayers = 6;
|
private int maxPlayers = 6;
|
||||||
|
private int maxSpectators = 6;
|
||||||
private int judgeIndex = 0;
|
private int judgeIndex = 0;
|
||||||
private final static int ROUND_INTERMISSION = 8 * 1000;
|
private final static int ROUND_INTERMISSION = 8 * 1000;
|
||||||
/**
|
/**
|
||||||
|
@ -302,6 +304,65 @@ public class Game {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a spectator to the game.
|
||||||
|
*
|
||||||
|
* Synchronizes on {@link #spectators}.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* Spectator to add to this game.
|
||||||
|
* @throws TooManySpectatorsException
|
||||||
|
* Thrown if this game is at its maximum spectator capacity.
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* Thrown if {@code user} is already in a game.
|
||||||
|
*/
|
||||||
|
public void addSpectator(final User user) throws TooManySpectatorsException,
|
||||||
|
IllegalStateException {
|
||||||
|
logger.info(String.format("%s joined game %d as a spectator.", user.toString(), id));
|
||||||
|
synchronized (spectators) {
|
||||||
|
if (spectators.size() >= maxSpectators) {
|
||||||
|
throw new TooManySpectatorsException();
|
||||||
|
}
|
||||||
|
// this will throw IllegalStateException if the user is already in a game, including this one.
|
||||||
|
user.joinGame(this);
|
||||||
|
spectators.add(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||||
|
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_SPECTATOR_JOIN.toString());
|
||||||
|
data.put(LongPollResponse.NICKNAME, user.getNickname());
|
||||||
|
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||||
|
|
||||||
|
gameManager.broadcastGameListRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a spectator from the game.
|
||||||
|
* <br/>
|
||||||
|
* Synchronizes on {@link #spectator}.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* Spectator to remove from the game.
|
||||||
|
*/
|
||||||
|
public void removeSpectator(final User user) {
|
||||||
|
logger.info(String.format("Removing spectator %s from game %d.", user.toString(), id));
|
||||||
|
synchronized (spectators) {
|
||||||
|
if (!spectators.remove(user)) {
|
||||||
|
return;
|
||||||
|
} // not actually spectating
|
||||||
|
user.leaveGame(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do this down here so the person that left doesn't get the notice too
|
||||||
|
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||||
|
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_SPECTATOR_LEAVE.toString());
|
||||||
|
data.put(LongPollResponse.NICKNAME, user.getNickname());
|
||||||
|
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||||
|
|
||||||
|
// Don't do this anymore, it was driving up a crazy amount of traffic.
|
||||||
|
// gameManager.broadcastGameListRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all played cards to their respective player's hand.
|
* Return all played cards to their respective player's hand.
|
||||||
* <br/>
|
* <br/>
|
||||||
|
@ -368,9 +429,11 @@ public class Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateGameSettings(final int newScoreGoal, final int newMaxPlayers,
|
public void updateGameSettings(final int newScoreGoal, final int newMaxPlayers,
|
||||||
|
final int newMaxSpectators,
|
||||||
final Set<CardSet> newCardSets, final String newPassword, final boolean newUseTimer) {
|
final Set<CardSet> newCardSets, final String newPassword, final boolean newUseTimer) {
|
||||||
this.scoreGoal = newScoreGoal;
|
this.scoreGoal = newScoreGoal;
|
||||||
this.maxPlayers = newMaxPlayers;
|
this.maxPlayers = newMaxPlayers;
|
||||||
|
this.maxSpectators = newMaxSpectators;
|
||||||
synchronized (this.cardSets) {
|
synchronized (this.cardSets) {
|
||||||
this.cardSets.clear();
|
this.cardSets.clear();
|
||||||
this.cardSets.addAll(newCardSets);
|
this.cardSets.addAll(newCardSets);
|
||||||
|
@ -424,6 +487,7 @@ public class Game {
|
||||||
}
|
}
|
||||||
info.put(GameInfo.CARD_SETS, cardSetIds);
|
info.put(GameInfo.CARD_SETS, cardSetIds);
|
||||||
info.put(GameInfo.PLAYER_LIMIT, maxPlayers);
|
info.put(GameInfo.PLAYER_LIMIT, maxPlayers);
|
||||||
|
info.put(GameInfo.SPECTATOR_LIMIT, maxSpectators);
|
||||||
info.put(GameInfo.SCORE_LIMIT, scoreGoal);
|
info.put(GameInfo.SCORE_LIMIT, scoreGoal);
|
||||||
info.put(GameInfo.USE_TIMER, useTimer);
|
info.put(GameInfo.USE_TIMER, useTimer);
|
||||||
if (includePassword) {
|
if (includePassword) {
|
||||||
|
@ -437,6 +501,14 @@ public class Game {
|
||||||
playerNames.add(player.toString());
|
playerNames.add(player.toString());
|
||||||
}
|
}
|
||||||
info.put(GameInfo.PLAYERS, playerNames);
|
info.put(GameInfo.PLAYERS, playerNames);
|
||||||
|
|
||||||
|
final User[] spectatorsCopy = spectators.toArray(new User[spectators.size()]);
|
||||||
|
final List<String> spectatorNames = new ArrayList<String>(spectatorsCopy.length);
|
||||||
|
for (final User spectator : spectatorsCopy) {
|
||||||
|
spectatorNames.add(spectator.toString());
|
||||||
|
}
|
||||||
|
info.put(GameInfo.SPECTATORS, spectatorNames);
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1160,6 +1232,9 @@ public class Game {
|
||||||
for (final Player player : playersCopy) {
|
for (final Player player : playersCopy) {
|
||||||
users.add(player.getUser());
|
users.add(player.getUser());
|
||||||
}
|
}
|
||||||
|
synchronized (spectators) {
|
||||||
|
users.addAll(spectators);
|
||||||
|
}
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1313,4 +1388,11 @@ public class Game {
|
||||||
public class TooManyPlayersException extends Exception {
|
public class TooManyPlayersException extends Exception {
|
||||||
private static final long serialVersionUID = -6603422097641992017L;
|
private static final long serialVersionUID = -6603422097641992017L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception to be thrown when there are too many spectators in a game.
|
||||||
|
*/
|
||||||
|
public class TooManySpectatorsException extends Exception {
|
||||||
|
private static final long serialVersionUID = -6603422097641992018L;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,7 @@ public class GameManager implements Provider<Integer> {
|
||||||
final List<User> usersToRemove = game.getUsers();
|
final List<User> usersToRemove = game.getUsers();
|
||||||
for (final User user : usersToRemove) {
|
for (final User user : usersToRemove) {
|
||||||
game.removePlayer(user);
|
game.removePlayer(user);
|
||||||
|
game.removeSpectator(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(String.format("Destroyed game %d.", game.getId()));
|
logger.info(String.format("Destroyed game %d.", game.getId()));
|
||||||
|
|
|
@ -43,6 +43,7 @@ public class ChangeGameOptionHandler extends GameWithPlayerHandler {
|
||||||
try {
|
try {
|
||||||
final int scoreLimit = Integer.parseInt(request.getParameter(AjaxRequest.SCORE_LIMIT));
|
final int scoreLimit = Integer.parseInt(request.getParameter(AjaxRequest.SCORE_LIMIT));
|
||||||
final int playerLimit = Integer.parseInt(request.getParameter(AjaxRequest.PLAYER_LIMIT));
|
final int playerLimit = Integer.parseInt(request.getParameter(AjaxRequest.PLAYER_LIMIT));
|
||||||
|
final int spectatorLimit = Integer.parseInt(request.getParameter(AjaxRequest.SPECTATOR_LIMIT));
|
||||||
final String[] cardSetsParsed = request.getParameter(AjaxRequest.CARD_SETS).split(",");
|
final String[] cardSetsParsed = request.getParameter(AjaxRequest.CARD_SETS).split(",");
|
||||||
final Set<CardSet> cardSets = new HashSet<CardSet>();
|
final Set<CardSet> cardSets = new HashSet<CardSet>();
|
||||||
for (final String cardSetId : cardSetsParsed) {
|
for (final String cardSetId : cardSetsParsed) {
|
||||||
|
@ -63,7 +64,7 @@ public class ChangeGameOptionHandler extends GameWithPlayerHandler {
|
||||||
if (null != useTimerString && !"".equals(useTimerString)) {
|
if (null != useTimerString && !"".equals(useTimerString)) {
|
||||||
useTimer = Boolean.valueOf(useTimerString);
|
useTimer = Boolean.valueOf(useTimerString);
|
||||||
}
|
}
|
||||||
game.updateGameSettings(scoreLimit, playerLimit, cardSets, password, useTimer);
|
game.updateGameSettings(scoreLimit, playerLimit, spectatorLimit, cardSets, password, useTimer);
|
||||||
|
|
||||||
// only broadcast an update if the password state has changed, because it needs to change
|
// only broadcast an update if the password state has changed, because it needs to change
|
||||||
// the text on the join button and the sort order
|
// the text on the join button and the sort order
|
||||||
|
|
|
@ -28,6 +28,7 @@ public class Handlers {
|
||||||
LIST.put(NamesHandler.OP, NamesHandler.class);
|
LIST.put(NamesHandler.OP, NamesHandler.class);
|
||||||
LIST.put(PlayCardHandler.OP, PlayCardHandler.class);
|
LIST.put(PlayCardHandler.OP, PlayCardHandler.class);
|
||||||
LIST.put(RegisterHandler.OP, RegisterHandler.class);
|
LIST.put(RegisterHandler.OP, RegisterHandler.class);
|
||||||
|
LIST.put(SpectateGameHandler.OP, SpectateGameHandler.class);
|
||||||
LIST.put(StartGameHandler.OP, StartGameHandler.class);
|
LIST.put(StartGameHandler.OP, StartGameHandler.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ public class LeaveGameHandler extends GameWithPlayerHandler {
|
||||||
final Map<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
|
final Map<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
|
||||||
|
|
||||||
game.removePlayer(user);
|
game.removePlayer(user);
|
||||||
|
game.removeSpectator(user);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2012, Andy Janata
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||||
|
* provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this list of conditions
|
||||||
|
* and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
||||||
|
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.socialgamer.cah.handlers;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
import net.socialgamer.cah.Constants.AjaxOperation;
|
||||||
|
import net.socialgamer.cah.Constants.AjaxRequest;
|
||||||
|
import net.socialgamer.cah.Constants.ErrorCode;
|
||||||
|
import net.socialgamer.cah.Constants.ReturnableData;
|
||||||
|
import net.socialgamer.cah.RequestWrapper;
|
||||||
|
import net.socialgamer.cah.data.Game;
|
||||||
|
import net.socialgamer.cah.data.Game.TooManySpectatorsException;
|
||||||
|
import net.socialgamer.cah.data.GameManager;
|
||||||
|
import net.socialgamer.cah.data.User;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to spectate a game.
|
||||||
|
*
|
||||||
|
* @author Gavin Lambert (cah@mirality.co.nz)
|
||||||
|
*/
|
||||||
|
public class SpectateGameHandler extends GameHandler {
|
||||||
|
|
||||||
|
public static final String OP = AjaxOperation.SPECTATE_GAME.toString();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public SpectateGameHandler(final GameManager gameManager) {
|
||||||
|
super(gameManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<ReturnableData, Object> handle(final RequestWrapper request,
|
||||||
|
final HttpSession session, final User user, final Game game) {
|
||||||
|
final Map<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
|
||||||
|
|
||||||
|
final String password = request.getParameter(AjaxRequest.PASSWORD);
|
||||||
|
final String gamePassword = game.getPassword();
|
||||||
|
if (gamePassword != null && !gamePassword.equals("")) {
|
||||||
|
if (password == null || !gamePassword.equals(password)) {
|
||||||
|
return error(ErrorCode.WRONG_PASSWORD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
game.addSpectator(user);
|
||||||
|
} catch (final IllegalStateException e) {
|
||||||
|
return error(ErrorCode.CANNOT_JOIN_ANOTHER_GAME);
|
||||||
|
} catch (final TooManySpectatorsException e) {
|
||||||
|
return error(ErrorCode.GAME_FULL);
|
||||||
|
}
|
||||||
|
gameManager.broadcastGameListRefresh();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue