1 /* Construction of Game API endpoint paths. Logically all endpoints
2 * are just local resources underneath the current path, but just
3 * referring to them by a local name only works if the current path
4 * has a trailing slash.
6 * That is, if the current path is:
8 * https://lmno.games/empires/WXYZ/
10 * Then a path of "register" goes to:
12 * https://lmno.games/empires/WXYZ/register
14 * Just as we want. But if the current path happens to be:
16 * https://lmno.games/empires/WXYZ
18 * Then a path of "register" goes to:
20 * https://lmno.games/empires/register
22 * Which cannot work since we have lost the game ID in the path.
24 * Of course, we would like to have canonical URLs for the game (with
25 * the trailing slash) but since that depends on macehinery outside
26 * the scope of this file, let's construct API paths that will work
29 function GAME_API(endpoint) {
30 var path = window.location.pathname;
31 if (! path.endsWith('/'));
38 spectator_id: undefined,
43 function undisplay(element) {
44 element.style.display="none";
47 function add_message(severity, message) {
48 message = `<div class="message ${severity}" onclick="undisplay(this)">
50 <span class="hide-button" onclick="undisplay(this.parentElement)">×</span>
52 const message_area = document.getElementById('message-area');
53 message_area.insertAdjacentHTML('beforeend', message);
56 function register(form) {
57 const spectator_req = new XMLHttpRequest();
59 /* Before registering as a player, first remove us as a spectator. */
60 spectator_req.open("DELETE", GAME_API("spectator/") + state.spectator_id);
63 const register_req = new XMLHttpRequest();
65 register_req.open("POST", GAME_API("register"));
66 register_req.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
68 "character": form.character.value
70 register_req.send(JSON.stringify(data));
75 function toggle_host_tools() {
76 const host_tools = document.getElementById("host-tools");
78 if (host_tools.style.display === "block")
79 host_tools.style.display = "none";
81 host_tools.style.display = "block";
84 function post_reveal() {
85 const request = new XMLHttpRequest();
87 request.open("POST", GAME_API("reveal"));
91 function post_start() {
92 const request = new XMLHttpRequest();
94 request.open("POST", GAME_API("start"));
98 function post_reset() {
99 const request = new XMLHttpRequest();
101 request.open("POST", GAME_API("reset"));
105 const events = new EventSource(GAME_API("events"));
107 events.onerror = function(event) {
108 if (event.target.readyState === EventSource.CLOSED) {
109 add_message("danger", "Connection to server lost.");
113 events.addEventListener("spectators", function(event) {
114 const spectators_div = document.getElementById("spectators-div");
115 const spectators_element = document.getElementById("spectators");
116 const spectators = JSON.parse(event.data);
118 spectators_element.innerHTML = '';
119 for (const spectator of spectators) {
120 var li = document.createElement('li');
121 li.id = "spectator-" + spectator.id;
122 li.innerText = spectator.name;
123 spectators_element.appendChild(li);
126 /* Force players list to be visible. */
127 spectators_div.style.display = "block";
130 events.addEventListener("spectator-join", function(event) {
131 const spectators_div = document.getElementById("spectators-div");
132 const spectators = document.getElementById("spectators");
133 const spectator = JSON.parse(event.data);
135 const li = document.createElement('li');
136 li.id = "spectator-" + spectator.id;
137 li.innerText = spectator.name;
138 spectators.appendChild(li);
140 /* Force spectators list to be visible. */
141 spectators_div.style.display = "block";
144 events.addEventListener("spectator-leave", function(event) {
145 const spectators = document.getElementById("spectators");
146 const id = JSON.parse(event.data);
147 const spectator = document.getElementById("spectator-" + id.id);
149 spectators.removeChild(spectator);
152 events.addEventListener("players", function(event) {
153 const players_div = document.getElementById("players-div");
154 const players_element = document.getElementById("players");
155 const players = JSON.parse(event.data);
157 players_element.innerHTML = '';
158 for (const player of players) {
159 var li = document.createElement('li');
160 li.id = "player-" + player.id;
161 li.innerText = player.name;
162 players_element.appendChild(li);
165 /* Force players list to be visible. */
166 players_div.style.display = "block";
169 events.addEventListener("player-join", function(event) {
170 const players_div = document.getElementById("players-div");
171 const players = document.getElementById("players");
172 const player = JSON.parse(event.data);
174 const li = document.createElement('li');
175 li.id = "player-" + player.id;
176 li.innerText = player.name;
177 players.appendChild(li);
179 /* Force players list to be visible. */
180 players_div.style.display = "block";
183 events.addEventListener("player-leave", function(event) {
184 const players = document.getElementById("players");
185 const id = JSON.parse(event.data);
186 const player = document.getElementById("player-" + id.id);
188 add_message("info", player.innerText + " has left the game.");
189 players.removeChild(player);
192 function spectator_on_load() {
193 state.spectator_id = JSON.parse(this.response);
196 events.addEventListener("game-phase", function(event) {
197 const data = JSON.parse(event.data);
198 const old_phase = data.old_phase;
199 const new_phase = data.new_phase;
201 const hide_selector = ".show-phase-" +old_phase+ ",.hide-phase-" +new_phase;
202 const show_selector = ".hide-phase-" +old_phase+ ",.show-phase-" +new_phase;
204 /* Hide all elements based on the phase transition. */
205 var elts = document.querySelectorAll(hide_selector);
206 for (const elt of elts) {
207 elt.style.display = "none";
210 /* And show all elements based on the same phase transition. */
211 elts = document.querySelectorAll(show_selector);
212 for (const elt of elts) {
213 elt.style.display = "block";
216 /* Whenever the game enters the "join" phase, add ourselves as a spectator. */
217 if (new_phase === "join") {
218 const request = new XMLHttpRequest();
220 request.addEventListener("load", spectator_on_load);
221 request.open("POST", GAME_API("spectator"));
226 events.addEventListener("character-reveal", function(event) {
227 const data = JSON.parse(event.data);
228 const character_name = data.character;
230 const character = document.getElementById("character-reveal");
232 character.innerText = character_name;
235 events.addEventListener("capture", function(event_string) {
236 const players = document.getElementById("players");
237 const event = JSON.parse(event_string.data);
238 const player = document.getElementById("player-" + event.captee);
240 players.removeChild(player);