changeset 36:1d406077b49b

Tarot Game: first draft
author Goffi <goffi@goffi.org>
date Tue, 17 May 2011 01:33:12 +0200
parents d43d6e4b9dc8
children b306aa090438
files browser_side/card_game.py browser_side/games.py browser_side/panels.py libervia.py libervia.tac public/libervia.css
diffstat 6 files changed, 227 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/browser_side/card_game.py	Tue May 17 01:33:12 2011 +0200
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+Libervia: a Salut à Toi frontend
+Copyright (C) 2011  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/>.
+"""
+
+import pyjd # this is dummy in pyjs
+from pyjamas.ui.AbsolutePanel import AbsolutePanel
+from pyjamas.ui.Image import Image
+
+from pyjamas.dnd import makeDraggable
+from pyjamas.ui.DragWidget import DragWidget, DragContainer
+from jid import JID
+from tools import html_sanitize
+from datetime import datetime
+from time import time
+from games import TarotCard
+
+
+
+CARD_WIDTH = 74
+CARD_HEIGHT = 136
+MIN_WIDTH = 950 #Minimum size of the panel
+MIN_HEIGHT = 500
+
+
+class CardWidget(TarotCard, Image):
+    """This class is used to represent a card, graphically and logically"""
+
+    def __init__(self, file):
+        """@param file: path of the PNG file"""
+        Image.__init__(self,file)
+        root_name = file[file.rfind("/")+1:-4]
+        suit,value = root_name.split('_')
+        TarotCard.__init__(self, (suit, value))
+        print "Carte:",suit, value #, self.bout
+
+    def draw(self, dc, x, y):
+        """Draw the card on the device context
+        @param dc: device context
+        @param x: abscissa 
+        @param y: ordinate"""
+        pass
+        #dc.DrawBitmap(self.bitmap, x, y, True)
+
+class CardPanel(AbsolutePanel):
+
+    def __init__(self, parent, referee, players, player_nick):
+        self._parent = parent
+        self._autoplay = None #XXX: use 0 to activate fake play, None else
+        self.referee = referee
+        self.players = players
+        self.played = {}
+        for player in players:
+            self.played[player] = None
+        self.player_nick = player_nick
+        self.bottom_nick = self.player_nick
+        idx = self.players.index(self.player_nick)
+        idx = (idx + 1) % len(self.players)
+        self.right_nick = self.players[idx]
+        idx = (idx + 1) % len(self.players)
+        self.top_nick = self.players[idx]
+        idx = (idx + 1) % len(self.players)
+        self.left_nick = self.players[idx]
+        self.bottom_nick = 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
+        AbsolutePanel.__init__(self)
+        self.setSize("%spx" % MIN_WIDTH, "%spx" % MIN_HEIGHT)
+        self.setStyleName("cardPanel")
+        self.loadCards()
+        self.mouse_over_card = None #contain the card to highlight
+        self.visible_size = CARD_WIDTH/2 #number of pixels visible for cards
+
+    def _getTarotCardsPathsCb(self, paths):
+        for file in paths:
+            card = CardWidget(file)
+            self.cards[(card.suit, card.value)]=card
+            self.deck.append(card)
+
+    def loadCards(self, dir):
+        """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
+        self._parent.host.bridge.call('getTarotCardsPaths', self._getTarotCardsPathsCb)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/browser_side/games.py	Tue May 17 01:33:12 2011 +0200
@@ -0,0 +1,78 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+Libervia: a Salut à Toi frontend
+Copyright (C) 2011  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/>.
+"""
+
+"""This library help manage general games (e.g. card games)"""
+
+    
+suits_order = ['pique', 'coeur', 'trefle', 'carreau', 'atout'] #I have switched the usual order 'trefle' and 'carreau' because card are more easy to see if suit colour change (black, red, black, red)
+values_order = map(str,range(1,11))+["valet","cavalier","dame","roi"]
+
+class TarotCard():
+    """This class is used to represent a car logically"""
+    #TODO: move this in a library in tools, and share this with frontends (e.g. card_game in wix use the same class)
+
+    def __init__(self, tuple_card):
+        """@param tuple_card: tuple (suit, value)"""
+        self.suit, self.value = tuple_card
+        self.bout = True if self.suit=="atout" and self.value in ["1","21","excuse"] else False
+        if self.bout or self.value == "roi":
+            self.points = 4.5
+        elif self.value == "dame":
+            self.points = 3.5
+        elif self.value == "cavalier":
+            self.points = 2.5
+        elif self.value == "valet":
+            self.points = 1.5
+        else:
+            self.points = 0.5
+
+    def get_tuple(self):
+        return (self.suit,self.value)
+
+    @staticmethod
+    def from_tuples(tuple_list):
+        result = []
+        for card_tuple in tuple_list:
+            result.append(TarotCard(card_tuple))
+        return result
+
+    def __cmp__(self, other):
+        if other == None:
+            return 1
+        if self.suit != other.suit:
+            idx1 = suits_order.index(self.suit)
+            idx2 = suits_order.index(other.suit)
+            return idx1.__cmp__(idx2)
+        if self.suit == 'atout':
+            if self.value == other.value == 'excuse':
+                return 0
+            if self.value == 'excuse':
+                return -1
+            if other.value == 'excuse':
+                return 1
+            return int(self.value).__cmp__(int(other.value))
+        #at this point we have the same suit which is not 'atout'
+        idx1 = values_order.index(self.value)
+        idx2 = values_order.index(other.value)
+        return idx1.__cmp__(idx2)
+
+    def __str__(self):
+        return "[%s,%s]" % (self.suit, self.value)
--- a/browser_side/panels.py	Mon May 16 18:19:35 2011 +0200
+++ b/browser_side/panels.py	Tue May 17 01:33:12 2011 +0200
@@ -47,6 +47,7 @@
 
 from pyjamas.dnd import makeDraggable
 from pyjamas.ui.DragWidget import DragWidget, DragContainer
+from card_game import CardPanel 
 from jid import JID
 from tools import html_sanitize
 from datetime import datetime
@@ -555,6 +556,13 @@
             return
         self.content.add(ChatText(timestamp, nick, mymess, msg))
         self.content_scroll.scrollToBottom()
+    
+    def startGame(self, game_type, referee, players):
+        """Configure the chat window to start a game"""
+        if game_type=="Tarot":
+            self.tarot_panel = CardPanel(self, referee, players, self.nick)
+            self.vpanel.insert(self.tarot_panel, 1)
+            self.vpanel.setCellHeight(self.tarot_panel, self.tarot_panel.getHeight())
 
 class MainDiscussionPanel(HorizontalPanel):
     
--- a/libervia.py	Mon May 16 18:19:35 2011 +0200
+++ b/libervia.py	Tue May 17 01:33:12 2011 +0200
@@ -66,7 +66,7 @@
     def __init__(self):
         LiberviaJsonProxy.__init__(self, "/json_api",
                         ["getContacts", "sendMessage", "sendMblog", "getMblogNodes", "getProfileJid", "getHistory",
-                         "getPresenceStatus", "getRoomJoined", "launchTarotGame"])
+                         "getPresenceStatus", "getRoomJoined", "launchTarotGame", "getTarotCardsPaths"])
 
 class BridgeSignals(LiberviaJsonProxy):
     def __init__(self):
@@ -167,6 +167,8 @@
             self._roomUserJoinedCb(*args)
         elif name == 'roomUserLeft':
             self._roomUserLeftCb(*args)
+        elif name == 'tarotGameStarted':
+            self._tarotGameStartedCb(*args)
 
     def _getProfileJidCB(self, jid):
         self.whoami = JID(jid)
@@ -189,7 +191,7 @@
             else:
                 _groups=None
             for panel in self.mpanels:
-                if isinstance(panel,MicroblogPanel) and (panel.isJidAccepted(sender) or _groups == None or _groups.intersection(panel.accepted_groups)):
+                if isinstance(panel,MicroblogPanel) and (panel.isJidAccepted(sender) or _groups == None or _groups.intersection(panel.accepted_groups)): #TODO: check this
                     content = data['content']
                     author = data.get('author')
                     timestamp = float(data.get('timestamp',0)) #XXX: int doesn't work here
@@ -231,7 +233,13 @@
         for panel in self.mpanels + self.other_panels:
             if isinstance(panel,ChatPanel) and panel.type == 'group' and panel.target.bare == "%s@%s" % (room_id, room_service):
                 panel.userLeft(user_nick, user_data)
-            
+        
+    def _tarotGameStartedCb(self, room_jid, referee, players):
+        print ("Tarot Game Started \o/")
+        for panel in self.mpanels + self.other_panels:
+            if isinstance(panel,ChatPanel) and panel.type == 'group' and panel.target.bare == room_jid:
+                panel.startGame("Tarot", referee, players)
+
     def _getPresenceStatusCB(self, presence_data):
         for entity in presence_data:
             for resource in presence_data[entity]:
--- a/libervia.tac	Mon May 16 18:19:35 2011 +0200
+++ b/libervia.tac	Tue May 17 01:33:12 2011 +0200
@@ -33,10 +33,13 @@
 from txjsonrpc import jsonrpclib
 from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService
 import re
+import glob
+import os.path
 from server_side.blog import MicroBlog
 
 TIMEOUT = 120 #Session's time out, after that the user will be disconnected
-
+LIBERVIA_DIR = "output/"
+CARDS_DIR = "cards/"
 
 class MethodHandler(jsonrpc.JSONRPC):
 
@@ -121,6 +124,12 @@
         profile = self.session.sat_profile
         self.sat_host.bridge.tarotGameLaunch(other_players, profile)
 
+    def jsonrpc_getTarotCardsPaths(self):
+        """Give the path of all the tarot cards"""
+        return map(lambda x: x[len(LIBERVIA_DIR):],glob.glob(os.path.join(LIBERVIA_DIR,CARDS_DIR,'*_*.png')));
+
+
+
 class Register(jsonrpc.JSONRPC):
     """This class manage the registration procedure with SàT
     It provide an api for the browser, check password and setup the web server"""
@@ -329,7 +338,7 @@
 class Libervia(service.Service):
    
     def __init__(self):
-        root = File("output/") 
+        root = File(LIBERVIA_DIR) 
         self.signal_handler = SignalHandler(self)
         _register = Register(self)
         self.signal_handler.plugRegister(_register)
@@ -344,7 +353,7 @@
             sys.exit(1)
         self.bridge.register("connected", self.signal_handler.connected)
         self.bridge.register("connectionError", self.signal_handler.connectionError)
-        for signal_name in ['presenceUpdate', 'personalEvent', 'newMessage', 'roomJoined', 'roomUserJoined', 'roomUserLeft']:
+        for signal_name in ['presenceUpdate', 'personalEvent', 'newMessage', 'roomJoined', 'roomUserJoined', 'roomUserLeft', 'tarotGameStarted']:
             self.bridge.register(signal_name, self.signal_handler.getGenericCb(signal_name))
         root.putChild('json_signal_api', self.signal_handler)
         root.putChild('json_api', MethodHandler(self))
--- a/public/libervia.css	Mon May 16 18:19:35 2011 +0200
+++ b/public/libervia.css	Tue May 17 01:33:12 2011 +0200
@@ -288,7 +288,14 @@
     height: 100%;
 }
 
-/* Test drag and drop */
+/* Games */
+
+.cardPanel {
+    background: green;
+    margin: 0 auto;
+}
+
+/* Drag and drop */
 
 .dragover {
     background: #8f8;