Mercurial > libervia-backend
view libervia/tui/game_tarot.py @ 4178:cf0ea77f9537
plugin XEP-0277, doc: conflict is now checked and avoided if `user_friendly_id_suffix` is `False`, doc updated
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 05 Dec 2023 13:39:03 +0100 |
parents | b620a8e882e1 |
children | 0d7bb4df2343 |
line wrap: on
line source
#!/usr/bin/env python3 # Libervia TUI # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from libervia.backend.core.i18n import _ import urwid from urwid_satext import sat_widgets from libervia.frontends.tools.games import TarotCard from libervia.frontends.quick_frontend.quick_game_tarot import QuickTarotGame from libervia.tui import xmlui from libervia.tui.keys import action_key_map as a_key class CardDisplayer(urwid.Text): """Show a card""" signals = ["click"] def __init__(self, card): self.__selected = False self.card = card urwid.Text.__init__(self, card.get_attr_text()) def selectable(self): return True def keypress(self, size, key): if key == a_key["CARD_SELECT"]: self.select(not self.__selected) self._emit("click") return key def mouse_event(self, size, event, button, x, y, focus): if urwid.is_mouse_event(event) and button == 1: self.select(not self.__selected) self._emit("click") return True return False def select(self, state=True): self.__selected = state attr, txt = self.card.get_attr_text() if self.__selected: attr += "_selected" self.set_text((attr, txt)) self._invalidate() def is_selected(self): return self.__selected def get_card(self): return self.card def render(self, size, focus=False): canvas = urwid.CompositeCanvas(urwid.Text.render(self, size, focus)) if focus: canvas.set_cursor((0, 0)) return canvas class Hand(urwid.WidgetWrap): """Used to display several cards, and manage a hand""" signals = ["click"] def __init__(self, hand=[], selectable=False, on_click=None, user_data=None): """@param hand: list of Card""" self.__selectable = selectable self.columns = urwid.Columns([], dividechars=1) if on_click: urwid.connect_signal(self, "click", on_click, user_data) if hand: self.update(hand) urwid.WidgetWrap.__init__(self, self.columns) def selectable(self): return self.__selectable def keypress(self, size, key): if CardDisplayer in [wid.__class__ for wid in self.columns.widget_list]: return self.columns.keypress(size, key) else: # No card displayed, we still have to manage the clicks if key == a_key["CARD_SELECT"]: self._emit("click", None) return key def get_selected(self): """Return a list of selected cards""" _selected = [] for wid in self.columns.widget_list: if isinstance(wid, CardDisplayer) and wid.is_selected(): _selected.append(wid.get_card()) return _selected def update(self, hand): """Update the hand displayed in this widget @param hand: list of Card""" try: del self.columns.widget_list[:] del self.columns.column_types[:] except IndexError: pass self.columns.contents.append((urwid.Text(""), ("weight", 1, False))) for card in hand: widget = CardDisplayer(card) self.columns.widget_list.append(widget) self.columns.column_types.append(("fixed", 3)) urwid.connect_signal(widget, "click", self.__on_click) self.columns.contents.append((urwid.Text(""), ("weight", 1, False))) self.columns.focus_position = 1 def __on_click(self, card_wid): self._emit("click", card_wid) class Card(TarotCard): """This class is used to represent a card, logically and give a text representation with attributes""" SIZE = 3 # size of a displayed card def __init__(self, suit, value): """@param file: path of the PNG file""" TarotCard.__init__(self, (suit, value)) def get_attr_text(self): """return text representation of the card with attributes""" try: value = "%02i" % int(self.value) except ValueError: value = self.value[0].upper() + self.value[1] if self.suit == "atout": if self.value == "excuse": suit = "c" else: suit = "A" color = "neutral" elif self.suit == "pique": suit = "♠" color = "black" elif self.suit == "trefle": suit = "♣" color = "black" elif self.suit == "coeur": suit = "♥" color = "red" elif self.suit == "carreau": suit = "♦" color = "red" if self.bout: color = "special" return ("card_%s" % color, "%s%s" % (value, suit)) def get_widget(self): """Return a widget representing the card""" return CardDisplayer(self) class Table(urwid.FlowWidget): """Represent the cards currently on the table""" def __init__(self): self.top = self.left = self.bottom = self.right = None def put_card(self, location, card): """Put a card on the table @param location: where to put the card (top, left, bottom or right) @param card: Card to play or None""" assert location in ["top", "left", "bottom", "right"] assert isinstance(card, Card) or card == None if [getattr(self, place) for place in ["top", "left", "bottom", "right"]].count( None ) == 0: # If the table is full of card, we remove them self.top = self.left = self.bottom = self.right = None setattr(self, location, card) self._invalidate() def rows(self, size, focus=False): return self.display_widget(size, focus).rows(size, focus) def render(self, size, focus=False): return self.display_widget(size, focus).render(size, focus) def display_widget(self, size, focus): cards = {} max_col, = size separator = " - " margin = max((max_col - Card.SIZE) / 2, 0) * " " margin_center = max((max_col - Card.SIZE * 2 - len(separator)) / 2, 0) * " " for location in ["top", "left", "bottom", "right"]: card = getattr(self, location) cards[location] = card.get_attr_text() if card else Card.SIZE * " " render_wid = [ urwid.Text([margin, cards["top"]]), urwid.Text([margin_center, cards["left"], separator, cards["right"]]), urwid.Text([margin, cards["bottom"]]), ] return urwid.Pile(render_wid) class TarotGame(QuickTarotGame, urwid.WidgetWrap): """Widget for card games""" def __init__(self, parent, referee, players): QuickTarotGame.__init__(self, parent, referee, players) self.load_cards() self.top = urwid.Pile([urwid.Padding(urwid.Text(self.top_nick), "center")]) # self.parent.host.debug() self.table = Table() self.center = urwid.Columns( [ ("fixed", len(self.left_nick), urwid.Filler(urwid.Text(self.left_nick))), urwid.Filler(self.table), ( "fixed", len(self.right_nick), urwid.Filler(urwid.Text(self.right_nick)), ), ] ) """urwid.Pile([urwid.Padding(self.top_card_wid,'center'), urwid.Columns([('fixed',len(self.left_nick),urwid.Text(self.left_nick)), urwid.Padding(self.center_cards_wid,'center'), ('fixed',len(self.right_nick),urwid.Text(self.right_nick)) ]), urwid.Padding(self.bottom_card_wid,'center') ])""" self.hand_wid = Hand(selectable=True, on_click=self.on_click) self.main_frame = urwid.Frame( self.center, header=self.top, footer=self.hand_wid, focus_part="footer" ) urwid.WidgetWrap.__init__(self, self.main_frame) self.parent.host.bridge.tarot_game_ready( self.player_nick, referee, self.parent.profile ) def load_cards(self): """Load all the cards in memory""" QuickTarotGame.load_cards(self) for value in list(map(str, list(range(1, 22)))) + ["excuse"]: card = Card("atout", value) self.cards[card.suit, card.value] = card self.deck.append(card) for suit in ["pique", "coeur", "carreau", "trefle"]: for value in list(map(str, list(range(1, 11)))) + ["valet", "cavalier", "dame", "roi"]: card = Card(suit, value) self.cards[card.suit, card.value] = card self.deck.append(card) def tarot_game_new_handler(self, hand): """Start a new game, with given hand""" if hand is []: # reset the display after the scores have been showed self.reset_round() for location in ["top", "left", "bottom", "right"]: self.table.put_card(location, None) self.parent.host.redraw() self.parent.host.bridge.tarot_game_ready( self.player_nick, self.referee, self.parent.profile ) return QuickTarotGame.tarot_game_new_handler(self, hand) self.hand_wid.update(self.hand) self.parent.host.redraw() def tarot_game_choose_contrat_handler(self, xml_data): """Called when the player has to select his contrat @param xml_data: SàT xml representation of the form""" form = xmlui.create( self.parent.host, xml_data, title=_("Please choose your contrat"), flags=["NO_CANCEL"], profile=self.parent.profile, ) form.show(valign="top") def tarot_game_show_cards_handler(self, game_stage, cards, data): """Display cards in the middle of the game (to show for e.g. chien ou poignée)""" QuickTarotGame.tarot_game_show_cards_handler(self, game_stage, cards, data) self.center.widget_list[1] = urwid.Filler(Hand(self.to_show)) self.parent.host.redraw() def tarot_game_your_turn_handler(self): QuickTarotGame.tarot_game_your_turn_handler(self) def tarot_game_score_handler(self, xml_data, winners, loosers): """Called when the round is over, display the scores @param xml_data: SàT xml representation of the form""" if not winners and not loosers: title = _("Draw game") else: title = _("You win \o/") if self.player_nick in winners else _("You loose :(") form = xmlui.create( self.parent.host, xml_data, title=title, flags=["NO_CANCEL"], profile=self.parent.profile, ) form.show() def tarot_game_invalid_cards_handler(self, phase, played_cards, invalid_cards): """Invalid cards have been played @param phase: phase of the game @param played_cards: all the cards played @param invalid_cards: cards which are invalid""" QuickTarotGame.tarot_game_invalid_cards_handler( self, phase, played_cards, invalid_cards ) self.hand_wid.update(self.hand) if self._autoplay == None: # No dialog if there is autoplay self.parent.host.bar_notify(_("Cards played are invalid !")) self.parent.host.redraw() def tarot_game_cards_played_handler(self, player, cards): """A card has been played by player""" QuickTarotGame.tarot_game_cards_played_handler(self, player, cards) self.table.put_card(self.get_player_location(player), self.played[player]) self._checkState() self.parent.host.redraw() def _checkState(self): if isinstance( self.center.widget_list[1].original_widget, Hand ): # if we have a hand displayed self.center.widget_list[1] = urwid.Filler( self.table ) # we show again the table if self.state == "chien": self.to_show = [] self.state = "wait" elif self.state == "wait_for_ecart": self.state = "ecart" self.hand.extend(self.to_show) self.hand.sort() self.to_show = [] self.hand_wid.update(self.hand) ##EVENTS## def on_click(self, hand, card_wid): """Called when user do an action on the hand""" if not self.state in ["play", "ecart", "wait_for_ecart"]: # it's not our turn, we ignore the click card_wid.select(False) return self._checkState() if self.state == "ecart": if len(self.hand_wid.get_selected()) == 6: pop_up_widget = sat_widgets.ConfirmDialog( _("Do you put these cards in chien ?"), yes_cb=self.on_ecart_done, no_cb=self.parent.host.remove_pop_up, ) self.parent.host.show_pop_up(pop_up_widget) elif self.state == "play": card = card_wid.get_card() self.parent.host.bridge.tarot_game_play_cards( self.player_nick, self.referee, [(card.suit, card.value)], self.parent.profile, ) self.hand.remove(card) self.hand_wid.update(self.hand) self.state = "wait" def on_ecart_done(self, button): """Called when player has finished his écart""" ecart = [] for card in self.hand_wid.get_selected(): ecart.append((card.suit, card.value)) self.hand.remove(card) self.hand_wid.update(self.hand) self.parent.host.bridge.tarot_game_play_cards( self.player_nick, self.referee, ecart, self.parent.profile ) self.state = "wait" self.parent.host.remove_pop_up()