PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/trunk/runtime/__init__.py

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