Mercurial > pyntnclick
changeset 511:93ddcac0b772
Merged in engine refactorings.
author | Jeremy Thurgood <firxen@gmail.com> |
---|---|
date | Sat, 04 Sep 2010 09:53:00 +0200 |
parents | d274cc414178 (diff) c1f4f9149349 (current diff) |
children | b10dae40dc32 |
files | gamelib/state.py |
diffstat | 10 files changed, 278 insertions(+), 79 deletions(-) [+] |
line wrap: on
line diff
--- a/TODO Sun Aug 29 20:38:03 2010 +0200 +++ b/TODO Sat Sep 04 09:53:00 2010 +0200 @@ -9,11 +9,16 @@ * Fix thing.enter being called multiple times without leaving Hints: - * More help with the starting puzzle - * Interacts with broken pipes & cryo/pipe / tins + * More help with the starting puzzle (another interactable cryo-chamber?) + * Interacts with broken pipes & cryo/pipe / tins / < 3 fixed pipe * Interacts with laser cutter & tins * Interacts with cameras * Interacts with cryo-resevoirs & empty bottle + * Interacts with cryo-puddles & cans. + * Interacts with fish bowl & engine room + * Make JIM messages more clearly from JIM. + * Make it clearer who JIM is (That he is separate from the ship computers) + * JIM Speaks after fixing engines & repairing environment? Game: * Keep track of total sentence.
--- a/gamelib/scenes/engine.py Sun Aug 29 20:38:03 2010 +0200 +++ b/gamelib/scenes/engine.py Sat Sep 04 09:53:00 2010 +0200 @@ -24,7 +24,6 @@ super(Engine, self).__init__(state) self.add_item(CanOpener('canopener')) self.add_thing(CanOpenerThing()) - self.add_item(BrokenSuperconductor('superconductor_broken')) self.add_thing(SuperconductorSocket()) self.add_thing(PowerLines()) self.add_thing(CryoContainers()) @@ -45,12 +44,6 @@ (549, 479, 60, 55), ) )) - self.add_thing(GenericDescThing('engine.controlpanel', 2, - "A control panel. It seems dead.", - ( - (513, 330, 58, 50), - ) - )) self.add_thing(GenericDescThing('engine.superconductors', 4, "Superconductors. The engines must be power hogs.", ( @@ -60,7 +53,8 @@ ) )) self.add_thing(GenericDescThing('engine.floor_hole', 5, - "A gaping hole in the floor of the room. You're guessing that's why there's a vacuum in here.", + "A gaping hole in the floor of the room. " + "It is clearly irreparable.", ( (257, 493, 141, 55), (301, 450, 95, 45), @@ -119,7 +113,8 @@ ) )) self.add_thing(GenericDescThing('engine.exit_sign', 13, - "It's one of those glow-in-the-dark exit signs that you see everywhere.", + "It's one of those glow-in-the-dark signs needed to satisfy the " + "health and safety inspectors.", ( (681, 322, 80, 33), ) @@ -182,11 +177,6 @@ "the vacuum has kept it in perfect condition.") -class BrokenSuperconductor(Item): - INVENTORY_IMAGE = 'superconductor_broken.png' - CURSOR = CursorSprite('superconductor_broken_cursor.png') - - class SuperconductorSocket(Thing): NAME = 'engine.superconductor' @@ -221,8 +211,8 @@ if self.get_data('present') and not self.get_data('working'): self.set_interact('removed') self.set_data('present', False) - self.state.add_inventory_item('superconductor_broken') - return Result("With leverage, the burned-out superconductor snaps out.") + return Result("With leverage, the burned-out superconductor snaps out. " + "You discard it.") def interact_with_superconductor(self, item): if self.get_data('present'): @@ -503,7 +493,10 @@ NAME = "engine.computer_console" INTERACTS = { - 'console': InteractNoImage(293, 287, 39, 36), + 'console': InteractRectUnion(( + (293, 287, 39, 36), + (513, 330, 58, 50), + )), } INITIAL = 'console'
--- a/gamelib/scenes/scene_widgets.py Sun Aug 29 20:38:03 2010 +0200 +++ b/gamelib/scenes/scene_widgets.py Sat Sep 04 09:53:00 2010 +0200 @@ -184,21 +184,31 @@ class BaseCamera(Thing): "Base class for the camera puzzles" - + INITIAL = 'online' INITIAL_DATA = { 'state': 'online', } - + def get_description(self): - return "A security camera watches over the room" - + status = self.state.scenes['bridge'].get_data('ai status') + if status == 'online': + return "A security camera watches over the room" + elif status == 'looping': + return "The security camera is currently offline but should be working soon" + else: + return "The security camera is powered down" + + def is_interactive(self): + return self.state.scenes['bridge'].get_data('ai status') == 'online' + def interact_with_escher_poster(self, item): # Order matters here, because of helper function - ai_response = make_jim_dialog("3D scene reconstruction failed. Critical error. Entering emergency shutdown.", self.state) - self.state.scenes['bridge'].set_data('ai status', 'looping') - return ai_response - + if self.state.scenes['bridge'].get_data('ai status') == 'online': + ai_response = make_jim_dialog("3D scene reconstruction failed. Critical error. Entering emergency shutdown.", self.state) + self.state.scenes['bridge'].set_data('ai status', 'looping') + return ai_response + def animate(self): ai_status = self.state.scenes['bridge'].get_data('ai status') if ai_status != self.get_data('status'):
--- a/gamelib/state.py Sun Aug 29 20:38:03 2010 +0200 +++ b/gamelib/state.py Sat Sep 04 09:53:00 2010 +0200 @@ -1,5 +1,7 @@ """Utilities and base classes for dealing with scenes.""" +import copy + from albow.resource import get_image, get_font from albow.utils import frame_rect from widgets import BoomLabel @@ -237,7 +239,9 @@ def __init__(self): self.data = {} if self.INITIAL_DATA: - self.data.update(self.INITIAL_DATA) + # deep copy of INITIAL_DATA allows lists, sets and + # other mutable types to safely be used in INITIAL_DATA + self.data.update(copy.deepcopy(self.INITIAL_DATA)) def set_data(self, key, value): self.data[key] = value
--- a/tools/rect_drawer.py Sun Aug 29 20:38:03 2010 +0200 +++ b/tools/rect_drawer.py Sat Sep 04 09:53:00 2010 +0200 @@ -10,16 +10,22 @@ from albow.root import RootWidget from albow.utils import frame_rect from albow.widget import Widget -from albow.controls import Button, Image +from albow.controls import Button, Image, Label from albow.palette_view import PaletteView from albow.file_dialogs import request_old_filename from albow.resource import get_font -from pygame.locals import SWSURFACE, K_LEFT, K_RIGHT, K_UP, K_DOWN +from pygame.locals import SWSURFACE, K_LEFT, K_RIGHT, K_UP, K_DOWN, \ + K_a, K_t, K_d, K_i, K_r, K_o, K_b, K_z, \ + BLEND_RGBA_MIN, SRCALPHA import pygame from pygame.colordict import THECOLORS from gamelib import constants constants.DEBUG = True +MENU_WIDTH = 200 +MENU_BUTTON_HEIGHT = 25 +ZOOM = 4 +ZOOM_STEP = 100 from gamelib import state state.DEBUG_RECTS = True @@ -39,7 +45,7 @@ def __init__(self, app_image): self.image = app_image - super(AppPalette, self).__init__((35, 35), 5, 5, margin=2) + super(AppPalette, self).__init__((35, 35), 4, 5, margin=2) self.selection = 0 self.image.rect_color = pygame.color.Color(self.colors[self.selection]) @@ -66,7 +72,7 @@ def __init__(self, state): self.state = state - super(AppImage, self).__init__(pygame.rect.Rect(0, 0, 800, 600)) + super(AppImage, self).__init__(pygame.rect.Rect(0, 0, constants.SCREEN[0], constants.SCREEN[1])) self.mode = 'draw' self.rects = [] self.images = [] @@ -90,15 +96,23 @@ self.draw_things = True self.draw_thing_rects = True self.draw_images = True + self.trans_images = False self.draw_toolbar = True + self.old_mouse_pos = None + self.zoom_display = False + self.draw_anim = False + self.zoom_offset = (600, 600) self.find_existing_intersects() + def _get_scene(self): + if self.state.current_detail: + return self.state.current_detail + else: + return self.state.current_scene + def find_existing_intersects(self): """Parse the things in the scene for overlaps""" - if self.state.current_detail: - scene = self.state.current_detail - else: - scene = self.state.current_scene + scene = self._get_scene() # Pylint hates this function for thing in scene.things.itervalues(): for interact_name in thing.interacts: @@ -128,10 +142,7 @@ def find_intersecting_rects(self, d): """Find if any rect collections intersect""" # I loath N^X brute search algorithm's, but whatever, hey - if self.state.current_detail: - scene = self.state.current_detail - else: - scene = self.state.current_scene + scene = self._get_scene() for (num, col) in enumerate(d): rect_list = d[col] for thing in scene.things.itervalues(): @@ -169,10 +180,7 @@ def toggle_thing_rects(self): self.draw_thing_rects = not self.draw_thing_rects - if self.state.current_detail: - scene = self.state.current_detail - else: - scene = self.state.current_scene + scene = self._get_scene() for thing in scene.things.itervalues(): if not self.draw_thing_rects: if not hasattr(thing, 'old_colour'): @@ -184,12 +192,22 @@ def toggle_images(self): self.draw_images = not self.draw_images + def toggle_trans_images(self): + self.trans_images = not self.trans_images + self.invalidate() + def toggle_rects(self): self.draw_rects = not self.draw_rects def toggle_toolbar(self): self.draw_toolbar = not self.draw_toolbar + def toggle_zoom(self): + self.zoom_display = not self.zoom_display + + def toggle_anim(self): + self.draw_anim = not self.draw_anim + def draw_mode(self): self.mode = 'draw' @@ -198,7 +216,32 @@ self.start_pos = None self.end_pos = None + def draw_sub_image(self, image, surface, cropped_rect): + """Tweaked image drawing to avoid albow's centring the image in the + subsurface""" + surf = pygame.surface.Surface((cropped_rect.w, cropped_rect.h), SRCALPHA).convert_alpha() + frame = surf.get_rect() + imsurf = image.get_image().convert_alpha() + r = imsurf.get_rect() + r.topleft = frame.topleft + if self.trans_images: + surf.fill(pygame.color.Color(255, 255, 255, 96)) + surf.blit(imsurf, r, None, BLEND_RGBA_MIN) + else: + surf.blit(imsurf, r, None) + surface.blit(surf, cropped_rect) + def draw(self, surface): + if self.zoom_display: + base_surface = surface.copy() + self.do_unzoomed_draw(base_surface) + zoomed = pygame.transform.scale(base_surface, (ZOOM * constants.SCREEN[0], ZOOM * constants.SCREEN[1])) + area = pygame.rect.Rect(self.zoom_offset[0], self.zoom_offset[1], self.zoom_offset[0] + constants.SCREEN[0], self.zoom_offset[1] + constants.SCREEN[1]) + surface.blit(zoomed, (0, 0), area) + else: + self.do_unzoomed_draw(surface) + + def do_unzoomed_draw(self, surface): if self.state.current_detail: if self.draw_things: self.state.draw_detail(surface, None) @@ -222,20 +265,18 @@ for image in self.images: if image.rect.colliderect(surface.get_rect()): cropped_rect = image.rect.clip(surface.get_rect()) - sub = surface.subsurface(cropped_rect) - image.draw(sub) + self.draw_sub_image(image, surface, cropped_rect) else: print 'image outside surface', image if self.current_image and self.mode == 'image': if self.current_image.rect.colliderect(surface.get_rect()): cropped_rect = self.current_image.rect.clip(surface.get_rect()) - sub = surface.subsurface(cropped_rect) - self.current_image.draw(sub) + self.draw_sub_image(self.current_image, surface, cropped_rect) if self.draw_toolbar: - toolbar_rect = pygame.rect.Rect(0, 550, 800, 50) - tb_surf = surface.subsurface(0, 550, 800, 50).convert_alpha() + toolbar_rect = pygame.rect.Rect(0, constants.SCREEN[1] - constants.BUTTON_SIZE, constants.SCREEN[0], constants.BUTTON_SIZE) + tb_surf = surface.subsurface(0, constants.SCREEN[1] - constants.BUTTON_SIZE, constants.SCREEN[0], constants.BUTTON_SIZE).convert_alpha() tb_surf.fill(pygame.color.Color(127, 0, 0, 191)) - surface.blit(tb_surf, (0, 550)) + surface.blit(tb_surf, (0, constants.SCREEN[1] - constants.BUTTON_SIZE)) # frame_rect(surface, (127, 0, 0), toolbar_rect, 2) def _make_dict(self): @@ -271,7 +312,7 @@ self.current_image = Image(image_data) self.place_image_menu.enabled = True # ensure we're off screen to start - self.current_image.rect = image_data.get_rect().move(1000, 600) + self.current_image.rect = image_data.get_rect().move(constants.SCREEN[0] + MENU_WIDTH, constants.SCREEN[1]) except pygame.error, e: print 'Unable to load image %s' % e @@ -279,28 +320,99 @@ self.mode = 'image' self.start_pos = None self.end_pos = None + # So we do the right thing for off screen images + self.old_mouse_pos = None - def mouse_move(self, e): + def cycle_mode(self): + self.mode = 'cycle' + + def _conv_pos(self, mouse_pos): + if self.zoom_display: + pos = ((mouse_pos[0] + self.zoom_offset[0]) / ZOOM, (mouse_pos[1] + self.zoom_offset[1]) / ZOOM) + else: + pos = mouse_pos + return pos + + def _check_limits(self, offset): + if offset[0] < 0: + offset[0] = 0 + if offset[1] < 0: + offset[1] = 0 + if offset[0] > ZOOM * constants.SCREEN[0] - constants.SCREEN[0]: + offset[0] = ZOOM * constants.SCREEN[0] - constants.SCREEN[0] + if offset[1] > ZOOM * constants.SCREEN[1] - constants.SCREEN[1]: + offset[1] = ZOOM * constants.SCREEN[1] - constants.SCREEN[1] + + def _make_zoom_offset(self, pos): + zoom_pos = (pos[0] * ZOOM, pos[1] * ZOOM) + offset = [zoom_pos[0] - constants.SCREEN[0] / 2, + zoom_pos[1] - constants.SCREEN[1] / 2] + self._check_limits(offset) + self.zoom_offset = tuple(offset) + + def _move_zoom(self, x, y): + offset = list(self.zoom_offset) + offset[0] += ZOOM_STEP * x + offset[1] += ZOOM_STEP * y + self._check_limits(offset) + self.zoom_offset = tuple(offset) + + def do_mouse_move(self, e): + pos = self._conv_pos(e.pos) + if not self.zoom_display: + # Construct zoom offset from mouse pos + self._make_zoom_offset(e.pos) if self.mode == 'image' and self.current_image: - self.current_image.rect.topleft = e.pos + if self.old_mouse_pos: + delta = (pos[0] - self.old_mouse_pos[0], pos[1] - self.old_mouse_pos[1]) + self.current_image.rect.center = (self.current_image.rect.center[0] + delta[0], self.current_image.rect.center[1] + delta[1]) + else: + self.current_image.rect.center = pos self.invalidate() + self.old_mouse_pos = pos def key_down(self, e): if self.mode == 'image' and self.current_image: # Move the image by 1 pixel - cur_pos = self.current_image.rect.topleft + cur_pos = self.current_image.rect.center if e.key == K_LEFT: - self.current_image.rect.topleft = (cur_pos[0] - 1, cur_pos[1]) + self.current_image.rect.center = (cur_pos[0] - 1, cur_pos[1]) + elif e.key == K_RIGHT: + self.current_image.rect.center = (cur_pos[0] + 1, cur_pos[1]) + elif e.key == K_UP: + self.current_image.rect.center = (cur_pos[0], cur_pos[1] - 1) + elif e.key == K_DOWN: + self.current_image.rect.center = (cur_pos[0], cur_pos[1] + 1) + elif self.zoom_display: + if e.key == K_LEFT: + self._move_zoom(-1, 0) elif e.key == K_RIGHT: - self.current_image.rect.topleft = (cur_pos[0] + 1, cur_pos[1]) + self._move_zoom(1, 0) elif e.key == K_UP: - self.current_image.rect.topleft = (cur_pos[0], cur_pos[1] - 1) + self._move_zoom(0, -1) elif e.key == K_DOWN: - self.current_image.rect.topleft = (cur_pos[0], cur_pos[1] + 1) + self._move_zoom(0, 1) + + if e.key == K_o: + self.toggle_trans_images() + elif e.key == K_t: + self.toggle_things() + elif e.key == K_r: + self.toggle_thing_rects() + elif e.key == K_i: + self.toggle_images() + elif e.key == K_d: + self.toggle_rects() + elif e.key == K_b: + self.toggle_toolbar() + elif e.key == K_z: + self.toggle_zoom() + elif e.key == K_a: + self.toggle_anim() def mouse_down(self, e): + pos = self._conv_pos(e.pos) if self.mode == 'del': - pos = e.pos cand = None # Images are drawn above rectangles, so search those first for image in self.images: @@ -318,13 +430,31 @@ if cand: self.rects.remove(cand) self.invalidate() + elif self.mode == 'cycle': + scene = self._get_scene() + cand = None + for thing in scene.things.itervalues(): + if thing.contains(pos): + cand = thing + break + if cand: + # Find current interacts in this thing + cur_interact = cand.current_interact + j = cand.interacts.values().index(cur_interact) + if j + 1< len(cand.interacts): + next_name = cand.interacts.keys()[j+1] + else: + next_name = cand.interacts.keys()[0] + if cand.interacts[next_name] != cur_interact: + cand.set_interact(next_name) elif self.mode == 'draw': - self.start_pos = e.pos - self.end_pos = e.pos + self.start_pos = pos + self.end_pos = pos elif self.mode == 'image': if self.current_image: self.images.append(self.current_image) self.current_image = None + self.old_mouse_pos = None self.invalidate() else: cand = None @@ -335,7 +465,8 @@ if cand: self.images.remove(cand) self.current_image = cand - self.current_image.topleft = e.pos + # We want to move relative to the current mouse pos, so + self.old_mouse_pos = pos self.invalidate() def mouse_up(self, e): @@ -349,16 +480,37 @@ def mouse_drag(self, e): if self.mode == 'draw': - self.end_pos = e.pos + self.end_pos = self._conv_pos(e.pos) self.invalidate() + def animate(self): + if self.draw_anim: + if self.state.animate(): + self.invalidate() + + +class ModeLabel(BoomLabel): + + def __init__(self, app_image): + self.app_image = app_image + super(ModeLabel, self).__init__('Mode : ', 200, + font=get_font(15, 'VeraBd.ttf'), + fg_color = pygame.color.Color(128, 0, 255)) + self.rect.move_ip(805, 0) + + def draw_all(self, surface): + self.set_text('Mode : %s' % self.app_image.mode) + super(ModeLabel, self).draw_all(surface) + + def make_button(text, action, ypos): - button = Button(text, action=action) + button = Button(text, action=action, font=get_font(15, 'VeraBd.ttf')) button.align = 'l' - button.rect = pygame.rect.Rect(0, 0, 200, 35) + button.rect = pygame.rect.Rect(0, 0, MENU_WIDTH, MENU_BUTTON_HEIGHT) button.rect.move_ip(805, ypos) return button + class RectApp(RootWidget): """Handle the app stuff for the rect drawer""" @@ -366,38 +518,73 @@ super(RectApp, self).__init__(display) self.image = AppImage(state) self.add(self.image) - draw = make_button('Draw Rect', self.image.draw_mode, 0) + mode_label = ModeLabel(self.image) + self.add(mode_label) + y = mode_label.get_rect().h + draw = make_button('Draw Rect', self.image.draw_mode, y) self.add(draw) - load_image = make_button("Load image", self.image.image_load, 35) + y += draw.get_rect().h + load_image = make_button("Load image", self.image.image_load, y) self.add(load_image) - add_image = make_button("Place/Move images", self.image.image_mode, 70) + y += load_image.get_rect().h + add_image = make_button("Place/Move images", self.image.image_mode, y) add_image.enabled = False self.add(add_image) self.image.place_image_menu = add_image - delete = make_button('Delete Objects', self.image.del_mode, 105) + y += add_image.get_rect().h + cycle = make_button("Cycle interacts", self.image.cycle_mode, y) + self.add(cycle) + y += cycle.get_rect().h + delete = make_button('Delete Objects', self.image.del_mode, y) self.add(delete) + y += delete.get_rect().h palette = AppPalette(self.image) - palette.rect.move_ip(810, 140) + palette.rect.move_ip(810, y) self.add(palette) - print_rects = make_button("Print objects", self.image.print_objs, 300) + y += palette.get_rect().h + print_rects = make_button("Print objects", self.image.print_objs, y) self.add(print_rects) - toggle_things = make_button("Toggle Things", self.image.toggle_things, 335) + y += print_rects.get_rect().h + toggle_things = make_button("Show Things (t)", self.image.toggle_things, y) self.add(toggle_things) - toggle_thing_rects = make_button("Toggle Thing Rects", self.image.toggle_thing_rects, 370) + y += toggle_things.get_rect().h + toggle_thing_rects = make_button("Show Thing Rects (r)", self.image.toggle_thing_rects, y) self.add(toggle_thing_rects) - toggle_images = make_button("Toggle Images", self.image.toggle_images, 405) + y += toggle_thing_rects.get_rect().h + toggle_images = make_button("Show Images (i)", self.image.toggle_images, y) self.add(toggle_images) - toggle_rects = make_button("Toggle Rects", self.image.toggle_rects, 440) + y += toggle_images.get_rect().h + trans_images = make_button("Opaque Images (o)", self.image.toggle_trans_images, y) + self.add(trans_images) + y += trans_images.get_rect().h + toggle_rects = make_button("Show Drawn Rects (d)", self.image.toggle_rects, y) self.add(toggle_rects) - toggle_toolbar = make_button("Toggle Toolbar", self.image.toggle_toolbar, 475) + y += toggle_rects.get_rect().h + toggle_toolbar = make_button("Show Toolbar (b)", self.image.toggle_toolbar, y) self.add(toggle_toolbar) - quit_but = make_button("Quit", self.quit, 565) + y += toggle_toolbar.get_rect().h + toggle_anim = make_button("Show Animations (a)", self.image.toggle_anim, y) + self.add(toggle_anim) + y += toggle_anim.get_rect().h + toggle_zoom = make_button("Zoom (z)", self.image.toggle_zoom, y) + self.add(toggle_zoom) + y += toggle_zoom.get_rect().h + quit_but = make_button("Quit", self.quit, 570) self.add(quit_but) + self.set_timer(constants.FRAME_RATE) def key_down(self, event): # Dispatch to image widget self.image.key_down(event) + def mouse_delta(self, event): + # We propogate mouse move from here to draw region, so images move + # off-screen + self.image.do_mouse_move(event) + + def begin_frame(self): + self.image.animate() + if __name__ == "__main__": # FIXME: should load an actual scene with current things, not just a @@ -409,7 +596,7 @@ pygame.font.init() # enable key repeating pygame.key.set_repeat(200, 100) - display = pygame.display.set_mode((1000, 600)) + display = pygame.display.set_mode((constants.SCREEN[0] + MENU_WIDTH, constants.SCREEN[1])) state = state.initial_state() if len(sys.argv) < 3: try: