frontends (quick_frontend, primitivus): fixes room games:

- add quick_frontend.quick_games for registering the signals and registering the UI classes
    - rename the signals handlers to fit the convention (e.g.: tarotGameScoreHandler)
    - rename card_game to game_tarot, quick_card_game to quick_game_tarot, CardGame to TarotGame
This commit is contained in:
souliane 2015-03-11 12:43:48 +01:00
commit e597a804d9
7 changed files with 183 additions and 198 deletions

View file

@ -25,7 +25,8 @@ from urwid_satext import sat_widgets
from urwid_satext.files_management import FileDialog
from sat_frontends.quick_frontend import quick_widgets
from sat_frontends.quick_frontend.quick_chat import QuickChat
from sat_frontends.primitivus.card_game import CardGame
from sat_frontends.quick_frontend import quick_games
from sat_frontends.primitivus import game_tarot
from sat_frontends.primitivus.constants import Const as C
from sat_frontends.primitivus.keys import action_key_map as a_key
from sat_frontends.primitivus.widget import PrimitivusWidget
@ -197,13 +198,21 @@ class Chat(PrimitivusWidget, QuickChat):
self.chat_colums.contents.remove((widget, options))
break
def _appendGamePanel(self, widget):
def addGamePanel(self, widget):
"""Insert a game panel to this Chat dialog.
@param widget (Widget): the game panel
"""
assert (len(self.pile.contents) == 1)
self.pile.contents.insert(0,(widget,('weight', 1)))
self.pile.contents.insert(1,(urwid.Filler(urwid.Divider('-'),('fixed', 1))))
self.pile.contents.insert(0, (widget, ('weight', 1)))
self.pile.contents.insert(1, (urwid.Filler(urwid.Divider('-'), ('fixed', 1))))
self.host.redraw()
def _removeGamePanel(self):
def removeGamePanel(self, widget):
"""Remove the game panel from this Chat dialog.
@param widget (Widget): the game panel
"""
assert (len(self.pile.contents) == 3)
del self.pile.contents[0]
self.host.redraw()
@ -333,18 +342,6 @@ class Chat(PrimitivusWidget, QuickChat):
elif self.getUserNick().lower() in msg.lower():
self.host.x_notify.sendNotification(_("Primitivus: %(user)s mentioned you in room '%(room)s'") % {'user': from_jid, 'room': self.target})
def startGame(self, game_type, referee, players):
"""Configure the chat window to start a game"""
if game_type=="Tarot":
self.tarot_wid = CardGame(self, referee, players, self.nick)
self._appendGamePanel(self.tarot_wid)
def getGame(self, game_type):
"""Return class managing the game type"""
#TODO: check that the game is launched, and manage errors
if game_type=="Tarot":
return self.tarot_wid
#MENU EVENTS#
def onTarotRequest(self, menu):
# TODO: move this to plugin_misc_tarot with dynamic menu
@ -379,3 +376,4 @@ class Chat(PrimitivusWidget, QuickChat):
quick_widgets.register(QuickChat, Chat)
quick_widgets.register(quick_games.Tarot, game_tarot.TarotGame)

View file

