home · contact · privacy
Only save maps that have been modified from default.
[plomrogue2] / plomrogue / game.py
index 1ffbf1acfa186f900d96a8252bd5db58dd5fa0a8..ccd51dcc1b363e1d6e1cb46428dd96b61ad023cb 100755 (executable)
@@ -4,6 +4,7 @@ from plomrogue.errors import GameError, PlayError
 from plomrogue.io import GameIO
 from plomrogue.misc import quote
 from plomrogue.mapping import YX, MapGeometrySquare, Map
 from plomrogue.io import GameIO
 from plomrogue.misc import quote
 from plomrogue.mapping import YX, MapGeometrySquare, Map
+import string
 
 
 
 
 
 
@@ -33,6 +34,19 @@ class GameBase:
 
 
 
 
 
 
+class SaveableMap(Map):
+    modified = False
+
+    def __setitem__(self, *args, **kwargs):
+        super().__setitem__(*args, **kwargs)
+        self.modified = True
+
+    def set_line(self, *args, **kwargs):
+        super().set_line(*args, **kwargs)
+        self.modified = True
+
+
+
 import os
 class Game(GameBase):
 
 import os
 class Game(GameBase):
 
@@ -43,11 +57,25 @@ class Game(GameBase):
         self.tasks = {}
         self.thing_types = {}
         self.sessions = {}
         self.tasks = {}
         self.thing_types = {}
         self.sessions = {}
-        self.map = Map(self.map_geometry.size)
-        self.map_control = Map(self.map_geometry.size)
+        self.maps = {}
+        self.map_controls = {}
         self.map_control_passwords = {}
         self.annotations = {}
         self.portals = {}
         self.map_control_passwords = {}
         self.annotations = {}
         self.portals = {}
+        self.player_chars = string.digits + string.ascii_letters
+        self.player_char_i = -1
+        self.terrains = {
+            '.': 'floor',
+            'X': 'wall',
+            '=': 'window',
+            '#': 'bed',
+            'T': 'desk',
+            '8': 'cupboard',
+            '[': 'glass door',
+            'o': 'sink',
+            'O': 'toilet'
+        }
+        self.new_world(self.map_geometry)
         if os.path.exists(self.io.save_file):
             if not os.path.isfile(self.io.save_file):
                 raise GameError('save file path refers to non-file')
         if os.path.exists(self.io.save_file):
             if not os.path.isfile(self.io.save_file):
                 raise GameError('save file path refers to non-file')
@@ -67,8 +95,9 @@ class Game(GameBase):
                 print("FILE INPUT LINE %5s: %s" % (i, line), end='')
                 self.io.handle_input(line, god_mode=True)
 
                 print("FILE INPUT LINE %5s: %s" % (i, line), end='')
                 self.io.handle_input(line, god_mode=True)
 
-    def can_do_tile_with_pw(self, yx, pw):
-        tile_class = self.map_control[yx]
+    def can_do_tile_with_pw(self, big_yx, little_yx, pw):
+        map_control = self.get_map(big_yx)
+        tile_class = map_control[little_yx]
         if tile_class in self.map_control_passwords:
             tile_pw = self.map_control_passwords[tile_class]
             if pw != tile_pw:
         if tile_class in self.map_control_passwords:
             tile_pw = self.map_control_passwords[tile_class]
             if pw != tile_pw:
@@ -76,7 +105,6 @@ class Game(GameBase):
         return True
 
     def get_string_options(self, string_option_type):
         return True
 
     def get_string_options(self, string_option_type):
-        import string
         if string_option_type == 'direction':
             return self.map_geometry.get_directions()
         elif string_option_type == 'char':
         if string_option_type == 'direction':
             return self.map_geometry.get_directions()
         elif string_option_type == 'char':
@@ -97,22 +125,27 @@ class Game(GameBase):
         self.io.send('TURN ' + str(self.turn))
         for c_id in self.sessions:
             player = self.get_thing(self.sessions[c_id])
         self.io.send('TURN ' + str(self.turn))
         for c_id in self.sessions:
             player = self.get_thing(self.sessions[c_id])
-            visible_terrain = player.fov_stencil_map(self.map)
+            visible_terrain = player.fov_stencil_map()
             self.io.send('FOV %s' % quote(player.fov_stencil.terrain), c_id)
             self.io.send('MAP %s %s %s' % (self.get_map_geometry_shape(),
             self.io.send('FOV %s' % quote(player.fov_stencil.terrain), c_id)
             self.io.send('MAP %s %s %s' % (self.get_map_geometry_shape(),
-                                           self.map_geometry.size,
+                                           player.fov_stencil.geometry.size,
                                            quote(visible_terrain)), c_id)
                                            quote(visible_terrain)), c_id)
-            visible_control = player.fov_stencil_map(self.map_control)
+            visible_control = player.fov_stencil_map('control')
             self.io.send('MAP_CONTROL %s' % quote(visible_control), c_id)
             self.io.send('MAP_CONTROL %s' % quote(visible_control), c_id)
