X-Git-Url: https://git.cworth.org/git?p=lmno.games;a=blobdiff_plain;f=empathy%2Fempathy.jsx;h=90bf0fd360829299facce85a1e3593c3a82db63a;hp=5782b7879076bdd8812bccffc58a281b63760d5b;hb=ca50b4b8fd8fc7d5fe23b7bba2544d1eef8de33e;hpb=5d49d1235592034458ce875b5d73d024ef23355d diff --git a/empathy/empathy.jsx b/empathy/empathy.jsx index 5782b78..90bf0fd 100644 --- a/empathy/empathy.jsx +++ b/empathy/empathy.jsx @@ -48,7 +48,7 @@ events.addEventListener("player-enter", event => { events.addEventListener("player-exit", event => { const info = JSON.parse(event.data); - window.game.remove_player(info); + window.game.disable_player(info); }); events.addEventListener("player-update", event => { @@ -227,23 +227,43 @@ const PlayerInfo = React.memo(props => { if (! props.player.id) return null; - const all_players = [props.player, ...props.other_players]; + const all_players = [{...props.player, active:true}, ...props.other_players]; const sorted_players = all_players.sort((a,b) => { return b.score - a.score; }); - const names_and_scores = sorted_players.map(player => { - if (player.score) - return `${player.name} (${player.score})`; - else - return player.name; - }).join(', '); + /* Return a new array with the separator interspersed between + * each element of the array passed in as the argument. + */ + function intersperse(arr, sep) { + return arr.reduce((acc, val) => [...acc, sep, val], []).slice(1); + } + + let names_and_scores = sorted_players.map(player => { + if (player.score) { + return ( + + {player.name} ({player.score}) + + ); + } else { + if (player.active) + return player.name; + else + return null; + } + }).filter(component => component != null); + + names_and_scores = intersperse(names_and_scores, ", "); return (
Players: - {names_and_scores} + {names_and_scores}
); }); @@ -283,7 +303,7 @@ class CategoryRequest extends React.PureComponent { const match = category.match(/[0-9]+/); if (match) { const num_items = parseInt(match[0], 10); - if (num_items <= MAX_PROMPT_ITEMS) + if (num_items > 0 && num_items <= MAX_PROMPT_ITEMS) category_input.setCustomValidity(""); } } @@ -311,6 +331,12 @@ class CategoryRequest extends React.PureComponent { return; } + if (num_items < 1) { + category_input.setCustomValidity("Category must require at least one item."); + form.reportValidity(); + return; + } + const response = await fetch_post_json("prompts", { items: num_items, prompt: category @@ -427,14 +453,34 @@ const PromptOptions = React.memo(props => { const LetsPlay = React.memo(props => { - const quorum = Math.round((props.num_players + 1) / 2); + const quorum = Math.max(0, props.num_players - props.prompts.length); const max_votes = props.prompts.reduce( (max_so_far, v) => Math.max(max_so_far, v.votes.length), 0); - if (max_votes < quorum) - return null; + if (max_votes < quorum) { + let text = `Before we play, we should collect a bit + more information about what category would + be interesting for this group. So, either + type a new category option above, or else`; + if (props.prompts.length) { + if (props.prompts.length > 1) + text += " vote on some of the categories below."; + else + text += " vote on the category below."; + } else { + text += " wait for others to submit, and then vote on them below."; + } + + return ( +
+

+ {text} +

+
+ ); + } - const candidates = props.prompts.filter(p => p.votes.length >= quorum); + const candidates = props.prompts.filter(p => p.votes.length >= max_votes); const index = Math.floor(Math.random() * candidates.length); const winner = candidates[index]; @@ -502,8 +548,11 @@ class Ambiguities extends React.PureComponent { const response = await fetch_post_json( `judged/${this.props.prompt.id}`,{ - word_groups: this.state.word_sets.map(set => Array.from(set)), - kudos: Array.from(this.state.starred) + word_groups: this.state.word_sets.map( + set => ({ + words: Array.from(set), + kudos: this.state.starred === set ? true : false + })) } ); @@ -595,7 +644,7 @@ class Ambiguities extends React.PureComponent { className="vote-button" onClick={() => fetch_post_json(`end-judging/${this.props.prompt.id}`) } > - Move On + Move On Without Their Input
{[...this.props.votes].map(v => { return ( @@ -666,11 +715,16 @@ class Ambiguities extends React.PureComponent {

Judging Answers

- Click on each pair of answers that should be scored as equivalent, - (and click any word twice to split it out from a group). Remember, + Click/tap on each pair of answers that should be scored as equivalent, + (or click a word twice to split it out from a group). Remember, what goes around comes around, so it's best to be generous when judging.

+

+ Also, for an especially fun or witty answer, you can give kudos + by clicking the star on the right. You may only do this for one + word/group. +

{this.props.prompt.prompt}

{this.state.word_sets.map(set => { return ( @@ -696,9 +750,15 @@ class Ambiguities extends React.PureComponent { } onClick={(event) => { event.stopPropagation(); - this.setState({ - starred: set - }); + if (this.state.starred === set) { + this.setState({ + starred: null + }); + } else { + this.setState({ + starred: set + }); + } }} > {this.state.starred === set ? @@ -783,29 +843,6 @@ class ActivePrompt extends React.PureComponent { } render() { - let move_on_button = null; - if (this.props.idle) { - move_on_button =( - - ); - } let still_waiting = null; const answering_players = Object.keys(this.props.players_answering);; @@ -839,6 +876,32 @@ class ActivePrompt extends React.PureComponent { ); } + let move_on_button = null; + if (this.props.idle) { + move_on_button =( + + ); + } + if (this.props.players_answered.has(this.props.player.name)) { return (
@@ -945,9 +1008,16 @@ class Game extends React.PureComponent { }); } - remove_player(info) { + disable_player(info) { + const idx = this.state.other_players.findIndex(o => o.id === info.id); + if (idx < 0) + return; + + const other_players_copy = [...this.state.other_players]; + other_players_copy[idx].active = false; + this.setState({ - other_players: this.state.other_players.filter(o => o.id !== info.id) + other_players: other_players_copy }); } @@ -1186,10 +1256,11 @@ class Game extends React.PureComponent { render() { const state = this.state; - const players_total = 1 + state.other_players.length; if (state.scores) { + const players_total = state.players_answered.size; + let perfect_score = 0; for (let i = 0; i < state.active_prompt.items && @@ -1206,12 +1277,22 @@ class Game extends React.PureComponent {
    {state.scores.scores.map(score => { let perfect = null; - if (score.score == perfect_score) { - perfect = Perfect!; + if (score.score === perfect_score) { + perfect = Perfect!; + } + let quirkster = null; + if (score.score === state.active_prompt.items) { + quirkster = Quirkster!; + } + let kudos_slam = null; + if (score.kudos > 0 && score.kudos >= players_total - 1) { + kudos_slam = Kudos Slam!; } return (
  • - {score.players.join("/")}: {score.score} {perfect} + {score.players.join("/")}: {score.score} + {score.kudos ? `, ${'★'.repeat(score.kudos)}` : ""} + {' '}{perfect} {quirkster} {kudos_slam}
  • ); })} @@ -1219,9 +1300,20 @@ class Game extends React.PureComponent {

    Words submitted

      {state.scores.words.map(word => { + let great_minds = null; + if (word.kudos.length && word.players.length > 1) { + great_minds = Great Minds!; + } + let kudos_slam = null; + if (word.kudos.length > 0 && word.kudos.length >= players_total - 1) { + kudos_slam = Kudos Slam!; + } return (
    • - {word.word} ({word.players.length}): {word.players.join(', ')} + {word.word} ({word.players.length} + {word.kudos.length ? `, ${'★'.repeat(word.kudos.length)}` : ""} + ): {word.players.join(', ')} + {' '}{great_minds}{kudos_slam}
    • ); })} @@ -1290,15 +1382,15 @@ class Game extends React.PureComponent { , + p.active).length} + prompts={state.prompts} + />, , - ]; }