laneya

multiplayer roguelike game
git clone https://git.ce9e.org/laneya.git

commit
0252f075b2837d8e775ff335e3dee82dc5657682
parent
94d705f49a24f735a1365131043ab08d83e133be
Author
Tobias Bengfort <tobias.bengfort@gmx.net>
Date
2014-10-31 14:03
add some map/sprites abstraction

Diffstat

M docs/source/index.rst 1 +
A laneya/map.py 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M laneya/server.py 44 +++++++++-----------------------------------

3 files changed, 122 insertions, 35 deletions


diff --git a/docs/source/index.rst b/docs/source/index.rst

@@ -10,6 +10,7 @@ Contents
   10    10     quickstart
   11    11     protocol
   12    12     actions
   -1    13     map
   13    14     deferred
   14    15 
   15    16 Indices and tables

diff --git a/laneya/map.py b/laneya/map.py

@@ -0,0 +1,112 @@
   -1     1 import protocol
   -1     2 
   -1     3 
   -1     4 class Map(object):
   -1     5     """A singel map containing sprites.
   -1     6 
   -1     7     The map object takes care of managing all sprites within one map. As only
   -1     8     very few actions involve multiple maps (e.g. moving from one map to
   -1     9     another) this covers a lot of the game logic.
   -1    10 
   -1    11     The passed server is used to send updates to the clients.
   -1    12 
   -1    13     Map objects expose an API for the server (e.g. :py:meth:`step`) and another
   -1    14     one for sprites (e.g. :py:meth:`move_sprite`).
   -1    15 
   -1    16     """
   -1    17     def __init__(self, server):
   -1    18         self.server = server
   -1    19         self.sprites = {}
   -1    20         self.movable_layer = [[None for i in xrange(100)] for i in xrange(100)]
   -1    21 
   -1    22     def step(self):
   -1    23         """Update this map and all of its sprites.
   -1    24 
   -1    25         If this map is currently active (contains at least on user), the server
   -1    26         should call this method once per mainloop cycle.
   -1    27 
   -1    28         """
   -1    29         for sprite in self.sprites.itervalues():
   -1    30             sprite.step()
   -1    31 
   -1    32     def is_collision_free(self, x, y):
   -1    33         """Check whether a sprite can move to field (x, y)."""
   -1    34         return self.movable_layer[x][y] is None
   -1    35 
   -1    36     def move_sprite(self, sprite, dx, dy):
   -1    37         """Move a sprite."""
   -1    38         if self.is_collision_free(sprite.x + dx, sprite.y + dy):
   -1    39             sprite.x += dx
   -1    40             sprite.y += dy
   -1    41             self.server.broadcastUpdate(
   -1    42                 'position',
   -1    43                 x=sprite.x,
   -1    44                 y=sprite.y,
   -1    45                 entity=sprite.id)
   -1    46         else:
   -1    47             raise protocol.IllegalError
   -1    48 
   -1    49 
   -1    50 class Sprite(object):
   -1    51     """Simple base class for visible game objects."""
   -1    52 
   -1    53     def __init__(self, name, _map, x, y):
   -1    54         self.id = "%s:%s" % (self.__class__.__name__, name)
   -1    55         self.map = _map
   -1    56         self.x = x
   -1    57         self.y = y
   -1    58 
   -1    59         self.map.sprites[self.id] = self
   -1    60 
   -1    61     def kill(self):
   -1    62         """Remove this sprite from the map."""
   -1    63         del self.map.sprites[self.id]
   -1    64 
   -1    65     def step(self):
   -1    66         """Update this sprite.
   -1    67 
   -1    68         This function is executed once per mainloop cycle. Subclasses should
   -1    69         overwrite this in order to define custom behavior.
   -1    70 
   -1    71         """
   -1    72         pass
   -1    73 
   -1    74     def interact(self, other):
   -1    75         """Interact with this sprite.
   -1    76 
   -1    77         Subclasses should overwrite this in order to define custom
   -1    78         interactions.
   -1    79 
   -1    80         """
   -1    81         pass
   -1    82 
   -1    83 
   -1    84 class MovingSprite(Sprite):
   -1    85     """A sprite that can move.
   -1    86 
   -1    87     You can set :py:attr:`direction` to one of 'north', 'east', 'south', 'west'
   -1    88     or 'stop'.  This sprite will then automatically move one filed in the
   -1    89     specified direction in every mainloop cycle.
   -1    90 
   -1    91     """
   -1    92 
   -1    93     def __init__(self, *args, **kwargs):
   -1    94         super(MovingSprite, self).__init__(*args, **kwargs)
   -1    95         self.direction = 'stop'
   -1    96 
   -1    97     def step(self):
   -1    98         if self.direction == 'north':
   -1    99             self.map.move_sprite(self, 0, -1)
   -1   100         elif self.direction == 'east':
   -1   101             self.map.move_sprite(self, 1, 0)
   -1   102         elif self.direction == 'south':
   -1   103             self.map.move_sprite(self, 0, 1)
   -1   104         elif self.direction == 'west':
   -1   105             self.map.move_sprite(self, -1, 0)
   -1   106 
   -1   107 
   -1   108 class User(MovingSprite):
   -1   109     """Sprite representing a user."""
   -1   110 
   -1   111 
   -1   112 __all__ = ['Map', 'Sprite', 'MovingSprite', 'User']

