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_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_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()
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