Mercurial > rinkhals
comparison gamelib/savegame.py @ 486:8897a436a8cb
Factor out save game logic and (new, simplified) dialogs into their own module. Add preferences folder to concept to config. Save games under preferences folder.
author | Simon Cross <hodgestar@gmail.com> |
---|---|
date | Wed, 25 Nov 2009 21:50:54 +0000 |
parents | |
children | 75ef6ea3b7a6 |
comparison
equal
deleted
inserted
replaced
485:6f0385ebcb4f | 486:8897a436a8cb |
---|---|
1 """Utilities and widgets for saving and restoring games.""" | |
2 | |
3 import xmlrpclib | |
4 import os | |
5 | |
6 from pgu import gui | |
7 | |
8 import config | |
9 import version | |
10 | |
11 | |
12 class BaseSaveRestoreDialog(gui.Dialog): | |
13 """Save game dialog.""" | |
14 | |
15 def __init__(self, title_txt, button_txt, allow_new, cls="dialog"): | |
16 self.value = None | |
17 self.save_folder = config.config.save_folder | |
18 | |
19 self.save_games = {} | |
20 self._populate_save_games() | |
21 | |
22 if allow_new: | |
23 self.name_input = gui.Input() | |
24 else: | |
25 self.name_input = None | |
26 | |
27 td_style = { | |
28 'padding_left': 4, | |
29 'padding_right': 4, | |
30 'padding_top': 2, | |
31 'padding_bottom': 2, | |
32 } | |
33 | |
34 self.save_list = gui.List(width=350, height=250) | |
35 games = self.save_games.keys() | |
36 games.sort() | |
37 for name in games: | |
38 self.save_list.add(name, value=name) | |
39 self.save_list.set_vertical_scroll(0) | |
40 self.save_list.connect(gui.CHANGE, self._save_list_change) | |
41 | |
42 button_ok = gui.Button(button_txt) | |
43 button_ok.connect(gui.CLICK, self._click_ok) | |
44 | |
45 button_cancel = gui.Button("Cancel") | |
46 button_cancel.connect(gui.CLICK, self._click_cancel) | |
47 | |
48 body = gui.Table() | |
49 body.tr() | |
50 body.td(self.save_list, style=td_style, colspan=2) | |
51 body.td(gui.Label("Image"), style=td_style, colspan=2) | |
52 body.tr() | |
53 if self.name_input: | |
54 body.td(gui.Label("Save as:"), style=td_style, align=1) | |
55 body.td(self.name_input, style=td_style) | |
56 else: | |
57 body.td(gui.Spacer(0, 0), style=td_style, colspan=2) | |
58 body.td(button_ok, style=td_style, align=1) | |
59 body.td(button_cancel, style=td_style, align=1) | |
60 | |
61 title = gui.Label(title_txt, cls=cls + ".title.label") | |
62 gui.Dialog.__init__(self, title, body) | |
63 | |
64 def get_fullpath(self): | |
65 """Return the fullpath of the select save game file or None.""" | |
66 if self.value is None: | |
67 return None | |
68 return os.path.join(self.save_folder, self.value + ".xml") | |
69 | |
70 def _populate_save_games(self): | |
71 """Read list of save games.""" | |
72 for filename in os.listdir(self.save_folder): | |
73 fullpath = os.path.join(self.save_folder, filename) | |
74 root, ext = os.path.splitext(filename) | |
75 if not os.path.isfile(fullpath): | |
76 continue | |
77 if ext != ".xml": | |
78 continue | |
79 self.save_games[root] = None | |
80 | |
81 def _save_list_change(self): | |
82 if self.name_input: | |
83 self.name_input.value = self.save_list.value | |
84 | |
85 def _click_ok(self): | |
86 if self.name_input: | |
87 self.value = self.name_input.value | |
88 else: | |
89 self.value = self.save_list.value | |
90 if self.value: | |
91 self.send(gui.CHANGE) | |
92 self.close() | |
93 | |
94 def _click_cancel(self): | |
95 self.value = None | |
96 self.send(gui.CHANGE) | |
97 self.close() | |
98 | |
99 | |
100 class SaveDialog(BaseSaveRestoreDialog): | |
101 """Save game dialog.""" | |
102 | |
103 def __init__(self, gameboard): | |
104 BaseSaveRestoreDialog.__init__(self, "Save Game ...", "Save", allow_new=True) | |
105 self.connect(gui.CHANGE, self._save, gameboard) | |
106 | |
107 def _save(self, gameboard): | |
108 filename = self.get_fullpath() | |
109 if filename is None: | |
110 return | |
111 data = gameboard.save_game() | |
112 params = (version.SAVE_GAME_VERSION, data) | |
113 xml = xmlrpclib.dumps(params, "foxassault") | |
114 try: | |
115 open(filename, "wb").write(xml) | |
116 except Exception, e: | |
117 print "Failed to save game: %s" % (e,) | |
118 | |
119 | |
120 class RestoreDialog(BaseSaveRestoreDialog): | |
121 """Restore game dialog.""" | |
122 | |
123 def __init__(self, gameboard): | |
124 BaseSaveRestoreDialog.__init__(self, "Load Game ...", "Load", allow_new=False) | |
125 self.connect(gui.CHANGE, self._restore, gameboard) | |
126 | |
127 def _restore(self, gameboard): | |
128 filename = self.get_fullpath() | |
129 if filename is None: | |
130 return | |
131 try: | |
132 xml = open(filename, "rb").read() | |
133 params, methodname = xmlrpclib.loads(xml) | |
134 if methodname != "foxassault": | |
135 raise ValueError("File does not appear to be a " | |
136 "Fox Assault save game.") | |
137 save_version = params[0] | |
138 if save_version != version.SAVE_GAME_VERSION: | |
139 raise ValueError("Incompatible save game version.") | |
140 data = params[1] | |
141 except Exception, e: | |
142 "Failed to load game: %s" % (e,) | |
143 return | |
144 | |
145 gameboard.restore_game(data) |