diff --git a/WebContent/game.jsp b/WebContent/game.jsp
index 20d688f..9134ce6 100644
--- a/WebContent/game.jsp
+++ b/WebContent/game.jsp
@@ -427,13 +427,28 @@ HttpSession hSession = request.getSession(true);
Spectators can watch and chat, but not actually play. Not even as Czar.
-
-
- Use idle timer.
+
+ Idle timer multiplier:
+
+ 0.25x
+ 0.5x
+ 0.75x
+ 1x
+ 1.25x
+ 1.5x
+ 1.75x
+ 2x
+ 2.5x
+ 3x
+ 4x
+ 5x
+ 10x
+ Unlimited
+
Card Sets
diff --git a/WebContent/js/cah.constants.js b/WebContent/js/cah.constants.js
index e6722af..5472837 100644
--- a/WebContent/js/cah.constants.js
+++ b/WebContent/js/cah.constants.js
@@ -7,98 +7,98 @@ cah.$.GamePlayerStatus = function() {
};
cah.$.GamePlayerStatus.prototype.dummyForAutocomplete = undefined;
cah.$.GamePlayerStatus.SPECTATOR = "sv";
-cah.$.GamePlayerStatus.HOST = "sh";
-cah.$.GamePlayerStatus.IDLE = "si";
cah.$.GamePlayerStatus.WINNER = "sw";
-cah.$.GamePlayerStatus.PLAYING = "sp";
-cah.$.GamePlayerStatus.JUDGE = "sj";
+cah.$.GamePlayerStatus.IDLE = "si";
+cah.$.GamePlayerStatus.HOST = "sh";
cah.$.GamePlayerStatus.JUDGING = "sjj";
+cah.$.GamePlayerStatus.JUDGE = "sj";
+cah.$.GamePlayerStatus.PLAYING = "sp";
cah.$.GamePlayerStatus_msg = {};
-cah.$.GamePlayerStatus_msg['sp'] = "Playing";
-cah.$.GamePlayerStatus_msg['sv'] = "Spectator";
-cah.$.GamePlayerStatus_msg['sh'] = "Host";
-cah.$.GamePlayerStatus_msg['sw'] = "Winner!";
-cah.$.GamePlayerStatus_msg['sj'] = "Card Czar";
cah.$.GamePlayerStatus_msg['sjj'] = "Selecting";
+cah.$.GamePlayerStatus_msg['sv'] = "Spectator";
+cah.$.GamePlayerStatus_msg['sw'] = "Winner!";
+cah.$.GamePlayerStatus_msg['sh'] = "Host";
cah.$.GamePlayerStatus_msg['si'] = "";
+cah.$.GamePlayerStatus_msg['sj'] = "Card Czar";
+cah.$.GamePlayerStatus_msg['sp'] = "Playing";
cah.$.GamePlayerStatus_msg_2 = {};
-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['sw'] = "You have won!";
-cah.$.GamePlayerStatus_msg_2['sj'] = "You are the Card Czar.";
cah.$.GamePlayerStatus_msg_2['sjj'] = "Select a winning card.";
+cah.$.GamePlayerStatus_msg_2['sv'] = "You are just spectating.";
+cah.$.GamePlayerStatus_msg_2['sw'] = "You have won!";
+cah.$.GamePlayerStatus_msg_2['sh'] = "Wait for players then click Start Game.";
cah.$.GamePlayerStatus_msg_2['si'] = "Waiting for players...";
+cah.$.GamePlayerStatus_msg_2['sj'] = "You are the Card Czar.";
+cah.$.GamePlayerStatus_msg_2['sp'] = "Select a card to play.";
cah.$.GamePlayerInfo = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.GamePlayerInfo.prototype.dummyForAutocomplete = undefined;
-cah.$.GamePlayerInfo.NAME = "N";
-cah.$.GamePlayerInfo.SCORE = "sc";
cah.$.GamePlayerInfo.STATUS = "st";
+cah.$.GamePlayerInfo.SCORE = "sc";
+cah.$.GamePlayerInfo.NAME = "N";
cah.$.GameOptionData = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.GameOptionData.prototype.dummyForAutocomplete = undefined;
-cah.$.GameOptionData.USE_TIMER = "ut";
-cah.$.GameOptionData.CARD_SETS = "css";
-cah.$.GameOptionData.BLANKS_LIMIT = "bl";
-cah.$.GameOptionData.SPECTATOR_LIMIT = "vL";
-cah.$.GameOptionData.PLAYER_LIMIT = "pL";
+cah.$.GameOptionData.TIMER_MULTIPLIER = "tm";
cah.$.GameOptionData.PASSWORD = "pw";
+cah.$.GameOptionData.SPECTATOR_LIMIT = "vL";
cah.$.GameOptionData.SCORE_LIMIT = "sl";
+cah.$.GameOptionData.BLANKS_LIMIT = "bl";
+cah.$.GameOptionData.PLAYER_LIMIT = "pL";
+cah.$.GameOptionData.CARD_SETS = "css";
cah.$.GameInfo = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.GameInfo.prototype.dummyForAutocomplete = undefined;
-cah.$.GameInfo.HOST = "H";
-cah.$.GameInfo.STATE = "S";
+cah.$.GameInfo.GAME_OPTIONS = "go";
cah.$.GameInfo.PLAYERS = "P";
cah.$.GameInfo.SPECTATORS = "V";
+cah.$.GameInfo.HOST = "H";
+cah.$.GameInfo.STATE = "S";
cah.$.GameInfo.ID = "gid";
-cah.$.GameInfo.GAME_OPTIONS = "go";
cah.$.GameInfo.HAS_PASSWORD = "hp";
cah.$.GameState = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.GameState.prototype.dummyForAutocomplete = undefined;
-cah.$.GameState.PLAYING = "p";
cah.$.GameState.ROUND_OVER = "ro";
cah.$.GameState.LOBBY = "l";
-cah.$.GameState.JUDGING = "j";
cah.$.GameState.DEALING = "d";
+cah.$.GameState.JUDGING = "j";
+cah.$.GameState.PLAYING = "p";
cah.$.GameState_msg = {};
-cah.$.GameState_msg['ro'] = "In Progress";
-cah.$.GameState_msg['d'] = "In Progress";
cah.$.GameState_msg['p'] = "In Progress";
-cah.$.GameState_msg['l'] = "Not Started";
+cah.$.GameState_msg['d'] = "In Progress";
cah.$.GameState_msg['j'] = "In Progress";
+cah.$.GameState_msg['l'] = "Not Started";
+cah.$.GameState_msg['ro'] = "In Progress";
cah.$.CardSetData = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.CardSetData.prototype.dummyForAutocomplete = undefined;
-cah.$.CardSetData.CARD_SET_DESCRIPTION = "csd";
-cah.$.CardSetData.WEIGHT = "w";
-cah.$.CardSetData.CARD_SET_NAME = "csn";
-cah.$.CardSetData.ID = "cid";
cah.$.CardSetData.WHITE_CARDS_IN_DECK = "wcid";
cah.$.CardSetData.BLACK_CARDS_IN_DECK = "bcid";
+cah.$.CardSetData.CARD_SET_NAME = "csn";
+cah.$.CardSetData.CARD_SET_DESCRIPTION = "csd";
cah.$.CardSetData.BASE_DECK = "bd";
+cah.$.CardSetData.ID = "cid";
+cah.$.CardSetData.WEIGHT = "w";
cah.$.BlackCardData = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.BlackCardData.prototype.dummyForAutocomplete = undefined;
-cah.$.BlackCardData.TEXT = "T";
+cah.$.BlackCardData.DRAW = "D";
cah.$.BlackCardData.PICK = "PK";
+cah.$.BlackCardData.TEXT = "T";
cah.$.BlackCardData.ID = "cid";
cah.$.BlackCardData.WATERMARK = "W";
-cah.$.BlackCardData.DRAW = "D";
cah.$.WhiteCardData = function() {
// Dummy constructor to make Eclipse auto-complete.
@@ -114,148 +114,148 @@ cah.$.LongPollResponse = function() {
};
cah.$.LongPollResponse.prototype.dummyForAutocomplete = undefined;
cah.$.LongPollResponse.WALL = "wall";
-cah.$.LongPollResponse.WHITE_CARDS = "wc";
-cah.$.LongPollResponse.REASON = "qr";
-cah.$.LongPollResponse.GAME_ID = "gid";
-cah.$.LongPollResponse.EMOTE = "me";
-cah.$.LongPollResponse.HAND = "h";
-cah.$.LongPollResponse.INTERMISSION = "i";
-cah.$.LongPollResponse.PLAYER_INFO = "pi";
-cah.$.LongPollResponse.BLACK_CARD = "bc";
-cah.$.LongPollResponse.WINNING_CARD = "WC";
-cah.$.LongPollResponse.GAME_STATE = "gs";
-cah.$.LongPollResponse.NICKNAME = "n";
-cah.$.LongPollResponse.CARDCAST_DECK_INFO = "cdi";
cah.$.LongPollResponse.PLAY_TIMER = "Pt";
+cah.$.LongPollResponse.ROUND_WINNER = "rw";
+cah.$.LongPollResponse.EMOTE = "me";
+cah.$.LongPollResponse.CARDCAST_DECK_INFO = "cdi";
+cah.$.LongPollResponse.PLAYER_INFO = "pi";
+cah.$.LongPollResponse.FROM = "f";
+cah.$.LongPollResponse.GAME_ID = "gid";
+cah.$.LongPollResponse.WHITE_CARDS = "wc";
+cah.$.LongPollResponse.EVENT = "E";
+cah.$.LongPollResponse.HAND = "h";
+cah.$.LongPollResponse.ERROR_CODE = "ec";
cah.$.LongPollResponse.MESSAGE = "m";
+cah.$.LongPollResponse.WINNING_CARD = "WC";
+cah.$.LongPollResponse.NICKNAME = "n";
+cah.$.LongPollResponse.BLACK_CARD = "bc";
cah.$.LongPollResponse.FROM_ADMIN = "fa";
+cah.$.LongPollResponse.TIMESTAMP = "ts";
+cah.$.LongPollResponse.GAME_STATE = "gs";
cah.$.LongPollResponse.GAME_INFO = "gi";
cah.$.LongPollResponse.ERROR = "e";
-cah.$.LongPollResponse.EVENT = "E";
-cah.$.LongPollResponse.FROM = "f";
-cah.$.LongPollResponse.ERROR_CODE = "ec";
-cah.$.LongPollResponse.TIMESTAMP = "ts";
-cah.$.LongPollResponse.ROUND_WINNER = "rw";
+cah.$.LongPollResponse.INTERMISSION = "i";
+cah.$.LongPollResponse.REASON = "qr";
cah.$.LongPollEvent = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.LongPollEvent.prototype.dummyForAutocomplete = undefined;
-cah.$.LongPollEvent.KICKED = "k";
-cah.$.LongPollEvent.HURRY_UP = "hu";
-cah.$.LongPollEvent.GAME_JUDGE_SKIPPED = "gjs";
-cah.$.LongPollEvent.GAME_PLAYER_LEAVE = "gpl";
-cah.$.LongPollEvent.KICKED_FROM_GAME_IDLE = "kfgi";
cah.$.LongPollEvent.GAME_ROUND_COMPLETE = "grc";
-cah.$.LongPollEvent.GAME_STATE_CHANGE = "gsc";
-cah.$.LongPollEvent.GAME_OPTIONS_CHANGED = "goc";
-cah.$.LongPollEvent.GAME_PLAYER_SKIPPED = "gps";
-cah.$.LongPollEvent.CHAT = "c";
cah.$.LongPollEvent.BANNED = "B&";
-cah.$.LongPollEvent.GAME_SPECTATOR_LEAVE = "gvl";
-cah.$.LongPollEvent.CARDCAST_ADD_CARDSET = "cac";
-cah.$.LongPollEvent.NEW_PLAYER = "np";
-cah.$.LongPollEvent.GAME_PLAYER_JOIN = "gpj";
-cah.$.LongPollEvent.GAME_SPECTATOR_JOIN = "gvj";
-cah.$.LongPollEvent.GAME_LIST_REFRESH = "glr";
cah.$.LongPollEvent.NOOP = "_";
-cah.$.LongPollEvent.GAME_PLAYER_KICKED_IDLE = "gpki";
+cah.$.LongPollEvent.CHAT = "c";
cah.$.LongPollEvent.GAME_PLAYER_INFO_CHANGE = "gpic";
-cah.$.LongPollEvent.CARDCAST_REMOVE_CARDSET = "crc";
-cah.$.LongPollEvent.GAME_BLACK_RESHUFFLE = "gbr";
-cah.$.LongPollEvent.GAME_WHITE_RESHUFFLE = "gwr";
-cah.$.LongPollEvent.PLAYER_LEAVE = "pl";
cah.$.LongPollEvent.HAND_DEAL = "hd";
+cah.$.LongPollEvent.CARDCAST_ADD_CARDSET = "cac";
+cah.$.LongPollEvent.PLAYER_LEAVE = "pl";
+cah.$.LongPollEvent.GAME_BLACK_RESHUFFLE = "gbr";
+cah.$.LongPollEvent.GAME_JUDGE_SKIPPED = "gjs";
+cah.$.LongPollEvent.GAME_LIST_REFRESH = "glr";
+cah.$.LongPollEvent.NEW_PLAYER = "np";
+cah.$.LongPollEvent.GAME_PLAYER_SKIPPED = "gps";
+cah.$.LongPollEvent.GAME_PLAYER_JOIN = "gpj";
+cah.$.LongPollEvent.GAME_WHITE_RESHUFFLE = "gwr";
+cah.$.LongPollEvent.CARDCAST_REMOVE_CARDSET = "crc";
+cah.$.LongPollEvent.GAME_OPTIONS_CHANGED = "goc";
+cah.$.LongPollEvent.GAME_PLAYER_KICKED_IDLE = "gpki";
+cah.$.LongPollEvent.GAME_SPECTATOR_LEAVE = "gvl";
+cah.$.LongPollEvent.GAME_PLAYER_LEAVE = "gpl";
+cah.$.LongPollEvent.GAME_SPECTATOR_JOIN = "gvj";
+cah.$.LongPollEvent.HURRY_UP = "hu";
cah.$.LongPollEvent.GAME_JUDGE_LEFT = "gjl";
+cah.$.LongPollEvent.KICKED = "k";
+cah.$.LongPollEvent.KICKED_FROM_GAME_IDLE = "kfgi";
+cah.$.LongPollEvent.GAME_STATE_CHANGE = "gsc";
cah.$.ErrorCode = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.ErrorCode.prototype.dummyForAutocomplete = undefined;
-cah.$.ErrorCode.TOO_MANY_GAMES = "tmg";
-cah.$.ErrorCode.NO_CARD_SPECIFIED = "ncs";
-cah.$.ErrorCode.ACCESS_DENIED = "ad";
-cah.$.ErrorCode.NOT_GAME_HOST = "ngh";
-cah.$.ErrorCode.CANNOT_JOIN_ANOTHER_GAME = "cjag";
-cah.$.ErrorCode.INVALID_CARD = "ic";
-cah.$.ErrorCode.RESERVED_NICK = "rn";
-cah.$.ErrorCode.TOO_MANY_USERS = "tmu";
-cah.$.ErrorCode.NO_GAME_SPECIFIED = "ngs";
-cah.$.ErrorCode.SESSION_EXPIRED = "se";
-cah.$.ErrorCode.CARDCAST_INVALID_ID = "cii";
-cah.$.ErrorCode.BAD_OP = "bo";
-cah.$.ErrorCode.TOO_FAST = "tf";
-cah.$.ErrorCode.NO_SESSION = "ns";
-cah.$.ErrorCode.NOT_REGISTERED = "nr";
-cah.$.ErrorCode.OP_NOT_SPECIFIED = "ons";
-cah.$.ErrorCode.NOT_JUDGE = "nj";
-cah.$.ErrorCode.WRONG_PASSWORD = "wp";
-cah.$.ErrorCode.NOT_IN_THAT_GAME = "nitg";
-cah.$.ErrorCode.NICK_IN_USE = "niu";
-cah.$.ErrorCode.SERVER_ERROR = "serr";
-cah.$.ErrorCode.GAME_FULL = "gf";
-cah.$.ErrorCode.NO_NICK_SPECIFIED = "nns";
-cah.$.ErrorCode.NOT_ADMIN = "na";
-cah.$.ErrorCode.NOT_YOUR_TURN = "nyt";
-cah.$.ErrorCode.BANNED = "B&";
cah.$.ErrorCode.INVALID_NICK = "in";
-cah.$.ErrorCode.ALREADY_STARTED = "as";
-cah.$.ErrorCode.BAD_REQUEST = "br";
-cah.$.ErrorCode.NO_SUCH_USER = "nsu";
-cah.$.ErrorCode.DO_NOT_HAVE_CARD = "dnhc";
+cah.$.ErrorCode.TOO_MANY_USERS = "tmu";
+cah.$.ErrorCode.NOT_GAME_HOST = "ngh";
cah.$.ErrorCode.MESSAGE_TOO_LONG = "mtl";
-cah.$.ErrorCode.ALREADY_STOPPED = "aS";
-cah.$.ErrorCode.NOT_ENOUGH_PLAYERS = "nep";
-cah.$.ErrorCode.INVALID_GAME = "ig";
-cah.$.ErrorCode.CARDCAST_CANNOT_FIND = "ccf";
+cah.$.ErrorCode.NO_NICK_SPECIFIED = "nns";
+cah.$.ErrorCode.BANNED = "B&";
+cah.$.ErrorCode.WRONG_PASSWORD = "wp";
+cah.$.ErrorCode.RESERVED_NICK = "rn";
+cah.$.ErrorCode.TOO_MANY_GAMES = "tmg";
+cah.$.ErrorCode.CANNOT_JOIN_ANOTHER_GAME = "cjag";
cah.$.ErrorCode.NO_MSG_SPECIFIED = "nms";
+cah.$.ErrorCode.ALREADY_STARTED = "as";
+cah.$.ErrorCode.NOT_ADMIN = "na";
+cah.$.ErrorCode.INVALID_GAME = "ig";
+cah.$.ErrorCode.NO_SESSION = "ns";
+cah.$.ErrorCode.ACCESS_DENIED = "ad";
+cah.$.ErrorCode.NICK_IN_USE = "niu";
+cah.$.ErrorCode.NOT_JUDGE = "nj";
+cah.$.ErrorCode.SERVER_ERROR = "serr";
+cah.$.ErrorCode.CARDCAST_INVALID_ID = "cii";
+cah.$.ErrorCode.TOO_FAST = "tf";
cah.$.ErrorCode.NOT_ENOUGH_CARDS = "nec";
+cah.$.ErrorCode.NO_CARD_SPECIFIED = "ncs";
+cah.$.ErrorCode.NO_GAME_SPECIFIED = "ngs";
+cah.$.ErrorCode.OP_NOT_SPECIFIED = "ons";
+cah.$.ErrorCode.BAD_REQUEST = "br";
+cah.$.ErrorCode.NOT_ENOUGH_PLAYERS = "nep";
+cah.$.ErrorCode.CARDCAST_CANNOT_FIND = "ccf";
+cah.$.ErrorCode.NOT_IN_THAT_GAME = "nitg";
+cah.$.ErrorCode.NO_SUCH_USER = "nsu";
+cah.$.ErrorCode.NOT_REGISTERED = "nr";
+cah.$.ErrorCode.BAD_OP = "bo";
+cah.$.ErrorCode.DO_NOT_HAVE_CARD = "dnhc";
+cah.$.ErrorCode.NOT_YOUR_TURN = "nyt";
+cah.$.ErrorCode.ALREADY_STOPPED = "aS";
+cah.$.ErrorCode.SESSION_EXPIRED = "se";
+cah.$.ErrorCode.GAME_FULL = "gf";
+cah.$.ErrorCode.INVALID_CARD = "ic";
cah.$.ErrorCode_msg = {};
-cah.$.ErrorCode_msg['tmg'] = "There are too many games already in progress. Either join an existing game, or wait for one to become available.";
-cah.$.ErrorCode_msg['ncs'] = "No card specified.";
-cah.$.ErrorCode_msg['ns'] = "Session not detected. Make sure you have cookies enabled.";
-cah.$.ErrorCode_msg['rn'] = "That nick is reserved.";
-cah.$.ErrorCode_msg['nr'] = "Not registered. Refresh the page.";
-cah.$.ErrorCode_msg['nitg'] = "You are not in that game.";
-cah.$.ErrorCode_msg['nep'] = "There are not enough players to start the game.";
-cah.$.ErrorCode_msg['tf'] = "You are chatting too fast. Wait a few seconds and try again.";
-cah.$.ErrorCode_msg['nyt'] = "It is not your turn to play a card.";
-cah.$.ErrorCode_msg['gf'] = "That game is full. Join another.";
-cah.$.ErrorCode_msg['aS'] = "The game has already stopped.";
-cah.$.ErrorCode_msg['mtl'] = "Messages cannot be longer than 200 characters.";
-cah.$.ErrorCode_msg['br'] = "Bad request.";
-cah.$.ErrorCode_msg['ngs'] = "No game specified.";
-cah.$.ErrorCode_msg['ic'] = "Invalid card specified.";
-cah.$.ErrorCode_msg['bo'] = "Invalid operation.";
-cah.$.ErrorCode_msg['tmu'] = "There are too many users connected. Either join another server, or wait for a user to disconnect.";
-cah.$.ErrorCode_msg['dnhc'] = "You don't have that card.";
-cah.$.ErrorCode_msg['ons'] = "Operation not specified.";
-cah.$.ErrorCode_msg['cjag'] = "You cannot join another game.";
-cah.$.ErrorCode_msg['ig'] = "Invalid game specified.";
-cah.$.ErrorCode_msg['nns'] = "No nickname specified.";
cah.$.ErrorCode_msg['cii'] = "Invalid Cardcast ID. Must be exactly 5 characters.";
-cah.$.ErrorCode_msg['ngh'] = "Only the game host can do that.";
-cah.$.ErrorCode_msg['nec'] = "You must add card sets containing at least 50 black cards and 20 times the player limit white cards.";
-cah.$.ErrorCode_msg['serr'] = "An error occured on the server.";
+cah.$.ErrorCode_msg['nr'] = "Not registered. Refresh the page.";
+cah.$.ErrorCode_msg['ns'] = "Session not detected. Make sure you have cookies enabled.";
cah.$.ErrorCode_msg['ccf'] = "Cannot find Cardcast deck with given ID. If you just added this deck to Cardcast, wait a few minutes and try again.";
+cah.$.ErrorCode_msg['nyt'] = "It is not your turn to play a card.";
+cah.$.ErrorCode_msg['bo'] = "Invalid operation.";
+cah.$.ErrorCode_msg['nec'] = "You must add card sets containing at least 50 black cards and 20 times the player limit white cards.";
+cah.$.ErrorCode_msg['ngh'] = "Only the game host can do that.";
+cah.$.ErrorCode_msg['tmg'] = "There are too many games already in progress. Either join an existing game, or wait for one to become available.";
+cah.$.ErrorCode_msg['br'] = "Bad request.";
cah.$.ErrorCode_msg['nsu'] = "No such user.";
-cah.$.ErrorCode_msg['wp'] = "That password is incorrect.";
-cah.$.ErrorCode_msg['as'] = "The game has already started.";
+cah.$.ErrorCode_msg['aS'] = "The game has already stopped.";
cah.$.ErrorCode_msg['se'] = "Your session has expired. Refresh the page.";
-cah.$.ErrorCode_msg['in'] = "Nickname must contain only upper and lower case letters, numbers, or underscores, must be 3 to 30 characters long, and must not start with a number.";
cah.$.ErrorCode_msg['nms'] = "No message specified.";
-cah.$.ErrorCode_msg['na'] = "You are not an administrator.";
+cah.$.ErrorCode_msg['nep'] = "There are not enough players to start the game.";
+cah.$.ErrorCode_msg['wp'] = "That password is incorrect.";
+cah.$.ErrorCode_msg['ic'] = "Invalid card specified.";
cah.$.ErrorCode_msg['niu'] = "Nickname is already in use.";
-cah.$.ErrorCode_msg['B&'] = "Banned.";
+cah.$.ErrorCode_msg['ngs'] = "No game specified.";
+cah.$.ErrorCode_msg['nitg'] = "You are not in that game.";
+cah.$.ErrorCode_msg['tmu'] = "There are too many users connected. Either join another server, or wait for a user to disconnect.";
+cah.$.ErrorCode_msg['ig'] = "Invalid game specified.";
+cah.$.ErrorCode_msg['gf'] = "That game is full. Join another.";
+cah.$.ErrorCode_msg['ncs'] = "No card specified.";
cah.$.ErrorCode_msg['ad'] = "Access denied.";
+cah.$.ErrorCode_msg['cjag'] = "You cannot join another game.";
+cah.$.ErrorCode_msg['B&'] = "Banned.";
+cah.$.ErrorCode_msg['mtl'] = "Messages cannot be longer than 200 characters.";
+cah.$.ErrorCode_msg['in'] = "Nickname must contain only upper and lower case letters, numbers, or underscores, must be 3 to 30 characters long, and must not start with a number.";
+cah.$.ErrorCode_msg['serr'] = "An error occured on the server.";
+cah.$.ErrorCode_msg['dnhc'] = "You don't have that card.";
+cah.$.ErrorCode_msg['as'] = "The game has already started.";
+cah.$.ErrorCode_msg['nns'] = "No nickname specified.";
+cah.$.ErrorCode_msg['tf'] = "You are chatting too fast. Wait a few seconds and try again.";
+cah.$.ErrorCode_msg['na'] = "You are not an administrator.";
+cah.$.ErrorCode_msg['ons'] = "Operation not specified.";
cah.$.ErrorCode_msg['nj'] = "You are not the judge.";
+cah.$.ErrorCode_msg['rn'] = "That nick is reserved.";
cah.$.ErrorInformation = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.ErrorInformation.prototype.dummyForAutocomplete = undefined;
-cah.$.ErrorInformation.WHITE_CARDS_REQUIRED = "wcr";
cah.$.ErrorInformation.WHITE_CARDS_PRESENT = "wcp";
+cah.$.ErrorInformation.WHITE_CARDS_REQUIRED = "wcr";
cah.$.ErrorInformation.BLACK_CARDS_REQUIRED = "bcr";
cah.$.ErrorInformation.BLACK_CARDS_PRESENT = "bcp";
@@ -263,70 +263,70 @@ cah.$.AjaxResponse = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.AjaxResponse.prototype.dummyForAutocomplete = undefined;
-cah.$.AjaxResponse.WHITE_CARDS = "wc";
-cah.$.AjaxResponse.CARD_SETS = "css";
-cah.$.AjaxResponse.GAME_ID = "gid";
-cah.$.AjaxResponse.HAND = "h";
-cah.$.AjaxResponse.PLAYER_INFO = "pi";
-cah.$.AjaxResponse.BLACK_CARD = "bc";
-cah.$.AjaxResponse.GAME_OPTIONS = "go";
-cah.$.AjaxResponse.IN_PROGRESS = "ip";
+cah.$.AjaxResponse.SERIAL = "s";
+cah.$.AjaxResponse.NAMES = "nl";
cah.$.AjaxResponse.GAMES = "gl";
+cah.$.AjaxResponse.PLAYER_INFO = "pi";
+cah.$.AjaxResponse.GAME_ID = "gid";
+cah.$.AjaxResponse.WHITE_CARDS = "wc";
+cah.$.AjaxResponse.HAND = "h";
+cah.$.AjaxResponse.ERROR_CODE = "ec";
+cah.$.AjaxResponse.MAX_GAMES = "mg";
+cah.$.AjaxResponse.IN_PROGRESS = "ip";
+cah.$.AjaxResponse.GAME_OPTIONS = "go";
cah.$.AjaxResponse.NICKNAME = "n";
-cah.$.AjaxResponse.CARD_ID = "cid";
+cah.$.AjaxResponse.BLACK_CARD = "bc";
cah.$.AjaxResponse.NEXT = "next";
cah.$.AjaxResponse.GAME_INFO = "gi";
+cah.$.AjaxResponse.CARD_ID = "cid";
cah.$.AjaxResponse.ERROR = "e";
-cah.$.AjaxResponse.ERROR_CODE = "ec";
-cah.$.AjaxResponse.SERIAL = "s";
-cah.$.AjaxResponse.MAX_GAMES = "mg";
-cah.$.AjaxResponse.NAMES = "nl";
+cah.$.AjaxResponse.CARD_SETS = "css";
cah.$.AjaxRequest = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.AjaxRequest.prototype.dummyForAutocomplete = undefined;
-cah.$.AjaxRequest.WALL = "wall";
-cah.$.AjaxRequest.MESSAGE = "m";
-cah.$.AjaxRequest.CARD_ID = "cid";
-cah.$.AjaxRequest.GAME_ID = "gid";
-cah.$.AjaxRequest.EMOTE = "me";
-cah.$.AjaxRequest.CARDCAST_ID = "cci";
-cah.$.AjaxRequest.GAME_OPTIONS = "go";
cah.$.AjaxRequest.SERIAL = "s";
-cah.$.AjaxRequest.PASSWORD = "pw";
+cah.$.AjaxRequest.GAME_OPTIONS = "go";
+cah.$.AjaxRequest.MESSAGE = "m";
cah.$.AjaxRequest.OP = "o";
cah.$.AjaxRequest.NICKNAME = "n";
+cah.$.AjaxRequest.WALL = "wall";
+cah.$.AjaxRequest.PASSWORD = "pw";
+cah.$.AjaxRequest.EMOTE = "me";
+cah.$.AjaxRequest.CARD_ID = "cid";
+cah.$.AjaxRequest.CARDCAST_ID = "cci";
+cah.$.AjaxRequest.GAME_ID = "gid";
cah.$.AjaxOperation = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.AjaxOperation.prototype.dummyForAutocomplete = undefined;
-cah.$.AjaxOperation.FIRST_LOAD = "fl";
-cah.$.AjaxOperation.START_GAME = "sg";
-cah.$.AjaxOperation.JUDGE_SELECT = "js";
-cah.$.AjaxOperation.LOG_OUT = "lo";
-cah.$.AjaxOperation.GAME_LIST = "ggl";
-cah.$.AjaxOperation.CHANGE_GAME_OPTIONS = "cgo";
-cah.$.AjaxOperation.PLAY_CARD = "pc";
-cah.$.AjaxOperation.CREATE_GAME = "cg";
-cah.$.AjaxOperation.CARDCAST_LIST_CARDSETS = "clc";
-cah.$.AjaxOperation.GAME_CHAT = "GC";
-cah.$.AjaxOperation.KICK = "K";
-cah.$.AjaxOperation.ADMIN_SET_VERBOSE_LOG = "svl";
-cah.$.AjaxOperation.GET_CARDS = "gc";
+cah.$.AjaxOperation.NAMES = "gn";
+cah.$.AjaxOperation.SCORE = "SC";
+cah.$.AjaxOperation.LEAVE_GAME = "lg";
cah.$.AjaxOperation.JOIN_GAME = "jg";
cah.$.AjaxOperation.CHAT = "c";
-cah.$.AjaxOperation.NAMES = "gn";
-cah.$.AjaxOperation.SPECTATE_GAME = "vg";
-cah.$.AjaxOperation.BAN = "b";
-cah.$.AjaxOperation.SCORE = "SC";
-cah.$.AjaxOperation.GET_GAME_INFO = "ggi";
+cah.$.AjaxOperation.GAME_LIST = "ggl";
cah.$.AjaxOperation.CARDCAST_ADD_CARDSET = "cac";
-cah.$.AjaxOperation.CARDCAST_REMOVE_CARDSET = "crc";
+cah.$.AjaxOperation.CARDCAST_LIST_CARDSETS = "clc";
+cah.$.AjaxOperation.PLAY_CARD = "pc";
+cah.$.AjaxOperation.CHANGE_GAME_OPTIONS = "cgo";
+cah.$.AjaxOperation.GET_GAME_INFO = "ggi";
+cah.$.AjaxOperation.GET_CARDS = "gc";
+cah.$.AjaxOperation.ADMIN_SET_VERBOSE_LOG = "svl";
cah.$.AjaxOperation.REGISTER = "r";
+cah.$.AjaxOperation.CARDCAST_REMOVE_CARDSET = "crc";
+cah.$.AjaxOperation.KICK = "K";
+cah.$.AjaxOperation.FIRST_LOAD = "fl";
+cah.$.AjaxOperation.START_GAME = "sg";
+cah.$.AjaxOperation.LOG_OUT = "lo";
+cah.$.AjaxOperation.BAN = "b";
+cah.$.AjaxOperation.CREATE_GAME = "cg";
cah.$.AjaxOperation.STOP_GAME = "Sg";
-cah.$.AjaxOperation.LEAVE_GAME = "lg";
+cah.$.AjaxOperation.GAME_CHAT = "GC";
+cah.$.AjaxOperation.SPECTATE_GAME = "vg";
+cah.$.AjaxOperation.JUDGE_SELECT = "js";
cah.$.ReconnectNextAction = function() {
// Dummy constructor to make Eclipse auto-complete.
@@ -339,9 +339,9 @@ cah.$.DisconnectReason = function() {
// Dummy constructor to make Eclipse auto-complete.
};
cah.$.DisconnectReason.prototype.dummyForAutocomplete = undefined;
-cah.$.DisconnectReason.BANNED = "B&";
cah.$.DisconnectReason.PING_TIMEOUT = "pt";
+cah.$.DisconnectReason.BANNED = "B&";
+cah.$.DisconnectReason.IDLE_TIMEOUT = "it";
cah.$.DisconnectReason.KICKED = "k";
cah.$.DisconnectReason.MANUAL = "man";
-cah.$.DisconnectReason.IDLE_TIMEOUT = "it";
diff --git a/WebContent/js/cah.game.js b/WebContent/js/cah.game.js
index b020bcd..e339cf3 100644
--- a/WebContent/js/cah.game.js
+++ b/WebContent/js/cah.game.js
@@ -95,7 +95,7 @@ cah.Game = function(id) {
$("#game_password_template_label", this.optionsElement_).attr("for", "game_password_" + id);
$("#game_hide_password_template_label", this.optionsElement_).attr("for",
"game_hide_password_" + id);
- $("#use_timer_template_label", this.optionsElement_).attr("for", "use_timer_" + id);
+ $("#timer_multiplier_template_label", this.optionsElement_).attr("for", "timer_multiplier_" + id);
$("#score_limit_template", this.optionsElement_).attr("id", "score_limit_" + id);
$("#player_limit_template", this.optionsElement_).attr("id", "player_limit_" + id);
@@ -104,7 +104,7 @@ cah.Game = function(id) {
$("#game_password_template", this.optionsElement_).attr("id", "game_password_" + id);
$("#game_fake_password_template", this.optionsElement_).attr("id", "game_fake_password_" + id);
$("#game_hide_password_template", this.optionsElement_).attr("id", "game_hide_password_" + id);
- $("#use_timer_template", this.optionsElement_).attr("id", "use_timer_" + id);
+ $("#timer_multiplier_template", this.optionsElement_).attr("id", "timer_multiplier_" + id);
$("#blanks_limit_template", this.optionsElement_).attr("id", "blanks_limit_" + id);
for ( var key in cah.CardSet.byWeight) {
@@ -307,7 +307,7 @@ cah.Game = function(id) {
$(".game_show_options", this.element_).click(cah.bind(this, this.showOptionsClick_));
$("select", this.optionsElement_).change(cah.bind(this, this.optionChanged_));
$("input", this.optionsElement_).blur(cah.bind(this, this.optionChanged_));
- $(".use_timer", this.optionsElement_).change(cah.bind(this, this.optionChanged_));
+ $(".timer_multiplier", this.optionsElement_).change(cah.bind(this, this.optionChanged_));
$(".card_set", this.optionsElement_).change(cah.bind(this, this.optionChanged_));
$(".game_hide_password", this.optionsElement_).click(cah.bind(this, this.showOrHidePassword_));
@@ -853,11 +853,8 @@ cah.Game.prototype.updateGameStatus = function(data) {
$(".player_limit", this.optionsElement_).val(options[cah.$.GameOptionData.PLAYER_LIMIT]);
$(".spectator_limit", this.optionsElement_).val(options[cah.$.GameOptionData.SPECTATOR_LIMIT]);
$(".game_password", this.optionsElement_).val(options[cah.$.GameOptionData.PASSWORD]);
- if (options[cah.$.GameOptionData.USE_TIMER]) {
- $(".use_timer", this.optionsElement_).attr("checked", "checked");
- } else {
- $(".use_timer", this.optionsElement_).removeAttr("checked");
- }
+ $(".timer_multiplier", this.optionsElement_).val(options[cah.$.GameOptionData.TIMER_MULTIPLIER]);
+
var cardSetIds = options[cah.$.GameOptionData.CARD_SETS];// .split(',');
$(".card_set", this.optionsElement_).removeAttr("checked");
for ( var key in cardSetIds) {
@@ -1482,7 +1479,8 @@ cah.Game.prototype.optionChanged_ = function(e) {
options[cah.$.GameOptionData.SPECTATOR_LIMIT] = $(".spectator_limit", this.optionsElement_).val();
options[cah.$.GameOptionData.PASSWORD] = $(".game_password", this.optionsElement_).val();
options[cah.$.GameOptionData.BLANKS_LIMIT] = $(".blanks_limit", this.optionsElement_).val();
- options[cah.$.GameOptionData.USE_TIMER] = !!$('.use_timer', this.optionsElement_).attr('checked');
+ options[cah.$.GameOptionData.TIMER_MULTIPLIER] = $('.timer_multiplier', this.optionsElement_)
+ .val();
cah.Ajax.build(cah.$.AjaxOperation.CHANGE_GAME_OPTIONS).withGameId(this.id_).withGameOptions(
options).run();
diff --git a/src/main/java/net/socialgamer/cah/Constants.java b/src/main/java/net/socialgamer/cah/Constants.java
index e6c9720..13f332b 100644
--- a/src/main/java/net/socialgamer/cah/Constants.java
+++ b/src/main/java/net/socialgamer/cah/Constants.java
@@ -670,7 +670,7 @@ public class Constants {
PLAYER_LIMIT("pL"),
SPECTATOR_LIMIT("vL"),
SCORE_LIMIT("sl"),
- USE_TIMER("ut");
+ TIMER_MULTIPLIER("tm");
private final String key;
diff --git a/src/main/java/net/socialgamer/cah/data/Game.java b/src/main/java/net/socialgamer/cah/data/Game.java
index 6abf203..328d59b 100644
--- a/src/main/java/net/socialgamer/cah/data/Game.java
+++ b/src/main/java/net/socialgamer/cah/data/Game.java
@@ -33,6 +33,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -40,6 +41,12 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.apache.log4j.Logger;
+import org.hibernate.Session;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
import net.socialgamer.cah.Constants.BlackCardData;
import net.socialgamer.cah.Constants.ErrorCode;
import net.socialgamer.cah.Constants.GameInfo;
@@ -56,12 +63,6 @@ import net.socialgamer.cah.data.GameManager.GameId;
import net.socialgamer.cah.data.QueuedMessage.MessageType;
import net.socialgamer.cah.task.SafeTimerTask;
-import org.apache.log4j.Logger;
-import org.hibernate.Session;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
/**
* Game data and logic class. Games are simple finite state machines, with 3 states that wait for
@@ -151,6 +152,14 @@ public class Game {
*/
private final static int JUDGE_TIMEOUT_PER_CARD = 7 * 1000;
private final static int MAX_SKIPS_BEFORE_KICK = 2;
+ private final static Set FINITE_PLAYTIMES;
+ static
+ {
+ final Set finitePlaytimes = new TreeSet(Arrays.asList(
+ new String[]{"0.25x", "0.5x", "0.75x", "1x", "1.25x", "1.5x", "1.75x", "2x", "2.5x", "3x", "4x", "5x", "10x"}));
+ FINITE_PLAYTIMES = Collections.unmodifiableSet(finitePlaytimes);
+ }
+
/**
* Lock object to prevent judging during idle judge detection and vice-versa.
*/
@@ -818,8 +827,7 @@ public class Game {
}
// Perhaps figure out a better way to do this...
- final int playTimer = options.useIdleTimer ? PLAY_TIMEOUT_BASE
- + (PLAY_TIMEOUT_PER_CARD * blackCard.getPick()) : Integer.MAX_VALUE;
+ final int playTimer = calculateTime(PLAY_TIMEOUT_BASE + (PLAY_TIMEOUT_PER_CARD * blackCard.getPick()));
final HashMap data = getEventMap();
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_STATE_CHANGE.toString());
@@ -841,6 +849,28 @@ public class Game {
}
}
+ private int calculateTime(final int base) {
+ double factor = 1.0d;
+ final String tm = options.timerMultiplier;
+
+ if(tm.equals("Unlimited")) {
+ return Integer.MAX_VALUE;
+ }
+
+ if(FINITE_PLAYTIMES.contains(tm))
+ {
+ factor = Double.valueOf(tm.substring(0, tm.length() - 2));
+ }
+
+ final long retval = Math.round(base * factor);
+
+ if(retval > Integer.MAX_VALUE) {
+ return Integer.MAX_VALUE;
+ }
+
+ return (int) retval;
+ }
+
/**
* Warn players that have not yet played that they are running out of time to do so.
*
@@ -1010,8 +1040,7 @@ public class Game {
state = GameState.JUDGING;
// Perhaps figure out a better way to do this...
- final int judgeTimer = options.useIdleTimer ? JUDGE_TIMEOUT_BASE
- + (JUDGE_TIMEOUT_PER_CARD * playedCards.size() * blackCard.getPick()) : Integer.MAX_VALUE;
+ final int judgeTimer = calculateTime(JUDGE_TIMEOUT_BASE + (JUDGE_TIMEOUT_PER_CARD * playedCards.size() * blackCard.getPick()));
final HashMap data = getEventMap();
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_STATE_CHANGE.toString());
diff --git a/src/main/java/net/socialgamer/cah/data/GameOptions.java b/src/main/java/net/socialgamer/cah/data/GameOptions.java
index 1fce7ac..7867bab 100644
--- a/src/main/java/net/socialgamer/cah/data/GameOptions.java
+++ b/src/main/java/net/socialgamer/cah/data/GameOptions.java
@@ -59,7 +59,7 @@ public class GameOptions {
public int scoreGoal = DEFAULT_SCORE_LIMIT;
public final Set cardSetIds = new HashSet();
public String password = "";
- public boolean useIdleTimer = true;
+ public String timerMultiplier = "1.0x";
/**
* Update the options in-place (so that the Game doesn't need more locks).
@@ -77,7 +77,7 @@ public class GameOptions {
}
this.blanksInDeck = newOptions.blanksInDeck;
this.password = newOptions.password;
- this.useIdleTimer = newOptions.useIdleTimer;
+ this.timerMultiplier = newOptions.timerMultiplier;
}
/**
@@ -96,7 +96,7 @@ public class GameOptions {
info.put(GameOptionData.PLAYER_LIMIT, playerLimit);
info.put(GameOptionData.SPECTATOR_LIMIT, spectatorLimit);
info.put(GameOptionData.SCORE_LIMIT, scoreGoal);
- info.put(GameOptionData.USE_TIMER, useIdleTimer);
+ info.put(GameOptionData.TIMER_MULTIPLIER, timerMultiplier);
if (includePassword) {
info.put(GameOptionData.PASSWORD, password);
}
@@ -128,7 +128,7 @@ public class GameOptions {
json.getInteger(GameOptionData.SPECTATOR_LIMIT, options.spectatorLimit)));
options.scoreGoal = Math.max(MIN_SCORE_LIMIT, Math.min(MAX_SCORE_LIMIT,
json.getInteger(GameOptionData.SCORE_LIMIT, options.scoreGoal)));
- options.useIdleTimer = json.getBoolean(GameOptionData.USE_TIMER, options.useIdleTimer);
+ options.timerMultiplier = json.getString(GameOptionData.TIMER_MULTIPLIER, options.timerMultiplier);
options.password = json.getString(GameOptionData.PASSWORD, options.password);
return options;