From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 10 Nov 2020 02:20:56 +0000 (+0100) Subject: Add terrain editing access control via passwords. X-Git-Url: https://plomlompom.com/repos/%7B%7Bdb.prefix%7D%7D/static/%7B%7Bprefix%7D%7D/%7B%7Byoutube_prefix%7D%7D%7B%7Bvideo_id%7D%7D?a=commitdiff_plain;h=ff01e1b466c1df7c938d1281ca34718e555bcf67;p=plomrogue2 Add terrain editing access control via passwords. --- diff --git a/config.json b/config.json index c594191..be3072c 100644 --- a/config.json +++ b/config.json @@ -6,6 +6,7 @@ "switch_to_study": "?", "switch_to_edit": "m", "flatten": "F", + "toggle_map_mode": "M", "hex_move_upleft": "w", "hex_move_upright": "e", "hex_move_right": "d", diff --git a/plomrogue/commands.py b/plomrogue/commands.py index e75cbe9..f8c8e3a 100644 --- a/plomrogue/commands.py +++ b/plomrogue/commands.py @@ -105,3 +105,11 @@ def cmd_MAP(game, geometry, size): map_geometry_class = globals()['MapGeometry' + geometry] game.new_world(map_geometry_class(size)) cmd_MAP.argtypes = 'string:map_geometry yx_tuple:pos' + +def cmd_MAP_CONTROL_LINE(game, y, line): + game.map_control.set_line(y, line) +cmd_MAP_CONTROL_LINE.argtypes = 'int:nonneg string' + +def cmd_MAP_CONTROL_PW(game, tile_class, password): + game.map_control_passwords[tile_class] = password +cmd_MAP_CONTROL_PW.argtypes = 'char string' diff --git a/plomrogue/game.py b/plomrogue/game.py index 68bcb65..accf35d 100755 --- a/plomrogue/game.py +++ b/plomrogue/game.py @@ -49,6 +49,8 @@ class Game(GameBase): self.thing_types = {'player': ThingPlayer} self.sessions = {} self.map = Map(self.map_geometry.size) + self.map_control = Map(self.map_geometry.size) + self.map_control_passwords = {} self.annotations = {} self.portals = {} if os.path.exists(self.io.save_file): @@ -98,6 +100,7 @@ class Game(GameBase): send_thing(t) self.io.send('MAP %s %s %s' % (self.get_map_geometry_shape(), self.map_geometry.size, quote(self.map.terrain))) + self.io.send('MAP_CONTROL %s' % quote(self.map_control.terrain)) for yx in self.portals: self.io.send('PORTAL %s %s' % (yx, quote(self.portals[yx]))) self.io.send('GAME_STATE_COMPLETE') @@ -196,8 +199,14 @@ class Game(GameBase): write(f, 'ANNOTATE %s %s' % (yx, quote(self.annotations[yx]))) for yx in self.portals: write(f, 'PORTAL %s %s' % (yx, quote(self.portals[yx]))) + for y, line in self.map_control.lines(): + write(f, 'MAP_CONTROL_LINE %5s %s' % (y, quote(line))) + for tile_class in self.map_control_passwords: + write(f, 'MAP_CONTROL_PW %s %s' % (tile_class, + self.map_control_passwords[tile_class])) def new_world(self, map_geometry): self.map_geometry = map_geometry self.map = Map(self.map_geometry.size) + self.map_control = Map(self.map_geometry.size) self.annotations = {} diff --git a/plomrogue/parser.py b/plomrogue/parser.py index 5782d69..6a6aaaa 100644 --- a/plomrogue/parser.py +++ b/plomrogue/parser.py @@ -106,6 +106,12 @@ class Parser: if not arg.isdigit(): raise ArgError('Argument must be non-negative integer.') args += [int(arg)] + elif tmpl == 'char': + try: + ord(arg) + except TypeError: + raise ArgError('Argument must be single character.') + args += [arg] elif tmpl == 'yx_tuple:nonneg': args += [self.parse_yx_tuple(arg, 'nonneg')] elif tmpl == 'yx_tuple:pos': diff --git a/plomrogue/tasks.py b/plomrogue/tasks.py index 99bb41f..55669ec 100644 --- a/plomrogue/tasks.py +++ b/plomrogue/tasks.py @@ -1,4 +1,4 @@ -from plomrogue.errors import PlayError +from plomrogue.errors import PlayError, GameError from plomrogue.mapping import YX @@ -48,10 +48,14 @@ class Task_MOVE(Task): class Task_WRITE(Task): todo = 1 - argtypes = 'string:char' + argtypes = 'string:char string' def check(self): - pass + tile_class = self.thing.game.map_control[self.thing.position] + if tile_class in self.thing.game.map_control_passwords: + tile_pw = self.thing.game.map_control_passwords[tile_class] + if self.args[1] != tile_pw: + raise GameError('wrong password for tile') def do(self): self.thing.game.map[self.thing.position] = self.args[0] diff --git a/rogue_chat.py b/rogue_chat.py index d2e4117..aebeffb 100755 --- a/rogue_chat.py +++ b/rogue_chat.py @@ -5,7 +5,7 @@ from plomrogue.io_tcp import PlomTCPServer from plomrogue.commands import (cmd_ALL, cmd_LOGIN, cmd_NICK, cmd_QUERY, cmd_PING, cmd_MAP, cmd_TURN, cmd_MAP_LINE, cmd_GET_ANNOTATION, cmd_ANNOTATE, cmd_PORTAL, cmd_GET_GAMESTATE, - cmd_TASKS) + cmd_TASKS, cmd_MAP_CONTROL_LINE, cmd_MAP_CONTROL_PW) from plomrogue.tasks import (Task_WAIT, Task_MOVE, Task_WRITE, Task_FLATTEN_SURROUNDINGS) import sys @@ -22,6 +22,8 @@ game.register_command(cmd_QUERY) game.register_command(cmd_TURN) game.register_command(cmd_MAP) game.register_command(cmd_MAP_LINE) +game.register_command(cmd_MAP_CONTROL_LINE) +game.register_command(cmd_MAP_CONTROL_PW) game.register_command(cmd_GET_ANNOTATION) game.register_command(cmd_ANNOTATE) game.register_command(cmd_PORTAL) diff --git a/rogue_chat_curses.py b/rogue_chat_curses.py index 64eaf03..bf5ad1f 100755 --- a/rogue_chat_curses.py +++ b/rogue_chat_curses.py @@ -103,6 +103,10 @@ def cmd_MAP(game, geometry, size, content): } cmd_MAP.argtypes = 'string:map_geometry yx_tuple:pos string' +def cmd_MAP_CONTROL(game, content): + game.map_control_content = content +cmd_MAP_CONTROL.argtypes = 'string' + def cmd_GAME_STATE_COMPLETE(game): game.info_db = {} if game.tui.mode.name == 'post_login_wait': @@ -166,6 +170,7 @@ class Game(GameBase): self.register_command(cmd_THING_POS) self.register_command(cmd_THING_NAME) self.register_command(cmd_MAP) + self.register_command(cmd_MAP_CONTROL) self.register_command(cmd_PORTAL) self.register_command(cmd_ANNOTATION) self.register_command(cmd_GAME_STATE_COMPLETE) @@ -214,6 +219,7 @@ class TUI: self.mode_login = self.Mode('login', has_input_prompt=True, is_intro=True) self.mode_post_login_wait = self.Mode('post_login_wait', is_intro=True) self.mode_teleport = self.Mode('teleport', has_input_prompt=True) + self.mode_password = self.Mode('password', has_input_prompt=True) self.game = Game() self.game.tui = self self.parser = Parser(self.game) @@ -221,15 +227,19 @@ class TUI: self.do_refresh = True self.queue = queue.Queue() self.login_name = None + self.map_mode = 'terrain' + self.password = 'foo' self.switch_mode('waiting_for_server') self.keys = { 'switch_to_chat': 't', 'switch_to_play': 'p', + 'switch_to_password': 'p', 'switch_to_annotate': 'm', 'switch_to_portal': 'P', 'switch_to_study': '?', 'switch_to_edit': 'm', 'flatten': 'F', + 'toggle_map_mode': 'M', 'hex_move_upleft': 'w', 'hex_move_upright': 'e', 'hex_move_right': 'd', @@ -269,6 +279,7 @@ class TUI: self.send('GET_ANNOTATION ' + str(self.explorer)) def switch_mode(self, mode_name, keep_position = False): + self.map_mode = 'terrain' self.mode = getattr(self, 'mode_' + mode_name) if self.mode.shows_info and not keep_position: player = self.game.get_thing(self.game.player_id, False) @@ -289,6 +300,8 @@ class TUI: self.input_ = info elif self.mode.name == 'portal' and self.explorer in self.game.portals: self.input_ = self.game.portals[self.explorer] + elif self.mode.name == 'password': + self.input_ = self.password def help(self): self.log_msg("HELP:"); @@ -303,16 +316,18 @@ class TUI: self.log_msg(" %s - move" % ','.join(self.movement_keys)); self.log_msg(" %s - switch to chat mode" % self.keys['switch_to_chat']); self.log_msg("commands specific to play mode:"); + self.log_msg(" %s - enter terrain password" % self.keys['switch_to_password']); if 'WRITE' in self.game.tasks: self.log_msg(" %s - write following ASCII character" % self.keys['switch_to_edit']); if 'FLATTEN_SURROUNDINGS' in self.game.tasks: self.log_msg(" %s - flatten surroundings" % self.keys['flatten']); self.log_msg(" %s - switch to study mode" % self.keys['switch_to_study']); self.log_msg("commands specific to study mode:"); + self.log_msg(" %s - switch to play mode" % self.keys['switch_to_play']); if 'MOVE' not in self.game.tasks: self.log_msg(" %s - move" % ','.join(self.movement_keys)); self.log_msg(" %s - annotate terrain" % self.keys['switch_to_annotate']); - self.log_msg(" %s - switch to play mode" % self.keys['switch_to_play']); + self.log_msg(" %s - toggle terrain/control view" % self.keys['toggle_map_mode']); def loop(self, stdscr): import time @@ -455,12 +470,16 @@ class TUI: if not self.game.turn_complete: return map_lines_split = [] + map_content = self.game.map_content + if self.map_mode == 'control': + map_content = self.game.map_control_content for y in range(self.game.map_geometry.size.y): start = self.game.map_geometry.size.x * y end = start + self.game.map_geometry.size.x - map_lines_split += [list(self.game.map_content[start:end])] - for t in self.game.things: - map_lines_split[t.position.y][t.position.x] = '@' + map_lines_split += [list(map_content[start:end])] + if self.map_mode == 'terrain': + for t in self.game.things: + map_lines_split[t.position.y][t.position.x] = '@' if self.mode.shows_info: map_lines_split[self.explorer.y][self.explorer.x] = '?' map_lines = [] @@ -548,6 +567,12 @@ class TUI: self.login_name = self.input_ self.send('LOGIN ' + quote(self.input_)) self.input_ = "" + elif self.mode == self.mode_password and key == '\n': + if self.input_ == '': + self.input_ = ' ' + self.password = self.input_ + self.input_ = "" + self.switch_mode('play') elif self.mode == self.mode_chat and key == '\n': if self.input_[0] == '/': if self.input_ in {'/' + self.keys['switch_to_play'], '/play'}: @@ -605,6 +630,11 @@ class TUI: self.switch_mode('annotate', keep_position=True) elif key == self.keys['switch_to_portal']: self.switch_mode('portal', keep_position=True) + elif key == self.keys['toggle_map_mode']: + if self.map_mode == 'terrain': + self.map_mode = 'control' + else: + self.map_mode = 'terrain' elif key in self.movement_keys: move_explorer(self.movement_keys[key]) elif self.mode == self.mode_play: @@ -612,6 +642,8 @@ class TUI: self.switch_mode('chat') elif key == self.keys['switch_to_study']: self.switch_mode('study') + elif key == self.keys['switch_to_password']: + self.switch_mode('password') if key == self.keys['switch_to_edit'] and\ 'WRITE' in self.game.tasks: self.switch_mode('edit') @@ -621,7 +653,7 @@ class TUI: elif key in self.movement_keys and 'MOVE' in self.game.tasks: self.send('TASK:MOVE ' + self.movement_keys[key]) elif self.mode == self.mode_edit: - self.send('TASK:WRITE ' + key) + self.send('TASK:WRITE %s %s' % (key, quote(self.password))) self.switch_mode('play') TUI('localhost:5000') diff --git a/rogue_chat_nocanvas_monochrome.html b/rogue_chat_nocanvas_monochrome.html index 4adb4ee..942cdbf 100644 --- a/rogue_chat_nocanvas_monochrome.html +++ b/rogue_chat_nocanvas_monochrome.html @@ -26,8 +26,10 @@ switch to chat mode: <input id="key_switch_to_chat" type="text" value="t" /><br switch to play mode: <input id="key_switch_to_play" type="text" value="p" /><br /> switch to study mode: <input id="key_switch_to_study" type="text" value="?" /><br /> edit terrain (from play mode): <input id="key_switch_to_edit" type="text" value="m" /><br /> +enter terrain password (from play mode): <input id="key_switch_to_password" type="text" value="P" /><br /> annotate terrain (from study mode): <input id="key_switch_to_annotate" type="text" value="m" /><br /> annotate portal (from study mode): <input id="key_switch_to_portal" type="text" value="P" /><br /> +toggle terrain/control view (from study mode): <input id="key_toggle_map_mode" type="text" value="M" /><br /> </div> <script> "use strict"; @@ -211,6 +213,8 @@ let server = { tui.init_keys(); game.map_size = parser.parse_yx(tokens[2]); game.map = tokens[3] + } else if (tokens[0] === 'MAP_CONTROL') { + game.map_control = tokens[1] } else if (tokens[0] === 'GAME_STATE_COMPLETE') { game.turn_complete = true; explorer.empty_info_db(); @@ -299,6 +303,7 @@ 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); +let mode_password = new Mode('enter terrain password', true, false, false); let tui = { mode: mode_waiting_for_server, @@ -309,6 +314,7 @@ let tui = { height_turn_line: 1, height_mode_line: 1, height_input: 1, + password: 'foo', init: function() { this.inputEl = document.getElementById("input"); this.inputEl.focus(); @@ -341,6 +347,7 @@ let tui = { }; }, switch_mode: function(mode, keep_pos=false) { + this.map_mode = 'terrain'; if (mode == mode_study && !keep_pos && game.player_id in game.things) { explorer.position = game.things[game.player_id].position; } @@ -363,6 +370,9 @@ let tui = { let portal = game.portals[explorer.position] this.inputEl.value = portal; this.recalc_input_lines(); + } else if (mode == mode_password) { + this.inputEl.value = this.password; + this.recalc_input_lines(); } else if (mode == mode_teleport) { tui.log_msg("@ May teleport to: " + tui.teleport_target); tui.log_msg("@ Enter 'YES!' to entusiastically affirm."); @@ -419,6 +429,7 @@ let tui = { } this.log_msg(" " + this.keys.switch_to_chat + " - switch to chat mode"); this.log_msg("commands specific to play mode:"); + this.log_msg(" " + this.keys.switch_to_password + " - enter terrain password"); if (game.tasks.includes('WRITE')) { this.log_msg(" " + this.keys.switch_to_edit + " - write following ASCII character"); } @@ -427,28 +438,35 @@ let tui = { } this.log_msg(" " + this.keys.switch_to_study + " - switch to study mode"); this.log_msg("commands specific to study mode:"); + this.log_msg(" " + this.keys.switch_to_play + " - switch to play mode"); if (!game.tasks.includes('MOVE')) { this.log_msg(" " + movement_keys_desc + " - move"); } this.log_msg(" " + this.keys.switch_to_annotate + " - annotate terrain"); - this.log_msg(" " + this.keys.switch_to_play + " - switch to play mode"); + this.log_msg(" " + this.keys.toggle_map_mode + " - toggle terrain/control view"); }, draw_map: function() { let map_lines_split = []; let line = []; + let map_content = game.map; + if (this.map_mode == 'control') { + map_content = game.map_control; + } for (let i = 0, j = 0; i < game.map.length; i++, j++) { if (j == game.map_size[1]) { map_lines_split.push(line); line = []; j = 0; }; - line.push(game.map[i]); + line.push(map_content[i]); }; map_lines_split.push(line); - for (const thing_id in game.things) { - let t = game.things[thing_id]; - map_lines_split[t.position[0]][t.position[1]] = '@'; - }; + if (this.map_mode == 'terrain') { + for (const thing_id in game.things) { + let t = game.things[thing_id]; + map_lines_split[t.position[0]][t.position[1]] = '@'; + }; + } if (tui.mode.shows_info) { map_lines_split[explorer.position[0]][explorer.position[1]] = '?'; } @@ -547,6 +565,7 @@ let game = { this.things = {}; this.turn = -1; this.map = ""; + this.map_control = ""; this.map_size = [0,0]; this.player_id = -1; this.portals = {}; @@ -685,7 +704,7 @@ tui.inputEl.addEventListener('input', (event) => { tui.recalc_input_lines(); tui.full_refresh(); } else if (tui.mode == mode_edit && tui.inputEl.value.length > 0) { - server.send(["TASK:WRITE", tui.inputEl.value[0]]); + server.send(["TASK:WRITE", tui.inputEl.value[0], tui.password]); tui.switch_mode(mode_play); } else if (tui.mode == mode_teleport) { if (['Y', 'y'].includes(tui.inputEl.value[0])) { @@ -710,6 +729,12 @@ tui.inputEl.addEventListener('keydown', (event) => { } else if (tui.mode == mode_annotate && event.key == 'Enter') { explorer.annotate(tui.inputEl.value); tui.switch_mode(mode_study, true); + } else if (tui.mode == mode_password && event.key == 'Enter') { + if (tui.inputEl.value.length == 0) { + tui.inputEl.value = " "; + } + tui.password = tui.inputEl.value + tui.switch_mode(mode_play); } else if (tui.mode == mode_teleport && event.key == 'Enter') { if (tui.inputEl.value == 'YES!') { server.reconnect_to(tui.teleport_target); @@ -767,6 +792,8 @@ tui.inputEl.addEventListener('keydown', (event) => { tui.switch_mode(mode_edit); } else if (event.key === tui.keys.switch_to_study) { tui.switch_mode(mode_study); + } else if (event.key === tui.keys.switch_to_password) { + tui.switch_mode(mode_password); } else if (event.key === tui.keys.flatten && game.tasks.includes('FLATTEN_SURROUNDINGS')) { server.send(["TASK:FLATTEN_SURROUNDINGS"]); @@ -785,9 +812,16 @@ tui.inputEl.addEventListener('keydown', (event) => { tui.switch_mode(mode_portal); } else if (event.key in tui.movement_keys) { explorer.move(tui.movement_keys[event.key]); + } else if (event.key == tui.keys.toggle_map_mode) { + if (tui.map_mode == 'terrain') { + tui.map_mode = 'control'; + } else { + tui.map_mode = 'terrain'; + } + tui.full_refresh(); } else if (event.key === tui.keys.switch_to_annotate) { - event.preventDefault(); - tui.switch_mode(mode_annotate); + event.preventDefault(); + tui.switch_mode(mode_annotate); }; } }, false);