-            for t in [t for t in self.things
-                      if player.fov_stencil[t.position] == '.']:
-                self.io.send('THING %s %s %s' % (t.position, t.type_, t.id_), c_id)
-                if hasattr(t, 'nickname'):
-                    self.io.send('THING_NAME %s %s' % (t.id_,
-                                                       quote(t.nickname)), c_id)
-            for yx in [yx for yx in self.portals
-                       if player.fov_stencil[yx] == '.']:
-                self.io.send('PORTAL %s %s' % (yx, quote(self.portals[yx])), c_id)
+            for t in [t for t in self.things if player.fov_test(*t.position)]:
+                target_yx = player.fov_stencil.target_yx(*t.position)
+                self.io.send('THING %s %s %s' % (target_yx, t.type_, t.id_), c_id)
+                if hasattr(t, 'name'):
+                    self.io.send('THING_NAME %s %s' % (t.id_, quote(t.name)), c_id)
+                if hasattr(t, 'player_char'):
+                    self.io.send('THING_CHAR %s %s' % (t.id_,
+                                                       quote(t.player_char)), c_id)
+            for big_yx in self.portals:
+                for little_yx in [little_yx for little_yx in self.portals[big_yx]
+                                  if player.fov_test(big_yx, little_yx)]:
+                    target_yx = player.fov_stencil.target_yx(big_yx, little_yx)
+                    portal = self.portals[big_yx][little_yx]
+                    self.io.send('PORTAL %s %s' % (target_yx, quote(portal)), c_id)
         self.io.send('GAME_STATE_COMPLETE')
 
     def run_tick(self):
         self.io.send('GAME_STATE_COMPLETE')
 
     def run_tick(self):
@@ -125,8 +158,8 @@ class Game(GameBase):
                     break
             if not connection_id_found:
                 t = self.get_thing(self.sessions[connection_id])
                     break
             if not connection_id_found:
                 t = self.get_thing(self.sessions[connection_id])
-                if hasattr(t, 'nickname'):
-                    self.io.send('CHAT ' + quote(t.nickname + ' left the map.'))
+                if hasattr(t, 'name'):
+                    self.io.send('CHAT ' + quote(t.name + ' left the map.'))
                 self.things.remove(t)
                 to_delete += [connection_id]
         for connection_id in to_delete:
                 self.things.remove(t)
                 to_delete += [connection_id]
         for connection_id in to_delete:
@@ -187,32 +220,56 @@ class Game(GameBase):
             return 1
         return max([t.id_ for t in self.things]) + 1
 
             return 1
         return max([t.id_ for t in self.things]) + 1
 
+    def get_next_player_char(self):
+        self.player_char_i += 1
+        if self.player_char_i >= len(self.player_chars):
+            self.player_char_i = 0
+        return self.player_chars[self.player_char_i]
+
     def save(self):
 
       def write(f, msg):
           f.write(msg + '\n')
 
       with open(self.io.save_file, 'w') as f:
     def save(self):
 
       def write(f, msg):
           f.write(msg + '\n')
 
       with open(self.io.save_file, 'w') as f:
-          # TODO: save tasks
           write(f, 'TURN %s' % self.turn)
           map_geometry_shape = self.get_map_geometry_shape()
           write(f, 'MAP %s %s' % (map_geometry_shape, self.map_geometry.size,))
           write(f, 'TURN %s' % self.turn)
           map_geometry_shape = self.get_map_geometry_shape()
           write(f, 'MAP %s %s' % (map_geometry_shape, self.map_geometry.size,))
-          for y, line in self.map.lines():
-              write(f, 'MAP_LINE %5s %s' % (y, quote(line)))
-          for yx in self.annotations:
-              write(f, 'GOD_ANNOTATE %s %s' % (yx, quote(self.annotations[yx])))
-          for yx in self.portals:
-              write(f, 'GOD_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 big_yx in [yx for yx in self.maps if self.maps[yx].modified]:
+              for y, line in self.maps[big_yx].lines():
+                  write(f, 'MAP_LINE %s %5s %s' % (big_yx, y, quote(line)))
+          for big_yx in self.annotations:
+              for little_yx in self.annotations[big_yx]:
+                  write(f, 'GOD_ANNOTATE %s %s %s' %
+                        (big_yx, little_yx, quote(self.annotations[big_yx][little_yx])))
+          for big_yx in self.portals:
+              for little_yx in self.portals[big_yx]:
+                  write(f, 'GOD_PORTAL %s %s %s' % (big_yx, little_yx,
+                                                    quote(self.portals[big_yx][little_yx])))
+          for big_yx in [yx for yx in self.map_controls
+                         if self.map_controls[yx].modified]:
+              for y, line in self.map_controls[big_yx].lines():
+                  write(f, 'MAP_CONTROL_LINE %s %5s %s' % (big_yx, 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]))
           for t in [t for t in self.things if not t.type_ == 'Player']:
           for tile_class in self.map_control_passwords:
               write(f, 'MAP_CONTROL_PW %s %s' % (tile_class,
                                                  self.map_control_passwords[tile_class]))
           for t in [t for t in self.things if not t.type_ == 'Player']:
-              write(f, 'THING %s %s %s' % (t.position, t.type_, t.id_))
+              write(f, 'THING %s %s %s %s' % (t.position[0],
+                                              t.position[1], t.type_, t.id_))
+              if hasattr(t, 'name'):
+                  write(f, 'THING_NAME %s %s' % (t.id_, quote(t.name)))
+
+    def get_map(self, big_yx, type_='normal'):
+        if type_ == 'normal':
+            maps = self.maps
+        elif type_ == 'control':
+            maps = self.map_controls
+        if not big_yx in maps:
+            maps[big_yx] = SaveableMap(self.map_geometry)
+        return maps[big_yx]
 
     def new_world(self, map_geometry):
         self.map_geometry = map_geometry
 
     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.maps[YX(0,0)] = SaveableMap(self.map_geometry)
+        self.map_controls[YX(0,0)] = SaveableMap(self.map_geometry)
         self.annotations = {}
         self.annotations = {}