PageRenderTime 40ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/demos/bin/space-aliens/runtime/__init__.py

http://game-baker.googlecode.com/
Python | 569 lines | 550 code | 12 blank | 7 comment | 5 complexity | e1ca3a8345f79a5a3fab8898b3233430 MD5 | raw file
Possible License(s): LGPL-3.0
  1. import pygame, time
  2. from pygame.locals import *
  3. import pygame.locals as pygamelocals
  4. import random
  5. from script_objects import *
  6. pygame.font.init()
  7. from constants import *
  8. from gb_exceptions import *
  9. def run_game(game):
  10. """run a general game
  11. - a bit messy, some time I will make it more OO
  12. """
  13. myscreen = Screen((800,600),game.title)
  14. def get_game_screen(name,game,scriptvars,screen,saved={},scriptglobals={}):
  15. if saved.has_key(name):
  16. return saved[name]
  17. else:
  18. return RunGameScreen(screen,game,game.gamescreens[name],scriptvars,scriptglobals)
  19. scriptvar = scriptgame(game)
  20. pygame.init()
  21. # Run Game init code to import modules etc.
  22. scriptglobals={}
  23. # TODO:
  24. # From Release 0.2 onward we can remove the except - left in to
  25. # retain compatibility with previous 0.1.x releases' games
  26. try:
  27. if not game.initcode == "":
  28. exec game.initcode in {}, scriptglobals
  29. except: pass
  30. #Check if a gamescreen is already loaded, otherwise return a new Rungamescreen object
  31. savedgamescreens = {}
  32. try:
  33. rungamescreen = get_game_screen(game.startscreen, game, \
  34. scriptvar, myscreen, savedgamescreens,scriptglobals=scriptglobals)
  35. except gb_runtime_exception, inst:
  36. pygame.quit()
  37. raise inst
  38. except Exception,inst:
  39. pygame.quit()
  40. raise gb_scripting_exception(log="Error Finding Initial GameScreen - " \
  41. + str(inst) ,orig_except = inst)
  42. currentgamescreen = game.startscreen
  43. # By default use 25 fps
  44. framerate = 25
  45. # Test the different clocks
  46. clock = pygame.time.Clock()
  47. # Give access to the frame per second counter
  48. scriptvar.get_fps = clock.get_fps
  49. starttime = time.time()
  50. clock.tick(framerate)
  51. timetaken = time.time() - starttime
  52. # Default (bad) way
  53. def timer_generator(fps,clock):
  54. lasttime=time.time()
  55. framedelay = 1./fps
  56. while 1:
  57. now = time.time()
  58. time_taken = now - lasttime
  59. if time_taken >= framedelay:
  60. lasttime = now
  61. clock.tick()
  62. yield
  63. else:
  64. time.sleep(framedelay - time_taken)
  65. lasttime = time.time()
  66. clock.tick()
  67. yield
  68. timer_generator_inst = timer_generator(framerate,clock)
  69. timerfunction = lambda x: timer_generator_inst.next()
  70. if (0.93 * timetaken) < (1./framerate) < (1.07 * timetaken):
  71. # Use the SDL timer
  72. timerfunction = clock.tick
  73. print "using sdl timer"
  74. else:
  75. print "using python timer"
  76. while 1:
  77. try:
  78. rungamescreen.gameloop(clock) # buffers for the next frame, but doesn't draw to the screen
  79. timerfunction(framerate)
  80. # Now draw it to the screen
  81. rungamescreen.render()
  82. # Catch events that have to be processed at this level
  83. except game_event,inst:
  84. try:
  85. command = inst.instruction[0]
  86. params = inst.instruction[1:]
  87. if command == "changeGameScreen":
  88. gsname = params[0]
  89. savestate = params[1]
  90. if savestate:
  91. savedgamescreens[currentgamescreen] = rungamescreen
  92. else:
  93. del savedgamescreens[currentgamescreen]
  94. currentgamescreen = gsname
  95. rungamescreen = get_game_screen(gsname,game,scriptvar,myscreen,savedgamescreens,scriptglobals=scriptglobals)
  96. except Exception,inst:
  97. pygame.quit()
  98. raise gb_runtime_exception(log="Error Changing Gamescreen",orig_except=inst)
  99. except exit_game:
  100. break
  101. except gb_scripting_exception:
  102. pygame.quit()
  103. raise
  104. except gb_runtime_exception:
  105. pygame.quit()
  106. raise
  107. except Exception, inst:
  108. print inst
  109. pygame.quit()
  110. raise gb_runtime_exception(log=str(inst),orig_except=inst)
  111. pygame.quit()
  112. class Screen(object):
  113. """"Screen/window Class for rendering onto."""
  114. def __init__(self,size,name):
  115. self.surface = pygame.display.set_mode(size)
  116. pygame.mouse.set_visible(1)
  117. self.surface.fill((255,255,255))
  118. pygame.display.set_caption(name)
  119. self.resolution = size
  120. def clear(self,color=(255,255,255)):
  121. self.surface.fill(color)
  122. def render(self):
  123. pygame.display.update()
  124. class RunGameScreen(object):
  125. """"The Class that represents an individual gamescreen (or level etc) during execution"""
  126. def __init__(self,screen,game,gamescreen,scriptgamevar,scriptglobals):
  127. self.game = game
  128. self.gamescreen = gamescreen # the game.gamescreen object
  129. self.screen = screen
  130. self.surface = pygame.Surface(self.gamescreen.dimensions)
  131. self.scriptglobals = scriptglobals
  132. self.scriptgamevar = scriptgamevar # "game" object for scripting
  133. # Cache for workstate events (compiled)
  134. self.__workstate_event_cache={}
  135. self.spriteobjects = {} # Loaded (runtime) sprite instances
  136. for go in gamescreen.startobjects:
  137. if go.sprite is not None:
  138. if not self.spriteobjects.has_key(go.sprite):
  139. self.spriteobjects[go.sprite] = RunSprite(self.game.sprites[go.sprite])
  140. # for items removed in scripts
  141. self.to_delete_scriptgameobjs = []
  142. self.time_since_last_call = 0
  143. # Order is important:
  144. # 1
  145. self.gameobjects = {}
  146. # 2
  147. self.scriptgamescreenvar = scriptgamescreen(self)
  148. # 3
  149. # HAVE TO CHANGE THIS - how to name objects?
  150. for a in gamescreen.startobjects:
  151. self.gameobjects[a.name] = RunGameObject(self,a)
  152. def get_workstate_event_code(self,workstate,event):
  153. cache = self.__workstate_event_cache
  154. wscache = cache.setdefault(workstate,{})
  155. event_code = wscache.get(event,None)
  156. if event_code is None:
  157. workstates = self.game.workstates
  158. ws = workstates[workstate]
  159. event_code = ws.actions.get(event,"")
  160. # Walk the inheritance tree and evaluate the method.
  161. while event_code == "" and ws.inherits is not None:
  162. ws = workstates.get(ws.inherits)
  163. event_code = ws.actions.get(event,"")
  164. if not event_code == "":
  165. event_code = compile(event_code,str(workstate) + ":<string>","exec")
  166. wscache[event]=event_code
  167. return event_code
  168. def add_object_instance(self,obj,name):
  169. # If we're removing sprites when they're not used we need to
  170. # split this
  171. self.gameobjects[name] = RunGameObject(self,self.game.gameobjects[obj])
  172. return self.gameobjects[name]
  173. def remove_object_instance(self,name):
  174. del self.gameobjects[name]
  175. def gameloop(self,clock):
  176. """Main Game Loop - called each itteration"""
  177. #self.input()
  178. self.itteration()
  179. # Draw this gamescreen on to our window.
  180. self.frame_buffer()
  181. # Get time since last clock cycle
  182. self.time_since_last_call = clock.get_time()
  183. events = pygame.event.get()
  184. game_objects = self.gameobjects.values()
  185. for event in events:
  186. if event.type == QUIT:
  187. # Simple solution
  188. raise exit_game()
  189. elif event.type == KEYDOWN:
  190. for gameobj in game_objects:
  191. gameobj._do_event(EVENT_KEYDOWN)
  192. gameobj._do_event(EVENT_KEYDOWN*event.key)
  193. pass
  194. elif event.type == KEYUP:
  195. for gameobj in game_objects:
  196. gameobj._do_event(EVENT_KEYUP)
  197. gameobj._do_event(EVENT_KEYUP*event.key)
  198. pass
  199. elif event.type == MOUSEBUTTONUP:
  200. (x,y) = pygame.mouse.get_pos()
  201. x = x - self.scriptgamescreenvar.x
  202. y = y - self.scriptgamescreenvar.y
  203. for gameobj in game_objects:
  204. if gameobj.check_point_collision(x,y):
  205. if event.button == 1:
  206. gameobj._do_event(EVENT_LMOUSEUP)
  207. elif event.button == 2:
  208. gameobj._do_event(EVENT_RMOUSEUP)
  209. # Get time since last clock cycle
  210. # Do this again in case a long time has elapsed
  211. self.time_since_last_call = clock.get_time()
  212. # Check timers for each object
  213. now = time.time()
  214. for go in game_objects:
  215. timers = go.scriptvar.timers
  216. if len(timers) > 0:
  217. while len(timers) > 0:
  218. if timers[0][0] < now:
  219. t,msg = timers.pop(0)
  220. go._do_event(EVENT_TIMER,extraparams = dict(timermsg = msg) )
  221. else: break
  222. for sgo in self.to_delete_scriptgameobjs:
  223. objs = [n for n in self.gameobjects.keys() if self.gameobjects[n].scriptvar == sgo]
  224. for n in objs:
  225. del self.gameobjects[n]
  226. def render(self):
  227. # blit the window on to the monitor.
  228. self.screen.render()
  229. def frame_buffer(self):
  230. """render a frame"""
  231. # Draw background - to be replaced with options (background image, colour etc)
  232. try:
  233. self.screen.clear(self.scriptgamescreenvar.color)
  234. except :
  235. self.screen.clear()
  236. # Draw objects - blit only part on screen
  237. swidth,sheight = self.screen.resolution
  238. gswidth,gsheight = self.gamescreen.dimensions
  239. #------------------------------------------#
  240. # NB: use the .z attribute of the game objects
  241. # to order the rendering.
  242. #------------------------------------------#
  243. gameobjs = {}
  244. gos = self.gameobjects.values()
  245. for i in range(len(gos)):
  246. gameobjs.setdefault(gos[i].scriptvar.z,[ ]).append(gos[i])
  247. zvals = gameobjs.keys()[:]
  248. zvals.sort()
  249. screen_x = self.scriptgamescreenvar.x
  250. screen_y = self.scriptgamescreenvar.y
  251. screensurf = self.screen.surface
  252. for i in zvals:
  253. objs = gameobjs[i]
  254. for item in objs:
  255. item_x = item.scriptvar.x
  256. item_y = item.scriptvar.y
  257. if item_x - screen_x + item.surface.get_width() < 0 or \
  258. item_x - screen_x > swidth or \
  259. item_y - screen_y + item.surface.get_height()< 0 or \
  260. item_y - screen_y > sheight:
  261. item._do_event(EVENT_OFFSCREEN)
  262. else:
  263. screensurf.blit(item.surface,(item_x - \
  264. screen_x,item_y - screen_y))
  265. def input(self):
  266. """Completely not done yet"""
  267. pass
  268. def itteration(self):
  269. """Called every itteration"""
  270. for item in self.gameobjects.values():
  271. # standard stuff (e.g. sprite frames)
  272. item._dostep()
  273. # non-standard iteration handler
  274. item._do_event(EVENT_ITERATION)
  275. pass
  276. class RunGameObject(object):
  277. """"The Class that represents a GameObject while the game is being executed"""
  278. def __init__(self,gamescreenobj,GameObj):
  279. # Object for scripting
  280. self.scriptvar = scriptgameobj(self,gamescreenobj)
  281. # Set up sprite
  282. scriptvar = self.scriptvar
  283. scriptvar.sprite = GameObj.sprite
  284. scriptvar.frame = 0
  285. scriptvar.__loopcounter = 0
  286. scriptvar.workstate = GameObj.baseworkstate
  287. # set x and y coordinates
  288. try:
  289. scriptvar.x = GameObj.x
  290. scriptvar.y = GameObj.y
  291. except: pass
  292. self.oldworkstate = GameObj.baseworkstate
  293. self.game = gamescreenobj.game
  294. self.gamescreenobj = gamescreenobj
  295. self.GameObject = GameObj
  296. self.surface = pygame.Surface((10,10))
  297. # call the init event
  298. try:
  299. self._do_event(EVENT_INIT)
  300. except gb_runtime_exception:
  301. raise
  302. except Exception,inst:
  303. raise gb_scripting_exception(log = "Error Running Initialisation Code for object",orig_except=inst,event="1")
  304. def _do_event(self,event,scriptlocals = None,extraparams=None):
  305. """ call a scripted event.
  306. Scriptlocals should normally be None
  307. exrtaparameters is a dict of any extra parameters for the call
  308. (e.g. for timer event)
  309. """
  310. """
  311. workstates = self.game.workstates
  312. ws = self.game.workstates[self.scriptvar.workstate]
  313. event_code = ws.actions.get(event,"")
  314. # Walk the inheritance tree and evaluate the method.
  315. while event_code == "" and ws.inherits is not None:
  316. ws = workstates.get(ws.inherits)
  317. event_code = ws.actions.get(event,"")
  318. """
  319. if scriptlocals is None:
  320. scriptlocals = {"self":self.scriptvar,"game": self.gamescreenobj.scriptgamevar, \
  321. "gamescreen":self.gamescreenobj.scriptgamescreenvar, \
  322. "random":random,"constants":pygamelocals}
  323. if extraparams is not None:
  324. for k in extraparams.keys():
  325. scriptlocals[k] = extraparams[k]
  326. try:
  327. event_code = self.gamescreenobj.get_workstate_event_code(self.scriptvar.workstate,event)
  328. if event_code == "":
  329. return
  330. exec event_code in self.gamescreenobj.scriptglobals, scriptlocals
  331. except gb_scripting_exception,inst:
  332. if inst.event == "Unknown":
  333. inst.event = event
  334. if inst.workstate is None:
  335. inst.workstate = self.scriptvar.workstate
  336. raise inst
  337. except gb_runtime_exception,inst:
  338. raise gb_scripting_exception(log="Error in User Script",orig_except=inst,event=event,workstate=self.scriptvar.workstate)
  339. except Exception,inst:
  340. raise gb_scripting_exception(log="Error in User Script",orig_except=inst,event=event,workstate=self.scriptvar.workstate)
  341. def check_point_collision(self,x,y):
  342. """Checks if a point is within the object's collision area"""
  343. (width,height) = self.surface.get_size()
  344. return self.scriptvar.x <= x and self.scriptvar.y <= y and self.scriptvar.x + width >= x and self.scriptvar.y + height >= y
  345. def drawsprite(self,sprite,x,y,frame = None):
  346. """draws a sprite on the object at position x,y """
  347. spr = self.gamescreenobj.spriteobjects.get(sprite,None)
  348. if spr is None:
  349. # Load sprite
  350. self.gamescreenobj.spriteobjects[sprite] = \
  351. RunSprite(self.gamescreenobj.game.sprites[sprite])
  352. spr = self.gamescreenobj.spriteobjects[sprite]
  353. if frame is None:
  354. self.surface.blit(spr.frames[self.scriptvar.frame],(x,y))
  355. return
  356. else:
  357. self.surface.blit(spr.frames[frame],(x,y))
  358. def drawtext(self,text,size=None,colour=None):
  359. """ Using "Colour" since that is the correct English spelling ;-)
  360. Also not using defaults so we can overload the function
  361. to allow different calling conventions later"""
  362. if not type(text) == type(""):
  363. text = str(text)
  364. if size is None:
  365. size = 12
  366. if colour is None:
  367. colour = (0,0,0)
  368. # Create a font
  369. font = pygame.font.Font(None, size)
  370. # Render the text
  371. text = font.render(text, True, colour)#, background colour
  372. # Create a rectangle
  373. textRect = text.get_rect()
  374. # Center the rectangle
  375. textRect.centerx = self.surface.get_rect().centerx
  376. textRect.centery = self.surface.get_rect().centery
  377. # Blit the text
  378. self.surface.blit(text, textRect)
  379. def clear(self):
  380. if self.scriptvar.sprite is None:
  381. self.surface.fill(self.scriptvar.color)
  382. def resize(self,res,height=None):
  383. if not height is None:
  384. res = (res,height)
  385. self.surface = pygame.transform.scale(self.surface,res)
  386. self.scriptvar.width = res[0]
  387. self.scriptvar.height = res[1]
  388. def _dostep(self):
  389. scriptvar = self.scriptvar
  390. # Check if we have changed workstate:
  391. if not self.oldworkstate == scriptvar.workstate:
  392. self._do_event(EVENT_INIT)
  393. self.oldworkstate = scriptvar.workstate
  394. if not scriptvar.sprite is None:
  395. spr = self.gamescreenobj.spriteobjects.get(scriptvar.sprite,None)
  396. if spr is None:
  397. # Load sprite
  398. self.gamescreenobj.spriteobjects[scriptvar.sprite] = \
  399. RunSprite(self.gamescreenobj.game.sprites[scriptvar.sprite])
  400. spr = self.gamescreenobj.spriteobjects.get(scriptvar.sprite)
  401. self.surface.set_colorkey(spr.alphakey)
  402. framerate = spr.framerate
  403. scriptvar.__loopcounter = (scriptvar.__loopcounter + 1) \
  404. % (framerate*len(spr.frames))
  405. scriptvar.frame = (scriptvar.__loopcounter ) / framerate
  406. self.surface.blit(spr.frames[scriptvar.frame], (0,0))
  407. class RunSprite(object):
  408. """"The Class that represents a Sprite while the game is being executed"""
  409. def __init__(self,sprite):
  410. self.sprite = sprite
  411. self.framerate = sprite.framerate
  412. self.frames = []
  413. # Set transparent colour based on sprite instance
  414. self.alphakey = sprite.alphakey
  415. # Now load the raw images into surfaces
  416. temp = {}
  417. for im in sprite.imagefiles:
  418. if not temp.has_key(im["filename"]):
  419. # TODO - if the image has per-pixel alpha then it should use convert_alpha()
  420. surface = pygame.image.load(im["filename"]).convert()
  421. temp[im["filename"]] = surface
  422. # And save sub-surfaces for each frame (effectively a pointer to pixelarray, so memory efficient)
  423. for im in sprite.imagefiles:
  424. x1,y1 = im["topleft"]
  425. x2,y2 = im["bottomright"]
  426. rec = pygame.Rect( x1 , y1 , x2 - x1 , y2 - y1 )
  427. self.frames.append(temp[im["filename"]].subsurface(rec))