+let server = {
+ init: function(url) {
+ this.url = url;
+ this.websocket = new WebSocket(this.url);
+ this.websocket.onopen = function(event) {
+ window.setInterval(function() { server.send(['PING']) }, 30000);
+ tui.log_msg("@ server connected! :)");
+ tui.switch_mode(mode_login);
+ };
+ this.websocket.onclose = function(event) {
+ tui.log_msg("@ server disconnected :(");
+ tui.log_msg("@ hint: try the '/reconnect' command");
+ };
+ this.websocket.onmessage = this.handle_event;
+ },
+ reconnect: function() {
+ this.reconnect_to(this.url);
+ },
+ reconnect_to: function(url) {
+ this.websocket.close();
+ this.init(url);
+ },
+ send: function(tokens) {
+ this.websocket.send(unparser.untokenize(tokens));
+ },
+ handle_event: function(event) {
+ let tokens = parser.tokenize(event.data)[0];
+ if (tokens[0] === 'TURN') {
+ game.turn_complete = false;
+ game.things = {};
+ game.portals = {};
+ game.turn = parseInt(tokens[1]);
+ } else if (tokens[0] === 'THING_POS') {
+ game.get_thing(tokens[1], true).position = parser.parse_yx(tokens[2]);
+ } else if (tokens[0] === 'THING_NAME') {
+ game.get_thing(tokens[1], true).name_ = tokens[2];
+ } else if (tokens[0] === 'MAP') {
+ game.map_geometry = tokens[1];
+ tui.init_keys();
+ game.map_size = parser.parse_yx(tokens[2]);
+ game.map = tokens[3]
+ } else if (tokens[0] === 'GAME_STATE_COMPLETE') {
+ game.turn_complete = true;
+ explorer.empty_info_db();
+ if (tui.mode == mode_post_login_wait) {
+ tui.switch_mode(mode_play);
+ tui.log_help();
+ } else if (tui.mode == mode_study) {
+ explorer.query_info();
+ }
+ let t = game.get_thing(game.player_id);
+ if (t.position in game.portals) {
+ tui.teleport_target = game.portals[t.position];
+ tui.switch_mode(mode_teleport);
+ return;
+ }
+ tui.full_refresh();
+ } else if (tokens[0] === 'CHAT') {
+ tui.log_msg('# ' + tokens[1], 1);
+ } else if (tokens[0] === 'PLAYER_ID') {
+ game.player_id = parseInt(tokens[1]);
+ } else if (tokens[0] === 'LOGIN_OK') {
+ this.send(['GET_GAMESTATE']);
+ tui.switch_mode(mode_post_login_wait);
+ } else if (tokens[0] === 'PORTAL') {
+ let position = parser.parse_yx(tokens[1]);
+ game.portals[position] = tokens[2];
+ } else if (tokens[0] === 'ANNOTATION') {
+ let position = parser.parse_yx(tokens[1]);
+ explorer.update_info_db(position, tokens[2]);
+ } else if (tokens[0] === 'UNHANDLED_INPUT') {
+ tui.log_msg('? unknown command');
+ } else if (tokens[0] === 'PLAY_ERROR') {
+ terminal.blink_screen();
+ } else if (tokens[0] === 'ARGUMENT_ERROR') {
+ tui.log_msg('? syntax error: ' + tokens[1]);
+ } else if (tokens[0] === 'GAME_ERROR') {
+ tui.log_msg('? game error: ' + tokens[1]);
+ } else if (tokens[0] === 'PONG') {
+ console.log('PONG');
+ } else {
+ tui.log_msg('? unhandled input: ' + event.data);
+ }
+ }
+}
+
+let unparser = {
+ quote: function(str) {
+ let quoted = ['"'];
+ for (let i = 0; i < str.length; i++) {
+ let c = str[i];
+ if (['"', '\\'].includes(c)) {
+ quoted.push('\\');
+ };
+ quoted.push(c);
+ }
+ quoted.push('"');
+ return quoted.join('');
+ },
+ to_yx: function(yx_coordinate) {
+ return "Y:" + yx_coordinate[0] + ",X:" + yx_coordinate[1];
+ },
+ untokenize: function(tokens) {
+ let quoted_tokens = [];
+ for (let token of tokens) {
+ quoted_tokens.push(this.quote(token));
+ }
+ return quoted_tokens.join(" ");
+ }
+}
+
+class Mode {
+ constructor(name, has_input_prompt=false, shows_info=false, is_intro=false) {
+ this.name = name;
+ this.has_input_prompt = has_input_prompt;
+ this.shows_info= shows_info;
+ this.is_intro = is_intro;
+ }
+}
+let mode_waiting_for_server = new Mode('waiting_for_server', false, false, true);
+let mode_login = new Mode('login', true, false, true);
+let mode_post_login_wait = new Mode('waiting for game world', false, false, true);
+let mode_chat = new Mode('chat / write messages to players', true, false);
+let mode_annotate = new Mode('add message to map tile', true, true);
+let mode_play = new Mode('play / move around', false, false);
+let mode_study = new Mode('check map tiles for messages', false, true);
+let mode_edit = new Mode('write ASCII char to map tile', false, false);
+let mode_teleport = new Mode('teleport away?', true);
+let mode_portal = new Mode('add portal to map tile', true, true);
+