diff --git a/ArknightsGameResource b/ArknightsGameResource index c291ad583..9c5c1d797 160000 --- a/ArknightsGameResource +++ b/ArknightsGameResource @@ -1 +1 @@ -Subproject commit c291ad583050f9c9c609167957ecfb451af5a488 +Subproject commit 9c5c1d79716b05715cf70a5a2ff082496ba8a85a diff --git a/arknights_mower/__init__.py b/arknights_mower/__init__.py index 8d0924696..a695b0397 100644 --- a/arknights_mower/__init__.py +++ b/arknights_mower/__init__.py @@ -2,7 +2,7 @@ import sys from pathlib import Path -__version__ = "2024.11.1.1" +__version__ = "2025.2.1" if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): __rootdir__ = Path(sys._MEIPASS).joinpath("arknights_mower").resolve() diff --git a/arknights_mower/__main__.py b/arknights_mower/__main__.py index cb60b8424..154c88934 100644 --- a/arknights_mower/__main__.py +++ b/arknights_mower/__main__.py @@ -48,8 +48,8 @@ def initialize( resting_priority=config.plan.conf.resting_priority, ling_xi=config.plan.conf.ling_xi, workaholic=config.plan.conf.workaholic, - max_resting_count=config.plan.conf.max_resting_count, free_blacklist=conf.free_blacklist, + ope_resting_priority=config.plan.conf.ope_resting_priority, resting_threshold=conf.resting_threshold, refresh_trading_config=config.plan.conf.refresh_trading, refresh_drained=config.plan.conf.refresh_drained, @@ -90,8 +90,8 @@ def initialize( i["conf"]["resting_priority"], ling_xi=i["conf"]["ling_xi"], workaholic=i["conf"]["workaholic"], - max_resting_count=i["conf"]["max_resting_count"], free_blacklist=i["conf"]["free_blacklist"], + ope_resting_priority=i["conf"]["ope_resting_priority"], resting_threshold=conf.resting_threshold, refresh_trading_config=i["conf"]["refresh_trading"], refresh_drained=i["conf"]["refresh_drained"], @@ -107,6 +107,7 @@ def initialize( trigger=backup_trigger, task=backup_task, trigger_timing=backup_trigger_timing, + name=i.get("name"), ) ) plan["backup_plans"] = backup_plans diff --git a/arknights_mower/data/agent.json b/arknights_mower/data/agent.json index 17712c5d3..ea96886df 100644 --- a/arknights_mower/data/agent.json +++ b/arknights_mower/data/agent.json @@ -1 +1 @@ -["芬", "梅", "宴", "砾", "孑", "吽", "红", "空", "黑", "W", "夕", "林", "令", "阿", "黍", "年", "山", "陈", "锏", "煌", "夜刀", "黑角", "杜林", "香草", "翎羽", "卡缇", "斑点", "炎熔", "芙蓉", "梓兰", "夜烟", "远山", "卡达", "深靛", "布丁", "流星", "红云", "白雪", "松果", "酸糖", "铅踝", "跃跃", "讯使", "红豆", "豆苗", "杜宾", "缠丸", "霜叶", "慕斯", "刻刀", "芳汀", "石英", "暗索", "末药", "清流", "褐果", "角峰", "泡泡", "露托", "古米", "坚雷", "地灵", "伊桑", "阿消", "维荻", "云迹", "微风", "瑰盐", "凛冬", "贾维", "青枳", "红隼", "苇草", "野鬃", "极境", "万顷", "夜半", "渡桥", "晓歌", "谜图", "鞭刃", "苍苔", "医生", "炎客", "摩根", "燧石", "断崖", "烈夏", "铎铃", "柏喙", "战车", "星极", "铸铁", "赤冬", "海沫", "奥达", "蓝毒", "白金", "灰喉", "四月", "隐现", "陨星", "慑砂", "截云", "苦艾", "雪绒", "天火", "惊蛰", "星源", "蜜蜡", "寒檀", "薄绿", "和弦", "蚀清", "耶拉", "洛洛", "至简", "折光", "温米", "梅尔", "稀音", "衡沙", "赫默", "亚叶", "锡兰", "絮雨", "图耶", "桑葚", "蜜莓", "刺玫", "明椒", "莎草", "临光", "深律", "森西", "槐琥", "乌有", "裁度", "雷蛇", "深巡", "可颂", "拜松", "火神", "石棉", "暮落", "闪击", "暴雨", "灰毫", "火哨", "极光", "洋灰", "菲莱", "玫拉", "子月", "熔泉", "冰酿", "空构", "崖心", "雪雉", "杏仁", "初雪", "巫恋", "海霓", "真理", "但书", "小满", "掠风", "海蒂", "月禾", "夏栎", "凛视", "波卜", "狮蝎", "绮良", "罗宾", "霜华", "贝娜", "风丸", "双月", "锡人", "空弦", "灰烬", "鸿雪", "远牙", "早露", "提丰", "莱伊", "风笛", "嵯峨", "忍冬", "琴柳", "焰尾", "伺夜", "异客", "澄闪", "黑键", "妮芙", "灵知", "铃兰", "魔王", "白铁", "塑心", "傀影", "老鲤", "温蒂", "水月", "艾拉", "闪灵", "夜莺", "流明", "星熊", "瑕光", "泥岩", "斥罪", "森蚺", "号角", "重岳", "银灰", "棘刺", "仇白", "佩佩", "左乐", "止颂", "暴行", "空爆", "猎蜂", "杰克", "夜魔", "巡林者", "12F", "玫兰莎", "泡普卡", "米格鲁", "克洛丝", "安赛尔", "格雷伊", "杰西卡", "安比尔", "清道夫", "桃金娘", "断罪者", "罗小黑", "休谟斯", "嘉维尔", "苏苏洛", "调香师", "蛇屠箱", "深海色", "波登可", "白面鸮", "诗怀雅", "芙兰卡", "莱欧斯", "因陀罗", "达格达", "幽灵鲨", "布洛卡", "导火索", "羽毛笔", "龙舌兰", "送葬人", "奥斯塔", "阿米娅", "特米米", "爱丽丝", "戴菲恩", "阿罗玛", "特克诺", "华法琳", "哈洛德", "卡夫卡", "车尔尼", "守林人", "安哲拉", "埃拉托", "凯瑟琳", "九色鹿", "食铁兽", "见行者", "能天使", "迷迭香", "伊内丝", "刻俄柏", "逻各斯", "麦哲伦", "弑君者", "多萝西", "凯尔希", "塞雷娅", "赫德雷", "艾丽妮", "赫拉格", "帕拉斯", "玛恩纳", "月见夜", "格拉尼", "斯卡蒂", "安德切尔", "史都华德", "艾丝黛尔", "罗比菈塔", "德克萨斯", "齐尔查克", "拉普兰德", "莱恩哈特", "炎狱炎熔", "濯尘芙蓉", "普罗旺斯", "格劳克斯", "菲亚梅塔", "维什戴尔", "娜仁图亚", "推进之王", "缪尔赛思", "伊芙利特", "莫斯提马", "艾雅法拉", "霍尔海雅", "玛露西尔", "卡涅利安", "安洁莉娜", "淬羽赫默", "歌蕾蒂娅", "阿斯卡纶", "引星棘刺", "焰影苇草", "乌尔比安", "史尔特尔", "薇薇安娜", "正义骑士号", "历阵锐枪芬", "火龙S黑角", "寒芒克洛丝", "承曦格雷伊", "假日威龙陈", "浊心斯卡蒂", "麒麟R夜刀", "琳琅诗怀雅", "归溟幽灵鲨", "涤火杰西卡", "百炼嘉维尔", "耀骑士临光", "圣约送葬人", "荒芜拉普兰德", "缄默德克萨斯", "纯烬艾雅法拉", "THRM-EX", "泰拉大陆调查团", "维娜·维多利亚", "Lancet-2", "Castle-3", "PhonoR-0", "Friston-3", "U-Official"] \ No newline at end of file +["芬", "梅", "宴", "砾", "孑", "吽", "红", "空", "黑", "W", "夕", "林", "令", "阿", "黍", "年", "余", "山", "陈", "锏", "煌", "夜刀", "黑角", "杜林", "香草", "翎羽", "卡缇", "斑点", "炎熔", "芙蓉", "梓兰", "夜烟", "远山", "卡达", "深靛", "布丁", "流星", "红云", "白雪", "松果", "酸糖", "铅踝", "跃跃", "讯使", "红豆", "豆苗", "杜宾", "缠丸", "霜叶", "慕斯", "刻刀", "芳汀", "石英", "暗索", "末药", "清流", "褐果", "角峰", "泡泡", "露托", "古米", "坚雷", "地灵", "伊桑", "阿消", "维荻", "云迹", "微风", "瑰盐", "凛冬", "贾维", "青枳", "红隼", "苇草", "野鬃", "极境", "万顷", "夜半", "渡桥", "晓歌", "谜图", "寻澜", "鞭刃", "苍苔", "医生", "炎客", "摩根", "燧石", "断崖", "烈夏", "铎铃", "柏喙", "战车", "星极", "铸铁", "赤冬", "海沫", "奥达", "蓝毒", "白金", "灰喉", "四月", "隐现", "陨星", "慑砂", "截云", "苦艾", "雪绒", "天火", "惊蛰", "星源", "蜜蜡", "寒檀", "薄绿", "和弦", "蚀清", "耶拉", "洛洛", "至简", "折光", "温米", "梅尔", "稀音", "衡沙", "赫默", "亚叶", "锡兰", "絮雨", "图耶", "桑葚", "蜜莓", "刺玫", "明椒", "莎草", "临光", "深律", "森西", "槐琥", "乌有", "裁度", "雷蛇", "深巡", "可颂", "拜松", "火神", "石棉", "暮落", "闪击", "暴雨", "灰毫", "火哨", "极光", "洋灰", "菲莱", "玫拉", "子月", "熔泉", "冰酿", "空构", "崖心", "雪雉", "杏仁", "初雪", "巫恋", "海霓", "真理", "但书", "小满", "掠风", "海蒂", "月禾", "夏栎", "行箸", "凛视", "波卜", "狮蝎", "绮良", "罗宾", "霜华", "贝娜", "风丸", "双月", "锡人", "空弦", "灰烬", "鸿雪", "远牙", "早露", "提丰", "莱伊", "风笛", "嵯峨", "忍冬", "琴柳", "焰尾", "伺夜", "异客", "澄闪", "黑键", "妮芙", "烛煌", "灵知", "铃兰", "魔王", "白铁", "塑心", "傀影", "老鲤", "温蒂", "水月", "艾拉", "闪灵", "夜莺", "流明", "星熊", "瑕光", "泥岩", "斥罪", "森蚺", "号角", "重岳", "银灰", "棘刺", "仇白", "佩佩", "左乐", "止颂", "暴行", "空爆", "猎蜂", "杰克", "夜魔", "巡林者", "12F", "玫兰莎", "泡普卡", "米格鲁", "克洛丝", "安赛尔", "格雷伊", "杰西卡", "安比尔", "清道夫", "桃金娘", "断罪者", "罗小黑", "休谟斯", "嘉维尔", "苏苏洛", "调香师", "蛇屠箱", "深海色", "波登可", "白面鸮", "诗怀雅", "芙兰卡", "莱欧斯", "因陀罗", "达格达", "幽灵鲨", "布洛卡", "导火索", "羽毛笔", "龙舌兰", "送葬人", "奥斯塔", "阿米娅", "特米米", "爱丽丝", "戴菲恩", "阿罗玛", "特克诺", "华法琳", "哈洛德", "卡夫卡", "车尔尼", "守林人", "安哲拉", "埃拉托", "凯瑟琳", "九色鹿", "食铁兽", "见行者", "能天使", "迷迭香", "伊内丝", "刻俄柏", "逻各斯", "麦哲伦", "弑君者", "多萝西", "凯尔希", "塞雷娅", "赫德雷", "艾丽妮", "赫拉格", "帕拉斯", "玛恩纳", "月见夜", "格拉尼", "斯卡蒂", "安德切尔", "史都华德", "艾丝黛尔", "罗比菈塔", "德克萨斯", "齐尔查克", "拉普兰德", "莱恩哈特", "炎狱炎熔", "濯尘芙蓉", "普罗旺斯", "格劳克斯", "菲亚梅塔", "维什戴尔", "娜仁图亚", "推进之王", "缪尔赛思", "伊芙利特", "莫斯提马", "艾雅法拉", "霍尔海雅", "玛露西尔", "卡涅利安", "安洁莉娜", "淬羽赫默", "歌蕾蒂娅", "阿斯卡纶", "引星棘刺", "焰影苇草", "乌尔比安", "史尔特尔", "薇薇安娜", "正义骑士号", "历阵锐枪芬", "火龙S黑角", "寒芒克洛丝", "承曦格雷伊", "假日威龙陈", "浊心斯卡蒂", "麒麟R夜刀", "琳琅诗怀雅", "归溟幽灵鲨", "涤火杰西卡", "百炼嘉维尔", "耀骑士临光", "圣约送葬人", "荒芜拉普兰德", "缄默德克萨斯", "纯烬艾雅法拉", "THRM-EX", "泰拉大陆调查团", "维娜·维多利亚", "Lancet-2", "Castle-3", "PhonoR-0", "Friston-3", "U-Official"] \ No newline at end of file diff --git a/arknights_mower/data/agent_profession.json b/arknights_mower/data/agent_profession.json index 8a346832a..a07e41153 100644 --- a/arknights_mower/data/agent_profession.json +++ b/arknights_mower/data/agent_profession.json @@ -1 +1 @@ -{"Lancet-2": "MEDIC", "Castle-3": "WARRIOR", "THRM-EX": "SPECIAL", "正义骑士号": "SNIPER", "泰拉大陆调查团": "SNIPER", "U-Official": "SUPPORT", "Friston-3": "TANK", "PhonoR-0": "SUPPORT", "夜刀": "PIONEER", "黑角": "TANK", "巡林者": "SNIPER", "杜林": "CASTER", "12F": "CASTER", "芬": "PIONEER", "香草": "PIONEER", "翎羽": "PIONEER", "玫兰莎": "WARRIOR", "泡普卡": "WARRIOR", "卡缇": "TANK", "米格鲁": "TANK", "斑点": "TANK", "克洛丝": "SNIPER", "安德切尔": "SNIPER", "炎熔": "CASTER", "芙蓉": "MEDIC", "安赛尔": "MEDIC", "史都华德": "CASTER", "梓兰": "SUPPORT", "夜烟": "CASTER", "远山": "CASTER", "格雷伊": "CASTER", "卡达": "CASTER", "深靛": "CASTER", "布丁": "CASTER", "杰西卡": "SNIPER", "流星": "SNIPER", "红云": "SNIPER", "梅": "SNIPER", "白雪": "SNIPER", "松果": "SNIPER", "安比尔": "SNIPER", "酸糖": "SNIPER", "铅踝": "SNIPER", "跃跃": "SNIPER", "讯使": "PIONEER", "清道夫": "PIONEER", "红豆": "PIONEER", "桃金娘": "PIONEER", "豆苗": "PIONEER", "杜宾": "WARRIOR", "缠丸": "WARRIOR", "断罪者": "WARRIOR", "霜叶": "WARRIOR", "艾丝黛尔": "WARRIOR", "慕斯": "WARRIOR", "刻刀": "WARRIOR", "宴": "WARRIOR", "芳汀": "WARRIOR", "罗小黑": "WARRIOR", "石英": "WARRIOR", "休谟斯": "WARRIOR", "砾": "SPECIAL", "孑": "SPECIAL", "暗索": "SPECIAL", "末药": "MEDIC", "嘉维尔": "MEDIC", "苏苏洛": "MEDIC", "调香师": "MEDIC", "清流": "MEDIC", "褐果": "MEDIC", "角峰": "TANK", "蛇屠箱": "TANK", "泡泡": "TANK", "露托": "TANK", "古米": "TANK", "坚雷": "TANK", "深海色": "SUPPORT", "地灵": "SUPPORT", "波登可": "SUPPORT", "罗比菈塔": "SUPPORT", "伊桑": "SPECIAL", "阿消": "SPECIAL", "维荻": "SPECIAL", "云迹": "SPECIAL", "白面鸮": "MEDIC", "微风": "MEDIC", "瑰盐": "MEDIC", "凛冬": "PIONEER", "德克萨斯": "PIONEER", "贾维": "PIONEER", "青枳": "PIONEER", "红隼": "PIONEER", "苇草": "PIONEER", "野鬃": "PIONEER", "历阵锐枪芬": "PIONEER", "极境": "PIONEER", "万顷": "PIONEER", "夜半": "PIONEER", "渡桥": "PIONEER", "晓歌": "PIONEER", "谜图": "PIONEER", "齐尔查克": "PIONEER", "诗怀雅": "WARRIOR", "鞭刃": "WARRIOR", "苍苔": "WARRIOR", "医生": "WARRIOR", "芙兰卡": "WARRIOR", "炎客": "WARRIOR", "摩根": "WARRIOR", "莱欧斯": "WARRIOR", "因陀罗": "WARRIOR", "燧石": "WARRIOR", "达格达": "WARRIOR", "拉普兰德": "WARRIOR", "断崖": "WARRIOR", "烈夏": "WARRIOR", "铎铃": "WARRIOR", "柏喙": "WARRIOR", "战车": "WARRIOR", "幽灵鲨": "WARRIOR", "布洛卡": "WARRIOR", "导火索": "WARRIOR", "星极": "WARRIOR", "铸铁": "WARRIOR", "赤冬": "WARRIOR", "火龙S黑角": "WARRIOR", "羽毛笔": "WARRIOR", "海沫": "WARRIOR", "龙舌兰": "WARRIOR", "奥达": "WARRIOR", "蓝毒": "SNIPER", "白金": "SNIPER", "灰喉": "SNIPER", "四月": "SNIPER", "寒芒克洛丝": "SNIPER", "隐现": "SNIPER", "陨星": "SNIPER", "慑砂": "SNIPER", "截云": "SNIPER", "送葬人": "SNIPER", "奥斯塔": "SNIPER", "阿米娅": "CASTER", "苦艾": "CASTER", "特米米": "CASTER", "雪绒": "CASTER", "天火": "CASTER", "惊蛰": "CASTER", "星源": "CASTER", "蜜蜡": "CASTER", "莱恩哈特": "CASTER", "寒檀": "CASTER", "薄绿": "CASTER", "爱丽丝": "CASTER", "和弦": "CASTER", "戴菲恩": "CASTER", "炎狱炎熔": "CASTER", "蚀清": "CASTER", "阿罗玛": "CASTER", "耶拉": "CASTER", "洛洛": "CASTER", "至简": "CASTER", "折光": "CASTER", "温米": "CASTER", "特克诺": "CASTER", "梅尔": "SUPPORT", "稀音": "SUPPORT", "衡沙": "SUPPORT", "赫默": "MEDIC", "华法琳": "MEDIC", "亚叶": "MEDIC", "锡兰": "MEDIC", "絮雨": "MEDIC", "图耶": "MEDIC", "桑葚": "MEDIC", "蜜莓": "MEDIC", "哈洛德": "MEDIC", "濯尘芙蓉": "MEDIC", "刺玫": "MEDIC", "明椒": "MEDIC", "莎草": "MEDIC", "临光": "TANK", "吽": "TANK", "深律": "TANK", "森西": "TANK", "红": "SPECIAL", "槐琥": "SPECIAL", "卡夫卡": "SPECIAL", "乌有": "SPECIAL", "裁度": "SPECIAL", "雷蛇": "TANK", "深巡": "TANK", "可颂": "TANK", "拜松": "TANK", "火神": "TANK", "石棉": "TANK", "暮落": "TANK", "车尔尼": "TANK", "闪击": "TANK", "暴雨": "TANK", "灰毫": "TANK", "火哨": "TANK", "极光": "TANK", "洋灰": "TANK", "菲莱": "TANK", "普罗旺斯": "SNIPER", "玫拉": "SNIPER", "守林人": "SNIPER", "安哲拉": "SNIPER", "子月": "SNIPER", "熔泉": "SNIPER", "埃拉托": "SNIPER", "承曦格雷伊": "SNIPER", "冰酿": "SNIPER", "空构": "SPECIAL", "崖心": "SPECIAL", "雪雉": "SPECIAL", "杏仁": "SPECIAL", "初雪": "SUPPORT", "巫恋": "SUPPORT", "海霓": "SUPPORT", "真理": "SUPPORT", "格劳克斯": "SUPPORT", "但书": "SUPPORT", "小满": "SUPPORT", "掠风": "SUPPORT", "凯瑟琳": "SUPPORT", "空": "SUPPORT", "海蒂": "SUPPORT", "月禾": "SUPPORT", "九色鹿": "SUPPORT", "夏栎": "SUPPORT", "凛视": "SUPPORT", "波卜": "SUPPORT", "狮蝎": "SPECIAL", "绮良": "SPECIAL", "食铁兽": "SPECIAL", "见行者": "SPECIAL", "罗宾": "SPECIAL", "霜华": "SPECIAL", "贝娜": "SPECIAL", "风丸": "SPECIAL", "双月": "SPECIAL", "锡人": "SPECIAL", "能天使": "SNIPER", "空弦": "SNIPER", "灰烬": "SNIPER", "黑": "SNIPER", "鸿雪": "SNIPER", "远牙": "SNIPER", "W": "SNIPER", "菲亚梅塔": "SNIPER", "早露": "SNIPER", "提丰": "SNIPER", "迷迭香": "SNIPER", "维什戴尔": "SNIPER", "假日威龙陈": "SNIPER", "莱伊": "SNIPER", "娜仁图亚": "SNIPER", "推进之王": "PIONEER", "风笛": "PIONEER", "嵯峨": "PIONEER", "忍冬": "PIONEER", "琴柳": "PIONEER", "焰尾": "PIONEER", "伺夜": "PIONEER", "缪尔赛思": "PIONEER", "伊内丝": "PIONEER", "伊芙利特": "CASTER", "莫斯提马": "CASTER", "艾雅法拉": "CASTER", "刻俄柏": "CASTER", "霍尔海雅": "CASTER", "逻各斯": "CASTER", "夕": "CASTER", "玛露西尔": "CASTER", "异客": "CASTER", "卡涅利安": "CASTER", "林": "CASTER", "澄闪": "CASTER", "荒芜拉普兰德": "CASTER", "黑键": "CASTER", "妮芙": "CASTER", "灵知": "SUPPORT", "安洁莉娜": "SUPPORT", "铃兰": "SUPPORT", "麦哲伦": "SUPPORT", "浊心斯卡蒂": "SUPPORT", "魔王": "SUPPORT", "淬羽赫默": "SUPPORT", "令": "SUPPORT", "白铁": "SUPPORT", "塑心": "SUPPORT", "傀影": "SPECIAL", "缄默德克萨斯": "SPECIAL", "麒麟R夜刀": "SPECIAL", "弑君者": "SPECIAL", "老鲤": "SPECIAL", "琳琅诗怀雅": "SPECIAL", "温蒂": "SPECIAL", "阿": "SPECIAL", "歌蕾蒂娅": "SPECIAL", "水月": "SPECIAL", "阿斯卡纶": "SPECIAL", "归溟幽灵鲨": "SPECIAL", "多萝西": "SPECIAL", "艾拉": "SPECIAL", "引星棘刺": "SPECIAL", "闪灵": "MEDIC", "夜莺": "MEDIC", "凯尔希": "MEDIC", "流明": "MEDIC", "纯烬艾雅法拉": "MEDIC", "焰影苇草": "MEDIC", "星熊": "TANK", "塞雷娅": "TANK", "瑕光": "TANK", "黍": "TANK", "年": "TANK", "泥岩": "TANK", "斥罪": "TANK", "森蚺": "TANK", "号角": "TANK", "涤火杰西卡": "TANK", "山": "WARRIOR", "重岳": "WARRIOR", "银灰": "WARRIOR", "棘刺": "WARRIOR", "仇白": "WARRIOR", "赫德雷": "WARRIOR", "乌尔比安": "WARRIOR", "佩佩": "WARRIOR", "陈": "WARRIOR", "艾丽妮": "WARRIOR", "锏": "WARRIOR", "煌": "WARRIOR", "百炼嘉维尔": "WARRIOR", "史尔特尔": "WARRIOR", "薇薇安娜": "WARRIOR", "维娜·维多利亚": "WARRIOR", "赫拉格": "WARRIOR", "左乐": "WARRIOR", "帕拉斯": "WARRIOR", "耀骑士临光": "WARRIOR", "止颂": "WARRIOR", "圣约送葬人": "WARRIOR", "玛恩纳": "WARRIOR", "暴行": "WARRIOR", "空爆": "SNIPER", "月见夜": "WARRIOR", "猎蜂": "WARRIOR", "杰克": "WARRIOR", "夜魔": "CASTER", "格拉尼": "PIONEER", "斯卡蒂": "WARRIOR"} \ No newline at end of file +{"Lancet-2": "MEDIC", "Castle-3": "WARRIOR", "THRM-EX": "SPECIAL", "正义骑士号": "SNIPER", "泰拉大陆调查团": "SNIPER", "U-Official": "SUPPORT", "Friston-3": "TANK", "PhonoR-0": "SUPPORT", "夜刀": "PIONEER", "黑角": "TANK", "巡林者": "SNIPER", "杜林": "CASTER", "12F": "CASTER", "芬": "PIONEER", "香草": "PIONEER", "翎羽": "PIONEER", "玫兰莎": "WARRIOR", "泡普卡": "WARRIOR", "卡缇": "TANK", "米格鲁": "TANK", "斑点": "TANK", "克洛丝": "SNIPER", "安德切尔": "SNIPER", "炎熔": "CASTER", "芙蓉": "MEDIC", "安赛尔": "MEDIC", "史都华德": "CASTER", "梓兰": "SUPPORT", "夜烟": "CASTER", "远山": "CASTER", "格雷伊": "CASTER", "卡达": "CASTER", "深靛": "CASTER", "布丁": "CASTER", "杰西卡": "SNIPER", "流星": "SNIPER", "红云": "SNIPER", "梅": "SNIPER", "白雪": "SNIPER", "松果": "SNIPER", "安比尔": "SNIPER", "酸糖": "SNIPER", "铅踝": "SNIPER", "跃跃": "SNIPER", "讯使": "PIONEER", "清道夫": "PIONEER", "红豆": "PIONEER", "桃金娘": "PIONEER", "豆苗": "PIONEER", "杜宾": "WARRIOR", "缠丸": "WARRIOR", "断罪者": "WARRIOR", "霜叶": "WARRIOR", "艾丝黛尔": "WARRIOR", "慕斯": "WARRIOR", "刻刀": "WARRIOR", "宴": "WARRIOR", "芳汀": "WARRIOR", "罗小黑": "WARRIOR", "石英": "WARRIOR", "休谟斯": "WARRIOR", "砾": "SPECIAL", "孑": "SPECIAL", "暗索": "SPECIAL", "末药": "MEDIC", "嘉维尔": "MEDIC", "苏苏洛": "MEDIC", "调香师": "MEDIC", "清流": "MEDIC", "褐果": "MEDIC", "角峰": "TANK", "蛇屠箱": "TANK", "泡泡": "TANK", "露托": "TANK", "古米": "TANK", "坚雷": "TANK", "深海色": "SUPPORT", "地灵": "SUPPORT", "波登可": "SUPPORT", "罗比菈塔": "SUPPORT", "伊桑": "SPECIAL", "阿消": "SPECIAL", "维荻": "SPECIAL", "云迹": "SPECIAL", "白面鸮": "MEDIC", "微风": "MEDIC", "瑰盐": "MEDIC", "凛冬": "PIONEER", "德克萨斯": "PIONEER", "贾维": "PIONEER", "青枳": "PIONEER", "红隼": "PIONEER", "苇草": "PIONEER", "野鬃": "PIONEER", "历阵锐枪芬": "PIONEER", "极境": "PIONEER", "万顷": "PIONEER", "夜半": "PIONEER", "渡桥": "PIONEER", "晓歌": "PIONEER", "谜图": "PIONEER", "齐尔查克": "PIONEER", "寻澜": "PIONEER", "诗怀雅": "WARRIOR", "鞭刃": "WARRIOR", "苍苔": "WARRIOR", "医生": "WARRIOR", "芙兰卡": "WARRIOR", "炎客": "WARRIOR", "摩根": "WARRIOR", "莱欧斯": "WARRIOR", "因陀罗": "WARRIOR", "燧石": "WARRIOR", "达格达": "WARRIOR", "拉普兰德": "WARRIOR", "断崖": "WARRIOR", "烈夏": "WARRIOR", "铎铃": "WARRIOR", "柏喙": "WARRIOR", "战车": "WARRIOR", "幽灵鲨": "WARRIOR", "布洛卡": "WARRIOR", "导火索": "WARRIOR", "星极": "WARRIOR", "铸铁": "WARRIOR", "赤冬": "WARRIOR", "火龙S黑角": "WARRIOR", "羽毛笔": "WARRIOR", "海沫": "WARRIOR", "龙舌兰": "WARRIOR", "奥达": "WARRIOR", "蓝毒": "SNIPER", "白金": "SNIPER", "灰喉": "SNIPER", "四月": "SNIPER", "寒芒克洛丝": "SNIPER", "隐现": "SNIPER", "陨星": "SNIPER", "慑砂": "SNIPER", "截云": "SNIPER", "送葬人": "SNIPER", "奥斯塔": "SNIPER", "阿米娅": "CASTER", "苦艾": "CASTER", "特米米": "CASTER", "雪绒": "CASTER", "天火": "CASTER", "惊蛰": "CASTER", "星源": "CASTER", "蜜蜡": "CASTER", "莱恩哈特": "CASTER", "寒檀": "CASTER", "薄绿": "CASTER", "爱丽丝": "CASTER", "和弦": "CASTER", "戴菲恩": "CASTER", "炎狱炎熔": "CASTER", "蚀清": "CASTER", "阿罗玛": "CASTER", "耶拉": "CASTER", "洛洛": "CASTER", "至简": "CASTER", "折光": "CASTER", "温米": "CASTER", "特克诺": "CASTER", "梅尔": "SUPPORT", "稀音": "SUPPORT", "衡沙": "SUPPORT", "赫默": "MEDIC", "华法琳": "MEDIC", "亚叶": "MEDIC", "锡兰": "MEDIC", "絮雨": "MEDIC", "图耶": "MEDIC", "桑葚": "MEDIC", "蜜莓": "MEDIC", "哈洛德": "MEDIC", "濯尘芙蓉": "MEDIC", "刺玫": "MEDIC", "明椒": "MEDIC", "莎草": "MEDIC", "临光": "TANK", "吽": "TANK", "深律": "TANK", "森西": "TANK", "红": "SPECIAL", "槐琥": "SPECIAL", "卡夫卡": "SPECIAL", "乌有": "SPECIAL", "裁度": "SPECIAL", "雷蛇": "TANK", "深巡": "TANK", "可颂": "TANK", "拜松": "TANK", "火神": "TANK", "石棉": "TANK", "暮落": "TANK", "车尔尼": "TANK", "闪击": "TANK", "暴雨": "TANK", "灰毫": "TANK", "火哨": "TANK", "极光": "TANK", "洋灰": "TANK", "菲莱": "TANK", "普罗旺斯": "SNIPER", "玫拉": "SNIPER", "守林人": "SNIPER", "安哲拉": "SNIPER", "子月": "SNIPER", "熔泉": "SNIPER", "埃拉托": "SNIPER", "承曦格雷伊": "SNIPER", "冰酿": "SNIPER", "空构": "SPECIAL", "崖心": "SPECIAL", "雪雉": "SPECIAL", "杏仁": "SPECIAL", "初雪": "SUPPORT", "巫恋": "SUPPORT", "海霓": "SUPPORT", "真理": "SUPPORT", "格劳克斯": "SUPPORT", "但书": "SUPPORT", "小满": "SUPPORT", "掠风": "SUPPORT", "凯瑟琳": "SUPPORT", "空": "SUPPORT", "海蒂": "SUPPORT", "月禾": "SUPPORT", "九色鹿": "SUPPORT", "夏栎": "SUPPORT", "行箸": "SUPPORT", "凛视": "SUPPORT", "波卜": "SUPPORT", "狮蝎": "SPECIAL", "绮良": "SPECIAL", "食铁兽": "SPECIAL", "见行者": "SPECIAL", "罗宾": "SPECIAL", "霜华": "SPECIAL", "贝娜": "SPECIAL", "风丸": "SPECIAL", "双月": "SPECIAL", "锡人": "SPECIAL", "能天使": "SNIPER", "空弦": "SNIPER", "灰烬": "SNIPER", "黑": "SNIPER", "鸿雪": "SNIPER", "远牙": "SNIPER", "W": "SNIPER", "菲亚梅塔": "SNIPER", "早露": "SNIPER", "提丰": "SNIPER", "迷迭香": "SNIPER", "维什戴尔": "SNIPER", "假日威龙陈": "SNIPER", "莱伊": "SNIPER", "娜仁图亚": "SNIPER", "推进之王": "PIONEER", "风笛": "PIONEER", "嵯峨": "PIONEER", "忍冬": "PIONEER", "琴柳": "PIONEER", "焰尾": "PIONEER", "伺夜": "PIONEER", "缪尔赛思": "PIONEER", "伊内丝": "PIONEER", "伊芙利特": "CASTER", "莫斯提马": "CASTER", "艾雅法拉": "CASTER", "刻俄柏": "CASTER", "霍尔海雅": "CASTER", "逻各斯": "CASTER", "夕": "CASTER", "玛露西尔": "CASTER", "异客": "CASTER", "卡涅利安": "CASTER", "林": "CASTER", "澄闪": "CASTER", "荒芜拉普兰德": "CASTER", "黑键": "CASTER", "妮芙": "CASTER", "烛煌": "CASTER", "灵知": "SUPPORT", "安洁莉娜": "SUPPORT", "铃兰": "SUPPORT", "麦哲伦": "SUPPORT", "浊心斯卡蒂": "SUPPORT", "魔王": "SUPPORT", "淬羽赫默": "SUPPORT", "令": "SUPPORT", "白铁": "SUPPORT", "塑心": "SUPPORT", "傀影": "SPECIAL", "缄默德克萨斯": "SPECIAL", "麒麟R夜刀": "SPECIAL", "弑君者": "SPECIAL", "老鲤": "SPECIAL", "琳琅诗怀雅": "SPECIAL", "温蒂": "SPECIAL", "阿": "SPECIAL", "歌蕾蒂娅": "SPECIAL", "水月": "SPECIAL", "阿斯卡纶": "SPECIAL", "归溟幽灵鲨": "SPECIAL", "多萝西": "SPECIAL", "艾拉": "SPECIAL", "引星棘刺": "SPECIAL", "闪灵": "MEDIC", "夜莺": "MEDIC", "凯尔希": "MEDIC", "流明": "MEDIC", "纯烬艾雅法拉": "MEDIC", "焰影苇草": "MEDIC", "星熊": "TANK", "塞雷娅": "TANK", "瑕光": "TANK", "黍": "TANK", "年": "TANK", "泥岩": "TANK", "斥罪": "TANK", "森蚺": "TANK", "号角": "TANK", "涤火杰西卡": "TANK", "余": "TANK", "山": "WARRIOR", "重岳": "WARRIOR", "银灰": "WARRIOR", "棘刺": "WARRIOR", "仇白": "WARRIOR", "赫德雷": "WARRIOR", "乌尔比安": "WARRIOR", "佩佩": "WARRIOR", "陈": "WARRIOR", "艾丽妮": "WARRIOR", "锏": "WARRIOR", "煌": "WARRIOR", "百炼嘉维尔": "WARRIOR", "史尔特尔": "WARRIOR", "薇薇安娜": "WARRIOR", "维娜·维多利亚": "WARRIOR", "赫拉格": "WARRIOR", "左乐": "WARRIOR", "帕拉斯": "WARRIOR", "耀骑士临光": "WARRIOR", "止颂": "WARRIOR", "圣约送葬人": "WARRIOR", "玛恩纳": "WARRIOR", "暴行": "WARRIOR", "空爆": "SNIPER", "月见夜": "WARRIOR", "猎蜂": "WARRIOR", "杰克": "WARRIOR", "夜魔": "CASTER", "格拉尼": "PIONEER", "斯卡蒂": "WARRIOR"} \ No newline at end of file diff --git a/arknights_mower/data/base.json b/arknights_mower/data/base.json index ff83f2616..8bb90eef9 100644 --- a/arknights_mower/data/base.json +++ b/arknights_mower/data/base.json @@ -1,20 +1,23 @@ [ - "central", - "meeting", - "room_1_1", - "room_1_2", - "room_1_3", - "dormitory_1", - "factory", - "room_2_1", - "room_2_2", - "room_2_3", - "dormitory_2", - "contact", - "room_3_1", - "room_3_2", - "room_3_3", - "dormitory_3", - "train", - "dormitory_4" -] \ No newline at end of file + "central", + "meeting", + "room_1_1", + "room_1_2", + "room_1_3", + "dormitory_1", + "factory", + "room_2_1", + "room_2_2", + "room_2_3", + "dormitory_2", + "contact", + "room_3_1", + "room_3_2", + "room_3_3", + "dormitory_3", + "train", + "dormitory_4", + "gaming_1", + "gaming_2", + "gaming_3" +] diff --git a/arknights_mower/data/key_mapping.json b/arknights_mower/data/key_mapping.json index a88d231ce..26b3b63df 100644 --- a/arknights_mower/data/key_mapping.json +++ b/arknights_mower/data/key_mapping.json @@ -111,6 +111,20 @@ "NORMAL", 10008 ], + "LMTGS_COIN_5801": [ + "LMTGS_COIN_5801", + "LMTGS_COIN_5801", + "寻访数据契约", + "NORMAL", + 10026 + ], + "寻访数据契约": [ + "LMTGS_COIN_5801", + "LMTGS_COIN_5801", + "寻访数据契约", + "NORMAL", + 10026 + ], "EPGS_COIN": [ "EPGS_COIN", "EPGS_COIN", @@ -391,6 +405,20 @@ "NORMAL", 40001 ], + "LIMITED_TKT_GACHA_10_5801": [ + "LIMITED_TKT_GACHA_10_5801", + "LIMITED_TKT_GACHA_10_5801", + "欢宴良宵寻访凭证", + "NORMAL", + 40003 + ], + "欢宴良宵寻访凭证": [ + "LIMITED_TKT_GACHA_10_5801", + "LIMITED_TKT_GACHA_10_5801", + "欢宴良宵寻访凭证", + "NORMAL", + 40003 + ], "7003": [ "7003", "TKT_GACHA", @@ -6754,6 +6782,34 @@ "MATERIAL", 700160 ], + "p_char_4052_surfer": [ + "p_char_4052_surfer", + "p_char_4052_surfer", + "寻澜的信物", + "MATERIAL", + 700161 + ], + "寻澜的信物": [ + "p_char_4052_surfer", + "p_char_4052_surfer", + "寻澜的信物", + "MATERIAL", + 700161 + ], + "p_char_4172_xingzh": [ + "p_char_4172_xingzh", + "p_char_4172_xingzh", + "行箸的信物", + "MATERIAL", + 700162 + ], + "行箸的信物": [ + "p_char_4172_xingzh", + "p_char_4172_xingzh", + "行箸的信物", + "MATERIAL", + 700162 + ], "p_char_103_angel": [ "p_char_103_angel", "p_char_103_angel", @@ -8196,6 +8252,34 @@ "MATERIAL", 600103 ], + "p_char_2026_yu": [ + "p_char_2026_yu", + "p_char_2026_yu", + "余的信物", + "MATERIAL", + 600104 + ], + "余的信物": [ + "p_char_2026_yu", + "p_char_2026_yu", + "余的信物", + "MATERIAL", + 600104 + ], + "p_char_1040_blaze2": [ + "p_char_1040_blaze2", + "p_char_1040_blaze2", + "烛煌的信物", + "MATERIAL", + 600105 + ], + "烛煌的信物": [ + "p_char_1040_blaze2", + "p_char_1040_blaze2", + "烛煌的信物", + "MATERIAL", + 600105 + ], "class_p_char_123_fang": [ "class_p_char_123_fang", "class_p_char_123_fang", diff --git a/arknights_mower/data/recruit.json b/arknights_mower/data/recruit.json index 850c45c25..98c0191e8 100644 --- a/arknights_mower/data/recruit.json +++ b/arknights_mower/data/recruit.json @@ -381,6 +381,16 @@ "先锋干员" ] }, + "char_452_bstalk": { + "name": "豆苗", + "stars": 4, + "tags": [ + "费用回复", + "召唤", + "远程位", + "先锋干员" + ] + }, "char_130_doberm": { "name": "杜宾", "stars": 4, @@ -846,6 +856,16 @@ "术师干员" ] }, + "char_338_iris": { + "name": "爱丽丝", + "stars": 5, + "tags": [ + "输出", + "资深干员", + "远程位", + "术师干员" + ] + }, "char_242_otter": { "name": "梅尔", "stars": 5, @@ -1116,6 +1136,16 @@ "狙击干员" ] }, + "char_332_archet": { + "name": "空弦", + "stars": 6, + "tags": [ + "输出", + "高级资深干员", + "远程位", + "狙击干员" + ] + }, "char_340_shwaz": { "name": "黑", "stars": 6, diff --git a/arknights_mower/data/recruit_result.json b/arknights_mower/data/recruit_result.json index 89286cc6c..d6a17d343 100644 --- a/arknights_mower/data/recruit_result.json +++ b/arknights_mower/data/recruit_result.json @@ -36,6 +36,7 @@ "char_356_broca", "char_279_excu", "char_346_aosta", + "char_338_iris", "char_171_bldsk", "char_214_kafka", "char_158_milu", @@ -68,6 +69,7 @@ "char_440_pinecn", "char_366_acdrop", "char_290_vigna", + "char_452_bstalk", "char_130_doberm", "char_289_gyuki", "char_193_frostl", @@ -112,6 +114,7 @@ "char_195_glassb", "char_343_tknogi", "char_215_mantic", + "char_332_archet", "char_197_poca", "char_222_bpipe", "char_358_lisa", diff --git a/arknights_mower/models/CONSUME.pkl b/arknights_mower/models/CONSUME.pkl index aeb1e4909..f21443f84 100644 Binary files a/arknights_mower/models/CONSUME.pkl and b/arknights_mower/models/CONSUME.pkl differ diff --git a/arknights_mower/models/NORMAL.pkl b/arknights_mower/models/NORMAL.pkl index 3adbab47c..9d51bd8d7 100644 Binary files a/arknights_mower/models/NORMAL.pkl and b/arknights_mower/models/NORMAL.pkl differ diff --git a/arknights_mower/models/avatar.pkl b/arknights_mower/models/avatar.pkl index 1266ec603..3d644fcef 100644 Binary files a/arknights_mower/models/avatar.pkl and b/arknights_mower/models/avatar.pkl differ diff --git a/arknights_mower/models/operator_room.model b/arknights_mower/models/operator_room.model index a23cdf0ec..3c2f6d3c4 100644 Binary files a/arknights_mower/models/operator_room.model and b/arknights_mower/models/operator_room.model differ diff --git a/arknights_mower/models/operator_select.model b/arknights_mower/models/operator_select.model index 2ada4b6ee..57a0e6222 100644 Binary files a/arknights_mower/models/operator_select.model and b/arknights_mower/models/operator_select.model differ diff --git a/arknights_mower/models/recruit.pkl b/arknights_mower/models/recruit.pkl index 06ea7e561..9ecea3dc1 100644 Binary files a/arknights_mower/models/recruit.pkl and b/arknights_mower/models/recruit.pkl differ diff --git a/arknights_mower/models/recruit_result.pkl b/arknights_mower/models/recruit_result.pkl index a80a13d2e..ef018cd87 100644 Binary files a/arknights_mower/models/recruit_result.pkl and b/arknights_mower/models/recruit_result.pkl differ diff --git a/arknights_mower/resources/arrange_check_in_small.png b/arknights_mower/resources/arrange_check_in_small.png new file mode 100644 index 000000000..a80b73be3 Binary files /dev/null and b/arknights_mower/resources/arrange_check_in_small.png differ diff --git a/arknights_mower/resources/arrange_confirm.png b/arknights_mower/resources/arrange_confirm.png index 26e550440..0e03ce467 100644 Binary files a/arknights_mower/resources/arrange_confirm.png and b/arknights_mower/resources/arrange_confirm.png differ diff --git a/arknights_mower/resources/clue/check_party.png b/arknights_mower/resources/clue/check_party.png new file mode 100644 index 000000000..e3197671c Binary files /dev/null and b/arknights_mower/resources/clue/check_party.png differ diff --git a/arknights_mower/resources/sss/confirm.png b/arknights_mower/resources/sss/confirm.png new file mode 100644 index 000000000..a9843a17e Binary files /dev/null and b/arknights_mower/resources/sss/confirm.png differ diff --git a/arknights_mower/resources/sss/next_step_button.png b/arknights_mower/resources/sss/next_step_button.png new file mode 100644 index 000000000..35d157b82 Binary files /dev/null and b/arknights_mower/resources/sss/next_step_button.png differ diff --git a/arknights_mower/solvers/base_mixin.py b/arknights_mower/solvers/base_mixin.py index f2438a1c9..263e0d9d8 100644 --- a/arknights_mower/solvers/base_mixin.py +++ b/arknights_mower/solvers/base_mixin.py @@ -33,18 +33,18 @@ class BaseMixin: def detect_arrange_order(self): name_list = ["工作状态", "技能", "心情", "信赖值"] - x_list = (1309, 1435, 1560, 1685) + x_list = (1135, 1262, 1363, 1490) y = 70 hsv = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV) mask = cv2.inRange(hsv, (95, 100, 100), (105, 255, 255)) for idx, x in enumerate(x_list): if np.count_nonzero(mask[y : y + 3, x : x + 5]): - return (name_list[idx], True) - if np.count_nonzero(mask[y + 10 : y + 13, x : x + 5]): return (name_list[idx], False) + if np.count_nonzero(mask[y + 10 : y + 13, x : x + 5]): + return (name_list[idx], True) def switch_arrange_order(self, name, ascending=False): - name_x = {"工作状态": 1309, "技能": 1439, "心情": 1565, "信赖值": 1690} + name_x = {"工作状态": 1135, "技能": 1264, "心情": 1368, "信赖值": 1495} if isinstance(name, int): name = list(name_x.keys())[name - 1] if isinstance(ascending, str): @@ -234,18 +234,59 @@ def detect_room(self) -> str: logger.debug("办公室B205") return room + def adjust_room(self, _room): + # 定义屏幕范围 + screen_min_x = 0 + screen_max_x = 1920 + + # 检查是否有点在屏幕范围内 + any_point_in_view = any(screen_min_x <= p[0] <= screen_max_x for p in _room) + + if any_point_in_view: + logger.debug( + f"At least one point of {_room} is within screen range [0, 1920]. No movement needed." + ) + for i in range(4): + _room[i, 0] = max(_room[i, 0], 0) + _room[i, 0] = min(_room[i, 0], self.recog.w) + _room[i, 1] = max(_room[i, 1], 0) + _room[i, 1] = min(_room[i, 1], self.recog.h) + return _room + + # 如果所有点都超出屏幕范围,则计算需要的移动距离 + min_x = min(p[0] for p in _room) + max_x = max(p[0] for p in _room) + + dx = 0 + start = (960, 540) + if min_x < screen_min_x: + # 左边超出,向右移动 + dx = screen_min_x - min_x + logger.debug(f"Moving right by {dx} to bring room into view.") + elif max_x > screen_max_x: + # 右边超出,向左移动 + dx = screen_max_x - max_x + logger.debug(f"Moving left by {-dx} to bring room into view.") + + # 如果需要移动,则移动视图 + if dx != 0: + movement = (dx, 0) # 仅水平移动 + self.swipe_noinertia(start, movement, interval=0.5) + # 更新 _room 的所有点位置 + for i in range(len(_room)): + _room[i][0] += dx + + # 返回修正后的 _room + return _room + def enter_room(self, room): """从基建首页进入房间""" - for enter_times in range(3): - for retry_times in range(10): + + for enter_times in range(2): + for retry_times in range(2): if pos := self.find("control_central"): _room = segment.base(self.recog.img, pos)[room] - for i in range(4): - _room[i, 0] = max(_room[i, 0], 0) - _room[i, 0] = min(_room[i, 0], self.recog.w) - _room[i, 1] = max(_room[i, 1], 0) - _room[i, 1] = min(_room[i, 1], self.recog.h) - self.tap(_room) + self.tap(self.adjust_room(_room)) elif self.detect_room() == room: return else: diff --git a/arknights_mower/solvers/base_schedule.py b/arknights_mower/solvers/base_schedule.py index 92b68fe0d..e17aae732 100644 --- a/arknights_mower/solvers/base_schedule.py +++ b/arknights_mower/solvers/base_schedule.py @@ -25,8 +25,10 @@ from arknights_mower.utils import config, detector, rapidocr from arknights_mower.utils import typealias as tp from arknights_mower.utils.csleep import MowerExit, csleep -from arknights_mower.utils.datetime import format_time, get_server_weekday -from arknights_mower.utils.deprecated import deprecated +from arknights_mower.utils.datetime import ( + format_time, + get_server_weekday, +) from arknights_mower.utils.device.device import Device from arknights_mower.utils.digit_reader import DigitReader from arknights_mower.utils.email import maa_template, send_message @@ -40,10 +42,8 @@ from arknights_mower.utils.scheduler_task import ( SchedulerTask, TaskTypes, - add_release_dorm, check_dorm_ordering, find_next_task, - merge_release_dorm, plan_metadata, scheduling, try_add_release_dorm, @@ -167,7 +167,7 @@ def transition(self) -> None: else: self.scene_graph_navigation(Scene.INFRA_MAIN) self.last_room = "" - logger.info("重设上次房间为空") + logger.debug("重设上次房间为空") def overtake_room(self): candidates = self.task.meta_data.split(",") @@ -179,91 +179,92 @@ def overtake_room(self): ] logger.debug(f"更新下班小组信息为{candidates}") # 在candidate 中,计算出需要的high free 和 Low free 数量 - if config.conf.flexible_shift_mode: - current_resting = ( - len(self.op_data.dorm) - - self.op_data.available_free() - - self.op_data.available_free("low") - ) - plan = {} - self.get_resting_plan_new(candidates, [], plan, current_resting) - if len(plan.items()) > 0: - self.tasks.append( - SchedulerTask( - datetime.now(), task_plan=plan, task_type=TaskTypes.SHIFT_OFF - ) - ) - else: - msg = f"无法完成 {self.task.meta_data} 的排班,如果重复接收此邮件请检查替换组是否被占用" - send_message(msg, level="ERROR") - # 尝试挤出当前休息的 - required = 0 - for x in candidates: - op = self.op_data.operators[x] - if op.workaholic: - continue - required += 1 - remove_name = set() - # 按心情降序排序 - sorted_dorms = sorted( - self.op_data.dorm, - key=lambda dorm: self.op_data.operators[dorm.name].mood - if dorm.name in self.op_data.operators - else 25, - reverse=True, + current_resting = ( + len(self.op_data.dorm) + - self.op_data.available_free() + - self.op_data.available_free("low") + ) + plan = {} + self.get_resting_plan(candidates, [], plan, current_resting) + if len(plan.items()) > 0: + self.tasks.append( + SchedulerTask( + datetime.now(), task_plan=plan, task_type=TaskTypes.SHIFT_OFF ) - for idx, dorm in enumerate(sorted_dorms): - if not dorm.name or dorm.name not in self.op_data.operators: - continue - if dorm.time is not None and dorm.time < datetime.now(): - logger.debug(f"跳过{str(dorm)},休息完毕") - continue - operator = self.op_data.operators[dorm.name] - if ( - operator.rest_in_full - or operator.name in self.op_data.rest_in_full_group - ): - # 如果回满,则跳过 - logger.debug(f"跳过{str(dorm)},回满") - continue - # 检查是否有分组 - if operator.group and operator.name not in remove_name: - # 增加当前宿舍组的所有在休息中的干员 - for name in self.op_data.groups[operator.group]: - if self.op_data.operators[name].is_resting(): - _, dorm = self.op_data.get_dorm_by_name(name) - # 跳过已经计算的休息完毕的人 - if dorm.time is not None and dorm.time < datetime.now(): - continue - remove_name.add(name) - else: - remove_name.add(dorm.name) + ) + else: + msg = f"无法完成 {self.task.meta_data} 的排班,如果重复接收此邮件请检查替换组是否被占用" + send_message(msg, level="ERROR") + # 简单暴力一点,移除所有非回满的 + # 智能情况的话,得在人数和替换冲突中做出选择 + required = 0 + for x in candidates: + op = self.op_data.operators[x] + if op.workaholic: + continue + required += 1 + remove_name = set() + # 按心情降序排序 + sorted_dorms = sorted( + self.op_data.dorm, + key=lambda dorm: self.op_data.operators[dorm.name].mood + if dorm.name in self.op_data.operators + else 25, + reverse=True, + ) + for idx, dorm in enumerate(sorted_dorms): + if not dorm.name or dorm.name not in self.op_data.operators: + continue + if dorm.time is not None and dorm.time < datetime.now(): + logger.debug(f"跳过{str(dorm)},休息完毕") + continue + operator = self.op_data.operators[dorm.name] + if ( + operator.rest_in_full + or operator.group in self.op_data.rest_in_full_group + ): + # 如果回满,则跳过 + logger.debug(f"跳过{str(dorm)},回满") + continue + if not operator.is_high(): + # 跳过非高优 + continue + if operator.group and operator.name not in remove_name: + # 增加当前宿舍组的所有在休息中的干员 + for name in self.op_data.groups[operator.group]: + if self.op_data.operators[name].is_resting(): + _, dorm = self.op_data.get_dorm_by_name(name) + # 跳过已经计算的休息完毕的人 + if dorm.time is not None and dorm.time < datetime.now(): + continue + remove_name.add(name) + else: + remove_name.add(dorm.name) - # 检查条件是否满足 - if current_resting - len(remove_name) + required <= len( - self.op_data.dorm - ): - break - if current_resting - len(remove_name) + required > len( + # 检查条件是否满足 + if current_resting - len(remove_name) + required <= len( self.op_data.dorm ): - msg = f"无法完成 {self.task.meta_data} 的排班,宿舍可用空位不足,请减少使用回满词条" - send_message(msg, level="ERROR") - return - logger.debug(f"需要提前移出宿舍的干员: {remove_name}") - planned = set() - for name in remove_name: - if name in planned: - continue - op = self.op_data.operators[name] - group = [op.name] if not op.group else self.op_data.groups[op.group] - for agent in group: - o = self.op_data.operators[agent] - if o.room not in plan: - plan[o.room] = ["Current"] * len(self.op_data.plan[o.room]) - plan[o.room][o.index] = agent - planned.add(o.name) - logger.debug(f"生成顶替上班任务{plan}") + break + if current_resting - len(remove_name) + required > len(self.op_data.dorm): + msg = f"无法完成 {self.task.meta_data} 的排班,宿舍可用空位不足,请减少使用回满词条" + send_message(msg, level="ERROR") + return + logger.debug(f"需要提前移出宿舍的干员: {remove_name}") + planned = set() + for name in remove_name: + if name in planned: + continue + op = self.op_data.operators[name] + group = [op.name] if not op.group else self.op_data.groups[op.group] + for agent in group: + o = self.op_data.operators[agent] + if o.room not in plan: + plan[o.room] = ["Current"] * len(self.op_data.plan[o.room]) + plan[o.room][o.index] = agent + planned.add(o.name) + logger.debug(f"生成顶替上班任务{plan}") + if plan: self.tasks.append(SchedulerTask(task_plan=plan)) # 执行完提前换班任务再次执行本任务 self.tasks.append( @@ -273,137 +274,11 @@ def overtake_room(self): task_type=self.task.type, ) ) - self.skip() - else: - _high_free = 0 - _low_free = 0 - remove_name = [] - for x in candidates: - if ( - self.op_data.operators[x].is_resting() - or self.op_data.operators[x].current_mood() == 24 - ): - remove_name.append(x) - else: - if self.op_data.operators[x].resting_priority == "high": - _high_free += 1 - else: - _low_free += 1 - candidates = [x for x in candidates if x not in remove_name] - self.agent_get_mood(force=True) - # 剩余高效组位置 - current_high = self.op_data.available_free() - # 剩余低效位置 - current_low = self.op_data.available_free("low") - logger.debug(f"剩余高效:{current_high},低效:{current_low}") - logger.debug(f"需求高效:{_high_free},低效:{_low_free}") - success = False - if current_high >= _high_free and current_low >= _low_free: - # 检查是否目前宿舍满足 low free 和high free 的数量需求,如果满足,则直接安排 - _plan = {} - _replacement = [] - _replacement, _plan, current_high, current_low = self.get_resting_plan( - candidates, _replacement, _plan, current_high, current_low - ) - if len(_plan.items()) > 0: - self.tasks.append( - SchedulerTask( - datetime.now(), - task_plan=_plan, - task_type=TaskTypes.SHIFT_OFF, - ) - ) - success = True - else: - msg = f"无法完成 {self.task.meta_data} 的排班,如果重复接收此邮件请检查替换组是否被占用" - send_message(msg, level="ERROR") - if not success: - # 如果不满足,则找到并且执行最近一个type 包含 超过数量的high free 和low free 的 任务并且 干员没有 exaust_require 词条 - task_index = -1 - current_high, current_low = 0, 0 - for idx, task in enumerate(self.tasks): - if "dorm" in task.meta_data: - # 检查数量 - ids = [int(w[4:]) for w in task.meta_data.split(",")] - is_exhaust_require = False - for _id in ids: - if not is_exhaust_require: - if ( - self.op_data.dorm[_id].name - in self.op_data.exhaust_agent - ): - is_exhaust_require = True - - if _id > self.op_data.config.max_resting_count - 1: - current_low += 1 - else: - current_high += 1 - # 休息满需要则跳过 - if ( - current_high >= _high_free - and current_low >= _low_free - and not is_exhaust_require - ): - task_index = idx - else: - current_low, current_high = 0, 0 - if task_index > -1: - # 修改执行时间 - self.tasks[task_index].time = datetime.now() - # 执行完提前换班任务再次执行本任务 - self.tasks.append( - SchedulerTask( - task_plan=copy.deepcopy(self.task.plan), - meta_data=self.task.meta_data, - task_type=self.task.type, - ) - ) - else: - # 任务全清 - rooms = [] - remove_idx = [] - for idx, task in enumerate(self.tasks): - if "dorm" in task.meta_data: - # 检查数量 - ids = [int(w[4:]) for w in task.meta_data.split(",")] - is_exhaust_require = False - _rooms = [] - for _id in ids: - if not is_exhaust_require: - if ( - self.op_data.dorm[_id].name - in self.op_data.exhaust_agent - ): - is_exhaust_require = True - __room = self.op_data.operators[ - self.op_data.dorm[_id].name - ].room - if __room not in _rooms: - _rooms.append(__room) - # 跳过需要休息满 - if not is_exhaust_require: - rooms.extend(_rooms) - remove_idx.append(idx) - for idx in sorted(remove_idx, reverse=True): - self.tasks.pop(idx) - plan = {} - for room in rooms: - if room not in plan.keys(): - plan[room] = [ - data.agent for data in self.op_data.plan[room] - ] - if len(plan.keys()) > 0: - self.tasks.append(SchedulerTask(task_plan=plan)) - # 执行完提前换班任务再次执行本任务 - self.tasks.append( - SchedulerTask( - task_plan=copy.deepcopy(self.task.plan), - meta_data=self.task.meta_data, - task_type=self.task.type, - ) - ) - self.skip() - return + else: + msg = f"无法完成 {self.task.meta_data} 的排班,请检查是否有替换组冲突" + logger.warning(msg) + send_message(msg, level="ERROR") + self.skip() def handle_error(self, force=False): if self.scene() == Scene.UNKNOWN: @@ -508,235 +383,7 @@ def plan_fia(self): self.tasks.sort(key=lambda task: task.time) def plan_metadata(self): - if not config.conf.flexible_shift_mode: - planned_index = [] - # 移除当前 SHIFT_ON 重新刷新 - for t in self.tasks: - if "dorm" in t.meta_data: - planned_index.extend([int(w[4:]) for w in t.meta_data.split(",")]) - _time = datetime.max - min_resting_time = datetime.max - _plan = {} - _type = [] - # 第一个心情低的且小于3 则只休息半小时 - short_rest = False - self.total_agent = list( - v - for k, v in self.op_data.operators.items() - if v.is_high() and not v.room.startswith("dorm") and not v.is_resting() - ) - self.total_agent.sort( - key=lambda x: x.current_mood() - x.lower_limit, reverse=False - ) - if ( - next( - ( - a - for a in self.total_agent - if (a.name not in self.op_data.exhaust_agent) - and not a.workaholic - and a.current_mood() <= 3 - ), - None, - ) - is not None - ): - short_rest = True - if not short_rest: - for x in self.total_agent: - if ( - not x.workaholic - and not x.exhaust_require - and x.room not in ["factory", "train"] - ): - min_resting_time = min(min_resting_time, x.predict_exhaust()) - logger.debug(f"预测最低休息时间为:{min_resting_time}") - low_priority = [] - for idx, dorm in enumerate(self.op_data.dorm): - logger.debug(f"开始计算{dorm}") - # 如果已经plan了,则跳过 - if idx in planned_index or idx in low_priority: - continue - _name = dorm.name - if _name == "": - continue - # 如果是rest in full,则新增单独任务.. - if ( - _name in self.op_data.operators.keys() - and self.op_data.operators[_name].is_high() - and self.op_data.operators[_name].rest_in_full - ): - __plan = {} - __rest_agent = [] - __type = [] - if self.op_data.operators[dorm.name].group == "": - __rest_agent.append(dorm.name) - else: - __rest_agent.extend( - self.op_data.groups[self.op_data.operators[dorm.name].group] - ) - if dorm.time is not None: - __time = dorm.time - else: - __time = datetime.max - need_early = True - for x in __rest_agent: - # 如果小组内没有耗尽,则提前8分钟上班 - if self.op_data.operators[x].exhaust_require: - need_early = False - # 如果同小组也是rest_in_full则取最大休息时间 否则忽略 - if x in low_priority: - logger.debug("检测到回满组已经安排") - _plan = {} - _idx, __dorm = self.op_data.get_dorm_by_name(x) - if ( - x in self.op_data.operators.keys() - and self.op_data.operators[x].rest_in_full - ): - if __dorm is not None and __dorm.time is not None: - if ( - __dorm.time > __time - and self.op_data.operators[x].resting_priority - == "high" - ): - __time = __dorm.time - if _idx is not None: - __type.append("dorm" + str(_idx)) - planned_index.append(_idx) - logger.debug(f"计划干员为{x}") - __room = self.op_data.operators[x].room - if __room not in __plan.keys(): - __plan[__room] = ["Current"] * len( - self.op_data.plan[__room] - ) - __plan[__room][self.op_data.operators[x].index] = x - if __time < datetime.now(): - __time = datetime.now() - if __time != datetime.max: - if need_early: - __time -= timedelta(minutes=8) - logger.info("全组无耗尽,提前8分钟上班") - self.tasks.append( - SchedulerTask( - time=__time, - task_type=TaskTypes.SHIFT_ON, - task_plan=__plan, - meta_data=",".join(__type), - ) - ) - try_add_release_dorm(__plan, __time, self.op_data, self.tasks) - else: - self.op_data.reset_dorm_time() - self.error = True - # 如果非 rest in full, 则同组取时间最小值 - elif ( - _name in self.op_data.operators.keys() - and not self.op_data.operators[_name].is_high() - and self.op_data.config.free_room - ): - # 释放满心情其他干员 - add_release_dorm(self.tasks, self.op_data, _name) - elif self.op_data.operators[_name].is_high(): - if dorm.time is not None and dorm.time < _time: - logger.debug(f"更新任务时间{dorm.time}") - _time = dorm.time - __room = self.op_data.operators[_name].room - __rest_agent = [] - if self.op_data.operators[_name].group == "": - __rest_agent.append(_name) - else: - __rest_agent.extend( - self.op_data.groups[self.op_data.operators[_name].group] - ) - logger.debug(f"小组分组为{__rest_agent}") - # 如果小组有其他人是rest_in_full则跳过 - if next( - ( - d - for d in __rest_agent - if d in self.op_data.operators.keys() - and self.op_data.operators[d].rest_in_full - ), - None, - ): - continue - for x in __rest_agent: - if x in low_priority: - continue - __room = self.op_data.operators[x].room - if __room not in base_room_list: - continue - if __room not in _plan.keys(): - _plan[__room] = ["Current"] * len(self.op_data.plan[__room]) - _plan[__room][self.op_data.operators[x].index] = x - _dorm_idx, __dorm = self.op_data.get_dorm_by_name(x) - if __dorm is not None: - _type.append("dorm" + str(_dorm_idx)) - planned_index.append(_dorm_idx) - if ( - __dorm.time is not None - and __dorm.time < _time - and self.op_data.operators[x].resting_priority == "high" - ): - logger.debug(f"更新任务时间{dorm.time}") - _time = __dorm.time - - if x not in low_priority: - low_priority.append(x) - # 生成单个任务 - if len(_plan.items()) > 0: - if _time != datetime.max: - _time = min(_time, min_resting_time) - _time -= timedelta(minutes=8) - if _time < datetime.now(): - _time = datetime.now() - _time = ( - _time - if not short_rest - else (datetime.now() + timedelta(hours=0.5)) - ) - self.tasks.append( - SchedulerTask( - time=_time, - task_plan=_plan, - task_type=TaskTypes.SHIFT_ON, - meta_data=",".join(_type), - ) - ) - try_add_release_dorm(_plan, _time, self.op_data, self.tasks) - else: - logger.debug("检测到时间数据不存在") - self.op_data.reset_dorm_time() - self.error = True - # 最后再做不养闲人刷新 - if self.op_data.config.free_room: - - def should_keep(task): - if task.type != TaskTypes.RELEASE_DORM: - return True - elif len(task.plan) == 0: - return False - name = task.meta_data - free_room = list(task.plan.keys())[0] - free_op = task.plan[free_room] - if ( - self.op_data.operators[name].current_room != free_room - or free_op[self.op_data.operators[name].current_index] != "Free" - ): - logger.info(f"检测到{task.meta_data}不在对应位置,移除相关任务") - return False - i, d = self.op_data.get_dorm_by_name(task.meta_data) - if i is None: - logger.info(f"检测到{task.meta_data}不在宿舍,移除相关任务") - return False - return True - - self.tasks = [t for t in self.tasks if should_keep(t)] - merge_interval = config.conf.merge_interval - merge_release_dorm(self.tasks, merge_interval) - else: - # 移除当前 SHIFT_ON and Free Room 重新刷新 - self.tasks = plan_metadata(self.op_data, self.tasks) + self.tasks = plan_metadata(self.op_data, self.tasks) def infra_main(self): """位于基建首页""" @@ -1517,10 +1164,7 @@ def plan_solver(self): logger.info("有未完成的下班任务") return self.plan_metadata() - if config.conf.flexible_shift_mode: - self.resting_new() - else: - self.resting_old() + self.resting() except MowerExit: raise except Exception as e: @@ -1538,147 +1182,12 @@ def plan_solver(self): datetime.now() + timedelta(minutes=0.75 * len(re_order_dorm_plan)) ): logger.info(f"新增宿舍移位任务{re_order_dorm_plan}") - task = SchedulerTask(task_plan=re_order_dorm_plan) - self.tasks.append(task) - - @deprecated("该休息逻辑即将在2025年1月1日被自动替代,请尽量选择弹性休息模式") - def resting_old(self): - self.total_agent.sort( - key=lambda x: x.current_mood() - x.lower_limit, reverse=False - ) - # 剩余高效组位置 - high_free = self.op_data.available_free() - # 剩余低效位置 - low_free = self.op_data.available_free("low") - _replacement = [] - _plan = {} - for op in self.total_agent: - # 忽略掉菲亚梅塔充能的干员 - if high_free == 0 and low_free == 0: - break - if op.name in self.op_data.workaholic_agent: - continue - # 忽略掉正在休息的 - if ( - op.is_resting() - or op.current_room in ["factory", "train"] - or op.room in ["factory", "train"] - ): - continue - # 忽略掉心情值没低于上限的的 - if op.current_mood() > int( - (op.upper_limit - op.lower_limit) - * self.op_data.config.resting_threshold - + op.lower_limit - ): - continue - if op.name in self.op_data.exhaust_agent: - if op.current_mood() <= op.lower_limit + 2: - if ( - self.find_next_task( - task_type=TaskTypes.EXHAUST_OFF, meta_data=op.name - ) - is None - ): - self.enter_room(op.current_room) - result = self.get_agent_from_room( - op.current_room, [op.current_index] - ) - _time = datetime.now() - if ( - result[op.current_index]["time"] is not None - and result[op.current_index]["time"] > _time - ): - _time = result[op.current_index]["time"] - timedelta( - minutes=10 - ) - elif ( - op.current_mood() > 0.25 + op.lower_limit - and op.depletion_rate != 0 - ): - _time = ( - datetime.now() - + timedelta( - hours=(op.current_mood() - op.lower_limit - 0.25) - / op.depletion_rate - ) - - timedelta(minutes=10) - ) - self.back() - # plan 是空的是因为得动态生成 - update_time = False - if op.group != "": - # 检查是否有其他同组任务,刷新时间 - for item in self.op_data.groups[op.group]: - if item not in self.op_data.exhaust_agent: - continue - elif self.find_next_task( - task_type=TaskTypes.EXHAUST_OFF, meta_data=item - ): - update_time = True - exh_task = self.find_next_task( - task_type=TaskTypes.EXHAUST_OFF, - meta_data=item, - ) - if _time < exh_task.time: - logger.info( - f"检测到用尽同组{op.name}比{item}提前下班,更新任务时间为{_time}" - ) - exh_task.time = _time - exh_task.meta_data += f",{op.name}" - logger.debug( - f"更新用尽meta_data为{exh_task.meta_data}" - ) - if not update_time: - self.tasks.append( - SchedulerTask( - time=_time, - task_type=TaskTypes.EXHAUST_OFF, - meta_data=op.name, - ) - ) - # 如果是生成的过去时间,则停止 plan 其他 - if _time < datetime.now(): - break - continue - if op.group != "": - if op.group in self.op_data.exhaust_group: - # 忽略掉用尽心情的分组 - continue - - self.rearrange_resting_priority(op.group) - # 如果在group里则同时上下班 - group_resting = self.op_data.groups[op.group] - - skip_resting = False - for operator in group_resting: - if self.op_data.operators[operator].resting_priority == "high" and ( - self.op_data.operators[operator].upper_limit - - self.op_data.operators[operator].current_mood() - < 2 - ): - skip_resting = True - break - if skip_resting: - logger.debug( - f"{op.group}组内干员{operator}的心情{self.op_data.operators[operator].current_mood()}过高,跳过休息" - ) - continue - - _replacement, _plan, high_free, low_free = self.get_resting_plan( - group_resting, _replacement, _plan, high_free, low_free - ) - else: - _replacement, _plan, high_free, low_free = self.get_resting_plan( - [op.name], _replacement, _plan, high_free, low_free - ) - if len(_plan.keys()) > 0: - self.tasks.append( - SchedulerTask(task_plan=_plan, task_type=TaskTypes.SHIFT_OFF) + task = SchedulerTask( + task_plan=re_order_dorm_plan, task_type=TaskTypes.SHIFT_OFF ) - logger.info(f"生成{_plan}的下班任务") + self.tasks.append(task) - def resting_new(self): + def resting(self): self.total_agent.sort( key=lambda x: x.current_mood() - x.lower_limit, reverse=False ) @@ -1692,7 +1201,7 @@ def resting_new(self): self.ideal_resting_count = ( 4 if self.op_data.average_mood() - > self.op_data.config.resting_threshold * 0.75 + > self.op_data.config.resting_threshold * config.conf.rescue_threshold else len(self.op_data.dorm) ) logger.debug(f"当前理想休息人数是{self.ideal_resting_count}") @@ -1701,7 +1210,7 @@ def resting_new(self): for op in self.total_agent: if ( current_resting + len(_replacement) >= self.ideal_resting_count - and self.op_data.available_free() >= self.ideal_resting_count + and self.op_data.available_free() == 0 ): break if op.name in self.op_data.workaholic_agent: @@ -1730,18 +1239,16 @@ def resting_new(self): # 忽略掉用尽心情的分组 continue group_resting = self.op_data.groups[op.group] - self.get_resting_plan_new( + self.get_resting_plan( group_resting, _replacement, _plan, current_resting ) else: - self.get_resting_plan_new( - [op.name], _replacement, _plan, current_resting - ) + self.get_resting_plan([op.name], _replacement, _plan, current_resting) if len(_plan.keys()) > 0: self.tasks.append( SchedulerTask(task_plan=_plan, task_type=TaskTypes.SHIFT_OFF) ) - logger.info(f"生成{_plan}的下班任务 新") + logger.info(f"生成{_plan}的下班任务") def backup_plan_solver(self, timing=None): if timing is None: @@ -1815,7 +1322,7 @@ def rearrange_resting_priority(self, group): ) high_count -= 1 - def get_resting_plan_new(self, agents, exist_replacement, plan, current_resting): + def get_resting_plan(self, agents, exist_replacement, plan, current_resting): __replacement = [] __plan = {} required = 0 @@ -1898,151 +1405,6 @@ def get_resting_plan_new(self, agents, exist_replacement, plan, current_resting) plan[k][idx] = name logger.debug(f"当前plan{plan}") - def get_resting_plan(self, agents, exist_replacement, plan, high_free, low_free): - _low, _high = 0, 0 - __replacement = [] - __plan = {} - for x in agents: - if self.op_data.operators[x].workaholic: - continue - if self.op_data.operators[x].resting_priority == "low": - _low += 1 - else: - _high += 1 - logger.debug(f"需求高效:{_high},低效:{_low}") - # 肥鸭充能新模式:https://github.com/ArkMowers/arknights-mower/issues/551 - fia_plan, fia_room = self.check_fia() - # 排序 - # 1. 肥鸭充能列表中的干员靠前 - # 2. 不在加工站的干员靠前 - # 3. 心情低的干员靠前 - agents.sort( - key=lambda y: ( - y not in fia_plan if fia_plan else True, - self.op_data.operators[y].current_room in ["factory", "train"], - self.op_data.operators[y].current_mood() - - self.op_data.operators[y].lower_limit, - ) - ) - # 进行位置数量的初步判定 - # 对于252可能需要进行额外判定,由于 low_free 性质等同于 high_free - success = True - if high_free - _high >= 0 and low_free - _low >= 0: - logger.debug(f"计算排班:{agents}") - for agent in agents: - if not success: - break - x = self.op_data.operators[agent] - if x.room not in base_room_list: - logger.debug(f"干员房间出错:{agent}") - success = False - break - if self.op_data.get_dorm_by_name(x.name)[0] is not None: - # 如果干员已经被安排了 - success = False - break - _rep = next( - ( - obj - for obj in x.replacement - if ( - not ( - self.op_data.operators[obj].current_room != "" - and not self.op_data.operators[obj].is_resting() - ) - ) - and obj not in ["但书", "龙舌兰", "佩佩"] - and obj not in exist_replacement - and obj not in __replacement - and self.op_data.operators[obj].current_room != x.room - ), - None, - ) - if _rep is not None: - __replacement.append(_rep) - if x.room not in __plan.keys(): - __plan[x.room] = ["Current"] * len(self.op_data.plan[x.room]) - __plan[x.room][x.index] = _rep - else: - success = False - if success: - # 记录替换组 - exist_replacement.extend(__replacement) - new_plan = False - if any( - self.op_data.operators[a_name].resting_priority == "low" - for a_name in agents - ): - first_low = last_high = None - for a in agents: - ag = self.op_data.operators[a] - if ag.workaholic: - continue - if ag.resting_priority == "low" and first_low is None: - first_low = ag - if ag.resting_priority == "high": - last_high = ag - # 如果低优先的心情低于高优先 - if first_low is None or last_high is None: - pass - elif ( - first_low.current_mood() - last_high.current_mood() - < (last_high.lower_limit - last_high.upper_limit) / 8 - ): - logger.info("低优先级干员心情过低,自动按心情切换优先级") - new_plan = True - workaholic_count = 0 - for idx, x in enumerate(agents): - if self.op_data.operators[x].workaholic: - workaholic_count += 1 - continue - if new_plan: - self.op_data.operators[x].resting_priority = ( - "high" if idx + 1 - workaholic_count <= _high else "low" - ) - logger.info( - f"自动更新{x} 优先级为 {self.op_data.operators[x].resting_priority}" - ) - _dorm = self.op_data.assign_dorm(x) - if _dorm.position[0] not in plan.keys(): - plan[_dorm.position[0]] = ["Current"] * 5 - plan[_dorm.position[0]][_dorm.position[1]] = _dorm.name - for k, v in __plan.items(): - if k not in plan.keys(): - plan[k] = __plan[k] - for idx, name in enumerate(__plan[k]): - if plan[k][idx] == "Current" and name != "Current": - plan[k][idx] = name - else: - success = False - if not success: - _high, _low = 0, 0 - else: - # 如果组内心情人差距过大,则报错 - low_mood = 24 - high_mood = 0 - low_name = "" - high_name = "" - for agent in agents: - x = self.op_data.operators[agent] - if x.resting_priority == "high" and not x.workaholic: - mood = 24 - x.upper_limit + x.current_mood() - if mood < low_mood: - low_mood = mood + 0 - low_name = agent - if mood > high_mood: - high_mood = mood + 0 - high_name = agent - logger.debug(f"低心情:{low_mood}") - logger.debug(f"高心情:{high_mood}") - if low_mood + 4 <= high_mood: - low_agent = self.op_data.operators[low_name] - if not low_agent.rest_in_full: - msg = f"同组干员{low_name}与{high_name}心情差值大于4,请注意!" - logger.warning(msg) - send_message(msg, level="WARNING") - return exist_replacement, plan, high_free - _high, low_free - _low - def initialize_operators(self): self.op_data = Operators(self.global_plan) Operators.current_room_changed_callback = self.current_room_changed @@ -2106,238 +1468,203 @@ def todo_list(self) -> None: self.todo_task = True def clue_new(self): - logger.info("基建:线索") - self.scene_graph_navigation(Scene.INFRA_MAIN) - self.enter_room("meeting") - - clue_size = (162, 216) - clue_top_left = { - "daily": (1118, 334), - "receive": (1305, 122), - "give_away": (30, 208), - # 摆放线索界面,线索框的左上角 - 1: (72, 228), - 2: (374, 334), - 3: (679, 198), - 4: (1003, 265), - 5: (495, 660), - 6: (805, 573), - 7: (154, 608), - } - dot_offset = (168, -8) - main_offset = (425, 0) - main_time_offset = (443, 257) - - def va(a, b): - return a[0] + b[0], a[1] + b[1] - - def tl2p(top_left): - return top_left, va(top_left, clue_size) - - def is_orange(dot): - orange_dot = (255, 104, 1) - return all([abs(dot[i] - orange_dot[i]) < 3 for i in range(3)]) - - clue_scope = {} - for index, top_left in clue_top_left.items(): - clue_scope[index] = tl2p(top_left) - clue_dots = {} - main_dots = {} - main_time = {} - main_scope = {} - for i in range(1, 8): - clue_dots[i] = va(clue_top_left[i], dot_offset) - main_dots[i] = va(clue_dots[i], main_offset) - main_time[i] = va(clue_top_left[i], main_time_offset) - main_scope[i] = tl2p(va(clue_top_left[i], main_offset)) - - class ClueTaskManager: - def __init__(self): - # 操作顺序:领取每日线索、接收好友线索、摆线索、送线索、更新线索交流结束时间 - self.task_list = [ - "daily", - "receive", - "place", - "give_away", - "party_time", - ] - self.task = self.task_list[0] - - def complete(self, task): - task = task or self.task - if task in self.task_list: - self.task_list.remove(task) - self.task = self.task_list[0] if self.task_list else None - - tm_thres = 0.6 - - def clue_cls(scope): - scope_dict = clue_scope if isinstance(scope, str) else main_scope - img = cropimg(self.recog.img, scope_dict[scope]) + try: + logger.info("基建:线索") + self.scene_graph_navigation(Scene.INFRA_MAIN) + self.enter_room("meeting") + + clue_size = (162, 216) + clue_top_left = { + "daily": (1118, 334), + "receive": (1305, 122), + "give_away": (30, 208), + # 摆放线索界面,线索框的左上角 + 1: (72, 228), + 2: (374, 334), + 3: (679, 198), + 4: (1003, 265), + 5: (495, 660), + 6: (805, 573), + 7: (154, 608), + } + dot_offset = (168, -8) + main_offset = (425, 0) + main_time_offset = (443, 257) + + def va(a, b): + return a[0] + b[0], a[1] + b[1] + + def tl2p(top_left): + return top_left, va(top_left, clue_size) + + def is_orange(dot): + orange_dot = (255, 104, 1) + return all([abs(dot[i] - orange_dot[i]) < 3 for i in range(3)]) + + clue_scope = {} + for index, top_left in clue_top_left.items(): + clue_scope[index] = tl2p(top_left) + clue_dots = {} + main_dots = {} + main_time = {} + main_scope = {} for i in range(1, 8): - res = loadres(f"clue/{i}") - result = cv2.matchTemplate(img, res, cv2.TM_CCOEFF_NORMED) - min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) - if max_val > tm_thres: - return i - return None - - exit_pos = (1239, 144) + clue_dots[i] = va(clue_top_left[i], dot_offset) + main_dots[i] = va(clue_dots[i], main_offset) + main_time[i] = va(clue_top_left[i], main_time_offset) + main_scope[i] = tl2p(va(clue_top_left[i], main_offset)) + + class ClueTaskManager: + def __init__(self): + # 操作顺序:领取每日线索、接收好友线索、摆线索、送线索、更新线索交流结束时间 + self.task_list = [ + "daily", + "receive", + "place", + "give_away", + "party_time", + ] + self.task = self.task_list[0] + + def complete(self, task): + task = task or self.task + if task in self.task_list: + self.task_list.remove(task) + self.task = self.task_list[0] if self.task_list else None + + tm_thres = 0.6 + + def clue_cls(scope): + scope_dict = clue_scope if isinstance(scope, str) else main_scope + img = cropimg(self.recog.img, scope_dict[scope]) + for i in range(1, 8): + res = loadres(f"clue/{i}") + result = cv2.matchTemplate(img, res, cv2.TM_CCOEFF_NORMED) + min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) + if max_val > tm_thres: + return i + return None - ctm = ClueTaskManager() + exit_pos = (1239, 144) - friend_clue = [] + ctm = ClueTaskManager() - clue_status = {} + friend_clue = [] - def place_index(): - for cl, st in clue_status.items(): - if st in ["available", "self", "available_self_only"]: - return cl, st - return None, None + clue_status = {} - def detect_unlock(): - unlock_pos = self.find("clue/button_unlock") - if unlock_pos is None: + def place_index(): + for cl, st in clue_status.items(): + if st in ["available", "self", "available_self_only"]: + return cl, st + return None, None + + def detect_unlock(): + unlock_pos = self.find("clue/button_unlock") + if unlock_pos is None: + return None + color = self.get_color(self.get_pos(unlock_pos)) + if all(color > [252] * 3): + return unlock_pos return None - color = self.get_color(self.get_pos(unlock_pos)) - if all(color > [252] * 3): - return unlock_pos - return None - - while ctm.task: - scene = self.scene() - - if scene == Scene.INFRA_DETAILS: - if ctm.task == "party_time": - if self.find("clue/title_party", scope=((1600, 190), (1880, 260))): - self.party_time = self.double_read_time( - ((1768, 438), (1902, 480)) - ) - logger.info(f"线索交流结束时间:{self.party_time}") - if not find_next_task( - self.tasks, - task_type=TaskTypes.CLUE_PARTY, - ): - self.tasks.append( - SchedulerTask( - time=self.party_time - timedelta(milliseconds=1), - task_type=TaskTypes.CLUE_PARTY, - ) + + while ctm.task: + scene = self.scene() + + if scene == Scene.INFRA_DETAILS: + logger.info("INFRA_DETAILS") + if ctm.task == "party_time": + if pos := self.find("clue/check_party"): + logger.info("tap") + self.tap(pos) + self.party_time = self.double_read_time( + ((1768, 438), (1902, 480)) ) + logger.info(f"线索交流结束时间:{self.party_time}") + if not find_next_task( + self.tasks, + task_type=TaskTypes.CLUE_PARTY, + ): + self.tasks.append( + SchedulerTask( + time=self.party_time + - timedelta(milliseconds=1), + task_type=TaskTypes.CLUE_PARTY, + ) + ) + else: + self.party_time = None + logger.info("线索交流未开启") + ctm.complete("party_time") + logger.info("party_time") else: - self.party_time = None - logger.info("线索交流未开启") - ctm.complete("party_time") - else: - # 点击左下角,关闭进驻信息,进入线索界面 - self.tap((725, 850)) - - elif scene == Scene.INFRA_CONFIDENTIAL: - if ctm.task == "daily": - # 检查是否领过线索 - daily_scope = ((1815, 200), (1895, 250)) - if self.find("clue/badge_new", scope=daily_scope): - self.tap((1800, 270)) - else: - ctm.complete("daily") - elif ctm.task == "receive": - receive_scope = ((1815, 360), (1895, 410)) - if self.find("clue/badge_new", scope=receive_scope): - self.ctap((1800, 430)) - else: - ctm.complete("receive") - elif ctm.task == "place": - if unlock_pos := detect_unlock(): - self.tap(unlock_pos) - continue - for i in range(1, 8): - if is_orange(self.get_color(main_dots[i])): - clue_status[i] = "available" - elif clue_cls(i): - hsv = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV) - if 160 < hsv[main_time[i][1]][main_time[i][0]][0] < 180: - clue_status[i] = "friend" + # 点击左下角,关闭进驻信息,进入线索界面 + self.tap((330, 1000)) + + elif scene == Scene.INFRA_CONFIDENTIAL: + logger.info("INFRA_CONFIDENTIAL") + if ctm.task == "daily": + # 检查是否领过线索 + daily_scope = ((1815, 200), (1895, 250)) + if self.find("clue/badge_new", scope=daily_scope): + self.tap((1800, 270)) + else: + ctm.complete("daily") + elif ctm.task == "receive": + receive_scope = ((1815, 360), (1895, 410)) + if self.find("clue/badge_new", scope=receive_scope): + self.ctap((1800, 430)) + else: + ctm.complete("receive") + elif ctm.task == "place": + if unlock_pos := detect_unlock(): + self.tap(unlock_pos) + continue + for i in range(1, 8): + if is_orange(self.get_color(main_dots[i])): + clue_status[i] = "available" + elif clue_cls(i): + hsv = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV) + if 160 < hsv[main_time[i][1]][main_time[i][0]][0] < 180: + clue_status[i] = "friend" + else: + clue_status[i] = "self" else: - clue_status[i] = "self" + clue_status[i] = None + cl, st = place_index() + if st in ["available", "self", "available_self_only"]: + self.tap(main_scope[cl]) + continue else: - clue_status[i] = None - cl, st = place_index() - if st in ["available", "self", "available_self_only"]: - self.tap(main_scope[cl]) - continue - else: - ctm.complete("place") - elif ctm.task == "give_away": - self.ctap((1799, 578)) - elif ctm.task == "party_time": - self.back() - - elif scene == Scene.CLUE_DAILY: - if not self.find( - "clue/icon_notification", scope=((1400, 0), (1920, 400)) - ) and (clue := clue_cls("daily")): - logger.info(f"领取今日线索({clue}号)") - self.tap_element("clue/button_get") - ctm.complete("daily") - else: - # 今日线索已领取,点X退出 - self.tap((1484, 152)) + ctm.complete("place") + elif ctm.task == "give_away": + self.ctap((1799, 578)) + elif ctm.task == "party_time": + self.back() - elif scene == Scene.CLUE_RECEIVE: - if self.find( - "infra_trust_complete", scope=((1230, 0), (1920, 1080)), score=0.1 - ): - self.sleep() - continue - if clue := clue_cls("receive"): - name_scope = ((1580, 220), (1880, 255)) - name_img = cropimg(self.recog.gray, name_scope) - name_img = cv2.copyMakeBorder( - name_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE - ) - name = rapidocr.engine( - name_img, - use_det=True, - use_cls=False, - use_rec=True, - )[0][0][1] - name = name.strip() if name else "好友" - logger.info(f"接收{name}的{clue}号线索") - self.tap(name_scope) - else: - ctm.complete("receive") - self.tap(exit_pos) - - elif scene == Scene.CLUE_PLACE: - cl, st = place_index() - if cl is None: - if unlock_pos := detect_unlock(): - self.tap(unlock_pos) + elif scene == Scene.CLUE_DAILY: + logger.info("CLUE_DAILY") + if not self.find( + "clue/icon_notification", scope=((1400, 0), (1920, 400)) + ) and (clue := clue_cls("daily")): + logger.info(f"领取今日线索({clue}号)") + self.tap_element("clue/button_get") + ctm.complete("daily") else: - ctm.complete("place") - self.tap(exit_pos) - continue - if self.get_color((1328 + 77 * cl, 114))[0] < 150: - # 右上角 1-7 - self.tap(clue_scope[cl]) - continue - receive = st in ["available", "self"] - filter_receive = (1900, 45) - filter_self = (1610, 70) - filter_pos = filter_receive if receive else filter_self - if not all(self.get_color(filter_pos) > [252] * 3): - self.tap(filter_pos) - continue - clue_pos = ((1305, 208), (1305, 503), (1305, 797)) - clue_list = [] - for cp in clue_pos: - clue_img = cropimg(self.recog.img, tl2p(cp)) - res = loadres(f"clue/{cl}") - result = cv2.matchTemplate(clue_img, res, cv2.TM_CCOEFF_NORMED) - min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) - if max_val > tm_thres: - name_scope = (va(cp, (274, 99)), va(cp, (580, 134))) + # 今日线索已领取,点X退出 + self.tap((1484, 152)) + + elif scene == Scene.CLUE_RECEIVE: + logger.info("CLUE_RECEIVE") + if self.find( + "infra_trust_complete", + scope=((1230, 0), (1920, 1080)), + score=0.1, + ): + self.sleep() + continue + if clue := clue_cls("receive"): + name_scope = ((1580, 220), (1880, 255)) name_img = cropimg(self.recog.gray, name_scope) name_img = cv2.copyMakeBorder( name_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE @@ -2348,118 +1675,181 @@ def detect_unlock(): use_cls=False, use_rec=True, )[0][0][1] - if name: - name = name.strip() - time_scope = (va(cp, (45, 222)), va(cp, (168, 255))) - time_hsv = cropimg(self.recog.img, time_scope) - time_hsv = cv2.cvtColor(time_hsv, cv2.COLOR_RGB2HSV) - if 165 < time_hsv[0][0][0] < 175: - time_img = thres2(cropimg(self.recog.gray, time_scope), 180) - time_img = cv2.copyMakeBorder( - time_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE - ) - time = rapidocr.engine( - time_img, - use_det=True, - use_cls=False, - use_rec=True, - )[0][0][1] - if time: - time = time.strip() - else: - time = None - clue_list.append( - {"name": name, "time": time, "scope": tl2p(cp)} - ) + name = name.strip() if name else "好友" + logger.info(f"接收{name}的{clue}号线索") + self.tap(name_scope) else: - break - if clue_list: - list_name = "接收库" if receive else "自有库" - logger.info(f"{cl}号线索{list_name}:{clue_list}") - selected = None - for c in clue_list: - if c["time"]: - selected = c - break - selected = selected or clue_list[0] - self.tap(selected["scope"]) - if clue_status[cl] == "available": - clue_status[cl] = "friend" - elif clue_status[cl] == "available_self_only": - clue_status[cl] = "self_only" - elif clue_status[cl] == "self": - clue_status[cl] = "friend" - else: - clue_status[cl] = None - else: - if clue_status[cl] == "available": - clue_status[cl] = "available_self_only" - elif clue_status[cl] == "available_self_only": - clue_status[cl] = None - elif clue_status[cl] == "self": - clue_status[cl] = "self_only" - else: - clue_status[cl] = None + ctm.complete("receive") + self.tap(exit_pos) - elif scene == Scene.CLUE_GIVE_AWAY: - give_away_true = self.leifeng_mode or ( - not self.leifeng_mode and self.clue_count > self.clue_count_limit - ) - if (c := clue_cls("give_away")) and give_away_true: - if not friend_clue: - if self.find( - "clue/icon_notification", scope=((1400, 0), (1920, 400)) - ): - self.sleep() - continue - for i in range(4): - label_scope = ((1450, 228 + i * 222), (1580, 278 + i * 222)) - if not self.find("clue/label_give_away", scope=label_scope): - break - name_top_left = (870, 127 + 222 * i) - name_scope = (name_top_left, va(name_top_left, (383, 62))) + elif scene == Scene.CLUE_PLACE: + logger.info("CLUE_PLACE") + cl, st = place_index() + if cl is None: + if unlock_pos := detect_unlock(): + self.tap(unlock_pos) + else: + ctm.complete("place") + self.tap(exit_pos) + continue + if self.get_color((1328 + 77 * cl, 114))[0] < 150: + # 右上角 1-7 + self.tap(clue_scope[cl]) + continue + receive = st in ["available", "self"] + filter_receive = (1900, 45) + filter_self = (1610, 70) + filter_pos = filter_receive if receive else filter_self + if not all(self.get_color(filter_pos) > [252] * 3): + self.tap(filter_pos) + continue + clue_pos = ((1305, 208), (1305, 503), (1305, 797)) + clue_list = [] + for cp in clue_pos: + clue_img = cropimg(self.recog.img, tl2p(cp)) + res = loadres(f"clue/{cl}") + result = cv2.matchTemplate(clue_img, res, cv2.TM_CCOEFF_NORMED) + min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) + if max_val > tm_thres: + name_scope = (va(cp, (274, 99)), va(cp, (580, 134))) + name_img = cropimg(self.recog.gray, name_scope) + name_img = cv2.copyMakeBorder( + name_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE + ) name = rapidocr.engine( - cropimg(self.recog.gray, name_scope), + name_img, use_det=True, use_cls=False, use_rec=True, )[0][0][1] if name: name = name.strip() - data = {"name": name} - for j in range(1, 8): - pos = (1230 + j * 64, 142 + i * 222) - data[j] = self.get_color(pos)[0] < 137 - friend_clue.append(data) - logger.debug(friend_clue) - friend = None - for idx, fc in enumerate(friend_clue): - if not fc[c]: - friend = idx - fc[c] = True + time_scope = (va(cp, (45, 222)), va(cp, (168, 255))) + time_hsv = cropimg(self.recog.img, time_scope) + time_hsv = cv2.cvtColor(time_hsv, cv2.COLOR_RGB2HSV) + if 165 < time_hsv[0][0][0] < 175: + time_img = thres2( + cropimg(self.recog.gray, time_scope), 180 + ) + time_img = cv2.copyMakeBorder( + time_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE + ) + time = rapidocr.engine( + time_img, + use_det=True, + use_cls=False, + use_rec=True, + )[0][0][1] + if time: + time = time.strip() + else: + time = None + clue_list.append( + {"name": name, "time": time, "scope": tl2p(cp)} + ) + else: break - friend = friend or 0 - logger.info(f"给{friend_clue[friend]['name']}送一张线索{c}") - self.tap(clue_scope["give_away"]) - self.clue_count -= 1 - self.tap((1790, 200 + friend * 222)) - else: - ctm.complete("give_away") - self.tap((1868, 54)) + if clue_list: + list_name = "接收库" if receive else "自有库" + logger.info(f"{cl}号线索{list_name}:{clue_list}") + selected = None + for c in clue_list: + if c["time"]: + selected = c + break + selected = selected or clue_list[0] + self.tap(selected["scope"]) + if clue_status[cl] == "available": + clue_status[cl] = "friend" + elif clue_status[cl] == "available_self_only": + clue_status[cl] = "self_only" + elif clue_status[cl] == "self": + clue_status[cl] = "friend" + else: + clue_status[cl] = None + else: + if clue_status[cl] == "available": + clue_status[cl] = "available_self_only" + elif clue_status[cl] == "available_self_only": + clue_status[cl] = None + elif clue_status[cl] == "self": + clue_status[cl] = "self_only" + else: + clue_status[cl] = None - elif scene == Scene.CLUE_SUMMARY: - self.back() + elif scene == Scene.CLUE_GIVE_AWAY: + logger.info("CLUE_GIVE_AWAY") + give_away_true = self.leifeng_mode or ( + not self.leifeng_mode + and self.clue_count > self.clue_count_limit + ) + if (c := clue_cls("give_away")) and give_away_true: + if not friend_clue: + if self.find( + "clue/icon_notification", scope=((1400, 0), (1920, 400)) + ): + self.sleep() + continue + for i in range(4): + label_scope = ( + (1450, 228 + i * 222), + (1580, 278 + i * 222), + ) + if not self.find( + "clue/label_give_away", scope=label_scope + ): + break + name_top_left = (870, 127 + 222 * i) + name_scope = ( + name_top_left, + va(name_top_left, (383, 62)), + ) + name = rapidocr.engine( + cropimg(self.recog.gray, name_scope), + use_det=True, + use_cls=False, + use_rec=True, + )[0][0][1] + if name: + name = name.strip() + data = {"name": name} + for j in range(1, 8): + pos = (1230 + j * 64, 142 + i * 222) + data[j] = self.get_color(pos)[0] < 137 + friend_clue.append(data) + logger.debug(friend_clue) + friend = None + for idx, fc in enumerate(friend_clue): + if not fc[c]: + friend = idx + fc[c] = True + break + friend = friend or 0 + logger.info(f"给{friend_clue[friend]['name']}送一张线索{c}") + self.tap(clue_scope["give_away"]) + self.clue_count -= 1 + self.tap((1790, 200 + friend * 222)) + else: + ctm.complete("give_away") + self.tap((1868, 54)) - elif scene in self.waiting_scene: - self.waiting_solver() + elif scene == Scene.CLUE_SUMMARY: + logger.info("CLUE_SUMMARY") + self.back() - else: - self.scene_graph_navigation(Scene.INFRA_MAIN) - self.enter_room("meeting") + elif scene in self.waiting_scene: + logger.info("waiting_scene") + self.waiting_solver() - shop_solver = CreditShop(self.device, self.recog) - shop_solver.run() - self.scene_graph_navigation(Scene.INFRA_MAIN) + else: + self.scene_graph_navigation(Scene.INFRA_MAIN) + self.enter_room("meeting") + shop_solver = CreditShop(self.device, self.recog) + shop_solver.run() + self.scene_graph_navigation(Scene.INFRA_MAIN) + except Exception as e: + logger.exception(e) + return def adjust_order_time(self, accelerate, room): error_count = 0 @@ -2497,7 +1887,7 @@ def adjust_order_time(self, accelerate, room): ): task.time = task_time logger.info( - f'房间 {room} 无人机加速后接单时间为 {task_time.strftime("%H:%M:%S")}' + f"房间 {room} 无人机加速后接单时间为 {task_time.strftime('%H:%M:%S')}" ) action_required_task = scheduling(self.tasks) else: @@ -2536,7 +1926,7 @@ def drone( if accelerate: drone_count = self.digit_reader.get_drone(self.recog.gray) logger.info(f"当前无人机数量为:{drone_count}") - if drone_count < config.conf.drone_count_limit or drone_count > 200: + if drone_count < config.conf.drone_count_limit or drone_count > 225: logger.info(f"无人机数量小于{config.conf.drone_count_limit}->停止") return logger.info("制造站加速") @@ -2895,8 +2285,8 @@ def choose_agent( right_swipe = 0 last_special_filter = profession elif agent and agent[0] in agent_list: - if is_dorm and agent[0] != "阿米娅": - # 只有在宿舍中打开职介筛选 + if agent[0] != "阿米娅": + # 只要不是阿米娅就打开职介筛选 profession = agent_profession[agent[0]] self.profession_filter(profession) if last_special_filter != profession: @@ -2904,6 +2294,10 @@ def choose_agent( last_special_filter = profession if index_change: self.switch_arrange_order(3, "true") + elif is_dorm and agent[0] == "阿米娅" and last_special_filter != "ALL": + # 如果是阿米娅且filter 不是all + self.profession_filter("ALL") + last_special_filter = "ALL" if ( agent[0] in self.op_data.operators and self.op_data.operators[agent[0]].is_resting() @@ -2944,6 +2338,8 @@ def choose_agent( ed = ret[0][1][0] # 终点 self.swipe_noinertia(st, (ed[0] - st[0], 0)) right_swipe += 1 + if right_swipe >= 3: + self.sleep(0.3) if len(agent) == 0: if siege: if last_special_filter != "ALL": @@ -3049,7 +2445,10 @@ def turn_on_room_detail(self, room): self.sleep(interval=0.5) elif pos := self.find("arrange_check_in"): self.tap(pos, interval=0.7) + elif pos := self.find("arrange_check_in_small"): + self.tap(pos, interval=0.7) else: + logger.info("sleep") self.sleep() for back_time in range(3): if pos := self.find("control_central"): @@ -3068,7 +2467,7 @@ def get_agent_from_room(self, room, read_time_index=None): self.sleep(0.5) self.recog.update() clue_res = self.read_screen( - self.recog.img, limit=10, cord=((645, 977), (755, 1018)) + self.recog.img, limit=10, cord=((439, 987), (577, 1033)) ) if clue_res != 11: self.clue_count = clue_res @@ -3399,7 +2798,7 @@ def agent_arrange_room( if current[idx]["agent"] != name and name != "Free": if not (room == "train" and idx == 1): logger.error( - f'检测到的干员{current[idx]["agent"]},需要安排的干员{name}' + f"检测到的干员{current[idx]['agent']},需要安排的干员{name}" ) raise Exception("检测到安排干员未成功") else: @@ -3740,11 +3139,11 @@ def append_maa_task(self, type): "Award", { "award": True, - "mail": False, - "recruit": False, - "orundum": False, - "mining": False, - "specialaccess": False, + "mail": config.conf.maa_mail, + "recruit": config.conf.maa_recruit, + "orundum": config.conf.maa_orundum, + "mining": config.conf.maa_mining, + "specialaccess": config.conf.maa_specialaccess, }, ) diff --git a/arknights_mower/solvers/cultivate_depot.py b/arknights_mower/solvers/cultivate_depot.py index da74a61cf..d3b467222 100644 --- a/arknights_mower/solvers/cultivate_depot.py +++ b/arknights_mower/solvers/cultivate_depot.py @@ -33,6 +33,7 @@ def start(self): headers=get_sign_header( ingame, "get", body, self.sign_token ), + timeout=30, ).json() with open(self.record_path, "w", encoding="utf-8") as file: json.dump(resp, file, ensure_ascii=False, indent=4) diff --git a/arknights_mower/solvers/record.py b/arknights_mower/solvers/record.py index 1c02cc6d9..057e2784e 100644 --- a/arknights_mower/solvers/record.py +++ b/arknights_mower/solvers/record.py @@ -7,6 +7,7 @@ import pytz from tzlocal import get_localzone +from arknights_mower.utils import config from arknights_mower.utils.log import logger from arknights_mower.utils.path import get_path @@ -111,7 +112,7 @@ def save_wrapper(*args, **kwargs): # Create a table if it doesn't exist cursor.execute( - "CREATE TABLE IF NOT EXISTS saved_state (" "time TEXT," "state BLOB" ")" + "CREATE TABLE IF NOT EXISTS saved_state (time TEXT,state BLOB)" ) # Delete the previous saved state @@ -165,17 +166,52 @@ def load_state(): return loaded_state +def clear_data(date_time): + database_path = get_path("@app/tmp/data.db") + try: + connection = sqlite3.connect(database_path) + cursor = connection.cursor() + + # Ensure date_time is in the correct format + if isinstance(date_time, datetime): + date_time_str = date_time.strftime("%Y-%m-%d %H:%M:%S") + else: + date_time_str = date_time + + # Execute the DELETE statement with parameterized query + cursor.execute( + "DELETE FROM agent_action WHERE `current_time` < ?", (date_time_str,) + ) + + connection.commit() + connection.close() + logger.info(f"已删除 早于 {date_time_str} 的干员心情记录") + + except sqlite3.Error as e: + logger.error(f"SQLite error: {e}") + + def get_work_rest_ratios(): # TODO 整理数据计算工休比 database_path = get_path("@app/tmp/data.db") - + favorite = [] if config.conf.favorite == "" else config.conf.favorite.split(",") + sel = "" + for name in favorite: + sel = ( + sel + + """ + UNION + SELECT '{}' AS name + """.format(name) + ) try: # 连接到数据库 conn = sqlite3.connect(database_path) # conn = sqlite3.connect('../../tmp/data.db') cursor = conn.cursor() # 查询数据 - cursor.execute(""" + cursor.execute( + """ SELECT a.* FROM agent_action a JOIN ( @@ -185,12 +221,14 @@ def get_work_rest_ratios(): AND b.is_high = 1 AND b.current_room NOT LIKE 'dormitory%' UNION SELECT '菲亚梅塔' AS name - UNION - SELECT '歌蕾蒂娅' AS name + """ + + sel + + """ ) AS subquery ON a.name = subquery.name WHERE DATE(a.current_time) >= DATE('now', '-1 month', 'localtime') ORDER BY a.current_time; - """) + """ + ) data = cursor.fetchall() # 关闭数据库连接 conn.close() @@ -241,13 +279,23 @@ def get_work_rest_ratios(): # 整理心情曲线 def get_mood_ratios(): database_path = get_path("@app/tmp/data.db") - + favorite = [] if config.conf.favorite == "" else config.conf.favorite.split(",") + sel = "" + for name in favorite: + sel = ( + sel + + """ + UNION + SELECT '{}' AS name + """.format(name) + ) try: # 连接到数据库 conn = sqlite3.connect(database_path) cursor = conn.cursor() # 查询数据(筛掉宿管和替班组的数据) - cursor.execute(""" + cursor.execute( + """ SELECT a.* FROM agent_action a JOIN ( @@ -257,13 +305,15 @@ def get_mood_ratios(): AND b.is_high = 1 AND b.current_room NOT LIKE 'dormitory%' UNION SELECT '菲亚梅塔' AS name - UNION - SELECT '歌蕾蒂娅' AS name + """ + + sel + + """ ) AS subquery ON a.name = subquery.name WHERE DATE(a.current_time) >= DATE('now', '-7 day', 'localtime') ORDER BY a.agent_group DESC, a.current_time; - """) + """ + ) data = cursor.fetchall() # 关闭数据库连接 conn.close() diff --git a/arknights_mower/solvers/skland.py b/arknights_mower/solvers/skland.py index 96fcd2281..f9228c064 100644 --- a/arknights_mower/solvers/skland.py +++ b/arknights_mower/solvers/skland.py @@ -47,7 +47,7 @@ def start(self): self.reward.append( {"nickName": item.account, "reward": resp.get("message")} ) - logger.info(f'{i.get("nickName")}:{resp.get("message")}') + logger.info(f"{i.get('nickName')}:{resp.get('message')}") continue awards = resp["data"]["awards"] for j in awards: @@ -59,7 +59,7 @@ def start(self): } ) logger.info( - f'{i.get("nickName")}获得了{res["name"]}×{j.get("count") or 1}' + f"{i.get('nickName')}获得了{res['name']}×{j.get('count') or 1}" ) if len(self.reward) > 0: return self.record_log() @@ -78,7 +78,7 @@ def log(self, account): headers=header_login, ).json() if r.get("status") != 0: - raise Exception(f'获得token失败:{r["msg"]}') + raise Exception(f"获得token失败:{r['msg']}") return r["data"]["token"] def record_log(self): diff --git a/arknights_mower/tests/scheduler_task_tests.py b/arknights_mower/tests/scheduler_task_tests.py index a441b12ef..b53c20f59 100644 --- a/arknights_mower/tests/scheduler_task_tests.py +++ b/arknights_mower/tests/scheduler_task_tests.py @@ -228,16 +228,14 @@ def test_check_dorm_ordering_add_plan_3(self): check_dorm_ordering(tasks, op_data) # 如果非VIP位置被占用,则刷新 - self.assertEqual(2, len(tasks)) + self.assertEqual(1, len(tasks)) # 验证第任务包含换班+宿舍任务 self.assertEqual(2, len(tasks[0].plan)) # 假设换班任务执行完毕 del tasks[0] # 重复执行不会生成新的 check_dorm_ordering(tasks, op_data) - self.assertEqual(1, len(tasks)) - # 验证第任务包宿舍+换班任务 - self.assertEqual(1, len(tasks[0].plan)) + self.assertEqual(0, len(tasks)) def test_check_dorm_ordering_add_plan_4(self): # 测试 方程有效 @@ -321,12 +319,12 @@ def test_check_dorm_ordering_not_plan2(self): check_dorm_ordering(tasks, op_data) # 如果VIP位已经被占用,则不会生成新任务 - self.assertEqual(2, len(tasks)) + self.assertEqual(1, len(tasks)) # 验证第任务包含换班+宿舍任务 self.assertEqual(2, len(tasks[0].plan)) # 重复执行不会生成新的 check_dorm_ordering(tasks, op_data) - self.assertEqual(2, len(tasks)) + self.assertEqual(1, len(tasks)) # 验证第任务包宿舍+换班任务 self.assertEqual(2, len(tasks[0].plan)) @@ -380,7 +378,10 @@ def test_reorder_1(self): op_data.operators["凯尔希"].current_index = 2 op_data.dorm[2].name = "夕" plan = try_reorder(op_data) - self.assertEqual(len(plan), 2) + self.assertEqual(plan["dormitory_1"][2], "夕") + tasks = [SchedulerTask(task_plan=plan, task_type=TaskTypes.SHIFT_OFF)] + check_dorm_ordering(tasks, op_data) + self.assertEqual(len(tasks), 2) def test_reorder_2(self): # 非高优高效不会被移动 @@ -390,10 +391,15 @@ def test_reorder_2(self): op_data.dorm[2].name = "夕" op_data.dorm[3].name = "见行者" op_data.dorm[4].name = "森蚺" + + # op_data.config.ope_resting_priority=["森蚺","夕"] plan = try_reorder(op_data) self.assertEqual(len(plan), 3) self.assertEqual(plan["dormitory_1"][2], "夕") self.assertEqual(plan["dormitory_1"][4], "凯尔希") + tasks = [SchedulerTask(task_plan=plan, task_type=TaskTypes.SHIFT_OFF)] + check_dorm_ordering(tasks, op_data) + self.assertEqual(len(tasks), 2) def test_reorder_3(self): # 如果高优都占了,则不动 @@ -409,6 +415,9 @@ def test_reorder_3(self): plan = try_reorder(op_data) self.assertEqual(plan["dormitory_1"][2], "夕") self.assertEqual(plan["dormitory_1"][3], "见行者") + tasks = [SchedulerTask(task_plan=plan, task_type=TaskTypes.SHIFT_OFF)] + check_dorm_ordering(tasks, op_data) + self.assertEqual(len(tasks), 2) def init_opdata(self): agent_base_config = PlanConfig( diff --git a/arknights_mower/utils/config/conf.py b/arknights_mower/utils/config/conf.py index 1bd2a6886..3b3aa785d 100644 --- a/arknights_mower/utils/config/conf.py +++ b/arknights_mower/utils/config/conf.py @@ -457,10 +457,14 @@ class RunOrderGrandetModeConf(ConfModel): "菲亚防呆" fia_threshold: float = 0.9 "菲亚阈值" + rescue_threshold: float = 0.75 + "急救阈值" + favorite: str = "" + "替换组心情监视" merge_interval: float = 10 "不养闲人合并间隔" - flexible_shift_mode: bool = False - "弹性休息模式" + dorm_order: str = "" + "宿舍优先级" class SimulatorPart(ConfModel): @@ -543,6 +547,19 @@ class SKLandAccount(BaseModel): "森空岛账号" +class MaaRewardPart(ConfModel): + maa_mail: bool = False + "领取所有邮件奖励" + maa_recruit: bool = False + "进行限定池赠送的每日免费单抽" + maa_orundum: bool = False + "领取幸运墙的每日合成玉奖励" + maa_mining: bool = False + "领取限时开采许可的每日合成玉奖励" + maa_specialaccess: bool = False + "领取五周年赠送月卡奖励" + + class Conf( CluePart, EmailPart, @@ -554,6 +571,7 @@ class Conf( RIICPart, SimulatorPart, SKLandPart, + MaaRewardPart, ): @property def APPNAME(self): diff --git a/arknights_mower/utils/config/plan.py b/arknights_mower/utils/config/plan.py index d76508bb2..39ec32433 100644 --- a/arknights_mower/utils/config/plan.py +++ b/arknights_mower/utils/config/plan.py @@ -8,8 +8,6 @@ class PlanConf(BaseModel): ling_xi: int = 1 "令夕模式,1感知 2烟火 3均衡" - max_resting_count: int = 4 - "最大组人数" exhaust_require: str = "" "耗尽" rest_in_full: str = "" @@ -22,6 +20,8 @@ class PlanConf(BaseModel): "跑单时间刷新干员" refresh_drained: str = "" "用尽时间刷新干员" + ope_resting_priority: str = "" + "休息排序优先级" class BackupPlanConf(PlanConf): @@ -52,6 +52,12 @@ class Plan1(BaseModel): "办公室" train: Optional[Facility] = None "训练室" + gaming_1: Optional[Facility] = None + "活动室1" + gaming_2: Optional[Facility] = None + "活动室2" + gaming_3: Optional[Facility] = None + "活动室3" dormitory_1: Optional[Facility] = None dormitory_2: Optional[Facility] = None dormitory_3: Optional[Facility] = None @@ -78,6 +84,12 @@ class Task(BaseModel): "办公室" train: Optional[list[str]] = None "训练室" + gaming_1: Optional[list[str]] = None + "活动室1" + gaming_2: Optional[list[str]] = None + "活动室2" + gaming_3: Optional[list[str]] = None + "训练室3" dormitory_1: Optional[list[str]] = None dormitory_2: Optional[list[str]] = None dormitory_3: Optional[list[str]] = None diff --git a/arknights_mower/utils/device/utils.py b/arknights_mower/utils/device/utils.py index a913d6740..cfb4becef 100644 --- a/arknights_mower/utils/device/utils.py +++ b/arknights_mower/utils/device/utils.py @@ -8,7 +8,7 @@ def download_file(target_url: str) -> str: """download file to temp path, and return its file path for further usage""" logger.debug(f"downloading: {target_url}") - resp = requests.get(target_url, verify=False) + resp = requests.get(target_url, verify=False, timeout=300) with tempfile.NamedTemporaryFile("wb+", delete=False) as f: file_name = f.name f.write(resp.content) diff --git a/arknights_mower/utils/news.py b/arknights_mower/utils/news.py index 6146686c7..20f15299b 100644 --- a/arknights_mower/utils/news.py +++ b/arknights_mower/utils/news.py @@ -10,7 +10,7 @@ def get_update_time(): host = "https://ak.hypergryph.com" url = host + "/news/" - response = requests.get(url) + response = requests.get(url, timeout=30) response.encoding = "utf8" soup = BeautifulSoup(response.text, "lxml") soup.encode("utf-8") diff --git a/arknights_mower/utils/operators.py b/arknights_mower/utils/operators.py index 7f0ae188f..e28dcaa14 100644 --- a/arknights_mower/utils/operators.py +++ b/arknights_mower/utils/operators.py @@ -3,12 +3,12 @@ from evalidate import Expr, base_eval_model +from arknights_mower.utils import config from arknights_mower.utils.plan import BaseProduct, PlanConfig from ..data import agent_arrange_order, agent_list, base_room_list from ..solvers.record import save_action_to_sqlite_decorator from ..utils.log import logger -from . import config class SkillUpgradeSupport: @@ -233,7 +233,7 @@ def init_and_validate(self, update=False): if "菲亚梅塔" in missing_replacements: return "菲亚梅塔替换缺失" if len(missing_replacements): - return f'以下干员替换组缺失:{",".join(missing_replacements)}' + return f"以下干员替换组缺失:{','.join(missing_replacements)}" dorm_names = [k for k in self.plan.keys() if k.startswith("dorm")] dorm_names.sort(key=lambda d: d, reverse=False) added = [] @@ -251,7 +251,6 @@ def init_and_validate(self, update=False): _dorm.agent == "Free" and not free_found and (dorm + str(_idx)) not in added - and len(added) < self.config.max_resting_count ): self.dorm.append(Dormitory((dorm, _idx))) added.append(dorm + str(_idx)) @@ -265,12 +264,36 @@ def init_and_validate(self, update=False): if _dorm.agent == "Free" and (dorm + str(_idx)) not in added: self.dorm.append(Dormitory((dorm, _idx))) added.append(dorm + str(_idx)) + if config.conf.dorm_order == "": + logger.info(self.dorm) + config.conf.dorm_order = ",".join( + [ + dorm.position[0] + "_" + str(dorm.position[1]) + for dorm in self.dorm + ] + ) + logger.info(config.conf.dorm_order) + config.save_conf() # 保存配置 + else: + dorm_order = config.conf.dorm_order.split(",") + current_dorm_names = { + dorm.position[0] + "_" + str(dorm.position[1]) for dorm in self.dorm + } + saved_dorm_names = set(dorm_order) + if saved_dorm_names == current_dorm_names: + self.dorm.sort( + key=lambda dorm: dorm_order.index( + dorm.position[0] + "_" + str(dorm.position[1]) + ) + ) + else: + return ( + "宿舍优先级和当前宿舍不匹配,请清除优先级自动排序或者自己更正" + ) else: for key, value in self.shadow_copy.items(): if key not in self.operators: self.add(Operator(key, "")) - if len(self.dorm) < self.config.max_resting_count: - return f"宿舍Free总数 {len(self.dorm)}小于最大分组数 {self.config.max_resting_count}" # 跑单 for x, y in self.plan.items(): if not x.startswith("room"): @@ -281,13 +304,8 @@ def init_and_validate(self, update=False): for char in ["但书", "龙舌兰", "佩佩"] ): self.run_order_rooms[x] = {} - # 判定分组排班可能性 - current_high = self.config.max_resting_count - current_low = len(self.dorm) - self.config.max_resting_count for key in self.groups: total_count = 0 - high_count = 0 - low_count = 0 _replacement = [] for name in self.groups[key]: _candidate = next( @@ -304,16 +322,8 @@ def init_and_validate(self, update=False): _replacement.append(_candidate) if self.operators[name].workaholic: continue - if self.operators[name].resting_priority == "high": - high_count += 1 - else: - low_count += 1 - if ( - high_count > current_high or low_count > current_low - ) and not config.conf.flexible_shift_mode: - return f"{key} 分组无法排班,宿舍可用高优先{current_high},低优先{current_low}->分组需要高优先{high_count},低优先{low_count}" total_count += 1 - if total_count > len(self.dorm) and config.conf.flexible_shift_mode: + if total_count > len(self.dorm): return f"{key} 分组无法排班,分组总数(不包含0心情工作){total_count}大于总宿舍数{len(self.dorm)}" # 设定令夕模式的心情阈值 self.init_mood_limit() @@ -542,10 +552,6 @@ def get_refresh_index(self, room, plan): if room.startswith("dorm") and self.config.free_room: return [i for i, x in enumerate(self.plan[room]) if x == "Free"] for idx, dorm in enumerate(self.dorm): - # Filter out resting priority low - if idx >= self.config.max_resting_count: - if not self.config.free_room: - break if dorm.position[0] == room: for i, _name in enumerate(plan): if _name not in self.operators.keys(): @@ -618,9 +624,12 @@ def average_mood(self): ) return current_mood / total_mood - def available_free(self, free_type="high"): + def available_free(self, free_type="high", time=None): + if not time: + time = datetime.now() ret = 0 freeName = [] + max_count = sum(1 for key in self.plan if key.startswith("dorm")) if free_type == "high": idx = 0 for dorm in self.dorm: @@ -629,17 +638,16 @@ def available_free(self, free_type="high"): and not self.operators[dorm.name].is_high() ): ret += 1 - elif dorm.time is not None and dorm.time < datetime.now(): + elif dorm.time is not None and dorm.time < time: logger.info(f"检测到房间休息完毕,释放{dorm.name}宿舍位") freeName.append(dorm.name) ret += 1 - if idx == self.config.max_resting_count - 1: + if idx == max_count - 1: break else: idx += 1 else: - idx = self.config.max_resting_count - for i in range(idx, len(self.dorm)): + for i in range(max_count, len(self.dorm)): dorm = self.dorm[i] # 释放满休息位 # TODO 高效组且低优先可以相互替换 @@ -648,7 +656,7 @@ def available_free(self, free_type="high"): and not self.operators[dorm.name].is_high() ): ret += 1 - elif dorm.time is not None and dorm.time < datetime.now(): + elif dorm.time is not None and dorm.time < time: logger.info(f"检测到房间休息完毕,释放{dorm.name}宿舍位") freeName.append(dorm.name) ret += 1 @@ -657,23 +665,15 @@ def available_free(self, free_type="high"): if name in agent_list: self.operators[name].mood = self.operators[name].upper_limit self.operators[name].depletion_rate = 0 - self.operators[name].time_stamp = datetime.now() + self.operators[name].time_stamp = time return ret def assign_dorm(self, name, is_new=False): is_high = self.operators[name].resting_priority == "high" - if is_high: - _room = next( - obj - for obj in self.dorm - if obj.name not in self.operators.keys() - or not self.operators[obj.name].is_high() - ) - else: - _room = None - for i in range( - 4 if is_new else self.config.max_resting_count, len(self.dorm) - ): + _room = None + max_count = sum(1 for key in self.plan if key.startswith("dorm")) + if not is_high: + for i in range(max_count, len(self.dorm)): _name = self.dorm[i].name if ( _name == "" @@ -685,6 +685,16 @@ def assign_dorm(self, name, is_new=False): ): _room = self.dorm[i] break + if is_high or _room is None: + if not is_high: + logger.warning("弹性模式下请勿设置过多低优先") + _room = next( + obj + for obj in self.dorm + if obj.name not in self.operators.keys() + or not self.operators[obj.name].is_high() + or (obj.time is not None and obj.time < datetime.now()) + ) _room.name = name _room.time = None return _room @@ -765,6 +775,7 @@ def __init__( self.time_stamp = time_stamp self.workaholic = False self.arrange_order = [2, "false"] + self.exhaust_time = None @property def current_room(self): @@ -826,14 +837,14 @@ def not_valid(self): ) return False - def current_mood(self): + def current_mood(self, time=None): + if not time: + time = datetime.now() predict = self.mood if self.time_stamp is not None: predict = ( self.mood - - self.depletion_rate - * (datetime.now() - self.time_stamp).total_seconds() - / 3600 + - self.depletion_rate * (time - self.time_stamp).total_seconds() / 3600 ) if 0 <= predict <= 24: return predict diff --git a/arknights_mower/utils/plan.py b/arknights_mower/utils/plan.py index 4da526b00..cccab8515 100644 --- a/arknights_mower/utils/plan.py +++ b/arknights_mower/utils/plan.py @@ -41,12 +41,12 @@ def __init__( resting_priority: str, ling_xi: int = 0, workaholic: str = "", - max_resting_count: int = 4, free_blacklist: str = "", resting_threshold: float = 0.5, refresh_trading_config: str = "", free_room: bool = False, refresh_drained: str = "", + ope_resting_priority: str = "", ): """排班的设置 @@ -56,7 +56,6 @@ def __init__( resting_priority: 低优先级 ling_xi: 令夕模式 workaholic: 0心情工作 - max_resting_count: 最大组人数 free_blacklist: 宿舍黑名单 resting_threshold: 心情阈值 refresh_trading_config: 跑单时间刷新干员 @@ -66,7 +65,6 @@ def __init__( self.exhaust_require = to_list(exhaust_require) self.workaholic = to_list(workaholic) self.resting_priority = to_list(resting_priority) - self.max_resting_count = max_resting_count self.free_blacklist = to_list(free_blacklist) # 0 为均衡模式 # 1 为感知信息模式 @@ -80,6 +78,7 @@ def __init__( # 夕(room_3_1,room_1_3),令(room_3_1) self.refresh_trading_config = to_list(refresh_trading_config) self.refresh_drained = to_list(refresh_drained) + self.ope_resting_priority = to_list(ope_resting_priority) def is_rest_in_full(self, agent_name) -> bool: return agent_name in self.rest_in_full @@ -122,6 +121,7 @@ def merge_config(self, target: Self) -> Self: "free_blacklist", "refresh_trading_config", "refresh_drained", + "ope_resting_priority", ]: p_dict = set(getattr(n, p)) target_p = set(getattr(target, p)) @@ -154,6 +154,12 @@ def __init__( else: self.product = product + def __repr__(self): + return ( + f"Room(agent='{self.agent}', group='{self.group}', replacement={self.replacement}, " + f"facility='{self.facility}', product='{self.product}')" + ) + class Plan: def __init__( @@ -163,6 +169,7 @@ def __init__( trigger: Optional[LogicExpression] = None, task: Optional[dict[str, list[str]]] = None, trigger_timing: Optional[str] = None, + name: Optional[str] = "", ): """ Args: @@ -177,6 +184,7 @@ def __init__( self.trigger = trigger self.task = task self.trigger_timing = self.set_timing_enum(trigger_timing) + self.name = name @staticmethod def set_timing_enum(value: str) -> PlanTriggerTiming: diff --git a/arknights_mower/utils/recognize.py b/arknights_mower/utils/recognize.py index 146ea9efd..55c942368 100644 --- a/arknights_mower/utils/recognize.py +++ b/arknights_mower/utils/recognize.py @@ -34,6 +34,8 @@ def __init__(self, device: Device, screencap: bytes = None) -> None: self.scene = Scene.UNDEFINED self.loading_time = 0 self.LOADING_TIME_LIMIT = 5 + self.last_scene = None + self.last_scene_time = time.time() def clear(self): self._screencap = None @@ -136,6 +138,16 @@ def check_announcement(self): def get_scene(self) -> int: """get the current scene in the game""" + current_time = time.time() + # 检查模拟器是否卡死,超过跑单时间*90秒则视为卡死 + if self.scene == self.last_scene: + elapsed_time = current_time - self.last_scene_time + if elapsed_time > config.conf.run_order_delay * 90: + logger.warning("相同场景等待超时 ") + self.last_scene = None + self.last_scene_time = current_time + self.device.exit() + if self.scene != Scene.UNDEFINED: return self.scene @@ -310,7 +322,12 @@ def get_scene(self) -> int: self.scene = Scene.FRIEND_LIST elif self.find("credit_visiting"): self.scene = Scene.FRIEND_VISITING - elif self.find("arrange_check_in") or self.find("arrange_check_in_on"): + elif ( + self.find("arrange_check_in") + or self.find("arrange_check_in_on") + or self.find("room_detail") + or self.find("arrange_check_in_small") + ): self.scene = Scene.INFRA_DETAILS elif self.find("ope_failed"): self.scene = Scene.OPERATOR_FAILED @@ -352,7 +369,8 @@ def get_scene(self) -> int: self.check_current_focus() logger.info(f"Scene {self.scene}: {SceneComment[self.scene]}") - + self.last_scene = self.scene + self.last_scene_time = current_time return self.scene def find_ra_battle_exit(self) -> bool: @@ -559,7 +577,7 @@ def get_sss_scene(self) -> int: self.scene = Scene.SSS_START elif self.find("sss/ec_button"): self.scene = Scene.SSS_EC - elif self.find("sss/device_button"): + elif self.find("sss/device_button") or self.find("sss/next_step_button"): self.scene = Scene.SSS_DEVICE elif self.find("sss/squad_button"): self.scene = Scene.SSS_SQUAD @@ -567,6 +585,8 @@ def get_sss_scene(self) -> int: self.scene = Scene.SSS_DEPLOY elif self.find("sss/redeploy_button"): self.scene = Scene.SSS_REDEPLOY + elif self.find("sss/confirm"): + self.scene = Scene.SSS_CONFIRM elif self.find("sss/loading"): self.scene = Scene.SSS_LOADING elif self.find("sss/close_button"): @@ -645,7 +665,7 @@ def find( color = { "1800": (158, 958), "12cadpa": (1810, 21), - "arrange_confirm": (755, 903), + "arrange_confirm": (963, 969), "arrange_order_options": (1652, 23), "arrange_order_options_scene": (369, 199), "clue": (1740, 855), @@ -739,7 +759,6 @@ def find( "recruit/riic_res/WARRIOR": 0.7, "recruit/time": 0.8, "recruit/stone": 0.7, - "arrange_confirm": 0.85, } if res in color: @@ -766,7 +785,7 @@ def find( return None template_matching = { - "arrange_check_in": ((30, 300), (175, 700)), + # "arrange_check_in": ((30, 300), (175, 700)), "arrange_check_in_on": ((30, 300), (175, 700)), "biography": (768, 934), "business_card": (55, 165), @@ -870,10 +889,10 @@ def find( ] if scope is None and threshold == 0.0: - if res == "arrange_check_in": - scope = ((0, 350), (200, 530)) - threshold = 0.55 - elif res == "arrange_check_in_on": + # if res == "arrange_check_in": + # scope = ((0, 350), (200, 530)) + # threshold = 0.55 + if res == "arrange_check_in_on": scope = ((0, 350), (200, 530)) elif res == "connecting": scope = ((1087, 978), (1430, 1017)) diff --git a/arknights_mower/utils/scene.py b/arknights_mower/utils/scene.py index e999e84a7..b681455e2 100644 --- a/arknights_mower/utils/scene.py +++ b/arknights_mower/utils/scene.py @@ -247,6 +247,8 @@ class Scene: "正在进入" SSS_GUIDE = 1009 "保全教程" + SSS_CONFIRM = 1010 + "保全确定" SF_ENTRANCE = 1101 "隐秘战线入口" SF_INFO = 1102 diff --git a/arknights_mower/utils/scheduler_task.py b/arknights_mower/utils/scheduler_task.py index dbc4090b1..612999bf2 100644 --- a/arknights_mower/utils/scheduler_task.py +++ b/arknights_mower/utils/scheduler_task.py @@ -176,6 +176,7 @@ def generate_plan_by_drom(tasks, op_data): ordered = sorted(tasks.items()) result = [] planned = set() + current_time = datetime.now() for time, (dorms, rest_in_full) in ordered: logger.debug(f"{time},{dorms},{rest_in_full}") plan = {} @@ -201,7 +202,11 @@ def generate_plan_by_drom(tasks, op_data): planned.add(o.name) if rest_in_full: result.append( - SchedulerTask(task_plan=plan, time=time, task_type=TaskTypes.SHIFT_ON) + SchedulerTask( + task_plan=plan, + time=max(time, current_time), + task_type=TaskTypes.SHIFT_ON, + ) ) else: added = False @@ -222,7 +227,9 @@ def generate_plan_by_drom(tasks, op_data): idx, SchedulerTask( task_plan=plan, - time=result[idx].time - timedelta(seconds=1), + time=max( + result[idx].time, current_time - timedelta(seconds=1) + ), task_type=TaskTypes.RELEASE_DORM if rest_in_full is None else TaskTypes.SHIFT_ON, @@ -234,7 +241,7 @@ def generate_plan_by_drom(tasks, op_data): result.append( SchedulerTask( task_plan=plan, - time=time - timedelta(seconds=1), + time=max(time, current_time - timedelta(seconds=1)), task_type=TaskTypes.RELEASE_DORM if rest_in_full is None else TaskTypes.SHIFT_ON, @@ -308,7 +315,7 @@ def plan_metadata(op_data, tasks): limit = 5400 if op_data.power_plant_count == 2 else 3600 if dorm.time and (base_time - dorm.time).total_seconds() > limit: logger.debug( - f"{high_dorms[0].name} 的时间 {base_time} 被调整为 {dorm.time},因为时间差超过{limit/3600}小时" + f"{high_dorms[0].name} 的时间 {base_time} 被调整为 {dorm.time},因为时间差超过{limit / 3600}小时" ) max_rest_in_full_time = base_time if op_data.operators[high_dorms[0].name].exhaust_require: @@ -380,35 +387,50 @@ def plan_metadata(op_data, tasks): def try_reorder(op_data): # 复制副本,防止原本的dorm错误触发纠错 dorm = copy.deepcopy(op_data.dorm) - # 如果当前高优有空位(非高优人员),则重新排序,正在休息的人逐个往前挤 + priority_list = op_data.config.ope_resting_priority vip = sum(1 for key in op_data.plan.keys() if key.startswith("dorm")) logger.debug(f"当前vip个数{vip}") if vip == 0: return - ready_index = 0 - for idx, room in enumerate(dorm): - logger.debug(room) - if not room.name: - continue - op = op_data.operators[room.name] - if op.operator_type == "high" and idx >= vip and op.resting_priority != "high": - if idx == ready_index: - ready_index += 1 - elif ready_index >= vip: - dorm[ready_index].name, room.name = ( - room.name, - dorm[ready_index].name, - ) - room.time = None - ready_index += 1 - elif op.operator_type == "high": - if idx != ready_index: - dorm[ready_index].name, room.name = ( - room.name, - dorm[ready_index].name, - ) - room.time = None - ready_index += 1 + + def get_ranking(name): + if name in op_data.operators: + op = op_data.operators[name] + if op.operator_type == "high" and op.resting_priority == "high": + return "high" + elif op.operator_type == "high": + return "normal" + return "low" + + dorm_info = [ + { + "name": room.name, + "index": idx, + "time": room.time, + "priority": get_ranking(room.name), + } + for idx, room in enumerate(dorm) # **跳过 name 为空的 dorm** + ] + + def sort_key(op): + length = len(priority_list) + priority_order = { + "high": length, + "normal": length + 1, + "low": length + 2, + } # **先排 priority_list,再按 high > normal > low** + return ( + priority_list.index(op["name"]) + if op["name"] in priority_list and op["name"] != "" + else priority_order[op["priority"]], + op["index"], + ) + + dorm_info.sort(key=sort_key) + for idx in range(len(dorm)): + if dorm[idx].name: # **只修改非空 dorm** + dorm[idx].name = dorm_info[idx]["name"] + dorm[idx].time = dorm_info[idx]["time"] plan = {} logger.debug(f"更新房间信息{dorm}") for room in dorm: @@ -522,32 +544,34 @@ def check_dorm_ordering(tasks, op_data): if room.startswith("dorm"): # 是否检查过vip位置 pass_first_free = False + clear = False for idx, agent in enumerate(v): - # 如果当前位置非宿管 且无人员变动(有变动则是下班干员) - if "Free" == plan[room][idx].agent and agent == "Current": - # 如果高优先不变,则跳过逻辑判定 - if not pass_first_free: - continue - current = next( - ( - obj - for obj in op_data.operators.values() - if obj.current_room == room and obj.current_index == idx - ), - None, - ) - if current: - if current.name not in working_agent: - v[idx] = current.name - else: - logger.debug(f"检测到干员{current.name}已经上班") - v[idx] = "Free" + # 如果当前位置为VIP,且有人员变动,则清除后续人员 + if pass_first_free and clear: + if agent == "Current": + current = next( + ( + obj + for obj in op_data.operators.values() + if obj.current_room == room + and obj.current_index == idx + ), + None, + ) + if current: + if current.name not in working_agent: + v[idx] = current.name + else: + logger.debug(f"检测到干员{current.name}已经上班") + v[idx] = "Free" if room not in extra_plan: extra_plan[room] = copy.deepcopy(v) # 新生成移除任务 --> 换成移除 extra_plan[room][idx] = "" if "Free" == plan[room][idx].agent and not pass_first_free: pass_first_free = True + if agent != "Current": + clear = True else: other_plan[room] = v tasks[0].meta_data = "宿舍排序完成" @@ -555,6 +579,8 @@ def check_dorm_ordering(tasks, op_data): for k, v in other_plan.items(): del tasks[0].plan[k] extra_plan[k] = v + for k, v in extra_plan.items(): + extra_plan[k] = [item for item in v if item != ""] logger.info("新增排序任务任务") task = SchedulerTask( task_plan=extra_plan, diff --git a/arknights_mower/utils/segment.py b/arknights_mower/utils/segment.py index 0b7198f5f..b3433a4b2 100644 --- a/arknights_mower/utils/segment.py +++ b/arknights_mower/utils/segment.py @@ -184,6 +184,16 @@ def minus(i: int) -> int: raise RecognizeError(e) +def add_gamingroom(base_x1, base_x2, base_y1, base_y2, alpha, room_name, ret): + """ + 在指定区域右边添加一个房间,同时保留原区域。 + """ + room_x1 = base_x2 + 259 * alpha # 房间的左边界 + room_x2 = room_x1 + 458 * alpha # 房间的右边界 + room_y1, room_y2 = base_y1, base_y2 # 房间的垂直范围与原区域一致 + ret[room_name] = get_poly(room_x1, room_x2, room_y1, room_y2) + + def base( img: tp.Image, central: tp.Scope, draw: bool = False ) -> dict[str, tp.Rectangle]: @@ -201,8 +211,9 @@ def base( y1 -= 67 * alpha y2 += 67 * alpha central = get_poly(x1, x2, y1, y2) + # 先计算出中枢位置 ret["central"] = central - + # 根据中枢计算出4个宿舍位置 for i in range(1, 5): y1 = y2 + 25 * alpha y2 = y1 + 134 * alpha @@ -211,10 +222,12 @@ def base( else: dormitory = get_poly(x1 + 158 * alpha, x2, y1, y2) ret[f"dormitory_{i}"] = dormitory - + print( + f"Dormitory {i} Length: {abs(ret[f'dormitory_{i}'][2][0] - ret[f'dormitory_{i}'][0][0]) / alpha}" + ) x1, y1 = ret["dormitory_1"][0] x2, y2 = ret["dormitory_1"][2] - + # 根据1号宿舍计算出加工站 x1 = x2 + 419 * alpha x2 = x1 + 297 * alpha factory = get_poly(x1, x2, y1, y2) @@ -222,7 +235,7 @@ def base( y2 = y1 - 25 * alpha y1 = y2 - 134 * alpha - meeting = get_poly(x1 - 158 * alpha, x2, y1, y2) + meeting = get_poly(x1 - 158 * alpha, x2 + 158 * alpha, y1, y2) ret["meeting"] = meeting y1 = y2 + 25 * alpha @@ -237,6 +250,36 @@ def base( train = get_poly(x1, x2, y1, y2) ret["train"] = train + add_gamingroom( + ret["factory"][0][0], + ret["factory"][2][0], + ret["factory"][0][1], + ret["factory"][2][1], + alpha, + "gaming_1", + ret, + ) + + add_gamingroom( + ret["contact"][0][0], + ret["contact"][2][0], + ret["contact"][0][1], + ret["contact"][2][1], + alpha, + "gaming_2", + ret, + ) + + add_gamingroom( + ret["train"][0][0], + ret["train"][2][0], + ret["train"][0][1], + ret["train"][2][1], + alpha, + "gaming_3", + ret, + ) + for floor in range(1, 4): x1, y1 = ret[f"dormitory_{floor}"][0] x2, y2 = ret[f"dormitory_{floor}"][2] diff --git a/arknights_mower/utils/skland.py b/arknights_mower/utils/skland.py index 23fccf87d..e6bd60b04 100644 --- a/arknights_mower/utils/skland.py +++ b/arknights_mower/utils/skland.py @@ -86,7 +86,7 @@ def get_grant_code(token): if response.status_code != 200: raise Exception(f"获得认证代码失败:{resp}") if resp.get("status") != 0: - raise Exception(f'获得认证代码失败:{resp["msg"]}') + raise Exception(f"获得认证代码失败:{resp['msg']}") return resp["data"]["code"] @@ -103,7 +103,7 @@ def get_cred(grant): ).json() if resp["code"] != 0: - raise Exception(f'获得cred失败:{resp["message"]}') + raise Exception(f"获得cred失败:{resp['message']}") return resp["data"] @@ -118,6 +118,7 @@ def get_binding_list(sign_token): None, sign_token, ), + timeout=30, ).json() if resp["code"] != 0: @@ -141,8 +142,9 @@ def log(account): token_password_url, json={"phone": account.account, "password": account.password}, headers=header_login, + timeout=30, ).json() if r.get("status") != 0: - raise Exception(f'获得token失败:{r["msg"]}') + raise Exception(f"获得token失败:{r['msg']}") logger.info("森空岛登陆成功") return r["data"]["token"] diff --git a/arknights_mower/utils/solver.py b/arknights_mower/utils/solver.py index 695148eae..a7e1b7847 100644 --- a/arknights_mower/utils/solver.py +++ b/arknights_mower/utils/solver.py @@ -705,11 +705,13 @@ def to_sss(self): self.tap((ec_x, 540)) self.tap_element("sss/ec_button") elif scene == Scene.SSS_DEVICE: - self.tap_element("sss/device_button") + self.tap((1824, 1026)) elif scene == Scene.SSS_SQUAD: self.tap_element("sss/squad_button") elif scene == Scene.SSS_GUIDE: self.tap_element("sss/close_button") + elif scene == Scene.SSS_CONFIRM: + self.tap_element("sss/confirm") else: self.sleep() except MowerExit: diff --git a/arknights_mower/utils/update.py b/arknights_mower/utils/update.py index 42ccd80ff..2cf028ab6 100644 --- a/arknights_mower/utils/update.py +++ b/arknights_mower/utils/update.py @@ -90,7 +90,7 @@ def download_version(version): if chunk: f.write(chunk) index += len(chunk) - print(f"更新进度:{'%.2f%%' % (index*100 / total)}({index}/{total})") + print(f"更新进度:{'%.2f%%' % (index * 100 / total)}({index}/{total})") zip_file = zipfile.ZipFile("./tmp/mower.zip") zip_list = zip_file.namelist() diff --git a/auto_get_res_new.py b/auto_get_res_new.py index b60efaa65..558b71d0e 100644 --- a/auto_get_res_new.py +++ b/auto_get_res_new.py @@ -616,7 +616,7 @@ def 获得干员基建描述(self): 干员技能详情["skill_level"] = skill_level skill_level += 1 干员技能详情["phase_level"] = ( - f'精{item2["cond"]["phase"]} {item2["cond"]["level"]}级' + f"精{item2['cond']['phase']} {item2['cond']['level']}级" ) 干员技能详情["skillname"] = buff_table[item2["buffId"]][0] text = buff_table[item2["buffId"]][1] diff --git a/server.py b/server.py index b2cb76afa..853670ef4 100755 --- a/server.py +++ b/server.py @@ -17,8 +17,8 @@ from tzlocal import get_localzone from werkzeug.exceptions import NotFound -from arknights_mower import __system__ -from arknights_mower.solvers.record import load_state, save_state +from arknights_mower import __system__, __version__ +from arknights_mower.solvers.record import clear_data, load_state, save_state from arknights_mower.utils import config from arknights_mower.utils.datetime import get_server_time from arknights_mower.utils.log import get_log_by_time, logger @@ -122,7 +122,22 @@ def read_depot(): @app.route("/running") def running(): - return "true" if mower_thread and mower_thread.is_alive() else "false" + response = { + "running": bool(mower_thread and mower_thread.is_alive()), + "plan_condition": [], + } + if response["running"]: + from arknights_mower.__main__ import base_scheduler + + if base_scheduler and mower_thread.is_alive(): + response["plan_condition"] = list(base_scheduler.op_data.plan_condition) + for idx, plan in enumerate(base_scheduler.op_data.backup_plans): + if response["plan_condition"][idx]: + response["plan_condition"][idx] = plan.name + response["plan_condition"] = [ + name for name in response["plan_condition"] if name + ] + return response @app.route("/start/") @@ -369,6 +384,21 @@ def getTradingHistory(): return record.get_trading_history(start_date, end_date) +@app.route("/record/clear-data", methods=["DELETE"]) +def clear_data_route(): + date_time_str = request.json.get("date_time") + logger.info(date_time_str) + if not date_time_str: + return "日期时间参数缺失", 400 + try: + date_time = datetime.datetime.fromtimestamp(date_time_str / 1000.0) + except ValueError: + return "日期时间格式不正确", 400 + + clear_data(date_time) + return "数据已清除", 200 + + @app.route("/getwatermark") def getwatermark(): from arknights_mower.__init__ import __version__ @@ -647,6 +677,12 @@ def submit_feedback(): logger.debug(f"收到反馈务请求:{req}") try: log_files = [] + logger.debug(__version__) + from arknights_mower.__main__ import base_scheduler + + if base_scheduler and mower_thread.is_alive(): + for k, v in base_scheduler.op_data.plan.items(): + logger.debug(str(v)) if req["type"] == "Bug": dt = datetime.datetime.fromtimestamp(req["endTime"] / 1000.0) logger.info(dt) @@ -654,7 +690,7 @@ def submit_feedback(): logger.info("log 文件发送中,请等待") if not log_files: raise ValueError("对应时间log 文件无法找到") - body = f"

