comparison src/browser/sat_browser/game_tarot.py @ 672:b39a9eddfe56 frontends_multi_profiles

browser_side: fixes room games: - use quick_frontend.quick_games - rename the signals handlers to fit the convention (e.g.: tarotGameScoreHandler) - rename card_game to game_tarot, radiocol to game_radiocol, CardGame to TarotPanel
author souliane <souliane@mailoo.org>
date Wed, 11 Mar 2015 12:50:19 +0100
parents src/browser/sat_browser/card_game.py@79fbc20c786b
children 849ffb24d5bf
comparison
equal deleted inserted replaced
671:2201ff543a05 672:b39a9eddfe56
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # Libervia: a Salut à Toi frontend
5 # Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org>
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 import pyjd # this is dummy in pyjs
21 from sat.core.log import getLogger
22 log = getLogger(__name__)
23
24 from sat.core.i18n import _
25 from sat_frontends.tools.games import TarotCard
26
27 from pyjamas.ui.AbsolutePanel import AbsolutePanel
28 from pyjamas.ui.DockPanel import DockPanel
29 from pyjamas.ui.SimplePanel import SimplePanel
30 from pyjamas.ui.Image import Image
31 from pyjamas.ui.Label import Label
32 from pyjamas.ui.ClickListener import ClickHandler
33 from pyjamas.ui.MouseListener import MouseHandler
34 from pyjamas.ui import HasAlignment
35 from pyjamas import Window
36 from pyjamas import DOM
37
38 import dialog
39 import xmlui
40
41
42 CARD_WIDTH = 74
43 CARD_HEIGHT = 136
44 CARD_DELTA_Y = 30
45 MIN_WIDTH = 950 # Minimum size of the panel
46 MIN_HEIGHT = 500
47
48
49 class CardWidget(TarotCard, Image, MouseHandler):
50 """This class is used to represent a card, graphically and logically"""
51
52 def __init__(self, parent, file_):
53 """@param file: path of the PNG file"""
54 self._parent = parent
55 Image.__init__(self, file_)
56 root_name = file_[file_.rfind("/") + 1:-4]
57 suit, value = root_name.split('_')
58 TarotCard.__init__(self, (suit, value))
59 MouseHandler.__init__(self)
60 self.addMouseListener(self)
61
62 def onMouseEnter(self, sender):
63 if self._parent.state == "ecart" or self._parent.state == "play":
64 DOM.setStyleAttribute(self.getElement(), "top", "0px")
65
66 def onMouseLeave(self, sender):
67 if not self in self._parent.hand:
68 return
69 if not self in list(self._parent.selected): # FIXME: Workaround pyjs bug, must report it
70 DOM.setStyleAttribute(self.getElement(), "top", "%dpx" % CARD_DELTA_Y)
71
72 def onMouseUp(self, sender, x, y):
73 if self._parent.state == "ecart":
74 if self not in list(self._parent.selected):
75 self._parent.addToSelection(self)
76 else:
77 self._parent.removeFromSelection(self)
78 elif self._parent.state == "play":
79 self._parent.playCard(self)
80
81
82 class TarotPanel(DockPanel, ClickHandler):
83
84 def __init__(self, parent, referee, players):
85 DockPanel.__init__(self)
86 ClickHandler.__init__(self)
87 self._parent = parent
88 self._autoplay = None # XXX: use 0 to activate fake play, None else
89 self.referee = referee
90 self.players = players
91 self.player_nick = parent.nick
92 self.bottom_nick = self.player_nick
93 idx = self.players.index(self.player_nick)
94 idx = (idx + 1) % len(self.players)
95 self.right_nick = self.players[idx]
96 idx = (idx + 1) % len(self.players)
97 self.top_nick = self.players[idx]
98 idx = (idx + 1) % len(self.players)
99 self.left_nick = self.players[idx]
100 self.bottom_nick = self.player_nick
101 self.selected = set() # Card choosed by the player (e.g. during ecart)
102 self.hand_size = 13 # number of cards in a hand
103 self.hand = []
104 self.to_show = []
105 self.state = None
106 self.setSize("%dpx" % MIN_WIDTH, "%dpx" % MIN_HEIGHT)
107 self.setStyleName("cardPanel")
108
109 # Now we set up the layout
110 _label = Label(self.top_nick)
111 _label.setStyleName('cardGamePlayerNick')
112 self.add(_label, DockPanel.NORTH)
113 self.setCellWidth(_label, '100%')
114 self.setCellHorizontalAlignment(_label, HasAlignment.ALIGN_CENTER)
115
116 self.hand_panel = AbsolutePanel()
117 self.add(self.hand_panel, DockPanel.SOUTH)
118 self.setCellWidth(self.hand_panel, '100%')
119 self.setCellHorizontalAlignment(self.hand_panel, HasAlignment.ALIGN_CENTER)
120
121 _label = Label(self.left_nick)
122 _label.setStyleName('cardGamePlayerNick')
123 self.add(_label, DockPanel.WEST)
124 self.setCellHeight(_label, '100%')
125 self.setCellVerticalAlignment(_label, HasAlignment.ALIGN_MIDDLE)
126
127 _label = Label(self.right_nick)
128 _label.setStyleName('cardGamePlayerNick')
129 self.add(_label, DockPanel.EAST)
130 self.setCellHeight(_label, '100%')
131 self.setCellHorizontalAlignment(_label, HasAlignment.ALIGN_RIGHT)
132 self.setCellVerticalAlignment(_label, HasAlignment.ALIGN_MIDDLE)
133
134 self.center_panel = DockPanel()
135 self.inner_left = SimplePanel()
136 self.inner_left.setSize("%dpx" % CARD_WIDTH, "%dpx" % CARD_HEIGHT)
137 self.center_panel.add(self.inner_left, DockPanel.WEST)
138 self.center_panel.setCellHeight(self.inner_left, '100%')
139 self.center_panel.setCellHorizontalAlignment(self.inner_left, HasAlignment.ALIGN_RIGHT)
140 self.center_panel.setCellVerticalAlignment(self.inner_left, HasAlignment.ALIGN_MIDDLE)
141
142 self.inner_right = SimplePanel()
143 self.inner_right.setSize("%dpx" % CARD_WIDTH, "%dpx" % CARD_HEIGHT)
144 self.center_panel.add(self.inner_right, DockPanel.EAST)
145 self.center_panel.setCellHeight(self.inner_right, '100%')
146 self.center_panel.setCellVerticalAlignment(self.inner_right, HasAlignment.ALIGN_MIDDLE)
147
148 self.inner_top = SimplePanel()
149 self.inner_top.setSize("%dpx" % CARD_WIDTH, "%dpx" % CARD_HEIGHT)
150 self.center_panel.add(self.inner_top, DockPanel.NORTH)
151 self.center_panel.setCellHorizontalAlignment(self.inner_top, HasAlignment.ALIGN_CENTER)
152 self.center_panel.setCellVerticalAlignment(self.inner_top, HasAlignment.ALIGN_BOTTOM)
153
154 self.inner_bottom = SimplePanel()
155 self.inner_bottom.setSize("%dpx" % CARD_WIDTH, "%dpx" % CARD_HEIGHT)
156 self.center_panel.add(self.inner_bottom, DockPanel.SOUTH)
157 self.center_panel.setCellHorizontalAlignment(self.inner_bottom, HasAlignment.ALIGN_CENTER)
158 self.center_panel.setCellVerticalAlignment(self.inner_bottom, HasAlignment.ALIGN_TOP)
159
160 self.inner_center = SimplePanel()
161 self.center_panel.add(self.inner_center, DockPanel.CENTER)
162 self.center_panel.setCellHorizontalAlignment(self.inner_center, HasAlignment.ALIGN_CENTER)
163 self.center_panel.setCellVerticalAlignment(self.inner_center, HasAlignment.ALIGN_MIDDLE)
164
165 self.add(self.center_panel, DockPanel.CENTER)
166 self.setCellWidth(self.center_panel, '100%')
167 self.setCellHeight(self.center_panel, '100%')
168 self.setCellVerticalAlignment(self.center_panel, HasAlignment.ALIGN_MIDDLE)
169 self.setCellHorizontalAlignment(self.center_panel, HasAlignment.ALIGN_CENTER)
170
171 self.loadCards()
172 self.mouse_over_card = None # contain the card to highlight
173 self.visible_size = CARD_WIDTH / 2 # number of pixels visible for cards
174 self.addClickListener(self)
175
176 def loadCards(self):
177 """Load all the cards in memory"""
178 def _getTarotCardsPathsCb(paths):
179 log.debug("_getTarotCardsPathsCb")
180 for file_ in paths:
181 log.debug("path: %s" % file_)
182 card = CardWidget(self, file_)
183 log.debug("card: %s" % card)
184 self.cards[(card.suit, card.value)] = card
185 self.deck.append(card)
186 self._parent.host.bridge.call('tarotGameReady', None, self.player_nick, self.referee)
187 self.cards = {}
188 self.deck = []
189 self.cards["atout"] = {} # As Tarot is a french game, it's more handy & logical to keep french names
190 self.cards["pique"] = {} # spade
191 self.cards["coeur"] = {} # heart
192 self.cards["carreau"] = {} # diamond
193 self.cards["trefle"] = {} # club
194 self._parent.host.bridge.call('getTarotCardsPaths', _getTarotCardsPathsCb)
195
196 def onClick(self, sender):
197 if self.state == "chien":
198 self.to_show = []
199 self.state = "wait"
200 self.updateToShow()
201 elif self.state == "wait_for_ecart":
202 self.state = "ecart"
203 self.hand.extend(self.to_show)
204 self.hand.sort()
205 self.to_show = []
206 self.updateToShow()
207 self.updateHand()
208
209 def tarotGameNewHandler(self, hand):
210 """Start a new game, with given hand"""
211 if hand is []: # reset the display after the scores have been showed
212 self.selected.clear()
213 del self.hand[:]
214 del self.to_show[:]
215 self.state = None
216 #empty hand
217 self.updateHand()
218 #nothing on the table
219 self.updateToShow()
220 for pos in ['top', 'left', 'bottom', 'right']:
221 getattr(self, "inner_%s" % pos).setWidget(None)
222 self._parent.host.bridge.call('tarotGameReady', None, self.player_nick, self.referee)
223 return
224 for suit, value in hand:
225 self.hand.append(self.cards[(suit, value)])
226 self.hand.sort()
227 self.state = "init"
228 self.updateHand()
229
230 def updateHand(self):
231 """Show the cards in the hand in the hand_panel (SOUTH panel)"""
232 self.hand_panel.clear()
233 self.hand_panel.setSize("%dpx" % (self.visible_size * (len(self.hand) + 1)), "%dpx" % (CARD_HEIGHT + CARD_DELTA_Y + 10))
234 x_pos = 0
235 y_pos = CARD_DELTA_Y
236 for card in self.hand:
237 self.hand_panel.add(card, x_pos, y_pos)
238 x_pos += self.visible_size
239
240 def updateToShow(self):
241 """Show cards in the center panel"""
242 if not self.to_show:
243 _widget = self.inner_center.getWidget()
244 if _widget:
245 self.inner_center.remove(_widget)
246 return
247 panel = AbsolutePanel()
248 panel.setSize("%dpx" % ((CARD_WIDTH + 5) * len(self.to_show) - 5), "%dpx" % (CARD_HEIGHT))
249 x_pos = 0
250 y_pos = 0
251 for card in self.to_show:
252 panel.add(card, x_pos, y_pos)
253 x_pos += CARD_WIDTH + 5
254 self.inner_center.setWidget(panel)
255
256 def _ecartConfirm(self, confirm):
257 if not confirm:
258 return
259 ecart = []
260 for card in self.selected:
261 ecart.append((card.suit, card.value))
262 self.hand.remove(card)
263 self.selected.clear()
264 self._parent.host.bridge.call('tarotGamePlayCards', None, self.player_nick, self.referee, ecart)
265 self.state = "wait"
266 self.updateHand()
267
268 def addToSelection(self, card):
269 self.selected.add(card)
270 if len(self.selected) == 6:
271 dialog.ConfirmDialog(self._ecartConfirm, "Put these cards into chien ?").show()
272
273 def tarotGameInvalidCardsHandler(self, phase, played_cards, invalid_cards):
274 """Invalid cards have been played
275 @param phase: phase of the game
276 @param played_cards: all the cards played
277 @param invalid_cards: cards which are invalid"""
278
279 if phase == "play":
280 self.state = "play"
281 elif phase == "ecart":
282 self.state = "ecart"
283 else:
284 log.error("INTERNAL ERROR: unmanaged game phase") # FIXME: raise an exception here
285
286 for suit, value in played_cards:
287 self.hand.append(self.cards[(suit, value)])
288
289 self.hand.sort()
290 self.updateHand()
291 if self._autoplay == None: # No dialog if there is autoplay
292 Window.alert('Cards played are invalid !')
293 self.__fakePlay()
294
295 def removeFromSelection(self, card):
296 self.selected.remove(card)
297 if len(self.selected) == 6:
298 dialog.ConfirmDialog(self._ecartConfirm, "Put these cards into chien ?").show()
299
300 def tarotGameChooseContratHandler(self, xml_data):
301 """Called when the player has to select his contrat
302 @param xml_data: SàT xml representation of the form"""
303 body = xmlui.create(self._parent.host, xml_data, flags=['NO_CANCEL'])
304 _dialog = dialog.GenericDialog(_('Please choose your contrat'), body, options=['NO_CLOSE'])
305 body.setCloseCb(_dialog.close)
306 _dialog.show()
307
308 def tarotGameShowCardsHandler(self, game_stage, cards, data):
309 """Display cards in the middle of the game (to show for e.g. chien ou poignée)"""
310 self.to_show = []
311 for suit, value in cards:
312 self.to_show.append(self.cards[(suit, value)])
313 self.updateToShow()
314 if game_stage == "chien" and data['attaquant'] == self.player_nick:
315 self.state = "wait_for_ecart"
316 else:
317 self.state = "chien"
318
319 def getPlayerLocation(self, nick):
320 """return player location (top,bottom,left or right)"""
321 for location in ['top', 'left', 'bottom', 'right']:
322 if getattr(self, '%s_nick' % location) == nick:
323 return location
324 log.error("This line should not be reached")
325
326 def tarotGameCardsPlayedHandler(self, player, cards):
327 """A card has been played by player"""
328 if not len(cards):
329 log.warning("cards should not be empty")
330 return
331 if len(cards) > 1:
332 log.error("can't manage several cards played")
333 if self.to_show:
334 self.to_show = []
335 self.updateToShow()
336 suit, value = cards[0]
337 player_pos = self.getPlayerLocation(player)
338 player_panel = getattr(self, "inner_%s" % player_pos)
339
340 if player_panel.getWidget() != None:
341 #We have already cards on the table, we remove them
342 for pos in ['top', 'left', 'bottom', 'right']:
343 getattr(self, "inner_%s" % pos).setWidget(None)
344
345 card = self.cards[(suit, value)]
346 DOM.setElemAttribute(card.getElement(), "style", "")
347 player_panel.setWidget(card)
348
349 def tarotGameYourTurnHandler(self):
350 """Called when we have to play :)"""
351 if self.state == "chien":
352 self.to_show = []
353 self.updateToShow()
354 self.state = "play"
355 self.__fakePlay()
356
357 def __fakePlay(self):
358 """Convenience method for stupid autoplay
359 /!\ don't forgot to comment any interactive dialog for invalid card"""
360 if self._autoplay == None:
361 return
362 if self._autoplay >= len(self.hand):
363 self._autoplay = 0
364 card = self.hand[self._autoplay]
365 self._parent.host.bridge.call('tarotGamePlayCards', None, self.player_nick, self.referee, [(card.suit, card.value)])
366 del self.hand[self._autoplay]
367 self.state = "wait"
368 self._autoplay += 1
369
370 def playCard(self, card):
371 self.hand.remove(card)
372 self._parent.host.bridge.call('tarotGamePlayCards', None, self.player_nick, self.referee, [(card.suit, card.value)])
373 self.state = "wait"
374 self.updateHand()
375
376 def tarotGameScoreHandler(self, xml_data, winners, loosers):
377 """Show score at the end of a round"""
378 if not winners and not loosers:
379 title = "Draw game"
380 else:
381 if self.player_nick in winners:
382 title = "You <b>win</b> !"
383 else:
384 title = "You <b>loose</b> :("
385 body = xmlui.create(self._parent.host, xml_data, title=title, flags=['NO_CANCEL'])
386 _dialog = dialog.GenericDialog(title, body, options=['NO_CLOSE'])
387 body.setCloseCb(_dialog.close)
388 _dialog.show()