Paul Duncan
Paul Duncan

Reputation: 322

Python TypeError Passing Class vars?

So I'm trying to pass vars from one class to another. I want the Map class to receive the vars from the Game class. When I run it I get an error:

self.width      = w*self.multi 
TypeError: Error when calling the metaclass bases
can't multiply sequence by non-int of type 'dict'

How can I fix this, or should I be going about this differently?

import pygame, os
from pygame.locals import *
from pygame import Color
from time import time
from datetime import datetime




class Game():
    """ Lets try to get this going by simple steps
    One by one. First step, lets figure how to make a class
    that can do the display stuff. NOTE TO SELF: Remember, these
    are only called ONCE at the start. Lord have mercy on my soul"""
    def __init__(self, w=256, h=224, multi=3):
        """Initialization"""
        pygame.init()
        self.multi      = multi
        self.runGame    = True
        self.width      = w*self.multi
        self.height     = h*self.multi
        self.sprSz      = 16*self.multi
        self.clock      = pygame.time.Clock()
        self.screen     = pygame.display.set_mode((self.width, self.height))
        self.kl         = []
        self.walk       = [0, 0]
        self.speed      = self.multi*1.5
        self.x,self.y   = (self.width/2-(self.sprSz/2)), (self.height/2-(self.sprSz/2))
        self.music      = self.sndLoad('relent.ogg')
        self.playerSpr  = self.imgLoad('link1.png', self.multi, 0, 0)
        self.playerRec  = Rect(self.x,self.y,self.sprSz,self.sprSz)
        self.aSprite    = self.imgLoad('greenwall_01.png', self.multi, 0, 0)
        self.aSpriteRec = Rect(self.aSprite.get_rect())


    def imgLoad(self, image, size, flipx, flipy):
        try:
            self.img=pygame.image.load('images/'+image).convert_alpha()
        except pygame.error, message:
            print "Unable to load image: " + image
            raise SystemExit, message               
        if size>1:
            self.img=pygame.transform.scale(self.img, (self.img.get_width()*size, self.img.get_height()*size))
        if flipx==1:
            self.img=pygame.transform.flip(self.img, True, False)
        if flipy==1:
            self.img=pygame.transform.flip(self.img, False, True)
        if flipy>1 or flipy<0:
            self.img=pygame.transform.rotate(self.img, flipy)        
        return self.img


    def sndLoad(self, sound):
        try:
            self.sound = pygame.mixer.Sound('sounds/'+sound)
        except pygame.error, message:
            print "Cannot load sound: " + sound
            raise SystemExit, message
        return self.sound    


    def mainLoop(self):
        """Loop through the main game routines
        1. Drawing  2. Input handling  3. Updating
        Then loop through it until user quits"""
        self.music.play()
        self.music.set_volume(0.01)
        while self.runGame:
            self.clock.tick(160)
            self.events()
            self.draw()


    def events(self):
        """Time to handle some events"""
        for e in pygame.event.get():
            if (e.type == pygame.QUIT) or (e.type == KEYDOWN and e.key == K_ESCAPE):
                self.runGame = False
                break
            if e.type == KEYDOWN and e.key == K_PRINT:
                self.screenShot()
            if e.type==KEYDOWN:    
                if e.key==pygame.K_a: self.kl.append(1)
                if e.key==pygame.K_d: self.kl.append(2)
                if e.key==pygame.K_w: self.kl.append(3)
                if e.key==pygame.K_s: self.kl.append(4)             
            if e.type==pygame.KEYUP:
                if e.key==pygame.K_a: self.kl.remove(1)            
                if e.key==pygame.K_d: self.kl.remove(2)
                if e.key==pygame.K_w: self.kl.remove(3)             
                if e.key==pygame.K_s: self.kl.remove(4)

            if   self.kl[-1:]==[1]: self.walk=[-self.speed, 0]
            elif self.kl[-1:]==[2]: self.walk=[ self.speed, 0]
            elif self.kl[-1:]==[3]: self.walk=[0,-self.speed]
            elif self.kl[-1:]==[4]: self.walk=[0, self.speed]
            else:                   self.walk=[0, 0]

        self.playerRec.move_ip(*self.walk)              # instead of self.x+=self.walk[0] / self.y+=self.walk[1]
        self.playerRec.clamp_ip(self.screen.get_rect()) # probably do this right after 'move_ip'


    def screenShot(self):
        """Lets make a folder if it doesnt exist for screenshots
        Then lets name teh screenshot something useful and unique"""
        if not os.path.exists('screenshots'):
            os.makedirs('screenshots')
        t = datetime.now()
        pygame.image.save(self.screen, ('screenshots/'+str(t.strftime("%a-%d-%b-%Y-%H.%M.%S_%f"))+'.png'))


    def idk(self):
        pygame.sprite.collide_rect(left, right)

    def draw(self):
        """Draw and update the main screen. Sacrifice virgins to the
        unholy prankster god of programming and cross fingers"""
        pygame.display.set_caption('Grid2. FPS: '+str(round(self.clock.get_fps(), 1)))
        back = self.screen.fill(Color('darkblue'))
        map.drawMapArray(map.readMap('kk.txt'))             
        link = self.screen.blit(self.playerSpr, self.playerRec) # 'blit' accepts a 'Rect' as second parameter
        bush = self.screen.blit(self.aSprite, self.aSpriteRec)
        d = link.colliderect(bush)
        print d  
        pygame.display.update()



