#!/usr/bin/env python3
import curses
-import queue
-import threading
-import time
import sys
from plomrogue.game import GameBase
from plomrogue.parser import Parser
from plomrogue.mapping import YX, MapGeometrySquare, MapGeometryHex
from plomrogue.things import ThingBase
from plomrogue.misc import quote
-from plomrogue.errors import BrokenSocketConnection, ArgError
+from plomrogue.errors import ArgError
+from plomrogue_client.socket import ClientSocket
+from plomrogue_client.tui import msg_into_lines_of_width, TUI
+
+
mode_helps = {
'play': {
},
'enter_face': {
'short': 'edit face',
- 'intro': '@ enter face line (enter nothing to abort):',
+ 'intro': '@ enter face line:',
'long': 'Draw your face as ASCII art. The string you enter must be 18 characters long, and will be divided on display into 3 lines of 6 characters each, from top to bottom..'
},
- 'enter_hat': {
- 'short': 'edit hat',
- 'intro': '@ enter hat line (enter nothing to abort):',
- 'long': 'Draw your face as ASCII art. The string you enter must be 18 characters long, and will be divided on display into 3 lines of 6 characters each, from top to bottom. Eat cookies to extend the ASCII characters available for drawing.'
+ 'enter_design': {
+ 'short': 'edit design',
+ 'intro': '@ enter design:',
+ 'long': 'Enter design for carried thing as ASCII art.'
},
'write': {
'short': 'edit tile',
}
}
-from ws4py.client import WebSocketBaseClient
-class WebSocketClient(WebSocketBaseClient):
-
- def __init__(self, recv_handler, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.recv_handler = recv_handler
- self.connect()
-
- def received_message(self, message):
- if message.is_text:
- message = str(message)
- self.recv_handler(message)
-
- @property
- def plom_closed(self):
- return self.client_terminated
-
-from plomrogue.io_tcp import PlomSocket
-class PlomSocketClient(PlomSocket):
-
- def __init__(self, recv_handler, url):
- import socket
- self.recv_handler = recv_handler
- host, port = url.split(':')
- super().__init__(socket.create_connection((host, port)))
-
- def close(self):
- self.socket.close()
-
- def run(self):
- import ssl
- try:
- for msg in self.recv():
- if msg == 'NEED_SSL':
- self.socket = ssl.wrap_socket(self.socket)
- continue
- self.recv_handler(msg)
- except BrokenSocketConnection:
- pass # we assume socket will be known as dead by now
-
def cmd_TURN(game, n):
- game.turn = n
game.turn_complete = False
cmd_TURN.argtypes = 'int:nonneg'
def cmd_LOGIN_OK(game):
game.tui.switch_mode('post_login_wait')
game.tui.send('GET_GAMESTATE')
- game.tui.log_msg('@ welcome')
+ game.tui.log_msg('@ welcome!')
cmd_LOGIN_OK.argtypes = ''
def cmd_ADMIN_OK(game):
t.hat = hat
cmd_THING_HAT.argtypes = 'int:pos string'
+def cmd_THING_DESIGN(game, thing_id, size, design):
+ t = game.get_thing_temp(thing_id)
+ t.design = [size, design]
+cmd_THING_DESIGN.argtypes = 'int:pos yx_tuple string'
+
def cmd_THING_CHAR(game, thing_id, c):
t = game.get_thing_temp(thing_id)
t.thing_char = c
map_geometry_class = globals()['MapGeometry' + geometry]
game.map_geometry_new = map_geometry_class(size)
game.map_content_new = content
- if type(game.map_geometry) == MapGeometrySquare:
+ if type(game.map_geometry_new) == MapGeometrySquare:
game.tui.movement_keys = {
game.tui.keys['square_move_up']: 'UP',
game.tui.keys['square_move_left']: 'LEFT',
game.tui.keys['square_move_down']: 'DOWN',
game.tui.keys['square_move_right']: 'RIGHT',
}
- elif type(game.map_geometry) == MapGeometryHex:
+ elif type(game.map_geometry_new) == MapGeometryHex:
game.tui.movement_keys = {
game.tui.keys['hex_move_upleft']: 'UPLEFT',
game.tui.keys['hex_move_upright']: 'UPRIGHT',
game.map_control_content = game.map_control_content_new
game.player = game.get_thing(game.player_id)
game.players_hat_chars = game.players_hat_chars_new
+ game.bladder_pressure = game.bladder_pressure_new
+ game.energy = game.energy_new
game.turn_complete = True
if game.tui.mode.name == 'post_login_wait':
game.tui.switch_mode('play')
def cmd_THING_TYPE(game, thing_type, symbol_hint):
game.thing_types[thing_type] = symbol_hint
+ game.train_parser()
cmd_THING_TYPE.argtypes = 'string char'
def cmd_THING_INSTALLED(game, thing_id):
game.tui.set_random_colors()
cmd_RANDOM_COLORS.argtypes = ''
+def cmd_STATS(game, bladder_pressure, energy):
+ game.bladder_pressure_new = bladder_pressure
+ game.energy_new = energy
+cmd_STATS.argtypes = 'int:nonneg int'
+
class Game(GameBase):
turn_complete = False
tasks = {}
self.register_command(cmd_THING_CHAR)
self.register_command(cmd_THING_FACE)
self.register_command(cmd_THING_HAT)
+ self.register_command(cmd_THING_DESIGN)
self.register_command(cmd_THING_CARRYING)
self.register_command(cmd_THING_INSTALLED)
self.register_command(cmd_TERRAIN)
self.register_command(cmd_FOV)
self.register_command(cmd_DEFAULT_COLORS)
self.register_command(cmd_RANDOM_COLORS)
+ self.register_command(cmd_STATS)
self.map_content = ''
self.players_hat_chars = ''
self.player_id = -1
self.portals_new = {}
self.terrains = {}
self.player = None
+ self.parser = Parser(self)
+ self.train_parser()
- def get_string_options(self, string_option_type):
- if string_option_type == 'map_geometry':
- return ['Hex', 'Square']
- elif string_option_type == 'thing_type':
- return self.thing_types.keys()
- return None
+ def train_parser(self):
+ self.parser.string_options = {
+ 'map_geometry': {'Hex', 'Square'},
+ 'thing_type': self.thing_types.keys()
+ }
def get_command(self, command_name):
from functools import partial
return True
return False
-class TUI:
+class RogueChatTUI(TUI):
mode_admin_enter = Mode('admin_enter', has_input_prompt=True)
mode_admin = Mode('admin')
mode_play = Mode('play')
mode_take_thing = Mode('take_thing', has_input_prompt=True)
mode_drop_thing = Mode('drop_thing', has_input_prompt=True)
mode_enter_face = Mode('enter_face', has_input_prompt=True)
- mode_enter_hat = Mode('enter_hat', has_input_prompt=True)
+ mode_enter_design = Mode('enter_design', has_input_prompt=True)
is_admin = False
tile_draw = False
- def __init__(self, host):
+ def __init__(self, host, *args, **kwargs):
import os
import json
self.mode_play.available_modes = ["chat", "study", "edit", "admin_enter",
"command_thing", "take_thing",
"drop_thing"]
self.mode_play.available_actions = ["move", "teleport", "door", "consume",
- "install", "wear", "spin"]
+ "install", "wear", "spin", "dance"]
self.mode_study.available_modes = ["chat", "play", "admin_enter", "edit"]
self.mode_study.available_actions = ["toggle_map_mode", "move_explorer"]
self.mode_admin.available_modes = ["admin_thing_protect", "control_pw_type",
"control_tile_type", "chat",
"study", "play", "edit"]
- self.mode_admin.available_actions = ["move"]
+ self.mode_admin.available_actions = ["move", "toggle_map_mode"]
self.mode_control_tile_draw.available_modes = ["admin_enter"]
self.mode_control_tile_draw.available_actions = ["move_explorer",
"toggle_tile_draw"]
self.mode_edit.available_modes = ["write", "annotate", "portal",
- "name_thing", "enter_face", "enter_hat",
+ "name_thing", "enter_face", "enter_design",
"password",
"chat", "study", "play", "admin_enter"]
self.mode_edit.available_actions = ["move", "flatten", "install",
"toggle_map_mode"]
self.mode = None
- self.host = host
+ self.socket = ClientSocket(host, self.socket_log)
self.game = Game()
self.game.tui = self
- self.parser = Parser(self.game)
- self.log = []
- self.do_refresh = True
- self.queue = queue.Queue()
self.login_name = None
self.map_mode = 'terrain + things'
self.password = 'foo'
- self.switch_mode('waiting_for_server')
self.keys = {
'switch_to_chat': 't',
'switch_to_play': 'p',
'switch_to_admin_thing_protect': 'T',
'flatten': 'F',
'switch_to_enter_face': 'f',
- 'switch_to_enter_hat': 'H',
+ 'switch_to_enter_design': 'D',
'switch_to_take_thing': 'z',
'switch_to_drop_thing': 'u',
'teleport': 'p',
'install': 'I',
'wear': 'W',
'spin': 'S',
+ 'dance': 'T',
'help': 'h',
'toggle_map_mode': 'L',
'toggle_tile_draw': 'm',
for k in keys_conf:
self.keys[k] = keys_conf[k]
self.show_help = False
- self.disconnected = True
- self.force_instant_connect = True
self.input_lines = []
self.fov = ''
self.flash = False
self.ascii_draw_stage = 0
self.full_ascii_draw = ''
self.offset = YX(0,0)
- curses.wrapper(self.loop)
-
- def connect(self):
+ self.explorer = YX(0, 0)
+ self.input_ = ''
+ self.input_prompt = '> '
+ self.action_descriptions = {
+ 'move': 'move',
+ 'flatten': 'flatten surroundings',
+ 'teleport': 'teleport',
+ 'take_thing': 'pick up thing',
+ 'drop_thing': 'drop thing',
+ 'toggle_map_mode': 'toggle map view',
+ 'toggle_tile_draw': 'toggle protection character drawing',
+ 'install': '(un-)install',
+ 'wear': '(un-)wear',
+ 'door': 'open/close',
+ 'consume': 'consume',
+ 'spin': 'spin',
+ 'dance': 'dance',
+ }
+ self.action_tasks = {
+ 'flatten': 'FLATTEN_SURROUNDINGS',
+ 'take_thing': 'PICK_UP',
+ 'drop_thing': 'DROP',
+ 'door': 'DOOR',
+ 'install': 'INSTALL',
+ 'wear': 'WEAR',
+ 'move': 'MOVE',
+ 'command': 'COMMAND',
+ 'consume': 'INTOXICATE',
+ 'spin': 'SPIN',
+ 'dance': 'DANCE',
+ }
+ super().__init__(*args, **kwargs)
- def handle_recv(msg):
- if msg == 'BYE':
- self.socket.close()
- else:
- self.queue.put(msg)
-
- self.log_msg('@ attempting connect')
- socket_client_class = PlomSocketClient
- if self.host.startswith('ws://') or self.host.startswith('wss://'):
- socket_client_class = WebSocketClient
- try:
- self.socket = socket_client_class(handle_recv, self.host)
- self.socket_thread = threading.Thread(target=self.socket.run)
- self.socket_thread.start()
- self.disconnected = False
- self.game.thing_types = {}
- self.game.terrains = {}
- time.sleep(0.1) # give potential SSL negotation some time …
- self.socket.send('TASKS')
- self.socket.send('TERRAINS')
- self.socket.send('THING_TYPES')
- self.switch_mode('login')
- except ConnectionRefusedError:
- self.log_msg('@ server connect failure')
- self.disconnected = True
- self.switch_mode('waiting_for_server')
- self.do_refresh = True
+ def update_on_connect(self):
+ self.game.thing_types = {}
+ self.game.terrains = {}
+ self.game.train_parser()
+ self.is_admin = False
+ self.socket.send('TASKS')
+ self.socket.send('TERRAINS')
+ self.socket.send('THING_TYPES')
+ self.switch_mode('login')
def reconnect(self):
- self.log_msg('@ attempting reconnect')
- self.send('QUIT')
+ import time
+ self.log('@ attempting reconnect')
+ self.socket.send('QUIT')
# necessitated by some strange SSL race conditions with ws4py
time.sleep(0.1) # FIXME find out why exactly necessary
self.switch_mode('waiting_for_server')
- self.connect()
+ self.socket.connect()
+ self.update_on_connect()
def send(self, msg):
- try:
- if hasattr(self.socket, 'plom_closed') and self.socket.plom_closed:
- raise BrokenSocketConnection
- self.socket.send(msg)
- except (BrokenPipeError, BrokenSocketConnection):
- self.log_msg('@ server disconnected :(')
- self.disconnected = True
- self.force_instant_connect = True
+ self.socket.send(msg)
+ if self.socket.disconnected:
self.do_refresh = True
+ def socket_log(self, msg):
+ self.log('@ ' + msg)
+
def log_msg(self, msg):
- self.log += [msg]
- if len(self.log) > 100:
- self.log = self.log[-100:]
+ super().log(msg)
+ #self.log += [msg]
+ if len(self._log) > 100:
+ self.log = self._log[-100:]
def restore_input_values(self):
if self.mode.name == 'annotate' and self.explorer in self.game.annotations:
elif self.mode.name == 'admin_thing_protect':
if hasattr(self.game.player.carrying, 'protection'):
self.input_ = self.game.player.carrying.protection
- elif self.mode.name in {'enter_face', 'enter_hat'}:
+ elif self.mode.name == 'enter_face':
start = self.ascii_draw_stage * 6
end = (self.ascii_draw_stage + 1) * 6
- if self.mode.name == 'enter_face':
- self.input_ = self.game.player.face[start:end]
- elif self.mode.name == 'enter_hat':
- self.input_ = self.game.player.hat[start:end]
+ self.input_ = self.game.player.face[start:end]
+ elif self.mode.name == 'enter_design':
+ width = self.game.player.carrying.design[0].x
+ start = self.ascii_draw_stage * width
+ end = (self.ascii_draw_stage + 1) * width
+ self.input_ = self.game.player.carrying.design[1][start:end]
def send_tile_control_command(self):
self.send('SET_TILE_CONTROL %s %s' %
def switch_mode(self, mode_name):
def fail(msg, return_mode='play'):
- self.log_msg('? ' + msg)
+ self.log('? ' + msg)
self.flash = True
self.switch_mode(return_mode)
if self.mode and self.mode.name == 'control_tile_draw':
- self.log_msg('@ finished tile protection drawing.')
+ self.log('@ finished tile protection drawing.')
self.draw_face = False
self.tile_draw = False
+ self.ascii_draw_stage = 0
+ self.full_ascii_draw = ''
if mode_name == 'command_thing' and\
(not self.game.player.carrying or
not self.game.player.carrying.commandable):
return fail('not carrying anything commandable')
if mode_name == 'name_thing' and not self.game.player.carrying:
- return fail('not carrying anything to re-name')
+ return fail('not carrying anything to re-name', 'edit')
if mode_name == 'admin_thing_protect' and not self.game.player.carrying:
return fail('not carrying anything to protect')
if mode_name == 'take_thing' and self.game.player.carrying:
return fail('already carrying something')
if mode_name == 'drop_thing' and not self.game.player.carrying:
return fail('not carrying anything droppable')
- if mode_name == 'enter_hat' and not hasattr(self.game.player, 'hat'):
- return fail('not wearing hat to edit', 'edit')
+ if mode_name == 'enter_design' and\
+ (not self.game.player.carrying or
+ not hasattr(self.game.player.carrying, 'design')):
+ return fail('not carrying designable to edit', 'edit')
if mode_name == 'admin_enter' and self.is_admin:
mode_name = 'admin'
self.mode = getattr(self, 'mode_' + mode_name)
if self.mode.is_single_char_entry:
self.show_help = True
if len(self.mode.intro_msg) > 0:
- self.log_msg(self.mode.intro_msg)
+ self.log(self.mode.intro_msg)
if self.mode.name == 'login':
if self.login_name:
self.send('LOGIN ' + quote(self.login_name))
else:
- self.log_msg('@ enter username')
+ self.log('@ enter username')
elif self.mode.name == 'take_thing':
- self.log_msg('Portable things in reach for pick-up:')
+ self.log('Portable things in reach for pick-up:')
directed_moves = {
'HERE': YX(0, 0), 'LEFT': YX(0, -1), 'RIGHT': YX(0, 1)
}
else:
for i in range(len(self.selectables)):
t = self.game.get_thing(self.selectables[i])
- self.log_msg('%s %s: %s' % (i, directions[i],
+ self.log('%s %s: %s' % (i, directions[i],
self.get_thing_info(t)))
elif self.mode.name == 'drop_thing':
- self.log_msg('Direction to drop thing to:')
+ self.log('Direction to drop thing to:')
self.selectables =\
['HERE'] + list(self.game.tui.movement_keys.values())
for i in range(len(self.selectables)):
- self.log_msg(str(i) + ': ' + self.selectables[i])
- elif self.mode.name == 'enter_hat':
- self.log_msg('legal characters: ' + self.game.players_hat_chars)
+ self.log(str(i) + ': ' + self.selectables[i])
+ elif self.mode.name == 'enter_design':
+ if self.game.player.carrying.type_ == 'Hat':
+ self.log('@ The design you enter must be %s lines of max %s '
+ 'characters width each'
+ % (self.game.player.carrying.design[0].y,
+ self.game.player.carrying.design[0].x))
+ self.log('@ Legal characters: ' + self.game.players_hat_chars)
+ self.log('@ (Eat cookies to extend the ASCII characters available for drawing.)')
+ else:
+ self.log('@ Width of first line determines maximum width for remaining design')
+ self.log('@ Finish design by entering an empty line (multiple space characters do not count as empty)')
elif self.mode.name == 'command_thing':
self.send('TASK:COMMAND ' + quote('HELP'))
elif self.mode.name == 'control_pw_pw':
- self.log_msg('@ enter protection password for "%s":' % self.tile_control_char)
+ self.log('@ enter protection password for "%s":' % self.tile_control_char)
elif self.mode.name == 'control_tile_draw':
- self.log_msg('@ can draw protection character "%s", turn drawing on/off with [%s], finish with [%s].' % (self.tile_control_char, self.keys['toggle_tile_draw'], self.keys['switch_to_admin_enter']))
+ self.log('@ can draw protection character "%s", turn drawing on/off with [%s], finish with [%s].' % (self.tile_control_char, self.keys['toggle_tile_draw'], self.keys['switch_to_admin_enter']))
self.input_ = ""
self.restore_input_values()
def set_default_colors(self):
- curses.init_color(1, 1000, 1000, 1000)
- curses.init_color(2, 0, 0, 0)
+ if curses.can_change_color():
+ curses.init_color(7, 1000, 1000, 1000)
+ curses.init_color(0, 0, 0, 0)
self.do_refresh = True
def set_random_colors(self):
import random
return int(offset + random.random()*375)
- curses.init_color(1, rand(625), rand(625), rand(625))
- curses.init_color(2, rand(0), rand(0), rand(0))
+ if curses.can_change_color():
+ curses.init_color(7, rand(625), rand(625), rand(625))
+ curses.init_color(0, rand(0), rand(0), rand(0))
self.do_refresh = True
def get_info(self):
else:
for t in self.game.things:
if t.position == self.explorer:
- info_to_cache += 'THING: %s' % self.get_thing_info(t)
- protection = t.protection
- if protection == '.':
- protection = 'none'
- info_to_cache += ' / protection: %s\n' % protection
- if hasattr(t, 'hat'):
- info_to_cache += t.hat[0:6] + '\n'
- info_to_cache += t.hat[6:12] + '\n'
- info_to_cache += t.hat[12:18] + '\n'
- if hasattr(t, 'face'):
- info_to_cache += t.face[0:6] + '\n'
- info_to_cache += t.face[6:12] + '\n'
- info_to_cache += t.face[12:18] + '\n'
+ info_to_cache += '%s' % self.get_thing_info(t, True)
terrain_char = self.game.map_content[pos_i]
terrain_desc = '?'
if terrain_char in self.game.terrains:
terrain_desc = self.game.terrains[terrain_char]
- info_to_cache += 'TERRAIN: "%s" / %s\n' % (terrain_char,
+ info_to_cache += 'TERRAIN: %s (%s' % (terrain_char,
terrain_desc)
protection = self.game.map_control_content[pos_i]
- if protection == '.':
- protection = 'unprotected'
- info_to_cache += 'PROTECTION: %s\n' % protection
+ if protection != '.':
+ info_to_cache += '/protection:%s' % protection
+ info_to_cache += ')\n'
if self.explorer in self.game.portals:
info_to_cache += 'PORTAL: ' +\
self.game.portals[self.explorer] + '\n'
- else:
- info_to_cache += 'PORTAL: (none)\n'
if self.explorer in self.game.annotations:
info_to_cache += 'ANNOTATION: ' +\
self.game.annotations[self.explorer]
self.info_cached = info_to_cache
return self.info_cached
- def get_thing_info(self, t):
- info = '%s / %s' %\
- (t.type_, self.game.thing_types[t.type_])
+ def get_thing_info(self, t, detailed=False):
+ info = ''
+ if detailed:
+ info += '- '
+ info += self.game.thing_types[t.type_]
if hasattr(t, 'thing_char'):
info += t.thing_char
if hasattr(t, 'name'):
- info += ' (%s)' % t.name
+ info += ': %s' % t.name
+ info += ' (%s' % t.type_
if hasattr(t, 'installed'):
- info += ' / installed'
+ info += '/installed'
+ if t.type_ == 'Bottle':
+ if t.thing_char == '_':
+ info += '/empty'
+ elif t.thing_char == '~':
+ info += '/full'
+ if detailed:
+ protection = t.protection
+ if protection != '.':
+ info += '/protection:%s' % protection
+ info += ')\n'
+ if hasattr(t, 'hat') or hasattr(t, 'face'):
+ info += '----------\n'
+ if hasattr(t, 'hat'):
+ info += '| %s |\n' % t.hat[0:6]
+ info += '| %s |\n' % t.hat[6:12]
+ info += '| %s |\n' % t.hat[12:18]
+ if hasattr(t, 'face'):
+ info += '| %s |\n' % t.face[0:6]
+ info += '| %s |\n' % t.face[6:12]
+ info += '| %s |\n' % t.face[12:18]
+ info += '----------\n'
+ if hasattr(t, 'design'):
+ line_length = t.design[0].x
+ lines = []
+ for i in range(t.design[0].y):
+ start = i * line_length
+ end = (i + 1) * line_length
+ lines += [t.design[1][start:end]]
+ info += '-' * (line_length + 4) + '\n'
+ for line in lines:
+ info += '| %s |\n' % line
+ info += '-' * (line_length + 4) + '\n'
+ else:
+ info += ')'
return info
- def loop(self, stdscr):
- import datetime
+ def reset_size(self):
+ super().reset_size()
+ self.left_window_width = min(52, int(self.size.x / 2))
+ self.right_window_width = self.size.x - self.left_window_width
- def safe_addstr(y, x, line):
- if y < self.size.y - 1 or x + len(line) < self.size.x:
- stdscr.addstr(y, x, line, curses.color_pair(1))
- else: # workaround to <https://stackoverflow.com/q/7063128>
- cut_i = self.size.x - x - 1
- cut = line[:cut_i]
- last_char = line[cut_i]
- stdscr.addstr(y, self.size.x - 2, last_char, curses.color_pair(1))
- stdscr.insstr(y, self.size.x - 2, ' ')
- stdscr.addstr(y, x, cut, curses.color_pair(1))
+ def addstr(self, y, x, line, ignore=None):
+ super().addstr(y, x, line, curses.color_pair(1))
- def handle_input(msg):
- command, args = self.parser.parse(msg)
- command(*args)
+ def init_loop(self):
+ self.switch_mode('waiting_for_server')
+ curses.start_color()
+ self.set_default_colors()
+ curses.init_pair(1, 7, 0)
+ if not curses.can_change_color():
+ self.log('@ unfortunately, your terminal does not seem to '
+ 'support re-definition of colors; you might miss out '
+ 'on some color effects')
+ super().init_loop()
+
+ def recalc_input_lines(self):
+ if not self.mode.has_input_prompt:
+ self.input_lines = []
+ else:
+ self.input_lines = msg_into_lines_of_width(self.input_prompt
+ + self.input_ + '█',
+ self.right_window_width)
+ def draw_history(self):
+ lines = []
+ for line in self._log:
+ lines += msg_into_lines_of_width(line, self.right_window_width)
+ lines.reverse()
+ height_header = 2
+ max_y = self.size.y - len(self.input_lines)
+ for i in range(len(lines)):
+ if (i >= max_y - height_header):
+ break
+ self.addstr(max_y - i - 1, self.left_window_width, lines[i])
+
+ def draw_info(self):
+ info = 'MAP VIEW: %s\n%s' % (self.map_mode, self.get_info())
+ lines = msg_into_lines_of_width(info, self.right_window_width)
+ height_header = 2
+ for i in range(len(lines)):
+ y = height_header + i
+ if y >= self.size.y - len(self.input_lines):
+ break
+ self.addstr(y, self.left_window_width, lines[i])
+
+ def draw_input(self):
+ y = self.size.y - len(self.input_lines)
+ for i in range(len(self.input_lines)):
+ self.addstr(y, self.left_window_width, self.input_lines[i])
+ y += 1
+
+ def draw_stats(self):
+ stats = 'ENERGY: %s BLADDER: %s' % (self.game.energy,
+ self.game.bladder_pressure)
+ self.addstr(0, self.left_window_width, stats)
+
+ def draw_mode(self):
+ help = "hit [%s] for help" % self.keys['help']
+ if self.mode.has_input_prompt:
+ help = "enter /help for help"
+ self.addstr(1, self.left_window_width,
+ 'MODE: %s – %s' % (self.mode.short_desc, help))
+
+ def draw_map(self):
+ if (not self.game.turn_complete) and len(self.map_lines) == 0:
+ return
+ if self.game.turn_complete:
+ map_lines_split = []
+ 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
+ if self.map_mode == 'protections':
+ map_lines_split += [[c + ' ' for c
+ in self.game.map_control_content[start:end]]]
+ else:
+ map_lines_split += [[c + ' ' for c
+ in self.game.map_content[start:end]]]
+ if self.map_mode == 'terrain + annotations':
+ for p in self.game.annotations:
+ map_lines_split[p.y][p.x] = 'A '
+ elif self.map_mode == 'terrain + things':
+ for p in self.game.portals.keys():
+ original = map_lines_split[p.y][p.x]
+ map_lines_split[p.y][p.x] = original[0] + 'P'
+ used_positions = []
+
+ def draw_thing(t, used_positions):
+ symbol = self.game.thing_types[t.type_]
+ meta_char = ' '
+ if hasattr(t, 'thing_char'):
+ meta_char = t.thing_char
+ if t.position in used_positions:
+ meta_char = '+'
+ if hasattr(t, 'carrying') and t.carrying:
+ meta_char = '$'
+ map_lines_split[t.position.y][t.position.x] = symbol + meta_char
+ used_positions += [t.position]
+
+ for t in [t for t in self.game.things if t.type_ != 'Player']:
+ draw_thing(t, used_positions)
+ for t in [t for t in self.game.things if t.type_ == 'Player']:
+ draw_thing(t, used_positions)
+ if self.mode.shows_info or self.mode.name == 'control_tile_draw':
+ map_lines_split[self.explorer.y][self.explorer.x] = '??'
+ elif self.map_mode != 'terrain + things':
+ map_lines_split[self.game.player.position.y]\
+ [self.game.player.position.x] = '??'
+ self.map_lines = []
+ if type(self.game.map_geometry) == MapGeometryHex:
+ indent = 0
+ for line in map_lines_split:
+ self.map_lines += [indent * ' ' + ''.join(line)]
+ indent = 0 if indent else 1
+ else:
+ for line in map_lines_split:
+ self.map_lines += [''.join(line)]
+ window_center = YX(int(self.size.y / 2),
+ int(self.left_window_width / 2))
+ center = self.game.player.position
+ if self.mode.shows_info or self.mode.name == 'control_tile_draw':
+ center = self.explorer
+ center = YX(center.y, center.x * 2)
+ self.offset = center - window_center
+ if type(self.game.map_geometry) == MapGeometryHex and self.offset.y % 2:
+ self.offset += YX(0, 1)
+ term_y = max(0, -self.offset.y)
+ term_x = max(0, -self.offset.x)
+ map_y = max(0, self.offset.y)
+ map_x = max(0, self.offset.x)
+ while term_y < self.size.y and map_y < len(self.map_lines):
+ to_draw = self.map_lines[map_y][map_x:self.left_window_width + self.offset.x]
+ self.addstr(term_y, term_x, to_draw)
+ term_y += 1
+ map_y += 1
+
+ def draw_names(self):
+ players = [t for t in self.game.things if t.type_ == 'Player']
+ players.sort(key=lambda t: len(t.name))
+ players.reverse()
+ shrink_offset = max(0, (self.size.y - self.left_window_width // 2) // 2)
+ y = 0
+ for t in players:
+ offset_y = y - shrink_offset
+ max_len = max(5, (self.left_window_width // 2) - (offset_y * 2) - 8)
+ name = t.name[:]
+ if len(name) > max_len:
+ name = name[:max_len - 1] + '…'
+ self.addstr(y, 0, '@%s:%s' % (t.thing_char, name))
+ y += 1
+ if y >= self.size.y:
+ break
+
+ def draw_face_popup(self):
+ t = self.game.get_thing(self.draw_face)
+ if not t or not hasattr(t, 'face'):
+ self.draw_face = False
+ return
+
+ start_x = self.left_window_width - 10
+ def draw_body_part(body_part, end_y):
+ self.addstr(end_y - 3, start_x, '----------')
+ self.addstr(end_y - 2, start_x, '| ' + body_part[0:6] + ' |')
+ self.addstr(end_y - 1, start_x, '| ' + body_part[6:12] + ' |')
+ self.addstr(end_y, start_x, '| ' + body_part[12:18] + ' |')
+
+ if hasattr(t, 'face'):
+ draw_body_part(t.face, self.size.y - 3)
+ if hasattr(t, 'hat'):
+ draw_body_part(t.hat, self.size.y - 6)
+ self.addstr(self.size.y - 2, start_x, '----------')
+ name = t.name[:]
+ if len(name) > 7:
+ name = name[:6 - 1] + '…'
+ self.addstr(self.size.y - 1, start_x, '@%s:%s' % (t.thing_char, name))
+
+ def draw_help(self):
+ content = "%s help\n\n%s\n\n" % (self.mode.short_desc,
+ self.mode.help_intro)
+ if len(self.mode.available_actions) > 0:
+ content += "Available actions:\n"
+ for action in self.mode.available_actions:
+ if action in self.action_tasks:
+ if self.action_tasks[action] not in self.game.tasks:
+ continue
+ if action == 'move_explorer':
+ action = 'move'
+ if action == 'move':
+ key = ','.join(self.movement_keys)
+ else:
+ key = self.keys[action]
+ content += '[%s] – %s\n' % (key, self.action_descriptions[action])
+ content += '\n'
+ content += self.mode.list_available_modes(self)
+ for i in range(self.size.y):
+ self.addstr(i,
+ self.left_window_width * (not self.mode.has_input_prompt),
+ ' ' * self.left_window_width)
+ lines = []
+ for line in content.split('\n'):
+ lines += msg_into_lines_of_width(line, self.right_window_width)
+ for i in range(len(lines)):
+ if i >= self.size.y:
+ break
+ self.addstr(i,
+ self.left_window_width * (not self.mode.has_input_prompt),
+ lines[i])
+
+ def draw_screen(self):
+ self.stdscr.bkgd(' ', curses.color_pair(1))
+ self.recalc_input_lines()
+ if self.mode.has_input_prompt:
+ self.draw_input()
+ if self.mode.shows_info:
+ self.draw_info()
+ else:
+ self.draw_history()
+ self.draw_mode()
+ if not self.mode.is_intro:
+ self.draw_stats()
+ self.draw_map()
+ if self.show_help:
+ self.draw_help()
+ if self.mode.name in {'chat', 'play'}:
+ self.draw_names()
+ if self.draw_face:
+ self.draw_face_popup()
+
+ def handle_server_message(self, msg):
+ command, args = self.game.parser.parse(msg)
+ command(*args)
+
+ def on_each_loop_start(self):
+ prev_disconnected = self.socket.disconnected
+ self.socket.keep_connection_alive()
+ if prev_disconnected and not self.socket.disconnected:
+ self.update_on_connect()
+ if self.flash:
+ curses.flash()
+ self.flash = False
+
+ def on_key(self, key, keycode):
def task_action_on(action):
- return action_tasks[action] in self.game.tasks
-
- def msg_into_lines_of_width(msg, width):
- chunk = ''
- lines = []
- x = 0
- for i in range(len(msg)):
- if x >= width or msg[i] == "\n":
- lines += [chunk]
- chunk = ''
- x = 0
- if msg[i] == "\n":
- x -= 1
- if msg[i] != "\n":
- chunk += msg[i]
- x += 1
- lines += [chunk]
- return lines
-
- def reset_screen_size():
- self.size = YX(*stdscr.getmaxyx())
- self.size = self.size - YX(self.size.y % 4, 0)
- self.size = self.size - YX(0, self.size.x % 4)
- self.window_width = int(self.size.x / 2)
-
- def recalc_input_lines():
- if not self.mode.has_input_prompt:
- self.input_lines = []
- else:
- self.input_lines = msg_into_lines_of_width(input_prompt
- + self.input_ + '█',
- self.window_width)
+ return self.action_tasks[action] in self.game.tasks
def move_explorer(direction):
target = self.game.map_geometry.move_yx(self.explorer, direction)
self.send_tile_control_command()
else:
self.flash = True
-
- def draw_history():
- lines = []
- for line in self.log:
- lines += msg_into_lines_of_width(line, self.window_width)
- lines.reverse()
- height_header = 2
- max_y = self.size.y - len(self.input_lines)
- for i in range(len(lines)):
- if (i >= max_y - height_header):
- break
- safe_addstr(max_y - i - 1, self.window_width, lines[i])
-
- def draw_info():
- info = 'MAP VIEW: %s\n%s' % (self.map_mode, self.get_info())
- lines = msg_into_lines_of_width(info, self.window_width)
- height_header = 2
- for i in range(len(lines)):
- y = height_header + i
- if y >= self.size.y - len(self.input_lines):
- break
- safe_addstr(y, self.window_width, lines[i])
-
- def draw_input():
- y = self.size.y - len(self.input_lines)
- for i in range(len(self.input_lines)):
- safe_addstr(y, self.window_width, self.input_lines[i])
- y += 1
-
- def draw_turn():
- if not self.game.turn_complete:
- return
- safe_addstr(0, self.window_width, 'TURN: ' + str(self.game.turn))
-
- def draw_mode():
- help = "hit [%s] for help" % self.keys['help']
- if self.mode.has_input_prompt:
- help = "enter /help for help"
- safe_addstr(1, self.window_width,
- 'MODE: %s – %s' % (self.mode.short_desc, help))
-
- def draw_map():
- if (not self.game.turn_complete) and len(self.map_lines) == 0:
- return
- if self.game.turn_complete:
- map_lines_split = []
- 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
- if self.map_mode == 'protections':
- map_lines_split += [[c + ' ' for c
- in self.game.map_control_content[start:end]]]
- else:
- map_lines_split += [[c + ' ' for c
- in self.game.map_content[start:end]]]
- if self.map_mode == 'terrain + annotations':
- for p in self.game.annotations:
- map_lines_split[p.y][p.x] = 'A '
- elif self.map_mode == 'terrain + things':
- for p in self.game.portals.keys():
- original = map_lines_split[p.y][p.x]
- map_lines_split[p.y][p.x] = original[0] + 'P'
- used_positions = []
-
- def draw_thing(t, used_positions):
- symbol = self.game.thing_types[t.type_]
- meta_char = ' '
- if hasattr(t, 'thing_char'):
- meta_char = t.thing_char
- if t.position in used_positions:
- meta_char = '+'
- if hasattr(t, 'carrying') and t.carrying:
- meta_char = '$'
- map_lines_split[t.position.y][t.position.x] = symbol + meta_char
- used_positions += [t.position]
-
- for t in [t for t in self.game.things if t.type_ != 'Player']:
- draw_thing(t, used_positions)
- for t in [t for t in self.game.things if t.type_ == 'Player']:
- draw_thing(t, used_positions)
- if self.mode.shows_info or self.mode.name == 'control_tile_draw':
- map_lines_split[self.explorer.y][self.explorer.x] = '??'
- elif self.map_mode != 'terrain + things':
- map_lines_split[self.game.player.position.y]\
- [self.game.player.position.x] = '??'
- self.map_lines = []
- if type(self.game.map_geometry) == MapGeometryHex:
- indent = 0
- for line in map_lines_split:
- self.map_lines += [indent * ' ' + ''.join(line)]
- indent = 0 if indent else 1
- else:
- for line in map_lines_split:
- self.map_lines += [''.join(line)]
- window_center = YX(int(self.size.y / 2),
- int(self.window_width / 2))
- center = self.game.player.position
- if self.mode.shows_info or self.mode.name == 'control_tile_draw':
- center = self.explorer
- center = YX(center.y, center.x * 2)
- self.offset = center - window_center
- if type(self.game.map_geometry) == MapGeometryHex and self.offset.y % 2:
- self.offset += YX(0, 1)
- term_y = max(0, -self.offset.y)
- term_x = max(0, -self.offset.x)
- map_y = max(0, self.offset.y)
- map_x = max(0, self.offset.x)
- while term_y < self.size.y and map_y < len(self.map_lines):
- to_draw = self.map_lines[map_y][map_x:self.window_width + self.offset.x]
- safe_addstr(term_y, term_x, to_draw)
- term_y += 1
- map_y += 1
-
- def draw_face_popup():
- t = self.game.get_thing(self.draw_face)
- if not t or not hasattr(t, 'face'):
- self.draw_face = False
- return
-
- start_x = self.window_width - 10
- t_char = ' '
- if hasattr(t, 'thing_char'):
- t_char = t.thing_char
- def draw_body_part(body_part, end_y):
- safe_addstr(end_y - 4, start_x, ' _[ @' + t_char + ' ]_ ')
- safe_addstr(end_y - 3, start_x, '| |')
- safe_addstr(end_y - 2, start_x, '| ' + body_part[0:6] + ' |')
- safe_addstr(end_y - 1, start_x, '| ' + body_part[6:12] + ' |')
- safe_addstr(end_y, start_x, '| ' + body_part[12:18] + ' |')
-
- if hasattr(t, 'face'):
- draw_body_part(t.face, self.size.y - 2)
- if hasattr(t, 'hat'):
- draw_body_part(t.hat, self.size.y - 5)
- safe_addstr(self.size.y - 1, start_x, '| |')
-
- def draw_help():
- content = "%s help\n\n%s\n\n" % (self.mode.short_desc,
- self.mode.help_intro)
- if len(self.mode.available_actions) > 0:
- content += "Available actions:\n"
- for action in self.mode.available_actions:
- if action in action_tasks:
- if action_tasks[action] not in self.game.tasks:
- continue
- if action == 'move_explorer':
- action = 'move'
- if action == 'move':
- key = ','.join(self.movement_keys)
- else:
- key = self.keys[action]
- content += '[%s] – %s\n' % (key, action_descriptions[action])
- content += '\n'
- content += self.mode.list_available_modes(self)
- for i in range(self.size.y):
- safe_addstr(i,
- self.window_width * (not self.mode.has_input_prompt),
- ' ' * self.window_width)
- lines = []
- for line in content.split('\n'):
- lines += msg_into_lines_of_width(line, self.window_width)
- for i in range(len(lines)):
- if i >= self.size.y:
- break
- safe_addstr(i,
- self.window_width * (not self.mode.has_input_prompt),
- lines[i])
-
- def draw_screen():
- stdscr.clear()
- stdscr.bkgd(' ', curses.color_pair(1))
- recalc_input_lines()
- if self.mode.has_input_prompt:
- draw_input()
- if self.mode.shows_info:
- draw_info()
- else:
- draw_history()
- draw_mode()
- if not self.mode.is_intro:
- draw_turn()
- draw_map()
- if self.show_help:
- draw_help()
- if self.draw_face and self.mode.name in {'chat', 'play'}:
- draw_face_popup()
-
def pick_selectable(task_name):
try:
i = int(self.input_)
if i < 0 or i >= len(self.selectables):
- self.log_msg('? invalid index, aborted')
+ self.log('? invalid index, aborted')
else:
self.send('TASK:%s %s' % (task_name, self.selectables[i]))
except ValueError:
- self.log_msg('? invalid index, aborted')
+ self.log('? invalid index, aborted')
self.input_ = ''
self.switch_mode('play')
- def enter_ascii_art(command):
- if len(self.input_) != 6:
- self.log_msg('? wrong input length, must be 6; try again')
+ def enter_ascii_art(command, height, width,
+ with_pw=False, with_size=False):
+ if with_size and self.ascii_draw_stage == 0:
+ width = len(self.input_)
+ if width > 36:
+ self.log('? input too long, must be max 36; try again')
+ # TODO: move max width mechanism server-side
+ return
+ old_size = self.game.player.carrying.design[0]
+ if width != old_size.x:
+ # TODO: save remaining design?
+ self.game.player.carrying.design[1] = ''
+ self.game.player.carrying.design[0] = YX(old_size.y, width)
+ elif len(self.input_) > width:
+ self.log('? input too long, '
+ 'must be max %s; try again' % width)
return
- self.log_msg(' ' + self.input_)
- self.full_ascii_draw += self.input_
+ self.log(' ' + self.input_)
+ if with_size and self.input_ in {'', ' '}\
+ and self.ascii_draw_stage > 0:
+ height = self.ascii_draw_stage
+ else:
+ if with_size:
+ height = self.ascii_draw_stage + 2
+ if len(self.input_) < width:
+ self.input_ += ' ' * (width - len(self.input_))
+ self.full_ascii_draw += self.input_
+ if with_size:
+ old_size = self.game.player.carrying.design[0]
+ self.game.player.carrying.design[0] = YX(height, old_size.x)
self.ascii_draw_stage += 1
- if self.ascii_draw_stage < 3:
+ if self.ascii_draw_stage < height:
self.restore_input_values()
else:
- self.send('%s %s' % (command, quote(self.full_ascii_draw)))
+ if with_pw and with_size:
+ self.send('%s_SIZE %s %s' % (command, YX(height, width),
+ quote(self.password)))
+ if with_pw:
+ self.send('%s %s %s' % (command, quote(self.full_ascii_draw),
+ quote(self.password)))
+ else:
+ self.send('%s %s' % (command, quote(self.full_ascii_draw)))
self.full_ascii_draw = ""
self.ascii_draw_stage = 0
self.input_ = ""
self.switch_mode('edit')
- action_descriptions = {
- 'move': 'move',
- 'flatten': 'flatten surroundings',
- 'teleport': 'teleport',
- 'take_thing': 'pick up thing',
- 'drop_thing': 'drop thing',
- 'toggle_map_mode': 'toggle map view',
- 'toggle_tile_draw': 'toggle protection character drawing',
- 'install': '(un-)install',
- 'wear': '(un-)wear',
- 'door': 'open/close',
- 'consume': 'consume',
- 'spin': 'spin',
- }
-
- action_tasks = {
- 'flatten': 'FLATTEN_SURROUNDINGS',
- 'take_thing': 'PICK_UP',
- 'drop_thing': 'DROP',
- 'door': 'DOOR',
- 'install': 'INSTALL',
- 'wear': 'WEAR',
- 'move': 'MOVE',
- 'command': 'COMMAND',
- 'consume': 'INTOXICATE',
- 'spin': 'SPIN',
- }
-
- curses.curs_set(False) # hide cursor
- curses.start_color()
- self.set_default_colors()
- curses.init_pair(1, 1, 2)
- stdscr.timeout(10)
- reset_screen_size()
- self.explorer = YX(0, 0)
- self.input_ = ''
- input_prompt = '> '
- interval = datetime.timedelta(seconds=5)
- last_ping = datetime.datetime.now() - interval
- while True:
- if self.disconnected and self.force_instant_connect:
- self.force_instant_connect = False
- self.connect()
- now = datetime.datetime.now()
- if now - last_ping > interval:
- if self.disconnected:
- self.connect()
- else:
- self.send('PING')
- last_ping = now
- if self.flash:
- curses.flash()
- self.flash = False
- if self.do_refresh:
- draw_screen()
- self.do_refresh = False
- while True:
- try:
- msg = self.queue.get(block=False)
- handle_input(msg)
- except queue.Empty:
- break
- try:
- key = stdscr.getkey()
- self.do_refresh = True
- except curses.error:
- continue
- keycode = None
- if len(key) == 1:
- keycode = ord(key)
- self.show_help = False
- self.draw_face = False
- if key == 'KEY_RESIZE':
- reset_screen_size()
- elif self.mode.has_input_prompt and key == 'KEY_BACKSPACE':
- self.input_ = self.input_[:-1]
- elif (((not self.mode.is_intro) and keycode == 27) # Escape
- or (self.mode.has_input_prompt and key == '\n'
- and self.input_ == ''\
- and self.mode.name in {'chat', 'command_thing',
- 'take_thing', 'drop_thing',
- 'admin_enter'})):
- if self.mode.name not in {'chat', 'play', 'study', 'edit'}:
- self.log_msg('@ aborted')
- self.switch_mode('play')
- elif self.mode.has_input_prompt and key == '\n' and self.input_ == '/help':
- self.show_help = True
- self.input_ = ""
- self.restore_input_values()
- elif self.mode.has_input_prompt and key != '\n': # Return key
- self.input_ += key
- max_length = self.window_width * self.size.y - len(input_prompt) - 1
- if len(self.input_) > max_length:
- self.input_ = self.input_[:max_length]
- elif key == self.keys['help'] and not self.mode.is_single_char_entry:
- self.show_help = True
- elif self.mode.name == 'login' and key == '\n':
- self.login_name = self.input_
- self.send('LOGIN ' + quote(self.input_))
- self.input_ = ""
- elif self.mode.name == 'enter_face' and key == '\n':
- enter_ascii_art('PLAYER_FACE')
- elif self.mode.name == 'enter_hat' and key == '\n':
- enter_ascii_art('PLAYER_HAT')
- elif self.mode.name == 'take_thing' and key == '\n':
- pick_selectable('PICK_UP')
- elif self.mode.name == 'drop_thing' and key == '\n':
- pick_selectable('DROP')
- elif self.mode.name == 'command_thing' and key == '\n':
- self.send('TASK:COMMAND ' + quote(self.input_))
- self.input_ = ""
- elif self.mode.name == 'control_pw_pw' and key == '\n':
- if self.input_ == '':
- self.log_msg('@ aborted')
- else:
- self.send('SET_MAP_CONTROL_PASSWORD ' + quote(self.tile_control_char) + ' ' + quote(self.input_))
- self.log_msg('@ sent new password for protection character "%s"' % self.tile_control_char)
+ self.show_help = False
+ self.draw_face = False
+ if key == 'KEY_RESIZE':
+ self.reset_size()
+ elif self.mode.has_input_prompt and key == 'KEY_BACKSPACE':
+ self.input_ = self.input_[:-1]
+ elif (((not self.mode.is_intro) and keycode == 27) # Escape
+ or (self.mode.has_input_prompt and key == '\n'
+ and self.input_ == ''\
+ and self.mode.name in {'chat', 'command_thing',
+ 'take_thing', 'drop_thing',
+ 'admin_enter'})):
+ if self.mode.name not in {'chat', 'play', 'study', 'edit'}:
+ self.log('@ aborted')
+ self.switch_mode('play')
+ elif self.mode.has_input_prompt and key == '\n' and self.input_ == '/help':
+ self.show_help = True
+ self.input_ = ""
+ self.restore_input_values()
+ elif self.mode.has_input_prompt and key != '\n': # Return key
+ self.input_ += key
+ max_length = self.right_window_width * self.size.y - len(self.input_prompt) - 1
+ if len(self.input_) > max_length:
+ self.input_ = self.input_[:max_length]
+ elif key == self.keys['help'] and not self.mode.is_single_char_entry:
+ self.show_help = True
+ elif self.mode.name == 'login' and key == '\n':
+ self.login_name = self.input_
+ self.send('LOGIN ' + quote(self.input_))
+ self.input_ = ""
+ elif self.mode.name == 'enter_face' and key == '\n':
+ enter_ascii_art('PLAYER_FACE', 3, 6)
+ elif self.mode.name == 'enter_design' and key == '\n':
+ if self.game.player.carrying.type_ == 'Hat':
+ enter_ascii_art('THING_DESIGN',
+ self.game.player.carrying.design[0].y,
+ self.game.player.carrying.design[0].x, True)
+ else:
+ enter_ascii_art('THING_DESIGN',
+ self.game.player.carrying.design[0].y,
+ self.game.player.carrying.design[0].x,
+ True, True)
+ elif self.mode.name == 'take_thing' and key == '\n':
+ pick_selectable('PICK_UP')
+ elif self.mode.name == 'drop_thing' and key == '\n':
+ pick_selectable('DROP')
+ elif self.mode.name == 'command_thing' and key == '\n':
+ self.send('TASK:COMMAND ' + quote(self.input_))
+ self.input_ = ""
+ elif self.mode.name == 'control_pw_pw' and key == '\n':
+ if self.input_ == '':
+ self.log('@ aborted')
+ else:
+ self.send('SET_MAP_CONTROL_PASSWORD ' + quote(self.tile_control_char) + ' ' + quote(self.input_))
+ self.log('@ sent new password for protection character "%s"' % self.tile_control_char)
+ self.switch_mode('admin')
+ elif self.mode.name == 'password' and key == '\n':
+ if self.input_ == '':
+ self.input_ = ' '
+ self.password = self.input_
+ self.switch_mode('edit')
+ elif self.mode.name == 'admin_enter' and key == '\n':
+ self.send('BECOME_ADMIN ' + quote(self.input_))
+ self.switch_mode('play')
+ elif self.mode.name == 'control_pw_type' and key == '\n':
+ if len(self.input_) != 1:
+ self.log('@ entered non-single-char, therefore aborted')
self.switch_mode('admin')
- elif self.mode.name == 'password' and key == '\n':
- if self.input_ == '':
- self.input_ = ' '
- self.password = self.input_
- self.switch_mode('edit')
- elif self.mode.name == 'admin_enter' and key == '\n':
- self.send('BECOME_ADMIN ' + quote(self.input_))
- self.switch_mode('play')
- elif self.mode.name == 'control_pw_type' and key == '\n':
- if len(self.input_) != 1:
- self.log_msg('@ entered non-single-char, therefore aborted')
- self.switch_mode('admin')
- else:
- self.tile_control_char = self.input_
- self.switch_mode('control_pw_pw')
- elif self.mode.name == 'admin_thing_protect' and key == '\n':
- if len(self.input_) != 1:
- self.log_msg('@ entered non-single-char, therefore aborted')
- else:
- self.send('THING_PROTECTION %s' % (quote(self.input_)))
- self.log_msg('@ sent new protection character for thing')
+ else:
+ self.tile_control_char = self.input_
+ self.switch_mode('control_pw_pw')
+ elif self.mode.name == 'admin_thing_protect' and key == '\n':
+ if len(self.input_) != 1:
+ self.log('@ entered non-single-char, therefore aborted')
+ else:
+ self.send('THING_PROTECTION %s' % (quote(self.input_)))
+ self.log('@ sent new protection character for thing')
+ self.switch_mode('admin')
+ elif self.mode.name == 'control_tile_type' and key == '\n':
+ if len(self.input_) != 1:
+ self.log('@ entered non-single-char, therefore aborted')
self.switch_mode('admin')
- elif self.mode.name == 'control_tile_type' and key == '\n':
- if len(self.input_) != 1:
- self.log_msg('@ entered non-single-char, therefore aborted')
- self.switch_mode('admin')
- else:
- self.tile_control_char = self.input_
- self.switch_mode('control_tile_draw')
- elif self.mode.name == 'chat' and key == '\n':
- if self.input_ == '':
- continue
- if self.input_[0] == '/':
- if self.input_.startswith('/nick'):
- tokens = self.input_.split(maxsplit=1)
- if len(tokens) == 2:
- self.send('NICK ' + quote(tokens[1]))
- else:
- self.log_msg('? need login name')
+ else:
+ self.tile_control_char = self.input_
+ self.switch_mode('control_tile_draw')
+ elif self.mode.name == 'chat' and key == '\n':
+ if self.input_ == '':
+ return
+ if self.input_[0] == '/':
+ if self.input_.startswith('/nick'):
+ tokens = self.input_.split(maxsplit=1)
+ if len(tokens) == 2:
+ self.send('NICK ' + quote(tokens[1]))
else:
- self.log_msg('? unknown command')
+ self.log('? need login name')
else:
- self.send('ALL ' + quote(self.input_))
- self.input_ = ""
- elif self.mode.name == 'name_thing' and key == '\n':
- if self.input_ == '':
- self.input_ = ' '
- self.send('THING_NAME %s %s' % (quote(self.input_),
- quote(self.password)))
- self.switch_mode('edit')
- elif self.mode.name == 'annotate' and key == '\n':
- if self.input_ == '':
- self.input_ = ' '
- self.send('ANNOTATE %s %s %s' % (self.explorer, quote(self.input_),
- quote(self.password)))
- self.switch_mode('edit')
- elif self.mode.name == 'portal' and key == '\n':
- if self.input_ == '':
- self.input_ = ' '
- self.send('PORTAL %s %s %s' % (self.explorer, quote(self.input_),
- quote(self.password)))
- self.switch_mode('edit')
- elif self.mode.name == 'study':
- if self.mode.mode_switch_on_key(self, key):
- continue
- elif key == self.keys['toggle_map_mode']:
- self.toggle_map_mode()
- elif key in self.movement_keys:
- move_explorer(self.movement_keys[key])
- elif self.mode.name == 'play':
- if self.mode.mode_switch_on_key(self, key):
- continue
- elif key == self.keys['door'] and task_action_on('door'):
- self.send('TASK:DOOR')
- elif key == self.keys['consume'] and task_action_on('consume'):
- self.send('TASK:INTOXICATE')
- elif key == self.keys['wear'] and task_action_on('wear'):
- self.send('TASK:WEAR')
- elif key == self.keys['spin'] and task_action_on('spin'):
- self.send('TASK:SPIN')
- elif key == self.keys['teleport']:
- if self.game.player.position in self.game.portals:
- self.host = self.game.portals[self.game.player.position]
- self.reconnect()
- else:
- self.flash = True
- self.log_msg('? not standing on portal')
- elif key in self.movement_keys and task_action_on('move'):
- self.send('TASK:MOVE ' + self.movement_keys[key])
- elif self.mode.name == 'write':
- self.send('TASK:WRITE %s %s' % (key, quote(self.password)))
- self.switch_mode('edit')
- elif self.mode.name == 'control_tile_draw':
- if self.mode.mode_switch_on_key(self, key):
- continue
- elif key in self.movement_keys:
- move_explorer(self.movement_keys[key])
- elif key == self.keys['toggle_tile_draw']:
- self.tile_draw = False if self.tile_draw else True
- elif self.mode.name == 'admin':
- if self.mode.mode_switch_on_key(self, key):
- continue
- elif key in self.movement_keys and task_action_on('move'):
- self.send('TASK:MOVE ' + self.movement_keys[key])
- elif self.mode.name == 'edit':
- if self.mode.mode_switch_on_key(self, key):
- continue
- elif key == self.keys['flatten'] and task_action_on('flatten'):
- self.send('TASK:FLATTEN_SURROUNDINGS ' + quote(self.password))
- elif key == self.keys['install'] and task_action_on('install'):
- self.send('TASK:INSTALL %s' % quote(self.password))
- elif key == self.keys['toggle_map_mode']:
- self.toggle_map_mode()
- elif key in self.movement_keys and task_action_on('move'):
- self.send('TASK:MOVE ' + self.movement_keys[key])
+ self.log('? unknown command')
+ else:
+ self.send('ALL ' + quote(self.input_))
+ self.input_ = ""
+ elif self.mode.name == 'name_thing' and key == '\n':
+ if self.input_ == '':
+ self.input_ = ' '
+ self.send('THING_NAME %s %s' % (quote(self.input_),
+ quote(self.password)))
+ self.switch_mode('edit')
+ elif self.mode.name == 'annotate' and key == '\n':
+ if self.input_ == '':
+ self.input_ = ' '
+ self.send('ANNOTATE %s %s %s' % (self.explorer, quote(self.input_),
+ quote(self.password)))
+ self.switch_mode('edit')
+ elif self.mode.name == 'portal' and key == '\n':
+ if self.input_ == '':
+ self.input_ = ' '
+ self.send('PORTAL %s %s %s' % (self.explorer, quote(self.input_),
+ quote(self.password)))
+ self.switch_mode('edit')
+ elif self.mode.name == 'study':
+ if self.mode.mode_switch_on_key(self, key):
+ return
+ elif key == self.keys['toggle_map_mode']:
+ self.toggle_map_mode()
+ elif key in self.movement_keys:
+ move_explorer(self.movement_keys[key])
+ elif self.mode.name == 'play':
+ if self.mode.mode_switch_on_key(self, key):
+ return
+ elif key == self.keys['door'] and task_action_on('door'):
+ self.send('TASK:DOOR')
+ elif key == self.keys['consume'] and task_action_on('consume'):
+ self.send('TASK:INTOXICATE')
+ elif key == self.keys['wear'] and task_action_on('wear'):
+ self.send('TASK:WEAR')
+ elif key == self.keys['spin'] and task_action_on('spin'):
+ self.send('TASK:SPIN')
+ elif key == self.keys['dance'] and task_action_on('dance'):
+ self.send('TASK:DANCE')
+ elif key == self.keys['teleport']:
+ if self.game.player.position in self.game.portals:
+ self.socket.host = self.game.portals[self.game.player.position]
+ self.reconnect()
+ else:
+ self.flash = True
+ self.log('? not standing on portal')
+ elif key in self.movement_keys and task_action_on('move'):
+ self.send('TASK:MOVE ' + self.movement_keys[key])
+ elif self.mode.name == 'write':
+ self.send('TASK:WRITE %s %s' % (key, quote(self.password)))
+ self.switch_mode('edit')
+ elif self.mode.name == 'control_tile_draw':
+ if self.mode.mode_switch_on_key(self, key):
+ return
+ elif key in self.movement_keys:
+ move_explorer(self.movement_keys[key])
+ elif key == self.keys['toggle_tile_draw']:
+ self.tile_draw = False if self.tile_draw else True
+ elif self.mode.name == 'admin':
+ if self.mode.mode_switch_on_key(self, key):
+ return
+ elif key == self.keys['toggle_map_mode']:
+ self.toggle_map_mode()
+ elif key in self.movement_keys and task_action_on('move'):
+ self.send('TASK:MOVE ' + self.movement_keys[key])
+ elif self.mode.name == 'edit':
+ if self.mode.mode_switch_on_key(self, key):
+ return
+ elif key == self.keys['flatten'] and task_action_on('flatten'):
+ self.send('TASK:FLATTEN_SURROUNDINGS ' + quote(self.password))
+ elif key == self.keys['install'] and task_action_on('install'):
+ self.send('TASK:INSTALL %s' % quote(self.password))
+ elif key == self.keys['toggle_map_mode']:
+ self.toggle_map_mode()
+ elif key in self.movement_keys and task_action_on('move'):
+ self.send('TASK:MOVE ' + self.movement_keys[key])
if len(sys.argv) != 2:
raise ArgError('wrong number of arguments, need game host')
host = sys.argv[1]
-TUI(host)
+RogueChatTUI(host)