Merge branch 'dev'

This commit is contained in:
XcantloadX 2025-07-07 10:02:47 +08:00
commit c6b52a599f
37 changed files with 196 additions and 81 deletions

View File

@ -1 +1 @@
{"definitions":{"0949c622-9067-4f0d-bac2-3f938a1d2ed2":{"name":"Shop.ItemLessonNote","displayName":"レッスンノート","type":"template","annotationId":"0949c622-9067-4f0d-bac2-3f938a1d2ed2","useHintRect":false},"b2af59e9-60e3-4d97-8c72-c7ba092113a3":{"name":"Shop.ItemVeteranNote","displayName":"ベテランノート","type":"template","annotationId":"b2af59e9-60e3-4d97-8c72-c7ba092113a3","useHintRect":false},"835489e2-b29b-426c-b4c9-3bb9f8eb6195":{"name":"Shop.ItemSupportEnhancementPt","displayName":"サポート強化Pt 支援强化Pt","type":"template","annotationId":"835489e2-b29b-426c-b4c9-3bb9f8eb6195","useHintRect":false},"c5b7d67e-7260-4f08-a0e9-4d31ce9bbecf":{"name":"Shop.ItemSenseNoteVocal","displayName":"センスノート(ボーカル)感性笔记(声乐)","type":"template","annotationId":"c5b7d67e-7260-4f08-a0e9-4d31ce9bbecf","useHintRect":false},"0f7d581d-cea3-4039-9205-732e4cd29293":{"name":"Shop.ItemSenseNoteDance","displayName":"センスノート(ダンス)感性笔记(舞蹈)","type":"template","annotationId":"0f7d581d-cea3-4039-9205-732e4cd29293","useHintRect":false},"d3cc3323-51af-4882-ae12-49e7384b746d":{"name":"Shop.ItemSenseNoteVisual","displayName":"センスノート(ビジュアル)感性笔记(形象)","type":"template","annotationId":"d3cc3323-51af-4882-ae12-49e7384b746d","useHintRect":false},"a1df3af1-a3e7-4521-a085-e4dc3cd1cc57":{"name":"Shop.ItemLogicNoteVocal","displayName":"ロジックノート(ボーカル)理性笔记(声乐)","type":"template","annotationId":"a1df3af1-a3e7-4521-a085-e4dc3cd1cc57","useHintRect":false},"a9fcaf04-0c1f-4b0f-bb5b-ede9da96180f":{"name":"Shop.ItemLogicNoteDance","displayName":"ロジックノート(ダンス)理性笔记(舞蹈)","type":"template","annotationId":"a9fcaf04-0c1f-4b0f-bb5b-ede9da96180f","useHintRect":false},"c3f536d6-a04a-4651-b3f9-dd2c22593f7f":{"name":"Shop.ItemLogicNoteVisual","displayName":"ロジックノート(ビジュアル)理性笔记(形象)","type":"template","annotationId":"c3f536d6-a04a-4651-b3f9-dd2c22593f7f","useHintRect":false},"eef25cf9-afd0-43b1-b9c5-fbd997bd5fe0":{"name":"Shop.ItemAnomalyNoteVocal","displayName":"アノマリーノート(ボーカル)非凡笔记(声乐)","type":"template","annotationId":"eef25cf9-afd0-43b1-b9c5-fbd997bd5fe0","useHintRect":false},"df991b42-ed8e-4f2c-bf0c-aa7522f147b6":{"name":"Shop.ItemAnomalyNoteDance","displayName":"アノマリーノート(ダンス)非凡笔记(舞蹈)","type":"template","annotationId":"df991b42-ed8e-4f2c-bf0c-aa7522f147b6","useHintRect":false}},"annotations":[{"id":"0949c622-9067-4f0d-bac2-3f938a1d2ed2","type":"rect","data":{"x1":243,"y1":355,"x2":313,"y2":441}},{"id":"b2af59e9-60e3-4d97-8c72-c7ba092113a3","type":"rect","data":{"x1":414,"y1":355,"x2":484,"y2":441}},{"id":"835489e2-b29b-426c-b4c9-3bb9f8eb6195","type":"rect","data":{"x1":574,"y1":363,"x2":662,"y2":438}},{"id":"c5b7d67e-7260-4f08-a0e9-4d31ce9bbecf","type":"rect","data":{"x1":71,"y1":594,"x2":142,"y2":667}},{"id":"0f7d581d-cea3-4039-9205-732e4cd29293","type":"rect","data":{"x1":241,"y1":593,"x2":309,"y2":667}},{"id":"d3cc3323-51af-4882-ae12-49e7384b746d","type":"rect","data":{"x1":417,"y1":586,"x2":481,"y2":668}},{"id":"a1df3af1-a3e7-4521-a085-e4dc3cd1cc57","type":"rect","data":{"x1":585,"y1":591,"x2":651,"y2":669}},{"id":"a9fcaf04-0c1f-4b0f-bb5b-ede9da96180f","type":"rect","data":{"x1":69,"y1":825,"x2":138,"y2":899}},{"id":"c3f536d6-a04a-4651-b3f9-dd2c22593f7f","type":"rect","data":{"x1":242,"y1":820,"x2":310,"y2":898}},{"id":"eef25cf9-afd0-43b1-b9c5-fbd997bd5fe0","type":"rect","data":{"x1":413,"y1":821,"x2":481,"y2":897}},{"id":"df991b42-ed8e-4f2c-bf0c-aa7522f147b6","type":"rect","data":{"x1":583,"y1":823,"x2":649,"y2":900}}]}
{"definitions":{"0949c622-9067-4f0d-bac2-3f938a1d2ed2":{"name":"Shop.ItemLessonNote","displayName":"レッスンノート","type":"template","annotationId":"0949c622-9067-4f0d-bac2-3f938a1d2ed2","useHintRect":false},"b2af59e9-60e3-4d97-8c72-c7ba092113a3":{"name":"Shop.ItemVeteranNote","displayName":"ベテランノート","type":"template","annotationId":"b2af59e9-60e3-4d97-8c72-c7ba092113a3","useHintRect":false},"835489e2-b29b-426c-b4c9-3bb9f8eb6195":{"name":"Shop.ItemSupportEnhancementPt","displayName":"サポート強化Pt 支援强化Pt","type":"template","annotationId":"835489e2-b29b-426c-b4c9-3bb9f8eb6195","useHintRect":false},"c5b7d67e-7260-4f08-a0e9-4d31ce9bbecf":{"name":"Shop.ItemSenseNoteVocal","displayName":"センスノート(ボーカル)感性笔记(声乐)","type":"template","annotationId":"c5b7d67e-7260-4f08-a0e9-4d31ce9bbecf","useHintRect":false},"0f7d581d-cea3-4039-9205-732e4cd29293":{"name":"Shop.ItemSenseNoteDance","displayName":"センスノート(ダンス)感性笔记(舞蹈)","type":"template","annotationId":"0f7d581d-cea3-4039-9205-732e4cd29293","useHintRect":false},"d3cc3323-51af-4882-ae12-49e7384b746d":{"name":"Shop.ItemSenseNoteVisual","displayName":"センスノート(ビジュアル)感性笔记(形象)","type":"template","annotationId":"d3cc3323-51af-4882-ae12-49e7384b746d","useHintRect":false},"a1df3af1-a3e7-4521-a085-e4dc3cd1cc57":{"name":"Shop.ItemLogicNoteVocal","displayName":"ロジックノート(ボーカル)理性笔记(声乐)","type":"template","annotationId":"a1df3af1-a3e7-4521-a085-e4dc3cd1cc57","useHintRect":false},"a9fcaf04-0c1f-4b0f-bb5b-ede9da96180f":{"name":"Shop.ItemLogicNoteDance","displayName":"ロジックノート(ダンス)理性笔记(舞蹈)","type":"template","annotationId":"a9fcaf04-0c1f-4b0f-bb5b-ede9da96180f","useHintRect":false},"c3f536d6-a04a-4651-b3f9-dd2c22593f7f":{"name":"Shop.ItemLogicNoteVisual","displayName":"ロジックノート(ビジュアル)理性笔记(形象)","type":"template","annotationId":"c3f536d6-a04a-4651-b3f9-dd2c22593f7f","useHintRect":false},"eef25cf9-afd0-43b1-b9c5-fbd997bd5fe0":{"name":"Shop.ItemAnomalyNoteVocal","displayName":"アノマリーノート(ボーカル)非凡笔记(声乐)","type":"template","annotationId":"eef25cf9-afd0-43b1-b9c5-fbd997bd5fe0","useHintRect":false},"df991b42-ed8e-4f2c-bf0c-aa7522f147b6":{"name":"Shop.ItemAnomalyNoteDance","displayName":"アノマリーノート(ダンス)非凡笔记(舞蹈)","type":"template","annotationId":"df991b42-ed8e-4f2c-bf0c-aa7522f147b6","useHintRect":false},"81c97cd3-df53-44d3-bf3d-1eb4dc67b62a":{"name":"Daily.ButtonRefreshMoneyShop","displayName":"リスト更新1回無料","type":"template","annotationId":"81c97cd3-df53-44d3-bf3d-1eb4dc67b62a","useHintRect":false}},"annotations":[{"id":"0949c622-9067-4f0d-bac2-3f938a1d2ed2","type":"rect","data":{"x1":243,"y1":355,"x2":313,"y2":441}},{"id":"b2af59e9-60e3-4d97-8c72-c7ba092113a3","type":"rect","data":{"x1":414,"y1":355,"x2":484,"y2":441}},{"id":"835489e2-b29b-426c-b4c9-3bb9f8eb6195","type":"rect","data":{"x1":574,"y1":363,"x2":662,"y2":438}},{"id":"c5b7d67e-7260-4f08-a0e9-4d31ce9bbecf","type":"rect","data":{"x1":71,"y1":594,"x2":142,"y2":667}},{"id":"0f7d581d-cea3-4039-9205-732e4cd29293","type":"rect","data":{"x1":241,"y1":593,"x2":309,"y2":667}},{"id":"d3cc3323-51af-4882-ae12-49e7384b746d","type":"rect","data":{"x1":417,"y1":586,"x2":481,"y2":668}},{"id":"a1df3af1-a3e7-4521-a085-e4dc3cd1cc57","type":"rect","data":{"x1":585,"y1":591,"x2":651,"y2":669}},{"id":"a9fcaf04-0c1f-4b0f-bb5b-ede9da96180f","type":"rect","data":{"x1":69,"y1":825,"x2":138,"y2":899}},{"id":"c3f536d6-a04a-4651-b3f9-dd2c22593f7f","type":"rect","data":{"x1":242,"y1":820,"x2":310,"y2":898}},{"id":"eef25cf9-afd0-43b1-b9c5-fbd997bd5fe0","type":"rect","data":{"x1":413,"y1":821,"x2":481,"y2":897}},{"id":"df991b42-ed8e-4f2c-bf0c-aa7522f147b6","type":"rect","data":{"x1":583,"y1":823,"x2":649,"y2":900}},{"id":"81c97cd3-df53-44d3-bf3d-1eb4dc67b62a","type":"rect","data":{"x1":440,"y1":149,"x2":679,"y2":179}}]}

