data: switch to recovered game DB and refresh cards and charts
Publish GitHub Pages output / publish (push) Successful in 44s
Publish GitHub Pages output / publish (push) Successful in 44s
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"masterDataVersion": "845cee36af22b657511ca406b5417cac7076a001208518c9601d6389a5046ddd",
|
||||
"masterDataVersion": "546b8e2e37db368c6731e148b88611cef105bbaa66ff567f5c1584711ef979a4",
|
||||
"members": [
|
||||
{
|
||||
"id": "chr-00001",
|
||||
@@ -334,6 +334,11 @@
|
||||
"x": -1,
|
||||
"y": -10
|
||||
},
|
||||
{
|
||||
"groupId": "G-025",
|
||||
"x": 1,
|
||||
"y": -10
|
||||
},
|
||||
{
|
||||
"groupId": "G-022",
|
||||
"x": -1,
|
||||
@@ -1101,6 +1106,11 @@
|
||||
"x": -1,
|
||||
"y": -10
|
||||
},
|
||||
{
|
||||
"groupId": "G-025",
|
||||
"x": 1,
|
||||
"y": -10
|
||||
},
|
||||
{
|
||||
"groupId": "G-022",
|
||||
"x": -1,
|
||||
@@ -1868,6 +1878,11 @@
|
||||
"x": -1,
|
||||
"y": -10
|
||||
},
|
||||
{
|
||||
"groupId": "G-025",
|
||||
"x": 1,
|
||||
"y": -10
|
||||
},
|
||||
{
|
||||
"groupId": "G-022",
|
||||
"x": -1,
|
||||
@@ -2635,6 +2650,11 @@
|
||||
"x": -1,
|
||||
"y": -10
|
||||
},
|
||||
{
|
||||
"groupId": "G-025",
|
||||
"x": 1,
|
||||
"y": -10
|
||||
},
|
||||
{
|
||||
"groupId": "G-022",
|
||||
"x": -1,
|
||||
@@ -9069,6 +9089,31 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"G-025": [
|
||||
{
|
||||
"number": 1,
|
||||
"characterIds": [],
|
||||
"grade": 1,
|
||||
"type": "all_member",
|
||||
"skillTreePointCost": 3,
|
||||
"consumptions": [
|
||||
{
|
||||
"resourceType": "ResourceType_RESOURCE_TYPE_ITEM",
|
||||
"resourceId": "item-skill_tree_allmember-1",
|
||||
"quantity": "160"
|
||||
}
|
||||
],
|
||||
"viewConditionGroupId": "cd-player_level-30",
|
||||
"effect": {
|
||||
"id": "skill_tree_effect-mini_game_reward_quantity_up_permil_up-5-skill_tree_effect_target-all-always-splash_ball",
|
||||
"type": "mini_game_reward_quantity_up_permil_up",
|
||||
"value": 5,
|
||||
"targetId": "skill_tree_effect_target-all",
|
||||
"trigger": "always",
|
||||
"description": " 스플래시 볼의 획득 보상량이 [value/10]% UP"
|
||||
}
|
||||
}
|
||||
],
|
||||
"R-001": [
|
||||
{
|
||||
"number": 1,
|
||||
@@ -10930,7 +10975,7 @@
|
||||
"value": 1,
|
||||
"targetId": null,
|
||||
"trigger": "always",
|
||||
"description": "전체 악곡 플레이 시 스코어 보너스 [value/10]% UP"
|
||||
"description": "단체 악곡 플레이 시 스코어 보너스 [value/10]% UP"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -11425,7 +11470,7 @@
|
||||
"value": 1,
|
||||
"targetId": null,
|
||||
"trigger": "always",
|
||||
"description": "전체 악곡 플레이 시 스코어 보너스 [value/10]% UP"
|
||||
"description": "단체 악곡 플레이 시 스코어 보너스 [value/10]% UP"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -11535,7 +11580,7 @@
|
||||
"value": 5,
|
||||
"targetId": null,
|
||||
"trigger": "always",
|
||||
"description": "전체 악곡 플레이 시 스코어 보너스 [value/10]% UP"
|
||||
"description": "단체 악곡 플레이 시 스코어 보너스 [value/10]% UP"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"masterDataVersion": "845cee36af22b657511ca406b5417cac7076a001208518c9601d6389a5046ddd",
|
||||
"masterDataVersion": "546b8e2e37db368c6731e148b88611cef105bbaa66ff567f5c1584711ef979a4",
|
||||
"cards": [
|
||||
{
|
||||
"id": "card-00001-5-uniq-0000-00",
|
||||
@@ -4200,6 +4200,127 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card-00023-5-uniq-0085-00",
|
||||
"characterId": "chr-00023",
|
||||
"rarity": 5,
|
||||
"type": "퓨어",
|
||||
"name": "호쇼 마린",
|
||||
"versionName": "수영복",
|
||||
"generation": "hololive 3기생",
|
||||
"highestStat": "센스",
|
||||
"power": {
|
||||
"maxLevel": 80,
|
||||
"statsAtMaxLevel": {
|
||||
"performance": 7116,
|
||||
"technique": 6315,
|
||||
"sense": 10132
|
||||
}
|
||||
},
|
||||
"details": {
|
||||
"title": "보물 독점♡워터 캐논",
|
||||
"passive": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "[attribute=pure]퓨어 타입[/attribute] 2명 이상이면 [attribute=pure]퓨어 타입[/attribute] 2명의 센스가 32% UP ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": null
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "[attribute=pure]퓨어 타입[/attribute] 2명 이상이면 [attribute=pure]퓨어 타입[/attribute] 2명의 센스가 [highlight]43[/highlight]% UP ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": null
|
||||
}
|
||||
],
|
||||
"active": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "34초마다 중간 확률로 12초 동안 스코어가 100% UP ",
|
||||
"coolTimeSeconds": 34,
|
||||
"probabilityPercent": 46,
|
||||
"durationSeconds": 12,
|
||||
"condition": "",
|
||||
"effect": "스코어가 100% UP"
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "34초마다 중간 확률로 12초 동안 스코어가 [highlight]120[/highlight]% UP ",
|
||||
"coolTimeSeconds": 34,
|
||||
"probabilityPercent": 46,
|
||||
"durationSeconds": 12,
|
||||
"condition": "",
|
||||
"effect": "스코어가 120% UP"
|
||||
}
|
||||
],
|
||||
"special": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "14초 동안 스코어 서포트 효과 95% ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": 14,
|
||||
"condition": "",
|
||||
"effect": "스코어 서포트 효과 95%"
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "14초 동안 스코어 서포트 효과 [highlight]115[/highlight]% ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": 14,
|
||||
"condition": "",
|
||||
"effect": "스코어 서포트 효과 115%"
|
||||
}
|
||||
],
|
||||
"connect": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "범위 내의 holo멤버 보드 효과를 210% UP",
|
||||
"effectPermilUp": 2100,
|
||||
"extent": [
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "범위 내의 holo멤버 보드 효과를 [highlight]260[/highlight]% UP",
|
||||
"effectPermilUp": 2600,
|
||||
"extent": [
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"costume": {
|
||||
"name": "딥니스 마리나",
|
||||
"description": "[attribute=pure]퓨어 타입[/attribute] 2명 이상이면 전원의 센스가 80% UP [attribute=pure]퓨어 타입[/attribute] 2명 이상이면 전원의 스코어 서포트 효과 25%"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card-00026-5-uniq-0021-00",
|
||||
"characterId": "chr-00026",
|
||||
@@ -6142,6 +6263,143 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card-00037-5-uniq-0086-00",
|
||||
"characterId": "chr-00037",
|
||||
"rarity": 5,
|
||||
"type": "해피",
|
||||
"name": "하쿠이 코요리",
|
||||
"versionName": "수영복",
|
||||
"generation": "hololive holoX",
|
||||
"highestStat": "퍼포먼스",
|
||||
"power": {
|
||||
"maxLevel": 80,
|
||||
"statsAtMaxLevel": {
|
||||
"performance": 9484,
|
||||
"technique": 7256,
|
||||
"sense": 6971
|
||||
}
|
||||
},
|
||||
"details": {
|
||||
"title": "뒤돌아본 순간의 aventure",
|
||||
"passive": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "[attribute=happy]해피 타입[/attribute] 2명 이상이면 [attribute=happy]해피 타입[/attribute] 2명의 퍼포먼스가 32% UP ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": null
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "[attribute=happy]해피 타입[/attribute] 2명 이상이면 [attribute=happy]해피 타입[/attribute] 2명의 퍼포먼스가 [highlight]43[/highlight]% UP ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": null
|
||||
}
|
||||
],
|
||||
"active": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "25초마다 중간 확률로 9초 동안 스코어가 50% UP 라이프 600 이상이면 스코어가 100% UP ",
|
||||
"coolTimeSeconds": 25,
|
||||
"probabilityPercent": 46,
|
||||
"durationSeconds": 9,
|
||||
"condition": "라이프 600 이상이면",
|
||||
"effect": "스코어가 100% UP"
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "25초마다 중간 확률로 9초 동안 스코어가 [highlight]60[/highlight]% UP 라이프 600 이상이면 스코어가 [highlight]120[/highlight]% UP ",
|
||||
"coolTimeSeconds": 25,
|
||||
"probabilityPercent": 46,
|
||||
"durationSeconds": 9,
|
||||
"condition": "라이프 600 이상이면",
|
||||
"effect": "스코어가 120% UP"
|
||||
}
|
||||
],
|
||||
"special": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "12초 동안 스코어 서포트 효과 110% ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": 12,
|
||||
"condition": "",
|
||||
"effect": "스코어 서포트 효과 110%"
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "12초 동안 스코어 서포트 효과 [highlight]135[/highlight]% ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": 12,
|
||||
"condition": "",
|
||||
"effect": "스코어 서포트 효과 135%"
|
||||
}
|
||||
],
|
||||
"connect": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "범위 내의 holo멤버 보드 효과를 150% UP",
|
||||
"effectPermilUp": 1500,
|
||||
"extent": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": -3
|
||||
},
|
||||
{
|
||||
"x": -1,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "범위 내의 holo멤버 보드 효과를 [highlight]200[/highlight]% UP",
|
||||
"effectPermilUp": 2000,
|
||||
"extent": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": -3
|
||||
},
|
||||
{
|
||||
"x": -1,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"costume": {
|
||||
"name": "비치사이드 지오메트리",
|
||||
"description": "전원의 스코어 서포트 효과 60% "
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card-00039-5-uniq-0032-00",
|
||||
"characterId": "chr-00039",
|
||||
@@ -7874,6 +8132,127 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card-03009-5-uniq-0088-00",
|
||||
"characterId": "chr-03009",
|
||||
"rarity": 5,
|
||||
"type": "큐트",
|
||||
"name": "코보 카나에루",
|
||||
"versionName": "수영복",
|
||||
"generation": "hololive ID 3기생",
|
||||
"highestStat": "센스",
|
||||
"power": {
|
||||
"maxLevel": 80,
|
||||
"statsAtMaxLevel": {
|
||||
"performance": 6184,
|
||||
"technique": 6984,
|
||||
"sense": 10346
|
||||
}
|
||||
},
|
||||
"details": {
|
||||
"title": "파도 사이에 흔들리는 Body & Soul",
|
||||
"passive": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "[attribute=cute]큐트 타입[/attribute] 2명 이상이면 [attribute=cute]큐트 타입[/attribute] 2명의 센스가 32% UP ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": null
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "[attribute=cute]큐트 타입[/attribute] 2명 이상이면 [attribute=cute]큐트 타입[/attribute] 2명의 센스가 [highlight]43[/highlight]% UP ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": null
|
||||
}
|
||||
],
|
||||
"active": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "29초마다 높은 확률로 10초 동안 스코어가 95% UP ",
|
||||
"coolTimeSeconds": 29,
|
||||
"probabilityPercent": 55,
|
||||
"durationSeconds": 10,
|
||||
"condition": "",
|
||||
"effect": "스코어가 95% UP"
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "29초마다 높은 확률로 10초 동안 스코어가 [highlight]115[/highlight]% UP ",
|
||||
"coolTimeSeconds": 29,
|
||||
"probabilityPercent": 55,
|
||||
"durationSeconds": 10,
|
||||
"condition": "",
|
||||
"effect": "스코어가 115% UP"
|
||||
}
|
||||
],
|
||||
"special": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "10초 동안 스코어 서포트 효과 135% ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": 10,
|
||||
"condition": "",
|
||||
"effect": "스코어 서포트 효과 135%"
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "10초 동안 스코어 서포트 효과 [highlight]160[/highlight]% ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": 10,
|
||||
"condition": "",
|
||||
"effect": "스코어 서포트 효과 160%"
|
||||
}
|
||||
],
|
||||
"connect": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "범위 내의 holo멤버 보드 효과를 220% UP",
|
||||
"effectPermilUp": 2200,
|
||||
"extent": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": -3
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "범위 내의 holo멤버 보드 효과를 [highlight]270[/highlight]% UP",
|
||||
"effectPermilUp": 2700,
|
||||
"extent": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": -3
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"costume": {
|
||||
"name": "리플 디바",
|
||||
"description": "[attribute=cute]큐트 타입[/attribute] 2명 이상이면 전원의 센스가 130% UP "
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card-04001-5-uniq-0042-00",
|
||||
"characterId": "chr-04001",
|
||||
@@ -8962,6 +9341,167 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card-04012-5-uniq-0087-00",
|
||||
"characterId": "chr-04012",
|
||||
"rarity": 5,
|
||||
"type": "해피",
|
||||
"name": "하코스 벨즈",
|
||||
"versionName": "수영복",
|
||||
"generation": "hololive EN Promise",
|
||||
"highestStat": "센스",
|
||||
"power": {
|
||||
"maxLevel": 80,
|
||||
"statsAtMaxLevel": {
|
||||
"performance": 6564,
|
||||
"technique": 7131,
|
||||
"sense": 9917
|
||||
}
|
||||
},
|
||||
"details": {
|
||||
"title": "이게 나의 강추!",
|
||||
"passive": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "Promise(이)가 2명 이상이면 자신의 전체 파라미터가 25% UP ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": null
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "Promise(이)가 2명 이상이면 자신의 전체 파라미터가 [highlight]33[/highlight]% UP ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": null
|
||||
}
|
||||
],
|
||||
"active": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "22초마다 중간 확률로 8초 동안 스코어가 50% UP Promise(이)가 2명 이상이면 스코어가 105% UP ",
|
||||
"coolTimeSeconds": 22,
|
||||
"probabilityPercent": 46,
|
||||
"durationSeconds": 8,
|
||||
"condition": "Promise(이)가 2명 이상이면",
|
||||
"effect": "스코어가 105% UP"
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "22초마다 중간 확률로 8초 동안 스코어가 [highlight]65[/highlight]% UP Promise(이)가 2명 이상이면 스코어가 [highlight]125[/highlight]% UP ",
|
||||
"coolTimeSeconds": 22,
|
||||
"probabilityPercent": 46,
|
||||
"durationSeconds": 8,
|
||||
"condition": "Promise(이)가 2명 이상이면",
|
||||
"effect": "스코어가 125% UP"
|
||||
}
|
||||
],
|
||||
"special": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "15초 동안 스코어 서포트 효과 90% ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": 15,
|
||||
"condition": "",
|
||||
"effect": "스코어 서포트 효과 90%"
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "15초 동안 스코어 서포트 효과 [highlight]105[/highlight]% ",
|
||||
"coolTimeSeconds": null,
|
||||
"probabilityPercent": null,
|
||||
"durationSeconds": 15,
|
||||
"condition": "",
|
||||
"effect": "스코어 서포트 효과 105%"
|
||||
}
|
||||
],
|
||||
"connect": [
|
||||
{
|
||||
"level": 1,
|
||||
"description": "범위 내의 holo멤버 보드 효과를 85% UP",
|
||||
"effectPermilUp": 850,
|
||||
"extent": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": -1,
|
||||
"y": -1
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1
|
||||
},
|
||||
{
|
||||
"x": -1,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": -2,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": -1,
|
||||
"y": 1
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"description": "범위 내의 holo멤버 보드 효과를 [highlight]135[/highlight]% UP",
|
||||
"effectPermilUp": 1350,
|
||||
"extent": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": -2
|
||||
},
|
||||
{
|
||||
"x": -1,
|
||||
"y": -1
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1
|
||||
},
|
||||
{
|
||||
"x": -1,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": -2,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": -1,
|
||||
"y": 1
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"costume": {
|
||||
"name": "샤봉 피버",
|
||||
"description": "Promise(이)가 2명 이상이면 전원의 전체 파라미터가 50% UP "
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card-04013-5-uniq-0052-00",
|
||||
"characterId": "chr-04013",
|
||||
|
||||
+5064
-4645
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"source": {
|
||||
"repository": "https://github.com/HolodoriDB/holodori-db-kor-diff.git",
|
||||
"commit": "9234a7d99c5cfba9b71803c24a6de4c005c4842f",
|
||||
"kind": "android-master-db",
|
||||
"masterDataVersion": "546b8e2e37db368c6731e148b88611cef105bbaa66ff567f5c1584711ef979a4",
|
||||
"locale": "kor",
|
||||
"capturedAt": "2026-09-29T10:55:34.888Z",
|
||||
"manifestSha256": "9ce961de4f151aa5d06fe303c328e19443f1aa99dd0062eff10381e67b9365e2",
|
||||
"files": [
|
||||
"Mission.json",
|
||||
"MissionProgress.json"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"masterDataVersion": "845cee36af22b657511ca406b5417cac7076a001208518c9601d6389a5046ddd",
|
||||
"maxCount": 34,
|
||||
"masterDataVersion": "546b8e2e37db368c6731e148b88611cef105bbaa66ff567f5c1584711ef979a4",
|
||||
"maxCount": 35,
|
||||
"effects": [
|
||||
{
|
||||
"count": 1,
|
||||
|
||||
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { isSongReleased, releasedSongCharts, SONG_RELEASE_TIMES } from '../data/song-release.ts';
|
||||
|
||||
test('신규 악곡 공개 시각을 HolodoriDB의 한국 시각과 일치시킨다', () => {
|
||||
test('신규 악곡 공개 시각을 게임 DB의 한국 시각과 일치시킨다', () => {
|
||||
assert.deepEqual(SONG_RELEASE_TIMES, {
|
||||
m0332: Date.parse('2026-08-19T20:00:00+09:00'),
|
||||
m0353: Date.parse('2026-08-20T00:00:00+09:00'),
|
||||
@@ -31,7 +31,7 @@ test('채보 목록에서 공개되지 않은 악곡의 모든 난이도를 제
|
||||
assert.deepEqual(releasedSongCharts(charts, SONG_RELEASE_TIMES.m0332), charts);
|
||||
});
|
||||
|
||||
test('채보 카탈로그의 HolodoriDB 공개 시각을 수동 목록보다 우선한다', () => {
|
||||
test('채보 카탈로그의 게임 DB 공개 시각을 수동 목록보다 우선한다', () => {
|
||||
const releaseTime = Date.parse('2026-08-30T00:00:00+09:00');
|
||||
const charts = [
|
||||
{ songId: 'm0359', difficulty: 'expert', releaseTime },
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { leaderRatingCharts } from './song-rating.ts';
|
||||
import overrides from '../../../data/song-metadata-overrides.json' with { type: 'json' };
|
||||
import officialSong from '../../../docs/research/m0525-holodoridb.json' with { type: 'json' };
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { gunzipSync } from 'node:zlib';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { leaderRatingCharts } from './song-rating.ts';
|
||||
import reference from '../../../docs/research/song-score-formula-reference.json' with { type: 'json' };
|
||||
import susReference from '../../../docs/research/sus-score-formulas.json' with { type: 'json' };
|
||||
import calibrations from '../../../data/score-calibrations.json' with { type: 'json' };
|
||||
import catalog from '../data/exact-chart-catalog.json' with { type: 'json' };
|
||||
import pointer from '../../../data/game-db/current.json' with { type: 'json' };
|
||||
import { calculateExactScore, perfectNoteScoreFromPower, scoreRatioFromFormula } from './score.ts';
|
||||
|
||||
const snapshot = new URL('../../../data/game-db/' + pointer.snapshot + '/', import.meta.url);
|
||||
function masterRows(name: string): Record<string, any>[] {
|
||||
return JSON.parse(gunzipSync(readFileSync(new URL(name + '.json.gz', snapshot))).toString('utf8'))
|
||||
.map((row: { data: Record<string, any> }) => row.data);
|
||||
}
|
||||
const music = new Map(masterRows('Music').map(row => [row.id, row]));
|
||||
const levels = new Map(masterRows('MusicDifficulty').map(row => [row.musicId + '.' + row.difficultyType.split('_').at(-1).toLowerCase(), row.difficultyLevel]));
|
||||
const counts = new Map(masterRows('MusicDifficultyChart').map(row => [row.musicId + '.' + row.difficultyType.split('_').at(-1).toLowerCase(), row.fullComboNoteCount]));
|
||||
const titles = new Map(masterRows('LangMusic_Kor').map(row => [row.id, row.text]));
|
||||
|
||||
test('곡별 공식과 실제 점수 엔진이 일반 PERFECT 실측 19건을 재현한다', () => {
|
||||
let count = 0;
|
||||
for (const calibration of calibrations.charts) {
|
||||
const formula = reference.charts.find((row) => row.songId === calibration.songId && row.difficulty === calibration.difficulty);
|
||||
const formula = reference.charts.find(row => row.songId === calibration.songId && row.difficulty === calibration.difficulty);
|
||||
assert.ok(formula);
|
||||
for (const measurement of calibration.measurements) {
|
||||
const result = calculateExactScore([], {}, {
|
||||
@@ -25,39 +36,41 @@ test('곡별 공식과 실제 점수 엔진이 일반 PERFECT 실측 19건을
|
||||
perfectNoteScore: perfectNoteScoreFromPower(measurement.overallPower, scoreRatioFromFormula(formula)),
|
||||
directSongScoreBonusPercent: measurement.directSongScoreBonusPercent
|
||||
});
|
||||
assert.equal(result.baselineScore, measurement.normalPerfectScore, `${calibration.songId}.${calibration.difficulty}`);
|
||||
assert.equal(result.baselineScore, measurement.normalPerfectScore, calibration.songId + '.' + calibration.difficulty);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
assert.equal(count, 19);
|
||||
});
|
||||
|
||||
test('공식 적용 카탈로그와 실제 채보가 같은 분모 및 환산값을 사용한다', async () => {
|
||||
let formulaCount = 0;
|
||||
test('모든 채보의 메타데이터는 현재 DB와 일치하고 게임 원본으로 확인한 분모를 사용한다', async () => {
|
||||
const manifest = readFileSync(new URL('manifest.json', snapshot));
|
||||
assert.equal(createHash('sha256').update(manifest).digest('hex'), pointer.manifestSha256);
|
||||
const historical = [...reference.charts, ...susReference.charts, ...susReference.validationCharts.filter(row => row.fullComboNoteCount)];
|
||||
for (const row of catalog.charts) {
|
||||
const chart = JSON.parse(await readFile(new URL(`../../../static${row.path}`, import.meta.url), 'utf8'));
|
||||
const ref = [...reference.charts, ...susReference.charts, ...susReference.validationCharts.filter((item) => item.fullComboNoteCount)]
|
||||
.find((item) => item.songId === row.songId && item.difficulty === row.difficulty);
|
||||
assert.equal(chart.scoreRatio, row.scoreRatio);
|
||||
assert.equal(chart.scoreCalibrationSource, row.scoreCalibrationSource);
|
||||
if (!ref) {
|
||||
assert.notEqual(row.scoreCalibrationSource, 'formula-v1');
|
||||
continue;
|
||||
}
|
||||
const chart = JSON.parse(await readFile(new URL('../../../static' + row.path, import.meta.url), 'utf8'));
|
||||
const key = row.songId + '.' + row.difficulty;
|
||||
const master = music.get(row.songId);
|
||||
assert.ok(master, key);
|
||||
assert.equal(row.title, titles.get(master.titleLangId), key);
|
||||
assert.equal(row.releaseTime, Number(master.startTime), key);
|
||||
assert.equal(row.difficultyLevel, levels.get(key), key);
|
||||
assert.equal(row.totalNotes, counts.get(key), key);
|
||||
assert.equal(chart.notes.length, counts.get(key), key);
|
||||
assert.deepEqual(row.singerCharacterIds, master.characterIds ?? [], key);
|
||||
assert.equal(row.scoreCalibrationSource, 'formula-v1');
|
||||
assert.ok(typeof ref.liveScoreCoefficientPermil === 'number' && typeof ref.difficultyLevel === 'number');
|
||||
const formula = {
|
||||
perfectNoteWeight: ref.perfectNoteWeight,
|
||||
liveScoreCoefficientPermil: ref.liveScoreCoefficientPermil,
|
||||
difficultyLevel: ref.difficultyLevel
|
||||
};
|
||||
assert.equal(row.scoreRatio, scoreRatioFromFormula(formula));
|
||||
assert.deepEqual(chart.scoreFormula, row.scoreFormula);
|
||||
assert.deepEqual(chart.scoreFormula, formula);
|
||||
assert.equal(chart.notes.length, ref.fullComboNoteCount);
|
||||
formulaCount += 1;
|
||||
assert.equal(row.scoreFormula.liveScoreCoefficientPermil, master.liveScoreCoefficientPermil, key);
|
||||
assert.equal(row.scoreFormula.difficultyLevel, levels.get(key), key);
|
||||
assert.equal(row.scoreRatio, scoreRatioFromFormula(row.scoreFormula), key);
|
||||
assert.equal(chart.scoreRatio, row.scoreRatio, key);
|
||||
assert.deepEqual(chart.scoreFormula, row.scoreFormula, key);
|
||||
assert.equal(chart.source.masterDataSource.manifestSha256, pointer.manifestSha256, key);
|
||||
assert.equal(chart.source.kind, 'game-sus');
|
||||
const ref = historical.find(item => item.songId === row.songId && item.difficulty === row.difficulty);
|
||||
// Two older third-party reference values differ from the game SUS, re-fetched at Octo revision 93.
|
||||
const verifiedGameWeights: Record<string, number> = { 'm0337.hard': 539500, 'm0337.expert': 884700 };
|
||||
if (ref) assert.equal(row.scoreFormula.perfectNoteWeight, verifiedGameWeights[key] ?? ref.perfectNoteWeight, key);
|
||||
}
|
||||
assert.equal(formulaCount, catalog.charts.length);
|
||||
});
|
||||
|
||||
test('잘못된 분모와 난이도로 공식 환산값을 만들지 않는다', () => {
|
||||
@@ -67,37 +80,19 @@ test('잘못된 분모와 난이도로 공식 환산값을 만들지 않는다',
|
||||
}
|
||||
});
|
||||
|
||||
test('m0525는 보충 응답과 일치하는 공식 마스터를 우선 적용한다', async () => {
|
||||
const source = overrides.songs.m0525;
|
||||
const bytes = await readFile(new URL(`../../../${source.file}`, import.meta.url));
|
||||
assert.equal(createHash('sha256').update(bytes).digest('hex'), source.sha256);
|
||||
const master = JSON.parse(bytes.toString('utf8'));
|
||||
const charts = catalog.charts.filter((row) => row.songId === master.id);
|
||||
test('m0525의 DB 가창자·공개 시각과 게임 SUS 점수 분모를 유지한다', async () => {
|
||||
const master = music.get('m0525')!;
|
||||
const charts = catalog.charts.filter(row => row.songId === master.id);
|
||||
assert.equal(charts.length, 4);
|
||||
const releaseTime = Number(master.startTime);
|
||||
const weights = { easy: 161200, normal: 295900, hard: 597600, expert: 872450 };
|
||||
for (const row of charts) {
|
||||
const difficulty = master.difficulties.find((item: { difficultyType: string }) => item.difficultyType.toLowerCase().endsWith('_' + row.difficulty));
|
||||
const chart = JSON.parse(await readFile(new URL(`../../../static${row.path}`, import.meta.url), 'utf8'));
|
||||
const officialDifficulty = officialSong.difficulties.find((item) => item.difficultyType === difficulty.difficultyType);
|
||||
const officialChart = officialSong.charts.find((item) => item.difficultyType === difficulty.difficultyType);
|
||||
assert.equal(row.difficultyLevel, officialDifficulty?.difficultyLevel);
|
||||
assert.equal(row.totalNotes, officialChart?.fullComboNoteCount);
|
||||
assert.equal(officialChart?.chartHash, difficulty.chartHash);
|
||||
assert.equal(row.difficultyLevel, difficulty.difficultyLevel);
|
||||
assert.equal(row.totalNotes, difficulty.fullComboNoteCount);
|
||||
assert.equal(row.title, master.title.kor);
|
||||
const chart = JSON.parse(await readFile(new URL('../../../static' + row.path, import.meta.url), 'utf8'));
|
||||
assert.equal(row.durationSeconds, master.playingSeconds);
|
||||
assert.equal(row.releaseTime, releaseTime);
|
||||
assert.equal(row.scoreFormula.liveScoreCoefficientPermil, master.liveScoreCoefficientPermil);
|
||||
assert.equal(row.scoreFormula.perfectNoteWeight, weights[row.difficulty as keyof typeof weights]);
|
||||
assert.equal(row.singerType, 'group');
|
||||
assert.deepEqual(row.singerCharacterIds, master.characterIds);
|
||||
assert.deepEqual(chart.singerCharacterIds, master.characterIds);
|
||||
assert.deepEqual(chart.source.masterDataSource, officialSong.source);
|
||||
assert.equal(chart.source.metadataSource, undefined);
|
||||
assert.deepEqual(chart.singerCharacterIds, officialSong.music.characterIds);
|
||||
assert.equal(row.scoreFormula.liveScoreCoefficientPermil, officialSong.music.liveScoreCoefficientPermil);
|
||||
assert.equal(chart.specialStartSeconds.length, 5);
|
||||
assert.ok(chart.notes.every((note: { timeSeconds: number }) => Number.isFinite(note.timeSeconds) && note.timeSeconds <= row.durationSeconds));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user