diff --git a/backend/bang/cards.py b/backend/bang/cards.py index 9e79994..fa446db 100644 --- a/backend/bang/cards.py +++ b/backend/bang/cards.py @@ -247,7 +247,7 @@ class Birra(Card): import bang.expansions.gold_rush.characters as grch madamYto = [p for p in player.game.get_alive_players() if p.character.check(player.game, grch.MadamYto) and self.number != 42] for p in madamYto: - p.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player=p) p.notify_self() if 'gold_rush' in player.game.expansions and self.number != 42: from bang.players import PendingAction @@ -309,7 +309,7 @@ class Diligenza(Card): G.sio.emit('chat_message', room=player.game.name, data=f'_diligenza|{player.name}|{self.name}') for i in range(2): - player.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player) return True @@ -442,7 +442,7 @@ class WellsFargo(Card): G.sio.emit('chat_message', room=player.game.name, data=f'_wellsfargo|{player.name}|{self.name}') for i in range(3): - player.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player) return True diff --git a/backend/bang/deck.py b/backend/bang/deck.py index 36b4bab..cde7d15 100644 --- a/backend/bang/deck.py +++ b/backend/bang/deck.py @@ -4,6 +4,7 @@ import bang.cards as cs import bang.expansions.fistful_of_cards.card_events as ce import bang.expansions.high_noon.card_events as ceh import bang.expansions.gold_rush.shop_cards as grc +from globals import G class Deck: def __init__(self, game): @@ -81,12 +82,15 @@ class Deck: def put_on_top(self, card: cs.Card): self.cards.insert(0, card) - def draw(self, ignore_event = False) -> cs.Card: + def draw(self, ignore_event = False, player=None) -> cs.Card: if self.game.check_event(ce.MinieraAbbandonata) and len(self.scrap_pile) > 0 and not ignore_event: return self.draw_from_scrap_pile() card = self.cards.pop(0) if len(self.cards) == 0: self.reshuffle() + if player is not None: + G.sio.emit('card_drawn', room=self.game.name, data={'player': player.name, 'pile': 'deck'}) + player.hand.append(card) return card def reshuffle(self): diff --git a/backend/bang/expansions/dodge_city/cards.py b/backend/bang/expansions/dodge_city/cards.py index 97f32dc..ff5e8fb 100644 --- a/backend/bang/expansions/dodge_city/cards.py +++ b/backend/bang/expansions/dodge_city/cards.py @@ -43,7 +43,7 @@ class Schivata(Mancato): return False def use_card(self, player): - player.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player=player) player.notify_self() class RagTime(Panico): @@ -234,7 +234,7 @@ class Derringer(Pugnale): def play_card(self, player, against, _with=None): if self.can_be_used_now: - player.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player=player) return super().play_card(player, against=against) else: if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice): @@ -245,7 +245,7 @@ class Derringer(Pugnale): return False def use_card(self, player): - player.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player=player) player.notify_self() class Borraccia(Card): diff --git a/backend/bang/expansions/dodge_city/characters.py b/backend/bang/expansions/dodge_city/characters.py index 6ba2360..75d0a88 100644 --- a/backend/bang/expansions/dodge_city/characters.py +++ b/backend/bang/expansions/dodge_city/characters.py @@ -89,8 +89,8 @@ class ChuckWengam(Character): if super().special(player, data): if player.lives > 1 and player.is_my_turn: player.lives -= 1 - player.hand.append(player.game.deck.draw(True)) - player.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player=player) + player.game.deck.draw(True, player=player) player.notify_self() return True return False diff --git a/backend/bang/expansions/gold_rush/characters.py b/backend/bang/expansions/gold_rush/characters.py index 19ce2b1..72dc700 100644 --- a/backend/bang/expansions/gold_rush/characters.py +++ b/backend/bang/expansions/gold_rush/characters.py @@ -70,7 +70,7 @@ class RaddieSnake(Character): if player.gold_nuggets >= 1 and player.is_my_turn and player.special_use_count < 2: player.gold_nuggets -= 1 player.special_use_count += 1 - player.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player=player) player.notify_self() return True return False diff --git a/backend/bang/expansions/gold_rush/shop_cards.py b/backend/bang/expansions/gold_rush/shop_cards.py index 49ae8ed..5b4ef8f 100644 --- a/backend/bang/expansions/gold_rush/shop_cards.py +++ b/backend/bang/expansions/gold_rush/shop_cards.py @@ -117,7 +117,7 @@ class UnionPacific(ShopCard): G.sio.emit('chat_message', room=player.game.name, data=f'_UnionPacific|{player.name}|{self.name}') for i in range(4): - player.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player=player) return super().play_card(player, against, _with) class Calumet(ShopCard): @@ -191,7 +191,7 @@ class Setaccio(ShopCard): G.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}') player.gold_nuggets -= 1 player.setaccio_count += 1 - player.hand.append(player.game.deck.draw(True)) + player.game.deck.draw(True, player=player) player.notify_self() return True return False diff --git a/backend/bang/game.py b/backend/bang/game.py index 72e44cb..d294c38 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -261,7 +261,7 @@ class Game: G.sio.emit('chat_message', room=self.name, data=f'_choose_character|{self.players[i].name}|{self.players[i].character.name}') self.players[i].prepare() for k in range(self.players[i].max_lives): - self.players[i].hand.append(self.deck.draw()) + self.deck.draw(player=self.players[i]) self.players[i].notify_self() current_roles = [x.role.name for x in self.players] self.rng.shuffle(current_roles) @@ -486,7 +486,7 @@ class Game: if target_pl.character.check(self, grch.SimeonPicos): target_pl.gold_nuggets += 1 if any((isinstance(c, grc.Stivali) for c in target_pl.gold_rush_equipment)): - target_pl.hand.append(self.deck.draw(True)) + self.deck.draw(True, player=target_pl) target_pl.notify_self() self.is_russian_roulette_on = False self.players[self.turn].play_turn() @@ -565,8 +565,8 @@ class Game: pl.is_dead = False pl.is_ghost = False pl.lives = 2 - pl.hand.append(self.deck.draw()) - pl.hand.append(self.deck.draw()) + self.deck.draw(player=pl) + self.deck.draw(player=pl) if (ghost := next((c for c in pl.equipment if isinstance(c, tvosc.Fantasma)), None)) is not None: self.deck.scrap(ghost) pl.equipment.remove(ghost) @@ -707,8 +707,8 @@ class Game: Metrics.send_metric('player_death', points=[1], tags=[f"char:{player.character.name}", f"role:{player.role.name}"]) if any((isinstance(c, grc.Ricercato) for c in player.gold_rush_equipment)) and player.attacker and player.attacker in self.players: player.attacker.gold_nuggets += 1 - player.attacker.hand.append(self.deck.draw(True)) - player.attacker.hand.append(self.deck.draw(True)) + self.deck.draw(True, player=player.attacker) + self.deck.draw(True, player=player.attacker) player.attacker.notify_self() # se lo sceriffo uccide il proprio vice if player.attacker and player.attacker in self.players and isinstance(player.attacker.role, roles.Sheriff) and isinstance(player.role, roles.Vice): @@ -721,7 +721,7 @@ class Game: player.attacker.notify_self() elif player.attacker and player.attacker in self.players and (isinstance(player.role, roles.Outlaw) or self.initial_players == 3): for i in range(3): - player.attacker.hand.append(self.deck.draw(True)) + self.deck.draw(True, player=player.attacker) player.attacker.notify_self() print(f'{self.name}: player {player.name} died') if self.waiting_for > 0 and player.pending_action == pl.PendingAction.RESPOND: @@ -806,8 +806,8 @@ class Game: greg[i].lives = min(greg[i].lives+2, greg[i].max_lives) herb = [p for p in self.get_alive_players() if p.character.check(self, chd.HerbHunter)] for i in range(len(herb)): - herb[i].hand.append(self.deck.draw(True)) - herb[i].hand.append(self.deck.draw(True)) + self.deck.draw(True, player=herb[i]) + self.deck.draw(True, player=herb[i]) herb[i].notify_self() #se Vulture Sam o Herb Hounter è uno sceriffo e ha appena ucciso il suo Vice, deve scartare le carte che ha pescato con la sua abilità diff --git a/backend/bang/players.py b/backend/bang/players.py index 34b48a3..df00960 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -252,7 +252,7 @@ class Player: self.choose_text = 'choose_ranch' self.pending_action = PendingAction.CHOOSE elif self.character and self.character.check(self.game, chars.SuzyLafayette) and self.lives > 0 and len(self.hand) == 0 and ( not self.is_my_turn or self.pending_action == PendingAction.PLAY): - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) if self.lives <= 0 and self.max_lives > 0 and not self.is_dead: print('dying, attacker', self.attacker) if self.gold_nuggets >= 2 and any((isinstance(c, grc.Zaino) for c in self.gold_rush_equipment)): @@ -458,9 +458,9 @@ class Player: if self.character.check(self.game, grch.SimeonPicos): self.gold_nuggets += 1 if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)): - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) if self.character.check(self.game, chars.BartCassidy) and self.lives > 0: - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) G.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') self.heal_if_needed() if self.lives <= 0: @@ -571,10 +571,11 @@ class Player: if self.game.check_event(ce.LeggeDelWest): card.must_be_used = True if self.character.check(self.game, chars.BlackJack) and card.check_suit(self.game, [cs.Suit.HEARTS, cs.Suit.DIAMONDS]): - self.hand.append(self.game.deck.draw()) + self.game.deck.draw(player=self) self.hand.append(card) + G.sio.emit('card_drawn', room=self.game.name, data={'player': self.name, 'pile': pile}) else: - self.hand.append(self.game.deck.draw()) + self.game.deck.draw(player=self) self.manette() self.notify_self() @@ -611,16 +612,16 @@ class Player: if self.character.check(self.game, grch.SimeonPicos): self.gold_nuggets += 3 if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)): - self.hand.append(self.game.deck.draw()) - self.hand.append(self.game.deck.draw()) - self.hand.append(self.game.deck.draw()) + self.game.deck.draw(player=self) + self.game.deck.draw(player=self) + self.game.deck.draw(player=self) self.attacker = None self.game.deck.scrap(self.equipment.pop(i), True) G.sio.emit('chat_message', room=self.game.name, data=f'_explode|{self.name}') self.heal_if_needed() if self.character.check(self.game, chars.BartCassidy) and self.lives > 0: for i in range(3): - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) G.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') print(f'{self.name} Boom, -3 hp') break @@ -664,13 +665,13 @@ class Player: G.sio.emit('chat_message', room=self.game.name, data=f'_snake_bit|{self.name}') if self.character.check(self.game, chars.BartCassidy): G.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) if any((isinstance(c, grc.Talismano) for c in self.gold_rush_equipment)): self.gold_nuggets += 1 if self.character.check(self.game, grch.SimeonPicos): self.gold_nuggets += 1 if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)): - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) break if any((isinstance(c, cs.Prigione) for c in self.equipment)): self.notify_self() #TODO perchè solo le prigioni? e multiple dinamiti come si comportano con veracuster? @@ -870,8 +871,8 @@ class Player: elif 'choose_tornado' in self.choose_text: if card_index <= len(self.available_cards): self.game.deck.scrap(self.hand.pop(card_index)) - self.hand.append(self.game.deck.draw()) - self.hand.append(self.game.deck.draw()) + self.game.deck.draw(player=self) + self.game.deck.draw(player=self) self.pending_action = PendingAction.WAIT self.game.responders_did_respond_resume_turn() self.notify_self() @@ -961,7 +962,7 @@ class Player: self.hand = [c for c in self.hand if c not in self.discarded_cards] for i in range(len(self.discarded_cards)): self.game.deck.scrap(self.discarded_cards[i], True) - self.hand.append(self.game.deck.draw()) + self.game.deck.draw(player=self) self.discarded_cards = [] self.is_playing_ranch = False self.pending_action = PendingAction.PLAY @@ -1007,7 +1008,7 @@ class Player: self.game.deck.scrap(self.available_cards.pop()) #se c'è sia treno che piccone pesco un'altra carta if self.game.check_event(ceh.IlTreno) and any((isinstance(c, grc.Piccone) for c in self.gold_rush_equipment)): - self.hand.append(self.game.deck.draw()) + self.game.deck.draw(player=self) self.is_drawing = False self.pending_action = PendingAction.PLAY self.manette() @@ -1021,9 +1022,9 @@ class Player: self.game.deck.scrap(self.available_cards.pop(0), True) #scarto l'altra #legge del west non si applica perchè la seconda carta viene scartata if self.game.check_event(ceh.IlTreno): - self.hand.append(self.game.deck.draw()) + self.game.deck.draw(player=self) if any((isinstance(c, grc.Piccone) for c in self.gold_rush_equipment)): - self.hand.append(self.game.deck.draw()) + self.game.deck.draw(player=self) self.gold_nuggets += 1 self.is_drawing = False self.pending_action = PendingAction.PLAY @@ -1212,7 +1213,7 @@ class Player: for i in range(len(self.hand)): if isinstance(self.hand[i], cs.Birra): if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn: - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) self.lives += 1 if not self.character.check(self.game, chd.TequilaJoe) else 2 self.lives = min(self.lives, self.max_lives) self.game.deck.scrap(self.hand.pop(i), True) @@ -1227,7 +1228,7 @@ class Player: if self.character.check(self.game, chars.BartCassidy): G.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) elif self.character.check(self.game, chars.ElGringo) and self.attacker and self.attacker in self.game.get_alive_players() and len(self.attacker.hand) > 0: self.hand.append(self.attacker.hand.pop(randrange(0, len(self.attacker.hand)))) self.hand[-1].reset_card() @@ -1236,7 +1237,7 @@ class Player: self.attacker.notify_self() if isinstance(self.attacker, Player) and not self.game.check_event(ce.Lazo): if any((isinstance(c, tvosc.Taglia) for c in self.equipment)): - self.attacker.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self.attacker) G.sio.emit('chat_message', room=self.game.name, data=f'_taglia_reward|{self.name}|{self.attacker.name}') self.attacker.notify_self() @@ -1253,7 +1254,7 @@ class Player: if self.character.check(self.game, grch.SimeonPicos): self.gold_nuggets += 1 if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)): - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) self.heal_if_needed() self.mancato_needed = 0 self.expected_response = [] @@ -1285,7 +1286,7 @@ class Player: if hasattr(self.attacker,'character') and self.attacker.character.check(self.game, chars.SlabTheKiller) and isinstance(card, cs.Mancato): self.molly_discarded_cards += 1 else: - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) card.use_card(self) print(f'{self.game.name}: {self.name} responded with {card.name}') G.sio.emit('chat_message', room=self.game.name, data=f'_respond|{self.name}|{card.name}') @@ -1302,7 +1303,7 @@ class Player: else: if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn: for i in range(self.molly_discarded_cards): - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) self.molly_discarded_cards = 0 self.notify_self() self.game.responders_did_respond_resume_turn(did_lose=False) @@ -1317,12 +1318,12 @@ class Player: else: if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn: for i in range(self.molly_discarded_cards): - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) self.molly_discarded_cards = 0 self.notify_self() elif self.attacker and self.attacker in self.game.get_alive_players() and self.attacker.character.check(self.game, chd.MollyStark) and self.is_my_turn: for i in range(self.attacker.molly_discarded_cards): - self.attacker.hand.append(self.attacker.game.deck.draw(True)) + self.attacker.game.deck.draw(True, player=self.attacker) self.attacker.molly_discarded_cards = 0 self.attacker.notify_self() self.on_failed_response_cb() @@ -1364,8 +1365,8 @@ class Player: self.scrapped_cards = 0 self.lives = min(self.lives+1, self.max_lives) elif self.character.check(self.game, chd.JoseDelgado) and card.is_equipment and self.special_use_count < 2: - self.hand.append(self.game.deck.draw(True)) - self.hand.append(self.game.deck.draw(True)) + self.game.deck.draw(True, player=self) + self.game.deck.draw(True, player=self) self.special_use_count += 1 self.game.deck.scrap(card) self.notify_self() diff --git a/frontend/src/components/AnimatedCard.vue b/frontend/src/components/AnimatedCard.vue new file mode 100644 index 0000000..9838935 --- /dev/null +++ b/frontend/src/components/AnimatedCard.vue @@ -0,0 +1,42 @@ + + + + + + diff --git a/frontend/src/components/Deck.vue b/frontend/src/components/Deck.vue index dae3ea4..1f0b50f 100644 --- a/frontend/src/components/Deck.vue +++ b/frontend/src/components/Deck.vue @@ -17,7 +17,7 @@
-
+
diff --git a/frontend/src/components/Lobby.vue b/frontend/src/components/Lobby.vue index a8499e4..6694a28 100644 --- a/frontend/src/components/Lobby.vue +++ b/frontend/src/components/Lobby.vue @@ -56,7 +56,7 @@ - + {{getActionEmoji(p)}}
+
@@ -111,6 +112,20 @@ import TinyHand from './TinyHand.vue' import FullScreenInput from './FullScreenInput.vue' import Status from './Status.vue' import DeadRoleNotification from './DeadRoleNotification.vue' +import AnimatedCard from './AnimatedCard.vue' + +const cumulativeOffset = function(element) { + var top = 0, left = 0; + do { + top += element.offsetTop || 0; + left += element.offsetLeft || 0; + element = element.offsetParent; + } while(element); + return { + top: top, + left: left-Math.floor(Math.random() * 20)+10 + }; +}; export default { name: 'Lobby', @@ -124,7 +139,8 @@ export default { PrettyCheck, FullScreenInput, Status, - DeadRoleNotification + DeadRoleNotification, + AnimatedCard }, data: () => ({ username: '', @@ -153,6 +169,7 @@ export default { is_replay: false, turn: -1, deadRoleData: null, + cardsToAnimate: [], }), sockets: { room(data) { @@ -204,6 +221,29 @@ export default { } this.username = username }, + card_drawn(data) { + console.log('card_drawn'+data) + let decel = document.getElementById('actual-deck') + if (!decel) + return + let decelOffset = cumulativeOffset(decel) + let phand = document.getElementById(`${data.player}-hand`) + if (!phand) + return + let playerOffset = cumulativeOffset(phand) + playerOffset.top -= 30 + playerOffset.left += 10 + this.cardsToAnimate.push({ + card: { + name: 'PewPew!', + icon: '💥', + back: true, + }, startPosition: decelOffset, endPosition: playerOffset + }) + setTimeout(() => { + this.cardsToAnimate.shift() + }, 900); + }, mount_status() { this.displayAdminStatus = true },