Bug 发生时间区间:{datetime.datetime.fromtimestamp(req['startTime']/ 1000.0)}--{dt}


{req['description']}

" + body = f"

Bug 发生时间区间:{datetime.datetime.fromtimestamp(req['startTime'] / 1000.0)}--{dt}


{req['description']}

" else: body = req["description"] email = Email( diff --git a/ui/components.d.ts b/ui/components.d.ts index 1d4dc6d13..4ebe79a81 100644 --- a/ui/components.d.ts +++ b/ui/components.d.ts @@ -7,93 +7,95 @@ export {} declare module 'vue' { export interface GlobalComponents { - Buffer: typeof import('./src/components/buffer.vue')['default'] - Bufferinfo: typeof import('./src/components/bufferinfo.vue')['default'] - Clue: typeof import('./src/components/Clue.vue')['default'] - DailyMission: typeof import('./src/components/DailyMission.vue')['default'] - Depotswitch: typeof import('./src/components/Depotswitch.vue')['default'] - DropDown: typeof import('./src/components/DropDown.vue')['default'] - Email: typeof import('./src/components/Email.vue')['default'] - HelpText: typeof import('./src/components/HelpText.vue')['default'] - LongTasks: typeof import('./src/components/LongTasks.vue')['default'] - MaaBasic: typeof import('./src/components/MaaBasic.vue')['default'] - MaaRogue: typeof import('./src/components/MaaRogue.vue')['default'] - MaaSss: typeof import('./src/components/MaaSss.vue')['default'] - MaaWeekly: typeof import('./src/components/MaaWeekly.vue')['default'] - MaaWeeklyNew: typeof import('./src/components/MaaWeeklyNew.vue')['default'] - NA: typeof import('naive-ui')['NA'] - NAlert: typeof import('naive-ui')['NAlert'] - NAutoComplete: typeof import('naive-ui')['NAutoComplete'] - NAvatar: typeof import('naive-ui')['NAvatar'] - NButton: typeof import('naive-ui')['NButton'] - NButtonGroup: typeof import('naive-ui')['NButtonGroup'] - NCard: typeof import('naive-ui')['NCard'] - NCheckbox: typeof import('naive-ui')['NCheckbox'] - NCode: typeof import('naive-ui')['NCode'] - NConfigProvider: typeof import('naive-ui')['NConfigProvider'] - NDataTable: typeof import('naive-ui')['NDataTable'] - NDatePicker: typeof import('naive-ui')['NDatePicker'] - NDialogProvider: typeof import('naive-ui')['NDialogProvider'] - NDivider: typeof import('naive-ui')['NDivider'] - NDropdown: typeof import('naive-ui')['NDropdown'] - NDynamicInput: typeof import('naive-ui')['NDynamicInput'] - NDynamicTags: typeof import('naive-ui')['NDynamicTags'] - NFlex: typeof import('naive-ui')['NFlex'] - NForm: typeof import('naive-ui')['NForm'] - NFormItem: typeof import('naive-ui')['NFormItem'] - NFormItemGi: typeof import('naive-ui')['NFormItemGi'] - NGi: typeof import('naive-ui')['NGi'] - NGlobalStyle: typeof import('naive-ui')['NGlobalStyle'] - NGrid: typeof import('naive-ui')['NGrid'] - NH1: typeof import('naive-ui')['NH1'] - NH2: typeof import('naive-ui')['NH2'] - NIcon: typeof import('naive-ui')['NIcon'] - NImage: typeof import('naive-ui')['NImage'] - NInput: typeof import('naive-ui')['NInput'] - NInputNumber: typeof import('naive-ui')['NInputNumber'] - NLayout: typeof import('naive-ui')['NLayout'] - NLayoutContent: typeof import('naive-ui')['NLayoutContent'] - NLayoutFooter: typeof import('naive-ui')['NLayoutFooter'] - NLayoutSider: typeof import('naive-ui')['NLayoutSider'] - NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider'] - NLog: typeof import('naive-ui')['NLog'] - NMenu: typeof import('naive-ui')['NMenu'] - NMessageProvider: typeof import('naive-ui')['NMessageProvider'] - NModal: typeof import('naive-ui')['NModal'] - NRadio: typeof import('naive-ui')['NRadio'] - NRadioButton: typeof import('naive-ui')['NRadioButton'] - NRadioGroup: typeof import('naive-ui')['NRadioGroup'] - NScrollbar: typeof import('naive-ui')['NScrollbar'] - NSelect: typeof import('naive-ui')['NSelect'] - NSkeleton: typeof import('naive-ui')['NSkeleton'] - NSlider: typeof import('naive-ui')['NSlider'] - NSpace: typeof import('naive-ui')['NSpace'] - NSwitch: typeof import('naive-ui')['NSwitch'] - NTab: typeof import('naive-ui')['NTab'] - NTable: typeof import('naive-ui')['NTable'] - NTabs: typeof import('naive-ui')['NTabs'] - NTag: typeof import('naive-ui')['NTag'] - NTd: typeof import('naive-ui')['NTd'] - NThing: typeof import('naive-ui')['NThing'] - NTimePicker: typeof import('naive-ui')['NTimePicker'] - NTooltip: typeof import('naive-ui')['NTooltip'] - NTr: typeof import('naive-ui')['NTr'] - NTransfer: typeof import('naive-ui')['NTransfer'] - NUpload: typeof import('naive-ui')['NUpload'] - NVirtualList: typeof import('naive-ui')['NVirtualList'] - NWatermark: typeof import('naive-ui')['NWatermark'] - PlanEditor: typeof import('./src/components/PlanEditor.vue')['default'] - ReclamationAlgorithm: typeof import('./src/components/ReclamationAlgorithm.vue')['default'] - Recruit: typeof import('./src/components/Recruit.vue')['default'] - RenameDialog: typeof import('./src/components/RenameDialog.vue')['default'] - RouterLink: typeof import('vue-router')['RouterLink'] - RouterView: typeof import('vue-router')['RouterView'] - SecretFront: typeof import('./src/components/SecretFront.vue')['default'] - SKLand: typeof import('./src/components/SKLand.vue')['default'] - SlickOperatorSelect: typeof import('./src/components/SlickOperatorSelect.vue')['default'] - TaskDialog: typeof import('./src/components/TaskDialog.vue')['default'] - TriggerDialog: typeof import('./src/components/TriggerDialog.vue')['default'] - TriggerEditor: typeof import('./src/components/TriggerEditor.vue')['default'] - TriggerString: typeof import('./src/components/TriggerString.vue')['default'] + Buffer: (typeof import('./src/components/buffer.vue'))['default'] + Bufferinfo: (typeof import('./src/components/bufferinfo.vue'))['default'] + Clue: (typeof import('./src/components/Clue.vue'))['default'] + DailyMission: (typeof import('./src/components/DailyMission.vue'))['default'] + Depotswitch: (typeof import('./src/components/Depotswitch.vue'))['default'] + DropDown: (typeof import('./src/components/DropDown.vue'))['default'] + Email: (typeof import('./src/components/Email.vue'))['default'] + Feedback: (typeof import('./src/components/Feedback.vue'))['default'] + HelpText: (typeof import('./src/components/HelpText.vue'))['default'] + LongTasks: (typeof import('./src/components/LongTasks.vue'))['default'] + MaaBasic: (typeof import('./src/components/MaaBasic.vue'))['default'] + MaaReward: (typeof import('./src/components/MaaReward.vue'))['default'] + MaaRogue: (typeof import('./src/components/MaaRogue.vue'))['default'] + MaaSss: (typeof import('./src/components/MaaSss.vue'))['default'] + MaaWeekly: (typeof import('./src/components/MaaWeekly.vue'))['default'] + MaaWeeklyNew: (typeof import('./src/components/MaaWeeklyNew.vue'))['default'] + NA: (typeof import('naive-ui'))['NA'] + NAlert: (typeof import('naive-ui'))['NAlert'] + NAutoComplete: (typeof import('naive-ui'))['NAutoComplete'] + NAvatar: (typeof import('naive-ui'))['NAvatar'] + NButton: (typeof import('naive-ui'))['NButton'] + NButtonGroup: (typeof import('naive-ui'))['NButtonGroup'] + NCard: (typeof import('naive-ui'))['NCard'] + NCheckbox: (typeof import('naive-ui'))['NCheckbox'] + NCode: (typeof import('naive-ui'))['NCode'] + NConfigProvider: (typeof import('naive-ui'))['NConfigProvider'] + NDataTable: (typeof import('naive-ui'))['NDataTable'] + NDatePicker: (typeof import('naive-ui'))['NDatePicker'] + NDialogProvider: (typeof import('naive-ui'))['NDialogProvider'] + NDivider: (typeof import('naive-ui'))['NDivider'] + NDropdown: (typeof import('naive-ui'))['NDropdown'] + NDynamicInput: (typeof import('naive-ui'))['NDynamicInput'] + NDynamicTags: (typeof import('naive-ui'))['NDynamicTags'] + NFlex: (typeof import('naive-ui'))['NFlex'] + NForm: (typeof import('naive-ui'))['NForm'] + NFormItem: (typeof import('naive-ui'))['NFormItem'] + NFormItemGi: (typeof import('naive-ui'))['NFormItemGi'] + NGi: (typeof import('naive-ui'))['NGi'] + NGlobalStyle: (typeof import('naive-ui'))['NGlobalStyle'] + NGrid: (typeof import('naive-ui'))['NGrid'] + NH1: (typeof import('naive-ui'))['NH1'] + NH2: (typeof import('naive-ui'))['NH2'] + NIcon: (typeof import('naive-ui'))['NIcon'] + NImage: (typeof import('naive-ui'))['NImage'] + NInput: (typeof import('naive-ui'))['NInput'] + NInputNumber: (typeof import('naive-ui'))['NInputNumber'] + NLayout: (typeof import('naive-ui'))['NLayout'] + NLayoutContent: (typeof import('naive-ui'))['NLayoutContent'] + NLayoutFooter: (typeof import('naive-ui'))['NLayoutFooter'] + NLayoutSider: (typeof import('naive-ui'))['NLayoutSider'] + NLoadingBarProvider: (typeof import('naive-ui'))['NLoadingBarProvider'] + NLog: (typeof import('naive-ui'))['NLog'] + NMenu: (typeof import('naive-ui'))['NMenu'] + NMessageProvider: (typeof import('naive-ui'))['NMessageProvider'] + NModal: (typeof import('naive-ui'))['NModal'] + NRadio: (typeof import('naive-ui'))['NRadio'] + NRadioButton: (typeof import('naive-ui'))['NRadioButton'] + NRadioGroup: (typeof import('naive-ui'))['NRadioGroup'] + NScrollbar: (typeof import('naive-ui'))['NScrollbar'] + NSelect: (typeof import('naive-ui'))['NSelect'] + NSkeleton: (typeof import('naive-ui'))['NSkeleton'] + NSlider: (typeof import('naive-ui'))['NSlider'] + NSpace: (typeof import('naive-ui'))['NSpace'] + NSwitch: (typeof import('naive-ui'))['NSwitch'] + NTab: (typeof import('naive-ui'))['NTab'] + NTable: (typeof import('naive-ui'))['NTable'] + NTabs: (typeof import('naive-ui'))['NTabs'] + NTag: (typeof import('naive-ui'))['NTag'] + NTd: (typeof import('naive-ui'))['NTd'] + NThing: (typeof import('naive-ui'))['NThing'] + NTimePicker: (typeof import('naive-ui'))['NTimePicker'] + NTooltip: (typeof import('naive-ui'))['NTooltip'] + NTr: (typeof import('naive-ui'))['NTr'] + NUpload: (typeof import('naive-ui'))['NUpload'] + NVirtualList: (typeof import('naive-ui'))['NVirtualList'] + NWatermark: (typeof import('naive-ui'))['NWatermark'] + PlanEditor: (typeof import('./src/components/PlanEditor.vue'))['default'] + ReclamationAlgorithm: (typeof import('./src/components/ReclamationAlgorithm.vue'))['default'] + Recruit: (typeof import('./src/components/Recruit.vue'))['default'] + RenameDialog: (typeof import('./src/components/RenameDialog.vue'))['default'] + RouterLink: (typeof import('vue-router'))['RouterLink'] + RouterView: (typeof import('vue-router'))['RouterView'] + SecretFront: (typeof import('./src/components/SecretFront.vue'))['default'] + SKLand: (typeof import('./src/components/SKLand.vue'))['default'] + SlickDormSelect: (typeof import('./src/components/SlickDormSelect.vue'))['default'] + SlickOperatorSelect: (typeof import('./src/components/SlickOperatorSelect.vue'))['default'] + TaskDialog: (typeof import('./src/components/TaskDialog.vue'))['default'] + TriggerDialog: (typeof import('./src/components/TriggerDialog.vue'))['default'] + TriggerEditor: (typeof import('./src/components/TriggerEditor.vue'))['default'] + TriggerString: (typeof import('./src/components/TriggerString.vue'))['default'] } } diff --git "a/ui/public/avatar/\344\275\231.webp" "b/ui/public/avatar/\344\275\231.webp" new file mode 100644 index 000000000..27e292c1a Binary files /dev/null and "b/ui/public/avatar/\344\275\231.webp" differ diff --git "a/ui/public/avatar/\345\257\273\346\276\234.webp" "b/ui/public/avatar/\345\257\273\346\276\234.webp" new file mode 100644 index 000000000..e4a6d4e21 Binary files /dev/null and "b/ui/public/avatar/\345\257\273\346\276\234.webp" differ diff --git "a/ui/public/avatar/\347\203\233\347\205\214.webp" "b/ui/public/avatar/\347\203\233\347\205\214.webp" new file mode 100644 index 000000000..173224ecf Binary files /dev/null and "b/ui/public/avatar/\347\203\233\347\205\214.webp" differ diff --git "a/ui/public/avatar/\350\241\214\347\256\270.webp" "b/ui/public/avatar/\350\241\214\347\256\270.webp" new file mode 100644 index 000000000..700b81ada Binary files /dev/null and "b/ui/public/avatar/\350\241\214\347\256\270.webp" differ diff --git a/ui/public/building_skill/bskill_ctrl_train_spd2.webp b/ui/public/building_skill/bskill_ctrl_train_spd2.webp new file mode 100644 index 000000000..6bb99c40f Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_train_spd2.webp differ diff --git a/ui/public/building_skill/bskill_dorm_sui.webp b/ui/public/building_skill/bskill_dorm_sui.webp new file mode 100644 index 000000000..573302906 Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_sui.webp differ diff --git a/ui/public/building_skill/bskill_meet_spd&blacksteel1.webp b/ui/public/building_skill/bskill_meet_spd&blacksteel1.webp new file mode 100644 index 000000000..d98fb336b Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&blacksteel1.webp differ diff --git a/ui/public/building_skill/bskill_meet_spd&blacksteel2.webp b/ui/public/building_skill/bskill_meet_spd&blacksteel2.webp new file mode 100644 index 000000000..95a340fd0 Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&blacksteel2.webp differ diff --git a/ui/public/building_skill/bskill_train_bd.webp b/ui/public/building_skill/bskill_train_bd.webp new file mode 100644 index 000000000..52f591842 Binary files /dev/null and b/ui/public/building_skill/bskill_train_bd.webp differ diff --git "a/ui/public/depot/\344\275\231\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\275\231\347\232\204\344\277\241\347\211\251.webp" new file mode 100644 index 000000000..bd278f199 Binary files /dev/null and "b/ui/public/depot/\344\275\231\347\232\204\344\277\241\347\211\251.webp" differ diff --git "a/ui/public/depot/\345\257\273\346\276\234\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\257\273\346\276\234\347\232\204\344\277\241\347\211\251.webp" new file mode 100644 index 000000000..45d612c45 Binary files /dev/null and "b/ui/public/depot/\345\257\273\346\276\234\347\232\204\344\277\241\347\211\251.webp" differ diff --git "a/ui/public/depot/\346\254\242\345\256\264\350\211\257\345\256\265\345\257\273\350\256\277\345\207\255\350\257\201.webp" "b/ui/public/depot/\346\254\242\345\256\264\350\211\257\345\256\265\345\257\273\350\256\277\345\207\255\350\257\201.webp" new file mode 100644 index 000000000..34ad36771 Binary files /dev/null and "b/ui/public/depot/\346\254\242\345\256\264\350\211\257\345\256\265\345\257\273\350\256\277\345\207\255\350\257\201.webp" differ diff --git "a/ui/public/depot/\347\203\233\347\205\214\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\203\233\347\205\214\347\232\204\344\277\241\347\211\251.webp" new file mode 100644 index 000000000..0c34ab1a7 Binary files /dev/null and "b/ui/public/depot/\347\203\233\347\205\214\347\232\204\344\277\241\347\211\251.webp" differ diff --git "a/ui/public/depot/\350\241\214\347\256\270\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\241\214\347\256\270\347\232\204\344\277\241\347\211\251.webp" new file mode 100644 index 000000000..e333581e7 Binary files /dev/null and "b/ui/public/depot/\350\241\214\347\256\270\347\232\204\344\277\241\347\211\251.webp" differ diff --git a/ui/src/App.vue b/ui/src/App.vue index ced152f36..def235fa7 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -50,11 +50,32 @@ 日志 - +
设置
+ + +
+ + mower设置 + +
+
+ + maa设置 + +
+
+
@@ -142,11 +163,14 @@ import Newspaper from '@vicons/ionicons5/Newspaper' import Settings from '@vicons/ionicons5/Settings' import StatsChart from '@vicons/ionicons5/StatsChart' import Storefront from '@vicons/ionicons5/Storefront' +import RoseOutline from '@vicons/ionicons5/RoseOutline' +import Coffee from '@vicons/tabler/Coffee' import { NIcon } from 'naive-ui' import { storeToRefs } from 'pinia' import { h, inject, onMounted, provide, ref } from 'vue' const showModal = ref(false) +const showModal2 = ref(false) function renderIcon(icon) { return () => h(NIcon, null, { default: () => h(icon) }) } @@ -159,9 +183,22 @@ const menuOptions = [ key: 'go-to-log' }, { - label: () => h(RouterLink, { to: { path: '/settings' } }, { default: () => '全部设置' }), + label: () => '全部设置', icon: renderIcon(Settings), - key: 'go-to-allsetting' + key: 'allsetting', + children: [ + { + label: () => + h(RouterLink, { to: { path: '/mowersettings' } }, { default: () => 'mower设置' }), + icon: renderIcon(Coffee), + key: 'go-to-mowersetting' + }, + { + label: () => h(RouterLink, { to: { path: '/maasettings' } }, { default: () => 'maa设置' }), + icon: renderIcon(RoseOutline), + key: 'go-to-maasetting' + } + ] }, // { // label: () => h(RouterLink, { to: { path: '/aio' } }, { default: () => 'aio' }), diff --git a/ui/src/components/Clue.vue b/ui/src/components/Clue.vue index 3134d8aa0..ecf67b13a 100644 --- a/ui/src/components/Clue.vue +++ b/ui/src/components/Clue.vue @@ -5,14 +5,12 @@ import { storeToRefs } from 'pinia' const config_store = useConfigStore() const { - enable_party, shop_list, maa_mall_buy, maa_mall_blacklist, maa_mall_ignore_blacklist_when_full, maa_enable, maa_credit_fight, - leifeng_mode, credit_fight } = storeToRefs(config_store) @@ -72,18 +70,8 @@ const show_map = ref(false) - 我查看了Mower群文件,确保软件版本是最新版本 + 我查看了更新公告/下崽器,确保软件版本是最新版本 diff --git a/ui/src/components/MaaBasic.vue b/ui/src/components/MaaBasic.vue index a1731fae5..bb02207a9 100644 --- a/ui/src/components/MaaBasic.vue +++ b/ui/src/components/MaaBasic.vue @@ -43,7 +43,7 @@ const maa_touch_options = ['maatouch', 'minitouch', 'adb'].map((x) => { +
+ 当前激活的副表为: + + {{ x }} + +
{ if (sub_plan.value == 'main') { current_conf.value = { ling_xi: ling_xi.value, - max_resting_count: max_resting_count.value, rest_in_full: rest_in_full.value, resting_priority: resting_priority.value, workaholic: workaholic.value, exhaust_require: exhaust_require.value, refresh_trading: refresh_trading.value, free_blacklist: free_blacklist.value, - refresh_drained: refresh_drained.value + refresh_drained: refresh_drained.value, + ope_resting_priority: ope_resting_priority.value } } else { current_conf.value = backup_plans.value[sub_plan.value].conf @@ -158,7 +157,6 @@ watchEffect(() => { watchEffect(() => { if (sub_plan.value == 'main') { ling_xi.value = current_conf.value.ling_xi - max_resting_count.value = current_conf.value.max_resting_count rest_in_full.value = current_conf.value.rest_in_full exhaust_require.value = current_conf.value.exhaust_require resting_priority.value = current_conf.value.resting_priority @@ -166,6 +164,7 @@ watchEffect(() => { refresh_trading.value = current_conf.value.refresh_trading free_blacklist.value = current_conf.value.free_blacklist refresh_drained.value = current_conf.value.refresh_drained + ope_resting_priority.value = current_conf.value.ope_resting_priority } else { backup_plans.value[sub_plan.value].conf = current_conf.value } @@ -270,7 +269,9 @@ function movePlanForward() { @@ -351,12 +352,6 @@ function movePlanForward() {
- - - - - - @@ -410,6 +405,16 @@ function movePlanForward() { + + + + diff --git a/ui/src/pages/RecordPie.vue b/ui/src/pages/RecordPie.vue index 3da657f72..84e77feb8 100644 --- a/ui/src/pages/RecordPie.vue +++ b/ui/src/pages/RecordPie.vue @@ -1,6 +1,24 @@