Story
This commit is contained in:
32504
SVSim.Bootstrap/Data/story/chapters.json
Normal file
32504
SVSim.Bootstrap/Data/story/chapters.json
Normal file
File diff suppressed because it is too large
Load Diff
377
SVSim.Bootstrap/Data/story/sections.json
Normal file
377
SVSim.Bootstrap/Data/story/sections.json
Normal file
@@ -0,0 +1,377 @@
|
||||
[
|
||||
{
|
||||
"id": 0,
|
||||
"world_id": 1,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 1,
|
||||
"all_story_order_id": 6,
|
||||
"name_text_key": "story_section_00",
|
||||
"image_name": "btn_story_tutorial",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 1,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"world_id": 2,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 2,
|
||||
"all_story_order_id": 7,
|
||||
"name_text_key": "story_section_01",
|
||||
"image_name": "btn_story_section_01",
|
||||
"is_leader_select": true,
|
||||
"back_ground_id": 1,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"world_id": 2,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 3,
|
||||
"all_story_order_id": 8,
|
||||
"name_text_key": "story_section_02",
|
||||
"image_name": "btn_story_section_02",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 1,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"world_id": 3,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 4,
|
||||
"all_story_order_id": 9,
|
||||
"name_text_key": "story_section_03",
|
||||
"image_name": "btn_story_section_03",
|
||||
"is_leader_select": true,
|
||||
"back_ground_id": 2,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"world_id": 3,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 5,
|
||||
"all_story_order_id": 10,
|
||||
"name_text_key": "story_section_04",
|
||||
"image_name": "btn_story_section_04",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 3,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"world_id": 4,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 6,
|
||||
"all_story_order_id": 11,
|
||||
"name_text_key": "story_section_05",
|
||||
"image_name": "btn_story_section_05",
|
||||
"is_leader_select": true,
|
||||
"back_ground_id": 4,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"world_id": 4,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 7,
|
||||
"all_story_order_id": 12,
|
||||
"name_text_key": "story_section_06",
|
||||
"image_name": "btn_story_section_06",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 4,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"world_id": 4,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 8,
|
||||
"all_story_order_id": 13,
|
||||
"name_text_key": "story_section_07",
|
||||
"image_name": "btn_story_section_07",
|
||||
"is_leader_select": true,
|
||||
"back_ground_id": 7,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"world_id": 4,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 9,
|
||||
"all_story_order_id": 14,
|
||||
"name_text_key": "story_section_08",
|
||||
"image_name": "btn_story_section_08",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 7,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"world_id": 4,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 10,
|
||||
"all_story_order_id": 15,
|
||||
"name_text_key": "story_section_09",
|
||||
"image_name": "btn_story_section_09",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 9,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"world_id": 5,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 11,
|
||||
"all_story_order_id": 16,
|
||||
"name_text_key": "story_section_10",
|
||||
"image_name": "btn_story_section_10",
|
||||
"is_leader_select": true,
|
||||
"back_ground_id": 10,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"world_id": 5,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 12,
|
||||
"all_story_order_id": 17,
|
||||
"name_text_key": "story_section_11",
|
||||
"image_name": "btn_story_section_11",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 10,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"world_id": 5,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 13,
|
||||
"all_story_order_id": 18,
|
||||
"name_text_key": "story_section_12",
|
||||
"image_name": "btn_story_section_12",
|
||||
"is_leader_select": true,
|
||||
"back_ground_id": 12,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"world_id": 5,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 14,
|
||||
"all_story_order_id": 19,
|
||||
"name_text_key": "story_section_13",
|
||||
"image_name": "btn_story_section_13",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 12,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"world_id": 5,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 15,
|
||||
"all_story_order_id": 20,
|
||||
"name_text_key": "story_section_14",
|
||||
"image_name": "btn_story_section_14",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 10,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"world_id": 6,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 16,
|
||||
"all_story_order_id": 21,
|
||||
"name_text_key": "story_section_15",
|
||||
"image_name": "btn_story_section_15",
|
||||
"is_leader_select": true,
|
||||
"back_ground_id": 15,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"world_id": 6,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 17,
|
||||
"all_story_order_id": 22,
|
||||
"name_text_key": "story_section_16",
|
||||
"image_name": "btn_story_section_16",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 15,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"world_id": 7,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 18,
|
||||
"all_story_order_id": 23,
|
||||
"name_text_key": "story_section_17",
|
||||
"image_name": "btn_story_section_17",
|
||||
"is_leader_select": true,
|
||||
"back_ground_id": 17,
|
||||
"chapter_select_type": 2,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"world_id": 7,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 19,
|
||||
"all_story_order_id": 24,
|
||||
"name_text_key": "story_section_18",
|
||||
"image_name": "btn_story_section_18",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 17,
|
||||
"chapter_select_type": 2,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"world_id": 8,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 20,
|
||||
"all_story_order_id": 25,
|
||||
"name_text_key": "story_section_19",
|
||||
"image_name": "btn_story_section_19",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 19,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"world_id": 8,
|
||||
"story_api_type": "Main",
|
||||
"order_id": 21,
|
||||
"all_story_order_id": 26,
|
||||
"name_text_key": "story_section_20",
|
||||
"image_name": "btn_story_section_20",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 2,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 1,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 9001,
|
||||
"world_id": 1,
|
||||
"story_api_type": "Limited",
|
||||
"order_id": 1,
|
||||
"all_story_order_id": 2,
|
||||
"name_text_key": "story_section_9001",
|
||||
"image_name": "btn_story_section_9001",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 2,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 2,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 9002,
|
||||
"world_id": 1,
|
||||
"story_api_type": "Limited",
|
||||
"order_id": 2,
|
||||
"all_story_order_id": 3,
|
||||
"name_text_key": "story_section_9002",
|
||||
"image_name": "btn_story_section_9002",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 9,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 2,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 9003,
|
||||
"world_id": 1,
|
||||
"story_api_type": "Limited",
|
||||
"order_id": 3,
|
||||
"all_story_order_id": 4,
|
||||
"name_text_key": "story_section_9003",
|
||||
"image_name": "btn_story_section_9003",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 1,
|
||||
"chapter_select_type": 1,
|
||||
"story_type_overwrite": 2,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
},
|
||||
{
|
||||
"id": 9005,
|
||||
"world_id": 1,
|
||||
"story_api_type": "Limited",
|
||||
"order_id": 5,
|
||||
"all_story_order_id": 5,
|
||||
"name_text_key": "story_section_9005",
|
||||
"image_name": "btn_story_section_9005",
|
||||
"is_leader_select": false,
|
||||
"back_ground_id": 9005,
|
||||
"chapter_select_type": 2,
|
||||
"story_type_overwrite": 2,
|
||||
"is_under_maintenance": false,
|
||||
"is_play_another_end_appearance_animation": false
|
||||
}
|
||||
]
|
||||
758
SVSim.Bootstrap/Data/story/special-battle-settings.json
Normal file
758
SVSim.Bootstrap/Data/story/special-battle-settings.json
Normal file
File diff suppressed because one or more lines are too long
50
SVSim.Bootstrap/Data/story/worlds.json
Normal file
50
SVSim.Bootstrap/Data/story/worlds.json
Normal file
@@ -0,0 +1,50 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"title_text_key": "story_select_world_title_01",
|
||||
"panel_image_name": "story_select_world_panel_01",
|
||||
"ribbon_text": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title_text_key": "story_select_world_title_02",
|
||||
"panel_image_name": "story_select_world_panel_02",
|
||||
"ribbon_text": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title_text_key": "story_select_world_title_03",
|
||||
"panel_image_name": "story_select_world_panel_03",
|
||||
"ribbon_text": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title_text_key": "story_select_world_title_04",
|
||||
"panel_image_name": "story_select_world_panel_04",
|
||||
"ribbon_text": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"title_text_key": "story_select_world_title_05",
|
||||
"panel_image_name": "story_select_world_panel_05",
|
||||
"ribbon_text": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"title_text_key": "story_select_world_title_06",
|
||||
"panel_image_name": "story_select_world_panel_06",
|
||||
"ribbon_text": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"title_text_key": "story_select_world_title_07",
|
||||
"panel_image_name": "story_select_world_panel_07",
|
||||
"ribbon_text": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"title_text_key": "story_select_world_title_08",
|
||||
"panel_image_name": "story_select_world_panel_08-01",
|
||||
"ribbon_text": ""
|
||||
}
|
||||
]
|
||||
276
SVSim.Bootstrap/Importers/StoryImporter.cs
Normal file
276
SVSim.Bootstrap/Importers/StoryImporter.cs
Normal file
@@ -0,0 +1,276 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Entities.Story;
|
||||
|
||||
namespace SVSim.Bootstrap.Importers;
|
||||
|
||||
/// <summary>
|
||||
/// Reads worlds.json, sections.json, chapters.json, special-battle-settings.json from a story
|
||||
/// data directory and upserts the corresponding entities. Idempotent. FK ordering: SBS → Worlds
|
||||
/// → Sections → Chapters (with owned collections cascading).
|
||||
/// </summary>
|
||||
public class StoryImporter
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonOpts = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString,
|
||||
};
|
||||
|
||||
public async Task ImportAsync(SVSimDbContext context, string storyDataDir)
|
||||
{
|
||||
string worldsPath = Path.Combine(storyDataDir, "importer-worlds.json");
|
||||
string sectionsPath = Path.Combine(storyDataDir, "importer-sections.json");
|
||||
string chaptersPath = Path.Combine(storyDataDir, "importer-chapters.json");
|
||||
string sbsPath = Path.Combine(storyDataDir, "importer-sbs.json");
|
||||
|
||||
// Fallback to production filenames when fixture-prefixed names aren't present.
|
||||
if (!File.Exists(worldsPath)) worldsPath = Path.Combine(storyDataDir, "worlds.json");
|
||||
if (!File.Exists(sectionsPath)) sectionsPath = Path.Combine(storyDataDir, "sections.json");
|
||||
if (!File.Exists(chaptersPath)) chaptersPath = Path.Combine(storyDataDir, "chapters.json");
|
||||
if (!File.Exists(sbsPath)) sbsPath = Path.Combine(storyDataDir, "special-battle-settings.json");
|
||||
|
||||
if (!File.Exists(chaptersPath))
|
||||
{
|
||||
Console.Error.WriteLine($"[Story] chapters.json not found at {chaptersPath}; skipping story import.");
|
||||
return;
|
||||
}
|
||||
|
||||
var inputSbs = await ReadOrEmptyAsync<List<SbsInput>>(sbsPath);
|
||||
var inputWorlds = await ReadOrEmptyAsync<List<WorldInput>>(worldsPath);
|
||||
var inputSections = await ReadOrEmptyAsync<List<SectionInput>>(sectionsPath);
|
||||
var inputChapters = await ReadOrEmptyAsync<List<ChapterInput>>(chaptersPath);
|
||||
|
||||
Console.WriteLine($"[Story] Parsed {inputWorlds.Count} worlds, {inputSections.Count} sections, " +
|
||||
$"{inputChapters.Count} chapters, {inputSbs.Count} sbs payloads.");
|
||||
|
||||
int sbsCreated = 0, sbsUpdated = 0;
|
||||
var existingSbs = await context.SpecialBattleSettings.ToDictionaryAsync(x => x.Id);
|
||||
foreach (var s in inputSbs)
|
||||
{
|
||||
if (existingSbs.TryGetValue(s.Id, out var row))
|
||||
{
|
||||
Apply(row, s); sbsUpdated++;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.SpecialBattleSettings.Add(ToEntity(s)); sbsCreated++;
|
||||
}
|
||||
}
|
||||
|
||||
int wCreated = 0, wUpdated = 0;
|
||||
var existingWorlds = await context.StoryWorlds.ToDictionaryAsync(x => x.Id);
|
||||
foreach (var w in inputWorlds)
|
||||
{
|
||||
if (existingWorlds.TryGetValue(w.Id, out var row))
|
||||
{
|
||||
row.TitleTextKey = w.TitleTextKey; row.PanelImageName = w.PanelImageName; row.RibbonText = w.RibbonText;
|
||||
wUpdated++;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.StoryWorlds.Add(new StoryWorld {
|
||||
Id = w.Id, TitleTextKey = w.TitleTextKey,
|
||||
PanelImageName = w.PanelImageName, RibbonText = w.RibbonText });
|
||||
wCreated++;
|
||||
}
|
||||
}
|
||||
|
||||
int secCreated = 0, secUpdated = 0;
|
||||
var existingSections = await context.StorySections.ToDictionaryAsync(x => x.Id);
|
||||
foreach (var s in inputSections)
|
||||
{
|
||||
if (existingSections.TryGetValue(s.Id, out var row)) { Apply(row, s); secUpdated++; }
|
||||
else { context.StorySections.Add(ToEntity(s)); secCreated++; }
|
||||
}
|
||||
|
||||
int chCreated = 0, chUpdated = 0;
|
||||
var existingChapters = await context.StoryChapters
|
||||
.Include(c => c.BattleSettings).Include(c => c.Rewards).Include(c => c.SubChapters)
|
||||
.ToDictionaryAsync(x => x.StoryId);
|
||||
foreach (var c in inputChapters)
|
||||
{
|
||||
if (existingChapters.TryGetValue(c.StoryId, out var row)) { Apply(row, c); chUpdated++; }
|
||||
else { context.StoryChapters.Add(ToEntity(c)); chCreated++; }
|
||||
}
|
||||
|
||||
Console.WriteLine($"[Story] Saving: worlds +{wCreated}/~{wUpdated}, sections +{secCreated}/~{secUpdated}, " +
|
||||
$"chapters +{chCreated}/~{chUpdated}, sbs +{sbsCreated}/~{sbsUpdated}...");
|
||||
await context.SaveChangesAsync();
|
||||
Console.WriteLine("[Story] Done.");
|
||||
}
|
||||
|
||||
private static async Task<T> ReadOrEmptyAsync<T>(string path) where T : new()
|
||||
{
|
||||
if (!File.Exists(path)) return new T();
|
||||
await using var fs = File.OpenRead(path);
|
||||
return await JsonSerializer.DeserializeAsync<T>(fs, JsonOpts) ?? new T();
|
||||
}
|
||||
|
||||
// --- mapping helpers ---
|
||||
|
||||
private static SpecialBattleSetting ToEntity(SbsInput s) => Apply(new SpecialBattleSetting { Id = s.Id }, s);
|
||||
private static SpecialBattleSetting Apply(SpecialBattleSetting row, SbsInput s)
|
||||
{
|
||||
row.PlayerFirstTurn = s.PlayerFirstTurn;
|
||||
row.PlayerStartPp = s.PlayerStartPp; row.EnemyStartPp = s.EnemyStartPp;
|
||||
row.PlayerStartLife = s.PlayerStartLife; row.EnemyStartLife = s.EnemyStartLife;
|
||||
row.PlayerAttachSkill = s.PlayerAttachSkill ?? ""; row.EnemyAttachSkill = s.EnemyAttachSkill ?? "";
|
||||
row.IdOverrideInBattleLog = s.IdOverrideInBattleLog ?? "";
|
||||
row.BanishEffectOverride = s.BanishEffectOverride ?? "";
|
||||
row.TokenDrawEffectOverride = s.TokenDrawEffectOverride ?? "";
|
||||
row.SpecialTokenDrawEffectOverride = s.SpecialTokenDrawEffectOverride ?? "";
|
||||
row.ResultSkip = s.ResultSkip;
|
||||
row.VsEffectOverride = s.VsEffectOverride;
|
||||
row.ClassDestroyEffectOverride = s.ClassDestroyEffectOverride;
|
||||
row.Note = s.Note;
|
||||
return row;
|
||||
}
|
||||
|
||||
private static StorySection ToEntity(SectionInput s) => Apply(new StorySection { Id = s.Id }, s);
|
||||
private static StorySection Apply(StorySection row, SectionInput s)
|
||||
{
|
||||
row.WorldId = s.WorldId;
|
||||
row.StoryApiType = Enum.Parse<StoryApiType>(s.StoryApiType ?? "Main");
|
||||
row.OrderId = s.OrderId; row.AllStoryOrderId = s.AllStoryOrderId;
|
||||
row.NameTextKey = s.NameTextKey ?? ""; row.ImageName = s.ImageName ?? "";
|
||||
row.IsLeaderSelect = s.IsLeaderSelect; row.BackGroundId = s.BackGroundId;
|
||||
row.ChapterSelectType = s.ChapterSelectType; row.StoryTypeOverwrite = s.StoryTypeOverwrite;
|
||||
row.IsUnderMaintenance = s.IsUnderMaintenance;
|
||||
row.IsPlayAnotherEndAppearanceAnimation = s.IsPlayAnotherEndAppearanceAnimation;
|
||||
return row;
|
||||
}
|
||||
|
||||
private static StoryChapter ToEntity(ChapterInput c) => Apply(new StoryChapter { StoryId = c.StoryId }, c);
|
||||
private static StoryChapter Apply(StoryChapter row, ChapterInput c)
|
||||
{
|
||||
row.SectionId = c.SectionId; row.CharaId = c.CharaId;
|
||||
row.ChapterId = c.ChapterId ?? ""; row.NextChapterId = c.NextChapterId ?? "";
|
||||
row.RequiredChapterId = c.RequiredChapterId;
|
||||
row.SelectionDisplayPosition = c.SelectionDisplayPosition;
|
||||
row.SelectionTextId = c.SelectionTextId;
|
||||
row.ShowCoordinate = c.ShowCoordinate;
|
||||
row.XCoordinate = (decimal)c.XCoordinate; row.YCoordinate = (decimal)c.YCoordinate;
|
||||
row.IsCameraMovable = c.IsCameraMovable; row.ShowSubtitles = c.ShowSubtitles;
|
||||
row.BattleExists = c.BattleExists;
|
||||
row.EnemyCharaId = c.EnemyCharaId; row.EnemyClass = c.EnemyClass; row.EnemyAiId = c.EnemyAiId;
|
||||
row.BgFileName = c.BgFileName ?? "";
|
||||
row.ChapterEffectPath = c.ChapterEffectPath; row.ChapterClearTextId = c.ChapterClearTextId;
|
||||
row.Battle3dFieldId = c.Battle3dFieldId; row.BgmId = c.BgmId ?? "";
|
||||
row.SpecialBattleSettingId = c.SpecialBattleSettingId;
|
||||
row.ReleasePoint = c.ReleasePoint; row.IsMaintenanceChapter = c.IsMaintenanceChapter;
|
||||
row.IsPlayAnotherEndAppearanceAnimation = c.IsPlayAnotherEndAppearanceAnimation;
|
||||
row.IsReleasedAnotherEnd = c.IsReleasedAnotherEnd;
|
||||
row.IsSkipEnabled = c.IsSkipEnabled;
|
||||
|
||||
// Owned collections: clear + replace, EF tracks the deletes.
|
||||
row.BattleSettings.Clear();
|
||||
foreach (var b in c.BattleSettings ?? new())
|
||||
row.BattleSettings.Add(new StoryChapterBattleSetting
|
||||
{
|
||||
DeckClassId = b.DeckClassId,
|
||||
PlayerEmotionOverride = b.PlayerEmotionOverride,
|
||||
EnemyEmotionOverride = b.EnemyEmotionOverride,
|
||||
SkinIdOverride = b.SkinIdOverride,
|
||||
Battle3dFieldIdOverride = b.Battle3dFieldIdOverride,
|
||||
BgmIdOverride = b.BgmIdOverride,
|
||||
DeckSkinIdOverride = b.DeckSkinIdOverride,
|
||||
});
|
||||
|
||||
row.Rewards.Clear();
|
||||
foreach (var r in c.StoryReward ?? new())
|
||||
row.Rewards.Add(new StoryChapterReward
|
||||
{
|
||||
RewardType = r.RewardType,
|
||||
RewardDetailId = r.RewardDetailId,
|
||||
RewardNumber = r.RewardNumber,
|
||||
});
|
||||
|
||||
row.SubChapters.Clear();
|
||||
foreach (var sc in c.SubChapters ?? new())
|
||||
row.SubChapters.Add(new StorySubChapter
|
||||
{
|
||||
SubChapterId = sc.SubChapterId,
|
||||
SubChapterStoryId = sc.SubChapterStoryId,
|
||||
IsMaintenanceChapter = sc.IsMaintenanceChapter,
|
||||
});
|
||||
return row;
|
||||
}
|
||||
|
||||
// --- input shapes (snake_case via JsonOpts) ---
|
||||
|
||||
private class SbsInput
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int PlayerFirstTurn { get; set; }
|
||||
public int PlayerStartPp { get; set; } public int EnemyStartPp { get; set; }
|
||||
public int PlayerStartLife { get; set; } public int EnemyStartLife { get; set; }
|
||||
public string? PlayerAttachSkill { get; set; } public string? EnemyAttachSkill { get; set; }
|
||||
public string? IdOverrideInBattleLog { get; set; }
|
||||
public string? BanishEffectOverride { get; set; }
|
||||
public string? TokenDrawEffectOverride { get; set; }
|
||||
public string? SpecialTokenDrawEffectOverride { get; set; }
|
||||
public int ResultSkip { get; set; } public int VsEffectOverride { get; set; }
|
||||
public int ClassDestroyEffectOverride { get; set; }
|
||||
public string? Note { get; set; }
|
||||
}
|
||||
private class WorldInput
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string TitleTextKey { get; set; } = "";
|
||||
public string PanelImageName { get; set; } = "";
|
||||
public string RibbonText { get; set; } = "";
|
||||
}
|
||||
private class SectionInput
|
||||
{
|
||||
public int Id { get; set; } public int? WorldId { get; set; }
|
||||
public string? StoryApiType { get; set; }
|
||||
public int OrderId { get; set; } public int AllStoryOrderId { get; set; }
|
||||
public string? NameTextKey { get; set; } public string? ImageName { get; set; }
|
||||
public bool IsLeaderSelect { get; set; } public int BackGroundId { get; set; }
|
||||
public int ChapterSelectType { get; set; } public int StoryTypeOverwrite { get; set; }
|
||||
public bool IsUnderMaintenance { get; set; }
|
||||
public bool IsPlayAnotherEndAppearanceAnimation { get; set; }
|
||||
}
|
||||
private class ChapterInput
|
||||
{
|
||||
public int StoryId { get; set; } public int SectionId { get; set; } public int CharaId { get; set; }
|
||||
public string? ChapterId { get; set; } public string? NextChapterId { get; set; }
|
||||
public string? RequiredChapterId { get; set; }
|
||||
public string? SelectionDisplayPosition { get; set; } public string? SelectionTextId { get; set; }
|
||||
public int ShowCoordinate { get; set; }
|
||||
public double XCoordinate { get; set; } public double YCoordinate { get; set; }
|
||||
public int IsCameraMovable { get; set; } public int ShowSubtitles { get; set; }
|
||||
public bool BattleExists { get; set; } public int EnemyCharaId { get; set; }
|
||||
public int EnemyClass { get; set; } public int EnemyAiId { get; set; }
|
||||
public string? BgFileName { get; set; } public string? ChapterEffectPath { get; set; }
|
||||
public string? ChapterClearTextId { get; set; }
|
||||
[JsonPropertyName("battle3dfield_id")]
|
||||
public int Battle3dFieldId { get; set; }
|
||||
public string? BgmId { get; set; }
|
||||
public int? SpecialBattleSettingId { get; set; }
|
||||
public int ReleasePoint { get; set; } public bool IsMaintenanceChapter { get; set; }
|
||||
public bool IsPlayAnotherEndAppearanceAnimation { get; set; }
|
||||
public bool IsReleasedAnotherEnd { get; set; } public bool IsSkipEnabled { get; set; }
|
||||
public List<BattleSettingInput>? BattleSettings { get; set; }
|
||||
public List<RewardInput>? StoryReward { get; set; }
|
||||
public List<SubChapterInput>? SubChapters { get; set; }
|
||||
}
|
||||
private class BattleSettingInput {
|
||||
public int DeckClassId { get; set; }
|
||||
public int PlayerEmotionOverride { get; set; } public int EnemyEmotionOverride { get; set; }
|
||||
public int SkinIdOverride { get; set; }
|
||||
[JsonPropertyName("battle3dfield_id_override")]
|
||||
public int Battle3dFieldIdOverride { get; set; }
|
||||
public int BgmIdOverride { get; set; } public int DeckSkinIdOverride { get; set; }
|
||||
}
|
||||
private class RewardInput {
|
||||
public int RewardType { get; set; } public long RewardDetailId { get; set; } public int RewardNumber { get; set; }
|
||||
}
|
||||
private class SubChapterInput {
|
||||
public int SubChapterId { get; set; } public int SubChapterStoryId { get; set; }
|
||||
public bool IsMaintenanceChapter { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public static class Program
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (opts.SkipReference && opts.SkipCards && opts.SkipGlobals)
|
||||
if (opts.SkipReference && opts.SkipCards && opts.SkipGlobals && opts.SkipStory)
|
||||
{
|
||||
Console.Error.WriteLine("All --skip-* flags set; nothing to do.");
|
||||
return 1;
|
||||
@@ -82,6 +82,15 @@ public static class Program
|
||||
Console.WriteLine("[Bootstrap] --skip-globals set; skipping globals import.");
|
||||
}
|
||||
|
||||
if (!opts.SkipStory)
|
||||
{
|
||||
await new StoryImporter().ImportAsync(context, opts.StoryDataDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("[Bootstrap] --skip-story set; skipping story import.");
|
||||
}
|
||||
|
||||
Console.WriteLine("[Bootstrap] Complete.");
|
||||
return 0;
|
||||
}
|
||||
@@ -95,6 +104,8 @@ public static class Program
|
||||
bool skipReference = false;
|
||||
bool skipCards = false;
|
||||
bool skipGlobals = false;
|
||||
bool skipStory = false;
|
||||
string? storyDataDir = null;
|
||||
string? positionalCards = null;
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
@@ -109,6 +120,8 @@ public static class Program
|
||||
case "--skip-reference": skipReference = true; break;
|
||||
case "--skip-cards": skipCards = true; break;
|
||||
case "--skip-globals": skipGlobals = true; break;
|
||||
case "--skip-story": skipStory = true; break;
|
||||
case "--story-data-dir": storyDataDir = NextArg(args, ref i); break;
|
||||
default:
|
||||
// Back-compat: legacy positional form `svsim-card-import <cards.json> [connection]`.
|
||||
if (positionalCards is null && !a.StartsWith('-')) positionalCards = a;
|
||||
@@ -129,13 +142,16 @@ public static class Program
|
||||
string cardsFile = cards ?? positionalCards ?? shippedCardsFile;
|
||||
string capturesDir = captures ?? shippedCaptures;
|
||||
string refDir = referenceDataDir ?? shippedDataDir;
|
||||
string shippedStoryDir = Path.Combine(shippedDataDir, "story");
|
||||
string storyDir = storyDataDir ?? shippedStoryDir;
|
||||
|
||||
string connStr = connection
|
||||
?? Environment.GetEnvironmentVariable("NPGSQL_CONNECTION")
|
||||
?? DefaultConnectionString;
|
||||
|
||||
return new BootstrapOptions(
|
||||
cardsFile, capturesDir, refDir, connStr, skipReference, skipCards, skipGlobals);
|
||||
cardsFile, capturesDir, refDir, connStr, skipReference, skipCards, skipGlobals,
|
||||
skipStory, storyDir);
|
||||
}
|
||||
|
||||
private static string NextArg(string[] args, ref int i)
|
||||
@@ -165,6 +181,8 @@ public static class Program
|
||||
" --skip-reference Skip reference-data import (classes, sleeves, ranks, ...)\n" +
|
||||
" --skip-cards Skip card + card-cosmetic-reward import\n" +
|
||||
" --skip-globals Skip prod-captured globals import\n" +
|
||||
" --story-data-dir <dir> Override story data directory (default: shipped Data/story)\n" +
|
||||
" --skip-story Skip story import (worlds/sections/chapters/sbs)\n" +
|
||||
"\n" +
|
||||
"Back-compat: `svsim-bootstrap <cards.json> [connection]` still works (positional).");
|
||||
}
|
||||
@@ -176,5 +194,7 @@ public static class Program
|
||||
string ConnectionString,
|
||||
bool SkipReference,
|
||||
bool SkipCards,
|
||||
bool SkipGlobals);
|
||||
bool SkipGlobals,
|
||||
bool SkipStory,
|
||||
string StoryDataDir);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
<Content Include="Data\cards.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Data\story\*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user