diff --git a/werewolf/roles/blob.py b/werewolf/roles/blob.py new file mode 100644 index 0000000..bd7b598 --- /dev/null +++ b/werewolf/roles/blob.py @@ -0,0 +1,101 @@ +import logging +import random + +from werewolf.constants import ALIGNMENT_NEUTRAL, CATEGORY_NEUTRAL_EVIL +from werewolf.listener import wolflistener +from werewolf.player import Player +from werewolf.role import Role + +log = logging.getLogger("red.fox_v3.werewolf.role.blob") + + +class TheBlob(Role): + rand_choice = True + category = [CATEGORY_NEUTRAL_EVIL] # List of enrolled categories + alignment = ALIGNMENT_NEUTRAL # 1: Town, 2: Werewolf, 3: Neutral + channel_id = "" # Empty for no private channel + unique = True # Only one of this role per game + game_start_message = ( + "Your role is **The Blob**\n" + "You win by absorbing everyone town\n" + "Lynch players during the day with `[p]ww vote `\n" + "Each night you will absorb an adjacent player" + ) + description = ( + "A mysterious green blob of jelly, slowly growing in size.\n" + "The Blob fears no evil, must be dealt with in town" + ) + + def __init__(self, game): + super().__init__(game) + + self.blob_target = None + + async def see_alignment(self, source=None): + """ + Interaction for investigative roles attempting + to see team (Village, Werewolf, Other) + """ + return ALIGNMENT_NEUTRAL + + async def get_role(self, source=None): + """ + Interaction for powerful access of role + Unlikely to be able to deceive this + """ + return "The Blob" + + async def see_role(self, source=None): + """ + Interaction for investigative roles. + More common to be able to deceive these roles + """ + return "The Blob" + + async def kill(self, source): + """ + Called when someone is trying to kill you! + Can you do anything about it? + self.player.alive is now set to False, set to True to stay alive + """ + + # Blob cannot simply be killed + self.player.alive = True + + @wolflistener("at_night_start", priority=2) + async def _at_night_start(self): + if not self.player.alive: + return + + self.blob_target = None + idx = self.player.id + left_or_right = random.choice((-1, 1)) + while self.blob_target is None: + idx += left_or_right + if idx >= len(self.game.players): + idx = 0 + + player = self.game.players[idx] + + # you went full circle, everyone is a blob or something else is wrong + if player == self.player: + break + + if player.role.properties.get("been_blobbed", False): + self.blob_target = player + + if self.blob_target is not None: + await self.player.send_dm(f"**You will attempt to absorb {self.blob_target} tonight**") + else: + await self.player.send_dm(f"**No player will be absorbed tonight**") + + @wolflistener("at_night_end", priority=4) + async def _at_night_end(self): + if self.blob_target is None or not self.player.alive: + return + + target: "Player" = await self.game.visit(self.blob_target, self.player) + + if target is not None: + target.role.properties["been_blobbed"] = True + self.game.night_messages.append("The Blob grows...")