Market listings, offer dialogs, negotiation panels, and history now render prices from the full CurrencyConfig (symbol + singular/plural) instead of a nullable currency label string. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
67 lines
2.8 KiB
TypeScript
67 lines
2.8 KiB
TypeScript
export type NpcDialogueKind =
|
||
"firm" | "lowball" | "backward" | "stall" | "concede" | "good" | "overpay" | "closed" | "accept";
|
||
|
||
type DialogueLine = (amount: string, asking: string) => string;
|
||
|
||
const LINES: Record<NpcDialogueKind, DialogueLine[]> = {
|
||
firm: [
|
||
(amount) => `I'm not going any lower than ${amount}. Take it or leave it.`,
|
||
(amount) => `That's my final price — ${amount}. I've got other buyers knocking.`,
|
||
(amount) => `I'm holding at ${amount}. My prices are already fair.`,
|
||
],
|
||
lowball: [
|
||
(amount) => `That's an insult! I'll come down to ${amount} — not a fraction less.`,
|
||
(amount) => `You're joking, right? ${amount} and not a fraction less.`,
|
||
(amount) => `I paid more than that for lunch! ${amount} — final offer.`,
|
||
],
|
||
backward: [
|
||
(amount) => `Now you're offering less than before? I'm holding firm at ${amount}.`,
|
||
(amount) => `We were making progress and now this? I'm staying at ${amount}.`,
|
||
],
|
||
stall: [
|
||
() => "Really? That's hardly different. Negotiate like you mean it.",
|
||
() => "That tiny increase isn't negotiating. Come back with a real number.",
|
||
() => "You're just inching up now. Bring something meaningful.",
|
||
],
|
||
concede: [
|
||
(amount) => `Hmm, not sure about that... how about ${amount}?`,
|
||
(amount) => `I could stretch to ${amount}... if we're serious about this.`,
|
||
(amount) => `Let's not waste each other's time — ${amount}.`,
|
||
(amount) => `Alright, I'll bend a little. ${amount}?`,
|
||
],
|
||
good: [
|
||
() => "Now we're talking. That's a fair price — you've got a deal.",
|
||
() => "You drive a hard bargain, but that's a price I can live with.",
|
||
() => "That works for me. A man's gotta make a living, after all.",
|
||
],
|
||
overpay: [
|
||
() => "Wait — more than I asked? Sold! You won't regret it.",
|
||
() => "Generous! I'd have taken less, but I won't argue. It's yours.",
|
||
() => "Whoa, okay! Not going to talk you out of that one. Done deal.",
|
||
],
|
||
closed: [
|
||
(_amount, asking) => `Alright, that's enough haggling. Buy it at ${asking} or not at all.`,
|
||
(_amount, asking) => `I'm done negotiating. The price is ${asking} — take it or leave it.`,
|
||
],
|
||
accept: [
|
||
() => "I think that price is acceptable. Let's shake on it.",
|
||
() => "Done. Pleasure doing business with you.",
|
||
() => "Deal! Good doing business with you.",
|
||
],
|
||
};
|
||
|
||
/**
|
||
* Vendor flavour lines for the in-dialog chat. Amounts are formatted plain.
|
||
* Pass a custom `rng` (0–1) for deterministic output; otherwise a line is
|
||
* picked at random each call.
|
||
*/
|
||
export function npcDialogue(
|
||
kind: NpcDialogueKind,
|
||
amount?: number,
|
||
asking?: number,
|
||
rng: () => number = Math.random,
|
||
): string {
|
||
const lines = LINES[kind];
|
||
const index = Math.min(lines.length - 1, Math.max(0, Math.floor(rng() * lines.length)));
|
||
return lines[index]((amount ?? 0).toLocaleString(), (asking ?? 0).toLocaleString());
|
||
}
|