diff options
author | Walter Bender <walter@walter-laptop.(none)> | 2010-01-08 23:47:58 (GMT) |
---|---|---|
committer | Walter Bender <walter@walter-laptop.(none)> | 2010-01-08 23:47:58 (GMT) |
commit | d287399b5fc873c0bd545eb5ed37cd9158a76350 (patch) | |
tree | 0460934ff0275ec5bb19a59008325d567d030e9f | |
parent | 77633fe553d5940bb0eaeb4d174d9ac5557ac39a (diff) |
added sharing
-rw-r--r-- | VisualMatchActivity.py | 266 | ||||
-rwxr-xr-x | gencards.py | 48 | ||||
-rw-r--r-- | grid.py | 74 | ||||
-rw-r--r-- | window.py | 144 |
4 files changed, 384 insertions, 148 deletions
diff --git a/VisualMatchActivity.py b/VisualMatchActivity.py index 707c5ee..87f850b 100644 --- a/VisualMatchActivity.py +++ b/VisualMatchActivity.py @@ -40,12 +40,32 @@ from sugar.graphics.menuitem import MenuItem from sugar.graphics.icon import Icon from sugar.datastore import datastore +import telepathy +from dbus.service import method, signal +from dbus.gobject_service import ExportedGObject +from sugar.presence import presenceservice +from sugar.presence.tubeconn import TubeConnection +from sugar import profile + from gettext import gettext as _ import locale import os.path import logging _logger = logging.getLogger('visualmatch-activity') -import json +try: + _old_Sugar_system = False + import json + json.dumps + from json import load as jload + from json import dump as jdump +except (ImportError, AttributeError): + try: + import simplejson as json + from simplejson import load as jload + from simplejson import dump as jdump + except: + _old_Sugar_system = True + from StringIO import StringIO from constants import * @@ -104,7 +124,8 @@ class VisualMatchActivity(activity.Activity): try: datapath = os.path.join(activity.get_activity_root(), 'data') except: - datapath = os.path.join(os.environ['HOME'], SERVICE, 'data') + datapath = os.path.join(os.environ['HOME'], ".sugar", "default", + SERVICE, 'data') gencards.generator(datapath, _numberO, _numberC) # Create the toolbars @@ -113,6 +134,8 @@ class VisualMatchActivity(activity.Activity): # Activity toolbar activity_button = ActivityToolbarButton(self) + + ''' journal_button = ToolButton( "journal-write" ) journal_button.set_tooltip(_('Write in Journal')) journal_button.props.accelerator = '<Ctrl>j' @@ -120,6 +143,8 @@ class VisualMatchActivity(activity.Activity): activity.get_bundle_path()) activity_button.props.page.insert(journal_button, -1) journal_button.show() + ''' + toolbar_box.toolbar.insert(activity_button, 0) activity_button.show() @@ -356,13 +381,30 @@ class VisualMatchActivity(activity.Activity): self.vmw.matches = _matches self.vmw.robot_matches = _robot_matches self.vmw.total_time = _total_time + self.vmw.buddies = [] if not hasattr(self,'_saved_state'): self._saved_state = None # Start playing the game - window.new_game(self.vmw, self.vmw.cardtype, + window.new_game(self.vmw, self.vmw.cardtype, False, self._saved_state, _deck_index) + # + # A simplistic sharing model: the sharer is the master + # + + # Get the Presence Service + self.pservice = presenceservice.get_instance() + self.initiating = None # sharing (True) or joining (False) + + # Add my buddy object to the list + owner = self.pservice.get_owner() + self.owner = owner + self.vmw.buddies.append(self.owner) + self._share = "" + self.connect('shared', self._shared_cb) + self.connect('joined', self._joined_cb) + # # Write data to the Journal # @@ -382,44 +424,59 @@ class VisualMatchActivity(activity.Activity): self.metadata['deck_index'] = self.vmw.deck.index self.metadata['mime_type'] = 'application/x-visualmatch' f = file(file_path, 'w') - data = [] - for i in self.vmw.grid.grid: - if i is None: - data.append(None) - else: - data.append(i.index) - for i in self.vmw.clicked: - if i is None: - data.append(None) - else: - data.append(self.vmw.deck.spr_to_card(i).index) - for i in self.vmw.deck.cards: - data.append(i.index) - for i in self.vmw.match_list: - data.append(self.vmw.deck.spr_to_card(i).index) - io = StringIO() - json.dump(data,io) - f.write(io.getvalue()) + f.write(self._dump()) f.close() else: _logger.debug("Deferring saving to %s" % file_path) + def _dump(self): + data = [] + for i in self.vmw.grid.grid: + if i is None: + data.append(None) + else: + data.append(i.index) + for i in self.vmw.clicked: + if i is None: + data.append(None) + else: + data.append(self.vmw.deck.spr_to_card(i).index) + for i in self.vmw.deck.cards: + data.append(i.index) + for i in self.vmw.match_list: + data.append(self.vmw.deck.spr_to_card(i).index) + + if _old_Sugar_system is True: + return json.write(data) + else: + io = StringIO() + jdump(data, io) + return io.getvalue() + # # Read data from the Journal # def read_file(self, file_path): _logger.debug("Resuming from: %s" % file_path) f = open(file_path, 'r') - io = StringIO(f.read()) - saved_state = json.load(io) + self._load(f.read()) + f.close() + + def _load(self, data): + if _old_Sugar_system is True: + saved_state = json.read(data) + else: + io = StringIO(data) + saved_state = jload(io) if len(saved_state) > 0: self._saved_state = saved_state - f.close() # # Button callbacks # def _select_game_cb(self, button, activity, cardtype): + if window._joiner(self.vmw): # joiner cannot change level + return window.new_game(activity.vmw, cardtype) def _robot_cb(self, button, activity): @@ -434,6 +491,8 @@ class VisualMatchActivity(activity.Activity): self.robot_button.set_icon('robot-on') def _level_cb(self, button, activity): + if window._joiner(self.vmw): # joiner cannot change level + return activity.vmw.level = 1-activity.vmw.level self.level_label.set_text(self.calc_level_label(activity.vmw.low_score, activity.vmw.level)) @@ -452,6 +511,8 @@ class VisualMatchActivity(activity.Activity): int(low_score[play_level]%60)) def _number_card_O_cb(self, button, activity, numberO): + if window._joiner(self.vmw): # joiner cannot change level + return activity.vmw.numberO = numberO gencards.generate_number_cards(activity.vmw.path, activity.vmw.numberO, @@ -460,6 +521,8 @@ class VisualMatchActivity(activity.Activity): window.new_game(activity.vmw, 'number') def _number_card_C_cb(self, button, activity, numberC): + if window._joiner(self.vmw): # joiner cannot change level + return activity.vmw.numberC = numberC gencards.generate_number_cards(activity.vmw.path, activity.vmw.numberO, @@ -478,6 +541,161 @@ class VisualMatchActivity(activity.Activity): self.reveal() return True + # + # Sharing-related callbacks + # + + # Either set up initial share... + def _shared_cb(self, activity): + if self._shared_activity is None: + _logger.error("Failed to share or join activity ... \ + _shared_activity is null in _shared_cb()") + return + + self.initiating = True + self.waiting_for_deck = False + _logger.debug('I am sharing...') + + self.conn = self._shared_activity.telepathy_conn + self.tubes_chan = self._shared_activity.telepathy_tubes_chan + self.text_chan = self._shared_activity.telepathy_text_chan + + # call back for "NewTube" signal + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal \ + ('NewTube', self._new_tube_cb) + + _logger.debug('This is my activity: making a tube...') + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( + SERVICE, {}) + + # ...or join an exisiting share. + def _joined_cb(self, activity): + if self._shared_activity is None: + _logger.error("Failed to share or join activity ... \ + _shared_activity is null in _shared_cb()") + return + + self.initiating = False + _logger.debug('I joined a shared activity.') + + self.conn = self._shared_activity.telepathy_conn + self.tubes_chan = self._shared_activity.telepathy_tubes_chan + self.text_chan = self._shared_activity.telepathy_text_chan + + # call back for "NewTube" signal + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( \ + 'NewTube', self._new_tube_cb) + + _logger.debug('I am joining an activity: waiting for a tube...') + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + + self.waiting_for_deck = True + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + _logger.error('ListTubes() failed: %s', e) + + def _new_tube_cb(self, id, initiator, type, service, params, state): + _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' + 'params=%r state=%d', id, initiator, type, service, + params, state) + + if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self.tubes_chan[ \ + telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) + + tube_conn = TubeConnection(self.conn, + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, \ + group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + + # We'll use a chattube to send serialized data back and forth. + self.chattube = ChatTube(tube_conn, self.initiating, \ + self.event_received_cb) + + # Now that we have the tube, we can ask for the deck of cards. + if self.waiting_for_deck is True: + self._send_event("j") + + # Data is passed as tuples: cmd:text. + def event_received_cb(self, text): + if text[0] == 'B': + e,card_index = text.split(':') + _logger.debug("receiving card index: " + card_index) + window._process_selection(self.vmw, + self.vmw.deck.index_to_card(int(card_index)).spr) + elif text[0] == 'S': + e,card_index = text.split(':') + _logger.debug("receiving selection index: " + card_index) + window._process_selection(self.vmw, + self.vmw.selected[int(card_index)].spr) + elif text[0] == 'j': # request for current state from joiner + if self.initiating is True: + _logger.debug("serialize the project and send to joiner") + self._send_event("P:" + str(self.vmw.level)) + self._send_event("X:" + str(self.vmw.deck.index)) + self._send_event("M:" + str(self.vmw.matches)) + self._send_event("D:" + str(self._dump())) + elif text[0] == 'J': # new game, so make a request for current state + self._send_event("j") + self.waiting_for_deck = True + elif text[0] == 'P': + e,text = text.split(':') + _logger.debug("receiving play level from sharer " + text) + self.vmw.level = int(text) + elif text[0] == 'X': + e,text = text.split(':') + _logger.debug("receiving deck index from sharer " + text) + self.vmw.deck.index = int(text) + elif text[0] == 'M': + e,text = text.split(':') + _logger.debug("receiving matches from sharer " + text) + self.vmw.matches = int(text) + elif text[0] == 'D': + if self.waiting_for_deck: + e,text = text.split(':') + _logger.debug("receiving deck data from sharer") + self._load(text) + self.waiting_for_deck = False + window.new_game(self.vmw, self.vmw.cardtype, False, + self._saved_state, self.vmw.deck.index) + + # Send event through the tube + def _send_event(self, entry): + if hasattr(self, 'chattube') and self.chattube is not None: + self.chattube.SendText(entry) + +# +# Class for setting up tube for sharing +# +class ChatTube(ExportedGObject): + + def __init__(self, tube, is_initiator, stack_received_cb): + super(ChatTube, self).__init__(tube, PATH) + self.tube = tube + self.is_initiator = is_initiator # Are we sharing or joining activity? + self.stack_received_cb = stack_received_cb + self.stack = '' + + self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, \ + path=PATH, sender_keyword='sender') + + def send_stack_cb(self, text, sender=None): + if sender == self.tube.get_unique_name(): + return + # _logger.debug("This connection has no unique name yet.") + self.stack = text + self.stack_received_cb(text) + + @signal(dbus_interface=IFACE, signature='s') + def SendText(self, text): + self.stack = text + # # Toolbars for pre-0.86 Sugar # diff --git a/gencards.py b/gencards.py index c40d075..b59f22a 100755 --- a/gencards.py +++ b/gencards.py @@ -30,30 +30,29 @@ BLUE_STROKE = "#0060C8" BLUE_FILL = "#ACC8E4" GREEN_STROKE = "#00B418" GREEN_FILL = "#AFE8A8" -# PURPLE_STROKE = "#780078" -# PURPLE_FILL = "#E4AAE4" color_pairs = ([RED_STROKE,RED_FILL], [GREEN_STROKE,GREEN_FILL], [BLUE_STROKE,BLUE_FILL]) - -fill_styles = ("solid","none","gradient") -card_types = ("X","O","C") +fill_styles = ["solid","none","gradient"] +card_types = ["X","O","C"] roman_numerals = {5:'V',7:'VII',10:'X',11:'XI',14:'XIV',15:'XV',\ 21:'XXI',22:'XXII',33:'XXXIII'} - number_names = {5:_('five'),7:_('seven'),11:_('eleven'),10:_('ten'),\ 14:_('fourteen'),15:_('fifteen'),22:_('twenty two'),\ 21:_('twenty one'),33:_('thirty three')} - number_products = {5:'1×5',7:'1×7',11:'1×11',10:'2×5',\ 14:'2×7',15:'3×5',22:'2×11',\ 21:'3×7',33:'3×11'} - chinese_numerals = {5:'五',7:'七',10:'十',11:'十一',14:'十四',15:'十五',\ 21:'廿一',22:'廿二',33:'卅三'} +word_lists = [[_('mouse'),_('cat'),_('dog')],\ + [_('cheese'),_('apple'),_('bread')],\ + [_('moon'),_('sun'),_('earth')]] +word_styles = ["font-weight:bold;","","font-style:italic;"] + # # SVG generators # @@ -92,7 +91,6 @@ def svg_text(f,x,y,size,stroke,font,style,string): f.write(" style=\"font-size:"+str(size)+"px;\">"+string+"</tspan>\n") f.write(" </text>\n") - def svg_check(f, x, style, stroke, fill): f.write("<path d=\"m 28.4,70.2 -5.9,5.9 -5.9,-5.9 -4.1,-4.1 c -0.7,-0.7" +\ " -1.2,-1.8 -1.2,-2.9 0,-2.3 1.9,-4.1 4.1,-4.1 1.1,0 2.2,0.5" +\ @@ -331,7 +329,6 @@ def number_hash(f, t, n, stroke): for i in range(n): hash(f, nn, 5, y, stroke) y += 20 - def dice(f, t, n, stroke): if n == 5: @@ -470,14 +467,12 @@ def number_card(f, t, n, stroke, methodX, methodO, methodC): else: methodC(f, t, n, stroke) -def word_card(f, t, s, string, stroke): - if t == 'X': - svg_text(f,63.5,45.5,30,stroke,"DejaVu","font-weight:bold;",string[s]) - elif t == 'O': - svg_text(f,63.5,45.5,30,stroke,"DejaVu Serif","font-style:italic;",\ - string[s]) - else: - svg_text(f,63.5,45.5,30,stroke,"DejaVu","",string[s]) +def word_card(f, t, c, n, s): + svg_text(f,63.5,45.5,30,c[0],"DejaVu",s,word_lists[card_types.index(t)][n]) + +def pattern_card(f, t, c, n, s): + pattern_styles = [cross_card, circle_card, check_card] + pattern_styles[card_types.index(t)](f,n,s,c[0],c[1]) def open_file(datapath, filename): return file(os.path.join(datapath, filename), "w") @@ -499,12 +494,7 @@ def generate_pattern_cards(datapath): filename = "pattern-%d.svg" % (i) f = open_file(datapath, filename) header(f,"#000000",c[1],"0.5") - if t == "O": - circle_card(f,n,s,c[0],c[1]) - elif t == "C": - check_card(f,n,s,c[0],c[1]) - else: - cross_card(f,n,s,c[0],c[1]) + pattern_card(f,t,c,n,s) footer(f) close_file(f) i += 1 @@ -533,17 +523,11 @@ def generate_word_cards(datapath): for t in card_types: for c in color_pairs: for n in range(0,3): - for s in range(0,3): + for s in word_styles: filename = "word-%d.svg" % (i) f = open_file(datapath, filename) header(f,"#000000",c[1],"0.5") - if n == 0: - word_card(f,t,s,[_("mouse"),_("cat"),_("dog")],c[0]) - elif n == 1: - word_card(f,t,s,[_("cheese"),_("bread"),_("apple")], - c[0]) - else: - word_card(f,t,s,[_("moon"),_("sun"),_("earth")],c[0]) + word_card(f,t,c,n,s) footer(f) close_file(f) i += 1 @@ -31,48 +31,46 @@ from deck import * from constants import * # -# class for managing 3x5 matrix of cards +# Class for managing ROWxCOL matrix of cards # class Grid: def __init__(self, width, height, card_width, card_height): - # the playing surface is a 3x5 grid + # The playing surface self.grid = [] for i in range(ROW*COL): self.grid.append(None) - # how many cards are on the playing field - self.cards = 0 - # card spacing + # Card spacing self.left = int((width-(card_width*2))/2) self.xinc = int(card_width*1.2) - # self.top = int((height-(card_height*3.5))/2) self.top = 10 self.yinc = int(card_height*1.33) - # deal the initial deck of cards + # Deal an initial set of cards. def deal(self, deck): - self.cards = 0 for i in range(ROW*COL): if i < (ROW-1)*COL: self.grid[i] = deck.deal_next_card() self.place_a_card(self.grid[i], self.grid_to_xy(i)[0], self.grid_to_xy(i)[1]) - self.cards += 1 - else: # leave a blank row for extra cards + else: # Leave a blank row for extra cards at the bottom. self.grid[i] = None - # add cards when there is no match + # Add cards to the bottom row when there is no match. def deal_extra_cards(self, deck): - # if there are still cards in the deck and only 12 cards in the grid - if deck.empty() is False and self.cards == DEAL: - # add three extra cards to the playing field + # But only if there are still cards in the deck + # and only 12 cards in the grid + if deck.empty() is False and self.cards_in_grid() == DEAL: for c in range(0,COL): i = self.grid.index(None) self.grid[i] = deck.deal_next_card() self.place_a_card(self.grid[i], self.grid_to_xy(i)[0], self.grid_to_xy(i)[1]) - self.cards += 1 - # restore cards to grid upon resume + # How many cards are on the grid? + def cards_in_grid(self): + return ROW*COL-self.grid.count(None) + + # Restore cards to grid upon resume or share. def restore(self, deck, saved_card_index): self.hide() j = 0 @@ -84,33 +82,32 @@ class Grid: j += 1 self.show() - # remove a match from the grid and deal new cards from the deck + # Remove a match from the grid and replace it with new cards from the deck. def remove_and_replace(self, clicked_set, deck): for a in clicked_set: - # find the position in the grid of the clicked card - i = self.xy_to_grid(a.x,a.y) - # only add new cards if we are down to 12 cards - if self.cards == DEAL: + # Find the index into the grid of the clicked card + i = self.spr_to_grid(a) + # Don't add new cards if bottom row is occupied + if self.cards_in_grid() == DEAL: if deck.empty(): self.grid[i] = None else: - # save card in grid position of card we are replacing + # Put new card in grid position of card we are replacing. self.grid[i] = deck.deal_next_card() - self.place_a_card(self.grid[i], a.x, a.y) + self.place_a_card(self.grid[i], + self.grid_to_xy(i)[0], + self.grid_to_xy(i)[1]) else: - self.cards -= 1 - # mark grid positions of cards we are not replacing + # Mark as empty the grid positions we are not refilling self.grid[i] = None self.display_match(a, clicked_set.index(a)) - # move clicked card to the match area + # Move clicked card to the match area def display_match(self, spr, i): - spr.x = MATCH_POSITION - spr.y = self.top + i*self.yinc - spr.set_layer(2000) - spr.draw() + spr.move((MATCH_POSITION, self.top + i*self.yinc)) + spr.set_layer(2000) - # if we have removed cards from an expanded grid, we may need to consolidate + # If we have removed cards from an expanded grid, we have to consolidate. def consolidate(self): for j in range((ROW-1)*COL,ROW*COL): i = 0 @@ -118,43 +115,44 @@ class Grid: if self.grid[i] is None: self.grid[i] = self.grid[j] self.grid[i].spr.move(self.grid_to_xy(i)) + self.grid[i].spr.set_layer(2000) self.grid[j] = None else: i+=1 - # place a card at position x,y and display it + # Place a card at position x,y and display it. def place_a_card(self, c, x, y): if c is not None: c.spr.x = x c.spr.y = y c.show_card() - # convert from sprite x,y to grid index + # Convert from sprite x,y to grid index. def xy_to_grid(self, x, y): return int(COL*(y-self.top)/self.yinc) + int((x-self.left)/self.xinc) - # convert from grid index to sprite x,y + # Convert from grid index to sprite x,y. def grid_to_xy(self, i): return ((self.left+i%COL*self.xinc),(self.top+(i/COL)*self.yinc)) - # return sprite in grid element i + # Return the sprite in grid-position i. def grid_to_spr(self, i): return self.grid[i].spr - # return index of sprite in grid + # Return the index of a sprite in grid. def spr_to_grid(self, spr): for i in range(ROW*COL): if self.grid[i] is not None and self.grid[i].spr == spr: return(i) return None - # hide all of the cards on the gird + # Hide all of the cards on the grid. def hide(self): for i in range(ROW*COL): if self.grid[i] is not None: self.grid[i].hide_card() - # restore all card on the grid to their x,y positions + # Restore all card on the grid to their x,y positions. def show(self): for i in range(ROW*COL): self.place_a_card(self.grid[i],self.grid_to_xy(i)[0], @@ -45,7 +45,7 @@ difficulty_level = [LOW,HIGH] class vmWindow: pass # -# Handle launch from both within and without of Sugar environment +# Handle launch from both within and without of Sugar environment. # def new_window(canvas, path, parent=None): vmw = vmWindow() @@ -82,11 +82,11 @@ def new_window(canvas, path, parent=None): return vmw # -# Initialize for a new game +# Start a new game. # -def new_game(vmw, cardtype, saved_state=None, deck_index=0): +def new_game(vmw, cardtype, button_push=True, saved_state=None, deck_index=0): if not hasattr(vmw, 'deck'): - # first time through, initialize the deck, grid, and overlays + # The first time through, initialize the deck, grid, and overlays. vmw.deck = Deck(vmw.sprites, vmw.path, cardtype, vmw.card_width, vmw.card_height, difficulty_level[vmw.level]) vmw.grid = Grid(vmw.width, vmw.height, vmw.card_width, vmw.card_height) @@ -98,19 +98,21 @@ def new_game(vmw, cardtype, saved_state=None, deck_index=0): vmw.card_height, [MATCHMASK,0,0,0])) vmw.grid.display_match(vmw.match_display_area[i].spr, i) - vmw.deck.hide() # start by hiding everything + # Joiners in share cannot initiate new games. + if button_push and _joiner(vmw): + return + + vmw.deck.hide() + _unselect(vmw) + + # If cardtype has changed, we need to generate a new deck. if vmw.cardtype is not cardtype: vmw.cardtype = cardtype vmw.deck = Deck(vmw.sprites, vmw.path, vmw.cardtype, vmw.card_width, vmw.card_height, difficulty_level[vmw.level]) - vmw.deck.shuffle() - vmw.grid.deal(vmw.deck) - if _find_a_match(vmw) is False: - vmw.grid.deal_extra_cards(vmw.deck) - _unselect(vmw) - # restore saved state on resume + # Restore saved state on resume or share. if saved_state is not None: _logger.debug("Restoring state: %s" % (str(saved_state))) vmw.deck.index = deck_index @@ -121,18 +123,44 @@ def new_game(vmw, cardtype, saved_state=None, deck_index=0): _restore_selected(vmw, saved_state[ROW*COL:ROW*COL+3]) _restore_matches(vmw, saved_state[deck_stop:deck_stop+3*vmw.matches]) else: + vmw.deck.shuffle() + vmw.grid.deal(vmw.deck) + if _find_a_match(vmw) is False: + vmw.grid.deal_extra_cards(vmw.deck) vmw.matches = 0 vmw.robot_matches = 0 vmw.match_list = [] vmw.total_time = 0 + # If sharer starts a new game, ask joiners to request new deck. + if button_push and _sharer(vmw): + vmw.activity._send_event("J") + _update_labels(vmw) if _game_over(vmw): if hasattr(vmw,'timeout_id') and vmw.timeout_id is not None: gobject.source_remove(vmw.timeout_id) else: + if hasattr(vmw,'match_timeout_id') and vmw.match_timeout_id is not None: + gobject.source_remove(vmw.match_timeout_id) _timer_reset(vmw) +def _joiner(vmw): + if vmw.sugar is True and \ + hasattr(vmw.activity, 'chattube') and \ + vmw.activity.chattube is not None and \ + vmw.activity.initiating is False: + return True + return False + +def _sharer(vmw): + if vmw.sugar is True and \ + hasattr(vmw.activity, 'chattube') and \ + vmw.activity.chattube is not None and \ + vmw.activity.initiating is True: + return True + return False + # # Button press # @@ -149,24 +177,36 @@ def _button_release_cb(win, event, vmw): spr = vmw.sprites.find_sprite((x, y)) if spr is None: return True + if vmw.sugar is True and \ + hasattr(vmw.activity, 'chattube') and \ + vmw.activity.chattube is not None: + if vmw.deck.spr_to_card(spr) is not None: + vmw.activity._send_event("B:"+str(vmw.deck.spr_to_card(spr).index)) + i = _selected(vmw, spr) + if i is not -1: + vmw.activity._send_event("S:"+str(i)) return _process_selection(vmw, spr) +def _selected(vmw, spr): + for i in range(3): + if vmw.selected[i].spr == spr: + return i + return -1 + def _process_selection(vmw, spr): - # check to make sure a card in the matched pile isn't selected + # Make sure a card in the matched pile isn't selected. if spr.x == MATCH_POSITION: return True - # check to make sure that the current card isn't already selected - for a in vmw.clicked: - if a is spr: - # on second click, unselect - i = vmw.clicked.index(a) - vmw.clicked[i] = None - vmw.selected[i].hide_card() - return True + # Make sure that the current card isn't already selected. + i = _selected(vmw, spr) + if i is not -1: + # On a second click, unselect it. + vmw.clicked[i] = None + vmw.selected[i].hide_card() + return True - # add the selected card to the list - # and highlight it with the selection mask + # Otherwise highlight the card with a selection mask. for a in vmw.clicked: if a is None: i = vmw.clicked.index(a) @@ -174,17 +214,15 @@ def _process_selection(vmw, spr): vmw.selected[i].spr.x = spr.x vmw.selected[i].spr.y = spr.y vmw.selected[i].show_card() - break # we only want to add the card to the list once + break - # if we have three cards selected, test for a match - if None in vmw.clicked: - pass - else: + # If we have three cards selected, test for a match. + if None not in vmw.clicked: _test_for_a_match(vmw) return True # -# Game is over when the deck is empty and there are no more matches +# Game is over when the deck is empty and there are no more matches. # def _game_over(vmw): if vmw.deck.empty() and _find_a_match(vmw) is False: @@ -192,7 +230,7 @@ def _game_over(vmw): set_label(vmw,"clock","") set_label(vmw,"status","%s (%d:%02d)" % (_("Game over"),int(vmw.total_time/60),int(vmw.total_time%60))) - _show_matches(vmw, 0) + vmw.match_timeout_id = gobject.timeout_add(2000,_show_matches,vmw,0) return True return False @@ -205,22 +243,22 @@ def _test_for_a_match(vmw): vmw.deck.spr_to_card(vmw.clicked[2])], vmw.cardtype): - # stop the timer + # Stop the timer. if vmw.timeout_id is not None: gobject.source_remove(vmw.timeout_id) vmw.total_time += gobject.get_current_time()-vmw.start_time - # increment the match counter and add the match to the match list + # Increment the match counter and add the match to the match list. vmw.matches += 1 for i in vmw.clicked: vmw.match_list.append(i) - # remove the match and deal 3 new cards + # Remove the match and deal three new cards. vmw.grid.remove_and_replace(vmw.clicked, vmw.deck) set_label(vmw, "deck", "%d %s" % (vmw.deck.cards_remaining(), _("cards"))) - # test to see if the game is over + # Test to see if the game is over. if _game_over(vmw): gobject.source_remove(vmw.timeout_id) _unselect(vmw) @@ -235,20 +273,19 @@ def _test_for_a_match(vmw): vmw.activity.save_score() return True - # consolidate the grid + # Consolidate the grid. vmw.grid.consolidate() - # test to see if we need to deal extra cards + # Test to see if we need to deal extra cards. if _find_a_match(vmw) is False: vmw.grid.deal_extra_cards(vmw.deck) - # keep playing + # Keep playing. _update_labels(vmw) _timer_reset(vmw) - # whether or not there was a match, unselect all cards and refresh display + # Whether or not there was a match, unselect all cards. _unselect(vmw) - vmw.sprites.redraw_sprites() # # Unselect the cards @@ -275,7 +312,7 @@ def _destroy_cb(win, event, vmw): gtk.main_quit() # -# Write strings to a label in the toolbar +# Write strings to a label in the toolbar. # def _update_labels(vmw): set_label(vmw, "deck", "%d %s" % @@ -309,7 +346,7 @@ def set_label(vmw, label, s): vmw.win.set_title("%s: %s" % (_("Visual Match"),s)) # -# Restore selected cards upon resume +# Restore the selected cards upon resume or share. # def _restore_selected(vmw, saved_selected_indices): j = 0 @@ -325,7 +362,7 @@ def _restore_selected(vmw, saved_selected_indices): j += 1 # -# Restore match list upon resume +# Restore the match list upon resume or share. # def _restore_matches(vmw, saved_match_list_indices): j = 0 @@ -339,7 +376,7 @@ def _restore_matches(vmw, saved_match_list_indices): vmw.grid.display_match(vmw.match_list[l-3+j], j) # -# Display of seconds since start_time or find a match +# Display of seconds since start_time. # def _counter(vmw): seconds = int(gobject.get_current_time()-vmw.start_time) @@ -355,7 +392,7 @@ def _timer_reset(vmw): _counter(vmw) # -# Show all the matches +# Show all the matches as a simple animation. # def _show_matches(vmw, i): if i < vmw.matches: @@ -364,11 +401,11 @@ def _show_matches(vmw, i): vmw.match_timeout_id = gobject.timeout_add(2000,_show_matches,vmw,i+1) # -# Check to see whether there are any matches on the board +# Check to see whether there are any matches on the board. # def _find_a_match(vmw, robot_match=False): a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14] - for i in Permutation(a): # really should be Combination + for i in Permutation(a): # TODO: really should be combination cardarray = [vmw.grid.grid[i[0]],\ vmw.grid.grid[i[1]],\ vmw.grid.grid[i[2]]] @@ -382,28 +419,27 @@ def _find_a_match(vmw, robot_match=False): return False # -# For each attribute, either it is the same or different on every card +# For each attribute, either it is the same or different on every card. # def _match_check(cardarray, cardtype): for a in cardarray: if a is None: return False - if (cardarray[0].num + cardarray[1].num + cardarray[2].num)%3 != 0: + if (cardarray[0].shape + cardarray[1].shape + cardarray[2].shape)%3 != 0: return False if (cardarray[0].color + cardarray[1].color + cardarray[2].color)%3 != 0: return False - if (cardarray[0].shape + cardarray[1].shape + cardarray[2].shape)%3 != 0: + if (cardarray[0].fill + cardarray[1].fill + cardarray[2].fill)%3 != 0: return False - # special case for the word game: - # only check fill when numbers are the same + # Special case: only check number when shapes are the same if cardtype == 'word': - if cardarray[0].num == cardarray[1].num and \ - cardarray[0].num == cardarray[2].num and \ - (cardarray[0].fill + cardarray[1].fill + cardarray[2].fill)%3 != 0: + if cardarray[0].shape == cardarray[1].shape and \ + cardarray[0].shape == cardarray[2].shape and \ + (cardarray[0].num + cardarray[1].num + cardarray[2].num)%3 != 0: return False else: - if (cardarray[0].fill + cardarray[1].fill + cardarray[2].fill)%3 != 0: + if (cardarray[0].num + cardarray[1].num + cardarray[2].num)%3 != 0: return False return True |