Show ingredient tier/enchant + price age in haul advisor
All checks were successful
Deploy / deploy (push) Successful in 1m42s
All checks were successful
Deploy / deploy (push) Successful in 1m42s
- Opportunities breakdown now shows each ingredient's tier/enchant (e.g. T6.4) so enchant mismatches are visible at a glance. - Travel haul advisor now shows color-coded price-age badges (green fresh / yellow aging / red stale) for both buy and sell prices, like Opportunities already does. Note: the enchanted-ingredient pricing itself was already correct in code (T6_SHOES_PLATE_SET1@4); a deployed DB parsed before that fix must re-run Game Data -> Re-download to pick it up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
38c7ff5e56
commit
7ed585d6a9
3 changed files with 31 additions and 2 deletions
|
|
@ -141,6 +141,8 @@ export interface HaulItem {
|
||||||
weight: number;
|
weight: number;
|
||||||
buyAt: number; // sell-order price in origin (you buy instantly)
|
buyAt: number; // sell-order price in origin (you buy instantly)
|
||||||
sellAt: number; // sell-order price in destination
|
sellAt: number; // sell-order price in destination
|
||||||
|
buyAgeHours: number | null; // how stale the origin price is
|
||||||
|
sellAgeHours: number | null; // how stale the destination price is
|
||||||
netPerUnit: number; // after taxes at destination
|
netPerUnit: number; // after taxes at destination
|
||||||
unitsCarryable: number;
|
unitsCarryable: number;
|
||||||
tripProfit: number;
|
tripProfit: number;
|
||||||
|
|
@ -177,6 +179,8 @@ export async function haulAdvisor(from: string, to: string, maxWeight: number, m
|
||||||
weight: it.weight,
|
weight: it.weight,
|
||||||
buyAt: buy.price,
|
buyAt: buy.price,
|
||||||
sellAt: sell.price,
|
sellAt: sell.price,
|
||||||
|
buyAgeHours: buy.ageHours,
|
||||||
|
sellAgeHours: sell.ageHours,
|
||||||
netPerUnit,
|
netPerUnit,
|
||||||
unitsCarryable,
|
unitsCarryable,
|
||||||
tripProfit: netPerUnit * unitsCarryable,
|
tripProfit: netPerUnit * unitsCarryable,
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,14 @@ const CITIES = ['Bridgewatch', 'Caerleon', 'Fort Sterling', 'Lymhurst', 'Martloc
|
||||||
|
|
||||||
const silver = (n: number) => Math.round(n).toLocaleString('en-US');
|
const silver = (n: number) => Math.round(n).toLocaleString('en-US');
|
||||||
|
|
||||||
|
/** Derive a "T6.4" chip from an item id like "T6_SHOES_PLATE_SET1@4" (blank for non-tiered ids). */
|
||||||
|
function tierEnchant(id: string): string {
|
||||||
|
const t = id.match(/^T(\d)_/);
|
||||||
|
if (!t) return '';
|
||||||
|
const e = id.match(/@(\d)$/);
|
||||||
|
return `T${t[1]}${e ? `.${e[1]}` : ''}`;
|
||||||
|
}
|
||||||
|
|
||||||
function Age({ hours }: { hours: number | null }) {
|
function Age({ hours }: { hours: number | null }) {
|
||||||
if (hours == null) return null;
|
if (hours == null) return null;
|
||||||
const label = hours < 1 ? `${Math.round(hours * 60)}m` : hours < 48 ? `${Math.round(hours)}h` : `${Math.round(hours / 24)}d`;
|
const label = hours < 1 ? `${Math.round(hours * 60)}m` : hours < 48 ? `${Math.round(hours)}h` : `${Math.round(hours / 24)}d`;
|
||||||
|
|
@ -218,7 +226,12 @@ export default function OpportunitiesPage() {
|
||||||
<tbody>
|
<tbody>
|
||||||
{o.inputs.map((inp) => (
|
{o.inputs.map((inp) => (
|
||||||
<tr key={inp.item}>
|
<tr key={inp.item}>
|
||||||
<td>{o.inputNames[inp.item] ?? inp.item}</td>
|
<td>
|
||||||
|
{o.inputNames[inp.item] ?? inp.item}
|
||||||
|
{tierEnchant(inp.item) && (
|
||||||
|
<span className="tag" title={inp.item}>{tierEnchant(inp.item)}</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
<td>{inp.count}</td>
|
<td>{inp.count}</td>
|
||||||
<td>{inp.effectiveCount.toFixed(2)}</td>
|
<td>{inp.effectiveCount.toFixed(2)}</td>
|
||||||
<td>{silver(inp.unitCost)}</td>
|
<td>{silver(inp.unitCost)}</td>
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,22 @@ interface HaulItem {
|
||||||
weight: number;
|
weight: number;
|
||||||
buyAt: number;
|
buyAt: number;
|
||||||
sellAt: number;
|
sellAt: number;
|
||||||
|
buyAgeHours: number | null;
|
||||||
|
sellAgeHours: number | null;
|
||||||
netPerUnit: number;
|
netPerUnit: number;
|
||||||
unitsCarryable: number;
|
unitsCarryable: number;
|
||||||
tripProfit: number;
|
tripProfit: number;
|
||||||
profitPerWeight: number;
|
profitPerWeight: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Color-coded price-age badge: green fresh, yellow aging, red stale. */
|
||||||
|
function PriceAge({ hours }: { hours: number | null }) {
|
||||||
|
if (hours == null) return <span className="muted">—</span>;
|
||||||
|
const label = hours < 1 ? `${Math.round(hours * 60)}m` : hours < 48 ? `${Math.round(hours)}h` : `${Math.round(hours / 24)}d`;
|
||||||
|
const color = hours < 6 ? '#7fd18a' : hours < 24 ? '#e0c069' : '#e08869';
|
||||||
|
return <span style={{ color, fontSize: 12 }}>{label}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
interface Status {
|
interface Status {
|
||||||
zones: number;
|
zones: number;
|
||||||
connections: number;
|
connections: number;
|
||||||
|
|
@ -218,7 +228,7 @@ export default function TravelPage() {
|
||||||
<table style={{ marginTop: 10 }}>
|
<table style={{ marginTop: 10 }}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Item</th><th>Buy at {from}</th><th>Sell at {to}</th><th>Net/unit</th><th>Units</th><th>Trip profit</th><th>Profit/kg</th>
|
<th>Item</th><th>Buy at {from}</th><th>age</th><th>Sell at {to}</th><th>age</th><th>Net/unit</th><th>Units</th><th>Trip profit</th><th>Profit/kg</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -226,7 +236,9 @@ export default function TravelPage() {
|
||||||
<tr key={h.item}>
|
<tr key={h.item}>
|
||||||
<td>{h.name ?? h.item} <span className="tag">T{h.tier}</span></td>
|
<td>{h.name ?? h.item} <span className="tag">T{h.tier}</span></td>
|
||||||
<td>{silver(h.buyAt)}</td>
|
<td>{silver(h.buyAt)}</td>
|
||||||
|
<td><PriceAge hours={h.buyAgeHours} /></td>
|
||||||
<td>{silver(h.sellAt)}</td>
|
<td>{silver(h.sellAt)}</td>
|
||||||
|
<td><PriceAge hours={h.sellAgeHours} /></td>
|
||||||
<td>{silver(h.netPerUnit)}</td>
|
<td>{silver(h.netPerUnit)}</td>
|
||||||
<td>{h.unitsCarryable}</td>
|
<td>{h.unitsCarryable}</td>
|
||||||
<td style={{ color: '#7fd18a', fontWeight: 600 }}>{silver(h.tripProfit)}</td>
|
<td style={{ color: '#7fd18a', fontWeight: 600 }}>{silver(h.tripProfit)}</td>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue