Skip to content
This repository was archived by the owner on Sep 20, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ internal static class EncounterEventDetailResolver
EncounterPreviewSnapshot snapshot,
EHero? currentHero,
EncounterInventory? inventory = null,
int? currentDay = null
int? currentDay = null,
LiveAbilityValueResolver? resolveLiveValue = null
)
{
if (
Expand All @@ -21,22 +22,32 @@ internal static class EncounterEventDetailResolver
)
return null;

var resultText = EventPreviewLocalization.ResolveDescription(eventTemplate) ?? string.Empty;
var resultText =
EventPreviewLocalization.ResolveDescription(eventTemplate, resolveLiveValue)
?? string.Empty;
var rewardFilter = ResolveRewardFilter(eventTemplate, resultText);
var outcomeGroups = ResolveOutcomeGroups(
eventPlan,
snapshot,
currentHero,
inventory,
currentDay
currentDay,
resolveLiveValue
);

// A suppressed random-selection event (shop stock generation) must not fall
// back to rendering its spawn groups as choices either.
var choiceDetails =
outcomeGroups != null || eventPlan.IsRandomSelectionEvent
? Array.Empty<EncounterChoiceDetail>()
: ResolveChoiceDetails(eventPlan, snapshot, currentHero, inventory, currentDay);
: ResolveChoiceDetails(
eventPlan,
snapshot,
currentHero,
inventory,
currentDay,
resolveLiveValue
);
return new EncounterOption(
eventTemplate.TemplateId,
EventPreviewLocalization.ResolveTitle(eventTemplate) ?? eventTemplate.InternalName,
Expand All @@ -55,7 +66,8 @@ internal static class EncounterEventDetailResolver
EncounterPreviewSnapshot snapshot,
EHero? currentHero,
EncounterInventory? inventory,
int? currentDay
int? currentDay,
LiveAbilityValueResolver? resolveLiveValue
)
{
if (!eventPlan.IsRandomSelectionEvent || eventPlan.SuppressRandomOutcome)
Expand Down Expand Up @@ -113,7 +125,7 @@ group.DayCondition is { } dayCondition
);
continue;
}
AddChoiceDetail(details, template, isEligible: true);
AddChoiceDetail(details, template, isEligible: true, resolveLiveValue);
}

// Dynamic pools roll a summary line; the reward filter drives the
Expand Down Expand Up @@ -402,13 +414,15 @@ private static IReadOnlyList<EncounterChoiceDetail> ResolveChoiceDetails(
EncounterPreviewSnapshot snapshot,
EHero? currentHero,
EncounterInventory? inventory,
int? currentDay
int? currentDay,
LiveAbilityValueResolver? resolveLiveValue
)
{
var candidates = new List<(EncounterPreviewTemplatePlan Step, bool MeetsPrerequisites)>();
var pools = new List<EncounterChoiceDetail>();
var eventDescription = snapshot.TryGetTemplate(eventPlan.TemplateId, out var eventTemplate)
? EventPreviewLocalization.ResolveDescription(eventTemplate) ?? string.Empty
? EventPreviewLocalization.ResolveDescription(eventTemplate, resolveLiveValue)
?? string.Empty
: string.Empty;
foreach (var group in eventPlan.ChoiceGroups)
{
Expand All @@ -422,7 +436,14 @@ group.DayCondition is { } dayCondition
if (group.IsRandomPool)
{
if (
ResolveChoicePool(group, snapshot, currentHero, inventory, eventDescription) is
ResolveChoicePool(
group,
snapshot,
currentHero,
inventory,
eventDescription,
resolveLiveValue
) is
{ } pool
)
pools.Add(pool);
Expand Down Expand Up @@ -459,10 +480,20 @@ group.DayCondition is { } dayCondition
switch (dispositions[i])
{
case ChoicePresentation.Presented:
AddChoiceDetail(presented, candidates[i].Step, isEligible: true);
AddChoiceDetail(
presented,
candidates[i].Step,
isEligible: true,
resolveLiveValue
);
break;
case ChoicePresentation.Dimmed:
AddChoiceDetail(dimmed, candidates[i].Step, isEligible: false);
AddChoiceDetail(
dimmed,
candidates[i].Step,
isEligible: false,
resolveLiveValue
);
break;
}
}
Expand Down Expand Up @@ -517,7 +548,8 @@ int limit
EncounterPreviewSnapshot snapshot,
EHero? currentHero,
EncounterInventory? inventory,
string eventDescription
string eventDescription,
LiveAbilityValueResolver? resolveLiveValue
)
{
var entries = new List<EncounterChoiceDetail>();
Expand Down Expand Up @@ -551,7 +583,7 @@ string eventDescription
);
continue;
}
AddChoiceDetail(entries, template, isEligible: true);
AddChoiceDetail(entries, template, isEligible: true, resolveLiveValue);
}

