claimed_words: [], /* { owner_session, word_id, word_obj } */
/* Voting state. */
vote_pending: null,
- /* Letter request state. */
- letter_requests: new Set(),
/* Game state. */
finished: false,
done_players: new Set()
this._claim_timer = null;
this._claim_warning_timer = null;
this._vote_timer = null;
- this._letter_request_timer = null;
+ this._revealing = false;
this._reveal_timer = null;
}
*****************************************************/
/* Deal a letter from bag to center with a countdown reveal. */
- deal_letter() {
- if (this.state.bag.length === 0) return false;
+ handle_deal_letter(request, response) {
+ const session_id = request.session.id;
+ if (!this.state.player_words[session_id]) {
+ response.sendStatus(400);
+ return;
+ }
+
+ if (this.state.bag.length === 0 || this._revealing) {
+ response.json({ ok: false });
+ return;
+ }
const letter = this.state.bag.pop();
const id = this.state.next_letter_id++;
const tile = { id, letter };
this.state.center.push(tile);
- this.state.letter_requests = new Set();
+ this._revealing = true;
+
+ if (this._reveal_timer) clearTimeout(this._reveal_timer);
+ this._reveal_timer = setTimeout(() => {
+ this._revealing = false;
+ this._reveal_timer = null;
+ }, REVEAL_COUNTDOWN_MS);
this.broadcast_event_object("letter-reveal", {
tile,
countdown_ms: REVEAL_COUNTDOWN_MS
});
- /* Also broadcast updated bag count. */
this.broadcast_event_object("bag-count", {
remaining: this.state.bag.length
});
- return true;
- }
-
- /* Handle a player requesting a new letter. */
- handle_request_letter(request, response) {
- const session_id = request.session.id;
- if (!this.state.player_words[session_id]) {
- response.sendStatus(400);
- return;
- }
-
- if (this.state.bag.length === 0) {
- response.json({ remaining: 0 });
- return;
- }
-
- /* Don't allow requests during a reveal countdown. */
- if (this._reveal_timer) {
- response.json({ queued: false });
- return;
- }
-
- this.state.letter_requests.add(session_id);
-
- const joined = this.joined_player_count();
- const votes = this.state.letter_requests.size;
-
- this.broadcast_event_object("letter-request", {
- votes,
- needed: joined
- });
-
- /* Scale timer: all players voting = immediate.
- * One player = full 10-second wait.
- * Linear interpolation between. */
- if (this._letter_request_timer) {
- clearTimeout(this._letter_request_timer);
- this._letter_request_timer = null;
- }
-
- if (votes >= joined) {
- this.deal_letter();
- } else {
- const ratio = votes / joined;
- const delay_ms = Math.round(10000 * (1 - ratio));
- this._letter_request_timer = setTimeout(() => {
- this._letter_request_timer = null;
- this.deal_letter();
- }, delay_ms);
- }
-
- response.json({ queued: true, remaining: this.state.bag.length });
+ response.json({ ok: true });
}
/*****************************************************
request.game.handle_cancel_claim(request, response);
});
-router.post('/request-letter', (request, response) => {
- request.game.handle_request_letter(request, response);
+router.post('/deal-letter', (request, response) => {
+ request.game.handle_deal_letter(request, response);
});
router.post('/vote', (request, response) => {