From 404ad03d0d78d0e62d61a763653c2d4da0dcb83f Mon Sep 17 00:00:00 2001 From: 6d486f49 Date: Thu, 11 Jun 2026 09:04:54 -0400 Subject: [PATCH] Day 2 vibes --- ET/Console/Program.cs | 192 +- ET/Web/App.razor | 3 +- ET/Web/Layout/MainLayout.razor | 4 +- ET/Web/Layout/MainLayout.razor.css | 27 +- ET/Web/Layout/NavMenu.razor | 28 +- ET/Web/Layout/NavMenu.razor.css | 66 +- ET/Web/Models/NoteInfo.cs | 6 +- ET/Web/Pages/DocViewer.razor | 5 +- ET/Web/Pages/Docs.razor | 41 +- ET/Web/Pages/Gear.razor | 51 + ET/Web/Pages/Home.razor | 22 +- ET/Web/Pages/NotFound.razor | 19 +- ET/Web/Pages/Overview.razor | 48 + ET/Web/Pages/Simulation.razor | 299 + ET/Web/Program.cs | 3 + ET/Web/Services/DocsService.cs | 100 +- ET/Web/Services/GameSimulationService.cs | 355 + ET/Web/Web.csproj | 9 +- ET/Web/_Imports.razor | 2 + ET/Web/wwwroot/css/app.css | 741 +- ET/Web/wwwroot/docs/images/Contents.png | Bin 0 -> 1601364 bytes .../docs/images/Event Card Example 2.png | Bin 0 -> 925134 bytes .../docs/images/Event Card Example.png | Bin 0 -> 300606 bytes ET/Web/wwwroot/docs/images/Map 1.png | Bin 0 -> 604466 bytes ET/Web/wwwroot/docs/images/Map.png | Bin 0 -> 603311 bytes ET/Web/wwwroot/docs/images/Market Deck.png | Bin 0 -> 24182 bytes ET/Web/wwwroot/docs/images/Market Example.png | Bin 0 -> 77745 bytes .../images/Pasted image 20260609163414.png | Bin 0 -> 2595748 bytes .../images/Pasted image 20260609163625.png | Bin 0 -> 2185489 bytes .../images/Pasted image 20260609163839.png | Bin 0 -> 388663 bytes .../images/Pasted image 20260609170252.png | Bin 0 -> 413947 bytes .../images/Pasted image 20260609170321.png | Bin 0 -> 413947 bytes .../images/Pasted image 20260609170335.png | Bin 0 -> 413947 bytes .../images/Pasted image 20260609211711.png | Bin 0 -> 341498 bytes ET/Web/wwwroot/docs/images/Role Example 2.png | Bin 0 -> 454196 bytes ET/Web/wwwroot/docs/images/Role Example.png | Bin 0 -> 413947 bytes .../wwwroot/docs/images/Table MVP Example.png | Bin 0 -> 2317926 bytes ET/Web/wwwroot/docs/notes-index.json | 129 +- .../docs/notes/activate-flora-ecology.md | 1 + .../docs/notes/activate-predator-ecology.md | 4 + .../docs/notes/activate-prey-ecology.md | 4 + ET/Web/wwwroot/docs/notes/companion-cards.md | 3 + ET/Web/wwwroot/docs/notes/crest.md | 6 + ET/Web/wwwroot/docs/notes/dolewood-canoe.md | 2 +- ET/Web/wwwroot/docs/notes/end-turn.md | 3 + ET/Web/wwwroot/docs/notes/event-cards.md | 1 + ET/Web/wwwroot/docs/notes/event-type.md | 3 + ET/Web/wwwroot/docs/notes/events.md | 2 +- ET/Web/wwwroot/docs/notes/forest-regions.md | 3 + ET/Web/wwwroot/docs/notes/forest.md | 0 ET/Web/wwwroot/docs/notes/grass-regions.md | 2 + ET/Web/wwwroot/docs/notes/grasslands.md | 2 - ET/Web/wwwroot/docs/notes/lake.md | 0 ET/Web/wwwroot/docs/notes/mountain.md | 4 + .../Web/wwwroot/docs/notes/overview.md | 0 ET/Web/wwwroot/docs/notes/region-types.md | 4 + ET/Web/wwwroot/docs/notes/sun.md | 4 + ET/Web/wwwroot/docs/notes/supply.md | 4 +- ET/Web/wwwroot/docs/notes/terrain-cards.md | 8 +- ET/Web/wwwroot/docs/notes/terrain-deck.md | 8 + ET/Web/wwwroot/docs/notes/turn-start.md | 33 + ET/Web/wwwroot/docs/notes/water-regions.md | 3 + ET/Web/wwwroot/index.html | 47 +- .../lib/bootstrap/dist/css/bootstrap-grid.css | 7866 ++++--- .../bootstrap/dist/css/bootstrap-grid.rtl.css | 7867 ++++--- .../bootstrap/dist/css/bootstrap-reboot.css | 666 +- .../dist/css/bootstrap-reboot.rtl.css | 670 +- .../dist/css/bootstrap-utilities.css | 8572 ++++---- .../dist/css/bootstrap-utilities.rtl.css | 8570 ++++---- .../lib/bootstrap/dist/css/bootstrap.css | 18340 ++++++++------- .../lib/bootstrap/dist/css/bootstrap.rtl.css | 18349 +++++++++------- .../lib/bootstrap/dist/js/bootstrap.bundle.js | 12484 +++++------ .../lib/bootstrap/dist/js/bootstrap.esm.js | 6657 +++--- .../lib/bootstrap/dist/js/bootstrap.js | 9022 ++++---- docs/.obsidian/appearance.json | 2 +- docs/.obsidian/workspace.json | 189 +- docs/Mountain Regions.md | 3 + docs/Notes/Activate Flora Ecology.md | 1 + docs/Notes/Activate Predator Ecology.md | 4 + docs/Notes/Activate Prey Ecology.md | 4 + docs/Notes/Companion Cards.md | 3 + docs/Notes/Crest.md | 6 + docs/Notes/Dolewood Canoe.md | 2 +- docs/Notes/End Turn.md | 3 + docs/Notes/Event Cards.md | 1 + docs/Notes/Event Type.md | 3 + docs/Notes/Events.md | 2 +- docs/Notes/Forest Regions.md | 3 + docs/Notes/Forest.md | 0 docs/Notes/Grass Regions.md | 2 + docs/Notes/Grasslands.md | 2 - docs/Notes/Lake.md | 0 docs/Notes/Mountain.md | 4 + docs/Notes/Region Types.md | 4 + docs/Notes/Sun.md | 4 + docs/Notes/Supply.md | 4 +- docs/Notes/Terrain Cards.md | 8 +- docs/Notes/Terrain Deck.md | 8 + docs/Notes/Turn Start.md | 33 + docs/Notes/Water Regions.md | 3 + docs/Overview.md | 26 + docs/Rules.md | 12 + docs/Tasks/Generate overview page.md | 15 + docs/Tasks/Simulate the game state.md | 26 + docs/{ => Tasks}/_Tasks.base | 4 +- 105 files changed, 55677 insertions(+), 46068 deletions(-) create mode 100644 ET/Web/Pages/Gear.razor create mode 100644 ET/Web/Pages/Overview.razor create mode 100644 ET/Web/Pages/Simulation.razor create mode 100644 ET/Web/Services/GameSimulationService.cs create mode 100644 ET/Web/wwwroot/docs/images/Contents.png create mode 100644 ET/Web/wwwroot/docs/images/Event Card Example 2.png create mode 100644 ET/Web/wwwroot/docs/images/Event Card Example.png create mode 100644 ET/Web/wwwroot/docs/images/Map 1.png create mode 100644 ET/Web/wwwroot/docs/images/Map.png create mode 100644 ET/Web/wwwroot/docs/images/Market Deck.png create mode 100644 ET/Web/wwwroot/docs/images/Market Example.png create mode 100644 ET/Web/wwwroot/docs/images/Pasted image 20260609163414.png create mode 100644 ET/Web/wwwroot/docs/images/Pasted image 20260609163625.png create mode 100644 ET/Web/wwwroot/docs/images/Pasted image 20260609163839.png create mode 100644 ET/Web/wwwroot/docs/images/Pasted image 20260609170252.png create mode 100644 ET/Web/wwwroot/docs/images/Pasted image 20260609170321.png create mode 100644 ET/Web/wwwroot/docs/images/Pasted image 20260609170335.png create mode 100644 ET/Web/wwwroot/docs/images/Pasted image 20260609211711.png create mode 100644 ET/Web/wwwroot/docs/images/Role Example 2.png create mode 100644 ET/Web/wwwroot/docs/images/Role Example.png create mode 100644 ET/Web/wwwroot/docs/images/Table MVP Example.png create mode 100644 ET/Web/wwwroot/docs/notes/activate-flora-ecology.md create mode 100644 ET/Web/wwwroot/docs/notes/activate-predator-ecology.md create mode 100644 ET/Web/wwwroot/docs/notes/activate-prey-ecology.md create mode 100644 ET/Web/wwwroot/docs/notes/companion-cards.md create mode 100644 ET/Web/wwwroot/docs/notes/crest.md create mode 100644 ET/Web/wwwroot/docs/notes/end-turn.md create mode 100644 ET/Web/wwwroot/docs/notes/event-type.md create mode 100644 ET/Web/wwwroot/docs/notes/forest-regions.md delete mode 100644 ET/Web/wwwroot/docs/notes/forest.md create mode 100644 ET/Web/wwwroot/docs/notes/grass-regions.md delete mode 100644 ET/Web/wwwroot/docs/notes/grasslands.md delete mode 100644 ET/Web/wwwroot/docs/notes/lake.md rename docs/_Overview.md => ET/Web/wwwroot/docs/notes/overview.md (100%) create mode 100644 ET/Web/wwwroot/docs/notes/region-types.md create mode 100644 ET/Web/wwwroot/docs/notes/sun.md create mode 100644 ET/Web/wwwroot/docs/notes/terrain-deck.md create mode 100644 ET/Web/wwwroot/docs/notes/turn-start.md create mode 100644 ET/Web/wwwroot/docs/notes/water-regions.md create mode 100644 docs/Mountain Regions.md create mode 100644 docs/Notes/Activate Flora Ecology.md create mode 100644 docs/Notes/Activate Predator Ecology.md create mode 100644 docs/Notes/Activate Prey Ecology.md create mode 100644 docs/Notes/Companion Cards.md create mode 100644 docs/Notes/Crest.md create mode 100644 docs/Notes/End Turn.md create mode 100644 docs/Notes/Event Type.md create mode 100644 docs/Notes/Forest Regions.md delete mode 100644 docs/Notes/Forest.md create mode 100644 docs/Notes/Grass Regions.md delete mode 100644 docs/Notes/Grasslands.md delete mode 100644 docs/Notes/Lake.md create mode 100644 docs/Notes/Region Types.md create mode 100644 docs/Notes/Sun.md create mode 100644 docs/Notes/Terrain Deck.md create mode 100644 docs/Notes/Turn Start.md create mode 100644 docs/Notes/Water Regions.md create mode 100644 docs/Overview.md create mode 100644 docs/Rules.md create mode 100644 docs/Tasks/Generate overview page.md create mode 100644 docs/Tasks/Simulate the game state.md rename docs/{ => Tasks}/_Tasks.base (96%) diff --git a/ET/Console/Program.cs b/ET/Console/Program.cs index 48cc2d0..55c227e 100644 --- a/ET/Console/Program.cs +++ b/ET/Console/Program.cs @@ -1,10 +1,12 @@ -using System.Text.RegularExpressions; +using System.Text; +using System.Text.Encodings.Web; using System.Text.Json; +using System.Text.RegularExpressions; var exeDir = AppContext.BaseDirectory; var solutionDir = FindContainingDir(exeDir, "ET.sln") - ?? throw new InvalidOperationException("Cannot find ET.sln"); + ?? throw new InvalidOperationException("Cannot find ET.sln"); var srcDir = Path.GetFullPath(Path.Combine(solutionDir, "..", "docs", "Notes")); var dstDir = Path.GetFullPath(Path.Combine(solutionDir, "Web", "wwwroot", "docs", "notes")); @@ -18,13 +20,18 @@ if (!Directory.Exists(srcDir)) return 1; } -SyncNotes(srcDir, dstDir); +var entries = SyncNotes(srcDir, dstDir); +CopyOverview(solutionDir, dstDir, entries); +CopyImages(solutionDir, Path.GetFullPath(Path.Combine(solutionDir, "Web", "wwwroot", "docs"))); +var indexJson = BuildIndex(entries); +File.WriteAllText(Path.Combine(Path.GetDirectoryName(dstDir)!, "notes-index.json"), indexJson); GenerateMap(srcDir, Path.GetFullPath(Path.Combine(solutionDir, "Web", "wwwroot", "docs"))); +GenerateOverviewPage(solutionDir); Console.WriteLine("Done."); return 0; -static void SyncNotes(string srcDir, string dstDir) +static List SyncNotes(string srcDir, string dstDir) { Directory.CreateDirectory(dstDir); @@ -38,16 +45,33 @@ static void SyncNotes(string srcDir, string dstDir) var name = Path.GetFileNameWithoutExtension(file); var slug = Slugify(name); - File.Copy(file, Path.Combine(dstDir, $"{slug}.md"), overwrite: true); + File.Copy(file, Path.Combine(dstDir, $"{slug}.md"), true); string? category = null; + string? cost = null; + string? gearCategory = null; + string? effect = null; + string? location = null; var content = File.ReadAllText(file); var fmMatch = Regex.Match(content, @"^---\s*\n(.*?)\n---", RegexOptions.Singleline); if (fmMatch.Success) { - var catMatch = Regex.Match(fmMatch.Groups[1].Value, @"(?m)^category:\s*(.+)$"); + var fm = fmMatch.Groups[1].Value; + var catMatch = Regex.Match(fm, @"(?m)^category:\s*(.+)$", RegexOptions.IgnoreCase); if (catMatch.Success) category = catMatch.Groups[1].Value.Trim().Trim('"'); + var costMatch = Regex.Match(fm, @"(?m)^cost:\s*(.+)$", RegexOptions.IgnoreCase); + if (costMatch.Success) + cost = costMatch.Groups[1].Value.Trim().Trim('"'); + var gcMatch = Regex.Match(fm, @"(?m)^gear category:\s*(.+)$", RegexOptions.IgnoreCase); + if (gcMatch.Success) + gearCategory = gcMatch.Groups[1].Value.Trim().Trim('"'); + var effMatch = Regex.Match(fm, @"(?m)^effect:\s*(.+)$", RegexOptions.IgnoreCase); + if (effMatch.Success) + effect = effMatch.Groups[1].Value.Trim().Trim('"'); + var locMatch = Regex.Match(fm, @"(?m)^location:\s*(.+)$", RegexOptions.IgnoreCase); + if (locMatch.Success) + location = locMatch.Groups[1].Value.Trim().Trim('"'); } var entry = new Dictionary @@ -57,18 +81,122 @@ static void SyncNotes(string srcDir, string dstDir) }; if (category != null) entry["category"] = category; + if (cost != null) + entry["cost"] = cost; + if (gearCategory != null) + entry["gearCategory"] = gearCategory; + if (effect != null) + entry["effect"] = effect; + if (location != null) + entry["location"] = location; entries.Add(entry); } - var index = new Dictionary { ["notes"] = entries }; - var json = JsonSerializer.Serialize(index, new JsonSerializerOptions { WriteIndented = true }); - - var indexPath = Path.Combine(Path.GetDirectoryName(dstDir)!, "notes-index.json"); - File.WriteAllText(indexPath, json); - Console.WriteLine($"Copied {entries.Count} notes."); - Console.WriteLine($"Index written to: {indexPath}"); + return entries; +} + +static void CopyOverview(string solutionDir, string dstDir, List entries) +{ + var srcOverview = Path.GetFullPath(Path.Combine(solutionDir, "..", "docs", "Overview.md")); + if (!File.Exists(srcOverview)) return; + + var slug = "overview"; + File.Copy(srcOverview, Path.Combine(dstDir, $"{slug}.md"), true); + + entries.Add(new Dictionary + { + ["slug"] = slug, + ["title"] = "Overview", + ["category"] = "Overview" + }); + + Console.WriteLine("Copied Overview.md."); +} + +static void CopyImages(string solutionDir, string dstDir) +{ + var srcImgs = Path.GetFullPath(Path.Combine(solutionDir, "..", "docs", "Images")); + if (!Directory.Exists(srcImgs)) return; + + var dstImgs = Path.Combine(dstDir, "images"); + Directory.CreateDirectory(dstImgs); + + foreach (var file in Directory.EnumerateFiles(srcImgs)) + { + var name = Path.GetFileName(file); + File.Copy(file, Path.Combine(dstImgs, name), true); + } + + Console.WriteLine($"Copied images from {srcImgs}."); +} + +static string BuildIndex(List entries) +{ + var index = new Dictionary { ["notes"] = entries }; + return JsonSerializer.Serialize(index, new JsonSerializerOptions { WriteIndented = true }); +} + +static void GenerateOverviewPage(string solutionDir) +{ + var pagesDir = Path.GetFullPath(Path.Combine(solutionDir, "Web", "Pages")); + Directory.CreateDirectory(pagesDir); + + var component = """ +@page "/overview" +@inject DocsService DocsService + +Overview + +@if (loading) +{ +
+
+ Loading... +
+
+} +else if (doc == null) +{ +

Overview Not Found

+

The overview document could not be found.

+} +else +{ +
+

@doc.Title

+
+
+ + @if (!string.IsNullOrEmpty(doc.FrontmatterHtml)) + { +
+ Frontmatter + @((MarkupString)doc.FrontmatterHtml) +
+ } + +
+ @((MarkupString)doc.HtmlContent) +
+} + +@code { + private NoteDocument? doc; + private bool loading = true; + + protected override async Task OnInitializedAsync() + { + doc = await DocsService.GetNoteAsync("overview"); + loading = false; + } +} +"""; + + var overviewPagePath = Path.Combine(pagesDir, "Overview.razor"); + File.WriteAllText(overviewPagePath, component); + Console.WriteLine($"Overview page written to: {overviewPagePath}"); } static void GenerateMap(string srcDir, string dstDir) @@ -79,7 +207,7 @@ static void GenerateMap(string srcDir, string dstDir) ["Forest"] = "#2e7d32", ["Mountain"] = "#78909c", ["Water"] = "#42a5f5", - ["Wasteland"] = "#8d6e63", + ["Wasteland"] = "#8d6e63" }; var regionFiles = Directory.EnumerateFiles(srcDir, "*.md") @@ -130,19 +258,19 @@ static void GenerateMap(string srcDir, string dstDir) var srcMapImage = Path.GetFullPath(Path.Combine(srcDir, "..", "Images", "Map.png")); var dstMapImage = Path.Combine(dstDir, "Map.png"); if (File.Exists(srcMapImage)) - File.Copy(srcMapImage, dstMapImage, overwrite: true); + File.Copy(srcMapImage, dstMapImage, true); var nameLookup = regions.ToDictionary(r => r.Name); var pad = 60; var maxX = (regions.Count > 0 ? regions.Max(r => r.X) : 0) + pad * 2; var maxY = (regions.Count > 0 ? regions.Max(r => r.Y) : 0) + pad * 2; - var svg = new System.Text.StringBuilder(); + var svg = new StringBuilder(); svg.AppendLine(""); svg.AppendLine(""""""); - svg.AppendLine($""""""); + svg.AppendLine(""""""); foreach (var (terrain, color) in terrainColors) { svg.AppendLine($""""""); @@ -150,17 +278,18 @@ static void GenerateMap(string srcDir, string dstDir) svg.AppendLine($""""""); svg.AppendLine(""); } + svg.AppendLine(""); foreach (var region in regions) + foreach (var conn in region.Connections) { - foreach (var conn in region.Connections) - { - if (!nameLookup.TryGetValue(conn, out var target) || string.Compare(region.Name, conn, StringComparison.OrdinalIgnoreCase) >= 0) - continue; + if (!nameLookup.TryGetValue(conn, out var target) || + string.Compare(region.Name, conn, StringComparison.OrdinalIgnoreCase) >= 0) + continue; - svg.AppendLine($""""""); - } + svg.AppendLine( + $""""""); } foreach (var region in regions) @@ -171,17 +300,20 @@ static void GenerateMap(string srcDir, string dstDir) svg.AppendLine($""""""); svg.AppendLine($""""""); - svg.AppendLine($""""""); + svg.AppendLine( + $""""""); var labelX = cx + 24; - svg.AppendLine($""""""); - svg.Append(System.Text.Encodings.Web.HtmlEncoder.Default.Encode(region.Name)); + svg.AppendLine( + $""""""); + svg.Append(HtmlEncoder.Default.Encode(region.Name)); svg.AppendLine(""); foreach (var lm in region.Landmarks) { - svg.AppendLine($""""""); - svg.Append(System.Text.Encodings.Web.HtmlEncoder.Default.Encode($"\u2605 {lm}")); + svg.AppendLine( + $""""""); + svg.Append(HtmlEncoder.Default.Encode($"\u2605 {lm}")); svg.AppendLine(""); } @@ -231,10 +363,11 @@ static string? FindContainingDir(string startDir, string markerFile) return dir.FullName; dir = dir.Parent; } + return null; } -class RegionData +internal class RegionData { public string Name { get; set; } = ""; public string Slug { get; set; } = ""; @@ -244,4 +377,3 @@ class RegionData public List Connections { get; set; } = new(); public List Landmarks { get; set; } = new(); } - diff --git a/ET/Web/App.razor b/ET/Web/App.razor index 3e84bca..735b1ff 100644 --- a/ET/Web/App.razor +++ b/ET/Web/App.razor @@ -1,4 +1,5 @@ - +@using Web.Pages + diff --git a/ET/Web/Layout/MainLayout.razor b/ET/Web/Layout/MainLayout.razor index fdc3d5d..29a0f6f 100644 --- a/ET/Web/Layout/MainLayout.razor +++ b/ET/Web/Layout/MainLayout.razor @@ -5,7 +5,9 @@
- @Body + + @Body +
\ No newline at end of file diff --git a/ET/Web/Layout/MainLayout.razor.css b/ET/Web/Layout/MainLayout.razor.css index ecf25e5..96750bb 100644 --- a/ET/Web/Layout/MainLayout.razor.css +++ b/ET/Web/Layout/MainLayout.razor.css @@ -9,7 +9,8 @@ main { } .sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); + background-color: var(--bg-sidebar); + border-right: 1px solid var(--border-color); } .top-row { @@ -21,20 +22,20 @@ main { align-items: center; } - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } +.top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; +} - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } +.top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; +} - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } +.top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; +} @media (max-width: 640.98px) { .top-row { diff --git a/ET/Web/Layout/NavMenu.razor b/ET/Web/Layout/NavMenu.razor index 6525fc7..421d6e3 100644 --- a/ET/Web/Layout/NavMenu.razor +++ b/ET/Web/Layout/NavMenu.razor @@ -1,8 +1,10 @@ -@inject Web.Services.DocsService DocsService +@inject DocsService DocsService