Back to index

gcompris  8.2.2
gnumch.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 #  gcompris - gnumch
00003 #
00004 # Time-stamp: <2005/10/03 09:16 Joe Neeman>
00005 #
00006 # Copyright (C) 2005 Joe Neeman
00007 #
00008 #   This program is free software; you can redistribute it and/or modify
00009 #   it under the terms of the GNU General Public License as published by
00010 #   the Free Software Foundation; either version 2 of the License, or
00011 #   (at your option) any later version.
00012 #
00013 #   This program is distributed in the hope that it will be useful,
00014 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 #   GNU General Public License for more details.
00017 #
00018 #   You should have received a copy of the GNU General Public License
00019 #   along with this program; if not, write to the Free Software
00020 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021 #
00022 
00023 import gnomecanvas
00024 import gcompris
00025 import gcompris.utils
00026 import gcompris.skin
00027 import gcompris.bonus
00028 import gcompris.score
00029 import gcompris.anim
00030 import gobject
00031 import gtk
00032 import gtk.gdk
00033 import random
00034 import math
00035 from gettext import gettext as _
00036 
00037 class Number:
00038     def __init__(self, text, good):
00039         self.text = text
00040         self.good = good
00041 
00042 class Square:
00043     def __init__(self, x, y):
00044         self.num = None
00045         self.pic = game.rootitem.add( gnomecanvas.CanvasText,
00046                                       text = "",
00047                                       font = gcompris.skin.get_font("gcompris/content"),
00048                                       x = x,
00049                                       y = y )
00050 
00051     def setNum(self, num):
00052         self.num = num
00053         if num != None:
00054             self.pic.set( text = num.text )
00055         else:
00056             self.pic.set( text = "" )
00057 
00058 class Level:
00059     def __init__(self, title, numlist):
00060         self.title = title
00061         self.numbers = numlist
00062 
00063 # the following class goes unused: is is only here to document the Levelset interface.
00064 class Levelset:
00065     def __init__(self):
00066         pass
00067 
00068     def getError(self, num):
00069         pass
00070 
00071     def getTitle(self):
00072         pass
00073 
00074     def setLevel(self, level, sublevel):
00075         pass
00076 
00077     def getNumber(self):
00078         pass
00079 
00080 def isPrime(n):
00081     if n == 1:
00082         return 0
00083     for i in range(2, int(math.sqrt(n) + 1) ):
00084         if (n % i) == 0:
00085             return 0
00086     return 1
00087 
00088 def makeNumList(nums):
00089     if len(nums) == 0:
00090         return ""
00091     fmt = '%d'
00092     if len(nums) >= 2:
00093         for i in range(1, len(nums)-1):
00094             fmt += _(', %d')
00095         fmt += _(' and %d')
00096     return fmt
00097 
00098 def getFactors(n):
00099     f = []
00100     for i in range(1, n/2 + 1):
00101         if n%i == 0:
00102             f.append(i)
00103     f.append(n)
00104     return f
00105 
00106 class PrimeLevelset:
00107     def __init__(self):
00108         self.numlevels = 1
00109         self.num_sublevels = 9
00110         self.level_max = [ 3, 5, 7, 11, 13, 17, 19, 23, 29 ]
00111         self.curlevel = 1
00112         self.cur_sublevel = 1
00113 
00114     def getError(self, num):
00115         fmt = _('%d is divisible by %s.')
00116         n = int(num.text)
00117 
00118         if n == 1:
00119             return _("1 is not a prime number.")
00120 
00121         factors = []
00122         for i in range(2, n/2 + 1):
00123             if n % i == 0:
00124                 factors.append(i)
00125         s = makeNumList(factors) % tuple(factors)
00126         return fmt % (n,s)
00127 
00128     def getTitle(self):
00129         return _('Primes less than %d') % ( self.level_max[self.cur_sublevel-1] + 1 )
00130 
00131     def setLevel(self, level, sublevel):
00132         self.cur_sublevel = level
00133         self.cur_sublevel = sublevel
00134 
00135     def getNumber(self):
00136         n = random.randint( 1, self.level_max[self.cur_sublevel-1] )
00137         return Number( str(n), isPrime(n) )
00138 
00139 class FactorLevelset:
00140     def __init__(self):
00141         self.num_sublevels = 9
00142         self.numlevels = 1
00143         self.level_multiple = [ 4, 6, 8, 10, 12, 15, 18, 20, 24 ]
00144         self.curlevel = 1
00145         self.cur_sublevel = 1
00146         self.factors = []
00147         self.nonfactors = []
00148 
00149     def getError(self, num):
00150         fmt = _('Multiples of %d include %s,\nbut %d is not a multiple of %d.')
00151         n = int(num.text)
00152         mults = []
00153         for i in range(2, 5):
00154             mults.append(n*i)
00155         s = makeNumList(mults) % tuple(mults)
00156         return fmt % (n, s, self.level_multiple[self.cur_sublevel-1], n)
00157 
00158     def getTitle(self):
00159         return _('Factors of %d') % ( self.level_multiple[self.cur_sublevel-1] )
00160 
00161     def setLevel(self, level, sublevel):
00162         self.curlevel = level
00163         self.cur_sublevel = sublevel
00164         self.factors = []
00165         self.nonfactors = []
00166         for i in range(1, self.level_multiple[sublevel-1]+1):
00167             if self.level_multiple[sublevel-1] % i == 0:
00168                 self.factors.append(i)
00169             else:
00170                 self.nonfactors.append(i)
00171 
00172     def getNumber(self):
00173         if random.randint(0,1):
00174             # choose a good number
00175             n = self.factors[ random.randint(0, len(self.factors)-1) ]
00176             num = Number( str(n), 1 )
00177         else:
00178             # choose a wrong number
00179             n = self.nonfactors[ random.randint(0, len(self.nonfactors)-1) ]
00180             num = Number( str(n), 0 )
00181         return num
00182 
00183 class MultipleLevelset:
00184     def __init__(self):
00185         self.numlevels = 4
00186         self.num_sublevels = 9
00187         self.min_mult = 4
00188         self.curlevel = 1
00189         self.cur_sublevel = 1
00190 
00191     def getError(self, num):
00192         fmt = _('%s are the factors of %d.')
00193         n = int(num.text)
00194 
00195         factors = []
00196         for i in range(1, n/2+1):
00197             if n % i == 0:
00198                 factors.append(i)
00199         factors.append(n)
00200         s = makeNumList(factors) % tuple(factors)
00201         return fmt % (s, n)
00202 
00203     def getTitle(self):
00204         return _('Multiples of %d') % ( self.cur_sublevel+1 )
00205 
00206     def setLevel(self, level, sublevel):
00207         self.curlevel = level
00208         self.cur_sublevel = sublevel
00209 
00210     def getNumber(self):
00211         if random.randint(0,1):
00212             # choose a good number
00213             n = (self.cur_sublevel+1) * random.randint(1, self.min_mult + self.curlevel*2)
00214             num = Number( str(n), 1 )
00215         else:
00216             # choose a wrong number
00217             n = (self.cur_sublevel+1) * random.randint(1, self.min_mult + self.curlevel*2) - random.randint(1, self.cur_sublevel)
00218             num = Number( str(n), 0 )
00219         return num
00220 
00221 # for all expression-based levels, we add a value field to the Number
00222 class ExpressionLevelset(object):
00223     def __init__(self):
00224         self.numlevels = 7
00225         self.num_sublevels = 7
00226         self.levelops = [ [self.getPlus],
00227                           [self.getMinus],
00228                           [self.getPlus, self.getMinus],
00229                           [self.getTimes],
00230                           [self.getPlus, self.getMinus, self.getTimes],
00231                           [self.getDivide],
00232                           [self.getPlus, self.getMinus, self.getTimes, self.getDivide]
00233                         ]
00234         self.curlevel = 1
00235         self.cur_sublevel = 1
00236 
00237     def getError(self, num):
00238         fmt = _('%s = %d')
00239         return fmt % (num.text, num.value)
00240 
00241     def getNumberWithAnswer(self, answer):
00242         fn = random.choice( self.levelops[self.curlevel-1] )
00243         num = fn(answer)
00244         num.value = answer
00245         return num
00246 
00247     def getPlus(self, answer):
00248         n = random.randint(0, answer)
00249         num = Number( _(u'%d + %d') % (n, answer-n), 1 )
00250         return num
00251 
00252     def getMinus(self, answer):
00253         n = random.randint(answer, answer*2)
00254         num = Number( _(u'%d \u2212 %d') % (n, n-answer), 1 )
00255         return num
00256 
00257     def getTimes(self, answer):
00258         n = random.choice( getFactors(answer) )
00259         return Number( _(u'%d \u00d7 %d') % (n, answer/n), 1 )
00260 
00261     def getDivide(self, answer):
00262         n = random.randint(1, 5)
00263         return Number( _(u'%d \u00f7 %d') % (answer*n, n), 1 )
00264 
00265 class EqualityLevelset(ExpressionLevelset):
00266     def __init__(self):
00267         super(EqualityLevelset, self).__init__()
00268         self.answermin = 5
00269 
00270     def getTitle(self):
00271         return _('Equal to %d') % (self.answer,)
00272 
00273     def setLevel(self, level, sublevel):
00274         self.curlevel = level
00275         self.cur_sublevel = sublevel
00276         self.answer = self.answermin + self.cur_sublevel
00277 
00278     def getNumber(self):
00279         if random.randint(0, 1):
00280             # correct number
00281             num = self.getNumberWithAnswer(self.answer)
00282             num.good = 1
00283         else:
00284             # wrong number
00285             ans = random.choice( range(self.answermin, self.answer) + range(self.answer+1, self.answer*2) )
00286             num = self.getNumberWithAnswer(ans)
00287             num.good = 0
00288         return num
00289 
00290 class InequalityLevelset(EqualityLevelset):
00291     def getTitle(self):
00292         return _('Not equal to %d') % (self.answer,)
00293 
00294     def getNumber(self):
00295         num = super(InequalityLevelset, self).getNumber()
00296         if num.good:
00297             num.good = 0
00298         else:
00299             num.good = 1
00300         return num
00301 
00302 class Player(object):
00303 
00304     def __init__(self):
00305         self.move_stepnum = 0
00306         self.x = self.y = self.x_old = self.y_old = -1
00307         self.moving = self.exists = False
00308         self.action_start = 0
00309         self.anim = None
00310         self.velocity = []
00311 
00312         self.movestep_timer = 0
00313         self.munch_timer = 0
00314 
00315         # food chain status
00316         self.foodchain = 0
00317 
00318     # These are defined in Muncher and Troggle
00319     def spawn(self):
00320         pass
00321 
00322     def die(self):
00323         if self.movestep_timer != 0:
00324             gtk.timeout_remove(self.movestep_timer)
00325             self.movestep_timer = 0
00326         if self.munch_timer != 0:
00327             gtk.timeout_remove(self.munch_timer)
00328             self.munch_timer = 0
00329 
00330     def getEaten(self):
00331         pass
00332 
00333     def isAt(self, x, y):
00334         return self.exists and not self.moving and (self.x == x and self.y == y)
00335 
00336     def isNear(self, x, y):
00337         return self.exists and (    (self.x == x and self.y == y)
00338                                 or  (self.moving and self.x_old == x
00339                                                  and self.y_old == y)
00340                                )
00341 
00342     def move_step(self):
00343         if self.move_stepnum < game.num_moveticks-1:
00344             self.move_stepnum += 1
00345             x_old = self.anim.gnomecanvas.get_property("x")
00346             y_old = self.anim.gnomecanvas.get_property("y")
00347             x = self.anim.gnomecanvas.get_property("x") + self.velocity[0]*game.sw/game.num_moveticks
00348             y = self.anim.gnomecanvas.get_property("y") + self.velocity[1]*game.sh/game.num_moveticks
00349             ret = True
00350         else:
00351             self.move_stepnum = 0
00352             x = game.sw * self.x + game.left
00353             y = game.sh * self.y + game.top
00354             self.stop()
00355             self.movestep_timer = 0
00356             ret = False
00357 
00358         self.anim.gnomecanvas.set(x=x, y=y)
00359         return ret
00360 
00361     def move(self, x_old, y_old, x, y):
00362         self.x_old = x_old
00363         self.y_old = y_old
00364         self.x = x
00365         self.y = y
00366         self.velocity = [x-x_old, y-y_old]
00367         self.anim.gnomecanvas.set(x=(self.x_old * game.sw + game.left),
00368                                    y=(self.y_old * game.sh + game.top))
00369         self.moving = True
00370 
00371         # it takes game.num_moveticks iterations of duration game.move_tick to move squares
00372         if x != x_old or y != y_old:
00373             self.anim.setState(1)
00374             self.movestep_timer = game.timeout_add(game.move_tick, self.move_step)
00375         else:
00376             self.stop()
00377 
00378     def startMunching(self):
00379         self.anim.setState(2)
00380         self.munch_timer = game.timeout_add(game.munch_time, self.stopMunching)
00381         return False
00382 
00383     def stopMunching(self):
00384         self.munch_timer = 0
00385         self.anim.setState(0)
00386         return False
00387 
00388     def stop(self):
00389         self.anim.setState(0)
00390         self.moving = False
00391 
00392         # work out eating stuff
00393         for p in game.players:
00394             if p.isAt(self.x, self.y) and p != self:
00395                 if self.foodchain >= p.foodchain:
00396                     p.getEaten()
00397                     self.startMunching()
00398                 else:
00399                     self.getEaten()
00400                     p.startMunching()
00401 
00402 
00403 class Muncher(Player):
00404     def __init__(self):
00405         super(Muncher, self).__init__()
00406         self.lives = 1
00407         self.anim = gcompris.anim.CanvasItem(game.munchanimation, game.rootitem)
00408         self.spare = gcompris.anim.CanvasItem(game.munchanimation, game.rootitem)
00409         self.anim.gnomecanvas.hide()
00410         self.key_queue = []
00411         self.spare.gnomecanvas.set(x=0, y=0)
00412 
00413     def spawn(self):
00414         if self.lives >= 1:
00415             self.spare.gnomecanvas.show()
00416         elif self.lives == 0:
00417             self.spare.gnomecanvas.hide()
00418         else:
00419             game.loseGame()
00420         self.key_queue = []
00421         game.hide_message()
00422         self.exists = True
00423         self.move(0,0,0,0)
00424         self.anim.gnomecanvas.show()
00425 
00426     def die(self):
00427         super(Muncher, self).die()
00428         self.lives -= 1
00429         self.exists = False
00430         self.anim.gnomecanvas.hide()
00431         self.key_queue = []
00432 
00433     def getEaten(self):
00434         game.show_message( _("You were eaten by a Troggle.\nPress <Return> to continue.") )
00435         self.die()
00436 
00437     def push_key(self, key):
00438         if self.exists:
00439             if self.moving:
00440                 self.key_queue.append(key)
00441             elif len(self.key_queue) == 0:
00442                 self.handle_key(key)
00443             else:
00444                 self.key_queue.append(key)
00445                 self.handle_key(self.key_queue.pop(0))
00446         else:
00447             if key == gtk.keysyms.Return:
00448                 self.spawn()
00449 
00450     def handle_key(self, key):
00451         if key == gtk.keysyms.Left:
00452             if self.x > 0:
00453                 self.move(self.x, self.y, (self.x-1), self.y)
00454             else:
00455                 self.stop()
00456         elif key == gtk.keysyms.Right:
00457             if self.x < game.width - 1:
00458                 self.move(self.x, self.y, (self.x+1), self.y)
00459             else:
00460                 self.stop()
00461         elif key == gtk.keysyms.Up:
00462             if self.y > 0:
00463                 self.move(self.x, self.y, self.x, (self.y-1))
00464             else:
00465                 self.stop()
00466         elif key == gtk.keysyms.Down:
00467             if self.y < game.height - 1:
00468                 self.move(self.x, self.y, self.x, (self.y+1))
00469             else:
00470                 self.stop()
00471         elif key == gtk.keysyms.space:
00472             self.munch()
00473             if len( self.key_queue ) > 0: # we don't need to wait for munching to finish to start the next action
00474                 self.handle_key( self.key_queue.pop(0) )
00475 
00476     def munch(self):
00477         num = game.squares[self.x][self.y].num
00478         if num == None:
00479             return
00480         if num.good:
00481             self.startMunching()
00482         else:
00483             game.show_message( _("You ate a wrong number.\n") +game.levelset.getError(num) +
00484                                _("\nPress <Return> to continue.") )
00485             self.die()
00486         game.setNum(self.x, self.y, None)
00487 
00488     def stop(self):
00489         super(Muncher, self).stop()
00490         if len(self.key_queue) > 0:
00491             key = self.key_queue.pop(0)
00492             self.handle_key(key)
00493 
00494 
00495 class Troggle(Player):
00496     def __init__(self):
00497         super(Troggle, self).__init__()
00498         self.anim = gcompris.anim.CanvasItem(game.munchanimation, game.rootitem)
00499 
00500         self.nextspawn_timer = 0
00501         self.nextmove_timer = 0
00502         self.warn_timer = 0
00503 
00504         self.foodchain = 1
00505 
00506     def spawn(self):
00507         self.nextspawn_timer = 0
00508         self.warn_timer = 0
00509         self.exists = True
00510         index = random.randint(0, (len( game.troganimation )-1) * game.board.sublevel / game.board.number_of_sublevel)
00511         self.anim.swapAnimation(game.troganimation[index])
00512         self.getMove = game.trogmoves[index]
00513         self.onMove = game.onmove[index]
00514         self.onStop = game.onstop[index]
00515         if random.randint(0,1) == 0:
00516             if random.randint(0,1) == 0:
00517                 self.x_old = -1
00518                 self.x = 0
00519             else:
00520                 self.x_old = game.width
00521                 self.x = game.width - 1
00522             self.y = self.y_old = random.randint(0, game.height-1)
00523         else:
00524             if random.randint(0,1) == 0:
00525                 self.y_old = -1
00526                 self.y = 0
00527             else:
00528                 self.y_old = game.height
00529                 self.y = game.height - 1
00530             self.x = self.x_old = random.randint(0, game.width-1)
00531         self.move(self.x_old, self.y_old, self.x, self.y)
00532         self.anim.gnomecanvas.show()
00533         game.hide_trogwarning()
00534 
00535     def die(self):
00536         super(Troggle, self).die()
00537         self.exists = 0
00538         self.anim.gnomecanvas.hide()
00539 
00540         time = game.trog_spawn_time()
00541         self.nextspawn_timer = game.timeout_add( time + game.trogwarn_time, self.spawn )
00542         self.warn_timer = game.timeout_add( time, game.show_trogwarning )
00543         if self.nextmove_timer != 0:
00544             gtk.timeout_remove(self.nextmove_timer)
00545             self.nextmove_timer = 0
00546 
00547     def getEaten(self):
00548         self.die()
00549 
00550     def move(self, a, b, c, d):
00551         if self.onMove != None and (a != c or b != d) and game.onBoard(a, b):
00552             self.x = a
00553             self.y = b
00554             self.onMove(self)
00555         super(Troggle, self).move(a, b, c, d)
00556 
00557     def stop(self):
00558         self.moving = False
00559         if self.x < 0 or self.x >= game.width or self.y < 0 or self.y >= game.height:
00560             self.die()
00561         else:
00562             super(Troggle, self).stop()
00563             self.nextmove_timer = game.timeout_add(game.trog_wait, self.getTrogMove)
00564             if self.onStop != None:
00565                 self.onStop(self)
00566 
00567     def getTrogMove(self):
00568         self.nextmove_timer = 0
00569         x_old = self.x
00570         y_old = self.y
00571         x, y = self.getMove(self)
00572         self.move(x_old, y_old, x, y)
00573 
00574     # the troggle move types
00575     def trogMove_straight(self):
00576         x = self.x + (self.x - self.x_old)
00577         y = self.y + (self.y - self.y_old)
00578         return x, y
00579 
00580     def trogMove_random(self):
00581         x = self.x
00582         y = self.y
00583         r = random.randint(0,3)
00584         if r >= 1: # move straight
00585             x += self.x - self.x_old
00586             y += self.y - self.y_old
00587         elif r == 2: # turn left
00588             x += self.y - self.y_old
00589             y -= self.x - self.x_old
00590         else: # turn right
00591             x -= self.y - self.y_old
00592             y += self.x - self.x_old
00593         return x, y
00594 
00595     def trogMove_chase(self):
00596         x = self.x
00597         y = self.y
00598         dx = game.muncher.x - x
00599         dy = game.muncher.y - y
00600         if dx == 0 and dy == 0:
00601             return self.trogMove_straight()
00602         if not game.muncher.exists or abs(dx) > game.width/2 or abs(dy) > game.height/2:
00603             return self.trogMove_straight()
00604         if abs(dx) > abs(dy) or (abs(dx) == abs(dy) and random.randint(0,1) == 0):
00605             x += dx / abs(dx)
00606         else:
00607             y += dy / abs(dy)
00608         return x, y
00609 
00610     def trogMove_run(self):
00611         x = self.x
00612         y = self.y
00613         dx = game.muncher.x - x
00614         dy = game.muncher.y - y
00615         if not game.muncher.exists or abs(dx) > game.width/2 or abs(dy) > game.height/2:
00616             return self.trogMove_random()
00617         if abs(dx) > abs(dy) or (abs(dx) == abs(dy) and random.randint(0,1) == 0):
00618             x -= dx / abs(dx)
00619         else:
00620             y -= dy / abs(dy)
00621         return x, y
00622 
00623     def onMove_create(self):
00624         game.setNum( self.x, self.y, game.levelset.getNumber() )
00625 
00626     def onStop_munch(self):
00627         if game.squares[self.x][self.y].num != None:
00628             self.startMunching()
00629             game.setNum( self.x, self.y, None )
00630 
00631 class Gcompris_gnumch:
00632     def __init__(self, board):
00633         global game
00634         game = self
00635 
00636         self.board = board
00637         self.board.disable_im_context = True
00638         self.scrw = gcompris.BOARD_WIDTH
00639         self.scrh = gcompris.BOARD_HEIGHT
00640         self.width = 6
00641         self.height = 6
00642 
00643         self.sw = self.scrw / (self.width + 1)
00644         self.sh = self.scrh / (self.width + 1)
00645         self.left = self.scrw - (self.sw * self.width)
00646         self.top = self.scrh - (self.sh * self.height)
00647 
00648         self.munchanimation = gcompris.anim.Animation("gnumch/muncher.txt")
00649         self.troganimation = []
00650         self.trogmoves = [Troggle.trogMove_straight, Troggle.trogMove_random,
00651                           Troggle.trogMove_run, Troggle.trogMove_random,
00652                           Troggle.trogMove_chase]
00653         self.onmove = [ None, Troggle.onMove_create, None, None, None ]
00654         self.onstop = [ None, None, None, Troggle.onStop_munch, None ]
00655         for file in [ "gnumch/reggie.txt",
00656                       "gnumch/diaper.txt",
00657                       "gnumch/fraidy.txt",
00658                       "gnumch/eater.txt",
00659                       "gnumch/smarty.txt" ]:
00660             self.troganimation.append( gcompris.anim.Animation(file) )
00661 
00662         self.goodies = 0
00663         self.paused = 0
00664         self.stopped = 0
00665         if board.mode == "primes":
00666             self.levelset = PrimeLevelset()
00667         elif board.mode == "factors":
00668             self.levelset = FactorLevelset()
00669         elif board.mode == "multiples":
00670             self.levelset = MultipleLevelset()
00671         elif board.mode == "equality":
00672             self.levelset = EqualityLevelset()
00673         elif board.mode == "inequality":
00674             self.levelset = InequalityLevelset()
00675         else:
00676             print "Warning: no levelset type specified, defaulting to primes"
00677             self.levelset = PrimeLevelset()
00678 
00679         print "Gcompris_gnumch __init__."
00680 
00681         # config options
00682         self.move_tick = 30
00683         self.num_moveticks = 10
00684         self.munch_time = 400
00685         self.trog_wait = 1000
00686         self.trogwarn_time = 1000
00687         self.trogspawn_min = 3000
00688         self.trogspawn_max = 10000
00689 
00690     def start(self):
00691         self.board.level = 1
00692         self.board.maxlevel = self.levelset.numlevels
00693         self.board.sublevel = 1
00694         self.board.number_of_sublevel = self.levelset.num_sublevels
00695         self.trog_wait = 1900
00696 
00697         gcompris.bar_set(0)
00698         gcompris.set_background(self.board.canvas.root(), gcompris.skin.image_to_skin("gcompris-bg.jpg"))
00699         gcompris.bar_set_level(self.board)
00700 
00701         pixmap = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin("button_reload.png"))
00702         if(pixmap):
00703             gcompris.bar_set_repeat_icon(pixmap)
00704             gcompris.bar_set(gcompris.BAR_LEVEL | gcompris.BAR_REPEAT_ICON)
00705         else:
00706             gcompris.bar_set(gcompris.BAR_LEVEL | gcompris.BAR_REPEAT)
00707 
00708         # create our rootitem. We put each canvas item here so at the end we only
00709         # need to destroy the rootitem
00710         self.rootitem = self.board.canvas.root().add(gnomecanvas.CanvasGroup,
00711                                                      x=0.0,
00712                                                      y=0.0)
00713 
00714         # draw the board on top of the background
00715         for i in range(0,self.width+1):
00716             self.rootitem.add(gnomecanvas.CanvasLine,
00717                               points = (i*self.sw + self.left, self.top,
00718                                         i*self.sw + self.left, self.scrh),
00719                               fill_color_rgba = 0x000000FFL,
00720                               width_units = 3.0)
00721         for i in range(0,self.height+1):
00722             self.rootitem.add(gnomecanvas.CanvasLine,
00723                               points = (self.left, self.top + i*self.sh,
00724                                         self.scrw, self.top + i*self.sh),
00725                               fill_color_rgba = 0x000000FFL,
00726                               width_units = 3.0)
00727 
00728         # munchers and troggles
00729         self.players = []
00730         self.muncher = Muncher()
00731         self.troggles = [Troggle(), Troggle(), Troggle()]
00732 
00733         self.players[:] = self.troggles
00734         self.players.append(self.muncher)
00735 
00736         # create the squares
00737         self.squares = []
00738         for i in range(0, self.width):
00739             tmp = []
00740             for j in range(0, self.height):
00741                 s = Square(self.left + self.sw*i + self.sw/2, self.top + self.sh*j + self.sh/2)
00742                 s.pic.raise_to_top()
00743                 tmp.append( s )
00744             self.squares.append(tmp)
00745 
00746         # so that the troggles get clipped to the board area
00747         self.rootitem.add(gnomecanvas.CanvasRect,
00748                           x1=0, y1=0,
00749                           x2=self.scrw, y2=self.top,
00750                           fill_color_rgba = 0xFFFFFFFFL)
00751         self.rootitem.add(gnomecanvas.CanvasRect,
00752                           x1=0, y1=0,
00753                           x2=self.left, y2=self.scrh,
00754                           fill_color_rgba = 0xFFFFFFFFL)
00755 
00756         # the board title
00757         self.title = self.rootitem.add(gnomecanvas.CanvasText,
00758                                        text = "",
00759                                        font = gcompris.skin.get_font("gcompris/board/huge bold"),
00760                                        x = self.scrw/2,
00761                                        y = self.top/2)
00762 
00763         # the message
00764         self.message_back = self.rootitem.add(gnomecanvas.CanvasRect,
00765                                         x1=0, y1=0, x2=1, y2=1,
00766                                         fill_color_rgba = 0x60F06060L)
00767         self.message = self.rootitem.add(gnomecanvas.CanvasText,
00768                                         text = "",
00769                                         justification = gtk.JUSTIFY_CENTER,
00770                                         font = gcompris.skin.get_font("gcompris/board/huge bold"),
00771                                         x = self.scrw/2,
00772                                         y = self.scrh/2)
00773         self.message.hide()
00774 
00775         # the trogwarning
00776         self.trogwarning = self.rootitem.add(gnomecanvas.CanvasText,
00777                                         text = _("T\nR\nO\nG\nG\nL\nE"),
00778                                         justification = gtk.JUSTIFY_CENTER,
00779                                         font = gcompris.skin.get_font("gcompris/board/huge bold"),
00780                                         x = self.left/2,
00781                                         y = self.scrh/2)
00782         self.trogwarning.hide()
00783         self.trogwarning_num = 0
00784 
00785         # the spare life
00786         self.muncher.spare.gnomecanvas.raise_to_top()
00787 
00788         self.startGame()
00789 
00790     def show_trogwarning(self):
00791         self.trogwarning_num += 1
00792         if self.trogwarning_num == 1:
00793             self.trogwarning.show()
00794 
00795     def hide_trogwarning(self):
00796         self.trogwarning_num -= 1
00797         if self.trogwarning_num == 0:
00798             self.trogwarning.hide()
00799 
00800     def show_message(self, text):
00801         self.message.set( text = text )
00802         w = self.message.get_property("text-width")
00803         h = self.message.get_property("text-height")
00804         self.message_back.set( x1 = (self.scrw - w)/2, y1 = (self.scrh - h)/2,
00805                                x2 = (self.scrw + w)/2, y2 = (self.scrh + h)/2 )
00806         self.message_back.show()
00807         self.message.show()
00808 
00809     def hide_message(self):
00810         self.message.hide()
00811         self.message_back.hide()
00812 
00813     def set_level(self, level):
00814         self.board.level = level;
00815         self.board.sublevel = 1;
00816         gcompris.bar_set_level(self.board);
00817         self.trog_wait = 2000 - self.board.level*100
00818         self.stopGame()
00819         self.startGame()
00820 
00821     def trog_spawn_time(self):
00822         return random.randint(self.trogspawn_min, self.trogspawn_max)
00823 
00824     def setNum(self, x, y, new):
00825         change = 0
00826         if new == None or not new.good:
00827             change -= 1
00828         else:
00829             change += 1
00830         if self.squares[x][y].num == None or not self.squares[x][y].num.good:
00831             change += 1
00832         else:
00833             change -= 1
00834 
00835         change /= 2
00836         self.goodies += change
00837         if self.goodies == 0:
00838             self.winGame()
00839         self.squares[x][y].setNum(new)
00840 
00841     def key_press(self, keyval, commit_str, preedit_str):
00842         self.muncher.push_key(keyval)
00843         return True
00844 
00845     def stopGame(self):
00846         self.stopped = 1
00847         if self.muncher.munch_timer != 0:
00848             gtk.timeout_remove(self.muncher.munch_timer)
00849         if self.muncher.movestep_timer != 0:
00850             gtk.timeout_remove(self.muncher.movestep_timer)
00851 
00852         for t in self.troggles:
00853             for timer in [t.munch_timer, t.movestep_timer, t.nextmove_timer, t.nextspawn_timer, t.warn_timer]:
00854                 if timer != 0:
00855                     gtk.timeout_remove(timer)
00856 
00857     def startGame(self):
00858         self.stopped = 0
00859         self.levelset.setLevel(self.board.level, self.board.sublevel)
00860         self.title.set(text = self.levelset.getTitle())
00861         self.trogwarning_num = 1
00862         self.hide_trogwarning()
00863         self.goodies = 0
00864         self.won_level = 0
00865         for col in self.squares:
00866             for s in col:
00867                 s.setNum( self.levelset.getNumber() )
00868                 if s.num.good:
00869                     self.goodies += 1
00870 
00871         self.muncher.lives = 1
00872         for i in range(0, len(self.troggles)):
00873             if i < self.board.sublevel-1:
00874                 self.troggles[i].die()
00875             else: # don't move them into the spawning queue
00876                 self.troggles[i].exists = 0
00877                 self.troggles[i].anim.gnomecanvas.hide()
00878         self.muncher.spawn()
00879 
00880     def winGame(self):
00881         self.stopGame()
00882         self.won_level = 1
00883         gcompris.bonus.display(gcompris.bonus.WIN, gcompris.bonus.TUX)
00884 
00885     def loseGame(self):
00886         self.stopGame()
00887         gcompris.bonus.display(gcompris.bonus.LOOSE, gcompris.bonus.TUX)
00888 
00889     def onBoard(self, x, y):
00890         return x >= 0 and x < self.width and y >= 0 and y < self.height
00891 
00892     def pause(self, p):
00893         self.paused = p
00894 
00895         if p == 0:
00896             if self.won_level:
00897                 # if we are paused, then unpaused it means that they beat the sublevel
00898                 self.increment_level()
00899             else:
00900                 self.set_level(self.board.level)
00901 
00902     def increment_level(self):
00903         self.board.sublevel += 1
00904         if self.board.sublevel > self.board.number_of_sublevel:
00905             self.set_level( self.board.level % self.board.maxlevel + 1)
00906         else:
00907             self.startGame();
00908 
00909 
00910     def repeat(self):
00911         self.stopGame()
00912         self.startGame()
00913 
00914     def timeout_add(self, t, fn):
00915         if not self.paused and not self.stopped:
00916             return gtk.timeout_add(t, fn)
00917         else:
00918             return 0
00919 
00920     def end(self):
00921         for i in range(0, len(self.troggles)):
00922             self.troggles[i].anim.destroy()
00923         if self.muncher.anim:
00924             self.muncher.anim.destroy()
00925         self.stopGame()
00926         self.rootitem.destroy()