自动购买每天&3天&每周刷新商品3.2.3 (#2892)

* 自动购买每天&3天&每周刷新商品3.2.2

* 回退购买商人標籤

* 回退

* 補充商品信息

* 修復BUG

* 自动购买每天&3天&每周刷新商品3.2.3-tags支援
This commit is contained in:
this-Fish
2026-02-17 16:00:16 +08:00
committed by GitHub
parent 16241cb9aa
commit bd88d115f6
8 changed files with 149 additions and 97 deletions

View File

@@ -45,20 +45,16 @@
- 周一刷新商品
- 周四刷新商品
- **每月1号刷新商品**:不受此设置影响,刷新即购买
4. **禁用的商人**:不想购买的商人,用空格分隔
4. **禁用标签功能**:可以输入要禁用的标签(空格分隔),如:
```
示例:阿扎莱 皮托
```
5. **禁用标签功能**:可以输入要禁用的标签(空格分隔),如:
```
示例:黑心商人 挪德卡莱
示例:黑心商人 挪德卡莱 卡琵莉亚
```
禁用后,脚本会跳过这些商人或带有指定标签的商人,不会前往购买。
可以使用国家/地区(如"蒙德"、"璃月")或其他标签(如商人信息总览中的标签)
6. **是否跳过调整时间动画**:开启后会啟用时瞬跳过调整时间动画
7. **无视购买记录强制购买**:开启后会忽略购买记录,重新购买所有商品
8. **调试模式**:开启后显示详细执行日志
5. **是否跳过调整时间动画**:开启后会啟用时瞬跳过调整时间动画
6. **无视购买记录强制购买**:开启后会忽略购买记录,重新购买所有商品
7. **调试模式**:开启后显示详细执行日志
### 购买记录系统说明
脚本会自动记录购买时间,避免重复购买:
@@ -106,7 +102,7 @@
| 璃月-石门 | 老周叔 | 大碗茶 | - | - | 稀少商品 |
| 璃月-轻策庄 | 小白 | - | 豆腐、杏仁、霓裳花 | - | 琉璃百合(每月刷新) |
| 璃月-轻策庄 | 凯叔 | 大碗茶 | - | - | 稀少商品 |
| 璃月-遗珑埠 | 丰泰 | - | 沉玉仙茗、琉璃袋、绝云椒椒 | 蟹黄 | |
| 璃月-遗珑埠 | 丰泰 | 稻米、豆腐 | 清水玉、石珀、夜泊石、琉璃袋、霓裳花、绝云椒椒、沉玉仙茗 | 蟹黄 | |
| 璃月-遗珑埠 | 连芳 | - | 沉玉仙茗 | - | |
| 稻妻-离岛 | 小畑 | 螃蟹、鱼肉、虾仁 | 海灵芝 | - | |
| 稻妻-离岛 | 秋月 | 铁块、白铁块、电气水晶 | - | - | |
@@ -151,10 +147,11 @@
| 挪德卡莱-那夏镇 | 雷科 | - | - | 幸运儿之杯、幸运儿鹰羽、幸运儿银冠、幸运儿绿花、幸运儿沙漏 | 狗粮商人 |
| 挪德卡莱-皮拉米达城 | 科菲策 | 牛奶、咖啡豆 | 微光角菌、琉鳞石 | - | 黑心商人 |
- **国家标签**:蒙德、璃月、稻妻、须弥、枫丹、纳塔、挪德卡莱
- **地区标签**:风起地、清泉镇、蒙德、璃月港、离岛、稻妻城、海祇岛等
- **小地图标签**:天使的馈赠、兰巴德酒馆、德波大饭店、灰河、锈舵酒馆
- **商人标签**:克罗丽丝、神奇的霍普金斯、杜拉夫等(所有商人名)
### **以下为有效标签**
- **国家**蒙德、璃月、稻妻、须弥、枫丹、纳塔、挪德卡莱
- **地区**:风起地、清泉镇、蒙德城、璃月港、离岛、稻妻城、海祇岛
- **小地图**:天使的馈赠、兰巴德酒馆、德波大饭店、灰河、锈舵酒馆等
- **商人**:克罗丽丝、神奇的霍普金斯、杜拉夫等(所有商人名)
- **移动**:指商人会移动,有机会购买失败
- **稀少商品**:指稀少商品
- **独立地图**:指需要进入独立空间的特殊地图,这些地图部份路径无法使用地图追踪功能

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@@ -227,7 +227,8 @@
"page": 2,
"time": "any",
"path": "assets/path/璃月-遗珑埠-丰泰.json",
"_3d_foods": ["沉玉仙茗", "琉璃袋", "绝云椒椒"],
"_1d_foods": ["稻米","豆腐"],
"_3d_foods": [ "清水玉","石珀","夜泊石","琉璃袋", "霓裳花", "绝云椒椒","沉玉仙茗"],
"_7d_foods": ["蟹黄"],
"tags": ["璃月", "遗珑埠", "丰泰"]
},