View File

@ -1 +1 @@
{"definitions":{"9340b854-025c-40da-9387-385d38433bef":{"name":"Shop.ItemAnomalyNoteVisual","displayName":"アノマリーノート(ビジュアル)非凡笔记(形象)","type":"template","annotationId":"9340b854-025c-40da-9387-385d38433bef","useHintRect":false},"ea1ba124-9cb3-4427-969a-bacd47e7d920":{"name":"Shop.ItemRechallengeTicket","displayName":"再挑戦チケット 重新挑战券","type":"template","annotationId":"ea1ba124-9cb3-4427-969a-bacd47e7d920","useHintRect":false},"1926f2f9-4bd7-48eb-9eba-28ec4efb0606":{"name":"Shop.ItemRecordKey","displayName":"記録の鍵 解锁交流的物品","type":"template","annotationId":"1926f2f9-4bd7-48eb-9eba-28ec4efb0606","useHintRect":false},"6720b6e8-ae80-4cc0-a885-518efe12b707":{"name":"Shop.IdolPiece.倉本千奈_WonderScale","displayName":"倉本千奈 WonderScale 碎片","type":"template","annotationId":"6720b6e8-ae80-4cc0-a885-518efe12b707","useHintRect":false},"afa06fdc-a345-4384-b25d-b16540830256":{"name":"Shop.IdolPiece.篠泽广_光景","displayName":"篠泽广 光景 碎片","type":"template","annotationId":"afa06fdc-a345-4384-b25d-b16540830256","useHintRect":false},"278b7d9c-707e-4392-9677-74574b5cdf42":{"name":"Shop.IdolPiece.紫云清夏_TameLieOneStep","displayName":"紫云清夏 Tame-Lie-One-Step 碎片","type":"template","annotationId":"278b7d9c-707e-4392-9677-74574b5cdf42","useHintRect":false},"e9ee330d-dfca-440e-8b8c-0a3b4e8c8730":{"name":"Daily.IconTitleDailyShop","displayName":"日常商店标题图标","type":"template","annotationId":"e9ee330d-dfca-440e-8b8c-0a3b4e8c8730","useHintRect":false}},"annotations":[{"id":"9340b854-025c-40da-9387-385d38433bef","type":"rect","data":{"x1":72,"y1":611,"x2":138,"y2":693}},{"id":"ea1ba124-9cb3-4427-969a-bacd47e7d920","type":"rect","data":{"x1":227,"y1":639,"x2":316,"y2":674}},{"id":"1926f2f9-4bd7-48eb-9eba-28ec4efb0606","type":"rect","data":{"x1":385,"y1":591,"x2":508,"y2":694}},{"id":"6720b6e8-ae80-4cc0-a885-518efe12b707","type":"rect","data":{"x1":589,"y1":633,"x2":638,"y2":678}},{"id":"afa06fdc-a345-4384-b25d-b16540830256","type":"rect","data":{"x1":83,"y1":867,"x2":134,"y2":912}},{"id":"278b7d9c-707e-4392-9677-74574b5cdf42","type":"rect","data":{"x1":247,"y1":864,"x2":301,"y2":907}},{"id":"e9ee330d-dfca-440e-8b8c-0a3b4e8c8730","type":"rect","data":{"x1":17,"y1":35,"x2":59,"y2":76}}]}
{"definitions":{"9340b854-025c-40da-9387-385d38433bef":{"name":"Shop.ItemAnomalyNoteVisual","displayName":"アノマリーノート(ビジュアル)非凡笔记(形象)","type":"template","annotationId":"9340b854-025c-40da-9387-385d38433bef","useHintRect":false},"ea1ba124-9cb3-4427-969a-bacd47e7d920":{"name":"Shop.ItemRechallengeTicket","displayName":"再挑戦チケット 重新挑战券","type":"template","annotationId":"ea1ba124-9cb3-4427-969a-bacd47e7d920","useHintRect":false},"1926f2f9-4bd7-48eb-9eba-28ec4efb0606":{"name":"Shop.ItemRecordKey","displayName":"記録の鍵 解锁交流的物品","type":"template","annotationId":"1926f2f9-4bd7-48eb-9eba-28ec4efb0606","useHintRect":false},"e9ee330d-dfca-440e-8b8c-0a3b4e8c8730":{"name":"Daily.IconTitleDailyShop","displayName":"日常商店标题图标","type":"template","annotationId":"e9ee330d-dfca-440e-8b8c-0a3b4e8c8730","useHintRect":false}},"annotations":[{"id":"9340b854-025c-40da-9387-385d38433bef","type":"rect","data":{"x1":72,"y1":611,"x2":138,"y2":693}},{"id":"ea1ba124-9cb3-4427-969a-bacd47e7d920","type":"rect","data":{"x1":227,"y1":639,"x2":316,"y2":674}},{"id":"1926f2f9-4bd7-48eb-9eba-28ec4efb0606","type":"rect","data":{"x1":385,"y1":591,"x2":508,"y2":694}},{"id":"e9ee330d-dfca-440e-8b8c-0a3b4e8c8730","type":"rect","data":{"x1":17,"y1":35,"x2":59,"y2":76}}]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

View File

@ -1 +0,0 @@
{"definitions":{"74ff07b3-d91c-4579-80cd-379ed7020622":{"name":"Shop.IdolPiece.葛城リーリヤ_白線","displayName":"葛城リーリヤ 白線 碎片","type":"template","annotationId":"74ff07b3-d91c-4579-80cd-379ed7020622","useHintRect":false}},"annotations":[{"id":"74ff07b3-d91c-4579-80cd-379ed7020622","type":"rect","data":{"x1":101,"y1":630,"x2":135,"y2":664}}]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 KiB

View File

@ -1 +0,0 @@
{"definitions":{"a7f5abf1-982f-4a55-8d41-3ad6f56798e0":{"name":"Shop.IdolPiece.姫崎薪波_cIclumsy_trick ","displayName":"姫崎薪波 cIclumsy trick 碎片","type":"template","annotationId":"a7f5abf1-982f-4a55-8d41-3ad6f56798e0","useHintRect":false}},"annotations":[{"id":"a7f5abf1-982f-4a55-8d41-3ad6f56798e0","type":"rect","data":{"x1":113,"y1":628,"x2":148,"y2":656}}]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

View File

@ -1 +0,0 @@
{"definitions":{"2bc00520-0afe-40e5-8743-d33fc6b2945a":{"name":"Shop.IdolPiece.花海咲季_FightingMyWay","displayName":"花海咲季 FightingMyWay 碎片","type":"template","annotationId":"2bc00520-0afe-40e5-8743-d33fc6b2945a","useHintRect":false}},"annotations":[{"id":"2bc00520-0afe-40e5-8743-d33fc6b2945a","type":"rect","data":{"x1":112,"y1":601,"x2":148,"y2":640}}]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

View File

@ -1 +0,0 @@
{"definitions":{"135ee57a-d30d-4ba8-83f0-9f1681a49ff7":{"name":"Shop.IdolPiece.藤田ことね_世界一可愛い私","displayName":"藤田ことね 世界一可愛い私 碎片","type":"template","annotationId":"135ee57a-d30d-4ba8-83f0-9f1681a49ff7","useHintRect":false}},"annotations":[{"id":"135ee57a-d30d-4ba8-83f0-9f1681a49ff7","type":"rect","data":{"x1":113,"y1":602,"x2":146,"y2":635}}]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

View File

@ -1 +0,0 @@
{"definitions":{"d15959bf-d07b-4f07-948a-c0aeaf17756a":{"name":"Shop.IdolPiece.花海佑芽_TheRollingRiceball","displayName":"花海佑芽 The Rolling Riceball 碎片","type":"template","annotationId":"d15959bf-d07b-4f07-948a-c0aeaf17756a","useHintRect":false}},"annotations":[{"id":"d15959bf-d07b-4f07-948a-c0aeaf17756a","type":"rect","data":{"x1":103,"y1":605,"x2":137,"y2":635}}]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

View File

@ -1 +0,0 @@
{"definitions":{"868b97a9-492e-4712-b47f-82b97495b019":{"name":"Shop.IdolPiece.月村手毬_LunaSayMaybe","displayName":"月村手毬 Luna say maybe 碎片","type":"template","annotationId":"868b97a9-492e-4712-b47f-82b97495b019","useHintRect":false}},"annotations":[{"id":"868b97a9-492e-4712-b47f-82b97495b019","type":"rect","data":{"x1":106,"y1":601,"x2":145,"y2":633}}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

View File

@ -0,0 +1 @@
{"definitions":{"3942ae40-7f22-412c-aebe-4b064f68db9b":{"name":"Shop.IdolPiece.花海咲季_FightingMyWay","displayName":"","type":"template","annotationId":"3942ae40-7f22-412c-aebe-4b064f68db9b","useHintRect":false},"185f7838-92a7-460b-9340-f60858948ce9":{"name":"Shop.IdolPiece.月村手毬_LunaSayMaybe","displayName":"","type":"template","annotationId":"185f7838-92a7-460b-9340-f60858948ce9","useHintRect":false},"cb3d0ca7-8d14-408a-a2f5-2e25f7b86d6c":{"name":"Shop.IdolPiece.藤田ことね_世界一可愛い私 ","displayName":"","type":"template","annotationId":"cb3d0ca7-8d14-408a-a2f5-2e25f7b86d6c","useHintRect":false},"213016c2-c3a2-43d8-86a3-ab4d27666ced":{"name":"Shop.IdolPiece.花海佑芽_TheRollingRiceball","displayName":"","type":"template","annotationId":"213016c2-c3a2-43d8-86a3-ab4d27666ced","useHintRect":false},"cc60b509-2ed5-493d-bb9f-333c6d2a6006":{"name":"Shop.IdolPiece.葛城リーリヤ_白線","displayName":"","type":"template","annotationId":"cc60b509-2ed5-493d-bb9f-333c6d2a6006","useHintRect":false},"5031808b-5525-4118-92b4-317ec8bda985":{"name":"Shop.IdolPiece.紫云清夏_TameLieOneStep","displayName":"","type":"template","annotationId":"5031808b-5525-4118-92b4-317ec8bda985","useHintRect":false},"ae9fe233-9acc-4e96-ba8e-1fb1d9bc2ea5":{"name":"Shop.IdolPiece.篠泽广_光景","displayName":"","type":"template","annotationId":"ae9fe233-9acc-4e96-ba8e-1fb1d9bc2ea5","useHintRect":false},"8f8b7b46-53bb-42ab-907a-4ea87eb09ab4":{"name":"Shop.IdolPiece.倉本千奈_WonderScale","displayName":"","type":"template","annotationId":"8f8b7b46-53bb-42ab-907a-4ea87eb09ab4","useHintRect":false},"0d9ac648-eefa-4869-ac99-1b0c83649681":{"name":"Shop.IdolPiece.有村麻央_Fluorite","displayName":"","type":"template","annotationId":"0d9ac648-eefa-4869-ac99-1b0c83649681","useHintRect":false}},"annotations":[{"id":"3942ae40-7f22-412c-aebe-4b064f68db9b","type":"rect","data":{"x1":409,"y1":342,"x2":477,"y2":413}},{"id":"185f7838-92a7-460b-9340-f60858948ce9","type":"rect","data":{"x1":71,"y1":512,"x2":140,"y2":585}},{"id":"cb3d0ca7-8d14-408a-a2f5-2e25f7b86d6c","type":"rect","data":{"x1":410,"y1":513,"x2":475,"y2":581}},{"id":"213016c2-c3a2-43d8-86a3-ab4d27666ced","type":"rect","data":{"x1":585,"y1":858,"x2":640,"y2":913}},{"id":"cc60b509-2ed5-493d-bb9f-333c6d2a6006","type":"rect","data":{"x1":247,"y1":690,"x2":303,"y2":743}},{"id":"5031808b-5525-4118-92b4-317ec8bda985","type":"rect","data":{"x1":80,"y1":860,"x2":133,"y2":908}},{"id":"ae9fe233-9acc-4e96-ba8e-1fb1d9bc2ea5","type":"rect","data":{"x1":418,"y1":852,"x2":471,"y2":912}},{"id":"8f8b7b46-53bb-42ab-907a-4ea87eb09ab4","type":"rect","data":{"x1":589,"y1":679,"x2":639,"y2":742}},{"id":"0d9ac648-eefa-4869-ac99-1b0c83649681","type":"rect","data":{"x1":83,"y1":690,"x2":136,"y2":744}}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 KiB

View File

@ -0,0 +1 @@
{"definitions":{"921eefeb-730e-46fc-9924-d338fb286592":{"name":"Shop.IdolPiece.姬崎莉波_clumsy_trick","displayName":"","type":"template","annotationId":"921eefeb-730e-46fc-9924-d338fb286592","useHintRect":false}},"annotations":[{"id":"921eefeb-730e-46fc-9924-d338fb286592","type":"rect","data":{"x1":88,"y1":914,"x2":141,"y2":963}}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

View File

@ -0,0 +1 @@
{"definitions":{"5d36b880-7b3f-49b1-a018-7de59867d376":{"name":"Daily.TextShopItemPurchased","displayName":"交換しました","type":"template","annotationId":"5d36b880-7b3f-49b1-a018-7de59867d376","useHintRect":false}},"annotations":[{"id":"5d36b880-7b3f-49b1-a018-7de59867d376","type":"rect","data":{"x1":275,"y1":626,"x2":432,"y2":655}}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

View File

@ -0,0 +1 @@
{"definitions":{"24dc7158-036c-4a66-9280-e934f470be53":{"name":"Daily.TextShopItemSoldOut","displayName":"交換済みです","type":"template","annotationId":"24dc7158-036c-4a66-9280-e934f470be53","useHintRect":false}},"annotations":[{"id":"24dc7158-036c-4a66-9280-e934f470be53","type":"rect","data":{"x1":287,"y1":625,"x2":434,"y2":655}}]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 973 B

View File

@ -145,6 +145,18 @@ class KotoneBot:
"""
raise NotImplementedError('Implement `_create_device` before using Kotonebot.')
def _on_init_context(self) -> None:
"""
初始化 Context 的钩子方法子类可以重写此方法来自定义初始化逻辑
默认实现调用 init_context 而不传入 target_screenshot_interval
"""
d = self._on_create_device()
init_context(
config_path=self.config_path,
config_type=self.config_type,
target_device=d
)
def _on_after_init_context(self):
"""
抽象方法 init_context() 被调用后立即执行
@ -155,8 +167,7 @@ class KotoneBot:
"""
按优先级顺序运行所有任务
"""
d = self._on_create_device()
init_context(config_path=self.config_path, config_type=self.config_type, target_device=d)
self._on_init_context()
self._on_after_init_context()
vars.flow.clear_interrupt()

View File

@ -27,6 +27,7 @@ from cv2.typing import MatLike
from kotonebot.client.device import Device, AndroidDevice, WindowsDevice
from kotonebot.backend.flow_controller import FlowController
from kotonebot.util import Interval
import kotonebot.backend.image as raw_image
from kotonebot.backend.image import (
TemplateMatchResult,
@ -718,8 +719,20 @@ class Forwarded:
T_Device = TypeVar('T_Device', bound=Device)
class ContextDevice(Generic[T_Device], Device):
def __init__(self, device: T_Device):
def __init__(self, device: T_Device, target_screenshot_interval: float | None = None):
"""
:param device: 目标设备
:param target_screenshot_interval: `ContextDevice.target_screenshot_interval`
"""
self._device = device
self.target_screenshot_interval: float | None = target_screenshot_interval
"""
目标截图间隔可用于限制截图速度若两次截图实际间隔小于该值则会自动等待
None 时不限制截图速度
"""
self._screenshot_interval: Interval | None = None
if self.target_screenshot_interval is not None:
self._screenshot_interval = Interval(self.target_screenshot_interval)
def screenshot(self, *, force: bool = False):
"""
@ -734,6 +747,9 @@ class ContextDevice(Generic[T_Device], Device):
img = current._inherit_screenshot
current._inherit_screenshot = None
else:
if self._screenshot_interval is not None:
self._screenshot_interval.wait()
if next_wait == 'screenshot':
delta = time.time() - last_screenshot_time
if delta < next_wait_time:
@ -780,7 +796,8 @@ class Context(Generic[T]):
self,
config_path: str,
config_type: Type[T],
device: Device
device: Device,
target_screenshot_interval: float | None = None
):
self.__ocr = ContextOcr(self)
self.__image = ContextImage(self)
@ -788,7 +805,7 @@ class Context(Generic[T]):
self.__vars = ContextGlobalVars()
self.__debug = ContextDebug(self)
self.__config = ContextConfig[T](self, config_path, config_type)
self.__device = ContextDevice(device)
self.__device = ContextDevice(device, target_screenshot_interval)
def inject(
self,
@ -900,6 +917,7 @@ def init_context(
config_type: Type[T] = dict[str, Any],
force: bool = False,
target_device: Device,
target_screenshot_interval: float | None = None,
):
"""
初始化 Context 模块
@ -911,6 +929,7 @@ def init_context(
:param force: 是否强制重新初始化
若为 `True`则忽略已存在的 Context 实例并重新创建一个新的实例
:param target_device: 目标设备
:param target_screenshot_interval: `ContextDevice.target_screenshot_interval`
"""
global _c, device, ocr, image, color, vars, debug, config
if _c is not None and not force:
@ -919,6 +938,7 @@ def init_context(
config_path=config_path,
config_type=config_type,
device=target_device,
target_screenshot_interval=target_screenshot_interval,
)
device._FORWARD_getter = lambda: _c.device # type: ignore
ocr._FORWARD_getter = lambda: _c.ocr # type: ignore

View File

@ -50,6 +50,8 @@ class BackendConfig(ConfigBaseModel):
"""Windows 截图方式的 AutoHotkey 可执行文件路径,为 None 时使用默认路径"""
mumu_background_mode: bool = False
"""MuMu12 模拟器后台保活模式"""
target_screenshot_interval: float | None = None
"""最小截图间隔,单位为秒。为 None 时不限制截图速度。"""
class PushConfig(ConfigBaseModel):
"""推送配置。"""

View File

@ -80,7 +80,7 @@ class DailyMoneyShopItems(IntEnum):
"""紫云清夏 Tame-Lie-One-Step 碎片"""
IdolPiece_葛城リーリヤ_白線 = 17
"""葛城リーリヤ 白線 碎片"""
IdolPiece_姫崎薪波_cIclumsy_trick = 18
IdolPiece_姬崎莉波_clumsy_trick = 18
"""姫崎薪波 cIclumsy trick 碎片"""
IdolPiece_花海咲季_FightingMyWay = 19
"""花海咲季 FightingMyWay 碎片"""
@ -90,6 +90,8 @@ class DailyMoneyShopItems(IntEnum):
"""花海佑芽 The Rolling Riceball 碎片"""
IdolPiece_月村手毬_LunaSayMaybe = 22
"""月村手毬 Luna say maybe 碎片"""
IdolPiece_有村麻央_Fluorite = 23
"""有村麻央 Fluorite 碎片"""
@classmethod
def to_ui_text(cls, item: "DailyMoneyShopItems") -> str:
@ -126,23 +128,25 @@ class DailyMoneyShopItems(IntEnum):
case cls.RecordKey:
return "记录钥匙"
case cls.IdolPiece_倉本千奈_WonderScale:
return "倉本千奈 WonderScale 碎片"
return "倉本千奈 WonderScale 碎片"
case cls.IdolPiece_篠泽广_光景:
return "篠泽广 光景 碎片"
return "篠泽广 光景 碎片"
case cls.IdolPiece_紫云清夏_TameLieOneStep:
return "紫云清夏 Tame-Lie-One-Step 碎片"
return "紫云清夏 Tame-Lie-One-Step 碎片"
case cls.IdolPiece_葛城リーリヤ_白線:
return "葛城リーリヤ 白線 碎片"
case cls.IdolPiece_姫崎薪波_cIclumsy_trick:
return "姫崎薪波 cIclumsy trick 碎片"
return "葛城リーリヤ 白線 碎片"
case cls.IdolPiece_姬崎莉波_clumsy_trick:
return "姫崎薪波 clumsy trick 碎片"
case cls.IdolPiece_花海咲季_FightingMyWay:
return "花海咲季 FightingMyWay 碎片"
return "花海咲季 FightingMyWay 碎片"
case cls.IdolPiece_藤田ことね_世界一可愛い私:
return "藤田ことね 世界一可愛い私 碎片"
return "藤田ことね 世界一可愛い私 碎片"
case cls.IdolPiece_花海佑芽_TheRollingRiceball:
return "花海佑芽 The Rolling Riceball 碎片"
return "花海佑芽 The Rolling Riceball 碎片"
case cls.IdolPiece_月村手毬_LunaSayMaybe:
return "月村手毬 Luna say maybe 碎片"
return "月村手毬 Luna say maybe 碎片"
case cls.IdolPiece_有村麻央_Fluorite:
return "有村麻央 Fluorite 碎片"
case _:
assert_never(item)
@ -202,8 +206,8 @@ class DailyMoneyShopItems(IntEnum):
return R.Shop.IdolPiece.紫云清夏_TameLieOneStep
case DailyMoneyShopItems.IdolPiece_葛城リーリヤ_白線:
return R.Shop.IdolPiece.葛城リーリヤ_白線
case DailyMoneyShopItems.IdolPiece_姫崎薪波_cIclumsy_trick:
return R.Shop.IdolPiece.姫崎薪波_cIclumsy_trick
case DailyMoneyShopItems.IdolPiece_姬崎莉波_clumsy_trick:
return R.Shop.IdolPiece.姬崎莉波_clumsy_trick
case DailyMoneyShopItems.IdolPiece_花海咲季_FightingMyWay:
return R.Shop.IdolPiece.花海咲季_FightingMyWay
case DailyMoneyShopItems.IdolPiece_藤田ことね_世界一可愛い私:
@ -212,6 +216,8 @@ class DailyMoneyShopItems(IntEnum):
return R.Shop.IdolPiece.花海佑芽_TheRollingRiceball
case DailyMoneyShopItems.IdolPiece_月村手毬_LunaSayMaybe:
return R.Shop.IdolPiece.月村手毬_LunaSayMaybe
case DailyMoneyShopItems.IdolPiece_有村麻央_Fluorite:
return R.Shop.IdolPiece.有村麻央_Fluorite
case _:
assert_never(self)
@ -225,13 +231,9 @@ class PurchaseConfig(ConfigBaseModel):
"""是否启用金币购买"""
money_items: list[DailyMoneyShopItems] = []
"""金币商店要购买的物品"""
money_refresh_on: Literal['never', 'not_found', 'always'] = 'never'
money_refresh: bool = True
"""
金币商店刷新逻辑
* never: 从不刷新
* not_found: 仅当要购买的物品不存在时刷新
* always: 总是刷新
是否使用每日一次免费刷新金币商店
"""
ap_enabled: bool = False
"""是否启用AP购买"""
@ -465,16 +467,22 @@ class MiscConfig(ConfigBaseModel):
check_update: Literal['never', 'startup'] = 'startup'
"""
检查更新时机
* never: 从不检查更新
* startup: 启动时检查更新
"""
auto_install_update: bool = True
"""
是否自动安装更新
若启用则每次自动检查更新时若有新版本会自动安装否则只是会提示
"""
expose_to_lan: bool = False
"""
是否允许局域网访问 Web 界面
启用后局域网内的其他设备可以通过本机 IP 地址访问 Web 界面
"""
class BaseConfig(ConfigBaseModel):
purchase: PurchaseConfig = PurchaseConfig()

View File

@ -37,12 +37,12 @@ ConfigKey = Literal[
'check_emulator', 'emulator_path',
'adb_emulator_name', 'emulator_args',
'_mumu_index', '_leidian_index',
'mumu_background_mode',
'mumu_background_mode', 'target_screenshot_interval',
# purchase
'purchase_enabled',
'money_enabled', 'ap_enabled',
'ap_items', 'money_items',
'ap_items', 'money_items', 'money_refresh',
# assignment
'assignment_enabled',
@ -92,8 +92,8 @@ ConfigKey = Literal[
'trace_recommend_card_detection',
# misc
'check_update', 'auto_install_update',
'check_update', 'auto_install_update', 'expose_to_lan',
'_selected_backend_index'
]
@ -761,6 +761,15 @@ class KotoneBotUI:
interactive=True
)
target_screenshot_interval = gr.Number(
label="最小截图间隔(秒)",
value=self.current_config.backend.target_screenshot_interval,
info=BackendConfig.model_fields['target_screenshot_interval'].description,
minimum=0,
step=0.1,
interactive=True
)
tab_mumu12.select(fn=partial(_update_emulator_tab_options, selected_index=0), inputs=[screenshot_impl], outputs=[screenshot_impl])
tab_leidian.select(fn=partial(_update_emulator_tab_options, selected_index=1), inputs=[screenshot_impl], outputs=[screenshot_impl])
tab_custom.select(fn=partial(_update_emulator_tab_options, selected_index=2), inputs=[screenshot_impl], outputs=[screenshot_impl])
@ -814,12 +823,14 @@ class KotoneBotUI:
# Common settings for all backend types
self.current_config.backend.screenshot_impl = data['screenshot_method']
self.current_config.backend.target_screenshot_interval = data['target_screenshot_interval']
self.current_config.keep_screenshots = data['keep_screenshots'] # This is a UserConfig field
return set_config, {
'adb_ip': adb_ip,
'adb_port': adb_port,
'screenshot_method': screenshot_impl, # screenshot_impl is the component
'target_screenshot_interval': target_screenshot_interval,
'keep_screenshots': keep_screenshots,
'check_emulator': check_emulator,
'emulator_path': emulator_path,
@ -854,6 +865,12 @@ class KotoneBotUI:
info=PurchaseConfig.model_fields['money_items'].description
)
money_refresh = gr.Checkbox(
label="每日一次免费刷新金币商店",
value=self.current_config.options.purchase.money_refresh,
info=PurchaseConfig.model_fields['money_refresh'].description
)
ap_enabled = gr.Checkbox(
label="启用AP购买",
value=self.current_config.options.purchase.ap_enabled,
@ -891,6 +908,7 @@ class KotoneBotUI:
config.purchase.enabled = data['purchase_enabled']
config.purchase.money_enabled = data['money_enabled']
config.purchase.money_items = [DailyMoneyShopItems(x) for x in data['money_items']]
config.purchase.money_refresh = data['money_refresh']
config.purchase.ap_enabled = data['ap_enabled']
ap_items_enum: List[Literal[0, 1, 2, 3]] = []
ap_items_map: Dict[str, APShopItems] = {
@ -909,7 +927,8 @@ class KotoneBotUI:
'money_enabled': money_enabled,
'ap_enabled': ap_enabled,
'ap_items': ap_items,
'money_items': money_items
'money_items': money_items,
'money_refresh': money_refresh
}
def _create_work_settings(self) -> ConfigBuilderReturnValue:
@ -1494,14 +1513,22 @@ class KotoneBotUI:
info=MiscConfig.model_fields['auto_install_update'].description,
interactive=True
)
expose_to_lan = gr.Checkbox(
label="允许局域网访问",
value=self.current_config.options.misc.expose_to_lan,
info=MiscConfig.model_fields['expose_to_lan'].description,
interactive=True
)
def set_config(config: BaseConfig, data: dict[ConfigKey, Any]) -> None:
config.misc.check_update = data['check_update']
config.misc.auto_install_update = data['auto_install_update']
config.misc.expose_to_lan = data['expose_to_lan']
return set_config, {
'check_update': check_update,
'auto_install_update': auto_install_update
'auto_install_update': auto_install_update,
'expose_to_lan': expose_to_lan
}
def _create_settings_tab(self) -> None:
@ -1888,7 +1915,9 @@ def main(kaa: Kaa | None = None) -> None:
kaa = kaa or Kaa('./config.json')
ui = KotoneBotUI(kaa)
app = ui.create_ui()
app.launch(inbrowser=True, show_error=True)
server_name = "0.0.0.0" if ui.current_config.options.misc.expose_to_lan else "127.0.0.1"
app.launch(inbrowser=True, show_error=True, server_name=server_name)
if __name__ == "__main__":
main()

View File

@ -110,6 +110,27 @@ class Kaa(KotoneBot):
logger.exception('Failed to save error report:')
return ''
@override
def _on_init_context(self) -> None:
"""
初始化 Context从配置中读取 target_screenshot_interval
"""
from kotonebot.config.manager import load_config
from kotonebot.backend.context import init_context
# 加载配置以获取 target_screenshot_interval
config = load_config(self.config_path, type=self.config_type)
user_config = config.user_configs[0] # HACK: 硬编码
target_screenshot_interval = user_config.backend.target_screenshot_interval
d = self._on_create_device()
init_context(
config_path=self.config_path,
config_type=self.config_type,
target_device=d,
target_screenshot_interval=target_screenshot_interval
)
@override
def _on_after_init_context(self):
if self.backend_instance is None:

View File

@ -1,6 +1,7 @@
"""收取活动费"""
import logging
from kotonebot.backend.loop import Loop
from kotonebot.kaa.tasks import R
from kotonebot.kaa.common import conf
from ..actions.scenes import at_home, goto_home
@ -16,17 +17,18 @@ def acquire_activity_funds():
if not at_home():
goto_home()
device.screenshot()
if color.find('#ff1249', rect=R.Daily.BoxHomeActivelyFunds):
logger.info('Claiming activity funds.')
device.click(R.Daily.BoxHomeActivelyFunds)
device.click(image.expect_wait(R.Common.ButtonClose))
logger.info('Activity funds claimed.')
else:
logger.info('No activity funds to claim.')
while not at_home():
pass
for _ in Loop():
if (
not color.find('#ff1249', rect=R.Daily.BoxHomeActivelyFunds)
and at_home()
):
break
elif image.find(R.Common.ButtonClose):
logger.info('Closing popup dialog.')
device.click()
else:
device.click(R.Daily.BoxHomeActivelyFunds)
if __name__ == '__main__':
import logging

View File

@ -2,9 +2,11 @@
import logging
from typing import Optional
from kotonebot.backend.loop import Loop
from kotonebot.kaa.tasks import R
from kotonebot.kaa.common import conf, DailyMoneyShopItems
from kotonebot.util import cropped
from kotonebot.primitives.geometry import Point
from kotonebot.util import Countdown, cropped
from kotonebot import task, device, image, action, sleep
from kotonebot.backend.dispatch import SimpleDispatcher
from ..actions.scenes import goto_home, goto_shop, at_daily_shop
@ -37,10 +39,10 @@ def money_items2(items: Optional[list[DailyMoneyShopItems]] = None):
scroll = 0
while items:
for item in items:
if image.find(item.to_resource(), colored=True):
if ret := image.find(item.to_resource(), colored=True):
logger.info(f'Purchasing {item.to_ui_text(item)}...')
device.click()
handle_purchase_dialog()
confirm_purchase(ret.position)
finished.append(item)
items = [item for item in items if item not in finished]
# 全都买完了
@ -71,16 +73,17 @@ def dispatch_recommended_items():
while True:
device.screenshot()
if image.find(R.Daily.TextShopRecommended):
if rec := image.find(R.Daily.TextShopRecommended):
logger.info(f'Clicking on recommended item.') # TODO: 计数
device.click()
handle_purchase_dialog()
confirm_purchase(rec.position)
sleep(2.5) #
elif image.find(R.Daily.IconTitleDailyShop) and not image.find(R.Daily.TextShopRecommended):
logger.info(f'No recommended item found. Finished.')
break
@action('确认购买', screenshot_mode='manual-inherit')
def handle_purchase_dialog():
def confirm_purchase(target_item_pos: Point | None = None):
"""
确认购买
@ -89,11 +92,27 @@ def handle_purchase_dialog():
"""
# 前置条件:[screenshots\shop\dialog.png]
# TODO: 需要有个更好的方式检测是否已购买
purchased = (SimpleDispatcher('dispatch_purchase_dialog')
.until(R.Common.ButtonConfirm, result=False)
.until(R.Daily.TextShopPurchased, result=True)
.timeout(timeout=3, result=True)
).run()
purchased = False
cd = Countdown(sec=3)
for _ in Loop():
if cd.expired():
purchased = True
break
if image.find(R.Daily.TextShopItemSoldOut):
logger.info('Item sold out.')
purchased = True
break
elif image.find(R.Daily.TextShopItemPurchased):
logger.info('Item already purchased.')
purchased = True
break
elif image.find(R.Common.ButtonConfirm):
logger.info('Confirming purchase...')
device.click()
sleep(0.5)
else:
if target_item_pos:
device.click(target_item_pos)
if purchased:
logger.info('Item sold out.')
@ -132,20 +151,19 @@ def ap_items():
logger.info(f'Purchasing #{index} AP item.')
device.click(results[index])
sleep(0.5)
with cropped(device, y1=0.3):
purchased = image.wait_for(R.Daily.TextShopPurchased, timeout=1)
if purchased is not None:
logger.info(f'AP item #{index} already purchased.')
continue
comfirm = image.expect_wait(R.Common.ButtonConfirm, timeout=2)
# 如果数量不是最大,调到最大
while image.find(R.Daily.ButtonShopCountAdd, colored=True):
logger.debug('Adjusting quantity(+1)...')
device.click()
sleep(0.3)
logger.debug(f'Confirming purchase...')
device.click(comfirm)
sleep(1.5)
purchased = image.wait_for(R.Daily.TextShopItemSoldOut, timeout=1)
if purchased is not None:
logger.info(f'AP item #{index} already purchased.')
continue
comfirm = image.expect_wait(R.Common.ButtonConfirm, timeout=2)
# 如果数量不是最大,调到最大
while image.find(R.Daily.ButtonShopCountAdd, colored=True):
logger.debug('Adjusting quantity(+1)...')
device.click()
sleep(0.3)
logger.debug(f'Confirming purchase...')
device.click(comfirm)
sleep(1.5)
else:
logger.warning(f'AP item #{index} not found')
logger.info(f'Purchasing AP items completed. {len(item_indices)} items purchased.')
@ -158,8 +176,8 @@ def purchase():
if not conf().purchase.enabled:
logger.info('Purchase is disabled.')
return
if not at_daily_shop():
goto_shop()
goto_shop()
# 进入每日商店 [screenshots\shop\shop.png]
device.click(image.expect(R.Daily.ButtonDailyShop)) # TODO: memoable
# 等待载入
@ -170,6 +188,12 @@ def purchase():
image.expect_wait(R.Daily.IconShopMoney)
money_items2()
sleep(0.5)
if image.find(R.Daily.ButtonRefreshMoneyShop):
logger.info('Refreshing money shop.')
device.click()
sleep(0.5)
money_items2()
sleep(0.5)
else:
logger.info('Money purchase is disabled.')
@ -178,7 +202,7 @@ def purchase():
# 点击 AP 选项卡
device.click(ap_tab)
# 等待 AP 选项卡加载完成
image.expect_wait(R.Daily.IconShopAp)
image.expect_wait(R.Daily.IconShopAp, threshold=0.7)
ap_items()
sleep(0.5)
else:

@ -1 +1 @@
Subproject commit bca78ea35b0883c24814c523cec1856615205199
Subproject commit 4328e2b53dc6e3efa4fef691064f40ceb998c97a