comparison pyntnclick/main.py @ 854:79b5c1be9a5e default tip

Remove pyntnclick, it's its own library, now
author Stefano Rivera <stefano@rivera.za.net>
date Sat, 21 Jun 2014 22:06:09 +0200
parents f95830b58336
children
comparison
equal deleted inserted replaced
852:f95830b58336 854:79b5c1be9a5e
1 '''Game main module.
2
3 Contains the entry point used by the run_game.py script.
4
5 '''
6 import sys
7 import gettext
8 import locale
9 import os
10 from optparse import OptionParser
11
12 import pygame
13 from pygame.locals import SWSURFACE
14
15 from pyntnclick.i18n import _, get_module_i18n_path
16 from pyntnclick.engine import Engine
17 from pyntnclick.gamescreen import DefMenuScreen, DefEndScreen, GameScreen
18 from pyntnclick.constants import GameConstants, DEBUG_ENVVAR
19 from pyntnclick.resources import Resources
20 from pyntnclick.sound import Sound
21 from pyntnclick import state
22
23 from pyntnclick.tools.rect_drawer import (RectEngine, RectDrawerError,
24 make_rect_display)
25 from pyntnclick.utils import list_scenes
26
27
28 class GameDescriptionError(Exception):
29 """Raised when an GameDescription is invalid."""
30
31
32 class GameDescription(object):
33
34 # initial scene for start of game (unless overridden by debug)
35 INITIAL_SCENE = None
36
37 # list of game scenes
38 SCENE_LIST = None
39
40 # starting menu
41 SCREENS = {
42 'menu': DefMenuScreen,
43 'end': DefEndScreen,
44 }
45
46 START_SCREEN = 'menu'
47
48 # Modules
49 RESOURCE_MODULE = 'data'
50 SCENE_MODULE = 'gamelib.scenes'
51
52 def __init__(self):
53 if self.INITIAL_SCENE is None:
54 raise GameDescriptionError("A game must have an initial scene.")
55 if not self.SCENE_LIST:
56 raise GameDescriptionError("A game must have a non-empty list"
57 " of scenes.")
58 if 'game' in self.SCREENS:
59 raise GameDescriptionError("The 'game' screen is reserved for the"
60 " game itself.")
61 self._initial_scene = self.INITIAL_SCENE
62 self._scene_list = self.SCENE_LIST
63 self._resource_module = self.RESOURCE_MODULE
64 self._debug_rects = False
65 self._screens = self.SCREENS.copy()
66 self._screens['game'] = GameScreen
67 self.constants = self.game_constants()
68
69 locale.setlocale(locale.LC_ALL, "")
70 lang = locale.getdefaultlocale(['LANGUAGE', 'LC_ALL', 'LC_CTYPE',
71 'LANG'])[0]
72 self.resource = Resources(self._resource_module, lang)
73 locale_path = self.resource.get_resource_path('locale')
74 gettext.bindtextdomain(self.constants.short_name, locale_path)
75 gettext.textdomain(self.constants.short_name)
76
77 popath = self.resource.get_resource_path('po')
78 self._check_translations(popath, locale_path)
79
80 self.sound = Sound(self.resource)
81 self.debug_options = []
82 self.running = False
83
84 def _check_translations(self, popath, locale_path):
85 """Check for outdated mo files"""
86 name = gettext.textdomain() # only check the current app
87 for candidate in os.listdir(popath):
88 if candidate.endswith('.po'):
89 polang = candidate.split('.', 1)[0]
90 pofile = os.path.join(popath, candidate)
91 mofile = gettext.find(name, locale_path, (polang,))
92 if mofile is None:
93 print 'Missing mo file for %s' % pofile
94 continue
95 if os.stat(pofile).st_mtime > os.stat(mofile).st_mtime:
96 print 'po file %s is newer than mo file %s' % (pofile,
97 mofile)
98
99 def initial_state(self, game_state=None):
100 """Create a copy of the initial game state."""
101 initial_state = state.Game(self, self.game_state_class()(game_state))
102 initial_state.set_debug_rects(self._debug_rects)
103 for scene in self._scene_list:
104 initial_state.load_scenes(scene)
105 if initial_state.data['current_scene'] is None:
106 initial_state.data.set_current_scene(self._initial_scene)
107 initial_state.change_scene(initial_state.data['current_scene'])
108 return initial_state
109
110 def game_state_class(self):
111 return state.GameState
112
113 def game_constants(self):
114 return GameConstants()
115
116 def option_parser(self):
117 parser = OptionParser()
118 parser.add_option("--no-sound", action="store_false", default=True,
119 dest="sound", help="disable sound")
120 # We flag these, so we can warn the user that these require debug mode
121 self.debug_options = ['--scene', '--no-rects', '--rect-drawer',
122 '--list-scenes', '--details']
123 if self.constants.debug:
124 parser.add_option("--scene", type="str", default=None,
125 dest="scene", help="initial scene")
126 parser.add_option("--no-rects", action="store_false", default=True,
127 dest="rects", help="disable debugging rects")
128 parser.add_option("--rect-drawer", action="store_true",
129 default=False, dest="rect_drawer",
130 help="Launch the rect drawing helper tool. Specify the"
131 " scene with --scene")
132 parser.add_option("--list-scenes", action="store_true",
133 default=False, dest='list_scenes', help="List all scenes"
134 " that can be used with --scene and exit.")
135 parser.add_option("--detail", type="str", default=None,
136 dest="detail", help="Detailed view for rect_drawer")
137 return parser
138
139 def warn_debug(self, option):
140 """Warn the user that he needs debug mode"""
141 print '%s is only valid in debug mode' % option
142 print 'set %s to enable debug mode' % DEBUG_ENVVAR
143 print
144
145 def main(self):
146 parser = self.option_parser()
147 # This is a bit hack'ish, but works
148 if not self.constants.debug:
149 for option in self.debug_options:
150 if option in sys.argv:
151 self.warn_debug(option)
152 opts, args = parser.parse_args(sys.argv)
153 pygame.display.init()
154 pygame.font.init()
155 if opts.sound:
156 self.sound.enable_sound(self.constants)
157 else:
158 self.sound.disable_sound()
159 if self.constants.debug:
160 if opts.scene is not None:
161 # debug the specified scene
162 self._initial_scene = opts.scene
163 self._debug_rects = opts.rects
164 if self.constants.debug and opts.list_scenes:
165 list_scenes(self.SCENE_MODULE, self._scene_list)
166 sys.exit(0)
167 if self.constants.debug and opts.rect_drawer:
168 if opts.scene is None:
169 print 'Need to supply a scene to use the rect drawer'
170 sys.exit(1)
171 locale_path = get_module_i18n_path(
172 self.resource.DEFAULT_RESOURCE_MODULE)
173 gettext.bindtextdomain('pyntnclick-tools', locale_path)
174 gettext.textdomain('pyntnclick-tools')
175 popath = get_module_i18n_path(
176 self.resource.DEFAULT_RESOURCE_MODULE, 'po')
177 self._check_translations(popath, locale_path)
178 make_rect_display()
179 try:
180 self.engine = RectEngine(self, opts.detail)
181 except RectDrawerError, e:
182 print 'RectDrawer failed with: %s' % e
183 sys.exit(1)
184 else:
185 pygame.display.set_mode(self.constants.screen, SWSURFACE)
186 if self.constants.icon:
187 pygame.display.set_icon(self.resource.get_image(
188 self.constants.icon, basedir='icons'))
189 if self.constants.title:
190 title = _(self.constants.title).encode('utf-8')
191 pygame.display.set_caption(title)
192
193 self.engine = Engine(self)
194 # Initialize the special screens in the engine
195 for name, cls in self._screens.iteritems():
196 screen = cls(self)
197 self.engine.add_screen(name, screen)
198 # Should we allow the menu not to be the opening screen?
199 self.engine.set_screen(self.START_SCREEN)
200 try:
201 self.engine.run()
202 except KeyboardInterrupt:
203 pass
204
205 def get_default_save_location(self):
206 """Return a default save game location."""
207 app = self.constants.short_name
208 if sys.platform.startswith("win"):
209 if "APPDATA" in os.environ:
210 return os.path.join(os.environ["APPDATA"], app)
211 return os.path.join(os.path.expanduser("~"), "." + app)
212 elif 'XDG_DATA_HOME' in os.environ:
213 return os.path.join(os.environ["XDG_DATA_HOME"], app)
214 return os.path.join(os.path.expanduser("~"), ".local", "share", app)