From 4112c67d62fc373fe0e225a377b288fc661400cd Mon Sep 17 00:00:00 2001 From: mno <718135749@qq.com> Date: Wed, 10 Dec 2025 21:44:46 +0800 Subject: [PATCH] =?UTF-8?q?js=EF=BC=9A=E9=87=87=E9=9B=86cd=E7=AE=A1?= =?UTF-8?q?=E7=90=862.4.1=20(#2473)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[发光髓]}/吉光虫.png | Bin .../[发光髓]}/燃素蜜虫.png | Bin .../[发光髓]}/萤火虫.png | Bin .../[晶核]}/冰晶蝶.png | Bin .../[晶核]}/岩晶蝶.png | Bin .../[晶核]}/月萤虫.png | Bin .../[晶核]}/水晶蝶.png | Bin .../[晶核]}/火晶蝶.png | Bin .../[晶核]}/草晶蝶.png | Bin .../[晶核]}/雷晶蝶.png | Bin .../[晶核]}/风晶蝶.png | Bin .../[蜥蜴尾巴]}/嗜髓蜥.png | Bin .../[蜥蜴尾巴]}/红角蜥.png | Bin .../[蜥蜴尾巴]}/绿角蜥.png | Bin .../[蜥蜴尾巴]}/蓝角蜥.png | Bin .../[蜥蜴尾巴]}/赤尾蜥.png | Bin .../[蝴蝶翅膀]}/蝴蝶.png | Bin .../[螃蟹]}/太阳蟹.png | Bin .../[螃蟹]}/将军蟹.png | Bin .../[螃蟹]}/海蓝蟹.png | Bin .../[螃蟹]}/薄红蟹.png | Bin .../[螃蟹]}/黃金蟹.png | Bin .../Z获得物品与交互名称不一致/[铁块]/固晶甲虫.png | Bin 0 -> 1510 bytes .../[青蛙]}/丛林树蛙.png | Bin .../[青蛙]}/泥蛙.png | Bin .../[青蛙]}/蓝蛙.png | Bin .../[青蛙]}/青蛙.png | Bin .../鱼肉 => Z获得物品与交互名称不一致/[鱼肉]}/蓝鳍鲈鱼.png | Bin .../鱼肉 => Z获得物品与交互名称不一致/[鱼肉]}/黃金鲈鱼.png | Bin .../鱼肉 => Z获得物品与交互名称不一致/[鱼肉]}/黑背鲈鱼.png | Bin .../[鳅鳅宝玉]}/晴天鳅鳅.png | Bin .../[鳅鳅宝玉]}/落日鳅鳅.png | Bin .../[鳅鳅宝玉]}/金鳅鳅.png | Bin .../[鳗肉]}/流沙鳗鳗.png | Bin .../[鳗肉]}/深海鳗鳗.png | Bin .../[鳗肉]}/藤纹陆鳗鳗.png | Bin .../[鳗肉]}/赤鳍陆鳗鳗.png | Bin repo/js/采集cd管理/assets/蒙德炉子.json | 115 ++ repo/js/采集cd管理/assets/调为夜晚.json | 31 + repo/js/采集cd管理/assets/调为白天.json | 31 + repo/js/采集cd管理/main.js | 1017 ++++++++++++----- repo/js/采集cd管理/manifest.json | 2 +- repo/js/采集cd管理/pathing/占位.js | 0 repo/js/采集cd管理/settings.json | 5 + 44 files changed, 942 insertions(+), 259 deletions(-) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/发光髓(3种) => Z获得物品与交互名称不一致/[发光髓]}/吉光虫.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/发光髓(3种) => Z获得物品与交互名称不一致/[发光髓]}/燃素蜜虫.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/发光髓(3种) => Z获得物品与交互名称不一致/[发光髓]}/萤火虫.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/晶核(8种) => Z获得物品与交互名称不一致/[晶核]}/冰晶蝶.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/晶核(8种) => Z获得物品与交互名称不一致/[晶核]}/岩晶蝶.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/晶核(8种) => Z获得物品与交互名称不一致/[晶核]}/月萤虫.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/晶核(8种) => Z获得物品与交互名称不一致/[晶核]}/水晶蝶.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/晶核(8种) => Z获得物品与交互名称不一致/[晶核]}/火晶蝶.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/晶核(8种) => Z获得物品与交互名称不一致/[晶核]}/草晶蝶.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/晶核(8种) => Z获得物品与交互名称不一致/[晶核]}/雷晶蝶.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/晶核(8种) => Z获得物品与交互名称不一致/[晶核]}/风晶蝶.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/蜥蜴尾巴(5种) => Z获得物品与交互名称不一致/[蜥蜴尾巴]}/嗜髓蜥.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/蜥蜴尾巴(5种) => Z获得物品与交互名称不一致/[蜥蜴尾巴]}/红角蜥.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/蜥蜴尾巴(5种) => Z获得物品与交互名称不一致/[蜥蜴尾巴]}/绿角蜥.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/蜥蜴尾巴(5种) => Z获得物品与交互名称不一致/[蜥蜴尾巴]}/蓝角蜥.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/蜥蜴尾巴(5种) => Z获得物品与交互名称不一致/[蜥蜴尾巴]}/赤尾蜥.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材 => Z获得物品与交互名称不一致/[蝴蝶翅膀]}/蝴蝶.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/螃蟹(5种) => Z获得物品与交互名称不一致/[螃蟹]}/太阳蟹.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/螃蟹(5种) => Z获得物品与交互名称不一致/[螃蟹]}/将军蟹.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/螃蟹(5种) => Z获得物品与交互名称不一致/[螃蟹]}/海蓝蟹.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/螃蟹(5种) => Z获得物品与交互名称不一致/[螃蟹]}/薄红蟹.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/螃蟹(5种) => Z获得物品与交互名称不一致/[螃蟹]}/黃金蟹.png (100%) create mode 100644 repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[铁块]/固晶甲虫.png rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/青蛙(4种) => Z获得物品与交互名称不一致/[青蛙]}/丛林树蛙.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/青蛙(4种) => Z获得物品与交互名称不一致/[青蛙]}/泥蛙.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/青蛙(4种) => Z获得物品与交互名称不一致/[青蛙]}/蓝蛙.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/青蛙(4种) => Z获得物品与交互名称不一致/[青蛙]}/青蛙.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/鱼肉 => Z获得物品与交互名称不一致/[鱼肉]}/蓝鳍鲈鱼.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/鱼肉 => Z获得物品与交互名称不一致/[鱼肉]}/黃金鲈鱼.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/鱼肉 => Z获得物品与交互名称不一致/[鱼肉]}/黑背鲈鱼.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/鳅鳅宝玉(3种) => Z获得物品与交互名称不一致/[鳅鳅宝玉]}/晴天鳅鳅.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/鳅鳅宝玉(3种) => Z获得物品与交互名称不一致/[鳅鳅宝玉]}/落日鳅鳅.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{I素材/鳅鳅宝玉(3种) => Z获得物品与交互名称不一致/[鳅鳅宝玉]}/金鳅鳅.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/鳗肉(4种) => Z获得物品与交互名称不一致/[鳗肉]}/流沙鳗鳗.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/鳗肉(4种) => Z获得物品与交互名称不一致/[鳗肉]}/深海鳗鳗.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/鳗肉(4种) => Z获得物品与交互名称不一致/[鳗肉]}/藤纹陆鳗鳗.png (100%) rename repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/{J食材/鳗肉(4种) => Z获得物品与交互名称不一致/[鳗肉]}/赤鳍陆鳗鳗.png (100%) create mode 100644 repo/js/采集cd管理/assets/蒙德炉子.json create mode 100644 repo/js/采集cd管理/assets/调为夜晚.json create mode 100644 repo/js/采集cd管理/assets/调为白天.json delete mode 100644 repo/js/采集cd管理/pathing/占位.js diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/发光髓(3种)/吉光虫.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[发光髓]/吉光虫.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/发光髓(3种)/吉光虫.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[发光髓]/吉光虫.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/发光髓(3种)/燃素蜜虫.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[发光髓]/燃素蜜虫.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/发光髓(3种)/燃素蜜虫.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[发光髓]/燃素蜜虫.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/发光髓(3种)/萤火虫.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[发光髓]/萤火虫.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/发光髓(3种)/萤火虫.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[发光髓]/萤火虫.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/冰晶蝶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/冰晶蝶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/冰晶蝶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/冰晶蝶.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/岩晶蝶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/岩晶蝶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/岩晶蝶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/岩晶蝶.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/月萤虫.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/月萤虫.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/月萤虫.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/月萤虫.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/水晶蝶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/水晶蝶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/水晶蝶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/水晶蝶.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/火晶蝶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/火晶蝶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/火晶蝶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/火晶蝶.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/草晶蝶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/草晶蝶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/草晶蝶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/草晶蝶.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/雷晶蝶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/雷晶蝶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/雷晶蝶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/雷晶蝶.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/风晶蝶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/风晶蝶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/晶核(8种)/风晶蝶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[晶核]/风晶蝶.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/嗜髓蜥.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/嗜髓蜥.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/嗜髓蜥.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/嗜髓蜥.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/红角蜥.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/红角蜥.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/红角蜥.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/红角蜥.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/绿角蜥.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/绿角蜥.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/绿角蜥.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/绿角蜥.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/蓝角蜥.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/蓝角蜥.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/蓝角蜥.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/蓝角蜥.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/赤尾蜥.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/赤尾蜥.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蜥蜴尾巴(5种)/赤尾蜥.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蜥蜴尾巴]/赤尾蜥.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蝴蝶.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蝴蝶翅膀]/蝴蝶.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/蝴蝶.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[蝴蝶翅膀]/蝴蝶.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/太阳蟹.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/太阳蟹.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/太阳蟹.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/太阳蟹.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/将军蟹.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/将军蟹.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/将军蟹.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/将军蟹.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/海蓝蟹.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/海蓝蟹.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/海蓝蟹.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/海蓝蟹.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/薄红蟹.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/薄红蟹.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/薄红蟹.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/薄红蟹.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/黃金蟹.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/黃金蟹.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/螃蟹(5种)/黃金蟹.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[螃蟹]/黃金蟹.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[铁块]/固晶甲虫.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[铁块]/固晶甲虫.png new file mode 100644 index 0000000000000000000000000000000000000000..fd8b853308d66eddd8592ad855a701d846d7d1c1 GIT binary patch literal 1510 zcmVTWSYpkKHU7*r#Y}xJKUAmo}ot?RR=icMP{Xe};bRKr^ z+}}Cpd(QX#`;AQTe|`i&N&jX~q(H&>qDAvKxx-M?2Cy1MN@#B$U|?q-gn~N*AS0DN zuLQtf>SPs$50-THIG~cp4#CTZtU~aGBLL;65zT|^5`wGgYqNo%Dhim!o+7>08#iPmiC6%YY~(;NH=SV>aCX%)TFc)} zBOf_pGDHXSmjGu<=uQK6V?!L)?M_%8RRg8%p?uJqLq#KEV({JvF9QrdSn)3&1t?F{ zvJlt%iY19`?XQ0Xs?Uoq{SYWUc7JqGdLnXTU=Z2#Z20WG^Adun<|5bDIY25qCweD- z0!YQpf|Z|1!3P8ojU4bV&!AFQ)SXc%UrWU9YNTwf`%L8OwZw7J1RSZ9fzgc{dt>83 zkgo#!w)EM`myPO8IuCH)2w?DjTboA(Deuh+X&O1sgZBwIb<7=yf$p)Kl}yeV{kF*g zN#P>`tiI~O`aQNTybRLV8z1!*9QGYwh>H1^`+)Xr4m@b3MlHZkN5})4fs#RLhXw4( zteSt`gB1n$JQ%C5%~NpiA1*NTer*k>?|C1OQ*zxHwP8oS5XRr@R z3+7k~eqsiw+*=9ee+Fo3B)A4NGCEgU@Bc0_8mz1U)+X>55Tr6(h+Q~%6AW#yjf6d} z3PH1+h^&04ubTYsm3feGVt8-~t}x}u%870UPIvUNzqJ|8;_41+ea zJY>Lc{Zjb9z898WiNIy_$#iD_w)$}LW$7<|%B&XTSpjEvJQY75zQ2B~koJ(_7dl`z zJ(U%hAo%zrq_mCIYHb4V0}s1VV^|8)m=1Ao`eIa|Q$|;306L5RoW41@`-iq--T74B zQg7x}q48W4{!1-NkMB-vfBOMoRFLw1guzdOdI;M2sS`?6zF*SIF9Oc^$xHst!3lz} z;PPQCo>CL9Xz%?c9a+j4*tHF?3{rU#Fjep_qR{&=lBfsJIVI)K?%KF8m!m5W?mW~5 za~o*ZLwox-kXo`YIt*@2Q)||*k2#nK{lEJAxm;sMv8;P^QtUH}*?!N14}aAMaR{y~ zW)94!#vvY0LkAQ}Q9j;zF)H2RgGQS_2gF~#db*?iO@(OtUw6ySM}Hh>5x6$rji1h6 zdaIqeAnApZLM2+2-Z2+@G7QpxZ#E1}ZaIi9{|Xg?wYBh7GF$|ZfdTt(d$yMLy?eZn zcK_`2$+TO(l+AnZ^eb2s5qRepO&J~u!F6pdl5-S-<&2V5-(^Ri5eFGRVywX`X4B(m z7Sk;egG+r!+O2l2MtNNt`N}-jQ0(KyB`LR)-4xC;k=(WdWbA9k7-JdBST<7K7uCh& zV?PQKPGWF-$1{C%cN}d+7mlezm@4RM4Tito#o31TdK%9vEo}HLnI3Z6tyZhG(vpWa z?3;c&2`UCR_4*BiqWM}x;q47D_~)iK!_a=f(UsSI(dfb_QA4H|K+xvDJKavV({;O@ zuDj;C-LC7pa`r1*ANjkVquqV2z;|!y!xO;DZ=&$r#rI}UMsj}h+$qg&8~-D-@M~0% z_>On=2VT$fJkRTSo}bA5Y4M&5>4ie30BcJDZT{*tAF#(Fa=muTRH8!g-RnVa(K&as zdS*3DKv)VAUz9;J3)NJ+t7&0O{f|G8ZoCrY2calZbdYf(_uB*Dzj7)t?h^decK`qY M07*qoM6N<$g7duKr~m)} literal 0 HcmV?d00001 diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/青蛙(4种)/丛林树蛙.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[青蛙]/丛林树蛙.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/青蛙(4种)/丛林树蛙.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[青蛙]/丛林树蛙.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/青蛙(4种)/泥蛙.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[青蛙]/泥蛙.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/青蛙(4种)/泥蛙.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[青蛙]/泥蛙.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/青蛙(4种)/蓝蛙.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[青蛙]/蓝蛙.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/青蛙(4种)/蓝蛙.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[青蛙]/蓝蛙.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/青蛙(4种)/青蛙.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[青蛙]/青蛙.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/青蛙(4种)/青蛙.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[青蛙]/青蛙.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鱼肉/蓝鳍鲈鱼.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鱼肉]/蓝鳍鲈鱼.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鱼肉/蓝鳍鲈鱼.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鱼肉]/蓝鳍鲈鱼.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鱼肉/黃金鲈鱼.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鱼肉]/黃金鲈鱼.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鱼肉/黃金鲈鱼.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鱼肉]/黃金鲈鱼.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鱼肉/黑背鲈鱼.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鱼肉]/黑背鲈鱼.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鱼肉/黑背鲈鱼.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鱼肉]/黑背鲈鱼.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/鳅鳅宝玉(3种)/晴天鳅鳅.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳅鳅宝玉]/晴天鳅鳅.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/鳅鳅宝玉(3种)/晴天鳅鳅.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳅鳅宝玉]/晴天鳅鳅.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/鳅鳅宝玉(3种)/落日鳅鳅.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳅鳅宝玉]/落日鳅鳅.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/鳅鳅宝玉(3种)/落日鳅鳅.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳅鳅宝玉]/落日鳅鳅.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/鳅鳅宝玉(3种)/金鳅鳅.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳅鳅宝玉]/金鳅鳅.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/I素材/鳅鳅宝玉(3种)/金鳅鳅.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳅鳅宝玉]/金鳅鳅.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鳗肉(4种)/流沙鳗鳗.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳗肉]/流沙鳗鳗.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鳗肉(4种)/流沙鳗鳗.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳗肉]/流沙鳗鳗.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鳗肉(4种)/深海鳗鳗.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳗肉]/深海鳗鳗.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鳗肉(4种)/深海鳗鳗.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳗肉]/深海鳗鳗.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鳗肉(4种)/藤纹陆鳗鳗.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳗肉]/藤纹陆鳗鳗.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鳗肉(4种)/藤纹陆鳗鳗.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳗肉]/藤纹陆鳗鳗.png diff --git a/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鳗肉(4种)/赤鳍陆鳗鳗.png b/repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳗肉]/赤鳍陆鳗鳗.png similarity index 100% rename from repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/J食材/鳗肉(4种)/赤鳍陆鳗鳗.png rename to repo/js/采集cd管理/assets/targetItems/其余材料图-实用版/Z获得物品与交互名称不一致/[鳗肉]/赤鳍陆鳗鳗.png diff --git a/repo/js/采集cd管理/assets/蒙德炉子.json b/repo/js/采集cd管理/assets/蒙德炉子.json new file mode 100644 index 000000000..52850589b --- /dev/null +++ b/repo/js/采集cd管理/assets/蒙德炉子.json @@ -0,0 +1,115 @@ +{ + "config": { + "realtime_triggers": { + "AutoPick": true + } + }, + "farming_info": { + "allow_farming_count": false, + "duration_seconds": 0, + "elite_details": "", + "elite_mob_count": 0, + "normal_mob_count": 0, + "primary_target": "", + "total_mora": 0 + }, + "info": { + "authors": [ + { + "links": "https://github.com/jiangziyanowo", + "name": "江紫烟owo" + } + ], + "bgi_version": "0.45.0", + "description": "蒙德做菜炉子", + "enable_monster_loot_split": false, + "items": [], + "last_modified_time": 1758891203475, + "map_match_method": "", + "map_name": "Teyvat", + "name": "每周做菜", + "order": 0, + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "", + "id": 1, + "move_mode": "run", + "point_ext_params": { + "description": "", + "enable_monster_loot_split": false, + "misidentification": { + "arrival_time": 0, + "handling_mode": "previousDetectedPoint", + "type": [ + "unrecognized" + ] + } + }, + "type": "teleport", + "x": -874.724609375, + "y": 2276.950439453125 + }, + { + "action": "", + "id": 2, + "move_mode": "run", + "point_ext_params": { + "description": "", + "enable_monster_loot_split": false, + "misidentification": { + "arrival_time": 0, + "handling_mode": "previousDetectedPoint", + "type": [ + "unrecognized" + ] + } + }, + "type": "path", + "x": -881.150390625, + "y": 2268.43115234375 + }, + { + "action": "", + "id": 3, + "move_mode": "run", + "point_ext_params": { + "description": "", + "enable_monster_loot_split": false, + "misidentification": { + "arrival_time": 0, + "handling_mode": "previousDetectedPoint", + "type": [ + "unrecognized" + ] + } + }, + "type": "path", + "x": -887.611328125, + "y": 2256.250244140625 + }, + { + "action": "", + "action_params": "", + "id": 4, + "move_mode": "run", + "point_ext_params": { + "description": "", + "enable_monster_loot_split": false, + "misidentification": { + "arrival_time": 0, + "handling_mode": "previousDetectedPoint", + "type": [ + "unrecognized" + ] + } + }, + "type": "target", + "x": -883.83203125, + "y": 2242.46826171875 + } + ] +} \ No newline at end of file diff --git a/repo/js/采集cd管理/assets/调为夜晚.json b/repo/js/采集cd管理/assets/调为夜晚.json new file mode 100644 index 000000000..5bab68057 --- /dev/null +++ b/repo/js/采集cd管理/assets/调为夜晚.json @@ -0,0 +1,31 @@ +{ + "info": { + "authors": [ + { + "links": "", + "name": "mno" + } + ], + "bgi_version": "0.45.0", + "description": "", + "enable_monster_loot_split": false, + "last_modified_time": 1765207268104, + "map_match_method": "", + "map_name": "Teyvat", + "name": "调为夜晚", + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "set_time", + "action_params": "18:00", + "id": 1, + "move_mode": "walk", + "type": "orientation", + "x": 0, + "y": 0 + } + ] +} \ No newline at end of file diff --git a/repo/js/采集cd管理/assets/调为白天.json b/repo/js/采集cd管理/assets/调为白天.json new file mode 100644 index 000000000..7fe4d442e --- /dev/null +++ b/repo/js/采集cd管理/assets/调为白天.json @@ -0,0 +1,31 @@ +{ + "info": { + "authors": [ + { + "links": "", + "name": "mno" + } + ], + "bgi_version": "0.45.0", + "description": "", + "enable_monster_loot_split": false, + "last_modified_time": 1765207268104, + "map_match_method": "", + "map_name": "Teyvat", + "name": "调为白天", + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "set_time", + "action_params": "6:00", + "id": 1, + "move_mode": "walk", + "type": "orientation", + "x": 0, + "y": 0 + } + ] +} \ No newline at end of file diff --git a/repo/js/采集cd管理/main.js b/repo/js/采集cd管理/main.js index 1e20b4609..762873691 100644 --- a/repo/js/采集cd管理/main.js +++ b/repo/js/采集cd管理/main.js @@ -4,7 +4,8 @@ let targetItems = []; let blacklist = []; let blacklistSet = new Set(); let gameRegion; -let state = { running: true, currentPathing: null }; +let state = { running: true }; +state.runPickupLog = []; // 本次路线运行中拾取/交互的物品明细 const rollingDelay = 32; const pickupDelay = 100; const timeMoveUp = Math.round((settings.timeMove || 1000) * 0.45); @@ -32,6 +33,14 @@ const userSettings = { disableJsons: settings.disableJsons || "" }; +let ingredientProcessingFood = settings.ingredientProcessingFood; +let foodCounts = settings.foodCount; + +let firstCook = true; +let firstsettime = true; +let lastCookTime = new Date(); +let lastsettimeTime = new Date(); + // 解析禁用名单 let disableArray = []; if (userSettings.disableJsons) { @@ -77,6 +86,15 @@ if (findFInterval > 200) { let lastRoll = new Date(); let checkDelay = Math.round(findFInterval / 2); +let Foods = []; +let foodCount = []; + +const FiconRo = RecognitionObject.TemplateMatch(fIcontemplate, 1102, 335, 34, 400); +FiconRo.Threshold = 0.95; +FiconRo.InitTemplate(); + +const mainUiRo = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150); + (async function () { /* ===== 零基构建 settings.json(BEGIN) ===== */ const SETTINGS_FILE = `settings.json`; @@ -120,6 +138,11 @@ let checkDelay = Math.round(findFInterval / 2); type: "input-text", label: "需要生成几个路径组配置(1-99)", default: "3" + }, + { + name: "enableMoreSettings", + type: "checkbox", + label: "勾选后下次运行展开高级设置\n用于进行路线筛选和排序" } ); @@ -156,11 +179,66 @@ let checkDelay = Math.round(findFInterval / 2); "type": "input-text", "label": "识别间隔(毫秒)\n两次检测f图标之间等待时间", "default": "100" + }, + { + "name": "ingredientProcessingFood", + "type": "input-text", + "label": "料理/食材名称\n建议料理和食材分开填写" + }, + { + "name": "foodCount", + "type": "input-text", + "label": "料理/食材数量\n数量对应上方的食材" + }, + { + "name": "setTimeMode", + "type": "select", + "label": "尝试调节时间来获得移速加成\n队伍中含迪希雅、嘉明或塔利雅时选择白天\n队伍中含罗莎莉亚时选择夜晚", + "options": [ + "不调节时间", + "尽量调为白天", + "尽量调为夜晚" + ], + "default": "不调节时间" } ); + if (settings.enableMoreSettings) { + newSettings.push( + { + "name": "priorityTags", + "type": "input-text", + "label": "优先关键词,文件名或拾取材料含关键词的路线会被视为最高效率\n不同关键词使用【中文逗号】分隔" + }, + { + "name": "sortMode", + "type": "select", + "label": "选择同组路线排序模式", + "options": [ + "文件顺序,按在文件夹中位置顺序运行", + "优先最早刷新,将优先执行最早刷新的路线", + "优先最高效率,将优先执行最高分均拾取物的路线" + ], + "default": "文件顺序,按在文件夹中位置顺序运行" + }, + { + "name": "weightedRule", + "type": "input-text", + "label": "加权规则,允许将特定物品视为多倍计算效率\n黑名单物品将自动视为0\n格式如下:\n物品名称*权重\n使用【中文逗号】分隔\n如:甜甜花*2,树莓*0" + } + ); + } + /* 5.4 路径组节点(整体移到最后) */ for (let g = 1; g <= groupCount; g++) { + /* 文件夹 */ + newSettings.push({ + name: `pathGroup${g}FolderName`, + type: "select", + label: `##############################################################################\n选择路径组${g}文件夹(pathing下第一层)`, + options: ["", ...uniqueDirs] + }); + /* CD类型 */ newSettings.push({ name: `pathGroup${g}CdType`, @@ -178,20 +256,21 @@ let checkDelay = Math.round(findFInterval / 2); ] }); - /* 文件夹 */ - newSettings.push({ - name: `pathGroup${g}FolderName`, - type: "select", - label: `选择路径组${g}文件夹(pathing下第一层)`, - options: ["", ...uniqueDirs] - }); - /* 队伍名 */ newSettings.push({ name: `pathGroup${g}PartyName`, type: "input-text", label: `输入路径组${g}使用配队名称` }); + + if (settings.enableMoreSettings) { + newSettings.push({ + "name": `pathGroup${g}thresholdEfficiency`, + "type": "input-text", + "label": `路径组${g}临界效率\n分均拾取个数效率低于临界效率的路线会被排除\n无历史记录或历史记录少于3次的路线会被视为恰好处于临界效率`, + "default": "0" + }); + } } /* 6. 一次性写入 & 日志 */ @@ -217,19 +296,25 @@ let checkDelay = Math.round(findFInterval / 2); } // 获取子文件夹路径 - const subFolderName = userSettings.infoFileName; // 使用设置后的 infoFileName + const subFolderName = userSettings.infoFileName; const subFolderPath = `${recordFolder}/${subFolderName}`; // 读取子文件夹中的所有文件路径 const filesInSubFolder = file.ReadPathSync(subFolderPath); - // 检查record.txt文件是否存在 + // 检查优先顺序:record.json > record.txt let indexDoExist = false; + let useJson = false; for (const filePath of filesInSubFolder) { - const fileName = basename(filePath); // 提取文件名 + const fileName = basename(filePath); + if (fileName === "record.json") { + indexDoExist = true; + useJson = true; + break; + } if (fileName === "record.txt") { indexDoExist = true; - break; + useJson = false; } } @@ -237,8 +322,9 @@ let checkDelay = Math.round(findFInterval / 2); log.info("重新生成索引文件模式,将覆盖现有索引文件"); } if (!indexDoExist) { - log.info("record.txt 文件不存在,将尝试生成索引文件"); + log.info("文件不存在,将尝试生成索引文件"); } + /* 禁用BGI原生拾取,强制模板匹配 */ targetItems = await loadTargetItems(); await loadBlacklist(true); @@ -246,263 +332,350 @@ let checkDelay = Math.round(findFInterval / 2); await fakeLog("采集cd管理", true, false, 1000); - // 统一的 record.txt 文件路径 - const recordFilePath = `${subFolderPath}/record.txt`; + // 统一的 record.json 文件路径 + const recordFilePath = `${subFolderPath}/record.json`; // 读取 pathing 文件夹下的所有 .json 文件 const pathingFolder = "pathing"; const files = await readFolder(pathingFolder, true); const filePaths = files.map(file => file.fullPath); - // 用于存储符合条件的文件名的数组 - const jsonFileNames = []; - const entryMap = {}; - - // 如果 record.txt 文件存在,则读取对应的原文件 - if (indexDoExist) { - let pathGroupContent = await file.readText(recordFilePath); - let pathGroupEntries = pathGroupContent.trim().split('\n'); - - // 创建一个对象来存储 entryName 和 entryTimestamp 的映射 - for (let j = 0; j < pathGroupEntries.length; j++) { - const entryWithTimestamp = pathGroupEntries[j].trim(); - const [entryName, entryTimestamp] = entryWithTimestamp.split('::'); - entryMap[entryName] = entryTimestamp; - } + // ① 先加载已有记录(整对象) + let recordArray = []; + if (indexDoExist && useJson) { + try { recordArray = JSON.parse(await file.readText(recordFilePath)); } catch (e) { } + } else if (indexDoExist && !useJson) { + try { + const txt = await file.readText(`${subFolderPath}/record.txt`); + txt.trim().split('\n').forEach(line => { + const [n, t] = line.trim().split('::'); + if (n && t) recordArray.push({ fileName: n + '.json', cdTime: t }); + }); + } catch (e) { } } - // 遍历文件路径数组并提取文件名 + // ② 建 Map 确保 history 存在 + const existMap = new Map(recordArray.map(it => [it.fileName, { + ...it, + history: it.history || [] // 补空数组 + }])); + + // ③ 对 pathing 里存在的路线:只更新 cdTime,其余保留 + const defaultTime = "1970/1/1 08:00:00"; for (const filePath of filePaths) { - const fileName = basename(filePath); // 提取文件名 - if (fileName.endsWith('.json')) { // 检查文件名是否以 .json 结尾 - const fileNameWithoutSuffix = removeJsonSuffix(fileName); // 移除 .json 后缀 + const fileName = basename(filePath); + if (!fileName.endsWith('.json')) continue; - // 给 routeTimeStamp 赋值为 defaultTimeStamp - let routeTimeStamp = defaultTimeStamp; + const old = existMap.get(fileName) || {}; + const newCd = (indexDoExist && + userSettings.operationMode !== "重新生成索引文件(用于强制刷新CD)" && + old.cdTime) + ? old.cdTime + : defaultTime; - if (indexDoExist && userSettings.operationMode !== "重新生成索引文件(用于强制刷新CD)" && entryMap[fileNameWithoutSuffix]) { - routeTimeStamp = entryMap[fileNameWithoutSuffix]; - } - - routeTimeStamp = `::${routeTimeStamp}`; - // 添加时间戳并存储 - jsonFileNames.push(`${fileNameWithoutSuffix}${routeTimeStamp}`); - } + existMap.set(fileName, { + ...old, // 保留所有旧字段 + fileName, + cdTime: newCd, + history: old.history || [] // 确保有 history + }); } - // 如果没有找到符合条件的文件,跳过当前路径组 - if (jsonFileNames.length === 0) { - log.info(`未找到符合条件的 .json 文件,record.txt 将为空`); - } - - // 将文件名数组转换为字符串,每个文件名占一行 - const fileNamesContent = jsonFileNames.join("\n"); - - // 将文件名写入记录文件 - const writeResult = file.writeTextSync(recordFilePath, fileNamesContent); + // ④ 写回(含已消失的路线) + const writeResult = file.writeTextSync(recordFilePath, + JSON.stringify(Array.from(existMap.values()), null, 2)); if (writeResult) { - log.info(`文件名已成功写入: ${recordFilePath}`); + log.info(`信息已成功写入: ${recordFilePath}`); } else { log.error(`写入文件失败: ${recordFilePath}`); } - { - // 循环处理多个路径组 - for (let i = 1; i <= groupCount; i++) { - const currentCdType = settings[`pathGroup${i}CdType`] || ""; - if (!currentCdType) continue; // 跳过本组 + if (typeof ingredientProcessingFood === 'string' && ingredientProcessingFood.trim()) { + Foods = ingredientProcessingFood + .split(/[,,;;\s]+/) // 支持中英文逗号、分号、空格 + .map(word => word.trim()) + .filter(word => word.length > 0); + } - const folder = folderNames[i - 1] || `路径组${i}`; - const targetFolder = `pathing/${folder}`; + if (typeof foodCounts === 'string' && foodCounts.trim()) { + foodCount = foodCounts + .split(/[,,;;\s]+/) + .map(word => word.trim()) + .filter(word => word.length > 0); + } - // 读取统一的 record.txt 文件内容 - let recordContent = await file.readText(recordFilePath); - let recordEntries = recordContent.trim().split('\n'); + let cookInterval = 60 * 60 * 1000; + let settimeInterval = 10 * 60 * 1000; - // 创建一个对象来存储 entryName 和 entryTimestamp 的映射 - const entryMap = {}; - for (let j = 0; j < recordEntries.length; j++) { - const entryWithTimestamp = recordEntries[j].trim(); - const [entryName, entryTimestamp] = entryWithTimestamp.split('::'); - entryMap[entryName] = entryTimestamp; - } + // ==================== 路径组循环 ==================== + for (let i = 1; i <= groupCount; i++) { + const currentCdType = settings[`pathGroup${i}CdType`] || ""; + if (!currentCdType) continue; - // 读取路径组文件夹中的任务文件 - const files = await readFolder(targetFolder, true); + const folder = folderNames[i - 1] || `路径组${i}`; + const targetFolder = `pathing/${folder}`; - if (userSettings.operationMode === "执行任务(若不存在索引文件则自动创建)") { - let groupNumber = i; - await genshin.returnMainUi(); + /* 运行期同样用 Map 只改 cdTime */ + const rawRecord = await file.readText(recordFilePath); + let recordArray = JSON.parse(rawRecord); + const cdMap = new Map(recordArray.map(it => [it.fileName, it])); - try { - const filePaths = files.map(file => file.fullPath); + const groupFiles = await readFolder(targetFolder, true); - // 读取 record.txt 文件内容 - let recordContent = await file.readText(recordFilePath); - let recordEntries = recordContent.trim().split('\n'); - let changedParty = false; + if (userSettings.operationMode === "执行任务(若不存在索引文件则自动创建)") { + const groupNumber = i; + await genshin.returnMainUi(); - for (const filePath of filePaths) { - const fileName = basename(filePath).replace('.json', ''); - const entry = recordEntries.find(e => e.startsWith(`${fileName}::`)); - const entryTimestamp = entry ? entry.split('::')[1] : null; - const entryDate = entryTimestamp ? new Date(entryTimestamp) : new Date(0); // 未记录的任务视为已刷新 + try { + const filePaths = groupFiles.map(f => f.fullPath); + let changedParty = false; - const startTime = new Date(); - if (startTime <= entryDate) { - log.info(`当前任务 ${fileName} 未刷新,跳过任务`); - continue; // 跳过当前任务 - } + /* ================== 提前计算分均效率(所有模式通用) ================== */ + // 0) 解析优先关键词 + const priorityKeywords = settings.priorityTags + ? settings.priorityTags.split(',').map(s => s.trim()).filter(Boolean) + : []; - if (await isTimeRestricted(settings.timeRule, 10)) { - break; - } - - let doSkip = false; - for (const keyword of disableArray) { - if (filePath.includes(keyword)) { - log.info(`路径文件 ${filePath} 包含禁用关键词 "${keyword}",跳过任务 ${fileName}`); - doSkip = true; - break; + // 1) 解析加权规则 + const weightMap = new Map(); + if (settings.weightedRule) { + settings.weightedRule + .split(',') + .map(s => s.trim()) + .forEach(rule => { + const [item, wStr] = rule.split('*'); + if (item && wStr) { + const w = Number(wStr); + weightMap.set(item, isNaN(w) ? 1 : w); } - } - if (doSkip) continue; + }); + } - // 切换到指定配队 - if (!changedParty) { - await switchPartyIfNeeded(partyNames[groupNumber - 1]); - changedParty = true; - } - // 伪造地图追踪开始的日志 - await fakeLog(fileName, false, true, 0); + // 2) 先计算一次基础效率,并找出全局最大效率 + filePaths.forEach(p => { + const fullName = basename(p); + const obj = cdMap.get(fullName); + let avgEff = 0; - /* 并发拾取:与路线任务同生命周期 */ - state.currentPathing = { items: [] }; - state.running = true; - const pickupTask = recognizeAndInteract(); // 直接并发,无需包裹函数 + if (obj && obj.history && obj.history.length >= 3) { + const effList = obj.history.map(log => { + const total = Object.entries(log.items).reduce((sum, [name, cnt]) => { + const w = blacklistSet.has(name) ? 0 : (weightMap.get(name) ?? 1); + return sum + cnt * w; + }, 0); + return (total / log.durationSec) * 60; + }); + avgEff = effList.reduce((a, b) => a + b, 0) / effList.length; + } else { + const threshold = Number(settings[`pathGroup${i}thresholdEfficiency`]) || 0; + avgEff = threshold; + } + p._efficiency = avgEff; // 先存基础值 + }); - // 日志输出当前任务信息 - log.info(`当前进度:路径组${i} ${folder} ${fileName} 为第 ${filePaths.indexOf(filePath) + 1}/${filePaths.length} 个`); + // 3) 计算全局最大效率值 + const maxEff = Math.max(...filePaths.map(p => p._efficiency), 0); - // 执行路径文件 - try { - await pathingScript.runFile(filePath); - log.info(`执行任务: ${fileName}`); - } catch (error) { - log.error(`路径文件 ${filePath} 不存在或执行失败: ${error}`); - continue; // 跳过当前任务 - } + // 4) 优先关键词加分 + filePaths.forEach(p => { + const fullName = basename(p); + const obj = cdMap.get(fullName); - // 捕获任务取消的信息并跳出循环 - try { - await sleep(1); - } catch (error) { - log.error(`发生错误: ${error}`); - break; // 终止循环 - } + // 4-1) 历史拾取物里是否含关键词 + const itemHit = obj?.history?.some(log => + Object.keys(log.items).some(item => + priorityKeywords.some(key => item.includes(key)) + ) + ); - // 获取结束时间 - const endTime = new Date(); + // 4-2) 文件路径(含文件名)是否含关键词 + const pathHit = priorityKeywords.some(key => p.includes(key)); - // 比较开始时间与结束时间 - const timeDiff = endTime.getTime() - startTime.getTime(); // 时间差(毫秒) + if (itemHit || pathHit) { + p._efficiency += maxEff; // 把最大效率值直接加给它 + } + }); - // 伪造地图追踪结束的日志 - await fakeLog(fileName, false, false, timeDiff); - state.running = false; // 停止拾取 - await pickupTask; // 等待拾取线程结束 - if (timeDiff > 3000) { // 时间差大于3秒 - // 获取当前路径组的 cdtype - const currentCdType = pathGroupCdType[groupNumber - 1] || "未知类型"; + /* ================== 排序分支 ================== */ + switch (settings.sortMode) { + case "优先最早刷新,将优先执行最早刷新的路线": + filePaths.sort((a, b) => { + const nameA = basename(a); + const nameB = basename(b); + const timeA = cdMap.has(nameA) ? new Date(cdMap.get(nameA).cdTime) : new Date(0); + const timeB = cdMap.has(nameB) ? new Date(cdMap.get(nameB).cdTime) : new Date(0); + return timeA - timeB; // 越早刷新越靠前 + }); + break; - // 初始化 newTimestamp 和 nextAvailableTime - let newTimestamp; - let nextAvailableTime; + case "优先最高效率,将优先执行最高分均拾取物的路线": + // 直接复用提前算好的 _efficiency + filePaths.sort((a, b) => (b._efficiency || 0) - (a._efficiency || 0)); + break; - // 根据 cdtype 执行不同的操作 - switch (currentCdType) { - case "1次0点刷新": - // 将任务文件中对应的时间戳改为下一个0点 - const tomorrow = new Date(startTime.getTime() + 24 * 60 * 60 * 1000); - tomorrow.setHours(0, 0, 0, 0); // 设置为下一个0点 - newTimestamp = tomorrow.toISOString(); - nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式 - break; + default: + // 保持原有顺序,不做任何排序 + break; + } - case "2次0点刷新": - // 将任务文件中对应的时间戳改为下下个0点 - const dayAfterTomorrow = new Date(startTime.getTime() + 48 * 60 * 60 * 1000); - dayAfterTomorrow.setHours(0, 0, 0, 0); // 设置为下下个0点 - newTimestamp = dayAfterTomorrow.toISOString(); - nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式 - break; + for (const filePath of filePaths) { + const fileName = basename(filePath).replace('.json', ''); + const fullName = fileName + '.json'; + const targetObj = cdMap.get(fullName); + const nextCD = targetObj ? new Date(targetObj.cdTime) : new Date(0); - case "3次0点刷新": - // 将任务文件中对应的时间戳改为下下下个0点 - const twoDaysAfterTomorrow = new Date(startTime.getTime() + 72 * 60 * 60 * 1000); - twoDaysAfterTomorrow.setHours(0, 0, 0, 0); // 设置为下下下个0点 - newTimestamp = twoDaysAfterTomorrow.toISOString(); - nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式 - break; + const startTime = new Date(); + if (startTime <= nextCD) { + log.info(`当前任务 ${fileName} 未刷新,跳过任务`); + continue; // 跳过,不写回 + } + if (await isTimeRestricted(settings.timeRule, 10)) break; - case "4点刷新": - // 将任务文件中对应的时间戳改为下一个4点 - const next4AM = new Date(startTime.getTime()); - next4AM.setHours(4, 0, 0, 0); // 设置为当天的4点 - if (next4AM <= startTime) { - next4AM.setDate(next4AM.getDate() + 1); // 如果当前时间已过4点,则设置为下一天的4点 - } - newTimestamp = next4AM.toISOString(); - nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式 - break; - - case "12小时刷新": - // 将任务文件中对应的时间戳改为开始时间后12小时0分0秒 - newTimestamp = new Date(startTime.getTime() + 12 * 60 * 60 * 1000).toISOString(); - nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式 - break; - - case "24小时刷新": - // 将任务文件中对应的时间戳改为开始时间后24小时0分0秒 - newTimestamp = new Date(startTime.getTime() + 24 * 60 * 60 * 1000).toISOString(); - nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式 - break; - - case "46小时刷新": - // 将任务文件中对应的时间戳改为开始时间后46小时0分0秒 - newTimestamp = new Date(startTime.getTime() + 46 * 60 * 60 * 1000).toISOString(); - nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式 - break; - - default: - log.warn(`路径组${groupNumber} 的 cdtype 是 ${currentCdType},执行默认操作`); - // 默认操作:将下一个可用时间设置为开始时间 - newTimestamp = startTime.toISOString(); - nextAvailableTime = startTime.toLocaleString(); // 转换为本地时间格式 - break; - } - - // 更新任务文件中的时间戳 - const updatedEntry = `${fileName}::${newTimestamp}`; - if (entry) { - recordEntries[recordEntries.indexOf(entry)] = updatedEntry; - } else { - recordEntries.push(updatedEntry); - } - - // 写回 record.txt - const updatedRecordContent = recordEntries.join('\n'); - await file.writeText(recordFilePath, updatedRecordContent); - log.info(`本任务执行大于3秒,cd信息已更新,下一次可用时间为 ${nextAvailableTime}`); + let doSkip = false; + for (const kw of disableArray) { + if (filePath.includes(kw)) { + log.info(`路径文件 ${filePath} 包含禁用关键词 "${kw}",跳过任务 ${fileName}`); + doSkip = true; break; } } - log.info(`路径组${groupNumber} 的所有任务运行完成`); - } catch (error) { - log.error(`读取路径组文件时出错: ${error}`); + if (doSkip) continue; + + // ===== 临界效率过滤 ===== + const routeEff = filePath._efficiency ?? 0; // 提前算好的分均效率 + const threshold = Number(settings[`pathGroup${i}thresholdEfficiency`]) || 0; + if (routeEff < threshold) { + log.info(`路线 ${fileName} 分均效率为 ${routeEff.toFixed(2)},低于设定的临界值 ${threshold},跳过`); + continue; + } + + let timeNow = new Date(); + if (Foods.length != 0 && (((timeNow - lastCookTime) > cookInterval) || firstCook)) { + firstCook = false; + await ingredientProcessing(); + lastCookTime = new Date(); + } + + if (settings.setTimeMode && settings.setTimeMode != "不调节时间" && (((timeNow - lastsettimeTime) > settimeInterval) || firstsettime)) { + firstsettime = false; + if (settings.setTimeMode === "尽量调为白天") { + await pathingScript.runFile("assets/调为白天.json"); + } else { + await pathingScript.runFile("assets/调为夜晚.json"); + } + lastsettimeTime = new Date(); + } + + if (!changedParty) { + await switchPartyIfNeeded(partyNames[groupNumber - 1]); + changedParty = true; + } + await fakeLog(fileName, false, true, 0); + + /* ========== 历史拾取物前置排序 ========== */ + // 0) 只有 history 里出现过的物品才需要前置 + const historyItemSet = new Set(); + const routeRec = cdMap.get(fullName); + if (routeRec?.history) { + routeRec.history.forEach(log => { + Object.keys(log.items).forEach(name => historyItemSet.add(name)); + }); + } + + // 1) 把 targetItems 拆成「历史出现」+「未出现」两部分 + const frontPart = []; + const backPart = []; + for (const it of targetItems) { + (historyItemSet.has(it.itemName) ? frontPart : backPart).push(it); + } + + // 2) 合并后重新赋值,完成前置 + targetItems = [...frontPart, ...backPart]; + /* ======================================= */ + + state.running = true; + const pickupTask = recognizeAndInteract(); + + log.info(`当前进度:路径组${i} ${folder} ${fileName} 为第 ${filePaths.indexOf(filePath) + 1}/${filePaths.length} 个`); + + try { + state.runPickupLog = []; // 新路线开始前清空 + await pathingScript.runFile(filePath); + } catch (error) { + log.error(`路径文件 ${filePath} 不存在或执行失败: ${error}`); + continue; + } + + try { await sleep(1); } + catch (error) { log.error(`发生错误: ${error}`); break; } + + const endTime = new Date(); + const timeDiff = endTime.getTime() - startTime.getTime(); + + await fakeLog(fileName, false, false, timeDiff); + state.running = false; + await pickupTask; + + // >>> 仅当 >3s 才更新 CD 并立即写回整条记录(含 history) <<< + if (timeDiff > 3000) { + let newTimestamp = new Date(startTime); + + switch (currentCdType) { + case "1次0点刷新": + newTimestamp.setDate(newTimestamp.getDate() + 1); + newTimestamp.setHours(0, 0, 0, 0); break; + case "2次0点刷新": + newTimestamp.setDate(newTimestamp.getDate() + 2); + newTimestamp.setHours(0, 0, 0, 0); break; + case "3次0点刷新": + newTimestamp.setDate(newTimestamp.getDate() + 3); + newTimestamp.setHours(0, 0, 0, 0); break; + case "4点刷新": + newTimestamp.setHours(4, 0, 0, 0); + if (newTimestamp <= startTime) newTimestamp.setDate(newTimestamp.getDate() + 1); break; + case "12小时刷新": + newTimestamp = new Date(startTime.getTime() + 12 * 60 * 60 * 1000); break; + case "24小时刷新": + newTimestamp = new Date(startTime.getTime() + 24 * 60 * 60 * 1000); break; + case "46小时刷新": + newTimestamp = new Date(startTime.getTime() + 46 * 60 * 60 * 1000); break; + default: + newTimestamp = startTime; break; + } + + // ===== 把本次拾取明细写进 history ===== + const durationSec = Math.round(timeDiff / 1000); + const itemCounter = {}; + for (const name of state.runPickupLog) { + itemCounter[name] = (itemCounter[name] || 0) + 1; + } + const logEntry = { + items: itemCounter, + durationSec: durationSec + }; + if (!targetObj.history) targetObj.history = []; // 兜底 + targetObj.history.push(logEntry); + // 保留最多 7 条记录,超出的旧记录丢弃 + if (targetObj.history.length > 7) { + targetObj.history = targetObj.history.slice(-7); + } + + // 只改 cdTime,其余字段(含 history)保持 + targetObj.cdTime = newTimestamp.toISOString(); + await file.writeText(recordFilePath, + JSON.stringify(Array.from(cdMap.values()), null, 2)); + + // 清空本次记录 + state.runPickupLog = []; + + log.info(`本任务执行大于3秒,cd信息已更新,下一次可用时间为 ${newTimestamp.toLocaleString()}`); + } } + log.info(`路径组${groupNumber} 的所有任务运行完成`); + } catch (error) { + log.error(`读取路径组文件时出错: ${error}`); } } } @@ -511,7 +684,6 @@ let checkDelay = Math.round(findFInterval / 2); log.error(`操作失败: ${error}`); } - //伪造js开始的日志 await fakeLog("采集cd管理", true, true, 0); })(); @@ -519,12 +691,20 @@ let checkDelay = Math.round(findFInterval / 2); async function recognizeAndInteract() { let lastcenterYF = 0, lastItemName = "", thisMoveUpTime = 0, lastMoveDown = 0, blacklistCounter = 0; gameRegion = captureGameRegion(); + let lastCheckItemFull = new Date(); + let checkTask = null; + while (state.running) { + let time1 = new Date(); gameRegion.dispose(); gameRegion = captureGameRegion(); - if (++blacklistCounter % 33 === 0) { - await checkItemFullAndOCR(); + + if (new Date() - lastCheckItemFull > 2500 && !checkTask) { + lastCheckItemFull = new Date(); + checkTask = checkItemFullAndOCR(); } + + let time2 = new Date(); const centerYF = await findFIcon(); if (!centerYF) { if (await isMainUI()) { @@ -533,8 +713,14 @@ async function recognizeAndInteract() { lastRoll = new Date(); } } + if (checkTask) { + try { await checkTask; } + catch (e) { log.error('背包满检查异常:', e); } + finally { checkTask = null; } + } continue; } + let time3 = new Date(); let itemName = null; itemName = await performTemplateMatch(centerYF); if (itemName) { @@ -542,17 +728,30 @@ async function recognizeAndInteract() { await sleep(160); lastcenterYF = -20; lastItemName = null; + if (checkTask) { + try { await checkTask; } + catch (e) { log.error('背包满检查异常:', e); } + finally { checkTask = null; } + } continue; } if (!blacklistSet.has(itemName)) { keyPress("F"); log.info(`交互或拾取:"${itemName}"`); - if (state.currentPathing) { - state.currentPathing.items.push(itemName); - state.currentPathing.items = [...new Set(state.currentPathing.items)].slice(-20); + let time4 = new Date(); + /* >>> 提到最前 begin >>> */ + const idx = targetItems.findIndex(it => it.itemName === itemName); + if (idx > 0) { + const [it] = targetItems.splice(idx, 1); + targetItems.unshift(it); } + /* <<< 提到最前 end <<< */ + state.runPickupLog.push(itemName); + lastcenterYF = centerYF; lastItemName = itemName; + let time5 = new Date(); + //log.info(`调试-截图用时${time2 - time1},找f用时${time3 - time2},匹配用时${time4 - time3},后处理用时${time5 - time4}`); await sleep(pickupDelay); } } else { @@ -570,17 +769,18 @@ async function recognizeAndInteract() { await keyMouseScript.runFile(`assets/滚轮上翻.json`); } await sleep(rollingDelay); + if (checkTask) { + try { await checkTask; } + catch (e) { log.error('背包满检查异常:', e); } + finally { checkTask = null; } + } } } async function findFIcon() { - const roi = RecognitionObject.TemplateMatch(fIcontemplate, 1102, 335, 34, 400); - roi.Threshold = 0.95; - roi.InitTemplate(); try { - const r = gameRegion.find(roi); + const r = gameRegion.find(FiconRo); if (r.isExist()) return Math.round(r.y + r.height / 2); - } catch (e) { log.error(`findFIcon:${e.message}`); } @@ -589,28 +789,37 @@ async function findFIcon() { } async function performTemplateMatch(centerYF) { + /* 一次性切 6 种宽度(0-5 汉字) */ + const regions = []; + for (let cn = 0; cn <= 6; cn++) { // 0~5 共 6 档 + const w = 12 + 28 * Math.min(cn, 5) + 2; + regions[cn] = gameRegion.DeriveCrop(1219, centerYF - 15, w, 30); + } + try { for (const it of targetItems) { - const cnLen = Math.min([...it.itemName].filter(c => c >= '\u4e00' && c <= '\u9fff').length, 5); - const roi = RecognitionObject.TemplateMatch(it.template, 1219, centerYF - 15, 12 + 28 * cnLen + 2, 30); - roi.Threshold = it.Threshold; - roi.InitTemplate(); - if (gameRegion.find(roi).isExist()) { + const cnLen = Math.min( + [...it.itemName].filter(c => c >= '\u4e00' && c <= '\u9fff').length, + 5 + ); // 0-5 + + if (regions[cnLen].find(it.roi).isExist()) { return it.itemName; } } } catch (e) { - log.error(`performTemplateMatch:${e.message}`); + log.error(`performTemplateMatch: ${e.message}`); + } finally { + regions.forEach(r => r.dispose()); } return null; } async function isMainUI() { - const roi = RecognitionObject.TemplateMatch(mainUITemplate, 0, 0, 150, 150); for (let i = 0; i < 1 && state.running; i++) { if (!gameRegion) gameRegion = captureGameRegion(); try { - if (gameRegion.find(roi).isExist()) { + if (gameRegion.find(mainUiRo).isExist()) { return true; } } catch (e) { @@ -657,9 +866,15 @@ async function checkItemFullAndOCR() { } const ratioMap = new Map(); for (const it of targetItems) { - const ratio = calcMatchRatio(it.itemName.replace(/[^\u4e00-\u9fa5]/g, ''), ocrText); - if (ratio > 0.75) { - ratioMap.set(it.itemName, ratio); + const candNames = [it.itemName, ...(it.otherName || [])]; + let maxRatioThisItem = 0; + for (const name of candNames) { + const ratio = calcMatchRatio(name.replace(/[^\u4e00-\u9fa5]/g, ''), ocrText); + if (ratio > maxRatioThisItem) maxRatioThisItem = ratio; + } + if (maxRatioThisItem > 0.75) { + const oldMax = ratioMap.get(it.itemName) || 0; + if (maxRatioThisItem > oldMax) ratioMap.set(it.itemName, maxRatioThisItem); } } if (ratioMap.size === 0) return; @@ -675,29 +890,38 @@ async function checkItemFullAndOCR() { // 加载拾取物图片 async function loadTargetItems() { - let targetItemPath; - - targetItemPath = "assets/targetItems/"; + const targetItemPath = "assets/targetItems/"; const items = await readFolder(targetItemPath, false); - // 统一预加载模板 for (const it of items) { try { it.template = file.ReadImageMatSync(it.fullPath); it.itemName = it.fileName.replace(/\.png$/i, ''); + it.roi = RecognitionObject.TemplateMatch(it.template); - // 新增:解析括号中的阈值 - const match = it.fullPath.match(/[((](.*?)[))]/); // 匹配英文或中文括号 - if (match) { - const val = parseFloat(match[1]); - it.Threshold = (!isNaN(val) && val >= 0 && val <= 1) ? val : 0.85; - } else { - it.Threshold = 0.85; + /* ---------- 1. 解析小括号阈值 ---------- */ + const match = it.fullPath.match(/[((](.*?)[))]/); + const itsThreshold = (match => { + if (!match) return 0.85; + const v = parseFloat(match[1]); + return !isNaN(v) && v >= 0 && v <= 1 ? v : 0.85; + })(match); + it.roi.Threshold = itsThreshold; + it.roi.InitTemplate(); + + /* ---------- 2. 解析中括号内容 ---------- */ + const otherNames = []; + const bracketMatch = it.fullPath.matchAll(/\[(.*?)\]/g); + for (const m of bracketMatch) { + if (m[1].trim()) otherNames.push(m[1].trim()); } - } catch (error) { } - } + it.otherName = otherNames; // 始终返回数组,无则为空数组 + } catch (error) { + log.error(`[loadTargetItems] ${it.fullPath}: ${error.message}`); + } + } return items; } @@ -973,3 +1197,280 @@ async function isTimeRestricted(timeRule, threshold = 5) { log.info("不处于限制时间"); return false; } + +/** +* 食材加工主函数,用于自动前往指定地点进行食材或料理的加工制作 +* +* 该函数会根据 Foods 和 foodCount 数组中的食材名称和数量,依次查找并制作对应的料理/食材 +* 支持两种类型:普通料理(需滚动查找)和调味品类食材(直接在“食材加工”界面查找) +* +* @returns {Promise} 无返回值,执行完所有加工流程后退出 +*/ +async function ingredientProcessing() { + /** +* 文字OCR识别封装函数(测试中,未封装完成,后续会优化逻辑) +* @param text 要识别的文字,默认为"空参数" +* @param timeout 超时时间,单位为秒,默认为10秒 +* @param afterBehavior 点击模式,0表示不点击,1表示点击识别到文字的位置,2表示输出模式,默认为0 +* @param debugmodel 调试代码,0表示输入判断模式,1表示输出位置信息,2表示输出判断模式,默认为0 +* @param x OCR识别区域的起始X坐标,默认为0 +* @param y OCR识别区域的起始Y坐标,默认为0 +* @param w OCR识别区域的宽度,默认为1920 +* @param h OCR识别区域的高度,默认为1080 +* @returns 包含识别结果的对象,包括识别的文字、坐标和是否找到的结果 +*/ + async function textOCR(text = "空参数", timeout = 10, afterBehavior = 0, debugmodel = 0, x = 0, y = 0, w = 1920, h = 1080) { + const startTime = new Date(); + let Outcheak = 0 + for (let ii = 0; ii < 10; ii++) { + // 获取一张截图 + let captureRegion = captureGameRegion(); + let res1 + let res2 + let conuntcottimecot = 1; + let conuntcottimecomp = 1; + // 对整个区域进行 OCR + let resList = captureRegion.findMulti(RecognitionObject.ocr(x, y, w, h)); + //log.info("OCR 全区域识别结果数量 {len}", resList.count); + if (resList.count !== 0) { + for (let i = 0; i < resList.count; i++) { // 遍历的是 C# 的 List 对象,所以要用 count,而不是 length + let res = resList[i]; + res1 = res.text + conuntcottimecomp++; + if (res.text.includes(text) && debugmodel == 3) { return result = { text: res.text, x: res.x, y: res.y, found: true }; } + if (res.text.includes(text) && debugmodel !== 2) { + conuntcottimecot++; + if (debugmodel === 1 & x === 0 & y === 0) { log.info("全图代码位置:({x},{y},{h},{w})", res.x - 10, res.y - 10, res.width + 10, res.Height + 10); } + if (afterBehavior === 1) { await sleep(1000); click(res.x, res.y); } else { if (debugmodel === 1 & x === 0 & y === 0) { log.info("点击模式:关") } } + if (afterBehavior === 2) { await sleep(100); keyPress("F"); } else { if (debugmodel === 1 & x === 0 & y === 0) { log.info("F模式:关"); } } + if (conuntcottimecot >= conuntcottimecomp / 2) { return result = { text: res.text, x: res.x, y: res.y, found: true }; } else { return result = { found: false }; } + } + if (debugmodel === 2) { + if (res1 === res2) { conuntcottimecot++; res2 = res1; } + //log.info("输出模式:全图代码位置:({x},{y},{h},{w},{string})", res.x-10, res.y-10, res.width+10, res.Height+10, res.text); + if (Outcheak === 1) { if (conuntcottimecot >= conuntcottimecomp / 2) { return result = { text: res.text, x: res.x, y: res.y, found: true }; } else { return result = { found: false }; } } + } + } + } + const NowTime = new Date(); + if ((NowTime - startTime) > timeout * 1000) { if (debugmodel === 2) { if (resList.count === 0) { return result = { found: false }; } else { Outcheak = 1; ii = 2; } } else { Outcheak = 0; if (debugmodel === 1 & x === 0 & y === 0) { log.info(`${timeout}秒超时退出,"${text}"未找到`) }; return result = { found: false }; } } + else { ii = 2; if (debugmodel === 1 & x === 0 & y === 0) { log.info(`"${text}"识别中……`); } } + await sleep(100); + } + } + if (Foods.length == 0) { log.error("未选择要加工的料理/食材"); return; } + if (Foods.length != foodCount.length) { log.error("请检查料理与对应的数量是否一致!"); return; } + const stove = "蒙德炉子"; + log.info(`正在前往${stove}进行食材加工`); + + try { + let filePath = `assets/${stove}.json`; + await pathingScript.runFile(filePath); + } catch (error) { + log.error(`执行 ${stove} 路径时发生错误`); + return; + } + + const res1 = await textOCR("烹饪", 5, 0, 0, 1150, 460, 155, 155); + if (res1.found) { + await sleep(10); + keyDown("VK_MENU"); + await sleep(500); + click(res1.x + 15, res1.y + 15); + } else { + log.warn("烹饪按钮未找到,正在寻找……"); + let attempts = 0; + const maxAttempts = 3; + let foundInRetry = false; + while (attempts < maxAttempts) { + log.info(`第${attempts + 1}次尝试寻找烹饪按钮`); + keyPress("W"); + const res2 = await textOCR("烹饪", 5, 0, 0, 1150, 460, 155, 155); + if (res2.found) { + await sleep(10); + keyDown("VK_MENU"); + await sleep(500); + click(res2.x + 15, res2.y + 15); + foundInRetry = true; + break; + } else { + attempts++; + await sleep(500); + } + } + if (!foundInRetry) { + log.error("多次未找到烹饪按钮,放弃寻找"); + return; + } + } + + await sleep(800); + keyUp("VK_MENU"); + await sleep(1000); + + for (let i = 0; i < Foods.length; i++) { + const targetFoods = new Set([ + "面粉", "兽肉", "鱼肉", "神秘的肉", "黑麦粉", "奶油", "熏禽肉", + "黄油", "火腿", "糖", "香辛料", "酸奶油", "蟹黄", "果酱", + "奶酪", "培根", "香肠" + ]); + if (targetFoods.has(Foods[i])) {//调味品就点到对应页面 + const res3 = await textOCR("食材加工", 1, 0, 0, 140, 30, 115, 30); + if (!res3.found) { + await sleep(500); + click(1010, 55); + await sleep(500); + } + + const res = await textOCR("全部领取", 1, 0, 0, 195, 1000, 120, 40); + if (res.found) { + click(res.x, res.y); + await sleep(800); + click(960, 750); + await sleep(500); + } + + const res1 = await textOCR(Foods[i], 1, 0, 3, 116, 116, 1165, 505); + + if (res1.found) { + log.info(`${Foods[i]}已找到`); + await click(res1.x + 50, res1.y - 60); + } else { + await sleep(500); + let ra = captureGameRegion(); + + try { + const ocrResult = ra.findMulti(RecognitionObject.ocr(115, 115, 1150, 502)); + const foodItems = []; // 存储找到的相关项目 + + // 收集所有包含"分钟"或"秒"的项目 + for (let j = 0; j < ocrResult.count; ++j) { + if (ocrResult[j].text.endsWith("分钟") || ocrResult[j].text.endsWith("秒")) { + foodItems.push({ + index: j, + x: ocrResult[j].x, + y: ocrResult[j].y + }); + } + } + + log.debug("检查到的正在加工食材的数量:" + foodItems.length); + + // 依次筛选这些项目 + for (const item of foodItems) { + // 点击该项目 + click(item.x, item.y); + await sleep(150); + click(item.x, item.y); + await sleep(800); + + let res2 = await textOCR("", 0.5, 0, 2, 1320, 100, 150, 50); + log.debug("当前项目:" + res2.text); + log.debug(item.x + "," + item.y); + await sleep(1000); + + if (res2.text === Foods[i]) { + ra?.dispose(); + res1.found = true; + log.info(`从正在加工的食材中找到了:${Foods[i]}`); + break; + } + } + if (!res1.found) { + log.error(`未找到目标食材: ${Foods[i]}`); + ra?.dispose(); + continue; + } + } finally { + ra?.dispose(); + } + } + + await sleep(1000); + click(1700, 1020);// 制作 + await sleep(800); + click(960, 460); + await sleep(800); + inputText(foodCount[i]); + log.info(`尝试制作${Foods[i]} ${foodCount[i]}个`); + log.warn("由于受到队列和背包食材数量限制,实际制作数量与上述数量可能不一致!"); + await sleep(800); + click(1190, 755); + await sleep(800); + } else { + const res3 = await textOCR("料理制作", 1, 0, 0, 140, 30, 115, 30); + if (!res3.found) { + await sleep(500); + click(910, 55); + await sleep(500); + } + + click(145, 1015);// 筛选 + await sleep(800); + + click(195, 1015);// 重置 + await sleep(800); + + click(500, 1020);// 确认筛选 + await sleep(800); + + //滚轮预操作 + await moveMouseTo(1287, 131); + await sleep(100); + await leftButtonDown(); + await sleep(100); + await moveMouseTo(1287, 161); + + let YOffset = 0; // Y轴偏移量,根据需要调整 + const maxRetries = 20; // 最大重试次数 + let retries = 0; // 当前重试次数 + while (retries < maxRetries) { + const res2 = await textOCR(Foods[i], 1, 0, 3, 116, 116, 1165, 880); + if (res2.found) { + await leftButtonUp(); + await sleep(500); + await click(res2.x + 50, res2.y - 60); + await sleep(1000); + + await sleep(1000); + click(1700, 1020);// 制作 + await sleep(1000); + + await textOCR("自动烹饪", 5, 1, 0, 725, 1000, 130, 45); + await sleep(800); + click(960, 460); + await sleep(800); + inputText(foodCount[i]); + await sleep(800); + click(1190, 755); + await sleep(2500); // 等待烹饪完成 + + keyPress("ESCAPE") + await sleep(500); + keyPress("ESCAPE") + await sleep(1500); + + break; + } else { + retries++; // 重试次数加1 + //滚轮操作 + YOffset += 50; + await sleep(500); + if (retries === maxRetries || 161 + YOffset > 1080) { + await leftButtonUp(); + await sleep(100); + await moveMouseTo(1287, 131); + await sleep(800); + leftButtonClick(); + log.error(`料理/食材:${Foods[i]} 未找到!请检查料理名称是否正确!`); + continue; + } + await moveMouseTo(1287, 161 + YOffset); + await sleep(300); + } + } + + } + } + await genshin.returnMainUi(); +} \ No newline at end of file diff --git a/repo/js/采集cd管理/manifest.json b/repo/js/采集cd管理/manifest.json index b69306e1c..cede75a8a 100644 --- a/repo/js/采集cd管理/manifest.json +++ b/repo/js/采集cd管理/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "采集cd管理", - "version": "2.2.0", + "version": "2.4.1", "bgi_version": "0.44.8", "description": "仅面对会操作文件和读readme的用户,基于文件夹操作自动管理采集路线的cd,会按照路径组的顺序依次运行,直到指定的时间,并会按照给定的cd类型,自动跳过未刷新的路线", "saved_files": [ diff --git a/repo/js/采集cd管理/pathing/占位.js b/repo/js/采集cd管理/pathing/占位.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/repo/js/采集cd管理/settings.json b/repo/js/采集cd管理/settings.json index 1865287d5..d272c25ec 100644 --- a/repo/js/采集cd管理/settings.json +++ b/repo/js/采集cd管理/settings.json @@ -10,5 +10,10 @@ "type": "input-text", "label": "需要生成几个路径组配置(1-99)", "default": "3" + }, + { + "name": "enableMoreSettings", + "type": "checkbox", + "label": "勾选后下次运行展开高级设置\n用于进行路线筛选和排序" } ] \ No newline at end of file