识别体力与树脂数量 (#981)

* 识别体力与树脂数量

* 璃月6-奥藏山

* update repo.json

* 地图追踪 星银矿石 (#1025)

* Add files via upload

* 自动修复 JSON 格式和版本号 [ci skip]

* Delete repo/pathing/矿物/星银矿石/desktop.ini

* Add files via upload

---------

Co-authored-by: GitHub Actions Bot <actions@github.com>

* Update main.js (#1024)

手动终止夹断

* update repo.json

* 6.7 富Abpro (#1022)

* Add files via upload

* Add files via upload

* Delete repo/js/AutoArtifactsPro/assets/ArtifactsPath/A路线/01普通/000【激活程序】稻妻大炮.json

* Delete repo/js/AutoArtifactsPro/assets/ArtifactsPath/A路线/01普通/404须弥-天臂池七天神像4.json

* Delete repo/js/AutoArtifactsPro/assets/ArtifactsPath/A路线/01普通/601纳塔-流泉之众4.json

* Delete repo/js/AutoArtifactsPro/assets/ArtifactsPath/A路线/01普通/602纳塔-悬木人声望2.json

* Delete repo/js/AutoArtifactsPro/assets/ArtifactsPath/A路线/01普通/603纳塔-花羽会西2.json

* Add files via upload

* update repo.json

* `七圣召唤七日历练`: 修复牌币已满时卡在出战界面的问题 (#1017)

* update repo.json

* js:狗粮重置版1.2更新 (#1026)

* update repo.json

* fix: 修复部分情况下未找到图标直接结束

* temp

* add: 璃月7

* feat: 新的数据结构

* add: 合并代码,功能待实现

---------

Co-authored-by: 寒烟 <2841974482@qq.com>
Co-authored-by: physligl <181079228+physligl@users.noreply.github.com>
Co-authored-by: Tooltingsu <143606015+Tooltingsu@users.noreply.github.com>
Co-authored-by: GitHub Actions Bot <actions@github.com>
Co-authored-by: JJMdzh <jjjjedyx@qq.com>
Co-authored-by: 汐 <121607261+jiegedabaobei@users.noreply.github.com>
Co-authored-by: Patrick-Ze <19711799+Patrick-Ze@users.noreply.github.com>
Co-authored-by: mno <718135749@qq.com>
Co-authored-by: 秋云 <physligl@gmail.com>
This commit is contained in:
214-hanyan
2025-06-09 06:13:27 +08:00
committed by GitHub
parent fb8d637976
commit 5f5249b348
47 changed files with 10564 additions and 5102 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月6-奥藏山-1",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 1220.19140625,
"y": 1535.111328125,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月6-奥藏山-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 1086.873046875,
"y": 1428.01220703125,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月6-奥藏山-3",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "怪物多,需要绕路",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 901.2138671875,
"y": 1235.8271484375,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月6-奥藏山-4",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "路途需处理风萨满",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 833.4765625,
"y": 1181.693359375,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月6-奥藏山-5-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 900.1533203125,
"y": 896.95703125,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月6-奥藏山-6-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 892.236328125,
"y": 747.74560546875,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月6-奥藏山-7-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 594.7021484375,
"y": 437.15625,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月6-奥藏山-8-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "绕开冰雾花",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 683.0986328125,
"y": 529.06396484375,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月6-奥藏山-9-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 455.041015625,
"y": 546.3779296875,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,22 @@
{
"info": {
"name": "璃月7-遁玉陵-1",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749399134857
},
"positions": [
{
"id": 1,
"x": 824.28,
"y": -325.68,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,22 @@
{
"info": {
"name": "璃月7-遁玉陵-2",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749399385367
},
"positions": [
{
"id": 1,
"x": 721.94,
"y": -106.43,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,22 @@
{
"info": {
"name": "璃月7-遁玉陵-3",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749399818683
},
"positions": [
{
"id": 1,
"x": 752.42,
"y": 3.88,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,22 @@
{
"info": {
"name": "璃月7-遁玉陵-4",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749399950280
},
"positions": [
{
"id": 1,
"x": 815.03,
"y": 136.35,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,22 @@
{
"info": {
"name": "璃月7-遁玉陵-5",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749400498877
},
"positions": [
{
"id": 1,
"x": 681.46,
"y": 528.76,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"info": {
"name": "璃月7-遁玉陵-6",
"type": "collect",
"author": "寒烟",
"version": "1.1",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 455.4,
"y": 546.56,
"type": "target",
"move_mode": "walk"
}
]
}

View File

@@ -0,0 +1,31 @@
{
"info": {
"name": "璃月6-奥藏山-1",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"action": "",
"move_mode": "walk",
"type": "teleport",
"x": 1271.1943359375,
"y": 1563.9794921875,
"action_params": ""
},
{
"id": 2,
"x": 1220.19140625,
"y": 1535.111328125,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,58 @@
{
"info": {
"name": "璃月6-奥藏山-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 1182.2958984375,
"y": 1520.43359375,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 1141.4580078125,
"y": 1496.076171875,
"type": "path",
"move_mode": "run",
"action": "",
"action_params": ""
},
{
"id": 3,
"x": 1129.1796875,
"y": 1474.1357421875,
"type": "path",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 4,
"x": 1120.623046875,
"y": 1440.248046875,
"type": "path",
"move_mode": "fly",
"action": "",
"action_params": ""
},
{
"id": 5,
"x": 1086.873046875,
"y": 1428.01220703125,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,76 @@
{
"info": {
"name": "璃月6-奥藏山-3",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "怪物多,需要绕路",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 1121.11328125,
"y": 1190.404296875,
"type": "teleport",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 1090.66015625,
"y": 1172.01904296875,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 3,
"x": 1024.1650390625,
"y": 1168.46826171875,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 4,
"x": 987.1689453125,
"y": 1175.048828125,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 5,
"x": 946.499348918609,
"y": 1208.4989920278222,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 6,
"x": 918.666015625,
"y": 1200.896484375,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 7,
"x": 901.2138671875,
"y": 1235.8271484375,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,31 @@
{
"info": {
"name": "璃月6-奥藏山-4",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "路途需处理风萨满",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 860.724609375,
"y": 1208.26953125,
"type": "path",
"move_mode": "dash",
"action": "fight",
"action_params": ""
},
{
"id": 2,
"x": 833.4765625,
"y": 1181.693359375,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,31 @@
{
"info": {
"name": "璃月6-奥藏山-5-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 1045.5576171875,
"y": 950.61474609375,
"type": "teleport",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 900.1533203125,
"y": 896.95703125,
"type": "path",
"move_mode": "fly",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,49 @@
{
"info": {
"name": "璃月6-奥藏山-6-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 899.5732421875,
"y": 819.48681640625,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 921.95703125,
"y": 785.88525390625,
"type": "path",
"move_mode": "fly",
"action": "",
"action_params": ""
},
{
"id": 3,
"x": 887.9111328125,
"y": 754.49658203125,
"type": "path",
"move_mode": "jump",
"action": "",
"action_params": ""
},
{
"id": 4,
"x": 892.236328125,
"y": 747.74560546875,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,68 @@
{
"info": {
"name": "璃月6-奥藏山-7-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"action": "",
"move_mode": "walk",
"type": "teleport",
"x": 489.412109375,
"y": 222.228515625,
"action_params": ""
},
{
"id": 2,
"x": 500.255859375,
"y": 225.5390625,
"type": "orientation",
"move_mode": "walk",
"action": "",
"action_params": "",
"locked": false
},
{
"id": 3,
"x": 500.255859375,
"y": 225.5390625,
"type": "target",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 4,
"x": 512.90625,
"y": 258.54150390625,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 5,
"x": 551.162109375,
"y": 359.5361328125,
"type": "path",
"move_mode": "fly",
"action": "",
"action_params": ""
},
{
"id": 6,
"x": 594.7021484375,
"y": 437.15625,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,49 @@
{
"info": {
"name": "璃月6-奥藏山-8-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "绕开冰雾花",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 628.423828125,
"y": 468.4296875,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 674.7001953125,
"y": 477.642578125,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 3,
"x": 673.2099609375,
"y": 528.255859375,
"type": "path",
"move_mode": "fly",
"action": "stop_flying",
"action_params": "100"
},
{
"id": 4,
"x": 683.0986328125,
"y": 529.06396484375,
"type": "path",
"move_mode": "walk",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,40 @@
{
"info": {
"name": "璃月6-奥藏山-9-2",
"type": "collect",
"author": "寒烟",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 341.3388671875,
"y": 548.0703125,
"type": "teleport",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 391.1884765625,
"y": 545.533203125,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 3,
"x": 455.041015625,
"y": 546.3779296875,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,33 @@
{
"info": {
"name": "璃月7-遁玉陵-1",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749399134857
},
"positions": [
{
"id": 1,
"x": 978.7,
"y": -353.6,
"type": "teleport",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 2,
"action": "stop_flying",
"move_mode": "fly",
"type": "path",
"x": 824.28,
"y": -325.68,
"action_params": ""
}
]
}

View File

@@ -0,0 +1,33 @@
{
"info": {
"name": "璃月7-遁玉陵-2",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749399385367
},
"positions": [
{
"id": 1,
"x": 611.74,
"y": -284.65,
"type": "teleport",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 721.94,
"y": -106.43,
"type": "path",
"move_mode": "fly",
"action": "stop_flying",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,60 @@
{
"info": {
"name": "璃月7-遁玉陵-3",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749399818683
},
"positions": [
{
"id": 1,
"x": 717.76,
"y": -94.89,
"type": "path",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 716.96,
"y": -48.63,
"type": "path",
"move_mode": "fly",
"action": "stop_flying",
"action_params": ""
},
{
"id": 3,
"x": 716.66,
"y": -25.97,
"type": "path",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 4,
"x": 736.17,
"y": -14.65,
"type": "path",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 5,
"x": 752.42,
"y": 3.88,
"type": "path",
"move_mode": "walk",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,42 @@
{
"info": {
"name": "璃月7-遁玉陵-4",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749399950280
},
"positions": [
{
"id": 1,
"x": 837.46,
"y": 235.15,
"type": "teleport",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 824.83,
"y": 156.61,
"type": "path",
"move_mode": "fly",
"action": "",
"action_params": ""
},
{
"id": 3,
"x": 815.03,
"y": 136.35,
"type": "path",
"move_mode": "walk",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,60 @@
{
"info": {
"name": "璃月7-遁玉陵-5",
"type": "collect",
"author": "秋云",
"version": "1.0",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0",
"tags": [],
"last_modified_time": 1749400498877
},
"positions": [
{
"id": 1,
"x": 876.75,
"y": 587.25,
"type": "teleport",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 868.48,
"y": 570.17,
"type": "path",
"move_mode": "jump",
"action": "",
"action_params": ""
},
{
"id": 3,
"x": 840.12,
"y": 562.36,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 4,
"x": 761.5,
"y": 561.25,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
},
{
"id": 5,
"x": 681.46,
"y": 528.76,
"type": "path",
"move_mode": "fly",
"action": "stop_flying",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,31 @@
{
"info": {
"name": "璃月7-遁玉陵-6",
"type": "collect",
"author": "寒烟",
"version": "1.1",
"description": "",
"map_name": "Teyvat",
"bgi_version": "0.45.0"
},
"positions": [
{
"id": 1,
"x": 341.35,
"y": 548.07,
"type": "teleport",
"move_mode": "walk",
"action": "",
"action_params": ""
},
{
"id": 2,
"x": 455.4,
"y": 546.56,
"type": "path",
"move_mode": "dash",
"action": "",
"action_params": ""
}
]
}

View File

@@ -0,0 +1,859 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
import os
import sys
import re
def format_coord(num):
return round(float(num), 2)
def calculate_distance(x1, y1, x2, y2):
return ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5
# 定义区域排序顺序
REGION_ORDER = {
"蒙德": 1,
"璃月": 2,
"稻妻": 3,
"须弥": 4,
"枫丹": 5,
"纳塔": 6,
"未知区域": 99 # 未知区域放最后
}
def get_region_sort_key(region_name):
"""获取区域的排序键值,用于排序"""
# 从完整区域名称(如"枫丹1-1")中提取基本区域名称(如"枫丹"
base_region = re.match(r"^([^0-9]+)", region_name)
if base_region:
base_region = base_region.group(1)
return REGION_ORDER.get(base_region, 99)
return 99
def normalize_region_name(region_name, region_num, route_num, route_sub_num=None):
"""规范化区域名称,确保命名一致性
例如:
- 标准格式: 蒙德3, 奔狼领, 2 -> 蒙德3-2
- 特殊格式: 蒙德2, 清泉镇, 4, 1 -> 蒙德2-4-1
"""
if route_sub_num is not None:
# 特殊格式
return f"{region_name}{region_num}-{route_num}-{route_sub_num}"
else:
# 标准格式
return f"{region_name}{region_num}-{route_num}"
def is_same_region_base(region1, region2):
"""检查两个区域名称是否属于同一基本区域
例如,"蒙德2-4-1""蒙德2-4-2"属于同一基本区域"蒙德2-4"
"""
# 提取区域名称的基本部分(例如从"蒙德2-4-1"提取"蒙德2-4"
base_region1 = re.match(r"^([^-]+-\d+)", region1)
base_region2 = re.match(r"^([^-]+-\d+)", region2)
return (base_region1 and base_region2 and
base_region1.group(1) == base_region2.group(1))
def parse_region_area_number(filename):
"""从文件名解析区域、地区和编号
支持两种格式:
1. 标准格式: 蒙德3-奔狼领-2.json -> ("蒙德", 3, "奔狼领", 2)
2. 特殊格式: 蒙德2-清泉镇-4-1.json -> ("蒙德", 2, "清泉镇", 4, 1)
"""
# 先尝试匹配特殊格式(带有额外连字符的格式)
special_pattern = r"^([^0-9]+)(\d+)-(.+)-(\d+)-(\d+(?:\.\d+)?)\.json$"
match = re.match(special_pattern, filename)
if match:
region_name = match.group(1)
region_num = int(match.group(2))
area = match.group(3)
route_num = int(match.group(4))
route_sub_num = int(match.group(5))
return region_name, region_num, area, route_num, route_sub_num
# 如果不是特殊格式,尝试匹配标准格式
standard_pattern = r"^([^0-9]+)(\d+)-(.+)-(\d+(?:\.\d+)?)\.json$"
match = re.match(standard_pattern, filename)
if match:
region_name = match.group(1)
region_num = int(match.group(2))
area = match.group(3)
route_num = int(match.group(4))
return region_name, region_num, area, route_num
# 如果都不匹配,返回默认值
print(f"警告:无法解析文件名 {filename}")
return None, None, None, None
def generate_new_data_structure_from_pathing():
"""直接从assets/pathing目录读取文件生成新的数据结构"""
# 获取脚本所在目录
script_dir = os.path.dirname(os.path.abspath(__file__))
pathing_dir = os.path.join(script_dir, "assets", "pathing")
target_dir = os.path.join(pathing_dir, "target")
# 检查目录是否存在
if not os.path.exists(pathing_dir):
print(f"错误:找不到路径目录 {pathing_dir}")
return None
# 初始化新数据结构,分开存储 blossom 和 teleport
new_data = {
"teleports": [], # 传送点节点
"blossoms": [], # 地脉花节点
"edges": [], # 边集合
"indexes": {
"edgesBySource": {},
"edgesByTarget": {}
}
}
# 创建独立的ID计数器和节点映射
next_teleport_id = 1 # 传送点专用ID计数器
next_blossom_id = 1000 # 地脉花专用ID计数器
teleport_nodes = {} # 按坐标存储传送点节点 (x, y) -> node_id
target_nodes = {} # 按坐标存储目标点节点 (x, y) -> node_id
# 创建文件路径映射
file_paths = {}
# 第一遍:读取所有路径文件并创建节点
print("第一遍:读取路径文件并创建节点...")
for root, _, files in os.walk(pathing_dir):
for file in sorted(files):
# 跳过非JSON文件和重运行文件
if not file.endswith('.json') or 'rerun' in file or 'rerun' in root:
continue
# 跳过target目录
if "target" in root.split(os.path.sep):
continue
file_path = os.path.join(root, file)
try:
with open(file_path, 'r', encoding='utf-8') as f:
path_data = json.load(f)
# 记录文件路径
relative_path = os.path.relpath(file_path, script_dir)
relative_path = relative_path.replace("\\", "/")
file_paths[file] = relative_path
# 检查文件是否有position数据
if "positions" not in path_data or not path_data["positions"]:
continue
# 获取第一个位置点(通常是传送点)
first_pos = path_data["positions"][0]
if "type" in first_pos and first_pos["type"] == "teleport":
x = format_coord(first_pos["x"])
y = format_coord(first_pos["y"]) # 解析区域信息
result = parse_region_area_number(file)
if len(result) == 5: # 特殊格式
region_name, region_num, _, _, _ = result
else: # 标准格式或无法解析
region_name, region_num, _, _ = result
# 传送点仅显示区域名称,不显示编号
region = f"{region_name}" if region_name else "未知区域"
# 检查是否已存在相同坐标的传送点
existing_node = False
for coord, node_id in teleport_nodes.items():
if calculate_distance(coord[0], coord[1], x, y) < 10: # 将阈值从50降低到10
existing_node = True
break
if not existing_node: # 创建新的传送点节点
teleport_node = {
"id": next_teleport_id,
"region": region,
"position": {"x": x, "y": y}
}
new_data["teleports"].append(teleport_node)
teleport_nodes[(x, y)] = next_teleport_id
next_teleport_id += 1
print(f" 创建传送点: ID={teleport_node['id']}, 区域={region}, 坐标=({x}, {y})")
except Exception as e:
print(f" 警告:处理文件 {file} 时出错: {e}")
# 第二遍读取target目录文件并创建目标节点
print("\n第二遍读取target文件并创建目标节点...")
for file in sorted(os.listdir(target_dir)):
if not file.endswith('.json'):
continue
file_path = os.path.join(target_dir, file)
try:
with open(file_path, 'r', encoding='utf-8') as f:
target_data = json.load(f)
# 检查文件是否有position数据
if "positions" not in target_data or not target_data["positions"]:
continue
# 获取第一个位置点(通常是目标点)
target_pos = target_data["positions"][0]
x = format_coord(target_pos["x"])
y = format_coord(target_pos["y"])
result = parse_region_area_number(file)
if len(result) == 5: # 特殊格式
region_name, region_num, area, route_num, route_sub_num = result
# 使用规范化函数来生成区域名
region = normalize_region_name(region_name, region_num, route_num, route_sub_num)
else: # 标准格式或无法解析
region_name, region_num, area, route_num = result
# 使用规范化函数来生成区域名
region = normalize_region_name(region_name, region_num, route_num) if region_name and region_num and route_num else "未知区域" # 检查是否已存在相同坐标的目标点和相同区域名
existing_node = False
existing_node_id = None
for coord, node_id in target_nodes.items():
if calculate_distance(coord[0], coord[1], x, y) < 10: # 阈值为10
# 找到这个节点,检查它的区域是否与当前区域相同
for blossom in new_data["blossoms"]:
if blossom["id"] == node_id:
# 检查区域名完全相同
if blossom["region"] == region:
existing_node = True
existing_node_id = node_id
break
# 检查区域名基本部分是否相同(如"蒙德2-4"和"蒙德2-4-2"
# 提取区域名称的基本部分(例如从"蒙德2-4-1"提取"蒙德2-4"
current_base_region = re.match(r"^([^-]+-\d+)", region)
existing_base_region = re.match(r"^([^-]+-\d+)", blossom["region"])
if (current_base_region and existing_base_region and
current_base_region.group(1) == existing_base_region.group(1)):
print(f" 注意: 坐标 ({x}, {y}) 与已存在节点 '{blossom['region']}' 区域基本部分相同,视为同一节点")
existing_node = True
existing_node_id = node_id
break
if not existing_node:
print(f" 注意: 坐标 ({x}, {y}) 附近有另一个区域的节点,但将创建新节点")
if not existing_node:
# 创建新的目标点节点
target_node = {
"id": next_blossom_id,
"region": region,
"position": {"x": x, "y": y}
}
new_data["blossoms"].append(target_node)
target_nodes[(x, y)] = next_blossom_id
next_blossom_id += 1
print(f" 创建目标点: ID={target_node['id']}, 区域={region}, 坐标=({x}, {y})")
except Exception as e:
print(f" 警告:处理目标文件 {file} 时出错: {e}")
# 第三遍:创建边和索引
print("\n第三遍:创建边和索引...")
for file in sorted(os.listdir(pathing_dir)):
if not file.endswith('.json') or 'rerun' in file:
continue
# 跳过target和rerun目录中的文件
if file not in file_paths:
continue
# 解析文件名
result = parse_region_area_number(file)
if len(result) == 5: # 特殊格式
region_name, region_num, area, route_num, route_sub_num = result
else: # 标准格式
region_name, region_num, area, route_num = result
route_sub_num = None
if not region_name or not region_num or not area or not route_num:
continue
# 构建对应的target文件名
target_file = file
# 检查target文件是否存在
target_path = os.path.join(target_dir, target_file)
if not os.path.exists(target_path):
print(f" 警告:找不到对应的目标文件 {target_file}")
continue
try:
# 读取路径文件获取源传送点
file_path = os.path.join(pathing_dir, file)
with open(file_path, 'r', encoding='utf-8') as f:
path_data = json.load(f)
# 读取目标文件获取目标点
with open(target_path, 'r', encoding='utf-8') as f:
target_data = json.load(f)
# 获取传送点坐标
if "positions" in path_data and path_data["positions"]:
first_pos = path_data["positions"][0]
source_x = format_coord(first_pos["x"])
source_y = format_coord(first_pos["y"])
# 获取目标点坐标
if "positions" in target_data and target_data["positions"]:
target_pos = target_data["positions"][0]
target_x = format_coord(target_pos["x"])
target_y = format_coord(target_pos["y"]) # 查找源节点ID
source_id = None
for coord, node_id in teleport_nodes.items():
if calculate_distance(coord[0], coord[1], source_x, source_y) < 10: # 将阈值从50降低到10
source_id = node_id
break
# 查找目标节点ID
target_id = None
for coord, node_id in target_nodes.items():
if calculate_distance(coord[0], coord[1], target_x, target_y) < 10: # 将阈值从50降低到10
target_id = node_id
break
# 如果找到了源节点和目标节点,创建边
if source_id and target_id: # 添加边
edge = {
"source": source_id,
"target": target_id,
"route": file_paths[file],
# 保存原始位置信息用于在排序后更新ID稍后会删除
"sourcePosition": {
"x": source_x,
"y": source_y
},
"targetPosition": {
"x": target_x,
"y": target_y
}
}
new_data["edges"].append(edge)
# 更新索引
if str(source_id) not in new_data["indexes"]["edgesBySource"]:
new_data["indexes"]["edgesBySource"][str(source_id)] = []
new_data["indexes"]["edgesBySource"][str(source_id)].append(target_id)
if str(target_id) not in new_data["indexes"]["edgesByTarget"]:
new_data["indexes"]["edgesByTarget"][str(target_id)] = []
new_data["indexes"]["edgesByTarget"][str(target_id)].append(source_id)
print(f" 创建边: {source_id} -> {target_id}, 路径={file_paths[file]}")
except Exception as e:
print(f" 警告:处理边关系时出错 {file}: {e}") # 检查每个目标点是否至少有一条有向边
print("\n检查目标点的连通性...")
orphaned_targets = []
for blossom in new_data["blossoms"]:
target_id = blossom["id"]
# 检查是否有边指向此目标点
has_edge = False
for edge in new_data["edges"]:
if edge["target"] == target_id:
has_edge = True
break
if not has_edge:
orphaned_targets.append(blossom)
print(f" 警告: 目标点 ID={target_id}, 区域={blossom['region']}, 坐标=({blossom['position']['x']}, {blossom['position']['y']}) 没有入边")
# 处理没有边的目标点 - 尝试使用连续编号的路径
if orphaned_targets:
print(f"\n尝试为 {len(orphaned_targets)} 个孤立目标点寻找连续路径...")
# 先按区域分组目标点
region_targets = {}
for orphan in orphaned_targets:
region = orphan["region"]
if region not in region_targets:
region_targets[region] = []
region_targets[region].append(orphan)
# 然后按区域处理
for region, targets in region_targets.items():
print(f"\n处理区域 {region}{len(targets)} 个孤立目标点...")
# 获取该区域的所有边
region_edges = []
for edge in new_data["edges"]:
# 找到起点对应的传送点
source_teleport = None
for teleport in new_data["teleports"]:
if teleport["id"] == edge["source"]:
source_teleport = teleport
break
if source_teleport and source_teleport["region"] == region:
region_edges.append(edge)
if not region_edges:
print(f" 区域 {region} 没有现有路径,无法连接孤立目标点")
continue
# 从每条边的路径中提取区域和编号
route_info = []
for edge in region_edges:
route = edge["route"]
file_name = os.path.basename(route)
result = parse_region_area_number(file_name)
if len(result) == 5: # 特殊格式
region_name, region_num, area, route_num, route_sub_num = result
else: # 标准格式
region_name, region_num, area, route_num = result
route_sub_num = None
if region_name and region_num and area and route_num:
info = {
"edge": edge,
"region_name": region_name,
"region_num": region_num,
"area": area,
"route_num": route_num,
"file_name": file_name
}
if route_sub_num is not None:
info["route_sub_num"] = route_sub_num
route_info.append(info)
# 按区域和地区分组
area_routes = {}
for info in route_info:
key = f"{info['region_name']}{info['region_num']}-{info['area']}"
if key not in area_routes:
area_routes[key] = []
area_routes[key].append(info)
# 对每个地区内的路线按编号排序
for key, routes in area_routes.items():
routes.sort(key=lambda x: x["route_num"])
# 查找区域内每个孤立目标点附近的路径
for orphan in targets:
print(f" 处理目标点 ID={orphan['id']}, 坐标=({orphan['position']['x']}, {orphan['position']['y']})")
# 找到最近的传送点
nearest_teleport = None
min_distance = float('inf')
for teleport in new_data["teleports"]:
if teleport["region"] == orphan["region"]:
distance = calculate_distance(
teleport["position"]["x"], teleport["position"]["y"],
orphan["position"]["x"], orphan["position"]["y"]
)
if distance < min_distance:
min_distance = distance
nearest_teleport = teleport
if not nearest_teleport:
print(f" 未找到区域 {orphan['region']} 内的传送点")
continue
# 查找以这个传送点为起点的所有路径
teleport_routes = []
for key, routes in area_routes.items():
for info in routes:
if info["edge"]["source"] == nearest_teleport["id"]:
teleport_routes.append(info)
if not teleport_routes:
print(f" 未找到以传送点 ID={nearest_teleport['id']} 为起点的路径")
continue
# 按路线编号排序
teleport_routes.sort(key=lambda x: x["route_num"])
# 找到编号最大的路径
last_route = teleport_routes[-1]
next_num = last_route["route_num"] + 1
print(f" 找到传送点 ID={nearest_teleport['id']} 的最后一条路径: {last_route['file_name']}")
# 检查是否是特殊格式路径
if "-" in last_route['area']:
# 特殊格式路径(如"清泉镇-4"
area_parts = last_route['area'].split("-")
if len(area_parts) == 2:
print(f" 建议手动创建新路径: {last_route['region_name']}{last_route['region_num']}-{area_parts[0]}-{area_parts[1]}-{next_num}.json")
else:
print(f" 建议手动创建新路径: {last_route['region_name']}{last_route['region_num']}-{last_route['area']}-{next_num}.json")
else:
# 标准格式路径
print(f" 建议手动创建新路径: {last_route['region_name']}{last_route['region_num']}-{last_route['area']}-{next_num}.json")
print(f" 从传送点 ({nearest_teleport['position']['x']}, {nearest_teleport['position']['y']}) 到目标点 ({orphan['position']['x']}, {orphan['position']['y']})")
# 按区域排序传送点和地脉花
print("\n按区域对节点进行排序...")
# 对传送点按区域排序
new_data["teleports"] = sorted(new_data["teleports"], key=lambda x: get_region_sort_key(x["region"]))
# 对地脉花按区域排序
new_data["blossoms"] = sorted(new_data["blossoms"], key=lambda x: get_region_sort_key(x["region"]))
# 更新排序后的ID可选
for i, teleport in enumerate(new_data["teleports"]):
teleport["id"] = i + 1
for i, blossom in enumerate(new_data["blossoms"]):
blossom["id"] = i + 1000
# 更新边的引用
for edge in new_data["edges"]:
# 查找新的source ID
for teleport in new_data["teleports"]:
if calculate_distance(teleport["position"]["x"], teleport["position"]["y"],
edge["sourcePosition"]["x"], edge["sourcePosition"]["y"]) < 10:
edge["source"] = teleport["id"]
break
# 查找新的target ID
for blossom in new_data["blossoms"]:
if calculate_distance(blossom["position"]["x"], blossom["position"]["y"],
edge["targetPosition"]["x"], edge["targetPosition"]["y"]) < 10:
edge["target"] = blossom["id"]
break
# 在更新ID后删除位置信息
print("\n删除边数据中的位置信息...")
for edge in new_data["edges"]:
if "sourcePosition" in edge:
del edge["sourcePosition"]
if "targetPosition" in edge:
del edge["targetPosition"]
# 按照target的顺序排列edges
print("\n按照目标节点(target)的顺序排列边...")
new_data["edges"] = sorted(new_data["edges"], key=lambda x: x["target"])
# 创建节点到节点的顺序边
sequential_edges_count = create_sequential_edges(new_data)
# 重新排序所有边(包括新的顺序边)
print("\n重新排序所有边...")
new_data["edges"] = sorted(new_data["edges"], key=lambda x: (x.get("type", "teleport"), x["target"]))
# 重建索引
new_data["indexes"] = {
"edgesBySource": {},
"edgesByTarget": {}
}
for edge in new_data["edges"]:
source_id = edge["source"]
target_id = edge["target"]
if str(source_id) not in new_data["indexes"]["edgesBySource"]:
new_data["indexes"]["edgesBySource"][str(source_id)] = []
new_data["indexes"]["edgesBySource"][str(source_id)].append(target_id)
if str(target_id) not in new_data["indexes"]["edgesByTarget"]:
new_data["indexes"]["edgesByTarget"][str(target_id)] = []
new_data["indexes"]["edgesByTarget"][str(target_id)].append(source_id)
# 保存新数据结构
output_file = os.path.join(script_dir, "LeyLineOutcropData.json")
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(new_data, f, ensure_ascii=False, indent=2)
print(f"\n已生成新的数据结构: {output_file}")
print(f"传送点数量: {len(new_data['teleports'])}")
print(f"地脉花数量: {len(new_data['blossoms'])}")
print(f"总节点数量: {len(new_data['teleports']) + len(new_data['blossoms'])}")
print(f"传送点到地脉花边数量: {len([e for e in new_data['edges'] if e.get('type', 'teleport') == 'teleport'])}")
print(f"节点间顺序边数量: {sequential_edges_count}")
print(f"总边数量: {len(new_data['edges'])}")
print(f"区域排序顺序: {', '.join([k for k, v in sorted(REGION_ORDER.items(), key=lambda item: item[1]) if v < 99])}")
# 报告孤立目标点
remaining_orphans = 0
orphaned_blossoms = []
for blossom in new_data["blossoms"]:
target_id = blossom["id"]
has_edge = False
for edge in new_data["edges"]:
if edge["target"] == target_id:
has_edge = True
break
if not has_edge:
remaining_orphans += 1
orphaned_blossoms.append(blossom)
if remaining_orphans > 0:
print(f"\n注意: 仍有 {remaining_orphans} 个目标点没有入边")
print("这些目标点按区域排序如下:")
# 按区域分组展示孤立目标点
orphaned_by_region = {}
for orphan in orphaned_blossoms:
region = orphan["region"]
base_region = re.match(r"^([^0-9]+)", region)
if base_region:
base_region = base_region.group(1)
if base_region not in orphaned_by_region:
orphaned_by_region[base_region] = []
orphaned_by_region[base_region].append(orphan)
# 按照指定顺序显示区域
for region_name, _ in sorted(REGION_ORDER.items(), key=lambda item: item[1]):
if region_name in orphaned_by_region:
print(f"\n {region_name}区域的孤立目标点:")
for orphan in orphaned_by_region[region_name]:
print(f" ID={orphan['id']}, 区域={orphan['region']}, 坐标=({orphan['position']['x']}, {orphan['position']['y']})")
print("\n请按照之前的建议手动创建连续编号的路径文件")
else:
print("\n✓ 所有目标点都至少有一条入边")
return new_data
def create_sequential_edges(new_data):
"""创建节点到节点的顺序边,实现正确的分支逻辑
正确的分支规则:
1. 主路线到分支: 蒙德2-3 → 蒙德2-4-1, 蒙德2-4-2
2. 分支到同序号分支: 蒙德2-4-1 → 蒙德2-5-1 (优先) → 蒙德2-5 (备选) → 终点
3. 分支到同序号分支: 蒙德2-4-2 → 蒙德2-5-2 (优先) → 蒙德2-5 (备选) → 终点
4. 分支内部不连接: 蒙德2-4-1 和 蒙德2-4-2 之间不相互连接
5. 主路线到主路线: 当没有分支时的直接连接
基于实际路径文件查找对应的路线
"""
print("\n创建节点到节点的顺序边...")
script_dir = os.path.dirname(os.path.abspath(__file__))
pathing_dir = os.path.join(script_dir, "assets", "pathing")
# 按基本区域分组地脉花节点(区域名+区域编号,如"蒙德2"
region_groups = {}
for blossom in new_data["blossoms"]:
region = blossom["region"]
parts = region.split("-")
if len(parts) >= 2:
# 提取基本区域名:区域名+区域编号(如"蒙德2"
import re
match = re.match(r'^([^0-9]+)(\d+)', parts[0])
if match:
region_name = match.group(1) # 如 "蒙德"
region_num = match.group(2) # 如 "2"
base_region = region_name + region_num # 如 "蒙德2"
if base_region not in region_groups:
region_groups[base_region] = []
region_groups[base_region].append(blossom)
# 创建地脉花节点的映射region -> blossom
region_to_blossom = {}
for blossom in new_data["blossoms"]:
region_to_blossom[blossom["region"]] = blossom
# 收集所有存在的路径文件
available_routes = {}
for root, _, files in os.walk(pathing_dir):
for file in sorted(files):
if not file.endswith('.json') or 'rerun' in file or 'rerun' in root:
continue
if "target" in root.split(os.path.sep):
continue
relative_path = os.path.relpath(os.path.join(root, file), script_dir)
relative_path = relative_path.replace("\\", "/")
available_routes[file] = relative_path
sequential_edges = []
# 为每个区域创建顺序边
for base_region, blossoms in region_groups.items():
print(f"\n处理区域: {base_region}")
# 将节点分为主路线和分支路线
main_routes = {} # {route_num: blossom}
branch_routes = {} # {route_num: {branch_num: blossom}}
for blossom in blossoms:
region = blossom["region"]
parts = region.split("-")
if len(parts) == 2: # 主路线格式:"蒙德2-3"
try:
route_num = int(parts[1])
main_routes[route_num] = blossom
print(f" 主路线: {region} (路线{route_num})")
except ValueError:
print(f" 警告:无法解析主路线编号: {region}")
elif len(parts) == 3: # 分支路线格式:"蒙德2-4-1"
try:
route_num = int(parts[1])
branch_num = int(parts[2])
if route_num not in branch_routes:
branch_routes[route_num] = {}
branch_routes[route_num][branch_num] = blossom
print(f" 分支路线: {region} (路线{route_num}, 分支{branch_num})")
except ValueError:
print(f" 警告:无法解析分支路线编号: {region}")
print(f" 找到 {len(main_routes)} 个主路线,{len(branch_routes)} 个分支组")
# 辅助函数:查找路径文件
def find_route_file(source_region, target_region):
# 从目标区域推断文件名
result = None
print(f" 查找路径文件: {source_region}{target_region}")
for blossom in new_data["blossoms"]:
if blossom["region"] == target_region:
# 尝试通过目标区域构造文件名
parts = target_region.split("-")
print(f" 目标区域部分: {parts}")
if len(parts) == 2: # 主路线
# 例如: 蒙德2-3 -> 蒙德2-xxx-3.json
pattern = f"-{parts[1]}.json"
print(f" 主路线模式: {pattern}")
for filename in available_routes.keys():
if pattern in filename and parts[0] in filename:
result = available_routes[filename]
print(f" 找到匹配文件: {filename} -> {result}")
break
elif len(parts) == 3: # 分支路线
# 例如: 蒙德2-4-1 -> 蒙德2-xxx-4-1.json
pattern = f"-{parts[1]}-{parts[2]}.json"
print(f" 分支路线模式: {pattern}")
for filename in available_routes.keys():
if pattern in filename and parts[0] in filename:
result = available_routes[filename]
print(f" 找到匹配文件: {filename} -> {result}")
break
if not result:
print(f" 警告:未找到匹配的路径文件")
print(f" 可用文件: {list(available_routes.keys())[:10]}...") # 只显示前10个
break
return result# 1. 创建主路线到分支路线的连接
for main_route_num, main_blossom in main_routes.items():
# 找到下一个路线号,看是否有分支
next_route_num = main_route_num + 1
if next_route_num in branch_routes:
# 连接到下一个路线号的所有分支
for branch_num, branch_blossom in branch_routes[next_route_num].items():
route_file = find_route_file(main_blossom["region"], branch_blossom["region"])
if route_file:
edge = {
"source": main_blossom["id"],
"target": branch_blossom["id"],
"route": route_file
}
sequential_edges.append(edge)
print(f" 主路线到分支: {main_blossom['region']}{branch_blossom['region']}")
# 2. 创建分支到下一个分支的连接(同序号优先)
for route_num, branches in branch_routes.items():
next_route_num = route_num + 1
for branch_num, branch_blossom in branches.items():
# 优先连接到同序号的下一个分支
target_found = False
# 第一优先级:同序号分支 (蒙德2-4-1 → 蒙德2-5-1)
if next_route_num in branch_routes and branch_num in branch_routes[next_route_num]:
target_blossom = branch_routes[next_route_num][branch_num]
route_file = find_route_file(branch_blossom["region"], target_blossom["region"])
if route_file:
edge = {
"source": branch_blossom["id"],
"target": target_blossom["id"],
"route": route_file
}
sequential_edges.append(edge)
print(f" 分支到同序号分支: {branch_blossom['region']}{target_blossom['region']}")
target_found = True
# 第二优先级:主路线 (蒙德2-4-1 → 蒙德2-5)
if not target_found and next_route_num in main_routes:
target_blossom = main_routes[next_route_num]
route_file = find_route_file(branch_blossom["region"], target_blossom["region"])
if route_file:
edge = {
"source": branch_blossom["id"],
"target": target_blossom["id"],
"route": route_file
}
sequential_edges.append(edge)
print(f" 分支到主路线: {branch_blossom['region']}{target_blossom['region']}")
target_found = True
# 如果没有找到目标,则为路线终点
if not target_found:
print(f" 分支终点: {branch_blossom['region']} (无下一个目标)")
# 3. 创建主路线到主路线的连接(当没有分支时)
sorted_main_routes = sorted(main_routes.keys())
for i in range(len(sorted_main_routes) - 1):
current_route = sorted_main_routes[i]
next_route = sorted_main_routes[i + 1]
# 只有在下一个路线没有分支时,才创建主路线到主路线的连接
if next_route not in branch_routes:
source_blossom = main_routes[current_route]
target_blossom = main_routes[next_route]
route_file = find_route_file(source_blossom["region"], target_blossom["region"])
if route_file:
edge = {
"source": source_blossom["id"],
"target": target_blossom["id"],
"route": route_file
}
sequential_edges.append(edge)
print(f" 主路线连接: {source_blossom['region']}{target_blossom['region']}")
# 将新的顺序边添加到数据中
new_data["edges"].extend(sequential_edges)
print(f"\n总共创建了 {len(sequential_edges)} 条顺序边")
return len(sequential_edges)
def test_filename_parsing():
"""测试文件名解析功能,确保能正确处理各种格式"""
test_files = [
"蒙德1-风啸山岭-1.json", # 标准格式
"璃月3-轻策庄-2.json", # 标准格式
"蒙德2-清泉镇-4-1.json", # 特殊格式(双连字符数字)
"须弥2-须弥城-3-2.json", # 特殊格式
"纳塔1-区域-5.5.json" # 小数点路线编号
]
print("测试文件名解析结果:")
for filename in test_files:
result = parse_region_area_number(filename)
if len(result) == 5: # 特殊格式
region_name, region_num, area, route_num, route_sub_num = result
print(f"文件: {filename} (特殊格式)")
print(f" 区域名称: {region_name}")
print(f" 区域编号: {region_num}")
print(f" 地区: {area}")
print(f" 路线编号: {route_num}")
print(f" 路线子编号: {route_sub_num}")
else: # 标准格式
region_name, region_num, area, route_num = result
print(f"文件: {filename} (标准格式)")
print(f" 区域名称: {region_name}")
print(f" 区域编号: {region_num}")
print(f" 地区: {area}")
print(f" 路线编号: {route_num}")
# 测试格式化逻辑
if region_name and region_num and route_num:
if len(result) == 5: # 特殊格式
route_sub_num = result[4]
# 特殊格式显示为: 蒙德2-5-1 (区域名-区域编号-路线编号-路线子编号)
formatted = f"{region_name}{region_num}-{int(route_num)}-{int(route_sub_num)}"
else: # 标准格式
# 标准格式显示为: 枫丹1-1 (区域名-区域编号-路线编号)
formatted = f"{region_name}{region_num}-{int(route_num)}"
print(f" 格式化区域: {formatted}")
else:
print(f" 解析失败")
print("")
if __name__ == "__main__":
# 测试文件名解析
if len(sys.argv) > 1 and sys.argv[1] == "--test":
test_filename_parsing()
else:
generate_new_data_structure_from_pathing()

View File

@@ -179,13 +179,72 @@ async function executeMatchingStrategy() {
async function loadNodeData() { async function loadNodeData() {
try { try {
const nodeDataText = await file.readText("LeyLineOutcropData.json"); const nodeDataText = await file.readText("LeyLineOutcropData.json");
return JSON.parse(nodeDataText); const rawData = JSON.parse(nodeDataText);
// 适配数据结构:将原始数据转换为代码期望的格式
return adaptNodeData(rawData);
} catch (error) { } catch (error) {
log.error(`加载节点数据失败: ${error.message}`); log.error(`加载节点数据失败: ${error.message}`);
throw new Error("无法加载 LeyLineOutcropData.json 文件"); throw new Error("无法加载 LeyLineOutcropData.json 文件");
} }
} }
/**
* 适配数据结构:将原始数据转换为代码期望的格式
* @param {Object} rawData - 原始JSON数据
* @returns {Object} 适配后的节点数据
*/
function adaptNodeData(rawData) {
const adaptedData = {
node: [],
indexes: rawData.indexes
};
// 添加传送点设置type为"teleport"
if (rawData.teleports) {
for (const teleport of rawData.teleports) {
adaptedData.node.push({
...teleport,
type: "teleport",
next: [],
prev: []
});
}
}
// 添加地脉花节点设置type为"blossom"
if (rawData.blossoms) {
for (const blossom of rawData.blossoms) {
adaptedData.node.push({
...blossom,
type: "blossom",
next: [],
prev: []
});
}
}
// 根据edges构建next和prev关系
if (rawData.edges) {
for (const edge of rawData.edges) {
const sourceNode = adaptedData.node.find(node => node.id === edge.source);
const targetNode = adaptedData.node.find(node => node.id === edge.target);
if (sourceNode && targetNode) {
sourceNode.next.push({
target: edge.target,
route: edge.route
});
targetNode.prev.push(edge.source);
}
}
}
log.debug(`适配数据完成:传送点 ${rawData.teleports ? rawData.teleports.length : 0} 个,地脉花 ${rawData.blossoms ? rawData.blossoms.length : 0} 个,边缘 ${rawData.edges ? rawData.edges.length : 0}`);
return adaptedData;
}
/** /**
* 根据位置找到对应的目标节点 * 根据位置找到对应的目标节点
* @param {Object} nodeData - 节点数据 * @param {Object} nodeData - 节点数据
@@ -654,7 +713,6 @@ async function adjustViewForReward(boxIconRo, token) {
const screenCenterX = 960; const screenCenterX = 960;
const screenCenterY = 540; const screenCenterY = 540;
const maxAngle = 10; // 最大允许偏离角度(度) const maxAngle = 10; // 最大允许偏离角度(度)
for (let i = 0; i < 20; i++) { for (let i = 0; i < 20; i++) {
// 检查是否取消操作 // 检查是否取消操作
if (token && token.isCancellationRequested) { if (token && token.isCancellationRequested) {

View File

@@ -0,0 +1,28 @@
if not region_edges:
print(f" 区域 {region} 没有现有路径,无法连接孤立目标点")
continue
# 从每条边的路径中提取区域和编号
route_info = []
for edge in region_edges:
route = edge["route"]
file_name = os.path.basename(route)
result = parse_region_area_number(file_name)
if len(result) == 5: # 特殊格式
region_name, region_num, area, route_num, route_sub_num = result
else: # 标准格式
region_name, region_num, area, route_num = result
route_sub_num = None
if region_name and region_num and area and route_num:
info = {
"edge": edge,
"region_name": region_name,
"region_num": region_num,
"area": area,
"route_num": route_num,
"file_name": file_name
}
if route_sub_num is not None:
info["route_sub_num"] = route_sub_num
route_info.append(info)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -53,6 +53,18 @@
"name": "isNotification", "name": "isNotification",
"type": "checkbox", "type": "checkbox",
"label": "通过BGI通知系统发送详细通知" "label": "通过BGI通知系统发送详细通知"
} },
[
{
"name": "isRecognizeResin",
"type": "checkbox",
"label": "树脂耗尽模式\n开启后脚本会在树脂耗尽时停止运行【开发中】"
},
{
"name": "isGoToSynthesizer",
"type": "select",
"label": "是否前往合成台合成浓缩树脂\n 体力大于40时自动合成浓缩树脂【开发中】"
}
]
] ]

View File

@@ -0,0 +1,58 @@
def test_filename_parsing():
"""测试文件名解析功能,确保能正确处理各种格式"""
test_files = [
"蒙德1-风啸山岭-1.json", # 标准格式
"璃月3-轻策庄-2.json", # 标准格式
"蒙德2-清泉镇-4-1.json", # 特殊格式(双连字符数字)
"须弥2-须弥城-3-2.json", # 特殊格式
"纳塔1-区域-5.5.json" # 小数点路线编号
]
print("测试文件名解析结果:")
for filename in test_files:
result = parse_region_area_number(filename)
if len(result) == 5: # 特殊格式
region_name, region_num, area, route_num, route_sub_num = result
print(f"文件: {filename} (特殊格式)")
print(f" 区域名称: {region_name}")
print(f" 区域编号: {region_num}")
print(f" 地区: {area}")
print(f" 路线编号: {route_num}")
print(f" 路线子编号: {route_sub_num}")
else: # 标准格式
region_name, region_num, area, route_num = result
print(f"文件: {filename} (标准格式)")
print(f" 区域名称: {region_name}")
print(f" 区域编号: {region_num}")
print(f" 地区: {area}")
print(f" 路线编号: {route_num}")
# 测试格式化逻辑
if region_name and region_num and route_num:
if len(result) == 5: # 特殊格式
route_sub_num = result[4]
if area and "-" in area:
# 特殊格式
area_parts = area.split("-")
if len(area_parts) == 2:
formatted = f"{region_name}{region_num}-{area_parts[1]}-{int(route_num)}-{int(route_sub_num)}"
else:
formatted = f"{region_name}{region_num}-{area}-{int(route_num)}-{int(route_sub_num)}"
else:
# 非预期情况,但仍处理
formatted = f"{region_name}{region_num}-{area}-{int(route_num)}-{int(route_sub_num)}"
else: # 标准格式
if area and "-" in area:
# 特殊格式
area_parts = area.split("-")
if len(area_parts) == 2:
formatted = f"{region_name}{region_num}-{area_parts[1]}-{int(route_num)}"
else:
formatted = f"{region_name}{region_num}-{int(route_num)}"
else:
# 标准格式
formatted = f"{region_name}{region_num}-{int(route_num)}"
print(f" 格式化区域: {formatted}")
else:
print(f" 解析失败")
print("")

View File

@@ -0,0 +1,58 @@
def test_filename_parsing():
"""测试文件名解析功能,确保能正确处理各种格式"""
test_files = [
"蒙德1-风啸山岭-1.json", # 标准格式
"璃月3-轻策庄-2.json", # 标准格式
"蒙德2-清泉镇-4-1.json", # 特殊格式(双连字符数字)
"须弥2-须弥城-3-2.json", # 特殊格式
"纳塔1-区域-5.5.json" # 小数点路线编号
]
print("测试文件名解析结果:")
for filename in test_files:
result = parse_region_area_number(filename)
if len(result) == 5: # 特殊格式
region_name, region_num, area, route_num, route_sub_num = result
print(f"文件: {filename} (特殊格式)")
print(f" 区域名称: {region_name}")
print(f" 区域编号: {region_num}")
print(f" 地区: {area}")
print(f" 路线编号: {route_num}")
print(f" 路线子编号: {route_sub_num}")
else: # 标准格式
region_name, region_num, area, route_num = result
print(f"文件: {filename} (标准格式)")
print(f" 区域名称: {region_name}")
print(f" 区域编号: {region_num}")
print(f" 地区: {area}")
print(f" 路线编号: {route_num}")
# 测试格式化逻辑
if region_name and region_num and route_num:
if len(result) == 5: # 特殊格式
route_sub_num = result[4]
if area and "-" in area:
# 特殊格式
area_parts = area.split("-")
if len(area_parts) == 2:
formatted = f"{region_name}{region_num}-{area_parts[1]}-{int(route_num)}-{int(route_sub_num)}"
else:
formatted = f"{region_name}{region_num}-{area}-{int(route_num)}-{int(route_sub_num)}"
else:
# 非预期情况,但仍处理
formatted = f"{region_name}{region_num}-{area}-{int(route_num)}-{int(route_sub_num)}"
else: # 标准格式
if area and "-" in area:
# 特殊格式
area_parts = area.split("-")
if len(area_parts) == 2:
formatted = f"{region_name}{region_num}-{area_parts[1]}-{int(route_num)}"
else:
formatted = f"{region_name}{region_num}-{int(route_num)}"
else:
# 标准格式
formatted = f"{region_name}{region_num}-{int(route_num)}"
print(f" 格式化区域: {formatted}")
else:
print(f" 解析失败")
print("")

View File

@@ -39,8 +39,7 @@ async function (position) {
let currentNode = targetNode; let currentNode = targetNode;
while (currentNode.next && currentRunTimes < settings.timesValue) { while (currentNode.next && currentRunTimes < settings.timesValue) {
if (currentNode.next.length === 1) { if (currentNode.next.length === 1) { // 获取下一个节点的ID 和 路径,并在节点数据中找到下一个节点
// 获取下一个节点的ID 和 路径,并在节点数据中找到下一个节点
const nextNodeId = currentNode.next[0].target; const nextNodeId = currentNode.next[0].target;
const nextRoute = currentNode.next[0].route; const nextRoute = currentNode.next[0].route;
const nextNode = nodeData.node.find(node => node.id === nextNodeId); const nextNode = nodeData.node.find(node => node.id === nextNodeId);
@@ -59,14 +58,11 @@ async function (position) {
currentRunTimes++; currentRunTimes++;
log.info(`完成节点 ID ${nextNodeId}, 已执行 ${currentRunTimes}/${settings.timesValue}`); log.info(`完成节点 ID ${nextNodeId}, 已执行 ${currentRunTimes}/${settings.timesValue}`); // 更新当前节点为下一个节点,继续检查
// 更新当前节点为下一个节点,继续检查
currentNode = nextNode; currentNode = nextNode;
currentNodePosition = { x: nextNode.position.x, y: nextNode.position.y }; currentNodePosition = { x: nextNode.position.x, y: nextNode.position.y };
} } else if (currentNode.next.length > 1) {
else if (currentNode.next.length > 1) { // 如果存在分支路线,先打开大地图判断下一个地脉花的位置,然后结合顺序边缘数据选择最优路线
// 如果存在分支路线,先打开大地图判断下一个地脉花的位置,根据下一个地脉花的位置选择路线
log.info("检测到多个分支路线,开始查找下一个地脉花位置"); log.info("检测到多个分支路线,开始查找下一个地脉花位置");
// 备份当前地脉花坐标 // 备份当前地脉花坐标
@@ -89,47 +85,92 @@ async function (position) {
log.info(`找到下一个地脉花,位置: (${leyLineX}, ${leyLineY})`); log.info(`找到下一个地脉花,位置: (${leyLineX}, ${leyLineY})`);
// 计算每个分支节点到地脉花的距离,选择最近的路径 // 优先使用顺序边缘数据来选择路径,如果没有则回退到距离计算
let closestRoute = null; const sequentialEdges = nodeData.indexes?.edgesBySource;
let closestDistance = Infinity; let selectedRoute = null;
let closestNodeId = null; let selectedNodeId = null;
for (const nextRoute of currentNode.next) { if (sequentialEdges) {
const nextNodeId = nextRoute.target; const currentNodeIdStr = currentNode.id.toString();
const nextNode = nodeData.node.find(node => node.id === nextNodeId); const nextTargetIds = sequentialEdges[currentNodeIdStr];
if (!nextNode) continue; if (nextTargetIds && nextTargetIds.length > 0) {
const nextTargetId = nextTargetIds[0];
log.info(`从顺序边缘数据中找到推荐的下一个目标节点ID: ${nextTargetId}`);
const distance = calculate2DDistance( // 在当前节点的分支中查找通向推荐目标节点的路径
leyLineX, leyLineY, for (const nextRoute of currentNode.next) {
nextNode.position.x, nextNode.position.y const nextNodeId = nextRoute.target;
);
// 检查这个路径是否通向推荐的目标节点(直接匹配或通过后续路径)
log.info(`路线到地脉花距离: ID ${nextNodeId}, 距离: ${distance.toFixed(2)}`); if (nextNodeId === nextTargetId) {
selectedRoute = nextRoute.route;
if (distance < closestDistance) { selectedNodeId = nextNodeId;
closestDistance = distance; log.info(`使用顺序边缘数据找到直接路径到推荐节点ID: ${nextTargetId}`);
closestRoute = nextRoute.route; break;
closestNodeId = nextNodeId; } else {
// 检查这个中间节点是否能通向推荐的目标节点
const intermediateNodeIdStr = nextNodeId.toString();
const intermediateNextTargets = sequentialEdges[intermediateNodeIdStr];
if (intermediateNextTargets && intermediateNextTargets.includes(nextTargetId)) {
selectedRoute = nextRoute.route;
selectedNodeId = nextNodeId;
log.info(`使用顺序边缘数据找到通过中间节点ID ${nextNodeId} 到达推荐节点ID ${nextTargetId} 的路径`);
break;
}
}
}
} }
} }
if (!closestRoute) { // 如果顺序边缘数据没有找到合适的路径,回退到距离计算
if (!selectedRoute) {
log.info("顺序边缘数据未找到合适路径,使用距离计算方法选择路径");
let closestDistance = Infinity; for (const nextRoute of currentNode.next) {
const nextNodeId = nextRoute.target;
const nextNode = nodeData.node.find(node => node.id === nextNodeId);
if (!nextNode) continue;
const distance = calculate2DDistance(
leyLineX, leyLineY,
nextNode.position.x, nextNode.position.y
);
log.info(`路线到地脉花距离: ID ${nextNodeId}, 距离: ${distance.toFixed(2)}`);
if (distance < closestDistance) {
closestDistance = distance;
selectedRoute = nextRoute.route;
selectedNodeId = nextNodeId;
}
}
}
if (!selectedRoute) {
log.error("无法找到合适的路线,终止执行"); log.error("无法找到合适的路线,终止执行");
// 恢复原始坐标 // 恢复原始坐标
leyLineX = currentLeyLineX; leyLineX = currentLeyLineX;
leyLineY = currentLeyLineY; leyLineY = currentLeyLineY;
return; return;
} const nextNode = nodeData.node.find(node => node.id === selectedNodeId);
if (!nextNode) {
log.error(`未找到节点ID ${selectedNodeId},终止执行`);
// 恢复原始坐标
leyLineX = currentLeyLineX;
leyLineY = currentLeyLineY;
return;
} }
const nextNode = nodeData.node.find(node => node.id === closestNodeId); log.info(`选择路线: ${selectedRoute}, 目标节点ID: ${selectedNodeId}`);
log.info(`选择最近的路线: ${closestRoute}, 目标节点ID: ${closestNodeId}`);
// 创建路径对象并执行 // 创建路径对象并执行
const pathObject = { const pathObject = {
startNode: currentNode, startNode: currentNode,
targetNode: nextNode, targetNode: nextNode,
routes: [closestRoute] routes: [selectedRoute]
}; };
await executePath(pathObject); await executePath(pathObject);

View File

@@ -0,0 +1,149 @@
/*
代码迁移中,还未完成适配
*/
const CondensedRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("RecognitionObject/Condensed Resin.png"));
const FragileRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("RecognitionObject/Fragile Resin.png"));
const TemporaryRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("RecognitionObject/5.png"));
CondensedRo.threshold = 0.70;
CondensedRo.Use3Channels = true;
FragileRo.threshold = 0.70;
FragileRo.Use3Channels = true;
this.recognitionResin =
async function() {
let totalRunNum = 0;
await genshin.returnMainUi();
await sleep(2000);
keyPress("m");
await sleep(2000);
let captureRegion = captureGameRegion();
let resList = captureRegion.findMulti(RecognitionObject.ocr(1043, 5, 300, 100));
let IsOver = false
for (let i = 0; i < resList.count; i++) {
let resStamina = resList[i];
log.info(`${i + 1} 个结果: ${resStamina.text}`);
await sleep(2000);
// 提取/前面的数字
const rawText = resStamina.text;
const splitResult = rawText.split('/'); // 用/分割字符串
// 确保分割后得到两部分且第一部分是有效数字
if (splitResult.length >= 1) {
const staminaValue = parseInt(splitResult[0]); // 只取第一部分
if (!isNaN(staminaValue)) {
log.info(`提取的体力值: ${staminaValue}`);
if (staminaValue >= 40) {
IsOver = true;
}
break;
} else {
log.warn("无效的数字格式");
}
} else {
log.warn("未找到/分隔符");
}
await sleep(2000)
await genshin.returnMainUi();
keyPress("b");
await sleep(2000);
click(1245, 50);
await sleep(2000);
// 浓缩树脂识别
let Condensed = captureGameRegion().find(CondensedRo);
let Isfive = false;
if (Condensed.isExist()) {
log.info("识别到浓缩树脂");
let CondensedX = Math.round(Condensed.x + Condensed.width / 2 - 20)
let Condensedy = Math.round(Condensed.y + Condensed.height / 2 + 60)
log.info(`点击坐标: (${CondensedX}, ${Condensedy})`);
let captureRegion = captureGameRegion();
let Condensedres = captureRegion.findMulti(RecognitionObject.ocr(CondensedX, Condensedy, 50, 50));
for (let i = 0; i < Condensedres.count; i++) {
let resCondensed = Condensedres[i];
log.info(`浓缩树脂: ${resCondensed.text}`);
await sleep(2000);
if (resCondensed.text == 5) {
Isfive = true;
log.info("浓缩树脂已满")
await sleep(2000);
}
}
}
// 脆弱树脂识别
let Fragile = captureGameRegion().find(FragileRo);
if (Fragile.isExist()) {
log.info("识别到脆弱树脂");
let FragileX = Math.round(Fragile.x + Fragile.width / 2 - 20)
let Fragiley = Math.round(Fragile.y + Fragile.height / 2 + 60)
let captureRegion = captureGameRegion();
let Fragileres = captureRegion.findMulti(RecognitionObject.ocr(FragileX, Fragiley, 50, 50));
if (Fragileres.count === 0) {
log.error("OCR识别失败未能识别到脆弱树脂数量");
} else {
for (let i = 0; i < Fragileres.count; i++) {
let resFragile = Fragileres[i];
if (resFragile.text && resFragile.text.trim() !== "") {
log.info("脆弱树脂数量: " + resFragile.text);
} else {
log.warn("OCR识别结果为空或无效");
}
}
}
} else {
log.info("未识别到脆弱树脂");
await sleep(2000);
}
// 须臾树脂识别
let Temporary = captureGameRegion().find(TemporaryRo);
if (Temporary.isExist()) {
log.info("识别到须臾树脂");
let TemporaryX = Math.round(Temporary.x + Temporary.width / 2 - 20)
let Temporaryy = Math.round(Temporary.y + Temporary.height / 2 + 40)
log.info(`点击坐标: (${TemporaryX}, ${Temporaryy})`);
let captureRegion = captureGameRegion();
let Temporaryres = captureRegion.findMulti(RecognitionObject.ocr(TemporaryX, Temporaryy, 50, 50));
} else {
log.info("未识别到脆弱树脂");
}
if (Temporaryres.count === 0) {
log.error("OCR识别失败未能识别到须臾树脂数量");
} else {
for (let i = 0; i < Temporaryres.count; i++) {
let resTemporary = Temporaryres[i];
if (resTemporary.text && resTemporary.text.trim() !== "") {
log.info("须臾树脂数量: " + resTemporary.text);
} else {
log.warn("OCR识别结果为空或无效");
}
await sleep(2000);
}
}
// 尝试调用任务
if (IsOver && Isfive == true) {
log.info("需要前往合成台"); // 输出 true
} else {
log.info("不需要前往合成台");
}
}
}