mirror of
https://github.com/LmeSzinc/StarRailCopilot.git
synced 2024-11-22 00:35:34 +00:00
Add: Display overall progress of character planner
This commit is contained in:
parent
d6d61bb6fc
commit
33eedc2c63
@ -47,6 +47,7 @@
|
|||||||
"ServerUpdate": "04:00"
|
"ServerUpdate": "04:00"
|
||||||
},
|
},
|
||||||
"Planner": {
|
"Planner": {
|
||||||
|
"PlannerOverall": {},
|
||||||
"Item_Credit": {},
|
"Item_Credit": {},
|
||||||
"Item_Trailblaze_EXP": {},
|
"Item_Trailblaze_EXP": {},
|
||||||
"Item_Traveler_Guide": {},
|
"Item_Traveler_Guide": {},
|
||||||
|
@ -220,6 +220,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Planner": {
|
"Planner": {
|
||||||
|
"PlannerOverall": {
|
||||||
|
"type": "stored",
|
||||||
|
"value": {},
|
||||||
|
"display": true,
|
||||||
|
"stored": "StoredPlannerOverall",
|
||||||
|
"order": 4,
|
||||||
|
"color": "#85e7f2"
|
||||||
|
},
|
||||||
"Item_Credit": {
|
"Item_Credit": {
|
||||||
"type": "planner",
|
"type": "planner",
|
||||||
"value": {},
|
"value": {},
|
||||||
@ -729,9 +737,7 @@
|
|||||||
"type": "stored",
|
"type": "stored",
|
||||||
"value": {},
|
"value": {},
|
||||||
"display": "hide",
|
"display": "hide",
|
||||||
"stored": "StoredEchoOfWar",
|
"stored": "StoredEchoOfWar"
|
||||||
"order": 4,
|
|
||||||
"color": "#85e7f2"
|
|
||||||
},
|
},
|
||||||
"SimulatedUniverse": {
|
"SimulatedUniverse": {
|
||||||
"type": "stored",
|
"type": "stored",
|
||||||
|
@ -129,15 +129,18 @@ DungeonStorage:
|
|||||||
stored: StoredDungeonDouble
|
stored: StoredDungeonDouble
|
||||||
EchoOfWar:
|
EchoOfWar:
|
||||||
stored: StoredEchoOfWar
|
stored: StoredEchoOfWar
|
||||||
order: 4
|
|
||||||
color: "#85e7f2"
|
|
||||||
SimulatedUniverse:
|
SimulatedUniverse:
|
||||||
stored: StoredSimulatedUniverse
|
stored: StoredSimulatedUniverse
|
||||||
order: 6
|
order: 6
|
||||||
color: "#8fb5fe"
|
color: "#8fb5fe"
|
||||||
SupportReward:
|
SupportReward:
|
||||||
Collect: true
|
Collect: true
|
||||||
Planner: {}
|
Planner:
|
||||||
|
PlannerOverall:
|
||||||
|
stored: StoredPlannerOverall
|
||||||
|
display: true
|
||||||
|
order: 4
|
||||||
|
color: "#85e7f2"
|
||||||
# Items will be injected in config updater
|
# Items will be injected in config updater
|
||||||
|
|
||||||
Weekly:
|
Weekly:
|
||||||
|
@ -61,6 +61,8 @@ Dashboard:
|
|||||||
HoursAgo:
|
HoursAgo:
|
||||||
DaysAgo:
|
DaysAgo:
|
||||||
LongTimeAgo:
|
LongTimeAgo:
|
||||||
|
# Planner
|
||||||
|
EtaDays:
|
||||||
|
|
||||||
AddAlas:
|
AddAlas:
|
||||||
PopupTitle:
|
PopupTitle:
|
||||||
|
@ -38,15 +38,15 @@
|
|||||||
"order": 3,
|
"order": 3,
|
||||||
"color": "#79dbc4"
|
"color": "#79dbc4"
|
||||||
},
|
},
|
||||||
"EchoOfWar": {
|
"PlannerOverall": {
|
||||||
"name": "EchoOfWar",
|
"name": "PlannerOverall",
|
||||||
"path": "Dungeon.DungeonStorage.EchoOfWar",
|
"path": "Dungeon.Planner.PlannerOverall",
|
||||||
"i18n": "DungeonStorage.EchoOfWar.name",
|
"i18n": "Planner.PlannerOverall.name",
|
||||||
"stored": "StoredEchoOfWar",
|
"stored": "StoredPlannerOverall",
|
||||||
"attrs": {
|
"attrs": {
|
||||||
"time": "2020-01-01 00:00:00",
|
"time": "2020-01-01 00:00:00",
|
||||||
"total": 3,
|
"comment": "<??d",
|
||||||
"value": 0
|
"value": "??%"
|
||||||
},
|
},
|
||||||
"order": 4,
|
"order": 4,
|
||||||
"color": "#85e7f2"
|
"color": "#85e7f2"
|
||||||
@ -714,6 +714,19 @@
|
|||||||
"order": 0,
|
"order": 0,
|
||||||
"color": "#777777"
|
"color": "#777777"
|
||||||
},
|
},
|
||||||
|
"EchoOfWar": {
|
||||||
|
"name": "EchoOfWar",
|
||||||
|
"path": "Dungeon.DungeonStorage.EchoOfWar",
|
||||||
|
"i18n": "DungeonStorage.EchoOfWar.name",
|
||||||
|
"stored": "StoredEchoOfWar",
|
||||||
|
"attrs": {
|
||||||
|
"time": "2020-01-01 00:00:00",
|
||||||
|
"total": 3,
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"order": 0,
|
||||||
|
"color": "#777777"
|
||||||
|
},
|
||||||
"DailyQuest": {
|
"DailyQuest": {
|
||||||
"name": "DailyQuest",
|
"name": "DailyQuest",
|
||||||
"path": "DailyQuest.DailyStorage.DailyQuest",
|
"path": "DailyQuest.DailyStorage.DailyQuest",
|
||||||
|
@ -53,7 +53,7 @@ class GeneratedConfig:
|
|||||||
|
|
||||||
# Group `DungeonSupport`
|
# Group `DungeonSupport`
|
||||||
DungeonSupport_Use = 'when_daily' # always_use, when_daily, do_not_use
|
DungeonSupport_Use = 'when_daily' # always_use, when_daily, do_not_use
|
||||||
DungeonSupport_Character = 'FirstCharacter' # FirstCharacter, Acheron, Argenti, Arlan, Asta, Aventurine, Bailu, BlackSwan, Blade, Boothill, Bronya, Clara, DanHeng, DanHengImbibitorLunae, DrRatio, FuXuan, Gallagher, Gepard, Guinaifen, Hanya, Herta, Himeko, Hook, Huohuo, JingYuan, Jingliu, Kafka, Luka, Luocha, Lynx, March7th, Misha, Natasha, Pela, Qingque, Robin, RuanMei, Sampo, Seele, Serval, SilverWolf, Sparkle, Sushang, Tingyun, TopazNumby, TrailblazerDestruction, TrailblazerPreservation, Welt, Xueyi, Yanqing, Yukong
|
DungeonSupport_Character = 'FirstCharacter' # FirstCharacter, Acheron, Argenti, Arlan, Asta, Aventurine, Bailu, BlackSwan, Blade, Boothill, Bronya, Clara, DanHeng, DanHengImbibitorLunae, DrRatio, FuXuan, Gallagher, Gepard, Guinaifen, Hanya, Herta, Himeko, Hook, Huohuo, JingYuan, Jingliu, Kafka, Luka, Luocha, Lynx, March7th, Misha, Natasha, Pela, Qingque, Robin, RuanMei, Sampo, Seele, Serval, SilverWolf, Sparkle, Sushang, Tingyun, TopazNumby, TrailblazerDestruction, TrailblazerHarmony, TrailblazerPreservation, Welt, Xueyi, Yanqing, Yukong
|
||||||
|
|
||||||
# Group `DungeonStorage`
|
# Group `DungeonStorage`
|
||||||
DungeonStorage_TrailblazePower = {}
|
DungeonStorage_TrailblazePower = {}
|
||||||
@ -66,6 +66,7 @@ class GeneratedConfig:
|
|||||||
SupportReward_Collect = True
|
SupportReward_Collect = True
|
||||||
|
|
||||||
# Group `Planner`
|
# Group `Planner`
|
||||||
|
Planner_PlannerOverall = {}
|
||||||
Planner_Item_Credit = {}
|
Planner_Item_Credit = {}
|
||||||
Planner_Item_Trailblaze_EXP = {}
|
Planner_Item_Trailblaze_EXP = {}
|
||||||
Planner_Item_Traveler_Guide = {}
|
Planner_Item_Traveler_Guide = {}
|
||||||
|
@ -465,6 +465,10 @@
|
|||||||
"name": "Character Planner Progress",
|
"name": "Character Planner Progress",
|
||||||
"help": "Character planner is prioritized. After completed, \"Dungeon Settings\" will be executed."
|
"help": "Character planner is prioritized. After completed, \"Dungeon Settings\" will be executed."
|
||||||
},
|
},
|
||||||
|
"PlannerOverall": {
|
||||||
|
"name": "Planner",
|
||||||
|
"help": "Overall progress of character planner"
|
||||||
|
},
|
||||||
"Item_Credit": {
|
"Item_Credit": {
|
||||||
"name": "Credit",
|
"name": "Credit",
|
||||||
"help": ""
|
"help": ""
|
||||||
@ -1269,7 +1273,8 @@
|
|||||||
"MinutesAgo": "{time}min ago",
|
"MinutesAgo": "{time}min ago",
|
||||||
"HoursAgo": "{time}h ago",
|
"HoursAgo": "{time}h ago",
|
||||||
"DaysAgo": "{time}d ago",
|
"DaysAgo": "{time}d ago",
|
||||||
"LongTimeAgo": "long time ago"
|
"LongTimeAgo": "long time ago",
|
||||||
|
"EtaDays": "ETA {time}d"
|
||||||
},
|
},
|
||||||
"AddAlas": {
|
"AddAlas": {
|
||||||
"PopupTitle": "Add new config",
|
"PopupTitle": "Add new config",
|
||||||
|
@ -465,6 +465,10 @@
|
|||||||
"name": "Progreso del planificador de personajes",
|
"name": "Progreso del planificador de personajes",
|
||||||
"help": "Se prioriza el planificador de personajes. Una vez completado, se ejecutará la \"Ajustes de Mazmorra\"."
|
"help": "Se prioriza el planificador de personajes. Una vez completado, se ejecutará la \"Ajustes de Mazmorra\"."
|
||||||
},
|
},
|
||||||
|
"PlannerOverall": {
|
||||||
|
"name": "Plan.",
|
||||||
|
"help": "Progreso general del planificador de personajes"
|
||||||
|
},
|
||||||
"Item_Credit": {
|
"Item_Credit": {
|
||||||
"name": "Crédito",
|
"name": "Crédito",
|
||||||
"help": ""
|
"help": ""
|
||||||
@ -1269,7 +1273,8 @@
|
|||||||
"MinutesAgo": "hace {time}m",
|
"MinutesAgo": "hace {time}m",
|
||||||
"HoursAgo": "hace {time}h",
|
"HoursAgo": "hace {time}h",
|
||||||
"DaysAgo": "hace {time}d",
|
"DaysAgo": "hace {time}d",
|
||||||
"LongTimeAgo": "hace mucho tiempo"
|
"LongTimeAgo": "hace mucho tiempo",
|
||||||
|
"EtaDays": "ETA {time}d"
|
||||||
},
|
},
|
||||||
"AddAlas": {
|
"AddAlas": {
|
||||||
"PopupTitle": "Añadir nueva configuración",
|
"PopupTitle": "Añadir nueva configuración",
|
||||||
|
@ -465,6 +465,10 @@
|
|||||||
"name": "Planner._info.name",
|
"name": "Planner._info.name",
|
||||||
"help": "Planner._info.help"
|
"help": "Planner._info.help"
|
||||||
},
|
},
|
||||||
|
"PlannerOverall": {
|
||||||
|
"name": "Planner.PlannerOverall.name",
|
||||||
|
"help": "Planner.PlannerOverall.help"
|
||||||
|
},
|
||||||
"Item_Credit": {
|
"Item_Credit": {
|
||||||
"name": "信用ポイント",
|
"name": "信用ポイント",
|
||||||
"help": ""
|
"help": ""
|
||||||
@ -1269,7 +1273,8 @@
|
|||||||
"MinutesAgo": "Gui.Dashboard.MinutesAgo",
|
"MinutesAgo": "Gui.Dashboard.MinutesAgo",
|
||||||
"HoursAgo": "Gui.Dashboard.HoursAgo",
|
"HoursAgo": "Gui.Dashboard.HoursAgo",
|
||||||
"DaysAgo": "Gui.Dashboard.DaysAgo",
|
"DaysAgo": "Gui.Dashboard.DaysAgo",
|
||||||
"LongTimeAgo": "Gui.Dashboard.LongTimeAgo"
|
"LongTimeAgo": "Gui.Dashboard.LongTimeAgo",
|
||||||
|
"EtaDays": "Gui.Dashboard.EtaDays"
|
||||||
},
|
},
|
||||||
"AddAlas": {
|
"AddAlas": {
|
||||||
"PopupTitle": "新しいコンフィグを追加",
|
"PopupTitle": "新しいコンフィグを追加",
|
||||||
|
@ -465,6 +465,10 @@
|
|||||||
"name": "养成规划进度",
|
"name": "养成规划进度",
|
||||||
"help": "优先执行养成规划,养成规划完成后,执行 \"每日副本设置\""
|
"help": "优先执行养成规划,养成规划完成后,执行 \"每日副本设置\""
|
||||||
},
|
},
|
||||||
|
"PlannerOverall": {
|
||||||
|
"name": "养成规划",
|
||||||
|
"help": "角色养成规划总体进度"
|
||||||
|
},
|
||||||
"Item_Credit": {
|
"Item_Credit": {
|
||||||
"name": "信用点",
|
"name": "信用点",
|
||||||
"help": ""
|
"help": ""
|
||||||
@ -1269,7 +1273,8 @@
|
|||||||
"MinutesAgo": "{time}分钟前",
|
"MinutesAgo": "{time}分钟前",
|
||||||
"HoursAgo": "{time}小时前",
|
"HoursAgo": "{time}小时前",
|
||||||
"DaysAgo": "{time}天前",
|
"DaysAgo": "{time}天前",
|
||||||
"LongTimeAgo": "很久以前"
|
"LongTimeAgo": "很久以前",
|
||||||
|
"EtaDays": "剩余{time}天"
|
||||||
},
|
},
|
||||||
"AddAlas": {
|
"AddAlas": {
|
||||||
"PopupTitle": "添加新配置",
|
"PopupTitle": "添加新配置",
|
||||||
|
@ -465,6 +465,10 @@
|
|||||||
"name": "養成規劃進度",
|
"name": "養成規劃進度",
|
||||||
"help": "優先執行養成規劃,養成規劃完成後,執行 \"每日副本設定\""
|
"help": "優先執行養成規劃,養成規劃完成後,執行 \"每日副本設定\""
|
||||||
},
|
},
|
||||||
|
"PlannerOverall": {
|
||||||
|
"name": "養成規劃",
|
||||||
|
"help": "角色養成規劃總體進度"
|
||||||
|
},
|
||||||
"Item_Credit": {
|
"Item_Credit": {
|
||||||
"name": "信用點",
|
"name": "信用點",
|
||||||
"help": ""
|
"help": ""
|
||||||
@ -1269,7 +1273,8 @@
|
|||||||
"MinutesAgo": "{time}分鐘前",
|
"MinutesAgo": "{time}分鐘前",
|
||||||
"HoursAgo": "{time}小時前",
|
"HoursAgo": "{time}小時前",
|
||||||
"DaysAgo": "{time}天前",
|
"DaysAgo": "{time}天前",
|
||||||
"LongTimeAgo": "很久以前"
|
"LongTimeAgo": "很久以前",
|
||||||
|
"EtaDays": "剩餘{time}日"
|
||||||
},
|
},
|
||||||
"AddAlas": {
|
"AddAlas": {
|
||||||
"PopupTitle": "添加新的設定",
|
"PopupTitle": "添加新的設定",
|
||||||
|
@ -404,3 +404,8 @@ class StoredPlanner(StoredBase):
|
|||||||
value: int
|
value: int
|
||||||
total: int
|
total: int
|
||||||
synthesize: int
|
synthesize: int
|
||||||
|
|
||||||
|
|
||||||
|
class StoredPlannerOverall(StoredBase):
|
||||||
|
value: str = '??%'
|
||||||
|
comment: str = '<??d'
|
||||||
|
@ -20,6 +20,7 @@ from module.config.stored.classes import (
|
|||||||
StoredImmersifier,
|
StoredImmersifier,
|
||||||
StoredInt,
|
StoredInt,
|
||||||
StoredPlanner,
|
StoredPlanner,
|
||||||
|
StoredPlannerOverall,
|
||||||
StoredSimulatedUniverse,
|
StoredSimulatedUniverse,
|
||||||
StoredSimulatedUniverseElite,
|
StoredSimulatedUniverseElite,
|
||||||
StoredTrailblazePower,
|
StoredTrailblazePower,
|
||||||
@ -33,6 +34,7 @@ class StoredGenerated:
|
|||||||
CloudRemainSeasonPass = StoredInt("Alas.CloudStorage.CloudRemainSeasonPass")
|
CloudRemainSeasonPass = StoredInt("Alas.CloudStorage.CloudRemainSeasonPass")
|
||||||
CloudRemainPaid = StoredInt("Alas.CloudStorage.CloudRemainPaid")
|
CloudRemainPaid = StoredInt("Alas.CloudStorage.CloudRemainPaid")
|
||||||
CloudRemainFree = StoredInt("Alas.CloudStorage.CloudRemainFree")
|
CloudRemainFree = StoredInt("Alas.CloudStorage.CloudRemainFree")
|
||||||
|
PlannerOverall = StoredPlannerOverall("Dungeon.Planner.PlannerOverall")
|
||||||
Item_Credit = StoredPlanner("Dungeon.Planner.Item_Credit")
|
Item_Credit = StoredPlanner("Dungeon.Planner.Item_Credit")
|
||||||
Item_Trailblaze_EXP = StoredPlanner("Dungeon.Planner.Item_Trailblaze_EXP")
|
Item_Trailblaze_EXP = StoredPlanner("Dungeon.Planner.Item_Trailblaze_EXP")
|
||||||
Item_Traveler_Guide = StoredPlanner("Dungeon.Planner.Item_Traveler_Guide")
|
Item_Traveler_Guide = StoredPlanner("Dungeon.Planner.Item_Traveler_Guide")
|
||||||
|
@ -341,6 +341,11 @@ class AlasGUI(Frame):
|
|||||||
put_text(config.get("value", nodata)).style("--dashboard-value--"),
|
put_text(config.get("value", nodata)).style("--dashboard-value--"),
|
||||||
put_text(f' / {config.get("total", "")}').style("--dashboard-time--"),
|
put_text(f' / {config.get("total", "")}').style("--dashboard-time--"),
|
||||||
]
|
]
|
||||||
|
elif "comment" in dic.get("attrs", []) and config.get("comment") is not None:
|
||||||
|
return [
|
||||||
|
put_text(config.get("value", nodata)).style("--dashboard-value--"),
|
||||||
|
put_text(f' {config.get("comment", "")}').style("--dashboard-time--"),
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
return [
|
return [
|
||||||
put_text(config.get("value", nodata)).style("--dashboard-value--"),
|
put_text(config.get("value", nodata)).style("--dashboard-value--"),
|
||||||
|
@ -346,6 +346,7 @@ def put_arg_stored(kwargs: T_Output_Kwargs) -> Output:
|
|||||||
value = values.pop("value", "")
|
value = values.pop("value", "")
|
||||||
total = values.pop("total", "")
|
total = values.pop("total", "")
|
||||||
time_ = values.pop("time", "")
|
time_ = values.pop("time", "")
|
||||||
|
comment = values.pop("comment", "")
|
||||||
|
|
||||||
if value != "" and total != "":
|
if value != "" and total != "":
|
||||||
# 0 / 100
|
# 0 / 100
|
||||||
@ -353,6 +354,12 @@ def put_arg_stored(kwargs: T_Output_Kwargs) -> Output:
|
|||||||
put_text(value).style("--dashboard-value--"),
|
put_text(value).style("--dashboard-value--"),
|
||||||
put_text(f" / {total}").style("--dashboard-time--"),
|
put_text(f" / {total}").style("--dashboard-time--"),
|
||||||
])]
|
])]
|
||||||
|
elif value != "" and comment != "":
|
||||||
|
# 88% <1.2d
|
||||||
|
rows = [put_scope(f"dashboard-value-{name}", [
|
||||||
|
put_text(value).style("--dashboard-value--"),
|
||||||
|
put_text(f" {comment}").style("--dashboard-time--"),
|
||||||
|
])]
|
||||||
elif value != "":
|
elif value != "":
|
||||||
# 100
|
# 100
|
||||||
rows = [put_scope(f"dashboard-value-{name}", [
|
rows = [put_scope(f"dashboard-value-{name}", [
|
||||||
@ -393,6 +400,11 @@ def put_arg_planner(kwargs: T_Output_Kwargs) -> Output | None:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# Hide items not needed by the planner
|
# Hide items not needed by the planner
|
||||||
return None
|
return None
|
||||||
|
eta = values.get("eta", 0)
|
||||||
|
if eta > 0:
|
||||||
|
eta = f" - {t('Gui.Dashboard.EtaDays', time=eta)}"
|
||||||
|
else:
|
||||||
|
eta = ""
|
||||||
|
|
||||||
value = values.pop('value', 0)
|
value = values.pop('value', 0)
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
@ -402,7 +414,7 @@ def put_arg_planner(kwargs: T_Output_Kwargs) -> Output | None:
|
|||||||
total = tuple(total.values())
|
total = tuple(total.values())
|
||||||
|
|
||||||
row = put_scope(f"arg_stored-stored-value-{name}", [
|
row = put_scope(f"arg_stored-stored-value-{name}", [
|
||||||
put_text(f"{progress:.2f}%").style("--dashboard-bold--"),
|
put_text(f"{progress:.2f}%{eta}").style("--dashboard-bold--"),
|
||||||
put_text(f"{value} / {total}").style("--dashboard-time--"),
|
put_text(f"{value} / {total}").style("--dashboard-time--"),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
import math
|
||||||
import typing as t
|
import typing as t
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from functools import partial
|
from functools import cached_property as functools_cached_property, partial
|
||||||
|
|
||||||
from pydantic import BaseModel, ValidationError, WrapValidator, field_validator, model_validator
|
from pydantic import BaseModel, ValidationError, WrapValidator, computed_field, field_validator, model_validator
|
||||||
|
|
||||||
from module.base.decorator import cached_property, del_cached_property
|
from module.base.decorator import cached_property, del_cached_property
|
||||||
from module.config.stored.classes import now
|
from module.config.stored.classes import now
|
||||||
@ -74,16 +75,37 @@ class MultiValue(BaseModelWithFallback):
|
|||||||
self.blue += other.blue
|
self.blue += other.blue
|
||||||
self.purple += other.purple
|
self.purple += other.purple
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
green = max(self.green - other.green, 0)
|
||||||
|
blue = max(self.blue - other.blue, 0)
|
||||||
|
purple = max(self.purple - other.purple, 0)
|
||||||
|
return MultiValue(green=green, blue=blue, purple=purple)
|
||||||
|
|
||||||
def equivalent_green(self):
|
def equivalent_green(self):
|
||||||
return self.green + self.blue * 3 + self.purple * 9
|
return self.green + self.blue * 3 + self.purple * 9
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.green = 0
|
||||||
|
self.blue = 0
|
||||||
|
self.purple = 0
|
||||||
|
|
||||||
|
|
||||||
|
SET_ROW_EXCLUDE = {
|
||||||
|
'drop_equivalent_green',
|
||||||
|
'combat_cost',
|
||||||
|
'progress_remain',
|
||||||
|
'progress_total',
|
||||||
|
'progress_current',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class StoredPlannerProxy(BaseModelWithFallback):
|
class StoredPlannerProxy(BaseModelWithFallback):
|
||||||
item: ITEM_TYPES
|
item: ITEM_TYPES
|
||||||
value: int | MultiValue = 0
|
value: int | MultiValue = 0
|
||||||
total: int | MultiValue = 0
|
total: int | MultiValue = 0
|
||||||
synthesize: int | MultiValue = 0
|
synthesize: int | MultiValue = 0
|
||||||
progress: float = 0.
|
# progress: float = 0.
|
||||||
|
# eta: float = 0.
|
||||||
time: datetime = DEFAULT_TIME
|
time: datetime = DEFAULT_TIME
|
||||||
|
|
||||||
@field_validator('item', mode='before')
|
@field_validator('item', mode='before')
|
||||||
@ -110,6 +132,12 @@ class StoredPlannerProxy(BaseModelWithFallback):
|
|||||||
self.synthesize = 0
|
self.synthesize = 0
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
if self.item.has_group_base:
|
||||||
|
self.value.clear()
|
||||||
|
else:
|
||||||
|
self.value = 0
|
||||||
|
|
||||||
def update_synthesize(self):
|
def update_synthesize(self):
|
||||||
if self.item.has_group_base:
|
if self.item.has_group_base:
|
||||||
green = self.value.green - self.total.green
|
green = self.value.green - self.total.green
|
||||||
@ -147,6 +175,82 @@ class StoredPlannerProxy(BaseModelWithFallback):
|
|||||||
else:
|
else:
|
||||||
self.value.blue += self.synthesize.purple * 3
|
self.value.blue += self.synthesize.purple * 3
|
||||||
|
|
||||||
|
@computed_field(repr=False)
|
||||||
|
@functools_cached_property
|
||||||
|
def drop_equivalent_green(self) -> float:
|
||||||
|
# Tracks_of_Destiny
|
||||||
|
if self.item.dungeon is None:
|
||||||
|
return 1
|
||||||
|
if self.item.dungeon.is_Calyx_Golden_Treasures:
|
||||||
|
return 24000
|
||||||
|
if self.item.dungeon.is_Calyx_Golden_Memories:
|
||||||
|
# purple, blue, green = 5, 1, 0
|
||||||
|
return 48
|
||||||
|
if self.item.dungeon.is_Calyx_Golden_Aether:
|
||||||
|
# purple, blue, green = 1, 2, 2.5
|
||||||
|
return 17.5
|
||||||
|
if self.item.is_ItemAscension:
|
||||||
|
return 3
|
||||||
|
if self.item.is_ItemTrace:
|
||||||
|
# purple, blue, green = 0.155, 1, 1.25
|
||||||
|
return 5.645
|
||||||
|
if self.item.is_ItemWeekly:
|
||||||
|
return 3
|
||||||
|
raise ScriptError(f'{self} has no drop_equivalent_green defined')
|
||||||
|
|
||||||
|
@computed_field(repr=False)
|
||||||
|
@functools_cached_property
|
||||||
|
def combat_cost(self) -> int:
|
||||||
|
# Tracks_of_Destiny
|
||||||
|
if self.item.dungeon is None:
|
||||||
|
return 30
|
||||||
|
if self.item.dungeon.is_Calyx_Golden:
|
||||||
|
return 10
|
||||||
|
if self.item.is_ItemAscension:
|
||||||
|
return 30
|
||||||
|
if self.item.is_ItemTrace:
|
||||||
|
return 10
|
||||||
|
if self.item.is_ItemWeekly:
|
||||||
|
return 30
|
||||||
|
raise ScriptError(f'{self} has no stamina_pre_combat defined')
|
||||||
|
|
||||||
|
@computed_field(repr=False)
|
||||||
|
@functools_cached_property
|
||||||
|
def progress_remain(self) -> float:
|
||||||
|
if self.item.has_group_base:
|
||||||
|
remain = self.total - self.value - self.synthesize
|
||||||
|
return remain.equivalent_green()
|
||||||
|
else:
|
||||||
|
remain = max(self.total - self.value, 0)
|
||||||
|
return remain
|
||||||
|
|
||||||
|
@computed_field(repr=False)
|
||||||
|
@functools_cached_property
|
||||||
|
def progress_total(self) -> float:
|
||||||
|
if self.item.has_group_base:
|
||||||
|
return self.total.equivalent_green()
|
||||||
|
else:
|
||||||
|
return self.total
|
||||||
|
|
||||||
|
@computed_field(repr=False)
|
||||||
|
@functools_cached_property
|
||||||
|
def progress_current(self) -> float:
|
||||||
|
if self.item.has_group_base:
|
||||||
|
current = self.progress_total - self.progress_remain
|
||||||
|
current = min(max(current, 0), self.progress_total)
|
||||||
|
return current
|
||||||
|
else:
|
||||||
|
current = self.value
|
||||||
|
current = min(max(current, 0), self.total)
|
||||||
|
return current
|
||||||
|
|
||||||
|
@computed_field
|
||||||
|
@functools_cached_property
|
||||||
|
def progress(self) -> float:
|
||||||
|
# 0 to 100
|
||||||
|
progress = self.progress_current / self.progress_total * 100
|
||||||
|
return round(min(max(progress, 0), 100), 2)
|
||||||
|
|
||||||
def is_approaching_total(self, wave_done: int = 0):
|
def is_approaching_total(self, wave_done: int = 0):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@ -157,45 +261,46 @@ class StoredPlannerProxy(BaseModelWithFallback):
|
|||||||
"""
|
"""
|
||||||
wave_done = max(wave_done, 0)
|
wave_done = max(wave_done, 0)
|
||||||
# Items with a static drop rate will have `AVG * (wave_done + 1)
|
# Items with a static drop rate will have `AVG * (wave_done + 1)
|
||||||
if self.item.dungeon.is_Calyx_Golden_Treasures:
|
remain = self.progress_remain
|
||||||
return self.value + 24000 * (wave_done + 12) >= self.total
|
cost = self.combat_cost
|
||||||
if self.item.dungeon.is_Calyx_Golden_Memories:
|
drop = self.drop_equivalent_green
|
||||||
# purple, blue, green = 5, 1, 0
|
if cost == 10:
|
||||||
value = self.value.equivalent_green()
|
return remain <= drop * (wave_done + 12)
|
||||||
total = self.total.equivalent_green()
|
|
||||||
return value + 48 * (wave_done + 12) >= total
|
|
||||||
if self.item.dungeon.is_Calyx_Golden_Aether:
|
|
||||||
# purple, blue, green = 1, 2, 2.5
|
|
||||||
value = self.value.equivalent_green()
|
|
||||||
total = self.total.equivalent_green()
|
|
||||||
return value + 17.5 * (wave_done + 12) >= total
|
|
||||||
if self.item.is_ItemAscension:
|
|
||||||
return self.value + 3 * (wave_done + 1) >= self.total
|
|
||||||
if self.item.is_ItemTrace:
|
|
||||||
# purple, blue, green = 0.155, 1, 1.25
|
|
||||||
value = self.value.equivalent_green()
|
|
||||||
total = self.total.equivalent_green()
|
|
||||||
return value + 5.645 * (wave_done + 12) >= total
|
|
||||||
if self.item.is_ItemWeekly:
|
|
||||||
return self.value + 3 * (wave_done + 1) >= self.total
|
|
||||||
return False
|
|
||||||
|
|
||||||
def update_progress(self):
|
|
||||||
if self.item.has_group_base:
|
|
||||||
total = self.total.equivalent_green()
|
|
||||||
green = min(self.value.green, self.total.green)
|
|
||||||
blue = min(self.value.blue + self.synthesize.blue, self.total.blue)
|
|
||||||
purple = min(self.value.purple + self.synthesize.purple, self.total.purple)
|
|
||||||
value = green + blue * 3 + purple * 9
|
|
||||||
progress = value / total * 100
|
|
||||||
self.progress = round(min(max(progress, 0), 100), 2)
|
|
||||||
else:
|
else:
|
||||||
progress = self.value / self.total * 100
|
return remain <= drop * (wave_done + 1)
|
||||||
self.progress = round(min(max(progress, 0), 100), 2)
|
|
||||||
|
@computed_field
|
||||||
|
@functools_cached_property
|
||||||
|
def eta(self) -> float:
|
||||||
|
"""
|
||||||
|
Estimate remaining days to farm
|
||||||
|
"""
|
||||||
|
if not self.need_farm():
|
||||||
|
return 0.
|
||||||
|
if self.item.dungeon is None:
|
||||||
|
return 0.
|
||||||
|
|
||||||
|
remain = self.progress_remain
|
||||||
|
cost = self.combat_cost
|
||||||
|
drop = self.drop_equivalent_green
|
||||||
|
|
||||||
|
if self.item.is_ItemWeekly:
|
||||||
|
weeks = math.ceil(remain / drop / 3)
|
||||||
|
return weeks * 7
|
||||||
|
else:
|
||||||
|
stamina = math.ceil(remain / drop) * cost
|
||||||
|
return round(stamina / 240, 1)
|
||||||
|
|
||||||
|
def update(self, time=False):
|
||||||
|
for attr in SET_ROW_EXCLUDE:
|
||||||
|
del_cached_property(self, attr)
|
||||||
|
del_cached_property(self, 'progress')
|
||||||
|
del_cached_property(self, 'eta')
|
||||||
|
|
||||||
def update(self):
|
|
||||||
self.update_synthesize()
|
self.update_synthesize()
|
||||||
self.update_progress()
|
_ = self.progress
|
||||||
|
_ = self.eta
|
||||||
|
if time:
|
||||||
self.time = now()
|
self.time = now()
|
||||||
|
|
||||||
def load_value_total(self, item: ItemBase, value=None, total=None, synthesize=None):
|
def load_value_total(self, item: ItemBase, value=None, total=None, synthesize=None):
|
||||||
@ -344,9 +449,11 @@ class PlannerProgressParser:
|
|||||||
|
|
||||||
def from_config(self, data):
|
def from_config(self, data):
|
||||||
self.rows = {}
|
self.rows = {}
|
||||||
for row in data.values():
|
for name, row in data.items():
|
||||||
if not row:
|
if not row:
|
||||||
continue
|
continue
|
||||||
|
if name == 'PlannerOverall':
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
row = StoredPlannerProxy(**row)
|
row = StoredPlannerProxy(**row)
|
||||||
except (ScriptError, ValidationError) as e:
|
except (ScriptError, ValidationError) as e:
|
||||||
@ -355,8 +462,7 @@ class PlannerProgressParser:
|
|||||||
if not row.item.is_group_base:
|
if not row.item.is_group_base:
|
||||||
logger.error(f'from_config: item is not group base {row}')
|
logger.error(f'from_config: item is not group base {row}')
|
||||||
continue
|
continue
|
||||||
row.update_synthesize()
|
row.update(time=False)
|
||||||
row.update_progress()
|
|
||||||
self.rows[row.item.name] = row
|
self.rows[row.item.name] = row
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -372,17 +478,37 @@ class PlannerProgressParser:
|
|||||||
self.rows[name] = row
|
self.rows[name] = row
|
||||||
|
|
||||||
for row in self.rows.values():
|
for row in self.rows.values():
|
||||||
row.update()
|
row.update(time=True)
|
||||||
|
|
||||||
def to_config(self) -> dict:
|
def to_config(self) -> dict:
|
||||||
data = {}
|
data = {}
|
||||||
for row in self.rows.values():
|
for row in self.rows.values():
|
||||||
name = f'Item_{row.item.name}'
|
name = f'Item_{row.item.name}'
|
||||||
dic = row.model_dump()
|
dic = row.model_dump(exclude=SET_ROW_EXCLUDE)
|
||||||
dic['item'] = row.item.name
|
dic['item'] = row.item.name
|
||||||
data[name] = dic
|
data[name] = dic
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def get_overall(self):
|
||||||
|
"""
|
||||||
|
Calculate overall progress
|
||||||
|
Note that this method will clear all values
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Progress percentage
|
||||||
|
float: ETA in days
|
||||||
|
"""
|
||||||
|
eta = 0.
|
||||||
|
progress_current = 0.
|
||||||
|
progress_total = 0.
|
||||||
|
for row in self.rows.values():
|
||||||
|
eta += row.eta
|
||||||
|
progress_current += row.progress_current
|
||||||
|
progress_total += row.progress_total
|
||||||
|
|
||||||
|
progress = round(progress_current / progress_total * 100, 2)
|
||||||
|
return progress, eta
|
||||||
|
|
||||||
def iter_row_to_farm(self, need_farm=True) -> t.Iterable[StoredPlannerProxy]:
|
def iter_row_to_farm(self, need_farm=True) -> t.Iterable[StoredPlannerProxy]:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@ -494,6 +620,7 @@ class PlannerMixin(UI):
|
|||||||
planner = self.planner
|
planner = self.planner
|
||||||
|
|
||||||
data = planner.to_config()
|
data = planner.to_config()
|
||||||
|
progress, eta = planner.get_overall()
|
||||||
|
|
||||||
with self.config.multi_set():
|
with self.config.multi_set():
|
||||||
# Set value
|
# Set value
|
||||||
@ -506,5 +633,14 @@ class PlannerMixin(UI):
|
|||||||
remove.append(key)
|
remove.append(key)
|
||||||
for key in remove:
|
for key in remove:
|
||||||
self.config.cross_set(f'Dungeon.Planner.{key}', {})
|
self.config.cross_set(f'Dungeon.Planner.{key}', {})
|
||||||
|
print(progress, eta)
|
||||||
|
# Set overall
|
||||||
|
self.config.stored.PlannerOverall.value = f'{progress:.2f}%'
|
||||||
|
self.config.stored.PlannerOverall.comment = f'<{eta:.1f}d'
|
||||||
|
|
||||||
del_cached_property(self, 'planner')
|
del_cached_property(self, 'planner')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
self = PlannerMixin('src')
|
||||||
|
self.planner_write(self.planner)
|
||||||
|
@ -22,6 +22,7 @@ DETAIL_TITLE.load_search(RESULT_CHECK.search)
|
|||||||
class OcrItemName(Ocr):
|
class OcrItemName(Ocr):
|
||||||
def after_process(self, result):
|
def after_process(self, result):
|
||||||
result = result.replace('念火之心', '忿火之心')
|
result = result.replace('念火之心', '忿火之心')
|
||||||
|
result = re.sub('^火之心', '忿火之心', result)
|
||||||
result = re.sub('工造机$', '工造机杼', result)
|
result = re.sub('工造机$', '工造机杼', result)
|
||||||
result = re.sub('工造迥?轮', '工造迴轮', result)
|
result = re.sub('工造迥?轮', '工造迴轮', result)
|
||||||
result = re.sub('月狂[療撩]?牙', '月狂獠牙', result)
|
result = re.sub('月狂[療撩]?牙', '月狂獠牙', result)
|
||||||
|
Loading…
Reference in New Issue
Block a user