DedupeDetails(entries);
Expand Down Expand Up @@ -616,10 +648,12 @@ private static bool MeetsOwnershipPrerequisites(
private static void AddChoiceDetail(
List<EncounterChoiceDetail> result,
EncounterPreviewTemplatePlan template,
bool isEligible
bool isEligible,
LiveAbilityValueResolver? resolveLiveValue
)
{
var resultText = EventPreviewLocalization.ResolveDescription(template) ?? string.Empty;
var resultText =
EventPreviewLocalization.ResolveDescription(template, resolveLiveValue) ?? string.Empty;
result.Add(
new EncounterChoiceDetail(
template.TemplateId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using BazaarGameShared.Domain.Cards;
using BazaarGameShared.Domain.Core.Types;
using BazaarGameShared.Domain.Game;
using BazaarGameShared.Domain.Values;
using BazaarPlusPlus.Game.Tooltips;
using BazaarPlusPlus.GameInterop.Cards;
using BazaarPlusPlus.GameInterop.DayTiers;
using BazaarPlusPlus.GameInterop.StaticCards;
using BazaarPlusPlus.Infrastructure;
Expand All @@ -19,6 +21,14 @@ internal interface IEncounterPreviewGameRuntime
Task<Dictionary<Guid, ITCard>?> LoadCardMapAsync(object source);
Dictionary<int, TLevelUp>? SnapshotLevelUps(object source);
TCardBase? GetCardTemplate(object source, Guid templateId);
bool TryEvaluateAbilityValue(
object source,
Guid templateId,
string effectId,
bool isAura,
out string valueText,
out string? unit
);
EHero? ReadCurrentHero();
EncounterInventory? ReadInventory();
GameDataDayTierResolution ResolveDayTiers(object source);
Expand Down Expand Up @@ -51,6 +61,43 @@ IGameDataDayTierResolver dayTierResolver
public TCardBase? GetCardTemplate(object source, Guid templateId) =>
BppStaticDataAccess.GetCardTemplate(source, templateId);

public bool TryEvaluateAbilityValue(
object source,
Guid templateId,
string effectId,
bool isAura,
out string valueText,
out string? unit
)
{
valueText = string.Empty;
unit = null;
var template = GetCardTemplate(source, templateId);
var run = Data.Run;
if (template == null || run == null)
return false;

try
{
var context = new ValueContext(run);
CardAbilityValue value;
var resolved = isAura
? CardAbilityValueReader.TryEvaluateAura(template, effectId, context, out value)
: CardAbilityValueReader.TryEvaluate(template, effectId, context, out value);
if (!resolved)
return false;
valueText = value.ValueText;
unit = value.Unit;
return true;
Comment on lines +89 to +91
}
catch (Exception)
{
// Unsupported live targets (for example, Self without a targeting card)
// degrade only this placeholder; the rest of the preview remains useful.
return false;
}
}

public EHero? ReadCurrentHero()
{
var runHero = Data.Run?.Player?.Hero;
Expand Down
40 changes: 37 additions & 3 deletions src/BazaarPlusPlus/Game/EventPreview/EncounterPreviewModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,14 +285,31 @@ public EventPreviewResult ResolveEvent(EventPreviewQuery query)
snapshot,
_runtime.ReadCurrentHero(),
_runtime.ReadInventory(),
currentDay
currentDay,
ResolveLiveValue
);
if (option == null || (!option.HasChoiceDetails && !option.HasOutcomeGroups))
return new EventPreviewResult(EventPreviewAvailability.Unsupported, null);

return Presentation(
EncounterPreviewTextFormatter.Build(option, _runtime.ColorKeywords, dayTiers.Table)
);

bool ResolveLiveValue(
Guid templateId,
string effectId,
bool isAura,
out string valueText,
out string? unit
) =>
_runtime.TryEvaluateAbilityValue(
source,
templateId,
effectId,
isAura,
out valueText,
out unit
);
}
catch (Exception)
{
Expand Down Expand Up @@ -334,7 +351,7 @@ public EventPreviewResult ResolveLevelUp(LevelUpPreviewQuery query)
{
try
{
if (!TryGetSnapshot(out _, out var snapshot, out var unavailable))
if (!TryGetSnapshot(out var source, out var snapshot, out var unavailable))
return new EventPreviewResult(unavailable, null);
if (!snapshot.TryGetLevelUp(query.CurrentLevel, out var levelUpPlan))
return new EventPreviewResult(EventPreviewAvailability.Missing, null);
Expand All @@ -344,11 +361,28 @@ public EventPreviewResult ResolveLevelUp(LevelUpPreviewQuery query)
id => snapshot.TryGetTemplate(id, out var template) ? template : null,
_runtime.ReadCurrentHero(),
_runtime.ColorKeywords,
query.CurrentLevel
query.CurrentLevel,
ResolveLiveValue
);
return string.IsNullOrWhiteSpace(content)
? new EventPreviewResult(EventPreviewAvailability.Unsupported, null)
: new EventPreviewResult(EventPreviewAvailability.Available, content);

bool ResolveLiveValue(
Guid templateId,
string effectId,
bool isAura,
out string valueText,
out string? unit
) =>
_runtime.TryEvaluateAbilityValue(
source,
templateId,
effectId,
isAura,
out valueText,
out unit
);
}
catch (Exception)
{
Expand Down
37 changes: 34 additions & 3 deletions src/BazaarPlusPlus/Game/EventPreview/EventPreviewLocalization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,33 @@

namespace BazaarPlusPlus.Game.EventPreview;

internal delegate bool LiveAbilityValueResolver(
Guid templateId,
string effectId,
bool isAura,
out string valueText,
out string? unit
);

internal static class EventPreviewLocalization
{
internal static Func<string, string?>? AttributeUnitLocalizer { get; set; }

internal static string? ResolveTitle(EncounterPreviewTemplatePlan template) =>
template == null ? null : PickText(template.Title);

internal static string? ResolveDescription(EncounterPreviewTemplatePlan template)
internal static string? ResolveDescription(
EncounterPreviewTemplatePlan template,
LiveAbilityValueResolver? resolveLiveValue = null
)
{
if (template == null)
return null;
return FormatAbilityPlaceholders(template, PickText(template.Description));
return FormatAbilityPlaceholders(
template,
PickText(template.Description),
resolveLiveValue
);
}

internal static IReadOnlyDictionary<string, EncounterPreviewAbilityValue> CaptureAbilityValues(
Expand Down Expand Up @@ -90,7 +105,8 @@ void AddAuraValue(string placeholder)

private static string? FormatAbilityPlaceholders(
EncounterPreviewTemplatePlan template,
string? text
string? text,
LiveAbilityValueResolver? resolveLiveValue
) =>
FormatAbilityPlaceholders(
text,
Expand All @@ -103,6 +119,21 @@ void AddAuraValue(string placeholder)
return true;
}

const string AuraPrefix = "aura.";
var isAura = abilityId.StartsWith(AuraPrefix, StringComparison.Ordinal);
var effectId = isAura ? abilityId[AuraPrefix.Length..] : abilityId;
if (
resolveLiveValue != null
&& resolveLiveValue(
template.TemplateId,
effectId,
isAura,
out valueText,
out unit
)
)
return true;

valueText = string.Empty;
unit = null;
return false;
Expand Down
Loading