def test_items_for_ammo_two_item_diverging_values(): # Setup item_a = 1 item_b = 2 total_pickup = 10 maximum = 200 included_ammo_for_item = {item_a: 0, item_b: 100} previous_pickup_for_item = {} ammo = Ammo("My Ammo", maximum=maximum, items=(item_a, item_b), broad_category=ItemCategory.BEAM_RELATED) state = AmmoState(0, total_pickup) maximum_ammo = {item_a: maximum, item_b: maximum} # Run ammo_per_pickup = randovania.generator.item_pool.ammo.items_for_ammo(ammo, state, included_ammo_for_item, previous_pickup_for_item, maximum_ammo) # Assert assert previous_pickup_for_item == { item_a: ammo, item_b: ammo, } assert ammo_per_pickup == [[20, 10]] * total_pickup
def bit_pack_unpack(cls, decoder: BitPackDecoder, metadata): from randovania.game_description import default_database item_database = default_database.default_prime2_item_database() default = cls.default() has_value = { item_key: bool(decoder.decode_single(2)) for item_key in default.maximum_ammo.keys() } maximum_ammo = { item_key: decoder.decode_single(256) if has_value[item_key] else default.maximum_ammo[item_key] for item_key, default_value in default.maximum_ammo.items() } num_items = decoder.decode_single(len(default.items_state)) indices_with_custom = { decoder.decode_single(len(default.items_state)) for _ in range(num_items) } items_state = {} for index, item in enumerate(item_database.ammo.values()): if index in indices_with_custom: items_state[item] = AmmoState.bit_pack_unpack(decoder, {}) else: items_state[item] = default.items_state[item] return cls(maximum_ammo, items_state)
def test_items_for_ammo_two_item(per_pickup: int, total_pickup: int, included: int): # Setup item_a = 1 item_b = 2 included_ammo_for_item = {item_a: included, item_b: included} previous_pickup_for_item = {} maximum = per_pickup * total_pickup + included ammo = Ammo("My Ammo", model_name="Model", maximum=maximum, items=(item_a, item_b), broad_category=ItemCategory.BEAM_RELATED) state = AmmoState(0, total_pickup) maximum_ammo = {item_a: maximum, item_b: maximum} # Run ammo_per_pickup = randovania.generator.item_pool.ammo.items_for_ammo( ammo, state, included_ammo_for_item, previous_pickup_for_item, maximum_ammo) # Assert assert previous_pickup_for_item == { item_a: ammo, item_b: ammo, } assert ammo_per_pickup == [[per_pickup, per_pickup]] * total_pickup
def test_items_for_ammo_one_item_non_divisible(): # Setup item_a = 1 maximum = 11 total_pickup = 5 included_ammo_for_item = {item_a: 0} previous_pickup_for_item = {} ammo = Ammo("My Ammo", model_name="Model", maximum=maximum, items=(item_a, ), broad_category=ItemCategory.BEAM_RELATED) state = AmmoState(0, total_pickup) maximum_ammo = {item_a: maximum} # Run ammo_per_pickup = randovania.generator.item_pool.ammo.items_for_ammo( ammo, state, included_ammo_for_item, previous_pickup_for_item, maximum_ammo) # Assert assert previous_pickup_for_item == {item_a: ammo} assert ammo_per_pickup == [[3]] + [[2]] * (total_pickup - 1)
def test_decode(state_with_data): # Setup data, expected = state_with_data # Run decoder = BitPackDecoder(data) result = AmmoState.bit_pack_unpack(decoder, {}) # Assert assert result == expected
def change_split(self, has_split: bool): with self._editor as editor: ammo_configuration = editor.ammo_configuration current_total = sum( ammo_configuration.items_state[ammo].pickup_count for ammo in (self.unified_ammo, *self.split_ammo) ) if has_split: split_state = AmmoState(pickup_count=current_total // len(self.split_ammo)) unified_state = AmmoState() else: split_state = AmmoState() unified_state = AmmoState(pickup_count=current_total) new_states = {self.unified_ammo: unified_state} for ammo in self.split_ammo: new_states[ammo] = split_state editor.ammo_configuration = ammo_configuration.replace_states(new_states)
def from_json(cls, value: dict, item_database: ItemDatabase) -> "AmmoConfiguration": return cls( maximum_ammo={ int(ammo_item): maximum for ammo_item, maximum in value["maximum_ammo"].items() }, items_state={ item_database.ammo[name]: AmmoState.from_json(state) for name, state in value["items_state"].items() }, )
def _change_split_ammo(self, has_split: bool): with self._editor as options: ammo_configuration = options.ammo_configuration current_total = sum( ammo_configuration.items_state[ammo].pickup_count for ammo in (self._dark_ammo_item, self._light_ammo_item, self._beam_ammo_item)) if has_split: dark_ammo_state = AmmoState(pickup_count=current_total // 2) light_ammo_state = AmmoState(pickup_count=current_total // 2) beam_ammo_state = AmmoState() else: dark_ammo_state = AmmoState() light_ammo_state = AmmoState() beam_ammo_state = AmmoState(pickup_count=current_total) ammo_configuration = ammo_configuration.replace_states({ self._dark_ammo_item: dark_ammo_state, self._light_ammo_item: light_ammo_state, self._beam_ammo_item: beam_ammo_state, }) options.ammo_configuration = ammo_configuration
def from_json(cls, value: dict, game: RandovaniaGame) -> "AmmoConfiguration": item_database = default_database.item_database_for_game(game) return cls( maximum_ammo={ int(ammo_item): maximum for ammo_item, maximum in value["maximum_ammo"].items() }, items_state={ item_database.ammo[name]: AmmoState.from_json(state) for name, state in value["items_state"].items() }, )
def bit_pack_unpack(cls, decoder: BitPackDecoder, metadata): default: AmmoConfiguration = metadata["reference"] # Maximum Ammo maximum_ammo = {} for item_key, default_value in default.maximum_ammo.items(): is_different = bitpacking.decode_bool(decoder) if is_different: maximum_ammo[item_key] = decoder.decode_single(256) else: maximum_ammo[item_key] = default_value items_state = {} for item, default_state in default.items_state.items(): is_different = bitpacking.decode_bool(decoder) if is_different: items_state[item] = AmmoState.bit_pack_unpack(decoder, {}) else: items_state[item] = default_state return cls(maximum_ammo, items_state)
def test_items_for_ammo_one_item(per_pickup: int, total_pickup: int, included: int): # Setup item_a = 1 included_ammo_for_item = {item_a: included} previous_pickup_for_item = {} maximum = per_pickup * total_pickup + included ammo = Ammo("My Ammo", maximum=maximum, items=(item_a, )) state = AmmoState(0, total_pickup) maximum_ammo = {item_a: maximum} # Run ammo_per_pickup = randovania.generator.item_pool.ammo.items_for_ammo( ammo, state, included_ammo_for_item, previous_pickup_for_item, maximum_ammo) # Assert assert previous_pickup_for_item == {item_a: ammo} assert ammo_per_pickup == [[per_pickup]] * total_pickup
def _create_ammo_pickup_boxes(self, size_policy, item_database: ItemDatabase): """ Creates the GroupBox with SpinBoxes for selecting the pickup count of all the ammo :param item_database: :return: """ self._ammo_maximum_spinboxes = collections.defaultdict(list) self._ammo_pickup_widgets = {} resource_database = default_database.resource_database_for(self.game) broad_to_category = { ItemCategory.BEAM_RELATED: ItemCategory.BEAM, ItemCategory.MORPH_BALL_RELATED: ItemCategory.MORPH_BALL, ItemCategory.MISSILE_RELATED: ItemCategory.MISSILE, } for ammo in item_database.ammo.values(): category_box, category_layout, _ = self._boxes_for_category[ broad_to_category[ammo.broad_category]] pickup_box = QGroupBox(category_box) pickup_box.setSizePolicy(size_policy) pickup_box.setTitle(ammo.name + "s") layout = QGridLayout(pickup_box) layout.setObjectName(f"{ammo.name} Box Layout") current_row = 0 for ammo_item in ammo.items: item = resource_database.get_by_type_and_index( ResourceType.ITEM, ammo_item) target_count_label = QLabel(pickup_box) target_count_label.setText(f"{item.long_name} Target" if len( ammo.items) > 1 else "Target count") maximum_spinbox = QSpinBox(pickup_box) maximum_spinbox.setMaximum(ammo.maximum) maximum_spinbox.valueChanged.connect( partial(self._on_update_ammo_maximum_spinbox, ammo_item)) self._ammo_maximum_spinboxes[ammo_item].append(maximum_spinbox) layout.addWidget(target_count_label, current_row, 0) layout.addWidget(maximum_spinbox, current_row, 1) current_row += 1 count_label = QLabel(pickup_box) count_label.setText("Pickup Count") count_label.setToolTip( "How many instances of this expansion should be placed.") pickup_spinbox = QSpinBox(pickup_box) pickup_spinbox.setMaximum(AmmoState.maximum_pickup_count()) pickup_spinbox.valueChanged.connect( partial(self._on_update_ammo_pickup_spinbox, ammo)) layout.addWidget(count_label, current_row, 0) layout.addWidget(pickup_spinbox, current_row, 1) current_row += 1 if ammo.temporaries: require_major_item_check = QCheckBox(pickup_box) require_major_item_check.setText( "Requires the major item to work?") require_major_item_check.stateChanged.connect( partial(self._on_update_ammo_require_major_item, ammo)) layout.addWidget(require_major_item_check, current_row, 0, 1, 2) current_row += 1 else: require_major_item_check = None expected_count = QLabel(pickup_box) expected_count.setWordWrap(True) expected_count.setText(_EXPECTED_COUNT_TEXT_TEMPLATE) expected_count.setToolTip( "Some expansions may provide 1 extra, even with no variance, if the total count " "is not divisible by the pickup count.") layout.addWidget(expected_count, current_row, 0, 1, 2) current_row += 1 self._ammo_pickup_widgets[ammo] = AmmoPickupWidgets( pickup_spinbox, expected_count, pickup_box, require_major_item_check) category_layout.addWidget(pickup_box)
def _state_with_data(request): return request.param["encoded"], AmmoState.from_json(request.param["json"])
def _create_ammo_pickup_boxes(self, size_policy, item_database: ItemDatabase): """ Creates the GroupBox with SpinBoxes for selecting the pickup count of all the ammo :param item_database: :return: """ self._ammo_maximum_spinboxes = collections.defaultdict(list) self._ammo_pickup_widgets = {} resource_database = default_prime2_resource_database() for ammo in item_database.ammo.values(): title_layout = QHBoxLayout() title_layout.setObjectName(f"{ammo.name} Title Horizontal Layout") expand_ammo_button = QToolButton(self.ammo_box) expand_ammo_button.setGeometry(QRect(20, 30, 24, 21)) expand_ammo_button.setText("+") title_layout.addWidget(expand_ammo_button) category_label = QLabel(self.ammo_box) category_label.setSizePolicy(size_policy) category_label.setText(ammo.name + "s") title_layout.addWidget(category_label) pickup_box = QGroupBox(self.ammo_box) pickup_box.setSizePolicy(size_policy) layout = QGridLayout(pickup_box) layout.setObjectName(f"{ammo.name} Box Layout") current_row = 0 for ammo_item in ammo.items: item = resource_database.get_by_type_and_index(ResourceType.ITEM, ammo_item) target_count_label = QLabel(pickup_box) target_count_label.setText(f"{item.long_name} Target" if len(ammo.items) > 1 else "Target count") maximum_spinbox = QSpinBox(pickup_box) maximum_spinbox.setMaximum(ammo.maximum) maximum_spinbox.valueChanged.connect(partial(self._on_update_ammo_maximum_spinbox, ammo_item)) self._ammo_maximum_spinboxes[ammo_item].append(maximum_spinbox) layout.addWidget(target_count_label, current_row, 0) layout.addWidget(maximum_spinbox, current_row, 1) current_row += 1 count_label = QLabel(pickup_box) count_label.setText("Pickup Count") count_label.setToolTip("How many instances of this expansion should be placed.") pickup_spinbox = QSpinBox(pickup_box) pickup_spinbox.setMaximum(AmmoState.maximum_pickup_count()) pickup_spinbox.valueChanged.connect(partial(self._on_update_ammo_pickup_spinbox, ammo)) layout.addWidget(count_label, current_row, 0) layout.addWidget(pickup_spinbox, current_row, 1) current_row += 1 if ammo.temporaries: require_major_item_check = QCheckBox(pickup_box) require_major_item_check.setText("Requires the major item to work?") require_major_item_check.stateChanged.connect(partial(self._on_update_ammo_require_major_item, ammo)) layout.addWidget(require_major_item_check, current_row, 0, 1, 2) current_row += 1 else: require_major_item_check = None expected_count = QLabel(pickup_box) expected_count.setWordWrap(True) expected_count.setText(_EXPECTED_COUNT_TEXT_TEMPLATE) expected_count.setToolTip("Some expansions may provide 1 extra, even with no variance, if the total count " "is not divisible by the pickup count.") layout.addWidget(expected_count, current_row, 0, 1, 2) current_row += 1 self._ammo_pickup_widgets[ammo] = (pickup_spinbox, expected_count, expand_ammo_button, category_label, pickup_box, require_major_item_check) expand_ammo_button.clicked.connect(partial(_toggle_box_visibility, expand_ammo_button, pickup_box)) pickup_box.setVisible(False) self.ammo_layout.addLayout(title_layout) self.ammo_layout.addWidget(pickup_box)