|
@@ -164,13 +164,32 @@ async function _render(apiKey: string, planet: string, usernames: string[], carg
|
|
|
${burnTable(combinedBurn, combinedStorage)}`;
|
|
${burnTable(combinedBurn, combinedStorage)}`;
|
|
|
|
|
|
|
|
const materials = new Map(rawMaterials.map((m) => [m.ticker, m]));
|
|
const materials = new Map(rawMaterials.map((m) => [m.ticker, m]));
|
|
|
- renderTarget.innerHTML += ship(materials, combinedBurn, combinedStorage, cargo);
|
|
|
|
|
|
|
+ const combinedShipping = ship(materials, combinedBurn, combinedStorage, cargo);
|
|
|
|
|
+ renderTarget.innerHTML += combinedShipping.html;
|
|
|
|
|
|
|
|
- renderTarget.innerHTML += userBurns.map(ub => ub.html).join('');
|
|
|
|
|
|
|
+ for (const ub of userBurns) {
|
|
|
|
|
+ renderTarget.innerHTML += ub.html;
|
|
|
|
|
+
|
|
|
|
|
+ const items = allStorages.get(ub.username);
|
|
|
|
|
+ if (combinedShipping.supply !== null && items !== undefined) {
|
|
|
|
|
+ let pasteLines: string[] = [];
|
|
|
|
|
+ for (const mat of combinedShipping.supply.keys()) {
|
|
|
|
|
+ const burn = ub.burn.get(mat);
|
|
|
|
|
+ if (burn === undefined || burn.Net >= 0)
|
|
|
|
|
+ continue;
|
|
|
|
|
+ const need = -burn.Net * combinedShipping.days - (items.get(mat) ?? 0);
|
|
|
|
|
+ if (need > 0)
|
|
|
|
|
+ // unfortunately, rounding means if 4 people each need 0.25, we'll buy 1 and contract each person 0
|
|
|
|
|
+ pasteLines.push(`${mat}\t${Math.round(need)}\t0.01`);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (pasteLines.length > 0)
|
|
|
|
|
+ renderTarget.innerHTML += `<textarea readonly>${pasteLines.join('\n')}</textarea>`;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async function userBurn(apiKey: string, planet: string, username: string, items?: Map<string, number>,
|
|
|
|
|
- lastUpdated?: number, wfNeeds?: Map<string, number>): Promise<{burn: Map<string, Burn>, html: string}> {
|
|
|
|
|
|
|
+async function userBurn(apiKey: string, planet: string, username: string, items?: Map<string, number>, lastUpdated?: number,
|
|
|
|
|
+ wfNeeds?: Map<string, number>): Promise<{username: string, burn: Map<string, Burn>, html: string}> {
|
|
|
const burns: Record<string, Burn[]> = await cachedFetchJSON(
|
|
const burns: Record<string, Burn[]> = await cachedFetchJSON(
|
|
|
`https://api.punoted.net/v1/production/user/burn?location=${planet}&username=${username}`,
|
|
`https://api.punoted.net/v1/production/user/burn?location=${planet}&username=${username}`,
|
|
|
{headers: {'X-Data-Token': apiKey}});
|
|
{headers: {'X-Data-Token': apiKey}});
|
|
@@ -195,7 +214,7 @@ async function userBurn(apiKey: string, planet: string, username: string, items?
|
|
|
const html = `<h2>${username}</h2>
|
|
const html = `<h2>${username}</h2>
|
|
|
last updated: ${lastUpdated ? new Date(lastUpdated).toLocaleString() : ''}
|
|
last updated: ${lastUpdated ? new Date(lastUpdated).toLocaleString() : ''}
|
|
|
${burnTable(burn, items)}`;
|
|
${burnTable(burn, items)}`;
|
|
|
- return {burn, html};
|
|
|
|
|
|
|
+ return {username, burn, html};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function burnTable(burn: Map<string, Burn>, items?: Map<string, number>): string {
|
|
function burnTable(burn: Map<string, Burn>, items?: Map<string, number>): string {
|
|
@@ -223,7 +242,7 @@ function burnTable(burn: Map<string, Burn>, items?: Map<string, number>): string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function ship(materials: Map<string, Material>, burn: Map<string, Burn>, items: Map<string, number>,
|
|
function ship(materials: Map<string, Material>, burn: Map<string, Burn>, items: Map<string, number>,
|
|
|
- cargo: {weight: number, volume: number}): string {
|
|
|
|
|
|
|
+ cargo: {weight: number, volume: number}): {supply: Map<string, number> | null, days: number, html: string} {
|
|
|
let targetDays = Infinity;
|
|
let targetDays = Infinity;
|
|
|
for (const b of burn.values()) {
|
|
for (const b of burn.values()) {
|
|
|
if (b.Net >= 0) continue;
|
|
if (b.Net >= 0) continue;
|
|
@@ -253,7 +272,7 @@ function ship(materials: Map<string, Material>, burn: Map<string, Burn>, items:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (optimal === null)
|
|
if (optimal === null)
|
|
|
- return 'nothing to supply';
|
|
|
|
|
|
|
+ return {supply: optimal, days: NaN, html: 'nothing to supply'};
|
|
|
const xitAct = {
|
|
const xitAct = {
|
|
|
'actions': [
|
|
'actions': [
|
|
|
{'name': 'BuyItems', 'type': 'CX Buy', 'group': 'A1', 'exchange': 'IC1',
|
|
{'name': 'BuyItems', 'type': 'CX Buy', 'group': 'A1', 'exchange': 'IC1',
|
|
@@ -266,8 +285,9 @@ function ship(materials: Map<string, Material>, burn: Map<string, Burn>, items:
|
|
|
'type': 'Manual', 'name': 'A1', 'materials': Object.fromEntries(optimal.entries())
|
|
'type': 'Manual', 'name': 'A1', 'materials': Object.fromEntries(optimal.entries())
|
|
|
}],
|
|
}],
|
|
|
};
|
|
};
|
|
|
- return `<h2>supply for ${format(optimalDays)} days (${format(totalWeightUsed)}t, ${format(totalVolumeUsed)}m³)</h2>
|
|
|
|
|
- <textarea>${JSON.stringify(xitAct)}</textarea>`;
|
|
|
|
|
|
|
+ const html = `<h2>supply for ${format(optimalDays)} days (${format(totalWeightUsed)}t, ${format(totalVolumeUsed)}m³)</h2>
|
|
|
|
|
+ <textarea readonly>${JSON.stringify(xitAct)}</textarea>`;
|
|
|
|
|
+ return {supply: optimal, days: optimalDays, html};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function supplyForDays(burn: Map<string, Burn>, items: Map<string, number>, targetDays: number): Map<string, number> {
|
|
function supplyForDays(burn: Map<string, Burn>, items: Map<string, number>, targetDays: number): Map<string, number> {
|