Mercurial > boomslang
comparison pyntnclick/gamescreen.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 # gamescreen.py | |
2 # Copyright Boomslang team, 2010 (see COPYING File) | |
3 # Main menu for the game | |
4 | |
5 import pygame.draw | |
6 from pygame import Surface | |
7 from pygame.color import Color | |
8 from pygame.locals import MOUSEBUTTONDOWN, MOUSEMOTION, KEYDOWN, K_ESCAPE | |
9 | |
10 from pyntnclick.i18n import _ | |
11 from pyntnclick.cursor import CursorScreen | |
12 from pyntnclick.engine import Screen | |
13 from pyntnclick.widgets.base import ( | |
14 Container, ModalStackContainer, ModalWrapper) | |
15 from pyntnclick.widgets.text import TextButton, WrappedTextLabel | |
16 from pyntnclick.widgets.imagebutton import ImageButtonWidget | |
17 | |
18 | |
19 class InventorySlot(ImageButtonWidget): | |
20 SELECTED_COLOR = Color("yellow") | |
21 SELECTED_WIDTH = 2 | |
22 | |
23 def __init__(self, pos, gd, size): | |
24 self.item = None | |
25 super(InventorySlot, self).__init__(pos, gd, None, size) | |
26 self.add_callback(MOUSEBUTTONDOWN, self.mouse_down) | |
27 | |
28 def set_item(self, item): | |
29 self.item = item | |
30 | |
31 def draw(self, surface): | |
32 if self.item: | |
33 surface.blit(self.item.get_inventory_image(), self.rect) | |
34 if self.selected: | |
35 pygame.draw.rect(surface, self.SELECTED_COLOR, | |
36 self.rect, self.SELECTED_WIDTH) | |
37 | |
38 @property | |
39 def selected(self): | |
40 return self.parent.game.tool is self.item | |
41 | |
42 def mouse_down(self, event, widget): | |
43 if event.button != 1 or not self.item: | |
44 return | |
45 if self.selected: | |
46 self.parent.select(None) | |
47 elif self.item.is_interactive(self.parent.game.tool): | |
48 result = self.item.interact(self.parent.game.tool) | |
49 self.parent.screen.handle_result(result) | |
50 else: | |
51 self.parent.select(self.item) | |
52 | |
53 | |
54 class UpDownButton(TextButton): | |
55 # TextButton for now. | |
56 def __init__(self, pos, gd, size=None): | |
57 super(UpDownButton, self).__init__(pos, gd, self.TEXT, size=size, | |
58 padding=3) | |
59 | |
60 | |
61 class UpButton(UpDownButton): | |
62 TEXT = u'\N{UPWARDS ARROW}UP' | |
63 | |
64 | |
65 class DownButton(UpDownButton): | |
66 TEXT = u'\N{DOWNWARDS ARROW}DN' | |
67 | |
68 | |
69 class InventoryView(Container): | |
70 MIN_UPDOWN_WIDTH = 20 | |
71 | |
72 def __init__(self, pos, gd, size, screen): | |
73 self.bsize = gd.constants.button_size | |
74 super(InventoryView, self).__init__(pos, gd, size) | |
75 self.screen = screen | |
76 self.game = screen.game | |
77 | |
78 slots = (self.rect.width - self.MIN_UPDOWN_WIDTH) / self.bsize | |
79 self.slots = [self.add(self.make_slot(i)) for i in range(slots)] | |
80 self.inv_offset = 0 | |
81 | |
82 self.updown_width = self.rect.width - slots * self.bsize | |
83 ud_left = self.rect.right - self.updown_width | |
84 self.up_button = self.add(UpButton((ud_left, self.rect.top), gd, | |
85 (self.updown_width, self.rect.height / 2))) | |
86 self.up_button.add_callback(MOUSEBUTTONDOWN, self.up_callback) | |
87 self.down_button = self.add(DownButton( | |
88 (ud_left, self.rect.top + self.rect.height / 2), gd, | |
89 (self.updown_width, self.rect.height / 2))) | |
90 self.down_button.add_callback(MOUSEBUTTONDOWN, self.down_callback) | |
91 | |
92 self.add_callback(MOUSEBUTTONDOWN, self.mouse_down) | |
93 self.update_slots() | |
94 | |
95 def make_slot(self, slot): | |
96 pos = (self.rect.left + slot * self.bsize, self.rect.top) | |
97 size = (self.bsize, self.rect.height) | |
98 return InventorySlot(pos, self.gd, size) | |
99 | |
100 def up_callback(self, event, widget): | |
101 self.inv_offset = max(self.inv_offset - len(self.slots), 0) | |
102 self.update_slots() | |
103 | |
104 def down_callback(self, event, widget): | |
105 self.inv_offset += len(self.slots) | |
106 self.update_slots() | |
107 | |
108 def update_slots(self): | |
109 items = (self.slot_items + [None] * len(self.slots))[:len(self.slots)] | |
110 for item, slot in zip(items, self.slots): | |
111 slot.set_item(item) | |
112 | |
113 if self.inv_offset <= 0: | |
114 self.up_button.disable() | |
115 else: | |
116 self.up_button.enable() | |
117 | |
118 max_slot = (self.inv_offset + len(self.slots)) | |
119 if max_slot >= len(self.game.inventory()): | |
120 self.down_button.disable() | |
121 else: | |
122 self.down_button.enable() | |
123 | |
124 @property | |
125 def slot_items(self): | |
126 item_names = self.game.inventory()[self.inv_offset:][:len(self.slots)] | |
127 return [self.game.get_item(name) for name in item_names] | |
128 | |
129 def mouse_down(self, event, widget): | |
130 if event.button != 1: | |
131 self.game.set_tool(None) | |
132 | |
133 def select(self, tool): | |
134 self.game.set_tool(tool) | |
135 | |
136 | |
137 class SceneWidget(Container): | |
138 DETAIL_BORDER = 4 | |
139 DETAIL_BORDER_COLOR = Color("black") | |
140 | |
141 def __init__(self, pos, gd, size, scene, screen, is_detail=False): | |
142 super(SceneWidget, self).__init__(pos, gd, size) | |
143 self.name = scene.NAME | |
144 self.scene = scene | |
145 self.screen = screen | |
146 self.game = screen.game | |
147 self.add_callback(MOUSEBUTTONDOWN, self.mouse_down) | |
148 self.add_callback(MOUSEMOTION, self.mouse_move) | |
149 self.is_detail = is_detail | |
150 if is_detail: | |
151 self.close_button = TextButton((0, 0), self.gd, _("Close")) | |
152 self.close_button.do_prepare() | |
153 # TODO: Don't muck around with close_button's rect | |
154 self.close_button.rect.midbottom = self.rect.midbottom | |
155 self.close_button.add_callback('clicked', self.close) | |
156 self.add(self.close_button) | |
157 | |
158 def draw(self, surface): | |
159 self.scene.draw(surface.subsurface(self.rect)) | |
160 if self.is_detail: | |
161 border = self.rect.inflate(self.DETAIL_BORDER, self.DETAIL_BORDER) | |
162 pygame.draw.rect( | |
163 surface, self.DETAIL_BORDER_COLOR, border, self.DETAIL_BORDER) | |
164 if self.parent.is_top(self): | |
165 self.scene.draw_description(surface) | |
166 super(SceneWidget, self).draw(surface) | |
167 | |
168 @property | |
169 def highlight_cursor(self): | |
170 return (self.scene.current_thing | |
171 and self.scene.current_thing.is_interactive()) | |
172 | |
173 def mouse_down(self, event, widget): | |
174 self.mouse_move(event, widget) | |
175 if event.button != 1: # We have a right/middle click | |
176 if self.game.tool: | |
177 self.game.set_tool(None) | |
178 elif self.is_detail: | |
179 self.close(event, widget) | |
180 else: | |
181 pos = self.global_to_local(event.pos) | |
182 result = self.scene.interact(self.game.tool, pos) | |
183 self.screen.handle_result(result) | |
184 | |
185 def animate(self): | |
186 self.scene.animate() | |
187 | |
188 def mouse_move(self, event, widget): | |
189 pos = self.global_to_local(event.pos) | |
190 self.scene.mouse_move(pos) | |
191 self.game.old_pos = event.pos | |
192 | |
193 def close(self, event, widget): | |
194 self.screen.close_detail(self) | |
195 | |
196 | |
197 class ToolBar(Container): | |
198 def __init__(self, pos, gd, size, screen): | |
199 self.screen = screen | |
200 | |
201 super(ToolBar, self).__init__(pos, gd, size) | |
202 | |
203 self.bg_color = (31, 31, 31) | |
204 self.left = self.rect.left | |
205 | |
206 self.menu_button = self.add_tool( | |
207 self.rect.height, TextButton, _("Menu"), | |
208 fontname=gd.constants.bold_font, | |
209 color="red", padding=1, border=0, bg_color="black") | |
210 self.menu_button.add_callback(MOUSEBUTTONDOWN, self.menu_callback) | |
211 | |
212 hand_image = gd.resource.get_image('items', 'hand.png') | |
213 self.hand_button = self.add_tool( | |
214 None, ImageButtonWidget, hand_image) | |
215 self.hand_button.add_callback(MOUSEBUTTONDOWN, self.hand_callback) | |
216 | |
217 self.inventory = self.add_tool( | |
218 self.rect.width - self.left, InventoryView, screen=screen) | |
219 | |
220 def add_tool(self, width, cls, *args, **kw): | |
221 pos = (self.left, self.rect.top) | |
222 if width is not None: | |
223 kw['size'] = (width, self.rect.height) | |
224 tool = cls(pos, self.gd, *args, **kw) | |
225 self.add(tool) | |
226 tool.do_prepare() | |
227 self.left += tool.rect.width | |
228 return tool | |
229 | |
230 def draw(self, surface): | |
231 bg = Surface(self.rect.size) | |
232 bg.fill(self.bg_color) | |
233 surface.blit(bg, self.rect) | |
234 super(ToolBar, self).draw(surface) | |
235 | |
236 def hand_callback(self, event, widget): | |
237 self.inventory.select(None) | |
238 | |
239 def menu_callback(self, event, widget): | |
240 self.screen.change_screen('menu') | |
241 | |
242 | |
243 class GameScreen(CursorScreen): | |
244 | |
245 def setup(self): | |
246 super(GameScreen, self).setup() | |
247 self.gd.running = False | |
248 self.create_initial_state = self.gd.initial_state | |
249 self.container.add_callback(KEYDOWN, self.key_pressed) | |
250 | |
251 def _clear_all(self): | |
252 self._message_queue = [] | |
253 for widget in self.container.children[:]: | |
254 self.container.remove(widget) | |
255 | |
256 def process_event(self, event_name, data): | |
257 getattr(self, 'game_event_%s' % event_name, lambda d: None)(data) | |
258 | |
259 def game_event_restart(self, data): | |
260 self.reset_game() | |
261 | |
262 def get_save_dir(self): | |
263 return self.gd.get_default_save_location() | |
264 | |
265 def game_event_load(self, data): | |
266 state = self.gd.game_state_class().load_game( | |
267 self.get_save_dir(), 'savegame') | |
268 # TODO: Handle this better. | |
269 if state is not None: | |
270 self.reset_game(state) | |
271 | |
272 def game_event_save(self, data): | |
273 self.game.data.save_game(self.get_save_dir(), 'savegame') | |
274 | |
275 def reset_game(self, game_state=None): | |
276 self._clear_all() | |
277 self.game = self.create_initial_state(game_state) | |
278 | |
279 self.screen_modal = self.container.add( | |
280 ModalStackContainer(self.container.pos, self.gd, | |
281 self.container.size)) | |
282 self.inner_container = self.screen_modal.add( | |
283 Container(self.container.pos, self.gd, self.container.size)) | |
284 | |
285 toolbar_height = self.gd.constants.button_size | |
286 | |
287 self.scene_modal = self.inner_container.add( | |
288 ModalStackContainer((0, 0), self.gd, | |
289 (self.surface_size[0], self.surface_size[1] - toolbar_height))) | |
290 self.toolbar = self.inner_container.add( | |
291 ToolBar((0, self.surface_size[1] - toolbar_height), self.gd, | |
292 (self.surface_size[0], toolbar_height), self)) | |
293 self.inventory = self.toolbar.inventory | |
294 | |
295 self.gd.running = True | |
296 | |
297 def game_event_inventory(self, data): | |
298 self.inventory.update_slots() | |
299 | |
300 def game_event_change_scene(self, data): | |
301 scene_name = data['name'] | |
302 if data['detail']: | |
303 self.show_detail(scene_name) | |
304 else: | |
305 self.change_scene(scene_name) | |
306 | |
307 def change_scene(self, scene_name): | |
308 for scene_widget in reversed(self.scene_modal.children[:]): | |
309 self.scene_modal.remove(scene_widget) | |
310 scene_widget.scene.leave() | |
311 self.game.data.set_current_scene(scene_name) | |
312 self._add_scene(self.game.scenes[scene_name]) | |
313 | |
314 def show_detail(self, detail_name): | |
315 detail_scene = self.game.detail_views[detail_name] | |
316 if detail_scene.name == self.scene_modal.top.name: | |
317 # Don't show the scene if we're already showing it | |
318 return | |
319 self._add_scene(detail_scene, True) | |
320 | |
321 def _add_scene(self, scene, detail=False): | |
322 pos = self.scene_modal.rect.topleft | |
323 size = self.scene_modal.rect.size | |
324 if detail: | |
325 size = scene.get_detail_size() | |
326 pos = ((self.scene_modal.rect.width - size[0]) / 2, | |
327 (self.scene_modal.rect.height - size[1]) / 2) | |
328 | |
329 self.scene_modal.add(SceneWidget(pos, self.gd, size, scene, self, | |
330 detail)) | |
331 self.handle_result(scene.enter()) | |
332 | |
333 def close_detail(self, detail=None): | |
334 if detail is None: | |
335 detail = self.scene_modal.top | |
336 self.scene_modal.remove(detail) | |
337 self.handle_result(detail.scene.leave()) | |
338 | |
339 def animate(self): | |
340 """Animate the scene widgets""" | |
341 for scene_widget in self.scene_modal.children: | |
342 scene_widget.animate() | |
343 | |
344 def key_pressed(self, event, widget): | |
345 if event.key == K_ESCAPE: | |
346 self.change_screen('menu') | |
347 | |
348 def end_game(self): | |
349 self.change_screen('end') | |
350 | |
351 def show_queued_widget(self): | |
352 if self._message_queue: | |
353 # Only add a message if there isn't already one up | |
354 if self.screen_modal.is_top(self.inner_container): | |
355 widget = self._message_queue.pop(0) | |
356 self.screen_modal.add( | |
357 ModalWrapper(widget, self.show_queued_widget)) | |
358 | |
359 def queue_widget(self, widget): | |
360 self._message_queue.append(widget) | |
361 self.show_queued_widget() | |
362 | |
363 def show_message(self, message): | |
364 max_width = self.gd.constants.screen[0] - 100 | |
365 widget = WrappedTextLabel((0, 0), self.gd, message, | |
366 max_width=max_width) | |
367 widget.do_prepare() | |
368 # TODO: Use the centering API when it exists | |
369 widget.rect.center = self.container.rect.center | |
370 self.queue_widget(widget) | |
371 | |
372 def handle_result(self, resultset): | |
373 """Handle dealing with result or result sequences""" | |
374 if resultset: | |
375 if hasattr(resultset, 'process'): | |
376 resultset = [resultset] | |
377 for result in resultset: | |
378 if result: | |
379 result.process(self) | |
380 | |
381 | |
382 class DefEndScreen(Screen): | |
383 """A placeholder 'Game Over' screen so people can get started easily""" | |
384 | |
385 def setup(self): | |
386 self.background = self.resource.get_image('pyntnclick/end.png') | |
387 | |
388 def draw(self, surface): | |
389 surface.blit(self.background, (0, 0)) | |
390 | |
391 | |
392 class DefMenuScreen(Screen): | |
393 """A placeholder Start screen so people can get started easily""" | |
394 | |
395 def setup(self): | |
396 self.background = self.resource.get_image('pyntnclick/start.png') | |
397 | |
398 def draw(self, surface): | |
399 surface.blit(self.background, (0, 0)) |