class Map(Game()):
    """What we need to do here is go out and open a map file. 
    Read the file, and for each charactor load it onto the surface 
    in the right x/y coords. Should be easy. lulz"""
    def __init__(self, md='maps/'):
        self.md     = md
        self.tiles  = []
        #self.sprSz  = game.sprSz
        #self.multi  = game.multi
        #self.screen = game.screen

    def readMap(self, mapfile):
        """Lets open that map file up in a semi elegant way. Let
        us code cleanly and improve on simple things. """
        try:
            self.mpath  = os.path.join(self.md, mapfile)
            self.map    = open(self.mpath, 'r')
        except IOError, message:
            print "Unable to Map: " + self.md+mapfile
            raise SystemExit, message

        self.lines  = self.map.readlines()        
        self.Ty     = len(self.lines)
        self.Tx     = len(self.lines[0])-1
        self.map.close()

        for c in range(self.Ty):
            self.tiles.append([])
            for r in range(self.Tx):
                self.tiles[c].append(self.lines[c][r])                        
        return self.tiles


    def drawMapArray(self, map):
        for x in range(0, self.Tx):
            for y in range(0, self.Ty):
                #Determines tile type.
                curTile=tile_d[map[y][x]][3]
                #print x,y
                #print map
                #print curTile
                #if  tile_d[self.readMap[y][x]][2]>-1:
                    #game.screen.blit(curTile, (x*game.sprSz, y*game.sprSz+game.multi*56), (tile_d[self.readMap[y][x]][2]*resmulti*16, 0, 16*resmulti, 16*resmulti))
                #else:
                e = self.screen.blit(curTile, (x*self.sprSz, y*self.sprSz+self.multi*56))
                #print e


if __name__ == "__main__":
    game = Game()
    map = Map()   
    tile_d={
'#' : ('Link', True, 0, game.imgLoad("link1.png", game.multi, 0, 0)),
' ' : ('Link', True, 0, game.imgLoad("empty.png", game.multi, 0, 0)),
'Q' : ('Link', True, 0, game.imgLoad("link1.png", game.multi, 0, 0)),
'R' : ('Link', True, 0, game.imgLoad("empty.png", game.multi, 0, 0)),
'W' : ('Link', True, 0, game.imgLoad("link1.png", game.multi, 0, 0)),
'E' : ('Link', True, 0, game.imgLoad("empty.png", game.multi, 0, 0)),
'R' : ('Link', True, 0, game.imgLoad("link1.png", game.multi, 0, 0)),
'B' : ('Link', True, 0, game.imgLoad("empty.png", game.multi, 0, 0)),
'T' : ('Link', True, 0, game.imgLoad("link1.png", game.multi, 0, 0)),
'Y' : ('Link', True, 0, game.imgLoad("empty.png", game.multi, 0, 0)),
}
    game.mainLoop()

Upvotes: 0

Views: 198

Answers (2)

dank.game
dank.game

Reputation: 4729

You're using Python's syntax for class inheritance. Inheritance is used for 'is a' relationships, hypothetical classes like Sidescroller or FPS, because they are types of games.

Map is not a type of game, it's something in a game, so Game and Map have a 'has a' relationship. Your game has a map, like it has sprites and sounds. There is no special syntax for a 'has a' relationship. The parent class just creates an instance of the child class.

Since you want access to Game's variables, you could pass its original instance to Map when you declare a map. If you declare Map within Game, you can pass the instance of game using self. Here is an example:

# print the game's title via the Map class

class Game:

    def __init__(self):
        self.title = "Robot Ninja Spaceman: Lazer Quest"
        self.map = Map(self)

class Map:

    def __init__(self, game):
        print game.title

if __name__ == "__main__":
    game = Game()

Upvotes: 2

Andrew Clark
Andrew Clark

Reputation: 208715

To have Map use Game as a base class, you should define it using class Map(Game) instead of class Map(Game()).

Here is a brief example of what happens when you try to use an instance of Game as the base class:

>>> class Game():
...     def __init__(*args):
...         print args
... 
>>> class Map(Game()):
...     pass
... 
(<__main__.Game instance at 0x7fa41d06f320>,)
(<__main__.Game instance at 0x7fa41d0549e0>, 'Map', (<__main__.Game instance at 0x7fa41d06f320>,), {'__module__': '__main__'})

So we see that when the class definition for Map runs, we get two calls to Game.__init__(). The first is for the Game instance being created with Game(), what happens next is a little weird. The instance that was just created is used as the base class for Map, so its __init__() method gets called using the same arguments as the three argument version of type() (the name, base classes, and a dictionary).

This is why you end up with that weird error you see, you end up with a call to Game.__init__() where w is the string 'Map' and multi is a dictionary, and attempting to multiply a string by a dictionary results in that TypeError:

>>> self.multi = 'Map' * {}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'dict'

Upvotes: 1

Related Questions