b451f83174
Captures the pre-Godot-port state of the codebase. This is the rollback anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md). All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
81 lines
3.4 KiB
C#
81 lines
3.4 KiB
C#
using Theriapolis.Core.Rules.Reputation;
|
|
|
|
namespace Theriapolis.Core.Rules.Dialogue;
|
|
|
|
/// <summary>
|
|
/// Phase 6 M3 — disposition-driven shop modifiers per the plan §4.6.
|
|
///
|
|
/// NEMESIS → service refused
|
|
/// HOSTILE → service refused
|
|
/// ANTAGONISTIC..UNFRIENDLY → +25% prices, +10% sell discount
|
|
/// NEUTRAL → base prices
|
|
/// FAVORABLE → -10% buy
|
|
/// FRIENDLY → -20% buy
|
|
/// ALLIED → -30% buy
|
|
/// CHAMPION → -40% buy
|
|
///
|
|
/// Phase 6 M3 ships buy-side adjustment only; sell-side is mirrored at
|
|
/// 50% of the buy modifier so a friendly merchant pays a small premium
|
|
/// without dual-tunable knobs.
|
|
/// </summary>
|
|
public static class ShopPricing
|
|
{
|
|
/// <summary>True if the player can shop at all given the disposition score.</summary>
|
|
public static bool ServiceAvailable(int dispositionScore)
|
|
{
|
|
var label = DispositionLabels.For(dispositionScore);
|
|
return label != DispositionLabel.Nemesis && label != DispositionLabel.Hostile;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Multiplier applied to a price the player pays. 1.0 = base; >1 =
|
|
/// markup; <1 = discount. Caller multiplies the item's listed cost
|
|
/// and rounds.
|
|
/// </summary>
|
|
public static float BuyMultiplier(int dispositionScore)
|
|
{
|
|
var label = DispositionLabels.For(dispositionScore);
|
|
return label switch
|
|
{
|
|
DispositionLabel.Nemesis => 99f, // shouldn't be called; sentinel
|
|
DispositionLabel.Hostile => 99f,
|
|
DispositionLabel.Antagonistic => 1.25f,
|
|
DispositionLabel.Unfriendly => 1.25f,
|
|
DispositionLabel.Neutral => 1.00f,
|
|
DispositionLabel.Favorable => 0.90f,
|
|
DispositionLabel.Friendly => 0.80f,
|
|
DispositionLabel.Allied => 0.70f,
|
|
DispositionLabel.Champion => 0.60f,
|
|
_ => 1.00f,
|
|
};
|
|
}
|
|
|
|
/// <summary>Multiplier applied to a price the merchant pays the player on sell-back.</summary>
|
|
public static float SellMultiplier(int dispositionScore)
|
|
{
|
|
// Mirror buy modifier toward 1.0 by 50%: friendly buy = 0.80 →
|
|
// friendly sell = 0.60 (you still take a haircut), antagonistic
|
|
// buy = 1.25 → antagonistic sell = 0.30.
|
|
var label = DispositionLabels.For(dispositionScore);
|
|
return label switch
|
|
{
|
|
DispositionLabel.Antagonistic => 0.35f,
|
|
DispositionLabel.Unfriendly => 0.40f,
|
|
DispositionLabel.Neutral => 0.50f,
|
|
DispositionLabel.Favorable => 0.55f,
|
|
DispositionLabel.Friendly => 0.60f,
|
|
DispositionLabel.Allied => 0.65f,
|
|
DispositionLabel.Champion => 0.70f,
|
|
_ => 0f, // refused at Hostile / Nemesis
|
|
};
|
|
}
|
|
|
|
/// <summary>Buy price for one unit (rounded up). Cost is in the item's "cost_fang" or equivalent unit.</summary>
|
|
public static int BuyPriceFor(int baseCost, int dispositionScore)
|
|
=> System.Math.Max(1, (int)System.Math.Ceiling(baseCost * BuyMultiplier(dispositionScore)));
|
|
|
|
/// <summary>Sell price for one unit (rounded down).</summary>
|
|
public static int SellPriceFor(int baseCost, int dispositionScore)
|
|
=> System.Math.Max(0, (int)System.Math.Floor(baseCost * SellMultiplier(dispositionScore)));
|
|
}
|