diff --git a/laneya/server.py b/laneya/server.py

@@ -6,62 +6,36 @@ from twisted.internet import reactor
    6     6 from twisted.internet import task
    7     7 
    8     8 import protocol
    9    -1 
   10    -1 
   11    -1 class User(object):
   12    -1     def __init__(self, position_x=0, position_y=0, direction='stop'):
   13    -1         self.position_x = position_x
   14    -1         self.position_y = position_y
   15    -1         self.direction = direction
   -1     9 from map import Map, User
   16    10 
   17    11 
   18    12 class Server(protocol.ServerProtocolFactory):
   19    13     def __init__(self):
   20    14         protocol.ServerProtocolFactory.__init__(self)
   21    15         self.users = {}
   22    -1         self.movable_layer = [[None for i in xrange(100)] for i in xrange(100)]
   -1    16         self.map = Map(self)  # TODO: more than one map
   23    17 
   24    18     def requestReceived(self, user, action, **kwargs):  # TODO
   25    19         if user not in self.users:
   26    -1             self.users[user] = User()
   -1    20             self.users[user] = User(user, self.map, 10, 10)
   27    21             print("login %s" % user)
   28    22 
   29    23         if action == 'move':
   30    24             self.users[user].direction = kwargs['direction']
   31    25         elif action == 'logout':
   -1    26             self.users[user].kill()
   32    27             del self.users[user]
   33    28             print("logout %s" % user)
   34    29         else:
   35    30             raise protocol.InvalidError
   36    31 
   37    32     def mainloop(self):
   38    -1         for key, user in self.users.iteritems():
   39    -1             if user.direction == 'north':
   40    -1                 self.move_user(user, 0, -1)
   41    -1             elif user.direction == 'east':
   42    -1                 self.move_user(user, 1, 0)
   43    -1             elif user.direction == 'south':
   44    -1                 self.move_user(user, 0, 1)
   45    -1             elif user.direction == 'west':
   46    -1                 self.move_user(user, -1, 0)
   47    -1             if user.direction != 'stop':
   48    -1                 self.broadcastUpdate(
   49    -1                     'position',
   50    -1                     x=user.position_x,
   51    -1                     y=user.position_y,
   52    -1                     entity=key)
   -1    33         # only the maps with users in them get updated
   -1    34         for _map in self.get_active_maps():
   -1    35             _map.step()
   53    36 
   54    -1     def collision_check(self, new_x, new_y):
   55    -1         return self.movable_layer[new_x][new_y] is None
   56    -1 
   57    -1     def move_user(self, user, dx, dy):
   58    -1         if self.collision_check(user.position_x, user.position_y - 1):
   59    -1             self.movable_layer[user.position_x][user.position_y] = None
   60    -1             user.position_x += dx
   61    -1             user.position_y += dy
   62    -1             self.movable_layer[user.position_x][user.position_y] = user
   63    -1         else:
   64    -1             raise protocol.IllegalError
   -1    37     def get_active_maps(self):
   -1    38         return set([user.map for user in self.users.itervalues()])
   65    39 
   66    40 
   67    41 def main():