def load_level(self, world: int, level: int, object_data_offset: int, enemy_data_offset: int, object_set_number: int): self._internal_level = Level(world, level, object_data_offset, enemy_data_offset, object_set_number) self._internal_level.data_changed.connect(self.data_changed.emit) self._internal_level.jumps_changed.connect(self.jumps_changed.emit) # actively emit, because we weren't connected yet, when the level sent it out self.data_changed.emit()
def _draw_objects(self, painter: QPainter, level: Level): for level_object in level.get_all_objects(): level_object.render() if level_object.description.lower() in SPECIAL_BACKGROUND_OBJECTS: width = LEVEL_MAX_LENGTH height = GROUND - level_object.y_position blocks_to_draw = [level_object.blocks[0]] * width * height for index, block_index in enumerate(blocks_to_draw): x = level_object.x_position + index % width y = level_object.y_position + index // width level_object._draw_block(painter, block_index, x, y, self.block_length, False) else: level_object.draw(painter, self.block_length, self.transparency) if level_object.selected: painter.save() painter.setPen(QPen(QColor(0x00, 0x00, 0x00, 0x80), width=1)) painter.drawRect(level_object.get_rect(self.block_length)) painter.restore()
def _draw_background(self, painter: QPainter, level: Level): painter.save() bg_color = bg_color_for_object_set(level.object_set_number, level.header.object_palette_index) painter.fillRect(level.get_rect(self.block_length), bg_color) painter.restore()
def _draw_objects(self, painter: QPainter, level: Level): for level_object in level.get_all_objects(): level_object.render() level_object.draw(painter, self.block_length, self.transparency) if level_object.selected: painter.save() painter.setPen(QPen(QColor(0x00, 0x00, 0x00, 0x80), width=1)) painter.drawRect(level_object.get_rect(self.block_length)) painter.restore()
def _draw_background(self, painter: QPainter, level: Level): painter.save() if level.object_set_number == CLOUDY_OBJECT_SET: bg_color = QColor( *NESPalette[load_palette_group(level.object_set_number, level.header.object_palette_index)[3][2]] ) else: bg_color = bg_color_for_object_set(level.object_set_number, level.header.object_palette_index) painter.fillRect(level.get_rect(self.block_length), bg_color) painter.restore()
def _draw_grid(self, painter: QPainter, level: Level): panel_width, panel_height = level.get_rect(self.block_length).size().toTuple() painter.setPen(self.grid_pen) for x in range(0, panel_width, self.block_length): painter.drawLine(x, 0, x, panel_height) for y in range(0, panel_height, self.block_length): painter.drawLine(0, y, panel_width, y) painter.setPen(self.screen_pen) if level.is_vertical: for y in range(0, panel_height, self.block_length * SCREEN_HEIGHT): painter.drawLine(0, y, panel_width, y) else: for x in range(0, panel_width, self.block_length * SCREEN_WIDTH): painter.drawLine(x, 0, x, panel_height)
def _draw_expansions(self, painter: QPainter, level: Level): for level_object in level.get_all_objects(): if level_object.selected: painter.drawRect(level_object.get_rect(self.block_length)) if self.draw_expansions: painter.save() painter.setPen(Qt.NoPen) if level_object.expands() == EXPANDS_BOTH: painter.setBrush(QColor(0xFF, 0, 0xFF, 0x80)) elif level_object.expands() == EXPANDS_HORIZ: painter.setBrush(QColor(0xFF, 0, 0, 0x80)) elif level_object.expands() == EXPANDS_VERT: painter.setBrush(QColor(0, 0, 0xFF, 0x80)) painter.drawRect(level_object.get_rect(self.block_length)) painter.restore()
def _draw_overlays(self, painter: QPainter, level: Level): painter.save() for level_object in level.get_all_objects(): name = level_object.description.lower() # only handle this specific enemy item for now if isinstance(level_object, EnemyObject) and "invisible door" not in name: continue pos = level_object.get_rect(self.block_length).topLeft() rect = level_object.get_rect(self.block_length) # invisible coins, for example, expand and need to have multiple overlays drawn onto them # set true by default, since for most overlays it doesn't matter fill_object = True # pipe entries if "pipe" in name and "can go" in name: if not self.draw_jumps_on_objects: continue fill_object = False # center() is one pixel off for some reason pos = rect.topLeft() + QPoint(*(rect.size() / 2).toTuple()) if "left" in name: image = LEFT_ARROW pos.setX(rect.right()) pos.setY(pos.y() - self.block_length / 2) elif "right" in name: image = RIGHT_ARROW pos.setX(rect.left() - self.block_length) pos.setY(pos.y() - self.block_length / 2) elif "down" in name: image = DOWN_ARROW pos.setX(pos.x() - self.block_length / 2) pos.setY(rect.top() - self.block_length) else: image = UP_ARROW pos.setX(pos.x() - self.block_length / 2) pos.setY(rect.bottom()) if not self._object_in_jump_area(level, level_object): image = NO_JUMP elif "door" == name or "door (can go" in name or "invisible door" in name: fill_object = False image = DOWN_ARROW pos.setY(rect.top() - self.block_length) if not self._object_in_jump_area(level, level_object): image = NO_JUMP # "?" - blocks, note blocks, wooden blocks and bricks elif "'?' with" in name or "brick with" in name or "bricks with" in name or "block with" in name: if not self.draw_items_in_blocks: continue pos.setY(pos.y() - self.block_length) if "flower" in name: image = FIRE_FLOWER elif "leaf" in name: image = LEAF elif "continuous star" in name: image = CONTINUOUS_STAR elif "star" in name: image = NORMAL_STAR elif "multi-coin" in name: image = MULTI_COIN elif "coin" in name: image = COIN elif "1-up" in name: image = ONE_UP elif "vine" in name: image = VINE elif "p-switch" in name: image = P_SWITCH else: image = EMPTY_IMAGE # draw little arrow for the offset item overlay arrow_pos = QPoint(pos) arrow_pos.setY(arrow_pos.y() + self.block_length / 4) painter.drawImage(arrow_pos, ITEM_ARROW.scaled(self.block_length, self.block_length)) elif "invisible" in name: if not self.draw_invisible_items: continue if "coin" in name: image = INVISIBLE_COIN elif "1-up" in name: image = INVISIBLE_1_UP else: image = EMPTY_IMAGE elif "silver coins" in name: if not self.draw_invisible_items: continue image = SILVER_COIN else: continue if fill_object: for x in range(level_object.rendered_width): adapted_pos = QPoint(pos) adapted_pos.setX(pos.x() + x * self.block_length) image = image.scaled(self.block_length, self.block_length) painter.drawImage(adapted_pos, image) if level_object.selected: painter.drawImage(adapted_pos, _make_image_selected(image)) else: image = image.scaled(self.block_length, self.block_length) painter.drawImage(pos, image) painter.restore()
def level(rom, qtbot): return Level(1, 1, level_1_1_object_address, level_1_1_enemy_address, PLAINS_OBJECT_SET)
class LevelRef(QObject): data_changed: SignalInstance = Signal() jumps_changed: SignalInstance = Signal() def __init__(self): super(LevelRef, self).__init__() self._internal_level: Optional[Level] = None def load_level(self, world: int, level: int, object_data_offset: int, enemy_data_offset: int, object_set_number: int): self._internal_level = Level(world, level, object_data_offset, enemy_data_offset, object_set_number) self._internal_level.data_changed.connect(self.data_changed.emit) self._internal_level.jumps_changed.connect(self.jumps_changed.emit) # actively emit, because we weren't connected yet, when the level sent it out self.data_changed.emit() @property def level(self): return self._internal_level @property def selected_objects(self): return [ obj for obj in self._internal_level.get_all_objects() if obj.selected ] @selected_objects.setter def selected_objects(self, selected_objects): if selected_objects == self.selected_objects: return for obj in self._internal_level.get_all_objects(): obj.selected = obj in selected_objects self.data_changed.emit() def __getattr__(self, item: str): if self._internal_level is None: return None else: return getattr(self._internal_level, item) def undo(self): if not self.undo_stack.undo_available: return self._internal_level.from_bytes(*self.undo_stack.undo(), new_level=False) self.data_changed.emit() def redo(self): if not self.undo_stack.redo_available: return self.level.from_bytes(*self.undo_stack.redo(), new_level=False) self.data_changed.emit() def save_level_state(self): self.undo_stack.save_level_state(self._internal_level.to_bytes()) self.data_changed.emit() def __bool__(self): return self._internal_level is not None
class LevelRef(QObject): data_changed: SignalInstance = Signal() jumps_changed: SignalInstance = Signal() def __init__(self): super(LevelRef, self).__init__() self._internal_level: Optional[Level] = Level() def load_level(self, level_name: str, object_data_offset: int, enemy_data_offset: int, object_set_number: int): self.level = Level(level_name, object_data_offset, enemy_data_offset, object_set_number) # actively emit, because we weren't connected yet, when the level sent it out self.data_changed.emit() @property def level(self): return self._internal_level @level.setter def level(self, level): self._internal_level = level self._internal_level.data_changed.connect(self.data_changed.emit) self._internal_level.jumps_changed.connect(self.jumps_changed.emit) @property def selected_objects(self): return [obj for obj in self._internal_level.get_all_objects() if obj.selected] @selected_objects.setter def selected_objects(self, selected_objects): if selected_objects == self.selected_objects: return for obj in self._internal_level.get_all_objects(): obj.selected = obj in selected_objects self.data_changed.emit() def __getattr__(self, item: str): if self._internal_level is None: return None else: return getattr(self._internal_level, item) def undo(self): if not self.undo_stack.undo_available: return self.set_level_state(*self.undo_stack.undo()) def redo(self): if not self.undo_stack.redo_available: return self.set_level_state(*self.undo_stack.redo()) def set_level_state(self, object_data, enemy_data): self.level.from_bytes(object_data, enemy_data, new_level=False) self.level.changed = True self.data_changed.emit() def import_undo_stack_data(self, undo_index, byte_data): self.level.undo_stack.import_data(undo_index, byte_data) if byte_data: self.set_level_state(*byte_data[undo_index]) def save_level_state(self): self.undo_stack.save_level_state(self._internal_level.to_bytes()) self.level.changed = True self.data_changed.emit() def __bool__(self): return self._internal_level.fully_loaded
def load_level(self, level_name: str, object_data_offset: int, enemy_data_offset: int, object_set_number: int): self.level = Level(level_name, object_data_offset, enemy_data_offset, object_set_number) # actively emit, because we weren't connected yet, when the level sent it out self.data_changed.emit()
def __init__(self): super(LevelRef, self).__init__() self._internal_level: Optional[Level] = Level()