1
2 """
3 Base class for simple gamelogic.
4 Subclass gameLogic01 to set custom menus, splash screen etc... passing as a parameter a gameplay class, subclassed as well to have a custom gameplay.
5
6 @author: fabius
7 @copyright: fabius@2010
8 @license: GCTA give credit to (the) authors
9 @version: 0.1
10 @date: 2010-09
11 @contact: astelix (U{panda3d forums<http://www.panda3d.org/forums/profile.php?mode=viewprofile&u=752>})
12 @status: eternal WIP
13
14 @todo:
15 aggiungere un terzo slide oltre titologame e scores di spiega del game (o meglio metterlo in gameplay? magari per ora solo immagini) e/o fare una voce di menu' HELP
16
17 @note:
18 - FSM mechanic:
19 - a) call a request 'X'
20 - b) FSM status goes to 'X' either if we specified enterX and filterX (respectively) methods or not, otherwise it will fall in the default methods
21 - c) stay cool in the last filterX method, waiting another different request and when it comes:
22 - will be summoned the eventual exitX method or the default one and then again b)
23
24 @warning: in this stuff will interact different input layers with FSM. That could be problematic so we gotta enable/disable the inputs passing fom FSM to other not FSM-driven pieces of code
25 """
26 import os, random, sys
27 from direct.showbase.ShowBase import ShowBase
28 from pandac.PandaModules import *
29 from direct.showbase.DirectObject import DirectObject
30 from direct.interval.IntervalGlobal import *
31 from direct.fsm import FSM
32 from direct.gui.OnscreenText import OnscreenText
33 from dgstuff import *
34 import scorer
35
36
37
39 _DEBUG=False
40 - def __init__(self, gameplay, settings={}):
41 """
42 """
43 ShowBase.__init__(self)
44 FSM.FSM.__init__(self, 'fsm_%r'%id(self))
45
46 self.gameplay=gameplay
47 self._backto=[]
48 self._menuqueue=[]
49 self.playername="Player1"
50 self.titledata=None
51 self.splashfile='data/textures/splash.png'
52
53 self.gamesettings={
54 'playernames': ['cpu', self.playername],
55 'opponent': 'cpu',
56 }
57 self.gamesettings.update(settings)
58
59 self.setinputs()
60
61 self._loadguidata()
62
63 self.request('Title')
64
65
79
80
81
82 _currenttune=-1
83 _tunes=[]
84 _tunesfolder="./data/sounds/music/"
85 playingtune=None
101
106
107
108
110 l=[]
111 for f in os.listdir(top):
112 pathname = os.path.join(top, f)
113 if os.path.isfile(pathname) and os.path.splitext(pathname)[1] == ext:
114 l.append(pathname)
115 l.sort()
116 return l
117
118
119
120
121
122
124 if request == 'quit':
125
126 return ('Menu', self.menu_main)
127 else:
128
129 return FSM.FSM.defaultFilter(self, request, args)
130
131
132
133
134
135
142
144 self.dbgprint("[Title] filtering (%s)"%request)
145 if request in ['advance', 'select']: return 'Gameplay'
146 else: return self.defaultFilter(request, args)
147
149 self.dbgprint("[Title] exiting")
150 self._title.finish()
151 del self._title
152
153
154 """ methods for the menu ovelapping mechanic to manage the add/remove of gui controls like menus etc.
155 """
156
158 if len(self._menuqueue): self._menuqueue[-1].pause()
159 self._menuqueue.append(dgmenu(menudata))
160
161 - def push_entry(self, menudata):
162 if len(self._menuqueue): self._menuqueue[-1].pause()
163 self._menuqueue.append(dginput(menudata))
164
166
167
168
170
171
172
173
175 self.dbgprint("[Menu] entering")
176
177 self.setinputs(False)
178 self._backto.append(self.oldState)
179
180 self.push_menu(args[0])
181
182
184 self.dbgprint("[Menu] filtering (%s)"%request)
185 if request == '_menuend':
186 menu=self._menuqueue.pop()
187 menu.finish()
188 if len(self._menuqueue): self._menuqueue[-1].play()
189 else:
190
191 bt=self._backto.pop()
192 self.dbgprint("Menu: back to old:%s" % bt)
193 return bt
194 else: return self.defaultFilter(request, args)
195
196
198
199 self.dbgprint("[Menu] exiting")
200 self.setinputs()
201 self._backto=[]
202 for menu in self._menuqueue:
203 menu.finish()
204 self._menuqueue=[]
205
206
207
208
209 gameplayshell=None
210
211 - def postgameover(self, score=None): return None
212
220
222 """ will enter here after 'Gameplay' FSM request.
223 Will be evaluated here the gameplay status and eventually the game will be paused/restarted.
224 """
225 self.dbgprint("[Gameplay] entering")
226 if self.gameplayshell:
227 self.dbgprint("returning off menu")
228 self.gameplayshell.pause(0)
229 else:
230 self.dbgprint("Starting new game")
231 if (self.istuneplaying()): self.playingtune.stop()
232
233 self.gameplayshell=self.gameplay(
234 self, settings=self.gamesettings,
235 callback=lambda arg: taskMgr.doMethodLater(
236 5, self.postgameover, '_pogaov',[arg]
237 )
238 )
239
241 """
242 @note: 'quit' request here will just pause the game, therefore the 'Menu' request must be called explicitly
243 """
244 self.dbgprint("[Gameplay] filtering (%s)"%request)
245 if request == 'quit':
246 self.gameplayshell.pause(1)
247 return 'Menu', self.menu_stopgame
248 else:
249 return self.defaultFilter(request, args)
250
253
254
255
258
259
260
262 """
263 The game presentation class.
264 Usually shows a splash screen, the scoreboard and help pages (in the future maybe).
265 """
266 - def __init__(
267 self, parent, top10data=None, splashfile='data/textures/splash.png'
268 ):
269 self.parent=parent
270 if top10data:
271 self._top10sb=scorer.scorer(filename=top10data['scorefile'], clamp=10)
272 fs="%%0%dd"%top10data.get('scoredigits',3)
273 items=[
274 [
275 {'label': line[1].strip()}, {'label': fs%line[0]}
276 ] for line in self._top10sb.getscores()
277 ]
278 top10data['items']=items
279 top10data['visible']=False
280 self.top10table=dgtable(top10data)
281 else: self.top10table=None
282 self.splash=OnscreenImage(
283 parent= self.parent.render2d, image = splashfile
284 )
285 self._setsplash()
286
287
288
290 self.splash.destroy()
291 if self.top10table:
292 taskMgr.remove('gametitletsk')
293 self.top10table.finish()
294
295
296
298 if self.top10table:
299 taskMgr.remove('gametitletsk')
300 self.top10table.canvas.hide()
301 self.splash.show()
302 taskMgr.doMethodLater(5, self._settop10, 'gametitletsk')
303
304
305
307 taskMgr.remove('gametitletsk')
308 self.splash.hide()
309 taskMgr.doMethodLater(8, self._setsplash, 'gametitletsk')
310 self.top10table.canvas.show()
311
312
313
315 """ To make a custom game, fist start subclassing this.
316 """
317 _DEBUG=True
318 _SETTINGS={
319 'opponent': 'human',
320 'playernames': ['CPU0', 'Player1']
321 }
322
323
334
335
336
342
343
344
345 _effects={}
346 _effectsfolder="./data/sounds/effects/"
349
353
354
355
357 d={}
358 for f in os.listdir(top):
359 pathname = os.path.join(top, f)
360 if os.path.isfile(pathname) and os.path.splitext(pathname)[1] == ext:
361 d[os.path.splitext(f)[0]]=self.parent.loadSfx(pathname)
362 return d
363
364
365
367
368 self.startclock()
369 self.ignoreAll()
370 for tsk in taskMgr.getTasksMatching('gpl01_*'): taskMgr.remove(tsk)
371 self.onfinish()
372
373
374 _GCLK=None
375 _FT=None
377 if self._GCLK and self._FT:
378 self._GCLK.setRealTime(self._FT)
379 self._GCLK.setMode(ClockObject.MNormal)
380 self.parent.enableParticles()
381 self._GCLK=None
382 self.dbgprint("[gpl01] restarting...")
383
385 if not self._GCLK:
386 self.dbgprint("[gpl01] pausing...")
387 self.parent.disableParticles()
388 self._GCLK=ClockObject.getGlobalClock()
389 self._FT=self._GCLK.getFrameTime()
390 self._GCLK.setMode(ClockObject.MSlave)
391
392 - def pause(self, force=None):
395
396
397
400
401
402
403 if __name__ == "__main__":
404 loadPrcFileData("", """win-size 800 600
405 win-origin 0 0
406 model-path $MAIN_DIR/data/models/
407 sync-video 0
408 #show-frame-rate-meter #t
409 """
410 )
411
412 """ Sample to show how to subclass the parent gamelogic class to have a functional game mechanic
413 """
418
419
420 - def postgameover(self, score=None):
421 self.dbgprint("Score report after gameover:\n%r"%score)
422 if score['opponent'] == 'cpu':
423 sc=scorer.scorer(filename='pong.sco')
424 sc.putscore(self.playername, score['value'])
425 self.abortgame()
426 return None
427
429 if v.strip():
430 self.playername=v
431 self.gamesettings['playernames'][1]=self.playername
432
434
435 self.titledata={'scorefile': 'gameplay01.sco', 'scoredigits': 6,
436 'title': '** TOP 10 SCORES **', 'titlescale': .09,
437 'scale':(1.5, 1.1), 'itemsvisible':10, 'margin': (.1,.15),
438 'texture':'data/models/textures/menu_pong.png',
439 'textfont': loader.loadFont("data/fonts/slkscre.ttf"),
440 'itemswidth': (.9, .4), 'itemsscale':.07,
441 'head': [
442 { 'label': 'Player', 'color':(1,1,1,1), 'textscale':.09},
443 { 'label': 'Score', 'color':(1,1,1,1), 'textscale':.09},
444 ],
445 }
446
447 def menumerge(a, intob):
448 r=intob.copy()
449 r.update(a)
450 return r
451
452 menucommon={'scale':(1.1, .6), 'titlescale':.1, 'pos':(0,0),
453 'texture':'data/models/textures/menu_pong.png',
454 'titlecolor': (1,1,1,1), 'itemscolor': (1,1,1,1), 'itemsscale': .08,
455 'highlightcolor':(0,0,0,1),
456 'textfont': loader.loadFont("data/fonts/slkscre.ttf"),
457 }
458
459 menu={'title': 'Sure to quit?', 'selected':1,
460 'items': [
461 {'label': 'Yes', 'callback': sys.exit},
462 {'label': 'No', 'callback': self.pop_menu},
463 ],
464 'exit': self.pop_menu,
465 }
466 self.menu_quitgame=menumerge(menu, menucommon)
467
468 def menu_name():
469
470 menu={
471 'align': 'center',
472 'title': 'Put Your Name', 'scale': (1.2,.6), 'margin':(.07, .05),
473 'titlecolor': (1,1,1,1),
474 'initialtext': self.playername, 'inputscale': .07, 'inputwidth': 25,
475 'inputcolor':(0,0,0,1), 'callback': lambda v: self._setplayername(v),
476 'exit': self.pop_menu, 'autoexit': True,
477 }
478 return menumerge(menu, menucommon)
479
480 menu={'title': 'MAIN MENU',
481 'items': [
482 {'label': 'Player Name',
483 'callback': lambda foo=None: self.push_entry(menu_name()),
484 },
485 {'label': 'Exit Game',
486 'callback': lambda foo=None: self.push_menu(self.menu_quitgame),
487 },
488 ],
489 'exit': self.pop_menu,
490 }
491 self.menu_main=menumerge(menu, menucommon)
492
493 menu={'title': 'Stop playing?', 'selected': 1, 'scale':(1.2, .5),
494 'items': [
495 {'label': 'Yess', 'callback': self.abortgame, },
496 {'label': 'Nope', 'callback': self.pop_menu, },
497 ],
498 'exit': self.pop_menu,
499 }
500 self.menu_stopgame=menumerge(menu, menucommon)
501
502 from pong3dgpl import gameplay
503 game=myGame(gameplay)
504 run()
505