diff --git a/bomber/engine.py b/bomber/engine.py index de3d8cb..a91cf08 100644 --- a/bomber/engine.py +++ b/bomber/engine.py @@ -55,6 +55,8 @@ def hide(self): class FireTrail(MapObject): def __init__(self, bomb, start, end): + self.start = start + self.end = end x, y = start _x, _y = end @@ -113,11 +115,26 @@ def state(self, value): self._state = value self.on_new_state(value) + def bomb_info(self, bomb): + # for clients it is not a difference if the bomb is exploding or burning + # since the exploding state is just present for 0.2 seconds it will probably produce bugs + # if the client doesn't know or care about it. + + default_info = (b.position_int, b.update_timer, b.state,) + if b.state == "exploding": + return (b.position_int, b.update_timer + 1.5, "burning",) + (self.fire_trails) + elif b.state == "burning": + return default_info + ([f.end for f in self.fire_trails],) + elif b.state == "hiding": + return default_info + ([w.position_int for w in self.destroyed_walls],) + return default_info + (None,) + def on_new_state(self, state): if state == "exploding": self.color = (255, 255, 230) self.update_timer = self.exploding_time self.deploy_fire_trails() + self.player.map.inform_async(self.bomb_info()) elif state == "burning": self.color = (255, 55, 10) self.update_timer = self.burn_time @@ -128,6 +145,7 @@ def on_new_state(self, state): self.player.points += len([w for w in self.destroyed_walls if not w.hidden]) for wall in self.destroyed_walls: wall.hide() + self.player.map.inform_async(self.bomb_info()) def deploy_fire_trails(self): for direction in "wasd": @@ -209,9 +227,19 @@ class IndestructableWall(Wall): COLLIDING_OBJECTS = (IndestructableWall, DestructableWall, Bomb) +def rest_call(fn): + def foo(self, *args, **kw): + ret = fn(self, *args, **kw) + if ret: + self.client.inform(*ret) + else: + self.client.inform(("ACK", None)) + return foo + + class Player: - def __init__(self, position, client, name="Hans", color=None, password="", id=None, map=None): + def __init__(self, position, client, name="Hans", color=None, password="", id=None, map=None, async=False): x, y = position self.__x = x self.__y = y @@ -220,6 +248,7 @@ def __init__(self, position, client, name="Hans", color=None, password="", id=No hashpassword = lambda x: x self.name = name self.client = client + self.async = async self.color = { "1": (255, 0, 0), # red "2": (0, 0, 255), # blue @@ -244,6 +273,7 @@ def __init__(self, position, client, name="Hans", color=None, password="", id=No client.on_message.connect(self.handle_msg) self.client.inform("OK", self.whoami_data) + self._setup_async_inform_function() @property def position_int(self): @@ -283,6 +313,20 @@ def get_direction_to_int_position(self): elif yf < yi: return "s" + def inform_async(self, msg_type, data): + self.client.inform(msg_type, data) + + def rest_inform(self, msg_type, data): + accepted_msgtypes = { + "BOMB", "MOVE" + } + if data[0] == self.id and msg_type in accepted_msgtypes: + self.client.inform(msg_type, data) + + def _setup_async_inform_function(self): + if not self.async: + self.inform_async = self.rest_inform + def die(self, hard=False): print("die {}, tonight you dine in hell".format(self.name)) loop = asyncio.get_event_loop() @@ -307,22 +351,16 @@ def handle_msg(self, msg): msg_type = msg.pop("type") try: handler = getattr(self, "do_{}".format(msg_type)) - ret = handler(**msg) - if ret: - if isinstance(ret, tuple) and len(ret) == 2: - self.client.inform(* ret) - else: - self.client.inform("ACK", ret) except AttributeError: self.client.inform("ERR", "The function ({}) you are calling is not available".format(msg_type)) + ret = handler(**msg) - def do_whoami(self, **kwargs): - return ("WHOAMI", self.whoami_data) - - def do_map(self, **kwargs): - return("MAP", "\n".join( - "".join(e.char for e in line) for line in self.map._map),) + if ret: + msg_type, *rest = ret + rest.insert(0, self.id) + self.map.inform_all(msg_type, rest) + # self.client.inform(* ret) def do_move(self, direction, distance=1., **kwargs): assert direction in "wasd" @@ -330,20 +368,40 @@ def do_move(self, direction, distance=1., **kwargs): self.direction = direction self.moving = distance * 10 # TODO, don't use constant + return ("MOVE", self.position_int, direction, distance) def do_bomb(self, **kwargs): - self.map.plant_bomb(self, fuse_time=kwargs.get("fuse_time", 5)) + fuse_time = kwargs.get("fuse_time", 5) + bomb = self.map.plant_bomb(self, fuse_time=fuse_time) + return ("BOMB", self._bomb_info(bomb)) + # REST packets, answer should only go to sender + + @rest_call + def do_whoami(self, **kwargs): + return ("WHOAMI", self.whoami_data) + + @rest_call + def do_map(self, **kwargs): + return("MAP", "\n".join( + "".join(e.char for e in line) for line in self.map._map),) + + @rest_call def do_what_bombs(self, **kwargs): return ("WHAT_BOMBS", [ - (b.position_int, b.update_timer, b.state,) for b in self.map.items if isinstance(b, Bomb) + b.bomb_info() for b in self.map.items if isinstance(b, Bomb) ]) + @rest_call def do_what_foes(self, **kwargs): return ("WHAT_FOES", [ (p.position_int, p.direction, p.id, p.name) for p in self.map.players ]) + @rest_call + def do_points(self, **kwargs): + return ("POINTS", [(p.id, p.name, p.points) for p in self.map.players]) + def update(self, dt): if not self.moving > 0 or not self.alive: return @@ -514,7 +572,7 @@ def key_down(self, key, code): elif code.lower() == "b": self.players[0].do_bomb() - def player_register(self, client, username, password="", **kw): + def player_register(self, client, username, password="", async=False, **kw): try: if username in self.users: position = self.users[username] @@ -533,6 +591,7 @@ def player_register(self, client, username, password="", **kw): map=self, name=username, password=password, + async=async, ) if old_player: player.points = old_player.points @@ -554,6 +613,10 @@ def player_unregister(self, position, password): # self.freespawnpoints.append(position) return old_player + def inform_all(self, msg_type, msg): + for player in self.players: + player.inform_async(msg_type, msg) + def plant_bomb(self, player, fuse_time): bombs = [b for b in self.items if isinstance(b, Bomb) and b.player is player and b.state == "ticking"] if len(bombs) >= player.bombamount: