#~ /*************************************************************************** #~ * packid.py #~ * #~ * Fri Oct 22 09:10:15 2004 #~ * Copyright 2004 Stas #~ * stas@linux.isbeter.nl #~ ****************************************************************************/ # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, write to the Free Software Foundation, # 675 Mass Ave, Cambridge, MA 02139, USA. # RCFILE = 0 import os,random,operator,string,locale,sys import pygame from pygame.constants import * from utils import load_image,load_sound,load_music,MyError,font2surf,MazeGen,\ trace_error,get_files,ChildsplayGoodies from CPConstants import DATADIR #0= wall 1=go #matrix = ((0,0,0,0,0),\ # (0,1,0,1,0),\ # (0,1,0,1,0),\ # (0,1,1,1,0),\ # (0,0,0,0,0)) class Img: pass class Snd: pass class PacKid: def __init__(self,matrix,startx,starty,speed=24): self.speed = speed self.pac_r = Img.pac_r self.pac_r_c = Img.pac_r_c self.pac_l = Img.pac_l self.pac_l_c = Img.pac_l_c self.pac_u = Img.pac_u self.pac_u_c = Img.pac_u_c self.pac_d = Img.pac_d self.pac_d_c = Img.pac_d_c self.pac_smile = Img.pac_smile self.img = self.pac_smile self.rect = self.img.get_rect() self.rect.move_ip(startx,starty) self.startrect = self.img.get_rect() self.startrect.move_ip(startx,starty) self.matrix = matrix self.row = 1 self.col = 1 self.dir_dic = {'UP':self._up,'DOWN':self._down,'LEFT':self._left,'RIGHT':self._right} def update(self,direc): Snd.waka.play() apply(self.dir_dic[direc]) return (self.row,self.col) #print self.row,self.col,self.rect def _up(self): if self.img == self.pac_u_c: self.img = self.pac_u else: self.img = self.pac_u_c if self.matrix[self.row-1][self.col]: # can we go here? self.row -= 1 self.rect.move_ip(0,-self.speed) def _down(self): if self.img == self.pac_d_c: self.img = self.pac_d else: self.img = self.pac_d_c if self.matrix[self.row+1][self.col]: # can we go here? self.row += 1 self.rect.move_ip(0,self.speed) def _left(self): if self.img == self.pac_l_c: self.img = self.pac_l else: self.img = self.pac_l_c if self.matrix[self.row][self.col-1]: # can we go here? self.col -= 1 self.rect.move_ip(-self.speed,0) def _right(self): if self.img == self.pac_r_c: self.img = self.pac_r else: self.img = self.pac_r_c if self.matrix[self.row][self.col+1]: # can we go here? self.col += 1 self.rect.move_ip(self.speed,0) class Memory: def __init__(self,points=('1','2','3','4','5')): self.memory = {} self.stack = [None]* len(points) def remember(self,(key,item)): #print key,item self.memory[key] = item self.stack.insert(0,key) try: del self.memory[self.stack.pop()] except KeyError: pass def recall(self,key): if self.memory.has_key(key): return self.memory[key] class Letters(Memory): instance = 0 def __init__(self,char,fcol,dest,ttf): Letters.instance += 2 self.wait = Letters.instance Memory.__init__(self) self.char = char self.img,spam = font2surf(char,28,fcol,ttf) self.rect = self.img.get_rect() self.lifetime = 50 # if we get in a loop # calculate the pix pos row*24+60, col*24+150 self.row = 8 self.col = 12 self.rect.move_ip(self.col*24+150,self.row*24+60) self.dest = dest self.old_move = None #self.org = (self.row,self.col) #self.vector = self.dest[0]-self.org[0],self.dest[1]-self.org[1] self.matrix = Img.matrix self.speed = 24 def update(self): if self.wait: self.wait -= 1 return 0 self.lifetime -= 1 if self.lifetime < 0: print 'died' return 1 # he died :-( self.org = (self.row,self.col) stop = self._iq((self.dest[0]-self.org[0],self.dest[1]-self.org[1])) return stop def _iq(self,vector): direcs = self._where_to_go() if (self.row,self.col) == (8,12):# still in the box direcs = ['u'] if len(direcs) == 1: self._move(direcs[0]) stop = self.dest == (self.row,self.col) return stop else: try: direcs.remove(self.old_move) except ValueError: pass if len(direcs) == 1: self._move(direcs[0]) stop = self.dest == (self.row,self.col) return stop else: prior = [None]*4 #print vector if abs(vector[0]) > abs(vector[1]): # y is pref. if vector[0] < 0: prior[0] = 'u' prior[3] = 'd' else: prior[0] = 'd' prior[3] = 'u' if vector[1] < 0: prior[1] = 'l' prior[2] = 'r' else: prior[1] = 'r' prior[2] = 'l' else: if vector[1] < 0: prior[0] = 'l' prior[3] = 'r' else: prior[0] = 'r' prior[3] = 'l' if vector[0] < 0: prior[1] = 'u' prior[2] = 'd' else: prior[1] = 'd' prior[2] = 'u' #print 'direcs',direcs,'prior',prior,'row/col',self.row,self.col for d in prior: if d in direcs: if self.recall((self.row,self.col)) == d: #print self.recall((self.row,self.col)) d = random.choice(direcs) self._move(d) break else: #print d self.remember(((self.row,self.col),d)) self._move(d) break stop = self.dest == (self.row,self.col) #print 'stop-iq',stop return stop def _where_to_go(self): direcs = [] if self.matrix[self.row-1][self.col]: # up direcs.append(('u')) if self.matrix[self.row+1][self.col]: # down direcs.append(('d')) if self.matrix[self.row][self.col-1]: # left direcs.append(('l')) if self.matrix[self.row][self.col+1]: # right direcs.append(('r')) return direcs def _move(self,choice): if choice == 'u': self.row -= 1 self.rect.move_ip(0,-self.speed) self.old_move = 'd' elif choice == 'd': self.row += 1 self.rect.move_ip(0,self.speed) self.old_move = 'u' elif choice == 'l': self.col -= 1 self.rect.move_ip(-self.speed,0) self.old_move = 'r' elif choice == 'r': self.col += 1 self.rect.move_ip(self.speed,0) self.old_move = 'l' return class Word: """ Highlight the letter in the word""" def __init__(self,word,ttf): self.chars = word self.ttf = ttf self.index = -1 self.fsize = 32 self.fcol = (255,255,51) self.hfcol = (255,0,0) def update(self): if len(self.chars) > self.index: self.index += 1 else: return self.surf #self.letter_to_speak = self.chars[self.index] return self._render(),self.chars[self.index] def _render(self): #print self.index high_letter,size1 = font2surf(self.chars[self.index],self.fsize,self.hfcol,self.ttf,1) if self.index == 0 or self.index == len(self.chars)-1: if self.index == 0: split = self.chars[1:] else: split = self.chars[:-1] word,size2 = font2surf(split,self.fsize,self.fcol,self.ttf,1) surfsize = (size1[0]+size2[0],size1[1]) surf = pygame.Surface(surfsize).convert() if self.index != 0:# blit first word and then the highlight (last letter) surf.blit(word,(0,0)) surf.blit(high_letter,(size2[0],0)) else:# first letter is highlight surf.blit(high_letter,(0,0)) surf.blit(word,(size1[0],0)) else: # word must be split word,size1a = font2surf(self.chars[:self.index],self.fsize,self.fcol,self.ttf,1) word1,size1b = font2surf(self.chars[self.index+1:],self.fsize,self.fcol,self.ttf,1) surfsize = (size1[0]+size1a[0]+size1b[0],size1[1]) surf = pygame.Surface(surfsize).convert() surf.blit(word,(0,0)) surf.blit(high_letter,(size1a[0],0)) surf.blit(word1,(size1[0]+size1a[0],0)) self.surf = surf # see self.update return self.surf class SidePanel: def __init__(self,levels,ttf): self.ttf = ttf self.surf = pygame.Surface((130,400)).convert() pygame.draw.rect(self.surf,(42,191,44),self.surf.get_rect(),6) self.when = 9# number of gooditems to play last level self.x = 30 self.y = 6 self.fsize = 24 self.fcol = (255,255,51) for l in levels[:-1]: # The last level is different self._set_level("Level "+str(l+1)+":") self.y += len(levels)*self.fsize self.lastlevel_y = self.y self.fcol = (186,184,154) self._set_last_level() self.fcol = (255,255,51) self.y = 12 self.goodimg = pygame.transform.scale(Img.pac_smile,(12,12)) self.wrongimg = pygame.transform.scale(Img.pac_sad,(12,12)) self.gooditems = 0 self.count = 0 def _set_last_level(self): oldy = self.y self.y = self.lastlevel_y self._set_level("Level "+str(4)+":")# XXX set dynamic level self.y = oldy def _set_level(self,level): levelimg,spam = font2surf(level,self.fsize+8,self.fcol,self.ttf,1) #self.y += spam[1] self.surf.blit(levelimg,(self.x-12,self.y)) def set_word(self,word): if self.count == 3: self.y += 32 self.count = 0 self.count += 1 wordimg,spam = font2surf(word,self.fsize,self.fcol,self.ttf,1) self.y += spam[1] self.surf.blit(wordimg,(self.x,self.y)) def set_good_wrong(self,gw): if gw > 2: self.surf.blit(self.wrongimg,(self.x-16,self.y+4)) else: self.surf.blit(self.goodimg,(self.x-16,self.y+4)) self.gooditems += 1 def get_surf(self): return self.surf.convert() def get_level_score(self): #print 'gooditems',self.gooditems if self.gooditems >= self.when: self.when = 0 self._set_last_level() return 1 else: return 0 class LastLevelImg: def __init__(self,music,img,pos): self.char = 'FRUIT' # to be compatible with the letters class in the loop self.file = os.path.join(Img.libdir,'PackidData',music) self.img = load_image(os.path.join(Img.libdir,'PackidData',img),1) self.pos = pos self.rect = self.img.get_rect() # calculate the pix pos row*24+60, col*24+150 self.row,self.col = pos self.rect.move_ip(self.col*24+150,self.row*24+60) def eat(self): load_music(self.file).play() return 25 #score class Game(Img,Snd): """ packid.py - part of childsplay.py, a suite of educational games for young children.""" def __init__(self,screen,backgr,rcdic,basepath,libdir): Img.screen = screen Img.libdir = libdir self.screen = screen self.level = 1 self.gamelevels = range(4) self.gameitems = range(3) self.rcdic = rcdic self.basedir = basepath self.libdir = libdir self.ttf = os.path.join(DATADIR,'bluehigh.ttf') self.stop = 0 self.stopflag = None# used to stop the loop when not level 4 self.score = 0 self.letters_spots = [] self._setup() def __del__(self): #print 'Reached del' try: Snd.walk.stop() except: pass def _setup(self): Snd.waka = load_sound(os.path.join(self.libdir,'PackidData','waka.wav')) Snd.walk = load_sound(os.path.join(self.libdir,'PackidData','walk.wav')) Snd.finlevel = load_sound(os.path.join(self.libdir,'PackidData','finlevel.wav')) Snd.bummer = load_sound(os.path.join(DATADIR,'bummer.wav')) Snd.eat = load_sound(os.path.join(self.libdir,'PackidData','eat.wav')) Img.pac_u = load_image(os.path.join(self.libdir,'PackidData','pac_u.png'),1) Img.pac_u_c = load_image(os.path.join(self.libdir,'PackidData','pac_u_c.png'),1) Img.pac_d = load_image(os.path.join(self.libdir,'PackidData','pac_d.png'),1) Img.pac_d_c = load_image(os.path.join(self.libdir,'PackidData','pac_d_c.png'),1) Img.pac_l = load_image(os.path.join(self.libdir,'PackidData','pac_l.png'),1) Img.pac_l_c = load_image(os.path.join(self.libdir,'PackidData','pac_l_c.png'),1) Img.pac_r = load_image(os.path.join(self.libdir,'PackidData','pac_r.png'),1) Img.pac_r_c = load_image(os.path.join(self.libdir,'PackidData','pac_r_c.png'),1) Img.pac_smile = load_image(os.path.join(self.libdir,'PackidData','pac_smile.png'),0) Img.pac_sad = load_image(os.path.join(self.libdir,'PackidData','pac_sad.png'),0) self.sidepan = SidePanel(self.gamelevels,self.ttf) # test if we have a words list for the locale try: #loc = locale.setlocale(locale.LC_ALL,'') wordsloc = ChildsplayGoodies.language wordlist = 'words-'+wordsloc if not os.path.exists(os.path.join(self.libdir,'PackidData',wordlist)): raise IndexError except IndexError,info: print >> sys.stderr,info,"\n Can't find words for locale",wordsloc,"\n Using english for the words and sounds." wordlist = 'words-en' wordsloc = 'en' try: f = open(os.path.join(self.libdir,'PackidData',wordlist)) items = f.readlines() f.close() except IOError,info: print 'can\'t open/read words file' trace_error() MyError.name = 'Module packid.py' MyError.line = 'IOError in Game.start, words file' raise MyError self.words = map(string.upper,filter(None,map(operator.getslice,items,(0,)*len(items),(-1,)*len(items)))) #print self.words # Look for alphabet sounds for this locale, if not we check for english. # If all fails we fall back to the "old" wahoo sound. alphabetdir = os.path.join(DATADIR,'AlphabetSounds',wordsloc) if not os.path.exists(alphabetdir): print >> sys.stderr, "Can't find",alphabetdir alphabetdir = os.path.join(DATADIR,'AlphabetSounds','en') print >> sys.stderr, "Trying, english sounds" if not os.path.exists(alphabetdir): print >> sys.stderr, "Can't find",alphabetdir print >> sys.stderr, "Giving up, using simple wahoo sound." alphabetdir = None self.alphabetdir = alphabetdir def _setup_last_level(self): self.fruits,spots = [],[] self.letters_spots = []# use this again for the fruits music = ['pac1.ogg','pac2.ogg','pac3.ogg','pac4.ogg','pac5.ogg','pac6.ogg'] random.shuffle(music) for file in ('kers.png','banaan.png','aardbei.png','citroen.png','appel.png','peer.png'): spot = self._rand_spot() if spot in spots or spot in ((1,1),(15,23),(15,24),(16,23),(16,24)): spot = self._rand_spot() spots.append((spot)) self.letters_spots.append((spot)) self.fruits.append((LastLevelImg(music.pop(),file,spot))) self.letters_spots.append(((15,23))) def start(self,level,i): """""" #print "Begin",self pygame.display.update(self.screen.fill((0,0,0))) self.level = level if level == 0: self.brick = load_image(os.path.join(self.libdir,'PackidData','brick.png'),0) matrix = self._read_grid('grid0.txt') self._build_field(matrix) elif level == 1: self.brick = load_image(os.path.join(self.libdir,'PackidData','leafs.png'),0) matrix = self._read_grid('grid1.txt') self._build_field(matrix,1) elif level == 2: self.brick = load_image(os.path.join(self.libdir,'PackidData','sea.png'),0) matrix = self._read_grid('grid2.txt') self._build_field(matrix,1) elif level == 3 and i < 1: if self.sidepan.get_level_score(): load_music(os.path.join(self.libdir,'PackidData','feelgood.ogg')).play() self.brick = load_image(os.path.join(self.libdir,'PackidData','camo.png'),0) m = MazeGen(17,25) matrix = m.get_maze() #matrix = self._read_grid('grid3.txt') self._build_field(matrix) self._setup_last_level() else: self.stopflag = 1# stop loop return else: self.stopflag = 1 return Img.backgr = self.screen.convert() Img.backgr_start = self.screen.convert()# keep a begin situation surface txt = _("Find all the letters in the right order. The word to find is: ") # now comes a horrible hack to split the line in two without adjusting the po files line1,line2 = txt.split('.') if level != 3: surf,spam = font2surf(line1,32,(183,255,50),self.ttf,1) pygame.display.update(self.screen.blit(surf,(28,4))) surf,self.surfword_offset = font2surf(line2,32,(183,255,50),self.ttf,1) pygame.display.update(self.screen.blit(surf,(8,30))) word = random.choice(self.words) # end of horrible hack # start the show and set some initial values self.gw = 0 #good/wrong counter if level == 3: self.letter_objs = [] for obj in self.fruits: pygame.display.update(self.screen.blit(obj.img,obj.rect)) self.letter_objs.append((obj)) r = self.screen.blit(self.packid.img,(174,84))# blit packid pygame.display.update(r) PLAYWALK = 0 else: self._start_letters(word) self.sidepan.set_word(word) self._update_sidepanel() PLAYWALK = 1 self.surfword = Word(word,self.ttf) self._update_surfword() x = 174 y = 84 self.packid = PacKid(matrix,x,y,24) #print self.packid # test #print self self.pack_pos = (self.packid.row,self.packid.col)# start postion if PLAYWALK: Snd.walk.play(-1)# stop when the letters are stopped (in def _move_objs) def _update_surfword(self): surf, letter_to_speak = self.surfword.update() pygame.display.update(self.screen.blit(surf,(8+self.surfword_offset[0],30))) pygame.time.wait(1000) #self._speak_letter(letter_to_speak.lower()) def _speak_letter(self,letter): """Not used, maybe removed in the future.""" return 0 file = os.path.join(self.libdir,'Speech',letter+'.wav') data = load_sound(file) data.play() def _update_sidepanel(self): pygame.display.update(self.screen.blit(self.sidepan.get_surf(),(0,60))) def _read_grid(self,name): grid = [] filename = os.path.join(self.libdir,'PackidData',name) f = open(filename,'r') for line in f.readlines(): grid.append((tuple(map(int,line[:-1])))) f.close() return tuple(grid) def _build_field(self,matrix,ran=0): Img.matrix = matrix #stash ref surf = pygame.Surface((620,428)) y = 10 for row in matrix: x = 10 for item in row: if not item: if ran: brick = self.brick.convert() angle = random.choice((0,90,180,270)) surf.blit(pygame.transform.rotate(brick,angle),(x,y)) else: surf.blit(self.brick,(x,y)) elif item == 2:# special i = load_image(os.path.join(self.libdir,'PackidData','exit.png'),0) #image = 48x48 #x -= 24 #y -= 24 surf.blit(i,(x,y)) break x +=24 y += 24 pygame.display.update(self.screen.blit(surf,(140,50))) def _start_letters(self,word): self.word,spots = word,[] self.letter_to_find = 0 self.objs_to_move,self.letter_objs = [],[]# lists to hold Letters objects Letters.instance = 0 #reset instance counter for item in word: spot = self._rand_spot() if spot in spots or spot in ((8,11),(8,12),(8,13),(1,1)):# same coords or inside the box,packid spot = self._rand_spot() spots.append((spot)) self.objs_to_move.append((Letters(item,(255,255,255),spot,self.ttf))) #print item,spot,Img.matrix[spot[0]][spot[1]] def _rand_spot(self): #return (1,3) while 1: spot = (random.randrange(1,17),random.randrange(1,24)) if Img.matrix[spot[0]][spot[1]]: break return spot def loop(self,events): if self.stopflag: return 1,0 #no last level, stop play. This is special stop see childsplay.contr self.stop = 0 self.score = 0 for event in events: pos = pygame.Rect(pygame.mouse.get_pos() + (4,4)) if event.type is KEYDOWN and not self.objs_to_move: key = self._on_keypress(event.key) if key: r = self.screen.blit(Img.backgr,self.packid.rect.inflate(2,2),self.packid.rect.inflate(2,2)) self.pack_pos = self.packid.update(key) rr = self.screen.blit(self.packid.img,self.packid.rect) pygame.display.update((r,rr)) if self.objs_to_move: # Are there letters to move? self._move_objs() elif self.pack_pos in self.letters_spots: # Have we hit a letter? if self.level == 3 and self.pack_pos == (15,23):# pac hit exit in last level #self._end_level() self.stop = -1 else: for item in self.letter_objs: if self.pack_pos == (item.row,item.col): if item.char == 'FRUIT': self.score = item.eat() self.letter_objs.remove(item) pygame.display.update(self.screen.blit(Img.backgr_start,item.rect,item.rect))# erase fruit self.packid.img = Img.pac_smile pygame.display.update(self.screen.blit(Img.pac_smile,self.packid.rect)) else: self._check_letter(item) break return self.stop,self.score def _on_keypress(self,key): if key == K_UP: return 'UP' elif key == K_DOWN: return 'DOWN' elif key == K_LEFT: return 'LEFT' elif key == K_RIGHT: return 'RIGHT' def _move_objs(self): objs_to_remove,dirty_rects = [],[] for obj in self.objs_to_move: dirty_rects.append((self.screen.blit(Img.backgr,obj.rect.inflate(2,2),obj.rect.inflate(2,2)))) flag = obj.update() if flag: rec = obj.rect rec.move_ip(4,0) Img.backgr.blit(obj.img,rec) objs_to_remove.append((obj)) self.letter_objs.append((obj)) # store letters objs # store the pos of the letter, used to check collide with packid # take these positions because a obj maybe died (look in class) self.letters_spots.append(((obj.row,obj.col))) dirty_rects.append((self.screen.blit(obj.img,obj.rect))) pygame.time.wait(100) pygame.display.update(dirty_rects) if objs_to_remove: for item in objs_to_remove: self.objs_to_move.remove(item) if self.objs_to_move == []: Snd.walk.stop() r= self.screen.blit(self.packid.img,(174,84))# blit packid pygame.display.update(r) def _check_letter(self,obj): if obj.char == self.word[self.letter_to_find]: # Good letter #print 'letter',obj.char self.letter_objs.remove(obj) Img.backgr.blit(Img.backgr_start,obj.rect,obj.rect)# erase letter self.packid.img = Img.pac_smile pygame.display.update(self.screen.blit(Img.pac_smile,self.packid.rect)) if self.alphabetdir: try: char = obj.char.lower()+'.ogg' pygame.time.wait(500) load_music(os.path.join(self.alphabetdir,char)).play() except Exception,info: print >> sys.stderr,"error while trying to play alphabet sounds",info Snd.eat.play() pygame.time.wait(500) self.score = 10 self.letters_spots.remove(self.pack_pos) #print self.letters_spots if self.letters_spots: self.letter_to_find += 1 else: # last letter Snd.finlevel.play() self.score = 100 self.stop = -1# level clear Img.backgr.blit(Img.backgr_start,obj.rect,obj.rect)# erase letter self.sidepan.set_good_wrong(self.gw) self._update_sidepanel() pygame.time.wait(2000) return self._update_surfword() else: #wrong letter if self.packid.img is Img.pac_sad: return # still on the wrong letter self.gw += 1 Snd.bummer.play() self.packid.img = Img.pac_sad pygame.display.update(self.screen.blit(Img.pac_sad,self.packid.rect)) pygame.time.wait(500) def __str__(self): """Must return the original, not translated, title of this game. It's needed by the high score class of childsplay.""" return "Packid" def helptitle(self): return "Packid" def help(self): txt=[_("The aim of the game:"), _("Try to 'eat' all the letters in the appropriated order."), " ", _("Difficulty : 4+ years."), " ", _("Number of levels : 3"), " ", _("There are thee levels with three words each."), _("When you have finished all the levels, without making to much mistakes"), _("(max two per word), you can play the last level which is a maze."), _("Try to find the way out while eating the fruits for extra points and"), _("funny sounds :-)")] return txt