]> git.cworth.org Git - lmno.games/blob - empires/game.js
empires: Construct reliable paths for all game API endpoints
[lmno.games] / empires / game.js
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.
5  *
6  * That is, if the current path is:
7  *
8  *      https://lmno.games/empires/WXYZ/
9  *
10  * Then a path of "register" goes to:
11  *
12  *      https://lmno.games/empires/WXYZ/register
13  *
14  * Just as we want. But if the current path happens to be:
15  *
16  *      https://lmno.games/empires/WXYZ
17  *
18  * Then a path of "register" goes to:
19  *
20  *      https://lmno.games/empires/register
21  *
22  * Which cannot work since we have lost the game ID in the path.
23  *
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
27  * either way.
28  */
29 function GAME_API(endpoint) {
30   var path = window.location.pathname;
31   if (! path.endsWith('/'));
32       path += '/';
33   path += endpoint;
34   return path;
35 }
36
37 function undisplay(element) {
38   element.style.display="none";
39 }
40
41 function add_message(severity, message) {
42   message = `<div class="message ${severity}" onclick="undisplay(this)">
43 ${message}
44 <span class="hide-button" onclick="undisplay(this.parentElement)">&times</span>
45 </div>`;
46   const message_area = document.getElementById('message-area');
47   message_area.insertAdjacentHTML('beforeend', message);
48 }
49
50 function register(form) {
51   var request = new XMLHttpRequest();
52
53   request.open("POST", GAME_API("register"));
54   request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
55   var data = {
56     "character": form.character.value
57   };
58   request.send(JSON.stringify(data));
59
60   form.reset();
61 }
62
63 function toggle_host_tools() {
64   const host_tools = document.getElementById("host-tools");
65
66   console.log("Toggling, host_tools.style.display is '" + host_tools.style.display + "'");
67
68   if (host_tools.style.display === "block")
69     host_tools.style.display = "none";
70   else
71     host_tools.style.display = "block";
72 }
73
74 function post_reveal() {
75   const request = new XMLHttpRequest();
76
77   request.open("POST", GAME_API("reveal"));
78   request.send();
79 }
80
81 function post_start() {
82   const request = new XMLHttpRequest();
83
84   request.open("POST", GAME_API("start"));
85   request.send();
86 }
87
88 function post_reset() {
89   const request = new XMLHttpRequest();
90
91   request.open("POST", GAME_API("reset"));
92   request.send();
93 }
94
95 const events = new EventSource(GAME_API("events"));
96
97 events.onerror = function(event) {
98   if (event.target.readyState === EventSource.CLOSED) {
99       add_message("danger", "Connection to server lost.");
100   }
101 };
102
103 events.addEventListener("players", function(event) {
104   const players_div = document.getElementById("players-div");
105   const players_element = document.getElementById("players");
106   const players = JSON.parse(event.data);
107
108   players_element.innerHTML = '';
109   for (const player of players) {
110     var li = document.createElement('li');
111     li.id = "player-" + player.id;
112     li.innerText = player.name;
113     players_element.appendChild(li);
114   }
115
116   /* Force players list to be visible. */
117   players_div.style.display = "block";
118 });
119
120 events.addEventListener("player-join", function(event) {
121   const players_div = document.getElementById("players-div");
122   const players = document.getElementById("players");
123   const player = JSON.parse(event.data);
124
125   const li = document.createElement('li');
126   li.id = "player-" + player.id;
127   li.innerText = player.name;
128   players.appendChild(li);
129
130   /* Force players list to be visible. */
131   players_div.style.display = "block";
132 });
133
134 events.addEventListener("player-leave", function(event) {
135   const players = document.getElementById("players");
136   const id = JSON.parse(event.data);
137   const player = document.getElementById("player-" + id.id);
138
139   add_message("info", player.innerText + " has left the game.");
140   players.removeChild(player);
141 });
142
143 events.addEventListener("game-state", function(event) {
144   const data = JSON.parse(event.data);
145   const old_state = data.old_state;
146   const new_state = data.new_state;
147
148   const hide_selector = ".show-state-" +old_state+ ",.hide-state-" +new_state;
149   const show_selector = ".hide-state-" +old_state+ ",.show-state-" +new_state;
150
151   /* Hide all elements based on the state transition. */
152   var elts = document.querySelectorAll(hide_selector);
153   for (const elt of elts) {
154     elt.style.display = "none";
155   }
156
157   /* And show all elements based on the same state transition. */
158   elts = document.querySelectorAll(show_selector);
159   for (const elt of elts) {
160     elt.style.display = "block";
161   }
162 });
163
164 events.addEventListener("character-reveal", function(event) {
165   const data = JSON.parse(event.data);
166   const character_name = data.character;
167
168   const character = document.getElementById("character-reveal");
169
170   character.innerText = character_name;
171 });
172
173 events.addEventListener("capture", function(event_string) {
174   const players = document.getElementById("players");
175   const event = JSON.parse(event_string.data);
176   const player = document.getElementById("player-" + event.captee);
177
178   players.removeChild(player);
179 });