|
11 | 11 | from ...model.custom import UnitAttribute |
12 | 12 |
|
13 | 13 |
|
| 14 | +def _normal_ex_equip_state(client: pcrclient): |
| 15 | + return { |
| 16 | + str(unit_id): {str(ex_slot.slot): ex_slot.serial_id for ex_slot in unit.ex_equip_slot} |
| 17 | + for unit_id, unit in client.data.unit.items() |
| 18 | + } |
| 19 | + |
| 20 | +def _group_ex_equip_changes(changes): |
| 21 | + grouped = {} |
| 22 | + for unit_id, slot, serial_id in changes: |
| 23 | + grouped.setdefault(unit_id, []).append(ExtraEquipChangeSlot(slot=slot, serial_id=serial_id)) |
| 24 | + return [ExtraEquipChangeUnit(unit_id=unit_id, ex_equip_slot=slots, cb_ex_equip_slot=None) for unit_id, slots in grouped.items()] |
| 25 | + |
| 26 | +def _ex_equip_state_cache_path(module: Module): |
| 27 | + from os.path import join |
| 28 | + return join(CACHE_DIR, "modules", "ex_equip_state", module._parent.id + ".json") |
| 29 | + |
| 30 | +def _save_ex_equip_state_cache(module: Module, state): |
| 31 | + from os import makedirs |
| 32 | + from os.path import dirname |
| 33 | + import json |
| 34 | + cache_path = _ex_equip_state_cache_path(module) |
| 35 | + makedirs(dirname(cache_path), exist_ok=True) |
| 36 | + with open(cache_path, "w") as f: |
| 37 | + json.dump(state, f) |
| 38 | + |
| 39 | +def _load_ex_equip_state_cache(module: Module): |
| 40 | + from os.path import exists |
| 41 | + import json |
| 42 | + cache_path = _ex_equip_state_cache_path(module) |
| 43 | + if not exists(cache_path): |
| 44 | + return None |
| 45 | + with open(cache_path, "r") as f: |
| 46 | + return json.load(f) |
| 47 | + |
| 48 | + |
14 | 49 | @name('彩装究极炼成') |
15 | 50 | @default(True) |
16 | 51 | @inttype('ex_equip_rainbow_enhance_pt_hold', '保留pt数(w)', 10, list(range(0, 10000))) |
@@ -390,6 +425,99 @@ async def do_task(self, client: pcrclient): |
390 | 425 | raise SkipError("没有可合成的EX装") |
391 | 426 |
|
392 | 427 |
|
| 428 | +@name('EX状态保存/恢复') |
| 429 | +@default(False) |
| 430 | +@singlechoice('ex_equip_state_action', '行为', '保存', ['保存', '恢复']) |
| 431 | +@description('保存或恢复所有角色当前穿戴的普通EX装备状态。不影响账号配置,恢复时只处理有差异的部分,不会全部卸载。') |
| 432 | +class ex_equip_state(Module): |
| 433 | + cache_key = 'state' |
| 434 | + |
| 435 | + @staticmethod |
| 436 | + def normal_ex_equip_state(client: pcrclient): |
| 437 | + return { |
| 438 | + str(unit_id): {str(ex_slot.slot): ex_slot.serial_id for ex_slot in unit.ex_equip_slot} |
| 439 | + for unit_id, unit in client.data.unit.items() |
| 440 | + } |
| 441 | + |
| 442 | + @staticmethod |
| 443 | + def group_ex_equip_changes(changes): |
| 444 | + grouped = {} |
| 445 | + for unit_id, slot, serial_id in changes: |
| 446 | + grouped.setdefault(unit_id, []).append(ExtraEquipChangeSlot(slot=slot, serial_id=serial_id)) |
| 447 | + return [ExtraEquipChangeUnit(unit_id=unit_id, ex_equip_slot=slots, cb_ex_equip_slot=None) for unit_id, slots in grouped.items()] |
| 448 | + |
| 449 | + async def do_task(self, client: pcrclient): |
| 450 | + action = self.get_config('ex_equip_state_action') |
| 451 | + if action == '保存': |
| 452 | + await self.save_state(client) |
| 453 | + elif action == '恢复': |
| 454 | + await self.restore_state(client) |
| 455 | + else: |
| 456 | + raise AbortError(f"未知操作{action}") |
| 457 | + |
| 458 | + async def save_state(self, client: pcrclient): |
| 459 | + state = self.normal_ex_equip_state(client) |
| 460 | + equipped_cnt = sum(1 for slots in state.values() for serial_id in slots.values() if serial_id) |
| 461 | + unit_cnt = sum(1 for slots in state.values() if any(slots.values())) |
| 462 | + self.save_cache(self.cache_key, state) |
| 463 | + self._log(f"已保存{unit_cnt}个角色的{equipped_cnt}件普通EX装备状态") |
| 464 | + |
| 465 | + async def restore_state(self, client: pcrclient): |
| 466 | + state = self.find_cache(self.cache_key) |
| 467 | + if not state: |
| 468 | + raise AbortError("未找到已保存的EX装备状态,请先执行保存") |
| 469 | + |
| 470 | + current_state = self.normal_ex_equip_state(client) |
| 471 | + current_position = { |
| 472 | + serial_id: (int(unit_id), int(slot)) |
| 473 | + for unit_id, slots in current_state.items() |
| 474 | + for slot, serial_id in slots.items() |
| 475 | + if serial_id |
| 476 | + } |
| 477 | + |
| 478 | + remove_changes = [] |
| 479 | + apply_changes = [] |
| 480 | + skipped_missing = [] |
| 481 | + touched = set() |
| 482 | + |
| 483 | + for unit_id in sorted(state.keys(), key=int): |
| 484 | + if unit_id not in current_state: |
| 485 | + continue |
| 486 | + for slot in sorted(state[unit_id].keys(), key=int): |
| 487 | + if slot not in current_state[unit_id]: |
| 488 | + continue |
| 489 | + target_serial_id = state[unit_id][slot] or 0 |
| 490 | + current_serial_id = current_state[unit_id][slot] or 0 |
| 491 | + slot_no = int(slot) |
| 492 | + if current_serial_id == target_serial_id: |
| 493 | + continue |
| 494 | + if target_serial_id and target_serial_id not in client.data.ex_equips: |
| 495 | + skipped_missing.append(target_serial_id) |
| 496 | + continue |
| 497 | + if target_serial_id and target_serial_id in current_position: |
| 498 | + occupy_unit_id, occupy_slot = current_position[target_serial_id] |
| 499 | + if occupy_unit_id != int(unit_id) or occupy_slot != slot_no: |
| 500 | + key = (occupy_unit_id, occupy_slot) |
| 501 | + if key not in touched: |
| 502 | + remove_changes.append((occupy_unit_id, occupy_slot, 0)) |
| 503 | + touched.add(key) |
| 504 | + apply_changes.append((int(unit_id), slot_no, target_serial_id)) |
| 505 | + |
| 506 | + if skipped_missing: |
| 507 | + skipped = ', '.join(map(str, sorted(set(skipped_missing)))) |
| 508 | + self._warn(f"跳过{len(set(skipped_missing))}件已不存在的EX装备(serial_id: {skipped})") |
| 509 | + |
| 510 | + if not remove_changes and not apply_changes: |
| 511 | + raise SkipError("当前普通EX装备状态与保存状态一致") |
| 512 | + |
| 513 | + if remove_changes: |
| 514 | + await client.unit_equip_ex(self.group_ex_equip_changes(remove_changes)) |
| 515 | + if apply_changes: |
| 516 | + await client.unit_equip_ex(self.group_ex_equip_changes(apply_changes)) |
| 517 | + |
| 518 | + self._log(f"恢复了{len(apply_changes)}个普通EX装备槽位") |
| 519 | + |
| 520 | + |
393 | 521 | @name('撤下会战EX装') |
394 | 522 | @default(True) |
395 | 523 | @description('') |
@@ -558,4 +686,3 @@ async def do_task(self, client: pcrclient): |
558 | 686 | msg.append(f"{name}★{star}") |
559 | 687 | msg = ','.join(msg) |
560 | 688 | self._log(f"{db.get_unit_name(unit_id)} 装备 {msg}") |
561 | | - |
|
0 commit comments