示例#1
0
def main():
    # create environment e
    # run e on new process
    env = Environment(1200.0, 800.0, 15.0, 7.0)
    backend = Backend([env])
    backend.run()

    # create frontend / main window
    # and start its event loop
    frontend = Frontend(env.create_gfx_items(), 1200, 800)
    frontend.run()
    def __init__(self,default_room):
        pygame.init()
        
        self.draw_menu_around_tile_not_mouse = True

        self.menu_pos = None
        self.last_mouse_time = pygame.time.get_ticks()
        self.last_mouse_pos = None
        self.last_mouse_tile_pos = None
        self.menu_hit_idx = None
        self.following_with_transitive_verb = None
        self.recent_presses = []

        self.backend = Backend()
        self.backend.load(default_room)
        
        self.scene_begin_ticks = self.state_begin_ticks = pygame.time.get_ticks()
        self.default_state_duration = 500
        self.state_duration = self.default_state_duration

        self.screen = pygame.display.set_mode( tuple(a*b+pad1+pad2 for a,b,pad1,pad2 in zip( self.backend.get_map_size(),
                                                                                            self.tile_size(),
                                                                                            self.get_screen_padding_lt(),
                                                                                            self.get_screen_padding_rb(),
                                                                                            ) ) )
        pygame.display.set_caption('Cartesianventure sample: Alchemical Distillery')

        self.background = pygame.Surface(self.screen.get_size()+self.get_screen_padding_lt()+self.get_screen_padding_rb())
        self.background = self.background.convert()
        self.background.fill((0, 0, 0))

        self.clock = pygame.time.Clock()
        
        self.FRAME_TIMER = pygame.USEREVENT+1