@ -0,0 +1,348 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Primitivus: a SAT frontend
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 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 sat.core.i18n import _
import urwid
from urwid_satext import sat_widgets
from sat_frontends.tools.games import TarotCard
from sat_frontends.quick_frontend.quick_game_tarot import QuickTarotGame
from sat_frontends.primitivus import xmlui
from sat_frontends.primitivus.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.getAttrText())
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.getAttrText()
if self.__selected:
attr += '_selected'
self.set_text((attr, txt))
self._invalidate()
def isSelected(self):
return self.__selected
def getCard(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 getSelected(self):
"""Return a list of selected cards"""
_selected = []
for wid in self.columns.widget_list:
if isinstance(wid, CardDisplayer) and wid.isSelected():
_selected.append(wid.getCard())
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.__onClick)
self.columns.contents.append((urwid.Text(''), ('weight', 1, False)))
self.columns.focus_position = 1
def __onClick(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 getAttrText(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 = u'♠'
color = 'black'
elif self.suit == "trefle":
suit = u'♣'
color = 'black'
elif self.suit == "coeur":
suit = u'♥'
color = 'red'
elif self.suit == "carreau":
suit = u'♦'
color = 'red'
if self.bout:
color = 'special'
return ('card_%s' % color, u"%s%s" % (value, suit))
def getWidget(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 putCard(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.getAttrText() 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.loadCards()
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.onClick)
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.tarotGameReady(self.player_nick, referee, self.parent.profile)
def loadCards(self):
"""Load all the cards in memory"""
QuickTarotGame.loadCards(self)
for value in map(str, 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 map(str, range(1, 11)) + ["valet", "cavalier", "dame", "roi"]:
card = Card(suit, value)
self.cards[card.suit, card.value] = card
self.deck.append(card)
def tarotGameNewHandler(self, hand):
"""Start a new game, with given hand"""
if hand is []: # reset the display after the scores have been showed
self.resetRound()
for location in ['top', 'left', 'bottom', 'right']:
self.table.putCard(location, None)
self.parent.host.redraw()
self.parent.host.bridge.tarotGameReady(self.player_nick, self.referee, self.parent.profile)
return
QuickTarotGame.tarotGameNewHandler(self, hand)
self.hand_wid.update(self.hand)
self.parent.host.redraw()
def tarotGameChooseContratHandler(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 tarotGameShowCardsHandler(self, game_stage, cards, data):
"""Display cards in the middle of the game (to show for e.g. chien ou poignée)"""
QuickTarotGame.tarotGameShowCardsHandler(self, game_stage, cards, data)
self.center.widget_list[1] = urwid.Filler(Hand(self.to_show))
self.parent.host.redraw()
def tarotGameYourTurnHandler(self):
QuickTarotGame.tarotGameYourTurnHandler(self)
def tarotGameScoreHandler(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 tarotGameInvalidCardsHandler(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.tarotGameInvalidCardsHandler(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.notify(_('Cards played are invalid !'))
self.parent.host.redraw()
def tarotGameCardsPlayedHandler(self, player, cards):
"""A card has been played by player"""
QuickTarotGame.tarotGameCardsPlayedHandler(self, player, cards)
self.table.putCard(self.getPlayerLocation(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 onClick(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.getSelected()) == 6:
pop_up_widget = sat_widgets.ConfirmDialog(_("Do you put these cards in chien ?"), yes_cb=self.onEcartDone, no_cb=self.parent.host.removePopUp)
self.parent.host.showPopUp(pop_up_widget)
elif self.state == "play":
card = card_wid.getCard()
self.parent.host.bridge.tarotGamePlayCards(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 onEcartDone(self, button):
"""Called when player has finished his écart"""
ecart = []
for card in self.hand_wid.getSelected():
ecart.append((card.suit, card.value))
self.hand.remove(card)
self.hand_wid.update(self.hand)
self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, ecart, self.parent.profile)
self.state = "wait"
self.parent.host.removePopUp()

View file

@ -45,7 +45,6 @@ from os.path import join
import signal
class EditBar(sat_widgets.ModalEdit):
"""
The modal edit bar where you would enter messages and commands.
@ -676,7 +675,7 @@ class PrimitivusApp(QuickApp, InputHistory):
title = _('Registration')
misc['target'] = data['target']
misc['action_back'] = self.bridge.gatewayRegister
ui = xmlui.create(self, title=title, xml_data = data['xml'], misc = misc)
ui = xmlui.create(self, title=title, xml_data=data['xml'], misc=misc, profile=profile)
if data['type'] == 'registration':
ui.show('popup')
else:

View file

@ -26,7 +26,7 @@ from sat.tools.misc import TriggerManager
from sat_frontends.tools import jid
from sat_frontends.quick_frontend.quick_widgets import QuickWidgetsManager
from sat_frontends.quick_frontend import quick_chat
from sat_frontends.quick_frontend import quick_chat, quick_games
from sat_frontends.quick_frontend.constants import Const as C
import sys
@ -252,25 +252,14 @@ class QuickApp(object):
self.registerSignal("roomUserLeft", iface="plugin")
self.registerSignal("roomUserChangedNick", iface="plugin")
self.registerSignal("roomNewSubject", iface="plugin")
self.registerSignal("tarotGameStarted", iface="plugin")
self.registerSignal("tarotGameNew", iface="plugin")
self.registerSignal("tarotGameChooseContrat", iface="plugin")
self.registerSignal("tarotGameShowCards", iface="plugin")
self.registerSignal("tarotGameYourTurn", iface="plugin")
self.registerSignal("tarotGameScore", iface="plugin")
self.registerSignal("tarotGameCardsPlayed", iface="plugin")
self.registerSignal("tarotGameInvalidCards", iface="plugin")
self.registerSignal("quizGameStarted", iface="plugin")
self.registerSignal("quizGameNew", iface="plugin")
self.registerSignal("quizGameQuestion", iface="plugin")
self.registerSignal("quizGamePlayerBuzzed", iface="plugin")
self.registerSignal("quizGamePlayerSays", iface="plugin")
self.registerSignal("quizGameAnswerResult", iface="plugin")
self.registerSignal("quizGameTimerExpired", iface="plugin")
self.registerSignal("quizGameTimerRestarted", iface="plugin")
self.registerSignal("chatStateReceived", iface="plugin")
self.registerSignal("personalEvent", iface="plugin")
# FIXME: do it dynamically
quick_games.Tarot.registerSignals(self)
quick_games.Quiz.registerSignals(self)
quick_games.Radiocol.registerSignals(self)
self.current_action_ids = set() # FIXME: to be removed
self.current_action_ids_cb = {} # FIXME: to be removed
self.media_dir = self.bridge.getConfig('', 'media_dir')
@ -576,107 +565,6 @@ class QuickApp(object):
chat_widget.setSubject(subject)
log.debug("new subject for room [%(room_jid)s]: %(subject)s" % {'room_jid': room_jid, "subject": subject})
def tarotGameStartedHandler(self, room_jid_s, referee, players, profile):
log.debug(_("Tarot Game Started \o/"))
room_jid = jid.JID(room_jid_s)
chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile)
chat_widget.startGame("Tarot", referee, players)
log.debug("new Tarot game started by [%(referee)s] in room [%(room_jid)s] with %(players)s" % {'referee': referee, 'room_jid': room_jid, 'players': [str(player) for player in players]})
def tarotGameNewHandler(self, room_jid_s, hand, profile):
log.debug(_("New Tarot Game"))
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Tarot").newGame(hand)
def tarotGameChooseContratHandler(self, room_jid_s, xml_data, profile):
"""Called when the player has to select his contrat"""
log.debug(_("Tarot: need to select a contrat"))
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Tarot").chooseContrat(xml_data)
def tarotGameShowCardsHandler(self, room_jid_s, game_stage, cards, data, profile):
log.debug(_("Show cards"))
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Tarot").showCards(game_stage, cards, data)
def tarotGameYourTurnHandler(self, room_jid_s, profile):
log.debug(_("My turn to play"))
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Tarot").myTurn()
def tarotGameScoreHandler(self, room_jid_s, xml_data, winners, loosers, profile):
"""Called when the game is finished and the score are updated"""
log.debug(_("Tarot: score received"))
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Tarot").showScores(xml_data, winners, loosers)
def tarotGameCardsPlayedHandler(self, room_jid_s, player, cards, profile):
log.debug(_("Card(s) played (%(player)s): %(cards)s") % {"player": player, "cards": cards})
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Tarot").cardsPlayed(player, cards)
def tarotGameInvalidCardsHandler(self, room_jid_s, phase, played_cards, invalid_cards, profile):
log.debug(_("Cards played are not valid: %s") % invalid_cards)
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Tarot").invalidCards(phase, played_cards, invalid_cards)
def quizGameStartedHandler(self, room_jid_s, referee, players, profile):
log.debug(_("Quiz Game Started \o/"))
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.startGame("Quiz", referee, players)
log.debug(_("new Quiz game started by [%(referee)s] in room [%(room_jid)s] with %(players)s") % {'referee': referee, 'room_jid': room_jid_s, 'players': [str(player) for player in players]})
def quizGameNewHandler(self, room_jid_s, data, profile):
log.debug(_("New Quiz Game"))
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Quiz").quizGameNewHandler(data)
def quizGameQuestionHandler(self, room_jid_s, question_id, question, timer, profile):
"""Called when a new question is asked"""
log.debug(_(u"Quiz: new question: %s") % question)
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Quiz").quizGameQuestionHandler(question_id, question, timer)
def quizGamePlayerBuzzedHandler(self, room_jid_s, player, pause, profile):
"""Called when a player pushed the buzzer"""
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Quiz").quizGamePlayerBuzzedHandler(player, pause)
def quizGamePlayerSaysHandler(self, room_jid_s, player, text, delay, profile):
"""Called when a player say something"""
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Quiz").quizGamePlayerSaysHandler(player, text, delay)
def quizGameAnswerResultHandler(self, room_jid_s, player, good_answer, score, profile):
"""Called when a player say something"""
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Quiz").quizGameAnswerResultHandler(player, good_answer, score)
def quizGameTimerExpiredHandler(self, room_jid_s, profile):
"""Called when nobody answered the question in time"""
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Quiz").quizGameTimerExpiredHandler()
def quizGameTimerRestartedHandler(self, room_jid_s, time_left, profile):
"""Called when the question is not answered, and we still have time"""
chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
if chat_widget:
chat_widget.getGame("Quiz").quizGameTimerRestartedHandler(time_left)
def chatStateReceivedHandler(self, from_jid_s, state, profile):
"""Called when a new chat state is received.

View file

@ -48,6 +48,7 @@ class QuickChat(quick_widgets.QuickWidget):
self.id = "" # FIXME: to be removed
self.nick = None
self.occupants = set()
self.games = {}
def __str__(self):
return u"Chat Widget [target: {}, type: {}, profile: {}]".format(self.target, self.type, self.profile)
@ -227,16 +228,6 @@ class QuickChat(quick_widgets.QuickWidget):
"""
raise NotImplementedError
def startGame(self, game_type, referee, players):
"""Configure the chat window to start a game"""
#No need to raise an error as game are not mandatory
log.warning(_('startGame is not implemented in this frontend'))
def getGame(self, game_type):
"""Return class managing the game type"""
#No need to raise an error as game are not mandatory
log.warning(_('getGame is not implemented in this frontend'))
def updateChatState(self, from_jid, state):
"""Set the chat state (XEP-0085) of the contact.
@ -244,4 +235,19 @@ class QuickChat(quick_widgets.QuickWidget):
"""
raise NotImplementedError
def addGamePanel(self, widget):
"""Insert a game panel to this Chat dialog.
@param widget (Widget): the game panel
"""
raise NotImplementedError
def removeGamePanel(self, widget):
"""Remove the game panel from this Chat dialog.
@param widget (Widget): the game panel
"""
raise NotImplementedError
quick_widgets.register(QuickChat)

View file

@ -0,0 +1,158 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# helper class for making a SAT frontend
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 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 sat.core.log import getLogger
log = getLogger(__name__)
from sat_frontends.tools.jid import JID
class QuickTarotGame(object):
def __init__(self, parent, referee, players):
self._autoplay = None #XXX: use 0 to activate fake play, None else
self.parent = parent
self.referee = referee
self.players = players
self.played = {}
for player in players:
self.played[player] = None
self.player_nick = parent.nick
self.bottom_nick = unicode(self.player_nick)
idx = self.players.index(self.player_nick)
idx = (idx + 1) % len(self.players)
self.right_nick = unicode(self.players[idx])
idx = (idx + 1) % len(self.players)
self.top_nick = unicode(self.players[idx])
idx = (idx + 1) % len(self.players)
self.left_nick = unicode(self.players[idx])
self.bottom_nick = unicode(self.player_nick)
self.selected = [] #Card choosed by the player (e.g. during ecart)
self.hand_size = 13 #number of cards in a hand
self.hand = []
self.to_show = []
self.state = None
def resetRound(self):
"""Reset the game's variables to be reatty to start the next round"""
del self.selected[:]
del self.hand[:]
del self.to_show[:]
self.state = None
for pl in self.played:
self.played[pl] = None
def getPlayerLocation(self, nick):
"""return player location (top,bottom,left or right)"""
for location in ['top','left','bottom','right']:
if getattr(self,'%s_nick' % location) == nick:
return location
assert(False)
def loadCards(self):
"""Load all the cards in memory
@param dir: directory where the PNG files are"""
self.cards={}
self.deck=[]
self.cards["atout"]={} #As Tarot is a french game, it's more handy & logical to keep french names
self.cards["pique"]={} #spade
self.cards["coeur"]={} #heart
self.cards["carreau"]={} #diamond
self.cards["trefle"]={} #club
def tarotGameNewHandler(self, hand):
"""Start a new game, with given hand"""
assert (len(self.hand) == 0)
for suit, value in hand:
self.hand.append(self.cards[suit, value])
self.hand.sort()
self.state = "init"
def tarotGameChooseContratHandler(self, xml_data):
"""Called when the player as to select his contrat
@param xml_data: SàT xml representation of the form"""
raise NotImplementedError
def tarotGameShowCardsHandler(self, game_stage, cards, data):
"""Display cards in the middle of the game (to show for e.g. chien ou poignée)"""
self.to_show = []
for suit, value in cards:
self.to_show.append(self.cards[suit, value])
if game_stage == "chien" and data['attaquant'] == self.player_nick:
self.state = "wait_for_ecart"
else:
self.state = "chien"
def tarotGameYourTurnHandler(self):
"""Called when we have to play :)"""
if self.state == "chien":
self.to_show = []
self.state = "play"
self.__fakePlay()
def __fakePlay(self):
"""Convenience method for stupid autoplay
/!\ don't forgot to comment any interactive dialog for invalid card"""
if self._autoplay == None:
return
if self._autoplay >= len(self.hand):
self._autoplay = 0
card = self.hand[self._autoplay]
self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, [(card.suit, card.value)], self.parent.profile)
del self.hand[self._autoplay]
self.state = "wait"
self._autoplay+=1
def tarotGameScoreHandler(self, xml_data, winners, loosers):
"""Called at the end of a game
@param xml_data: SàT xml representation of the scores
@param winners: list of winners' nicks
@param loosers: list of loosers' nicks"""
raise NotImplementedError
def tarotGameCardsPlayedHandler(self, player, cards):
"""A card has been played by player"""
if self.to_show:
self.to_show = []
pl_cards = []
if self.played[player] != None: #FIXME
for pl in self.played:
self.played[pl] = None
for suit, value in cards:
pl_cards.append(self.cards[suit, value])
self.played[player] = pl_cards[0]
def tarotGameInvalidCardsHandler(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"""
if phase == "play":
self.state = "play"
elif phase == "ecart":
self.state = "ecart"
else:
log.error ('INTERNAL ERROR: unmanaged game phase')
for suit, value in played_cards:
self.hand.append(self.cards[suit, value])
self.hand.sort()
self.__fakePlay()

View file

@ -0,0 +1,106 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# helper class for making a SAT frontend
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 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 sat.core.log import getLogger
log = getLogger(__name__)
from sat.core.i18n import _
from sat_frontends.tools import jid
from sat_frontends.quick_frontend.constants import Const as C
import quick_chat
class RoomGame(object):
_game_name = None
_signal_prefix = None
_signal_suffixes = None
@classmethod
def registerSignals(cls, host):
def make_handler(suffix, signal):
def handler(*args):
if suffix in ("Started", "Players"):
return cls.startedHandler(host, suffix, *args)
return cls.genericHandler(host, signal, *args)
return handler
for suffix in cls._signal_suffixes:
signal = cls._signal_prefix + suffix
host.registerSignal(signal, handler=make_handler(suffix, signal), iface="plugin")
@classmethod
def startedHandler(cls, host, suffix, *args):
room_jid, args, profile = jid.JID(args[0]), args[1:-1], args[-1]
referee, players, args = args[0], args[1], args[2:]
chat_widget = host.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile)
# self.occupants_panel.updateSpecials(players, SYMBOLS[self._game_name.lower()]) # FIXME
if suffix == "Players" or chat_widget.nick not in players:
return # waiting for other players to join, or not playing
if cls._game_name in chat_widget.games:
return # game panel is already there
real_class = host.widgets.getRealClass(cls)
if real_class == cls:
host.showDialog(_(u"A {game} activity between {players} has been started, but you couldn't take part because your client doesn't support it.").format(game=cls._game_name, players=', '.join(players)),
_(u"{game} Game").format(game=cls._game_name))
return
panel = real_class(chat_widget, referee, players, *args)
chat_widget.games[cls._game_name] = panel
chat_widget.addGamePanel(panel)
@classmethod
def genericHandler(cls, host, signal, *args):
room_jid, args, profile = jid.JID(args[0]), args[1:-1], args[-1]
chat_widget = host.widgets.getWidget(quick_chat.QuickChat, room_jid, profile)
if chat_widget:
try:
game_panel = chat_widget.games[cls._game_name]
except KeyError:
log.error("TODO: better game synchronisation - received signal %s but no panel is found" % signal)
return
else:
getattr(game_panel, "%sHandler" % signal)(*args)
class Tarot(RoomGame):
_game_name = "Tarot"
_signal_prefix = "tarotGame"
_signal_suffixes = ("Started", "Players", "New", "ChooseContrat",
"ShowCards", "YourTurn", "Score", "CardsPlayed",
"InvalidCards",
)
class Quiz(RoomGame):
_game_name = "Quiz"
_signal_prefix = "quizGame"
_signal_suffixes = ("Started", "New", "Question", "PlayerBuzzed",
"PlayerSays", "AnswerResult", "TimerExpired",
"TimerRestarted",
)
class Radiocol(RoomGame):
_game_name = "Radiocol"
_signal_prefix = "radiocol"
_signal_suffixes = ("Started", "Players", "SongRejected", "Preload",
"Play", "NoUpload", "UploadOk")