PageRenderTime 30ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/tags/V0.2.1/runtime/__init__.py

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