|
1 | | -from decimal import ROUND_HALF_UP, Decimal |
2 | | -from typing import List, Literal, Optional, Tuple |
| 1 | +from typing import List |
3 | 2 |
|
4 | 3 | from ...model.common import InventoryInfo |
5 | 4 | from ..modulebase import * |
|
10 | 9 | from ...db.database import db |
11 | 10 | from ...model.enums import * |
12 | 11 | from ...util.questutils import * |
| 12 | +from ...util.format_number import format_number |
13 | 13 |
|
14 | 14 | @description('仅开启时生效,氪体数将取满足条件的最大值,禅模式指不执行体力相关的功能,仅在清日常生效,单项执行将忽略。庆典包括其倍数,加速期间的所有倍数判断均x2') |
15 | 15 | @name("全局配置") |
@@ -285,235 +285,125 @@ async def do_task(self, client: pcrclient): |
285 | 285 | '香水', '扫荡券', '加速券', '大师币', '连结币', |
286 | 286 | ) |
287 | 287 |
|
288 | | -_CN_SCALE: Tuple[Tuple[str, int], ...] = (('亿', 10 ** 8), ('万', 10 ** 4)) |
289 | | -_EN_SCALE: Tuple[Tuple[str, int], ...] = (('b', 10 ** 9), ('m', 10 ** 6), ('k', 10 ** 3)) |
290 | | - |
291 | | - |
292 | | -def _resolve_scale_unit(val: int, scale: Literal['k', 'm', 'b', '万', '亿']) -> Tuple[int, str]: |
293 | | - ladder = _CN_SCALE if scale in ('万', '亿') else _EN_SCALE |
294 | | - start = next(i for i, (s, _) in enumerate(ladder) if s == scale) |
295 | | - av = abs(val) |
296 | | - for i in range(start, len(ladder)): |
297 | | - suf, div = ladder[i] |
298 | | - if av >= div: |
299 | | - return div, suf |
300 | | - return 1, '' |
301 | | - |
302 | | - |
303 | | -def _round_scaled_quotient(quotient: Decimal, decimals: int) -> str: |
304 | | - if decimals <= 0: |
305 | | - n = quotient.quantize(Decimal('1'), rounding=ROUND_HALF_UP) |
306 | | - return str(int(n)) |
307 | | - step = Decimal('1').scaleb(-decimals) |
308 | | - n = quotient.quantize(step, rounding=ROUND_HALF_UP) |
309 | | - s = format(n, 'f') |
310 | | - if '.' in s: |
311 | | - s = s.rstrip('0').rstrip('.') |
312 | | - return s |
313 | | - |
314 | | - |
315 | | -def _group_int_string(int_part: str, use_grouping: bool) -> str: |
316 | | - if not use_grouping or not int_part: |
317 | | - return int_part |
318 | | - sign = '' |
319 | | - if int_part[0] == '-': |
320 | | - sign = '-' |
321 | | - int_part = int_part[1:] |
322 | | - elif int_part[0] == '+': |
323 | | - sign = '+' |
324 | | - int_part = int_part[1:] |
325 | | - rev = int_part[::-1] |
326 | | - pieces = [rev[i:i + 3][::-1] for i in range(0, len(rev), 3)] |
327 | | - return sign + ','.join(reversed(pieces)) |
328 | | - |
329 | | - |
330 | | -def _apply_numeric_separator(num_str: str, mode: Literal['auto', 'no', 'yes'], original_val: int) -> str: |
331 | | - if mode == 'no': |
332 | | - use = False |
333 | | - elif mode == 'yes': |
334 | | - use = True |
335 | | - else: |
336 | | - use = abs(original_val) >= 100_000 |
337 | | - if '.' in num_str: |
338 | | - a, b = num_str.split('.', 1) |
339 | | - return _group_int_string(a, use) + '.' + b |
340 | | - return _group_int_string(num_str, use) |
341 | | - |
342 | | - |
343 | | -def format_number( |
344 | | - val: int, |
345 | | - scale: Optional[Literal['k', 'm', 'b', '万', '亿']] = None, |
346 | | - decimals: Optional[int] = None, |
347 | | - separator: Literal['auto', 'no', 'yes'] = 'auto', |
348 | | -) -> str: |
349 | | - if scale is None: |
350 | | - s = str(int(val)) |
351 | | - return _apply_numeric_separator(s, separator, val) |
352 | | - dec = 2 if decimals is None else decimals |
353 | | - div, suffix = _resolve_scale_unit(val, scale) |
354 | | - if not suffix: |
355 | | - s = str(int(val)) |
356 | | - return _apply_numeric_separator(s, separator, val) |
357 | | - q = Decimal(val) / Decimal(div) |
358 | | - coeff = _round_scaled_quotient(q, dec) |
359 | | - return _apply_numeric_separator(coeff, separator, val) + suffix |
360 | | - |
361 | | - |
362 | 288 | @description('展示基本信息,固定显示玩家名、体力、等级、钻石、母猪石、全角色战力,可自定义显示其他信息') |
363 | 289 | @name('基本信息') |
364 | 290 | @default(True) |
365 | | -@multichoice("user_info_display", "显示信息", |
| 291 | +@multichoice( |
| 292 | + "user_info_display", "显示信息", |
366 | 293 | ['心碎', '星幽碎片', '炼金点数', '香水'], |
367 | | - ['玛娜', '心碎', '星球杯', '星幽碎片', '属性球', '大师碎片', '炼金点数', '香水', '扫荡券', '加速券', '大师币', '连结币']) |
| 294 | + ['玛娜', '心碎', '星球杯', '星幽碎片', '属性球', '大师碎片', '炼金点数', '香水', '扫荡券', '加速券', '大师币', '连结币'] |
| 295 | +) |
368 | 296 | class user_info(Module): |
369 | | - async def do_task(self, client: pcrclient): |
370 | | - now = db.format_time(apiclient.datetime) |
371 | | - display_items = set(self.get_config('user_info_display')) |
| 297 | + def _collect_optional_info(self, client: pcrclient, display_items: set[str]) -> dict[str, str]: |
| 298 | + data = client.data |
| 299 | + inv = data.get_inventory |
372 | 300 |
|
373 | | - # 固定信息 |
374 | | - name = client.data.user_name |
375 | | - level = client.data.team_level |
376 | | - stamina = client.data.stamina |
377 | | - max_stamina = db.team_info[client.data.team_level].max_stamina |
378 | | - jewel = client.data.jewel.free_jewel + client.data.jewel.jewel |
379 | | - pig = client.data.get_inventory((eInventoryType.Item, 90005)) |
380 | | - tot_power = sum([client.data.get_unit_power(unit) for unit in client.data.unit]) |
381 | | - |
382 | | - # 可选信息 |
383 | | - optional_info = {} |
384 | | - |
385 | | - if '玛娜' in display_items: |
386 | | - mana = client.data.gold.gold_id_free + client.data.gold.gold_id_pay |
387 | | - optional_info['玛娜'] = format_number(mana, scale='亿', decimals=1, separator='no') |
388 | | - |
389 | | - if '心碎' in display_items: |
390 | | - heart = client.data.get_inventory(db.heart) |
391 | | - xinsui = client.data.get_inventory(db.xinsui) |
| 301 | + def fmt(n: int, **kwargs) -> str: |
| 302 | + return format_number(n, **kwargs) |
| 303 | + |
| 304 | + def fmt_heart() -> str: |
| 305 | + heart = inv(db.heart) |
| 306 | + xinsui = inv(db.xinsui) |
392 | 307 | if heart > 0: |
393 | | - optional_info['心碎'] = f"{format_number(xinsui)}(大心 {format_number(heart)})" |
394 | | - else: |
395 | | - optional_info['心碎'] = format_number(xinsui) |
396 | | - |
397 | | - if '星球杯' in display_items: |
398 | | - star_cup = client.data.get_inventory(db.xingqiubei) |
399 | | - optional_info['星球杯'] = format_number(star_cup) |
400 | | - |
401 | | - if '星幽碎片' in display_items: |
402 | | - xinyou = client.data.get_inventory(db.xinyou) |
403 | | - optional_info['星幽碎片'] = format_number(xinyou) |
404 | | - |
405 | | - if '属性球' in display_items: |
406 | | - fire = client.data.get_inventory(db.fire_ball) |
407 | | - water = client.data.get_inventory(db.water_ball) |
408 | | - wind = client.data.get_inventory(db.wind_ball) |
409 | | - sun = client.data.get_inventory(db.sun_ball) |
410 | | - dark = client.data.get_inventory(db.dark_ball) |
411 | | - optional_info['属性球'] = f"{format_number(fire)}/{format_number(water)}/{format_number(wind)}/{format_number(sun)}/{format_number(dark)}" |
412 | | - |
413 | | - if '大师碎片' in display_items: |
414 | | - master = client.data.get_inventory(db.master_fragment) |
415 | | - master_f = client.data.get_inventory(db.master_ffragment) |
| 308 | + return f"{fmt(xinsui)}(大心 {fmt(heart)})" |
| 309 | + return fmt(xinsui) |
| 310 | + |
| 311 | + def fmt_balls() -> str: |
| 312 | + items = [ |
| 313 | + db.fire_ball, |
| 314 | + db.water_ball, |
| 315 | + db.wind_ball, |
| 316 | + db.sun_ball, |
| 317 | + db.dark_ball, |
| 318 | + ] |
| 319 | + return '/'.join(fmt(inv(item)) for item in items) |
| 320 | + |
| 321 | + def fmt_master_fragment() -> str: |
| 322 | + master = inv(db.master_fragment) |
| 323 | + master_f = inv(db.master_ffragment) |
416 | 324 | if master_f > 0: |
417 | | - optional_info['大师碎片'] = f"{master}(残片 {master_f})" |
418 | | - else: |
419 | | - optional_info['大师碎片'] = str(master) |
420 | | - |
421 | | - if '炼金点数' in display_items: |
422 | | - alces_pt = client.data.get_inventory(db.ex_rainbow_enhance_pt) |
423 | | - optional_info['炼金点数'] = format_number(alces_pt, scale='万', decimals=0, separator='no') |
424 | | - |
425 | | - if '香水' in display_items: |
426 | | - perfume = client.data.get_inventory((eInventoryType.Item, 26203)) |
427 | | - optional_info['香水'] = str(perfume) |
428 | | - |
429 | | - if '扫荡券' in display_items: |
430 | | - sweep_ticket = client.data.get_inventory((eInventoryType.Item, 23001)) |
431 | | - optional_info['扫荡券'] = format_number(sweep_ticket) |
432 | | - |
433 | | - if '加速券' in display_items: |
434 | | - speed_ticket = client.data.get_inventory(db.travel_speed_up_paper) |
435 | | - optional_info['加速券'] = str(speed_ticket) |
436 | | - |
437 | | - if '大师币' in display_items: |
438 | | - master_coin = client.data.get_inventory((eInventoryType.Item, 90008)) |
439 | | - optional_info['大师币'] = format_number(master_coin, scale='万', decimals=1, separator='no') |
440 | | - |
441 | | - if '连结币' in display_items: |
442 | | - link_coin = client.data.get_inventory((eInventoryType.Item, 99007)) |
443 | | - optional_info['连结币'] = str(link_coin) |
444 | | - |
445 | | - # 注释掉的琐碎物品(方便后续启用) |
446 | | - # if 'EX装备币' in display_items: |
447 | | - # ex_weapon = client.data.get_inventory((eInventoryType.Item, 90009)) |
448 | | - # ex_armor = client.data.get_inventory((eInventoryType.Item, 90010)) |
449 | | - # ex_accessory = client.data.get_inventory((eInventoryType.Item, 90011)) |
450 | | - # fmt_coin = lambda n: format_number(n, scale='万', decimals=2, separator='no') |
451 | | - # optional_info['EX装备币'] = f"{fmt_coin(ex_weapon)}/{fmt_coin(ex_armor)}/{fmt_coin(ex_accessory)}" |
452 | | - |
453 | | - # if '商店币' in display_items: |
454 | | - # dungeon_coin = client.data.get_inventory((eInventoryType.Item, 90002)) |
455 | | - # arena_coin = client.data.get_inventory((eInventoryType.Item, 90003)) |
456 | | - # grand_arena_coin = client.data.get_inventory((eInventoryType.Item, 90004)) |
457 | | - # clan_coin = client.data.get_inventory((eInventoryType.Item, 90006)) |
458 | | - # fmt_coin = lambda n: format_number(n, scale='万', decimals=2, separator='no') |
459 | | - # optional_info['商店币'] = f"{fmt_coin(dungeon_coin)}/{fmt_coin(arena_coin)}/{fmt_coin(grand_arena_coin)}/{fmt_coin(clan_coin)}" |
460 | | - |
461 | | - # if '原矿' in display_items: |
462 | | - # # 原矿 ID 范围通常是 35000-35999,按ID倒序 |
463 | | - # ores = [(item_id, client.data.get_inventory((eInventoryType.Item, item_id))) |
464 | | - # for item_id in range(35999, 34999, -1) |
465 | | - # if client.data.get_inventory((eInventoryType.Item, item_id)) > 0] |
466 | | - # if ores: |
467 | | - # optional_info['原矿'] = '/'.join(str(cnt) for _, cnt in ores) |
468 | | - |
469 | | - # if '经验药剂' in display_items: |
470 | | - # # 经验药剂 ID: 20001-20004,按ID倒序 |
471 | | - # exp_potions = [client.data.get_inventory((eInventoryType.Item, item_id)) |
472 | | - # for item_id in range(20004, 20000, -1)] |
473 | | - # optional_info['经验药剂'] = '/'.join( |
474 | | - # format_number(cnt, scale='万', decimals=2, separator='auto') for cnt in exp_potions) |
475 | | - |
476 | | - # if '精炼石' in display_items: |
477 | | - # # 精炼石 ID: 22001-22006,按ID倒序 |
478 | | - # refine_stones = [client.data.get_inventory((eInventoryType.Item, item_id)) |
479 | | - # for item_id in range(22006, 22000, -1)] |
480 | | - # optional_info['精炼石'] = '/'.join( |
481 | | - # format_number(cnt, scale='万', decimals=2, separator='auto') for cnt in refine_stones) |
482 | | - |
483 | | - # if '好感礼物' in display_items: |
484 | | - # # 蛋糕类 ID: 50001-50003,按ID倒序 |
485 | | - # cakes = [client.data.get_inventory((eInventoryType.Item, item_id)) |
486 | | - # for item_id in range(50003, 50000, -1)] |
487 | | - # optional_info['好感礼物'] = '/'.join( |
488 | | - # format_number(cnt, scale='万', decimals=2, separator='auto') for cnt in cakes) |
489 | | - |
490 | | - # 输出格式 |
491 | | - if stamina >= max_stamina: |
492 | | - self._warn(f"体力爆了!") |
493 | | - |
494 | | - # 第一行:固定信息 |
495 | | - self._log(f"{name} 体力{stamina}({max_stamina}) 等级{level} 钻石{format_number(jewel)}") |
496 | | - |
497 | | - # 第二行:可选信息(前两项)+ 母猪石(顺序与配置候选项一致,避免 set 迭代无序) |
498 | | - line2_items = [] |
| 325 | + return f"{master}(残片 {master_f})" |
| 326 | + return str(master) |
| 327 | + |
| 328 | + handlers = { |
| 329 | + '玛娜': lambda: fmt( |
| 330 | + data.gold.gold_id_free + data.gold.gold_id_pay, |
| 331 | + scale='亿', |
| 332 | + decimals=1, |
| 333 | + separator='no', |
| 334 | + ), |
| 335 | + '心碎': fmt_heart, |
| 336 | + '星球杯': lambda: fmt(inv(db.xingqiubei)), |
| 337 | + '星幽碎片': lambda: fmt(inv(db.xinyou)), |
| 338 | + '母猪石': lambda: fmt(inv((eInventoryType.Item, 90005))), |
| 339 | + '属性球': fmt_balls, |
| 340 | + '大师碎片': fmt_master_fragment, |
| 341 | + '炼金点数': lambda: fmt( |
| 342 | + inv(db.ex_rainbow_enhance_pt), |
| 343 | + scale='万', |
| 344 | + decimals=0, |
| 345 | + separator='no', |
| 346 | + ), |
| 347 | + '香水': lambda: str(inv(db.ex_rainbow_enhance_ball)), |
| 348 | + '扫荡券': lambda: fmt(inv((eInventoryType.Item, 23001))), |
| 349 | + '加速券': lambda: str(inv(db.travel_speed_up_paper)), |
| 350 | + '大师币': lambda: fmt( |
| 351 | + inv((eInventoryType.Item, 90008)), |
| 352 | + scale='万', |
| 353 | + decimals=1, |
| 354 | + separator='no', |
| 355 | + ), |
| 356 | + '连结币': lambda: str(inv((eInventoryType.Item, 99007))), |
| 357 | + } |
| 358 | + |
| 359 | + return { |
| 360 | + key: handlers[key]() |
| 361 | + for key in _USER_INFO_DISPLAY_ORDER |
| 362 | + if key in display_items and key in handlers |
| 363 | + } |
| 364 | + |
| 365 | + def _log_optional_info(self, optional_info: dict[str, str], pig: int) -> None: |
499 | 366 | keys = [k for k in _USER_INFO_DISPLAY_ORDER if k in optional_info] |
500 | | - for i in range(min(2, len(keys))): |
501 | | - line2_items.append(f"{keys[i]}{optional_info[keys[i]]}") |
| 367 | + |
| 368 | + line2_items = [ |
| 369 | + f"{key}{optional_info[key]}" |
| 370 | + for key in keys[:2] |
| 371 | + ] |
502 | 372 | line2_items.append(f"母猪石{format_number(pig)}") |
503 | 373 | self._log(' '.join(line2_items)) |
504 | 374 |
|
505 | | - # 第三行及以后:每行3个属性 |
506 | | - remaining_keys = keys[2:] |
507 | | - for i in range(0, len(remaining_keys), 3): |
508 | | - line_items = [] |
509 | | - for j in range(3): |
510 | | - if i + j < len(remaining_keys): |
511 | | - key = remaining_keys[i + j] |
512 | | - line_items.append(f"{key}{optional_info[key]}") |
| 375 | + for i in range(2, len(keys), 3): |
| 376 | + line_items = [ |
| 377 | + f"{key}{optional_info[key]}" |
| 378 | + for key in keys[i:i + 3] |
| 379 | + ] |
513 | 380 | self._log(' '.join(line_items)) |
514 | 381 |
|
515 | | - # 最后一行:全角色战力 |
516 | | - self._log(f"全角色战力:{format_number(tot_power)}") |
517 | | - self._log(f"已氪体数:{client.data.recover_stamina_exec_count}") |
| 382 | + async def do_task(self, client: pcrclient): |
| 383 | + data = client.data |
| 384 | + now = db.format_time(apiclient.datetime) |
| 385 | + display_items = set(self.get_config('user_info_display')) |
| 386 | + |
| 387 | + name = data.user_name |
| 388 | + level = data.team_level |
| 389 | + stamina = data.stamina |
| 390 | + max_stamina = db.team_info[level].max_stamina |
| 391 | + jewel = data.jewel.free_jewel + data.jewel.jewel |
| 392 | + pig = data.get_inventory((eInventoryType.Item, 90005)) |
| 393 | + total_power = sum(data.get_unit_power(unit) for unit in data.unit) |
| 394 | + |
| 395 | + if stamina >= max_stamina: |
| 396 | + self._warn("体力爆了!") |
| 397 | + |
| 398 | + optional_info = self._collect_optional_info(client, display_items) |
| 399 | + |
| 400 | + self._log( |
| 401 | + f"{name} 体力{stamina}({max_stamina}) " |
| 402 | + f"等级{level} 钻石{format_number(jewel)}" |
| 403 | + ) |
| 404 | + self._log_optional_info(optional_info, pig) |
| 405 | + |
| 406 | + self._log(f"全角色战力:{format_number(total_power)}") |
| 407 | + self._log(f"已氪体数:{data.recover_stamina_exec_count}") |
518 | 408 | self._log(f"清日常时间:{now}") |
519 | 409 |
|
0 commit comments