class Frontend:
    def __init__(self, default_room):
        pygame.init()

        self.draw_menu_around_tile_not_mouse = True

        self.menu_pos = None
        self.last_mouse_time = pygame.time.get_ticks()
        self.last_mouse_pos = None
        self.last_mouse_tile_pos = None
        self.menu_hit_idx = None
        self.following_with_transitive_verb = None
        self.recent_presses = []

        self.backend = Backend()
        self.backend.load(default_room)

        self.scene_begin_ticks = self.state_begin_ticks = pygame.time.get_ticks()
        self.default_state_duration = 500
        self.state_duration = self.default_state_duration

        self.screen = pygame.display.set_mode(
            tuple(
                a * b + pad1 + pad2
                for a, b, pad1, pad2 in zip(
                    self.backend.get_map_size(),
                    self.tile_size(),
                    self.get_screen_padding_lt(),
                    self.get_screen_padding_rb(),
                )
            )
        )
        pygame.display.set_caption("Cartesianventure sample: Alchemical Distillery")

        self.background = pygame.Surface(
            self.screen.get_size() + self.get_screen_padding_lt() + self.get_screen_padding_rb()
        )
        self.background = self.background.convert()
        self.background.fill((0, 0, 0))

        self.clock = pygame.time.Clock()

        self.FRAME_TIMER = pygame.USEREVENT + 1

    def get_screen_padding_lt(self):
        return Pos(0, 0)

    def get_screen_padding_rb(self):
        return Pos(0, 0)

    def main(self):
        if enable_splash:
            ret = self.splash()
            if ret == "quit":
                return "quit"
        max_fps = 60
        fixed_fps = False
        if not fixed_fps:
            pygame.time.set_timer(self.FRAME_TIMER, 1000 // max_fps)
        while True:
            if fixed_fps:
                self.clock.tick(max_fps)
                events = pygame.event.get()
            else:
                events = pygame.event.get() or (pygame.event.wait(),)
            ret = self.loop_step(pygame.time.get_ticks(), events)
            if ret == "quit":
                break
        if log_file:
            log_file.close()
        pygame.quit()

    def loop_step(self, ticks, events=()):
        for event in events:
            ret = self.handle_event(event, ticks)
            if ret == "quit":
                return "quit"
        if self.finished_state(ticks) >= 1:
            self.advance_state(ticks)
            if self.backend.state_is_idle() and self.recent_presses:
                self.backend.start_move(self.dir_from_key(self.recent_presses[-1]))
        self.draw_all(ticks)
        self.handle_and_draw_menu(ticks)
        pygame.display.flip()
        sys.stdout.flush()

    def tile_size(self):
        return Pos(64, 64)

    def get_tile_from_screen_coords(self, pos):
        return (pos - self.get_screen_padding_lt()) / self.tile_size()

    def get_screen_from_tile_coords(self, pos):
        return self.get_screen_padding_lt() + pos * self.tile_size()

    def frac(self, ticks):
        return (ticks - self.state_begin_ticks) / float(self.state_duration)

    def finished_state(self, ticks):
        return self.backend.finished_state(self.frac(ticks))

    def advance_state(self, ticks):
        self.backend.advance_state()
        self.state_begin_ticks = ticks

    def draw_all(self, ticks):
        self.screen.blit(self.background, (0, 0))
        for tile_surface in self.backend.get_blit_surfaces(self.frac(ticks), self.tile_size()):
            tile_surface.blit_to(self.screen, self.get_screen_padding_lt())

    def handle_and_draw_menu(self, ticks):
        if self.following_with_transitive_verb:
            if pygame.mouse.get_focused():
                self.draw_transitive_menu(pygame.mouse.get_pos())
            return
        hover_delay_ms, hover_off_delay_ms = 50, 200
        if (
            self.menu_pos
            and self.last_mouse_in_menu_time + hover_off_delay_ms < ticks
            and pygame.mouse.get_focused()
            and not self.is_in_menu(pygame.mouse.get_pos())
        ):
            self.menu_pos = None
        if (
            (not self.menu_pos)
            and self.last_mouse_pos
            and self.last_mouse_time + hover_delay_ms < ticks
            and pygame.mouse.get_focused()
        ):
            self.menu_pos = self.last_mouse_pos
            self.menu_obj = self.last_mouse_obj
            self.last_mouse_in_menu_time = ticks
        if self.menu_pos:
            self.draw_menu(self.menu_pos)

    def dir_from_key(self, event_key):
        dir_sets = ((K_LEFT, K_RIGHT, K_UP, K_DOWN), (K_a, K_d, K_w, K_s))
        dir_keys = "lrud"
        return first(dir for dir_set in dir_sets for K_, dir in zip(dir_set, dir_keys) if K_ == event_key)

    def handle_event(self, event, ticks):
        if log_file:  # and event.type in (MOUSEBUTTONDOWN,MOUSEBUTTONUP,MOUSEMOTION,QUIT,KEYDOWN):
            my_event = Struct()
            my_event.type = event.type
            if event.type in (MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEMOTION):
                my_event.pos = event.pos
            if event.type in (KEYDOWN,):
                my_event.key, my_event.mod = event.key, event.mod
            pickle.dump(my_event, log_file)
            event = my_event  # test it works before we try loading from file
        if event.type in (MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEMOTION):
            curr_obj = self.backend.get_obj_at(self.get_tile_from_screen_coords(event.pos))
        if (event.type == QUIT) or (event.type == KEYDOWN and event.key == K_F4 and event.mod & KMOD_ALT):
            return "quit"
        elif event.type == KEYDOWN:
            self.recent_presses.append(event.key)
            dir = self.dir_from_key(event.key)
            if self.backend.state_is_idle():
                self.advance_state(ticks)
                if dir:
                    self.backend.start_move(dir)
            elif self.backend.state_is_chainable():
                if dir:
                    self.backend.start_move(dir)
        elif event.type == KEYUP:
            if event.key in self.recent_presses:
                self.recent_presses.remove(event.key)
        elif event.type == MOUSEMOTION:
            if self.following_with_transitive_verb:
                self.transitive_verb_putative_objects = self.transitive_verb_objects + [curr_obj]
            else:
                if curr_obj.is_hoverable():
                    self.last_mouse_pos = event.pos
                    self.last_mouse_time = ticks
                    self.last_mouse_obj = curr_obj
                else:
                    self.last_mouse_pos = None
                    self.last_mouse_tile_pos = None
                self.menu_hit_idx = None
                if self.menu_pos and self.is_in_menu(event.pos):
                    self.last_mouse_in_menu_time = ticks
                    for idx, hit_rect_struct in enumerate(self.menu_hit_rect_structs):
                        if hit_rect_struct.hit_rect.collidepoint(event.pos):
                            self.menu_hit_idx = idx
                            break
        elif event.type == MOUSEBUTTONDOWN:
            if self.following_with_transitive_verb:
                menu_hit_rect = self.menu_hit_rect_structs[self.menu_hit_idx]
                self.transitive_verb_objects.append(curr_obj)
                if self.menu_obj.get_remaining_verb_arity(menu_hit_rect.verb, *self.transitive_verb_objects) == 0:
                    self.backend.do(self.following_with_transitive_verb, self.menu_obj, *self.transitive_verb_objects)
                    self.following_with_transitive_verb = None
                    self.menu_pos = None
                    self.last_mouse_pos = None
            elif self.menu_pos:
                if self.menu_hit_idx is not None:
                    menu_hit_rect = self.menu_hit_rect_structs[self.menu_hit_idx]
                    # Do transitive verb or attach intr verb to mouse cursor
                    if menu_hit_rect.verb == "_undo":
                        self.backend.do_undo(menu_hit_rect.undo_event)
                        self.menu_pos = None
                        self.last_mouse_pos = None
                    elif menu_hit_rect.verb == "_redo":
                        self.backend.do_redo(menu_hit_rect.redo_event)
                        self.menu_pos = None
                        self.last_mouse_pos = None
                    else:
                        self.transitive_verb_objects = []
                        self.transitive_verb_putative_objects = []
                        if (
                            self.menu_obj.get_remaining_verb_arity(menu_hit_rect.verb, *self.transitive_verb_objects)
                            == 0
                        ):
                            self.backend.do(menu_hit_rect.verb, self.menu_obj, *self.transitive_verb_objects)
                            self.menu_pos = None
                            self.last_mouse_pos = None
                        else:
                            self.following_with_transitive_verb = menu_hit_rect.verb
                else:
                    self.menu_pos = None
                    self.last_mouse_pos = None
            else:
                pass
                # # Enable to enable menu for floor, but nothing in it atm
                # self.last_mouse_pos = event.pos
                # self.last_mouse_time = 0
                # self.last_mouse_obj = tile_obj or tile_base

    def draw_menu(self, mouse_pos):
        if not self.backend.state_is_idle():
            self.menu_hit_rect_structs = ()
            return
        tile_pos = self.get_screen_from_tile_coords(self.get_tile_from_screen_coords(mouse_pos))
        if self.draw_menu_around_tile_not_mouse:
            x_y = x, y = tile_pos + self.tile_size() / 2
        else:
            x_y = x, y = mouse_pos
        verb_spacing = 10
        verb_offset_x, verb_offset_y = +0, +20

        short_desc = self.menu_obj.get_short_desc()
        if short_desc:
            render_text(short_desc, **render_desc_defaults).blit_to(self.screen, (x - 20, y - 40))

        verb_width, verb_height = 120, 20
        undo_width, undo_height = 180, verb_height
        verb_vstride = verb_height + verb_spacing

        verb_list = ((sentence, dict(verb=verb)) for verb, sentence in self.menu_obj.get_verb_sentences_initcap())

        self.menu_hit_rect_structs = self.draw_and_get_hit_rect_structs(
            verb_list, self.menu_hit_idx, render_menu_defaults, topleft=x_y + (+0, +20)
        )

        undo_list = []

        if self.menu_obj.get_undoable_events():
            undo_event = self.menu_obj.get_undoable_events()[-1]
            undo_list.append(("Undo " + undo_event.event_text_ncase(), dict(verb="_undo", undo_event=undo_event)))

        if self.menu_obj.get_redoable_events():
            redo_event = self.menu_obj.get_redoable_events()[-1]
            undo_list.append(("Redo " + redo_event.event_text_ncase(), dict(verb="_redo", redo_event=redo_event)))

        undo_hit_idx = self.menu_hit_idx - len(self.menu_hit_rect_structs) if self.menu_hit_idx else None
        self.menu_hit_rect_structs.extend(
            self.draw_and_get_hit_rect_structs(undo_list, undo_hit_idx, render_menu_defaults, topright=x_y + (-5, +20))
        )

        # Menu rects
        #
        # Menu rects define area where mouse doesn't cause menu to disappear
        # Includes area of menu, slight border, area around mouse, and whole tile
        if self.draw_menu_around_tile_not_mouse:
            r_courtesy_area = Rect(x_y + (-20, -20), (40, 40))
        else:
            r_courtesy_area = Rect(tile_pos, self.tile_size())
        r_whole_menu = unionall(hit_rect_struct.hit_rect for hit_rect_struct in self.menu_hit_rect_structs)
        self.menu_rects = (r_courtesy_area, r_whole_menu.inflate(15, 15))

    def draw_and_get_hit_rect_structs(
        self, sentences, selected_idx=-1, render_text_props={}, topleft=None, topright=None
    ):
        hit_rect_structs = []
        vspacing = 10
        selected_props = merge(render_text_props, render_selected)
        if not sentences:
            return hit_rect_structs

        for idx, (sentence, d) in enumerate(sentences):
            hit_rect_struct = Struct()
            props = render_text_props if idx != selected_idx else selected_props
            hit_rect_struct.render_obj = render_text(sentence, **props)
            # hit_rect_struct.verb = verb
            for k, v in d.iteritems():
                setattr(hit_rect_struct, k, v)
            hit_rect_structs.append(hit_rect_struct)

        menu_width = max(hit_rect_struct.render_obj.get_size().x for hit_rect_struct in hit_rect_structs)
        next_pos = topleft if topleft is not None else topright - (menu_width, 0)

        for hit_rect_struct in hit_rect_structs:
            hit_rect_struct.render_obj.extend_width_to(menu_width)
            hit_rect_struct.render_obj.blit_to(self.screen, next_pos)
            hit_rect_struct.hit_rect = Rect(next_pos, hit_rect_struct.render_obj.get_size())
            next_pos.y += hit_rect_struct.hit_rect.h + vspacing

        return hit_rect_structs

    def draw_transitive_menu(self, pos):
        msg = self.menu_obj.get_verb_sentence_initcap(
            self.following_with_transitive_verb, *self.transitive_verb_putative_objects
        )
        render_obj = render_text(msg, **render_menu_defaults)
        render_obj.blit_to(self.screen, pos + (-30, -30))

    def is_in_menu(self, pt):
        return any((rect.collidepoint(pt) for rect in self.menu_rects))

    def splash(self, timeout_ms=None):
        welcome_msg = """\
Cartesianventure sample: Hijinks in the alchemical distillery department

By Jack Vickeridge

Using creative commons graphics by Daniel Harris, Daniel Cook and Florian Berger

Click to continue...
"""
        # self.screen.blit(self.background, (0, 0))
        self.blit_message(welcome_msg)
        end_time = pygame.time.get_ticks() + timeout_ms if timeout_ms else None
        while timeout_ms is None or pygame.time.get_ticks() < end_time:
            self.clock.tick(60)
            pygame.display.flip()
            for event in pygame.event.get():
                if event.type == QUIT:
                    return "quit"
                elif event.type == KEYDOWN:
                    return
                elif event.type == MOUSEBUTTONDOWN:
                    return

    def blit_message(self, msg):
        W, H = self.screen.get_size()
        x = W / 4
        width = W / 2
        y = H / 4
        text = txtlib.Text((width, 100), "freesans")
        text.text = msg
        text.update()
        self.screen.blit(text.area, (x, y))