View File

@@ -9,7 +9,7 @@
"bgi_version": "0.45.0",
"description": "",
"enable_monster_loot_split": false,
"last_modified_time": 1771248205039,
"last_modified_time": 1771299801495,
"map_match_method": "",
"map_name": "Teyvat",
"name": "蒙德-蒙德城-天使的馈赠-查尔斯",
@@ -49,6 +49,15 @@
"action": "",
"action_params": "",
"id": 4,
"move_mode": "walk",
"type": "path",
"x": -894.005859375,
"y": 2319.78125
},
{
"action": "",
"action_params": "",
"id": 5,
"move_mode": "run",
"type": "path",
"x": -908.830078125,
@@ -57,7 +66,7 @@
{
"action": "",
"action_params": "",
"id": 5,
"id": 6,
"move_mode": "dash",
"type": "path",
"x": -921.044921875,
@@ -66,7 +75,7 @@
{
"action": "",
"action_params": "",
"id": 6,
"id": 7,
"move_mode": "walk",
"type": "path",
"x": -923.63671875,
@@ -75,7 +84,7 @@
{
"action": "",
"action_params": "",
"id": 7,
"id": 8,
"move_mode": "dash",
"type": "path",
"x": -930.8125,
@@ -84,7 +93,7 @@
{
"action": "",
"action_params": "",
"id": 8,
"id": 9,
"move_mode": "walk",
"type": "path",
"x": -934.296875,
@@ -93,7 +102,7 @@
{
"action": "",
"action_params": "",
"id": 9,
"id": 10,
"move_mode": "walk",
"type": "path",
"x": -933.291015625,
@@ -102,7 +111,7 @@
{
"action": "combat_script",
"action_params": "wait(0.5),keypress(F),wait(0.2),keypress(F),wait(0.2),keypress(F),wait(7.5),keydown(a),keypress(SPACE),wait(0.5),keypress(SPACE),wait(0.5),keypress(SPACE),wait(0.5),keypress(SPACE),wait(0.5),keyup(a),wait(0.5),s(0.5),wait(0.5),d(2),wait(0.5),w(0.8),wait(0.5),a(0.5),wait(1.5)",
"id": 10,
"id": 11,
"move_mode": "walk",
"type": "target",
"x": -929.392578125,

View File

@@ -111,6 +111,9 @@ let npcData = {};
// 存储用户要购买的商品名称集合(中文名)
let userFoodsToBuy = new Set();
let userTagsToBuy = new Set(); // 标签名
let allTags = new Set(); // 所有可用标签(从 npcs.json 收集)
let requiredFoods = new Set(); // 所有需要加载图片的商品
async function loadExternalData() {
try {
@@ -119,26 +122,62 @@ async function loadExternalData() {
npcData = JSON.parse(npcsContent);
logConditional(`已加载商人数据: ${Object.keys(npcData).length} 个商人`);
// 解析用户要购买的商品列表(中文商品名,空格分隔)
// ========== 收集所有标签 ==========
for (let key in npcData) {
let npc = npcData[key];
if (npc.tags && Array.isArray(npc.tags)) {
npc.tags.forEach(tag => allTags.add(tag));
}
}
logConditional(`共收集到 ${allTags.size} 个标签`);
// ========== 解析用户输入 ==========
const foodsInput = (settings.foodsToBuy || "").trim();
if (foodsInput) {
const foodNames = foodsInput.split(/[,\s、]+/).filter(name => name.trim() !== "");
const items = foodsInput.split(/[,\s、]+/).filter(item => item.trim() !== "");
const enabledFoodsList = [];
for (const foodName of foodNames) {
// 直接使用用户输入的商品名,不需要验证是否存在(由用户自行确保)
userFoodsToBuy.add(foodName);
enabledFoodsList.push(foodName);
const enabledTagsList = [];
for (const item of items) {
if (allTags.has(item)) {
// 是标签
userTagsToBuy.add(item);
enabledTagsList.push(item);
} else {
// 视为商品名
userFoodsToBuy.add(item);
enabledFoodsList.push(item);
}
}
// 输出用户启用的标签和商品
if (enabledTagsList.length > 0) {
log.info(`用户启用了以下标签: ${enabledTagsList.join(", ")}`);
}
// 输出用户启用的商品列表
if (enabledFoodsList.length > 0) {
log.info(`用户启用了下商品: ${enabledFoodsList.join(", ")}`);
} else {
log.warn("用户未启用任何商品");
log.info(`用户启用了下商品: ${enabledFoodsList.join(", ")}`);
}
if (enabledTagsList.length === 0 && enabledFoodsList.length === 0) {
log.warn("用户未启用任何标签或商品");
}
} else {
log.warn("用户未指定要购买的商品");
log.warn("用户未指定要购买的商品或标签");
}
// 计算所有需要加载图片的商品
requiredFoods = new Set(userFoodsToBuy);
for (let key in npcData) {
let npc = npcData[key];
if (npc.tags && Array.isArray(npc.tags) && npc.tags.some(tag => userTagsToBuy.has(tag))) {
if (npc._1d_foods) npc._1d_foods.forEach(food => requiredFoods.add(food));
if (npc._3d_foods) npc._3d_foods.forEach(food => requiredFoods.add(food));
if (npc._7d_foods) npc._7d_foods.forEach(food => requiredFoods.add(food));
if (npc._thu_foods) npc._thu_foods.forEach(food => requiredFoods.add(food));
if (npc._month_foods) npc._month_foods.forEach(food => requiredFoods.add(food));
}
}
logConditional(`需要加载图片的商品总数: ${requiredFoods.size}`);
return true;
} catch (error) {
log.error(`加载外部数据失败: ${error.message}`);
@@ -146,6 +185,17 @@ async function loadExternalData() {
}
}
// ==================== 辅助函数:获取商人的所有商品 ====================
function getAllNpcFoods(npc) {
const all = [];
if (npc._1d_foods) all.push(...npc._1d_foods);
if (npc._3d_foods) all.push(...npc._3d_foods);
if (npc._7d_foods) all.push(...npc._7d_foods);
if (npc._thu_foods) all.push(...npc._thu_foods);
if (npc._month_foods) all.push(...npc._month_foods);
return all;
}
// ==================== 辅助函数:过滤用户要购买的商品 ====================
function filterUserFoods(foodList) {
if (!foodList || !Array.isArray(foodList)) {
@@ -231,12 +281,6 @@ let userName = settings.userName || "默认账户";
const ignoreRecords = settings.ignoreRecords || false;
const recordDebug = settings.recordDebug || false;
// 解析禁用的商人列表
const disabledNpcs = (settings.disabledNpcs || "").split(/[,\s、]+/).filter(npc => npc.trim() !== "");
if (disabledNpcs.length > 0) {
log.info(`已禁用商人: ${disabledNpcs.join(", ")}`);
}
// 解析禁用的标签列表
const disabledTags = (settings.disabledTags || "").split(/[,\s、]+/).filter(tag => tag.trim() !== "");
if (disabledTags.length > 0) {
@@ -457,54 +501,71 @@ function shouldBuyFoods(npc, npcRecord, currentPeriod, forceRefresh = false) {
"month": []
};
// 首先检查禁用(此处假设之前已检查过,但为防止遗漏,可再加一道保险)
// 实际上禁用检查在更外层initNpcData 和主循环)已经处理,这里可以省略
if (forceRefresh) {
// 强制刷新:所有启用商品都尝试
if (npc._1d_foods) foodsToBuy["1d"] = filterUserFoods(npc._1d_foods);
if (npc._3d_foods) foodsToBuy["3d"] = filterUserFoods(npc._3d_foods);
if (npc._7d_foods) foodsToBuy["7d"] = filterUserFoods(npc._7d_foods);
if (npc._thu_foods) foodsToBuy["thu"] = filterUserFoods(npc._thu_foods);
if (npc._month_foods) foodsToBuy["month"] = filterUserFoods(npc._month_foods);
// 强制刷新:决定使用完整列表还是具体商品列表
// 先判断是否命中标签
let useAll = false;
if (npc.tags && Array.isArray(npc.tags)) {
useAll = npc.tags.some(tag => userTagsToBuy.has(tag));
}
if (useAll) {
// 标签商人:购买所有商品
if (npc._1d_foods) foodsToBuy["1d"] = npc._1d_foods;
if (npc._3d_foods) foodsToBuy["3d"] = npc._3d_foods;
if (npc._7d_foods) foodsToBuy["7d"] = npc._7d_foods;
if (npc._thu_foods) foodsToBuy["thu"] = npc._thu_foods;
if (npc._month_foods) foodsToBuy["month"] = npc._month_foods;
} else {
// 非标签商人:只购买用户明确指定的商品
if (npc._1d_foods) foodsToBuy["1d"] = filterUserFoods(npc._1d_foods);
if (npc._3d_foods) foodsToBuy["3d"] = filterUserFoods(npc._3d_foods);
if (npc._7d_foods) foodsToBuy["7d"] = filterUserFoods(npc._7d_foods);
if (npc._thu_foods) foodsToBuy["thu"] = filterUserFoods(npc._thu_foods);
if (npc._month_foods) foodsToBuy["month"] = filterUserFoods(npc._month_foods);
}
return foodsToBuy;
}
// 辅助函数:处理单个刷新类型
function processType(type, enabledFoods, refreshLogic) {
if (!enabledFoods || enabledFoods.length === 0) return [];
// 辅助函数:处理单个类型,根据是否命中标签决定使用完整列表还是过滤列表
function processType(type, fullList) {
if (!fullList || fullList.length === 0) return [];
// 获取已购买列表(如果记录存在
// 判断该商人是否命中用户标签(只需判断一次,可在外层缓存结果
// 此处简单处理:每次调用都判断,但实际可优化
let useAll = false;
if (npc.tags && Array.isArray(npc.tags)) {
useAll = npc.tags.some(tag => userTagsToBuy.has(tag));
}
// 确定要购买的候选商品列表
let candidateList = useAll ? fullList : filterUserFoods(fullList);
if (candidateList.length === 0) return [];
// 获取已购买列表
const purchasedList = npcRecord && npcRecord[type] ? npcRecord[type] : [];
// 找出未购买的商品
const notPurchased = enabledFoods.filter(food => !purchasedList.includes(food));
const notPurchased = candidateList.filter(food => !purchasedList.includes(food));
// 如果没有记录或没有刷新时间,说明从未买过,全部尝试
if (!npcRecord || !npcRecord[`${type}_time`]) {
return enabledFoods;
return candidateList; // 从未买过,全部尝试
}
const nextRefreshTime = new Date(npcRecord[`${type}_time`]);
if (now >= nextRefreshTime) {
// 已刷新时间:所有商品都应重新尝试(已购买和未购买的都可能再次出现)
return enabledFoods;
return candidateList; // 已刷新,全部尝试
} else {
// 未刷新时间:只尝试未购买过的商品
return notPurchased;
return notPurchased; // 未刷新只尝试未购买过的
}
}
// 处理每天刷新商品
foodsToBuy["1d"] = processType("1d", filterUserFoods(npc._1d_foods));
// 处理3天刷新商品
foodsToBuy["3d"] = processType("3d", filterUserFoods(npc._3d_foods));
// 处理7天刷新商品周一刷新
foodsToBuy["7d"] = processType("7d", filterUserFoods(npc._7d_foods));
// 处理周四刷新商品
foodsToBuy["thu"] = processType("thu", filterUserFoods(npc._thu_foods));
// 处理每月刷新商品
foodsToBuy["month"] = processType("month", filterUserFoods(npc._month_foods));
foodsToBuy["1d"] = processType("1d", npc._1d_foods);
foodsToBuy["3d"] = processType("3d", npc._3d_foods);
foodsToBuy["7d"] = processType("7d", npc._7d_foods);
foodsToBuy["thu"] = processType("thu", npc._thu_foods);
foodsToBuy["month"] = processType("month", npc._month_foods);
return foodsToBuy;
}
@@ -719,8 +780,7 @@ let foodROMap = {}; // 键为商品名(中文),值为 RecognitionObject
// 加载识别对象(只加载用户选择的商品)
async function initRo() {
try {
for (let foodName of userFoodsToBuy) {
// 图片文件路径assets/images/商品名.png
for (let foodName of requiredFoods) {
const imagePath = `assets/images/${foodName}.png`;
try {
const ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync(imagePath));
@@ -732,13 +792,11 @@ async function initRo() {
log.error(`加载商品图片失败: ${imagePath},请确保图片存在`);
}
}
// 加载其他识别对象(购买按钮等)
for (let [key, item] of Object.entries(othrtRo)) {
item.ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync(item.file));
item.ro.Threshold = 0.85;
}
logConditional(`总共启用了 ${userFoodsToBuy.size} 种商品`);
logConditional(`总共启用了 ${requiredFoods.size} 种商品`);
return true;
}
catch (error) {
@@ -915,14 +973,6 @@ async function buyFoods(npcName, npcRecords, currentPeriod) {
// 初始化商人商品
async function initNpcData(records) {
for (let [key, npc] of Object.entries(npcData)) {
// 检查是否在禁用列表中
if (disabledNpcs.includes(npc.name)) {
npc.enable = false;
const displayName = getDisplayNameFromPath(npc.path);
logConditional(`已禁用: ${displayName}`);
continue;
}
// 检查是否通过标签禁用
if (npc.tags && Array.isArray(npc.tags)) {
const hasDisabledTag = npc.tags.some(tag => disabledTags.includes(tag));

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "自动购买每天&3天&每周刷新商品",
"version": "3.2.2",
"version": "3.2.3",
"description": "自动购买每天&3天&每周刷新商品\n每天刷新商品自动购买商品\n3天刷新商品未到刷新日不购买该商品\n每周刷新商品可指定每周购买商品",
"authors": [
{

View File

@@ -27,17 +27,11 @@
],
"default": "0"
},
{
"name": "disabledNpcs",
"type": "input-text",
"label": "要禁用的商人\n多个商人用空格分隔\n示例阿扎莱 皮托",
"default": "阿扎莱 皮托"
},
{
"name": "disabledTags",
"type": "input-text",
"label": "禁用标签\n多个标签以空格分隔黑心商人 挪德卡莱",
"default": ""
"label": "禁用标签\n多个标签以空格分隔 挪德卡莱 黑心商人 卡琵莉亚",
"default": "卡琵莉亚"
},
{
"name": "skip",

View File

@@ -29,7 +29,7 @@
| 璃月-石门 | 老周叔 | 大碗茶 | - | - | 稀少商品 |
| 璃月-轻策庄 | 小白 | - | 豆腐、杏仁、霓裳花 | - | 琉璃百合(每月刷新) |
| 璃月-轻策庄 | 凯叔 | 大碗茶 | - | - | 稀少商品 |
| 璃月-遗珑埠 | 丰泰 | - | 沉玉仙茗、琉璃袋、绝云椒椒 | 蟹黄 | |
| 璃月-遗珑埠 | 丰泰 | 稻米、豆腐 | 清水玉、石珀、夜泊石、琉璃袋、霓裳花、绝云椒椒、沉玉仙茗 | 蟹黄 | |
| 璃月-遗珑埠 | 连芳 | - | 沉玉仙茗 | - | |
| 稻妻-离岛 | 小畑 | 螃蟹、鱼肉、虾仁 | 海灵芝 | - | |
| 稻妻-离岛 | 秋月 | 铁块、白铁块、电气水晶 | - | - | |
@@ -74,10 +74,11 @@
| 挪德卡莱-那夏镇 | 雷科 | - | - | 幸运儿之杯、幸运儿鹰羽、幸运儿银冠、幸运儿绿花、幸运儿沙漏 | 狗粮商人 |
| 挪德卡莱-皮拉米达城 | 科菲策 | 牛奶、咖啡豆 | 微光角菌、琉鳞石 | - | 黑心商人 |
- **国家标签**:蒙德、璃月、稻妻、须弥、枫丹、纳塔、挪德卡莱
- **地区标签**:风起地、清泉镇、蒙德、璃月港、离岛、稻妻城、海祇岛等
- **小地图标签**:天使的馈赠、兰巴德酒馆、德波大饭店、灰河、锈舵酒馆
- **商人标签**:克罗丽丝、神奇的霍普金斯、杜拉夫等(所有商人名)
### **以下为有效标签**
- **国家**蒙德、璃月、稻妻、须弥、枫丹、纳塔、挪德卡莱
- **地区**:风起地、清泉镇、蒙德城、璃月港、离岛、稻妻城、海祇岛
- **小地图**:天使的馈赠、兰巴德酒馆、德波大饭店、灰河、锈舵酒馆等
- **商人**:克罗丽丝、神奇的霍普金斯、杜拉夫等(所有商人名)
- **移动**:指商人会移动,有机会购买失败
- **稀少商品**:指稀少商品
- **独立地图**:指需要进入独立空间的特殊地图,这些地图部份路径无法使用地图追踪功能