Feature: implement town market functionality with treasure selling and stashing

This commit is contained in:
Keith Solomon
2026-03-15 14:31:53 -05:00
parent 71bdc6d031
commit 8597b4fded
7 changed files with 393 additions and 3 deletions

View File

@@ -12,6 +12,7 @@ import {
startCombatInCurrentRoom,
travelCurrentExit,
} from "@/rules/runState";
import { queueTreasureForSale, sellPendingTreasure, sendTreasureToStash } from "@/rules/town";
import type { RunState } from "@/types/state";
function createDemoRun() {
@@ -114,6 +115,8 @@ function App() {
);
const equippedItems = run.adventurerSnapshot.inventory.equipped;
const latestLoot = run.lootedItems.slice(-4).reverse();
const pendingSales = run.townState.pendingSales;
const stash = run.townState.stash;
const handleReset = () => {
setRun(createDemoRun());
@@ -156,6 +159,18 @@ function App() {
);
};
const handleQueueSale = (definitionId: string) => {
setRun((previous) => queueTreasureForSale(sampleContentPack, previous, definitionId).run);
};
const handleStashTreasure = (definitionId: string) => {
setRun((previous) => sendTreasureToStash(sampleContentPack, previous, definitionId).run);
};
const handleSellPending = () => {
setRun((previous) => sellPendingTreasure(sampleContentPack, previous).run);
};
return (
<main className="app-shell">
<section className="hero">
@@ -335,6 +350,91 @@ function App() {
</div>
</article>
<article className="panel panel-town">
<div className="panel-header">
<h2>Town Market</h2>
<span>{run.townState.visits} town actions</span>
</div>
<div className="town-summary">
<div className="inventory-badge">
<span>Known Services</span>
<strong>{run.townState.knownServices.length}</strong>
</div>
<div className="inventory-badge">
<span>Queued Sales</span>
<strong>{pendingSales.length}</strong>
</div>
<div className="inventory-badge">
<span>Stash</span>
<strong>{stash.length}</strong>
</div>
</div>
<div className="town-actions">
<button
className="button button-primary"
onClick={handleSellPending}
disabled={pendingSales.length === 0}
>
Sell Queued Treasure
</button>
</div>
<section className="town-section">
<span className="inventory-label">Treasure In Pack</span>
<div className="inventory-list">
{carriedTreasure.length === 0 ? (
<p className="supporting-text">No treasure available for town actions.</p>
) : (
carriedTreasure.map((entry) => (
<article key={`town-pack-${entry.definitionId}`} className="town-card">
<div>
<strong>{formatInventoryEntry(entry.definitionId, entry.quantity)}</strong>
<span>Choose whether to sell or stash this treasure.</span>
</div>
<div className="enemy-actions">
<button className="button" onClick={() => handleStashTreasure(entry.definitionId)}>
Send To Stash
</button>
<button className="button button-primary" onClick={() => handleQueueSale(entry.definitionId)}>
Queue For Sale
</button>
</div>
</article>
))
)}
</div>
</section>
<section className="town-section">
<span className="inventory-label">Pending Sales</span>
<div className="inventory-list">
{pendingSales.length === 0 ? (
<p className="supporting-text">Nothing queued at the market.</p>
) : (
pendingSales.map((entry) => (
<article key={`pending-${entry.definitionId}`} className="inventory-card inventory-card-treasure">
<strong>{formatInventoryEntry(entry.definitionId, entry.quantity)}</strong>
<span>Ready to convert into gold</span>
</article>
))
)}
</div>
</section>
<section className="town-section">
<span className="inventory-label">Town Stash</span>
<div className="inventory-list">
{stash.length === 0 ? (
<p className="supporting-text">The stash is empty.</p>
) : (
stash.map((entry) => (
<article key={`stash-${entry.definitionId}`} className="inventory-card">
<strong>{formatInventoryEntry(entry.definitionId, entry.quantity)}</strong>
<span>Held safely in town storage</span>
</article>
))
)}
</div>
</section>
</article>
<article className="panel">
<div className="panel-header">
<h2>Current Room</h2>