/trunk/runtime/__init__.py
Python | 641 lines | 610 code | 21 blank | 10 comment | 5 complexity | 0ad4e2775670c33d52fb6a3fdebf29a9 MD5 | raw file
Possible License(s): LGPL-3.0
- # -*- coding: iso-8859-1 -*-
- import pygame, time
- from pygame.locals import *
- import pygame.locals as pygamelocals
- import random
- from script_objects import *
- pygame.font.init()
- from constants import *
- from gb_exceptions import *
- def run_game(game):
- rg = runtime_game(game)
- rg.run()
- class runtime_game(object):
- def __init__(self,game):
- self.game = game
- def save_game(self):
- game_object_for_save = dict( \
- sgame=self.scriptgame,
- savedgamescreens = self.savedgamescreens,
- currentgamescreen = self.currentgamescreen,
- currentrungamescreen = self.rungamescreen)
- # TODO:
- # Pickle the dict above
- import gbfileio
-
- gbfileio.savegame("testsavegame.save",game_object_for_save,method="pickle",typ="savegame")
-
- def run(self):
- """run a general game
- - a bit messy
- """
- game = self.game
- myscreen = Screen((800,600),game.title)
- def get_game_screen(name,game,scriptvars,screen,saved={},scriptglobals={}):
- if saved.has_key(name):
- return saved[name]
- else:
- return RunGameScreen(screen,game,game.gamescreens[name],scriptvars,scriptglobals)
-
- pygame.mixer.init()
- self.gamesounds = {}
- for a in game.sounds:
- self.gamesounds[a] = pygame.mixer.Sound(game.sounds[a].soundfile)
-
- scriptvar = scriptgame(game, self.save_game,self.gamesounds)
- self.scriptgame = scriptvar
- pygame.init()
- # Run Game init code to import modules etc.
- scriptglobals={\
- "mouse": script_objects.script_mouse()}
-
- # TODO:
- # From Release 0.2 onward we can remove the except - left in to
- # retain compatibility with previous 0.1.x releases' games
- if not game.initcode == "":
- exec game.initcode in {}, scriptglobals
-
-
-
- #Check if a gamescreen is already loaded, otherwise return a new Rungamescreen object
- savedgamescreens = {}
- self.savedgamescreens = savedgamescreens
- try:
- self.rungamescreen = get_game_screen(game.startscreen, game, \
- scriptvar, myscreen, savedgamescreens,scriptglobals=scriptglobals)
-
- except gb_runtime_exception, inst:
- pygame.quit()
- raise inst
- except Exception,inst:
- pygame.quit()
- raise gb_scripting_exception(log="Error Finding Initial GameScreen - " \
- + str(inst) ,orig_except = inst)
- self.currentgamescreen = game.startscreen
- # By default use 25 fps
- framerate = 25
- # Test the different clocks
- clock = pygame.time.Clock()
- # Give access to the frame per second counter
- scriptvar.get_fps = clock.get_fps
- starttime = time.time()
- clock.tick(framerate)
- timetaken = time.time() - starttime
- # Default (bad) way
- def timer_generator(fps,clock):
- lasttime=time.time()
- framedelay = 1./fps
- while 1:
- now = time.time()
- time_taken = now - lasttime
- if time_taken >= framedelay:
- lasttime = now
- clock.tick()
- yield
- else:
- time.sleep(framedelay - time_taken)
- lasttime = time.time()
- clock.tick()
- yield
- timer_generator_inst = timer_generator(framerate,clock)
- timerfunction = lambda x: timer_generator_inst.next()
- if (0.93 * timetaken) < (1./framerate) < (1.07 * timetaken):
- # Use the SDL timer
- timerfunction = clock.tick
- print "using sdl timer"
- else:
- print "using python timer"
- while 1:
- try:
- self.rungamescreen.gameloop(clock) # buffers for the next frame, but doesn't draw to the screen
- timerfunction(framerate)
- # Now draw it to the screen
- self.rungamescreen.render()
- # Catch events that have to be processed at this level
- except game_event,inst:
- try:
- command = inst.instruction[0]
- params = inst.instruction[1:]
- if command == "changeGameScreen":
- gsname = params[0]
- savestate = params[1]
- if savestate:
- savedgamescreens[self.currentgamescreen] = self.rungamescreen
- else:
- try:
- del savedgamescreens[self.currentgamescreen]
- except: pass
- self.currentgamescreen = gsname
- self.rungamescreen = get_game_screen(gsname,game,scriptvar,myscreen,savedgamescreens,scriptglobals=scriptglobals)
- except Exception,inst:
- print inst
- pygame.quit()
- raise gb_runtime_exception(log="Error Changing Gamescreen",orig_except=inst)
- except exit_game:
- break
- except gb_scripting_exception:
- pygame.quit()
- raise
- except gb_runtime_exception:
- pygame.quit()
- raise
- """except Exception, inst:
- print inst
- pygame.quit()
- raise gb_runtime_exception(log=str(inst),orig_except=inst)
- """
- pygame.quit()
- class Screen(object):
- """"Screen/window Class for rendering onto."""
- def __init__(self,size,name):
- self.surface = pygame.display.set_mode(size)
- pygame.mouse.set_visible(1)
- self.surface.fill((255,255,255))
- pygame.display.set_caption(name)
- self.resolution = size
- def clear(self,color=(255,255,255)):
- self.surface.fill(color)
- def render(self):
- pygame.display.update()
- class RunGameScreen(object):
- """"The Class that represents an individual gamescreen (or level etc) during execution"""
- def __init__(self,screen,game,gamescreen,scriptgamevar,scriptglobals):
- self.game = game
- self.gamescreen = gamescreen # the game.gamescreen object
- self.screen = screen
- self.surface = pygame.Surface(self.gamescreen.dimensions)
- self.scriptglobals = scriptglobals
- self.scriptgamevar = scriptgamevar # "game" object for scripting
- # Cache for workstate events (compiled)
- self.__workstate_event_cache={}
- self.spriteobjects = {} # Loaded (runtime) sprite instances
- for go in gamescreen.startobjects:
- if go.sprite is not None:
- if not self.spriteobjects.has_key(go.sprite):
- self.spriteobjects[go.sprite] = RunSprite(self.game.sprites[go.sprite])
- # for items removed in scripts
- self.to_delete_scriptgameobjs = []
- self.time_since_last_call = 0
- # Order is important:
- # 1
- self.gameobjects = {}
- # 2
- self.scriptgamescreenvar = scriptgamescreen(self)
-
- # 3
- # HAVE TO CHANGE THIS - how to name objects?
- for a in gamescreen.startobjects:
- self.gameobjects[a.name] = RunGameObject(self,a)
- def __getstate__(self):
- """For Pickling to save a game
- - just passes the script like objects"""
- dict(scriptgamescreenvar = self.scriptgamescreenvar,
- scriptgamevar = self.scriptgamevar,
- gameobjects = self.gameobjects)
- def __setstate__(self,state):
- """For Unpickling (loading)"""
- self.gameobjects = state["gameobjects"]
- self.scriptgamevar = state["scriptgamevar"]
- self.scriptgamescreenvar = state["scriptgamescreen"]
- def get_workstate_event_code(self,workstate,event):
- cache = self.__workstate_event_cache
- wscache = cache.setdefault(workstate,{})
- event_code = wscache.get(event,None)
- if event_code is None:
- workstates = self.game.workstates
- ws = workstates[workstate]
- event_code = ws.actions.get(event,"")
- # Walk the inheritance tree and evaluate the method.
- while event_code == "" and ws.inherits is not None:
- ws = workstates.get(ws.inherits)
- event_code = ws.actions.get(event,"")
- if not event_code == "":
- event_code = compile(event_code,str(workstate) + ":<string>","exec")
- wscache[event]=event_code
- return event_code
- def add_object_instance(self,obj,name):
- # If we're removing sprites when they're not used we need to
- # split this
- self.gameobjects[name] = RunGameObject(self,self.game.gameobjects[obj])
- return self.gameobjects[name]
- def remove_object_instance(self,name):
- del self.gameobjects[name]
- def gameloop(self,clock):
- """Main Game Loop - called each itteration"""
- #self.input()
- self.itteration()
- # Draw this gamescreen on to our window.
- self.frame_buffer()
- # Get time since last clock cycle
- self.time_since_last_call = clock.get_time()
- events = pygame.event.get()
- game_objects = self.gameobjects.values()
- for event in events:
- if event.type == QUIT:
- # Simple solution
- raise exit_game()
- elif event.type == KEYDOWN:
- for gameobj in game_objects:
- gameobj._do_event(EVENT_KEYDOWN)
- gameobj._do_event(EVENT_KEYDOWN*event.key)
- pass
- elif event.type == KEYUP:
- for gameobj in game_objects:
- gameobj._do_event(EVENT_KEYUP)
- gameobj._do_event(EVENT_KEYUP*event.key)
- pass
- elif event.type == MOUSEBUTTONUP:
- (x,y) = pygame.mouse.get_pos()
- x = x - self.scriptgamescreenvar.x
- y = y - self.scriptgamescreenvar.y
- for gameobj in game_objects:
- if gameobj.check_point_collision(x,y):
- if event.button == 1:
- gameobj._do_event(EVENT_LMOUSEUP)
- elif event.button == 2:
- gameobj._do_event(EVENT_RMOUSEUP)
- # Get time since last clock cycle
- # Do this again in case a long time has elapsed
- self.time_since_last_call = clock.get_time()
- # Check timers for each object
- now = time.time()
- for go in game_objects:
- timers = go.scriptvar.timers
- if len(timers) > 0:
- while len(timers) > 0:
- if timers[0][0] < now:
- t,msg = timers.pop(0)
- go._do_event(EVENT_TIMER,extraparams = dict(timermsg = msg) )
- else: break
- for sgo in self.to_delete_scriptgameobjs:
- objs = [n for n in self.gameobjects.keys() if self.gameobjects[n].scriptvar == sgo]
- for n in objs:
- del self.gameobjects[n]
-
- def render(self):
- # blit the window on to the monitor.
- self.screen.render()
- def frame_buffer(self):
- """render a frame"""
- # Draw background - to be replaced with options (background image, colour etc)
- try:
- self.screen.clear(self.scriptgamescreenvar.color)
- except :
- self.screen.clear()
- # Draw objects - blit only part on screen
- swidth,sheight = self.screen.resolution
- gswidth,gsheight = self.gamescreen.dimensions
- #------------------------------------------#
- # NB: use the .z attribute of the game objects
- # to order the rendering.
- #------------------------------------------#
- gameobjs = {}
- gos = self.gameobjects.values()
- for i in range(len(gos)):
- gameobjs.setdefault(gos[i].scriptvar.z,[ ]).append(gos[i])
- zvals = gameobjs.keys()[:]
- zvals.sort()
- screen_x = self.scriptgamescreenvar.x
- screen_y = self.scriptgamescreenvar.y
- screensurf = self.screen.surface
- for i in zvals:
- objs = gameobjs[i]
- for item in objs:
- item_x = item.scriptvar.x
- item_y = item.scriptvar.y
- if item_x - screen_x + item.surface.get_width() < 0 or \
- item_x - screen_x > swidth or \
- item_y - screen_y + item.surface.get_height()< 0 or \
- item_y - screen_y > sheight:
- item._do_event(EVENT_OFFSCREEN)
- else:
- screensurf.blit(item.surface,(item_x - \
- screen_x,item_y - screen_y))
- def input(self):
- """Completely not done yet"""
- pass
- def itteration(self):
- """Called every itteration"""
- for item in self.gameobjects.values():
- # standard stuff (e.g. sprite frames)
- item._dostep()
- # non-standard iteration handler
- item._do_event(EVENT_ITERATION)
- pass
- def get_game_object_name(self,gameobject):
- """Returns the name of a game object"""
- l = [k for k in self.gameobjects.keys() if self.gameobjects[k] == gameobject]
- if len(l) > 0:
- return l[0]
- # Should raise an appropriate exception here
- def set_game_object_name(self,gameobject,new_name):
- old_name = self.get_game_object_name(gameobject)
- self.gameobjects[new_name] = self.gameobjects[old_name]
- del self.gameobjects[old_name]
- class RunGameObject(object):
- """"The Class that represents a GameObject while the game is being executed"""
- def __init__(self,gamescreenobj,GameObj):
- # Object for scripting
- self.scriptvar = scriptgameobj(self,gamescreenobj)
- # Set up sprite
- scriptvar = self.scriptvar
- scriptvar.sprite = GameObj.sprite
- scriptvar.frame = 0
- scriptvar.__loopcounter = 0
- scriptvar.workstate = GameObj.baseworkstate
- # set x and y coordinates
- try:
- scriptvar.x = GameObj.x
- scriptvar.y = GameObj.y
- except: pass
- self.oldworkstate = GameObj.baseworkstate
- self.game = gamescreenobj.game
- self.gamescreenobj = gamescreenobj
- self.GameObject = GameObj
- self.surface = pygame.Surface((10,10))
- # call the init event
- try:
- self._do_event(EVENT_INIT)
- except gb_runtime_exception:
- raise
- except Exception,inst:
- raise gb_scripting_exception(log = "Error Running Initialisation Code for object",orig_except=inst,event="1")
- def __getstate__(self):
- """For Saving (pickling) a game"""
- return dict(scriptvar = self.scriptvar,
- gamescreenobj = self.gamescreenobj,
- )
- def __setstate__(self, state):
- self.scriptvar = state["scriptvar"]
- self.gamescreenobj = state["gamescreenobj"]
- def _do_event(self,event,scriptlocals = None,extraparams=None):
- """ call a scripted event.
- Scriptlocals should normally be None
- exrtaparameters is a dict of any extra parameters for the call
- (e.g. for timer event)
- """
- """
- workstates = self.game.workstates
- ws = self.game.workstates[self.scriptvar.workstate]
- event_code = ws.actions.get(event,"")
- # Walk the inheritance tree and evaluate the method.
- while event_code == "" and ws.inherits is not None:
- ws = workstates.get(ws.inherits)
- event_code = ws.actions.get(event,"")
- """
-
- if scriptlocals is None:
- scriptlocals = {"self":self.scriptvar,"game": self.gamescreenobj.scriptgamevar, \
- "gamescreen":self.gamescreenobj.scriptgamescreenvar, \
- "random":random,"constants":pygamelocals}
- if extraparams is not None:
- for k in extraparams.keys():
- scriptlocals[k] = extraparams[k]
- try:
- event_code = self.gamescreenobj.get_workstate_event_code(self.scriptvar.workstate,event)
- if event_code == "":
- return
- exec event_code in self.gamescreenobj.scriptglobals, scriptlocals
- except gb_scripting_exception,inst:
- if inst.event == "Unknown":
- inst.event = event
- if inst.workstate is None:
- inst.workstate = self.scriptvar.workstate
- raise inst
- except gb_runtime_exception,inst:
- raise gb_scripting_exception(log="Error in User Script",orig_except=inst,event=event,workstate=self.scriptvar.workstate)
- except game_event:
- raise
- except Exception,inst:
- raise gb_scripting_exception(log="Error in User Script",orig_except=inst,event=event,workstate=self.scriptvar.workstate)
-
-
- def check_point_collision(self,x,y):
- """Checks if a point is within the object's collision area"""
- (width,height) = self.surface.get_size()
- return self.scriptvar.x <= x and self.scriptvar.y <= y and self.scriptvar.x + width >= x and self.scriptvar.y + height >= y
- def drawsprite(self,sprite,x,y,frame = None):
- """draws a sprite on the object at position x,y """
- spr = self.gamescreenobj.spriteobjects.get(sprite,None)
- if spr is None:
- # Load sprite
- self.gamescreenobj.spriteobjects[sprite] = \
- RunSprite(self.gamescreenobj.game.sprites[sprite])
- spr = self.gamescreenobj.spriteobjects[sprite]
- if frame is None:
- self.surface.blit(spr.frames[self.scriptvar.frame],(x,y))
- return
- else:
- self.surface.blit(spr.frames[frame],(x,y))
- def drawtext(self,text,size=None,colour=None):
- """ Using "Colour" since that is the correct English spelling ;-)
- Also not using defaults so we can overload the function
- to allow different calling conventions later"""
- if not type(text) == type(""):
- text = str(text)
- if size is None:
- size = 12
- if colour is None:
- colour = (0,0,0)
- # Create a font
- font = pygame.font.Font(None, size)
- # Render the text
- text = font.render(text, True, colour)#, background colour
- # Create a rectangle
- textRect = text.get_rect()
- # Center the rectangle
- textRect.centerx = self.surface.get_rect().centerx
- textRect.centery = self.surface.get_rect().centery
- # Blit the text
- self.surface.blit(text, textRect)
- def clear(self):
- if self.scriptvar.sprite is None:
- self.surface.fill(self.scriptvar.color)
- def resize(self,res,height=None):
- if not height is None:
- res = (res,height)
- self.surface = pygame.transform.scale(self.surface,res)
- self.scriptvar.width = res[0]
- self.scriptvar.height = res[1]
- def _dostep(self):
- scriptvar = self.scriptvar
- # Check if we have changed workstate:
- if not self.oldworkstate == scriptvar.workstate:
- self._do_event(EVENT_INIT)
- self.oldworkstate = scriptvar.workstate
- if not scriptvar.sprite is None:
- spr = self.gamescreenobj.spriteobjects.get(scriptvar.sprite,None)
- if spr is None:
- # Load sprite
- self.gamescreenobj.spriteobjects[scriptvar.sprite] = \
- RunSprite(self.gamescreenobj.game.sprites[scriptvar.sprite])
- spr = self.gamescreenobj.spriteobjects.get(scriptvar.sprite)
- self.surface.set_colorkey(spr.alphakey)
- framerate = spr.framerate
- scriptvar.__loopcounter = (scriptvar.__loopcounter + 1) \
- % (framerate*len(spr.frames))
- scriptvar.frame = (scriptvar.__loopcounter ) / framerate
- self.surface.blit(spr.frames[scriptvar.frame], (0,0))
- class RunSprite(object):
- """"The Class that represents a Sprite while the game is being executed"""
- def __init__(self,sprite):
- self.sprite = sprite
- self.framerate = sprite.framerate
- self.frames = []
- # Set transparent colour based on sprite instance
- self.alphakey = sprite.alphakey
- # Now load the raw images into surfaces
- temp = {}
- for im in sprite.imagefiles:
- if not temp.has_key(im["filename"]):
- # TODO - if the image has per-pixel alpha then it should use convert_alpha()
- surface = pygame.image.load(im["filename"]).convert()
- temp[im["filename"]] = surface
-
- # And save sub-surfaces for each frame (effectively a pointer to pixelarray, so memory efficient)
- for im in sprite.imagefiles:
- x1,y1 = im["topleft"]
- x2,y2 = im["bottomright"]
- rec = pygame.Rect( x1 , y1 , x2 - x1 , y2 - y1 )
- self.frames.append(temp[im["filename"]].subsurface(rec))