M6.19: Confirm & Begin → CharacterBuilder handoff + hybrid pick plumbing
The wizard now produces a real runtime Character instead of just persisting the draft Resource. New Theriapolis.Godot/UI/CharacterAssembler bridges CharacterDraft → CharacterBuilder, picking the purebred Build or hybrid TryBuildHybrid path, threading clade/species/class/background lookups, ability scores, skill picks, dominant-parent, subclass id, and the items table for the starting kit. The captured PlayerCharacterState writes to user://character.json (resumability before the M7 save format lands) and the live Character is held in CharacterAssembler.LastBuilt for future PlayScreen pickup. StepReview.OnConfirmPressed surfaces build errors on the status label instead of crashing, logs HP/hybrid/skill totals on success, and keeps emitting the existing CharacterConfirmed signal. Hybrid lineage bonuses now match the wizard's preview math. CharacterBuilder gains HybridSireChosenAbility / HybridDamChosenAbility; TryBuildHybrid replaces the old "apply both clades' full mods + both species mods" blend with "apply each parent's chosen mod only" — species mods don't apply for hybrids per project decision. Picks stack additively when both parents land on the same ability (canidae +1 CON × ursidae +2 CON → +3 CON). Empty pick = no bonus (defensive fallback for headless builds). HybridCharacterTests' BlendsAbilityMods rewritten for the new rule, plus two new tests for stacking and empty-pick fallback. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -127,15 +127,27 @@ public partial class StepReview : VBoxContainer, IStep
|
||||
{
|
||||
if (WizardValidation.FirstIncomplete(_draft) != -1) return;
|
||||
|
||||
// Persist the draft so a future load path can pick it up.
|
||||
const string SavePath = "user://character.tres";
|
||||
var err = ResourceSaver.Save(_draft, SavePath);
|
||||
if (err != Error.Ok)
|
||||
GD.PushWarning($"[review] ResourceSaver.Save failed: {err}");
|
||||
// Persist the draft so a future load path can resume editing.
|
||||
const string DraftPath = "user://character.tres";
|
||||
var saveErr = ResourceSaver.Save(_draft, DraftPath);
|
||||
if (saveErr != Error.Ok)
|
||||
GD.PushWarning($"[review] ResourceSaver.Save failed: {saveErr}");
|
||||
else
|
||||
GD.Print($"[review] Saved character draft to {SavePath}");
|
||||
GD.Print($"[review] Saved character draft to {DraftPath}");
|
||||
|
||||
GD.Print($"[review] Confirmed: {Summarise(_draft)}");
|
||||
// The actual handoff: build the runtime Character via Core's
|
||||
// CharacterBuilder. Failure here is a content/wiring bug, not a
|
||||
// user error — the wizard's validation should have caught everything
|
||||
// the builder rejects. Surface the message and stay on this step.
|
||||
if (!CharacterAssembler.TryBuild(_draft, out var built, out string buildError))
|
||||
{
|
||||
GD.PushError($"[review] Character build failed: {buildError}");
|
||||
_confirmStatus.Text = $"Could not finalize character — {buildError}";
|
||||
return;
|
||||
}
|
||||
|
||||
GD.Print($"[review] Confirmed: {Summarise(_draft)} → HP={built!.MaxHp}, "
|
||||
+ $"hybrid={built.Hybrid is not null}, skills={built.SkillProficiencies.Count}");
|
||||
|
||||
EmitSignal(SignalName.CharacterConfirmed, _draft);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user