home · contact · privacy
Make name shortening more forgiving.
[plomrogue2] / rogue_chat_curses.py
index ff0c46c579559e0faacbb8bee6ff07ac19b01ffc..d2cc704173f33243e52ee37ab08cbc371021e30a 100755 (executable)
@@ -187,8 +187,6 @@ 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('@ hint: see top of terminal for how to get help.')
-    game.tui.log_msg('@ hint: enter study mode to understand your environment.')
 cmd_LOGIN_OK.argtypes = ''
 
 def cmd_ADMIN_OK(game):
@@ -623,6 +621,7 @@ class TUI:
             self.disconnected = False
             self.game.thing_types = {}
             self.game.terrains = {}
+            self.is_admin = False
             time.sleep(0.1)  # give potential SSL negotation some time …
             self.socket.send('TASKS')
             self.socket.send('TERRAINS')
@@ -706,6 +705,8 @@ class TUI:
             self.log_msg('@ 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):
@@ -786,13 +787,16 @@ class TUI:
             for i in range(len(self.selectables)):
                 self.log_msg(str(i) + ': ' + self.selectables[i])
         elif self.mode.name == 'enter_design':
-            self.log_msg('@ 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))
             if self.game.player.carrying.type_ == 'Hat':
+                self.log_msg('@ 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_msg('@ Legal characters: ' + self.game.players_hat_chars)
                 self.log_msg('@ (Eat cookies to extend the ASCII characters available for drawing.)')
+            else:
+                self.log_msg('@ Width of first line determines maximum width for remaining design')
+                self.log_msg('@ 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':
@@ -803,8 +807,9 @@ class TUI:
         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):
@@ -813,8 +818,9 @@ class TUI:
             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):
@@ -937,7 +943,8 @@ class TUI:
             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)
+            self.left_window_width = min(52, int(self.size.x / 2))
+            self.right_window_width = self.size.x - self.left_window_width
 
         def recalc_input_lines():
             if not self.mode.has_input_prompt:
@@ -945,7 +952,7 @@ class TUI:
             else:
                 self.input_lines = msg_into_lines_of_width(input_prompt
                                                            + self.input_ + '█',
-                                                           self.window_width)
+                                                           self.right_window_width)
 
         def move_explorer(direction):
             target = self.game.map_geometry.move_yx(self.explorer, direction)
@@ -960,41 +967,41 @@ class TUI:
         def draw_history():
             lines = []
             for line in self.log:
-                lines += msg_into_lines_of_width(line, self.window_width)
+                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
-                safe_addstr(max_y - i - 1, self.window_width, lines[i])
+                safe_addstr(max_y - i - 1, self.left_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)
+            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
-                safe_addstr(y, self.window_width, lines[i])
+                safe_addstr(y, self.left_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])
+                safe_addstr(y, self.left_window_width, self.input_lines[i])
                 y += 1
 
         def draw_stats():
             stats = 'ENERGY: %s BLADDER: %s' % (self.game.energy,
                                                 self.game.bladder_pressure)
-            safe_addstr(0, self.window_width, stats)
+            safe_addstr(0, self.left_window_width, stats)
 
         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,
+            safe_addstr(1, self.left_window_width,
                         'MODE: %s – %s' % (self.mode.short_desc, help))
 
         def draw_map():
@@ -1051,7 +1058,7 @@ class TUI:
                     for line in map_lines_split:
                         self.map_lines += [''.join(line)]
                 window_center = YX(int(self.size.y / 2),
-                                   int(self.window_width / 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
@@ -1064,18 +1071,35 @@ class TUI:
             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]
+                to_draw = self.map_lines[map_y][map_x:self.left_window_width + self.offset.x]
                 safe_addstr(term_y, term_x, to_draw)
                 term_y += 1
                 map_y += 1
 
+        def draw_names():
+            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] + '…'
+                safe_addstr(y, 0, '@%s:%s' % (t.thing_char, name))
+                y += 1
+                if y >= self.size.y:
+                    break
+
         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
+            start_x = self.left_window_width - 10
             def draw_body_part(body_part, end_y):
                 safe_addstr(end_y - 3, start_x, '----------')
                 safe_addstr(end_y - 2, start_x, '| ' + body_part[0:6] + ' |')
@@ -1088,8 +1112,8 @@ class TUI:
                 draw_body_part(t.hat, self.size.y - 6)
             safe_addstr(self.size.y - 2, start_x, '----------')
             name = t.name[:]
-            if len(name) > 6:
-                name = name[:6] + '…'
+            if len(name) > 7:
+                name = name[:6 - 1] + '…'
             safe_addstr(self.size.y - 1, start_x,
                         '@%s:%s' % (t.thing_char, name))
 
@@ -1113,16 +1137,16 @@ class TUI:
             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)
+                            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.window_width)
+                lines += msg_into_lines_of_width(line, self.right_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),
+                            self.left_window_width * (not self.mode.has_input_prompt),
                             lines[i])
 
         def draw_screen():
@@ -1141,8 +1165,10 @@ class TUI:
                 draw_map()
             if self.show_help:
                 draw_help()
-            if self.draw_face and self.mode.name in {'chat', 'play'}:
-                draw_face_popup()
+            if self.mode.name in {'chat', 'play'}:
+                draw_names()
+                if self.draw_face:
+                    draw_face_popup()
 
         def pick_selectable(task_name):
             try:
@@ -1156,19 +1182,43 @@ class TUI:
             self.input_ = ''
             self.switch_mode('play')
 
-        def enter_ascii_art(command, height, width, with_pw=False):
-            if len(self.input_) > width:
+        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_msg('? 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_msg('? input too long, '
                              'must be max %s; try again' % width)
                 return
-            if len(self.input_) < width:
-                self.input_ += ' ' * (width - len(self.input_))
             self.log_msg('  ' + self.input_)
-            self.full_ascii_draw += 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 < height:
                 self.restore_input_values()
             else:
+                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)))
@@ -1209,10 +1259,14 @@ class TUI:
             'dance': 'DANCE',
         }
 
-        curses.curs_set(False)  # hide cursor
+        curses.curs_set(0)  # hide cursor
         curses.start_color()
         self.set_default_colors()
-        curses.init_pair(1, 1, 2)
+        curses.init_pair(1, 7, 0)
+        if not curses.can_change_color():
+            self.log_msg('@ unfortunately, your terminal does not seem to '
+                         'support re-definition of colors; you might miss out '
+                         'on some color effects')
         stdscr.timeout(10)
         reset_screen_size()
         self.explorer = YX(0, 0)
@@ -1280,7 +1334,7 @@ class TUI:
                 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
+                max_length = self.right_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:
@@ -1292,9 +1346,15 @@ class TUI:
             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':
-                enter_ascii_art('THING_DESIGN',
-                                self.game.player.carrying.design[0].y,
-                                self.game.player.carrying.design[0].x, True)
+                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':