From 6c306a4a0f506b53f907ba997e5e222c5e5bef34 Mon Sep 17 00:00:00 2001 From: grimsace Date: Wed, 1 Jul 2026 15:03:12 -0500 Subject: [PATCH] json handling --- viewmodels/mainwindowviewmodel.cs | 119 +++++++++++++++++++++++++++++- views/mainwindow.axaml | 11 ++- views/mainwindow.axaml.cs | 73 ++++++++++++++++++ 3 files changed, 199 insertions(+), 4 deletions(-) diff --git a/viewmodels/mainwindowviewmodel.cs b/viewmodels/mainwindowviewmodel.cs index d9461c2..a3e520f 100644 --- a/viewmodels/mainwindowviewmodel.cs +++ b/viewmodels/mainwindowviewmodel.cs @@ -9,6 +9,9 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using finder2e_foundry_converter.Models; using finder2e_foundry_converter.Services; +using System.Text.Json.Nodes; +using System.Text.Json; +using finder2e_foundry_converter.Converters; namespace finder2e_foundry_converter.ViewModels { @@ -132,18 +135,128 @@ namespace finder2e_foundry_converter.ViewModels } } + [ObservableProperty] + private string _generatedNpcJson = string.Empty; + + [ObservableProperty] + private string _temporaryNpcPath = string.Empty; + [RelayCommand] private async Task Generate() { + // Minimal validation if (string.IsNullOrEmpty(SelectedModel)) { StatusMessage = "Please select a model first."; return; } - StatusMessage = "Generating..."; - var result = await CurrentService.GenerateResponseAsync(CurrentAddress, SelectedModel, Description, ImageItems.Select(i => i.Path).ToList()); - StatusMessage = result; + StatusMessage = "Generating NPC from template..."; + + try + { + // Load template JSON + var cwd = Directory.GetCurrentDirectory(); + var templatePath = Path.Combine(cwd, "foundry_templates", "npc_template.json"); + if (!File.Exists(templatePath)) + { + StatusMessage = $"Template not found at {templatePath}"; + return; + } + + var templateText = await File.ReadAllTextAsync(templatePath); + var node = JsonNode.Parse(templateText)!.AsObject(); + + // Fill basic fields + node["name"] = string.IsNullOrWhiteSpace(Name) ? "NPC" : Name; + node["img"] = node["img"] ?? "systems/pf2e/icons/default-icons/npc.svg"; + + // system defaults + var systemNode = node["system"] as JsonObject ?? new JsonObject(); + node["system"] = systemNode; + + // Set description/blurb and level + if (!systemNode.TryGetPropertyValue("details", out var detailsNode) || detailsNode is null) + { + detailsNode = new JsonObject(); + systemNode["details"] = detailsNode; + } + var details = detailsNode.AsObject(); + details["blurb"] = Description ?? string.Empty; + if (!details.TryGetPropertyValue("level", out var levelNode) || levelNode is null) + { + details["level"] = new JsonObject(); + } + details["level"].AsObject()["value"] = SelectedLevel; + + // Default to pf2e for now + string gameSystem = "pf2e"; + + // Compute numeric stats using NpcConverter helper functions with conservative tiers + var abilityTier = "moderate"; + var perceptionTier = "moderate"; + var acTier = "moderate"; + var savingTier = "moderate"; + var hpTier = "moderate"; + + var abilities = new JsonObject(); + string[] abilityNames = new[] { "str", "dex", "con", "int", "wis", "cha" }; + foreach (var ab in abilityNames) + { + var modRes = NpcConverter.AbilityScoreModifier(SelectedLevel, abilityTier, gameSystem); + abilities[ab] = new JsonObject { ["mod"] = modRes.IsSuccess ? modRes.Value : 0 }; + } + systemNode["abilities"] = abilities; + + // Perception + var percRes = NpcConverter.PerceptionModifier(SelectedLevel, perceptionTier, gameSystem); + systemNode["perception"] = new JsonObject { ["mod"] = percRes.IsSuccess ? percRes.Value : 0, ["details"] = string.Empty }; + + // Saves + var fort = NpcConverter.SavingThrow(SelectedLevel, savingTier, gameSystem); + var reflex = NpcConverter.SavingThrow(SelectedLevel, savingTier, gameSystem); + var will = NpcConverter.SavingThrow(SelectedLevel, savingTier, gameSystem); + systemNode["saves"] = new JsonObject + { + ["fortitude"] = new JsonObject { ["value"] = fort.IsSuccess ? fort.Value : 0, ["saveDetail"] = string.Empty }, + ["reflex"] = new JsonObject { ["value"] = reflex.IsSuccess ? reflex.Value : 0, ["saveDetail"] = string.Empty }, + ["will"] = new JsonObject { ["value"] = will.IsSuccess ? will.Value : 0, ["saveDetail"] = string.Empty } + }; + + // AC + var acRes = NpcConverter.Ac(SelectedLevel, acTier, gameSystem); + if (!systemNode.TryGetPropertyValue("attributes", out var attributesNode) || attributesNode is null) + { + attributesNode = new JsonObject(); + systemNode["attributes"] = attributesNode; + } + var attributes = attributesNode.AsObject(); + attributes["ac"] = new JsonObject { ["value"] = acRes.IsSuccess ? acRes.Value : 10, ["details"] = string.Empty }; + + // HP + var hpRes = NpcConverter.Hp(SelectedLevel, hpTier, gameSystem); + attributes["hp"] = new JsonObject { ["value"] = hpRes.IsSuccess ? hpRes.Value : 10, ["temp"] = 0, ["max"] = hpRes.IsSuccess ? hpRes.Value : 10, ["details"] = string.Empty }; + + // Skills (leave empty for now) + systemNode["skills"] = new JsonObject(); + + // Finalize JSON text + GeneratedNpcJson = node.ToJsonString(new JsonSerializerOptions { WriteIndented = true }); + + // Save to a temporary file while generating + var tempDir = Path.Combine(Path.GetTempPath(), "Finder2eFoundryConverter"); + if (!Directory.Exists(tempDir)) Directory.CreateDirectory(tempDir); + var safeName = string.IsNullOrWhiteSpace(Name) ? "npc" : string.Concat(Name.Select(ch => Path.GetInvalidFileNameChars().Contains(ch) ? '_' : ch)); + var tempPath = Path.Combine(tempDir, $"{safeName}_{DateTime.Now:yyyyMMddHHmmss}.json"); + await File.WriteAllTextAsync(tempPath, GeneratedNpcJson); + TemporaryNpcPath = tempPath; + + StatusMessage = $"NPC generated and saved to temporary path: {tempPath}"; + } + catch (Exception ex) + { + StatusMessage = $"Error generating NPC: {ex.Message}"; + } } [RelayCommand] diff --git a/views/mainwindow.axaml b/views/mainwindow.axaml index 356b360..2fa5188 100644 --- a/views/mainwindow.axaml +++ b/views/mainwindow.axaml @@ -63,8 +63,17 @@