diff --git a/SVSim.Bootstrap/Data/prod-captures/basic-puzzle-info-2026-05-23.json b/SVSim.Bootstrap/Data/prod-captures/basic-puzzle-info-2026-05-23.json
new file mode 100644
index 0000000..30fb69f
--- /dev/null
+++ b/SVSim.Bootstrap/Data/prod-captures/basic-puzzle-info-2026-05-23.json
@@ -0,0 +1 @@
+{"data_headers":{"sid":"41600731e6a1097b3c319a524bd73faf1779643058","short_udid":411054851,"viewer_id":906243102,"servertime":1779643058,"result_code":1},"data":[{"puzzle_master_id":"316","puzzle_data":[{"puzzle_id":"106","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"107","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"108","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"109","puzzle_difficulty":"2","is_cleared":false,"is_additional":true,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"110","puzzle_difficulty":"2","is_cleared":false,"is_additional":true,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"111","puzzle_difficulty":"2","is_cleared":false,"is_additional":true,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"112","puzzle_difficulty":"2","is_cleared":false,"is_additional":true,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"113","puzzle_difficulty":"2","is_cleared":false,"is_additional":true,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0316","is_mission_target":true},{"puzzle_master_id":"315","puzzle_data":[{"puzzle_id":"103","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"104","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"105","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0315","is_mission_target":true},{"puzzle_master_id":"314","puzzle_data":[{"puzzle_id":"100","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"101","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"102","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0314","is_mission_target":true},{"puzzle_master_id":"313","puzzle_data":[{"puzzle_id":"97","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"98","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"99","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0313","is_mission_target":true},{"puzzle_master_id":"312","puzzle_data":[{"puzzle_id":"94","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"95","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"96","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0312","is_mission_target":true},{"puzzle_master_id":"311","puzzle_data":[{"puzzle_id":"91","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"92","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"93","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0311","is_mission_target":true},{"puzzle_master_id":"310","puzzle_data":[{"puzzle_id":"84","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"85","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"86","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0310","is_mission_target":true},{"puzzle_master_id":"309","puzzle_data":[{"puzzle_id":"77","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"78","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"79","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0309","is_mission_target":true},{"puzzle_master_id":"308","puzzle_data":[{"puzzle_id":"74","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"75","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"76","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0308","is_mission_target":true},{"puzzle_master_id":"307","puzzle_data":[{"puzzle_id":"67","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"68","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"69","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0307","is_mission_target":true},{"puzzle_master_id":"306","puzzle_data":[{"puzzle_id":"64","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"65","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"66","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0306","is_mission_target":true},{"puzzle_master_id":"305","puzzle_data":[{"puzzle_id":"61","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"62","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"63","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0305","is_mission_target":true},{"puzzle_master_id":"304","puzzle_data":[{"puzzle_id":"46","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"47","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"48","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0304","is_mission_target":true},{"puzzle_master_id":"303","puzzle_data":[{"puzzle_id":"43","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"44","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"45","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0303","is_mission_target":true},{"puzzle_master_id":"302","puzzle_data":[{"puzzle_id":"40","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"41","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"42","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0302","is_mission_target":true},{"puzzle_master_id":"301","puzzle_data":[{"puzzle_id":"37","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"38","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"39","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3704","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3704","sort_type":"1","basic_title_text_id":"Puzzle_QuestSelect_0301","is_mission_target":true},{"puzzle_master_id":"9","puzzle_data":[{"puzzle_id":"87","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"88","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"89","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"90","puzzle_difficulty":"3","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"600090","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2","":"3"},"is_all_cleared":false,"chara_id":"600090","sort_type":"2","basic_title_text_id":"Puzzle_QuestSelect_0109","is_mission_target":false},{"puzzle_master_id":"8","puzzle_data":[{"puzzle_id":"80","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"81","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"82","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"83","puzzle_difficulty":"3","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"600080","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2","":"3"},"is_all_cleared":false,"chara_id":"600080","sort_type":"2","basic_title_text_id":"Puzzle_QuestSelect_0108","is_mission_target":false},{"puzzle_master_id":"7","puzzle_data":[{"puzzle_id":"70","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"71","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"72","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"73","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"600070","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"600070","sort_type":"2","basic_title_text_id":"Puzzle_QuestSelect_0107","is_mission_target":false},{"puzzle_master_id":"6","puzzle_data":[{"puzzle_id":"52","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"53","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"54","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"55","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"56","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"57","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"58","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"59","puzzle_difficulty":"3","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"60","puzzle_difficulty":"3","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"600060","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2","":"3"},"is_all_cleared":false,"chara_id":"600060","sort_type":"2","basic_title_text_id":"Puzzle_QuestSelect_0106","is_mission_target":false},{"puzzle_master_id":"5","puzzle_data":[{"puzzle_id":"49","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"50","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"51","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3801","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3801","sort_type":"2","basic_title_text_id":"Puzzle_QuestSelect_0105","is_mission_target":false},{"puzzle_master_id":"4","puzzle_data":[{"puzzle_id":"28","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"29","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"30","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"31","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"32","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"33","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"34","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"35","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"36","puzzle_difficulty":"3","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3603","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2","":"3"},"is_all_cleared":false,"chara_id":"3603","sort_type":"2","basic_title_text_id":"Puzzle_QuestSelect_0104","is_mission_target":false},{"puzzle_master_id":"3","puzzle_data":[{"puzzle_id":"20","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"21","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"22","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"23","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"24","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"25","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"26","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"27","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"3403","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"3403","sort_type":"2","basic_title_text_id":"Puzzle_QuestSelect_0103","is_mission_target":false},{"puzzle_master_id":"2","puzzle_data":[{"puzzle_id":"10","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"11","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"12","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"13","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"14","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"15","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"16","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"17","puzzle_difficulty":"3","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":"Puzzle_Unlock_Condition_0001"},{"puzzle_id":"18","puzzle_difficulty":"3","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":"Puzzle_Unlock_Condition_0001"},{"puzzle_id":"19","puzzle_difficulty":"3","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":"Puzzle_Unlock_Condition_0001"}],"puzzle_chara_id":"3208","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2","":"3"},"is_all_cleared":false,"chara_id":"2703","sort_type":"2","basic_title_text_id":"Puzzle_QuestSelect_0102","is_mission_target":false},{"puzzle_master_id":"1","puzzle_data":[{"puzzle_id":"1","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"2","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"3","puzzle_difficulty":"0","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"4","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"5","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"6","puzzle_difficulty":"1","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"7","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"8","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""},{"puzzle_id":"9","puzzle_difficulty":"2","is_cleared":false,"is_additional":false,"is_playable":true,"release_condition_text_id":""}],"puzzle_chara_id":"600050","puzzle_difficulty_name_list":{"Beginner":"0","Experienced":"1","Expert":"2"},"is_all_cleared":false,"chara_id":"600050","sort_type":"2","basic_title_text_id":"Puzzle_QuestSelect_0101","is_mission_target":false}]}
diff --git a/SVSim.Bootstrap/Data/prod-captures/basic-puzzle-mission-2026-05-23.json b/SVSim.Bootstrap/Data/prod-captures/basic-puzzle-mission-2026-05-23.json
new file mode 100644
index 0000000..0686c10
--- /dev/null
+++ b/SVSim.Bootstrap/Data/prod-captures/basic-puzzle-mission-2026-05-23.json
@@ -0,0 +1 @@
+{"data_headers":{"sid":"079f239bb83de281ebc6b2f68dbb2cd11779683743","short_udid":411054851,"viewer_id":906243102,"servertime":1779683743,"result_code":1},"data":[{"mission_name":"Clear all Dragoncraft and Portalcraft puzzles puzzles in the Special Round","require_number":"2","campaign_commence_time":1725670800,"reward_list":[{"reward_type":"4","reward_detail_id":"90001","reward_number":"1"}],"order_id":"5","total_count":"0","is_achieved":false},{"mission_name":"Clear all Forestcraft, Shadowcraft and Bloodcraft puzzles in the Special Round","require_number":"3","campaign_commence_time":1722646800,"reward_list":[{"reward_type":"4","reward_detail_id":"90001","reward_number":"1"}],"order_id":"4","total_count":"0","is_achieved":false},{"mission_name":"Clear all Swordcraft, Runecraft and Havencraft puzzles in the Special Round","require_number":"3","campaign_commence_time":1720227600,"reward_list":[{"reward_type":"4","reward_detail_id":"90001","reward_number":"1"}],"order_id":"3","total_count":"0","is_achieved":false},{"mission_name":"Clear all Special Round puzzles","require_number":"8","campaign_commence_time":1720227600,"reward_list":[{"reward_type":"7","reward_detail_id":"400004315","reward_number":"1"}],"order_id":"2","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 15 puzzles","require_number":"3","campaign_commence_time":1716598800,"reward_list":[{"reward_type":"7","reward_detail_id":"400004314","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 14 puzzles","require_number":"3","campaign_commence_time":1711760400,"reward_list":[{"reward_type":"6","reward_detail_id":"3065004","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 13 puzzles","require_number":"3","campaign_commence_time":1708736400,"reward_list":[{"reward_type":"7","reward_detail_id":"400004313","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 12 puzzles","require_number":"3","campaign_commence_time":1703898000,"reward_list":[{"reward_type":"6","reward_detail_id":"3074009","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 11 puzzles","require_number":"3","campaign_commence_time":1700269200,"reward_list":[{"reward_type":"6","reward_detail_id":"3074008","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 10 puzzles","require_number":"3","campaign_commence_time":1692406800,"reward_list":[{"reward_type":"6","reward_detail_id":"3074007","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 9 puzzles","require_number":"3","campaign_commence_time":1688173200,"reward_list":[{"reward_type":"6","reward_detail_id":"3074006","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 8 puzzles","require_number":"3","campaign_commence_time":1684544400,"reward_list":[{"reward_type":"6","reward_detail_id":"3074005","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 7 puzzles","require_number":"3","campaign_commence_time":1677286800,"reward_list":[{"reward_type":"6","reward_detail_id":"3074004","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 6 puzzles","require_number":"3","campaign_commence_time":1672448400,"reward_list":[{"reward_type":"6","reward_detail_id":"3074003","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 5 puzzles","require_number":"3","campaign_commence_time":1669424400,"reward_list":[{"reward_type":"6","reward_detail_id":"3074002","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 4 puzzles","require_number":"3","campaign_commence_time":1660959000,"reward_list":[{"reward_type":"6","reward_detail_id":"3074001","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 3 puzzles","require_number":"3","campaign_commence_time":1656725400,"reward_list":[{"reward_type":"7","reward_detail_id":"400004105","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 2 puzzles","require_number":"3","campaign_commence_time":1653096600,"reward_list":[{"reward_type":"7","reward_detail_id":"400004104","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 1 puzzles","require_number":"3","campaign_commence_time":1651282200,"reward_list":[{"reward_type":"10","reward_detail_id":"3704","reward_number":"1"}],"order_id":"1","total_count":"3","is_achieved":true}]}
diff --git a/SVSim.Bootstrap/Importers/GlobalsImporter.cs b/SVSim.Bootstrap/Importers/GlobalsImporter.cs
index 694d347..161f8e1 100644
--- a/SVSim.Bootstrap/Importers/GlobalsImporter.cs
+++ b/SVSim.Bootstrap/Importers/GlobalsImporter.cs
@@ -1,4 +1,5 @@
using System.Text.Json;
+using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using SVSim.Database;
using SVSim.Database.Enums;
@@ -32,6 +33,8 @@ public class GlobalsImporter
JsonElement? paymentItemList = LoadCapture(capturesDir, "payment-item-list");
JsonElement? practiceInfo = LoadCapture(capturesDir, "practice-info");
JsonElement? packInfo = LoadCapture(capturesDir, "pack-info");
+ JsonElement? basicPuzzleInfo = LoadCapture(capturesDir, "basic-puzzle-info");
+ JsonElement? basicPuzzleMission = LoadCapture(capturesDir, "basic-puzzle-mission");
int total = 0;
@@ -83,6 +86,17 @@ public class GlobalsImporter
total += await ImportPacks(context, packInfo.Value);
}
+ if (basicPuzzleInfo.HasValue)
+ {
+ total += await ImportPuzzleGroups(context, basicPuzzleInfo.Value);
+ total += await ImportPuzzles(context, basicPuzzleInfo.Value);
+ }
+
+ if (basicPuzzleMission.HasValue)
+ {
+ total += await ImportPuzzleMissions(context, basicPuzzleMission.Value);
+ }
+
await context.SaveChangesAsync();
Console.WriteLine($"[GlobalsImporter] Done: {total} total rows changed.");
return total;
@@ -929,6 +943,140 @@ public class GlobalsImporter
return created + updated;
}
+ // ---------- Basic Puzzle Groups + Puzzles ----------
+
+ ///
+ /// /basic_puzzle/info capture is an array of group objects keyed on puzzle_master_id.
+ /// Numeric wire fields come through as strings — GetInt tolerates both. Idempotent upsert
+ /// by puzzle_master_id; rows missing from a partial capture are left intact.
+ ///
+ private async Task ImportPuzzleGroups(SVSimDbContext context, JsonElement infoData)
+ {
+ if (infoData.ValueKind != JsonValueKind.Array) return 0;
+
+ var existing = await context.PuzzleGroups.ToDictionaryAsync(e => e.Id);
+ int created = 0, updated = 0;
+
+ foreach (var row in infoData.EnumerateArray())
+ {
+ int masterId = GetInt(row, "puzzle_master_id");
+ if (masterId == 0) continue;
+
+ var entry = existing.TryGetValue(masterId, out var ex) ? ex : new PuzzleGroupEntry { Id = masterId };
+ entry.BasicTitleTextId = GetString(row, "basic_title_text_id");
+ entry.PuzzleCharaId = GetInt(row, "puzzle_chara_id");
+ entry.CharaId = GetInt(row, "chara_id");
+ entry.SortType = GetInt(row, "sort_type");
+ entry.DifficultyNameListJson = row.TryGetProperty("puzzle_difficulty_name_list", out var d)
+ ? Serialize(d)
+ : "{}";
+
+ if (ex is null) { context.PuzzleGroups.Add(entry); created++; }
+ else updated++;
+ }
+
+ Console.WriteLine($"[GlobalsImporter] PuzzleGroups: +{created}/~{updated}");
+ return created + updated;
+ }
+
+ ///
+ /// Walks each group's puzzle_data array and upserts PuzzleEntry rows keyed on puzzle_id.
+ /// Groups must have been imported first (FK PuzzleEntry.GroupId → PuzzleGroupEntry.Id).
+ ///
+ private async Task ImportPuzzles(SVSimDbContext context, JsonElement infoData)
+ {
+ if (infoData.ValueKind != JsonValueKind.Array) return 0;
+
+ var existing = await context.Puzzles.ToDictionaryAsync(e => e.Id);
+ int created = 0, updated = 0;
+
+ foreach (var group in infoData.EnumerateArray())
+ {
+ int masterId = GetInt(group, "puzzle_master_id");
+ if (masterId == 0 || !group.TryGetProperty("puzzle_data", out var puzzleArray)) continue;
+ if (puzzleArray.ValueKind != JsonValueKind.Array) continue;
+
+ foreach (var p in puzzleArray.EnumerateArray())
+ {
+ int puzzleId = GetInt(p, "puzzle_id");
+ if (puzzleId == 0) continue;
+
+ var entry = existing.TryGetValue(puzzleId, out var ex) ? ex : new PuzzleEntry { Id = puzzleId };
+ entry.GroupId = masterId;
+ entry.PuzzleDifficulty = GetInt(p, "puzzle_difficulty");
+ entry.IsAdditional = GetBool(p, "is_additional");
+ entry.IsPlayable = GetBool(p, "is_playable");
+ entry.ReleaseConditionTextId = GetString(p, "release_condition_text_id");
+
+ if (ex is null) { context.Puzzles.Add(entry); created++; }
+ else updated++;
+ }
+ }
+
+ Console.WriteLine($"[GlobalsImporter] Puzzles: +{created}/~{updated}");
+ return created + updated;
+ }
+
+ // ---------- Basic Puzzle Missions ----------
+
+ private static readonly Regex RoundMissionPattern =
+ new(@"^Clear all Round (\d+) puzzles$", RegexOptions.Compiled);
+
+ /// Maps the captured mission_name to its target puzzle_master_id. Returns null for
+ /// Special-Round entries — Phase 1 surfaces them with total_count=0 (see design § Out of Scope).
+ internal static int? DeriveTargetPuzzleGroupId(string missionName)
+ {
+ var m = RoundMissionPattern.Match(missionName);
+ return m.Success ? 300 + int.Parse(m.Groups[1].Value) : null;
+ }
+
+ private async Task ImportPuzzleMissions(SVSimDbContext context, JsonElement missionData)
+ {
+ if (missionData.ValueKind != JsonValueKind.Array) return 0;
+
+ // Key by 1-based sequence (the wire has no stable id); first run inserts, re-runs match by index.
+ var existing = await context.PuzzleMissions.ToDictionaryAsync(e => e.Id);
+ int created = 0, updated = 0, unmapped = 0;
+
+ int seq = 1;
+ foreach (var row in missionData.EnumerateArray())
+ {
+ string name = GetString(row, "mission_name");
+ if (string.IsNullOrEmpty(name)) { seq++; continue; }
+
+ var entry = existing.TryGetValue(seq, out var ex) ? ex : new PuzzleMissionEntry { Id = seq };
+ entry.MissionName = name;
+ entry.AchievedMessage = RoundMissionPattern.IsMatch(name)
+ ? RoundMissionPattern.Replace(name, m => $"Cleared all Round {m.Groups[1].Value} puzzles")
+ : "Mission achieved"; // Special-Round fallback; only surfaces if a Special mission ever flips, which won't in Phase 1.
+ entry.RequireNumber = GetInt(row, "require_number");
+ entry.CampaignCommenceTime = GetLong(row, "campaign_commence_time");
+ entry.OrderId = GetInt(row, "order_id");
+
+ // reward_list[0] — single reward per mission. Skip if missing/empty.
+ if (row.TryGetProperty("reward_list", out var rl) && rl.ValueKind == JsonValueKind.Array && rl.GetArrayLength() > 0)
+ {
+ var r = rl[0];
+ entry.RewardType = GetInt(r, "reward_type");
+ entry.RewardDetailId = GetLong(r, "reward_detail_id");
+ entry.RewardNumber = GetInt(r, "reward_number");
+ }
+
+ entry.TargetPuzzleGroupId = DeriveTargetPuzzleGroupId(name);
+ if (entry.TargetPuzzleGroupId is null) unmapped++;
+
+ if (ex is null) { context.PuzzleMissions.Add(entry); created++; }
+ else updated++;
+
+ seq++;
+ }
+
+ if (unmapped > 0)
+ Console.WriteLine($"[GlobalsImporter] PuzzleMissions: {unmapped} Special-Round missions left unmapped (Phase 1 deferral).");
+ Console.WriteLine($"[GlobalsImporter] PuzzleMissions: +{created}/~{updated}");
+ return created + updated;
+ }
+
// ---------- Helpers ----------
private static void WarnOrphans(string label, int count)
diff --git a/SVSim.Database/Enums/UserGoodsType.cs b/SVSim.Database/Enums/UserGoodsType.cs
new file mode 100644
index 0000000..84b99c5
--- /dev/null
+++ b/SVSim.Database/Enums/UserGoodsType.cs
@@ -0,0 +1,26 @@
+namespace SVSim.Database.Enums;
+
+///
+/// Mirrors the client's Wizard.UserGoods.Type enum (Shadowverse_Code/Wizard/UserGoods.cs).
+/// These integers travel on the wire as reward_type on reward_list entries; the
+/// client uses them in PlayerStaticData.UpdateHaveUserGoodsNumByJsonData to route the
+/// grant into the right collection / currency total.
+///
+public enum UserGoodsType
+{
+ RedEther = 1,
+ Crystal = 2,
+ // 3 is unused / placeholder in the client enum.
+ Item = 4,
+ Card = 5,
+ Sleeve = 6,
+ Emblem = 7,
+ Degree = 8,
+ Rupy = 9,
+ Skin = 10, // LeaderSkin in our schema
+ SpotCard = 11,
+ SpotCardPoint = 12,
+ SpotCardOnlyLatestCardPack = 13,
+ FreeGachaCount = 14,
+ MyPageBG = 15,
+}
diff --git a/SVSim.Database/Migrations/20260525055824_AddBasicPuzzle.Designer.cs b/SVSim.Database/Migrations/20260525055824_AddBasicPuzzle.Designer.cs
new file mode 100644
index 0000000..ec656c4
--- /dev/null
+++ b/SVSim.Database/Migrations/20260525055824_AddBasicPuzzle.Designer.cs
@@ -0,0 +1,2164 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using SVSim.Database;
+
+#nullable disable
+
+namespace SVSim.Database.Migrations
+{
+ [DbContext(typeof(SVSimDbContext))]
+ [Migration("20260525055824_AddBasicPuzzle")]
+ partial class AddBasicPuzzle
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.8")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.HasSequence("ShortUdidSequence")
+ .StartsAt(400000000L);
+
+ modelBuilder.Entity("DegreeEntryViewer", b =>
+ {
+ b.Property("DegreesId")
+ .HasColumnType("integer");
+
+ b.Property("ViewersId")
+ .HasColumnType("bigint");
+
+ b.HasKey("DegreesId", "ViewersId");
+
+ b.HasIndex("ViewersId");
+
+ b.ToTable("DegreeEntryViewer");
+ });
+
+ modelBuilder.Entity("EmblemEntryViewer", b =>
+ {
+ b.Property("EmblemsId")
+ .HasColumnType("integer");
+
+ b.Property("ViewersId")
+ .HasColumnType("bigint");
+
+ b.HasKey("EmblemsId", "ViewersId");
+
+ b.HasIndex("ViewersId");
+
+ b.ToTable("EmblemEntryViewer");
+ });
+
+ modelBuilder.Entity("LeaderSkinEntryViewer", b =>
+ {
+ b.Property("LeaderSkinsId")
+ .HasColumnType("integer");
+
+ b.Property("ViewersId")
+ .HasColumnType("bigint");
+
+ b.HasKey("LeaderSkinsId", "ViewersId");
+
+ b.HasIndex("ViewersId");
+
+ b.ToTable("LeaderSkinEntryViewer");
+ });
+
+ modelBuilder.Entity("MyPageBackgroundEntryViewer", b =>
+ {
+ b.Property("MyPageBackgroundsId")
+ .HasColumnType("integer");
+
+ b.Property("ViewersId")
+ .HasColumnType("bigint");
+
+ b.HasKey("MyPageBackgroundsId", "ViewersId");
+
+ b.HasIndex("ViewersId");
+
+ b.ToTable("MyPageBackgroundEntryViewer");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ArenaSeasonConfig", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Cost")
+ .HasColumnType("numeric(20,0)");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Enable")
+ .HasColumnType("integer");
+
+ b.Property("FormatInfo")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("IsJoin")
+ .HasColumnType("boolean");
+
+ b.Property("Mode")
+ .HasColumnType("integer");
+
+ b.Property("RupyCost")
+ .HasColumnType("numeric(20,0)");
+
+ b.Property("TicketCost")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("ArenaSeasons");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.AvatarAbilityEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Ability")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("AbilityCost")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("AbilityDesc")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("BattleStartFirstPlayerTurnBp")
+ .HasColumnType("integer");
+
+ b.Property("BattleStartMaxLife")
+ .HasColumnType("integer");
+
+ b.Property("BattleStartSecondPlayerTurnBp")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LeaderSkinId")
+ .HasColumnType("integer");
+
+ b.Property("PassiveAbility")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PassiveAbilityDesc")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("AvatarAbilities");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BannerEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("ChangeTime")
+ .HasColumnType("integer");
+
+ b.Property("Click")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ImageName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ImagePaths")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("RemainingTime")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Banners");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BattlePassLevelEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Level")
+ .HasColumnType("integer");
+
+ b.Property("RewardData")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.HasKey("Id");
+
+ b.ToTable("BattlePassLevels");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BattlefieldEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsOpen")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Battlefields");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.CardCosmeticReward", b =>
+ {
+ b.Property("CardId")
+ .HasColumnType("bigint");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.Property("CosmeticId")
+ .HasColumnType("bigint");
+
+ b.Property("Quantity")
+ .HasColumnType("integer");
+
+ b.HasKey("CardId", "Type", "CosmeticId");
+
+ b.HasIndex("CardId");
+
+ b.ToTable("CardCosmeticRewards");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ClassEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Classes");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ClassExpEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("NecessaryExp")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("ClassExpCurve");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ColosseumConfig", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("CardPoolName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ColosseumId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ColosseumName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeckFormat")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("EndTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsAllCardEnabled")
+ .HasColumnType("integer");
+
+ b.Property("IsColosseumPeriod")
+ .HasColumnType("boolean");
+
+ b.Property("IsDisplayTips")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsNormalTwoPick")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsRoundPeriod")
+ .HasColumnType("boolean");
+
+ b.Property("IsSpecialMode")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("NowRound")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SalesPeriodInfo")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("StartTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TipsId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Colosseums");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.DailyLoginBonusEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("BonusData")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("BonusId")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("DailyLoginBonuses");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.DefaultDeckEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("CardIdArray")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("ClassId")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeckName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DeckNo")
+ .HasColumnType("integer");
+
+ b.Property("LeaderSkinId")
+ .HasColumnType("integer");
+
+ b.Property("SleeveId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.ToTable("DefaultDecks");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.DefaultLeaderSkinSettingEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("ClassId")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsRandomLeaderSkin")
+ .HasColumnType("integer");
+
+ b.Property("LeaderSkinId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("DefaultLeaderSkinSettings");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.DegreeEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("Degrees");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.EmblemEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("Emblems");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.FeatureMaintenanceEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Data")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FeatureKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("FeatureMaintenances");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.GameConfigSection", b =>
+ {
+ b.Property("SectionName")
+ .HasColumnType("text");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ValueJson")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.HasKey("SectionName");
+
+ b.ToTable("GameConfigs");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ItemEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Items");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.LeaderSkinEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("ClassId")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EmoteId")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClassId");
+
+ b.ToTable("LeaderSkins");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.LoadingExclusionCardEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("bigint");
+
+ b.Property("CardId")
+ .HasColumnType("bigint");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("LoadingExclusionCards");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.MaintenanceCardEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("bigint");
+
+ b.Property("CardId")
+ .HasColumnType("bigint");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("MaintenanceCards");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.MasterPointRankingPeriodEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("BeginTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EndTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("NecessaryScore")
+ .HasColumnType("bigint");
+
+ b.Property("PeriodNum")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("MasterPointRankingPeriods");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.MyPageBackgroundEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("MyPageBackgrounds");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.MyRotationAbilityEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("AbilityId")
+ .HasColumnType("integer");
+
+ b.Property("Data")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("MyRotationAbilities");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.MyRotationSettingEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("AbilitiesCsv")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CardSetIdsCsv")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ReprintedCardIds")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("RestrictedCardIds")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("RotationId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("MyRotationSettings");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.PackConfigEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("BasePackId")
+ .HasColumnType("integer");
+
+ b.Property("CommenceDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CompleteDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("GachaDetail")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("GachaType")
+ .HasColumnType("integer");
+
+ b.Property("IsHide")
+ .HasColumnType("boolean");
+
+ b.Property("IsNew")
+ .HasColumnType("boolean");
+
+ b.Property("IsPreRelease")
+ .HasColumnType("boolean");
+
+ b.Property("OpenCountLimit")
+ .HasColumnType("integer");
+
+ b.Property("OverrideDrawEffectPackId")
+ .HasColumnType("integer");
+
+ b.Property("OverrideUiEffectPackId")
+ .HasColumnType("integer");
+
+ b.Property("PackCategory")
+ .HasColumnType("integer");
+
+ b.Property("PosterType")
+ .HasColumnType("integer");
+
+ b.Property("SalesPeriodTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SleeveId")
+ .HasColumnType("integer");
+
+ b.Property("SpecialSleeveId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("Packs");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.PaymentItemEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("ChargeCrystalNum")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EndTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FreeCrystalNum")
+ .HasColumnType("integer");
+
+ b.Property("ImageName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsResaleProduct")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Price")
+ .HasColumnType("numeric");
+
+ b.Property("ProductId")
+ .HasColumnType("integer");
+
+ b.Property("PurchaseLimit")
+ .HasColumnType("integer");
+
+ b.Property("RemainingTime")
+ .HasColumnType("integer");
+
+ b.Property("ResaleStartDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SpecialShopFlag")
+ .HasColumnType("integer");
+
+ b.Property("StartTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("StoreProductId")
+ .HasColumnType("bigint");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("PaymentItems");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.PracticeOpponentEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("AiDeckLevel")
+ .HasColumnType("integer");
+
+ b.Property("AiLogicLevel")
+ .HasColumnType("integer");
+
+ b.Property("AiMaxLife")
+ .HasColumnType("integer");
+
+ b.Property("Battle3dFieldId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CharaId")
+ .HasColumnType("integer");
+
+ b.Property("ClassId")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DegreeId")
+ .HasColumnType("integer");
+
+ b.Property("IsCampaignPractice")
+ .HasColumnType("boolean");
+
+ b.Property("IsMaintenance")
+ .HasColumnType("boolean");
+
+ b.Property("PracticeId")
+ .HasColumnType("integer");
+
+ b.Property("TextId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("PracticeOpponents");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.PreReleaseInfo", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("CardMasterId")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DefaultCardMasterId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DisplayEndTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EndTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FreeMatchStartTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsPreRotationFreeMatchTerm")
+ .HasColumnType("boolean");
+
+ b.Property("LatestReprintedBaseCardIds")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("NextCardSetId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PreReleaseCardMasterId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PreReleaseId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ReprintedBaseCardIds")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("RotationCardSetIdList")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("StartTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("PreReleaseInfos");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.PuzzleEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("GroupId")
+ .HasColumnType("integer");
+
+ b.Property("IsAdditional")
+ .HasColumnType("boolean");
+
+ b.Property("IsPlayable")
+ .HasColumnType("boolean");
+
+ b.Property("PuzzleDifficulty")
+ .HasColumnType("integer");
+
+ b.Property("PuzzleId")
+ .HasColumnType("integer");
+
+ b.Property("ReleaseConditionTextId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GroupId");
+
+ b.ToTable("Puzzles");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.PuzzleGroupEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("BasicTitleTextId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CharaId")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DifficultyNameListJson")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PuzzleCharaId")
+ .HasColumnType("integer");
+
+ b.Property("PuzzleMasterId")
+ .HasColumnType("integer");
+
+ b.Property("SortType")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("PuzzleGroups");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.PuzzleMissionEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("AchievedMessage")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CampaignCommenceTime")
+ .HasColumnType("bigint");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("MissionName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrderId")
+ .HasColumnType("integer");
+
+ b.Property("RequireNumber")
+ .HasColumnType("integer");
+
+ b.Property("RewardDetailId")
+ .HasColumnType("bigint");
+
+ b.Property("RewardNumber")
+ .HasColumnType("integer");
+
+ b.Property("RewardType")
+ .HasColumnType("integer");
+
+ b.Property