Esempio n. 1
0
    def open(self, event):
        dialog = wx.DirDialog(self, 'Select a Turrican II CDTV directory', '', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST)
        if dialog.ShowModal() != wx.ID_OK:
            return

        directory = dialog.GetPath()
        test_files = ['L1-1', 'L2-1', 'L3-1', 'L4-1', 'L5-1', 'LOADER', 'MAIN']
        for filename in test_files:
            if not os.path.exists(os.path.join(directory, filename)):
                wx.MessageBox('Not a valid Turrican II CDTV directory.', 'Invalid directory', wx.OK | wx.ICON_EXCLAMATION)
                return

        self._game_dir = directory
        self._graphics = Graphics(self._game_dir)
        self.load_worlds()

        self.Entities.set_graphics(self._graphics)
        self.Entities.set_font(self._font)

        self.LevelSelect.SetSelection(0)
        self.select_level(0, 0)
        self.update_menu_state()
        self.update_title()
Esempio n. 2
0
class FrameMain(FrameMainBase):

    def __init__(self):
        FrameMainBase.__init__(self, None)

        self.SetIcon(wx.Icon(u'res/icon-diamond.ico'))

        self.Enable(False)
        self.Status.SetFieldsCount(1)

        self._edit_mode_dialogs = {
            ui.dialogs.EDIT_TILES: EditMode.TILES,
            ui.dialogs.EDIT_ENTITIES: EditMode.ENTITIES,
            ui.dialogs.EDIT_START: EditMode.START
        }

        self._edit_mode_panels = {
            EditMode.TILES: self.PanelModeTiles,
            EditMode.ENTITIES: self.PanelModeEntities,
            EditMode.START: self.PanelModeStart
        }

        self._edit_modes = {
            EditMode.TILES: EditModeTiles(self),
            EditMode.ENTITIES: EditModeEntities(self),
            EditMode.START: EditModeStart(self),
        }
        self._edit_mode = None

        self._game_dir = None

        self._mouse_state = MouseState.NONE
        self._move_last_pos = 0

        self._graphics = None
        self._worlds = None
        self._world = None
        self._level = None

        self._presenter = None
        self._camera = None

        self._draw_tile_collision = False
        self._always_draw_entities = True
        self._draw_blockmap = False

        self._font = Font.from_png('fonts/zepto.png')

        self.set_mode(EditMode.TILES)
        self.update_menu_state()

        self.Tiles.Bind(ui.tileselector.TileSelector.EVT_SELECT_EVENT, self.selection_tile)
        self.Entities.Bind(ui.entitypicker.EntityPicker.EVT_ENTITY_PICK_EVENT, self.selection_entity)
        self.Bind(wx.EVT_CHAR_HOOK, self.char_hook)

        self.Maximize()
        self.Show()

        self.Enable(True)

    def open(self, event):
        dialog = wx.DirDialog(self, 'Select a Turrican II CDTV directory', '', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST)
        if dialog.ShowModal() != wx.ID_OK:
            return

        directory = dialog.GetPath()
        test_files = ['L1-1', 'L2-1', 'L3-1', 'L4-1', 'L5-1', 'LOADER', 'MAIN']
        for filename in test_files:
            if not os.path.exists(os.path.join(directory, filename)):
                wx.MessageBox('Not a valid Turrican II CDTV directory.', 'Invalid directory', wx.OK | wx.ICON_EXCLAMATION)
                return

        self._game_dir = directory
        self._graphics = Graphics(self._game_dir)
        self.load_worlds()

        self.Entities.set_graphics(self._graphics)
        self.Entities.set_font(self._font)

        self.LevelSelect.SetSelection(0)
        self.select_level(0, 0)
        self.update_menu_state()
        self.update_title()

    def char_hook(self, event):
        key_code = event.GetKeyCode()
        self._edit_mode.key_char(key_code)
        event.Skip()

    def load_worlds(self):
        with open('level-data.json', 'r') as fp:
            level_data = json.load(fp)

        self._worlds = []
        for data in level_data:
            world = World()
            world.load(os.path.join(self._game_dir, data['world_file']), data['levels'])
            self._worlds.append(world)

        for world_index, world in enumerate(self._worlds):
            for level_index, level in enumerate(world.levels):
                data = (world_index, level_index)
                self.LevelSelect.Append(level.name, data)

    def set_mode(self, new_mode):
        self._edit_mode = self._edit_modes[new_mode]

        # Show only the active mode panel.
        for panel in self._edit_mode_panels.values():
            panel.Hide()
        self._edit_mode_panels[new_mode].Show()

        self.Layout()
        self.Viewport.Refresh(False)

    def set_mode_from_menu(self, event):
        id = event.GetId()
        self.set_mode(self._edit_mode_dialogs[id])

    def viewport_resize(self, event):
        if not self._camera:
            return

        viewport_size = self.Viewport.GetSize()
        self._camera.set_size(viewport_size[0] / self._presenter.scale, viewport_size[1] / self._presenter.scale)

        self._presenter.resize()

    def viewport_paint(self, event):
        if not self._presenter:
            return

        surface = self._presenter.surface
        surface.clear()

        self._level.tilemap.render(surface, self._camera, self._world.tileset, self._draw_tile_collision)

        if self._draw_blockmap:
            _, _, block_width, block_height = self._level.get_blockmap_dimensions()
            block_width = int(block_width)
            block_height = int(block_height)
            for y in range(0, self._level.tilemap.height * 32, block_height):
                for x in range(-24, self._level.tilemap.width * 32 + 24, block_width):
                    rx, ry = self._camera.world_to_camera(x, y)
                    surface.box(rx, ry, block_width, block_height, 0xFFFF00FF)

        if self._always_draw_entities:
            editing_entities = (self._edit_mode == self._edit_modes[EditMode.ENTITIES])
            if editing_entities:
                hover_entity = self._edit_mode.get_hover_entity()
            else:
                hover_entity = None

            draw_origin = editing_entities
            translucent = not editing_entities
            self.paint_entities(surface, draw_origin, hover_entity, translucent)

        self._edit_mode.paint(surface, self._camera, self._graphics)

        self._presenter.present()

    def paint_entities(self, surface, draw_origin=False, hover_entity=None, translucent=False):
        if translucent:
            blend_op = BlendOp.ALPHA50
        else:
            blend_op = BlendOp.ALPHA_SIMPLE

        for entity in self._level.entities:
            template = self._level.get_entity_template(entity.type, entity.subtype)
            if template is None:
                continue

            origin_x, origin_y = self._camera.world_to_camera(entity.x * Level.ORIGIN_SIZE, entity.y * Level.ORIGIN_SIZE)
            origin_width = Level.ORIGIN_SIZE
            origin_height = Level.ORIGIN_SIZE

            sprite_surfaces = self._graphics.get_surfaces(template.gfx)

            if sprite_surfaces:
                sprite_surface = sprite_surfaces[template.gfx_index]
                sprite_x = origin_x + template.offset_x
                sprite_y = origin_y + template.offset_y
                sprite_width = sprite_surface.width
                sprite_height = sprite_surface.height
            else:
                sprite_surface = None
                sprite_x = origin_x
                sprite_y = origin_y
                sprite_width = 0
                sprite_height = 0

            x1 = min(origin_x, sprite_x)
            y1 = min(origin_y, sprite_y)
            x2 = max(origin_x + origin_width, sprite_x + sprite_width)
            y2 = max(origin_y + origin_height, sprite_y + sprite_height)

            if not self._camera.screen_contains(x1, y1, x2, y2):
                continue

            if sprite_surface is not None:
                surface.blit_blend(sprite_surface, sprite_x, sprite_y, blend_op)
                if entity == hover_entity:
                    surface.outline(sprite_surface, sprite_x, sprite_y, COLOR_ENTITY_HOVER)
                elif entity.selected:
                    surface.outline(sprite_surface, sprite_x, sprite_y, COLOR_ENTITY_SELECTED)

            if draw_origin:
                surface.box_fill(origin_x, origin_y, origin_width, origin_height, COLOR_ORIGIN, BlendOp.ALPHA50)

    def viewport_mouse_right_down(self, event):
        if not self._camera:
            return
        if not self._world:
            return

        self._mouse_state = MouseState.MOVE
        self.Viewport.SetCursor(wx.StockCursor(wx.CURSOR_SIZING))
        self._move_last_pos = event.GetPosition()

    def viewport_mouse_right_up(self, event):
        if not self._camera:
            return
        if not self._world:
            return

        self._mouse_state = MouseState.NONE
        self.Viewport.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))

    def viewport_mouse_move(self, event):
        if not self._camera:
            return
        if not self._world:
            return

        pos = event.GetPosition()
        pos = self._camera.camera_to_world(pos.x / self._presenter.scale, pos.y / self._presenter.scale)
        self._edit_mode.set_mouse_position(pos)

        if self._mouse_state == MouseState.MOVE:
            pos = event.GetPosition()

            delta_x = -((pos.x - self._move_last_pos[0]) * config.MOVE_SENSITIVITY) / self._presenter.scale
            delta_y = -((pos.y - self._move_last_pos[1]) * config.MOVE_SENSITIVITY) / self._presenter.scale

            self._camera.move_relative(delta_x, delta_y)

            if abs(delta_x) > 0:
                self._move_last_pos[0] = pos.x
            if abs(delta_y) > 0:
                self._move_last_pos[1] = pos.y

        else:
            self._edit_mode.mouse_move()

        self.Viewport.Refresh(False)

    def viewport_mouse_left_down(self, event):
        if not self._world:
            return

        self._edit_mode.mouse_left_down()
        self.Viewport.Refresh(False)

    def viewport_mouse_left_up(self, event):
        if not self._world:
            return

        self._edit_mode.mouse_left_up()
        self.Viewport.Refresh(False)

    def set_show_entities_menu(self, event):
        self._always_draw_entities = not self._always_draw_entities
        self.update_menu_state()
        self.Viewport.Refresh(False)

    def set_show_collision_menu(self, event):
        self._draw_tile_collision = not self._draw_tile_collision
        self.update_menu_state()
        self.Viewport.Refresh(False)

    def set_show_blockmap_menu(self, event):
        self._draw_blockmap = not self._draw_blockmap
        self.update_menu_state()
        self.Viewport.Refresh(False)

    def update_menu_state(self):
        self.LevelShowEntities.Check(self._always_draw_entities)
        self.LevelShowCollision.Check(self._draw_tile_collision)
        self.LevelShowBlockmap.Check(self._draw_blockmap)
        self.Tiles.show_collision(self._draw_tile_collision)

    def about(self, event):
        about = DialogAbout()
        about.ShowModal()

    def level_choice(self, event):
        index = self.LevelSelect.GetSelection()
        data = self.LevelSelect.GetClientData(index)
        self.select_level(data[0], data[1])

    def goto_start(self, event):
        self.center_on_start()
        self.Viewport.Refresh(False)

    def select_level(self, world, level):
        self._world = self._worlds[world]
        self._level = self._world.levels[level]

        self._presenter = Presenter.from_window(self.Viewport.GetHandle(), config.SCALE)

        if self._level.camera:
            self._camera = self._level.camera
            self._camera.set_size(self._presenter.surface.width, self._presenter.surface.height)
        else:
            width = self._presenter.surface.width
            height = self._presenter.surface.height
            max_x = self._level.tilemap.width * Tilemap.TILE_SIZE
            max_y = self._level.tilemap.height * Tilemap.TILE_SIZE
            self._camera = Camera(width, height, max_x, max_y)
            self._level.camera = self._camera
            self.center_on_start()

        for edit_mode in self._edit_modes.values():
            edit_mode.set_level(self._world, self._level)

        self.Tiles.set_tileset(self._world.tileset)
        self.Entities.set_level(self._level)
        self.update_status()
        self.update_title()

        self.Layout()
        self.Viewport.Refresh(False)

    def center_on_start(self):
        if not self._world:
            return

        x = (self._level.camera_tile_x * Tilemap.TILE_SIZE) + 152 - self._camera.width / 2
        y = (self._level.camera_tile_y * Tilemap.TILE_SIZE) + 96 - self._camera.height / 2
        self._camera.move_absolute(x, y)

    def undo_add(self):

        # If the undo stack has reached it's maximum size, prune off the first item.
        if len(self._level.undo) == config.MAX_UNDO:
            self._level.undo = self._level.undo[1:]
            self._level.undo_index -= 1

        # Prune the stack up to and including the current item.
        self._level.undo = self._level.undo[0:self._level.undo_index + 1]

        self._level.undo.append(self.undo_store_item())
        self._level.undo_index += 1

    def undo_do_undo(self):
        if not self._world:
            return
        if self._level.undo_index == -1:
            return

        undo_item = self._level.undo[self._level.undo_index]
        self._level.undo_index -= 1
        self.undo_restore_item(undo_item)

    def undo_restore_item(self, item):
        editmode = self._edit_modes[item['editmode']]
        editmode.undo_restore_item(item['data'])

    def undo_store_item(self):
        value_index = self._edit_modes.values().index(self._edit_mode)
        edit_mode_key = self._edit_modes.keys()[value_index]

        return {
            'editmode': edit_mode_key,
            'data': self._edit_mode.undo_store_item()
        }

    def undo(self, event):
        if not self._world:
            return

        self.undo_do_undo()

    def save(self, event):
        if not self._world:
            return

        self._world.save()
        self.update_title()

    def close_menu(self, event):
        self.Close(False)

    def close(self, event):
        if not self._world:
            event.Skip()
            return

        modified = False
        for level in self._world.levels:
            modified = modified | level.modified

        if modified:
            result = wx.MessageBox('Do you want to save your changes?', 'Unsaved changes', wx.YES_NO | wx.CANCEL | wx.ICON_EXCLAMATION)
            if result == wx.YES:
                self._world.save()
            elif result == wx.CANCEL:
                return

        event.Skip()

    def update_title(self):
        if self._level.modified:
            modified = ' *'
        else:
            modified = ''
        self.SetTitle('{} - {}{}'.format(config.APP_NAME, self._level.name, modified))

    def refresh_viewport(self):
        self.Viewport.Refresh(False)

    def set_viewport_cursor(self, stock_cursor):
        self.Viewport.SetCursor(wx.StockCursor(stock_cursor))

    def selection_tile(self, event):
        self._edit_modes[EditMode.TILES].set_selection(event.selection)

    def selection_entity(self, event):
        self._edit_modes[EditMode.ENTITIES].set_template(event.template)

    def set_level_modified(self, is_modified):
        self._level.modified = is_modified
        self.update_title()

    def update_status(self):
        bytes_left = self._level.get_entity_bytes_left()
        if bytes_left < 0:
            self.Status.SetStatusText('No entity bytes left!', 0)
            wx.MessageBox('There is no more room for entities. Delete entities or move entites to create empty blockmap blocks to free up more room. This level cannot be saved until enough room is made available.', 'No more room for entities', wx.ICON_EXCLAMATION | wx.OK)
        else:
            self.Status.SetStatusText('{} entity bytes left'.format(bytes_left), 0)