|
|
@@ -44,6 +44,7 @@ async function render(planetName: string, pop: Pop) {
|
|
|
try {
|
|
|
await _render(planetName, pop);
|
|
|
} catch (e) {
|
|
|
+ console.error(e);
|
|
|
renderTarget.textContent = (e as Error).message;
|
|
|
}
|
|
|
loader.style.display = 'none';
|
|
|
@@ -70,12 +71,25 @@ async function _render(planetName: string, pop: Pop) {
|
|
|
const lastPOPRts = Math.floor(new Date(lastPOPR.ReportTimestamp).getTime() / 1000);
|
|
|
const nextPOPRts = lastPOPRts + 7 * 24 * 60 * 60;
|
|
|
|
|
|
- let currentPop = 0;
|
|
|
- if (pop == 'pio') currentPop = lastPOPR.NextPopulationPioneer;
|
|
|
- else if (pop == 'set') currentPop = lastPOPR.NextPopulationSettler;
|
|
|
- else if (pop == 'tec') currentPop = lastPOPR.NextPopulationTechnician;
|
|
|
- else if (pop == 'eng') currentPop = lastPOPR.NextPopulationEngineer;
|
|
|
- else if (pop == 'sci') currentPop = lastPOPR.NextPopulationScientist;
|
|
|
+ let currentPop = 0, lowerPop = 0;
|
|
|
+ if (pop == 'pio')
|
|
|
+ currentPop = lastPOPR.NextPopulationPioneer;
|
|
|
+ else if (pop == 'set') {
|
|
|
+ currentPop = lastPOPR.NextPopulationSettler;
|
|
|
+ lowerPop = lastPOPR.NextPopulationPioneer;
|
|
|
+ }
|
|
|
+ else if (pop == 'tec') {
|
|
|
+ currentPop = lastPOPR.NextPopulationTechnician;
|
|
|
+ lowerPop = lastPOPR.NextPopulationSettler;
|
|
|
+ }
|
|
|
+ else if (pop == 'eng') {
|
|
|
+ currentPop = lastPOPR.NextPopulationEngineer;
|
|
|
+ lowerPop = lastPOPR.NextPopulationTechnician;
|
|
|
+ }
|
|
|
+ else if (pop == 'sci') {
|
|
|
+ currentPop = lastPOPR.NextPopulationScientist;
|
|
|
+ lowerPop = lastPOPR.NextPopulationEngineer;
|
|
|
+ }
|
|
|
|
|
|
const siteCount = siteCounts[0].Count;
|
|
|
|
|
|
@@ -186,14 +200,14 @@ async function _render(planetName: string, pop: Pop) {
|
|
|
<th>cost/day</th>
|
|
|
<th>unit cost</th>
|
|
|
</tr>
|
|
|
- ${paretoFront(pop, totalNeeds, siteCount, currentPOPIFilled, currentPop, prices).map((result) => {
|
|
|
+ ${paretoFront(pop, totalNeeds, siteCount, currentPOPIFilled, currentPop, lowerPop, prices).map((result) => {
|
|
|
let unitCost = '';
|
|
|
if (result.cost > 0)
|
|
|
- unitCost = formatNum(result.cost / result.migration);
|
|
|
+ unitCost = formatNum(result.cost / result.change);
|
|
|
return `<tr>
|
|
|
- <td>${[...result.config.entries()].map(([building, fill]) => `${building}: ${fill.numMats}`).join(', ')}</td>
|
|
|
+ <td>${[...result.config.entries()].map(([building, fill]) => `${building}: ${fill.numMats}`).join('<br>')}</td>
|
|
|
<td>${formatPct(result.happiness)}</td>
|
|
|
- <td>${formatDelta(result.migration)}</td>
|
|
|
+ <td>${formatDelta(result.change)}</td>
|
|
|
<td>${formatNum(result.cost)}</td>
|
|
|
<td>${unitCost}</td>
|
|
|
</tr>`;
|
|
|
@@ -252,28 +266,39 @@ function calcPOPIFilled(infra: Infrastructure): number | null {
|
|
|
}
|
|
|
|
|
|
function paretoFront(pop: Pop, totalNeeds: Record<Need, number>, siteCount: number, currentPOPIFilled: POPIFill,
|
|
|
- currentPop: number, prices: Map<string, number>): {config: POPIFill, happiness: number, migration: number, cost: number}[] {
|
|
|
- const results: {config: POPIFill, happiness: number, migration: number, cost: number}[] = [];
|
|
|
+ currentPop: number, lowerPop: number, prices: Map<string, number>): {config: POPIFill, happiness: number, change: number, cost: number}[] {
|
|
|
+ const results: {config: POPIFill, happiness: number, change: number, cost: number}[] = [];
|
|
|
+ let education = 0;
|
|
|
+ if (pop !== 'pio') {
|
|
|
+ education = EDUCATION[pop];
|
|
|
+ education += (currentPOPIFilled.get('PLANETARY_BROADCASTING_HUB')?.tier ?? 0) * 0.001;
|
|
|
+ education += (currentPOPIFilled.get('LIBRARY')?.tier ?? 0) * 0.002;
|
|
|
+ education += (currentPOPIFilled.get('UNIVERSITY')?.tier ?? 0) * 0.004;
|
|
|
+ }
|
|
|
+
|
|
|
for (const config of popiFillCombinations(currentPOPIFilled)) {
|
|
|
const happiness = projectedHappiness(pop, totalNeeds, siteCount, config);
|
|
|
- let migration = 0;
|
|
|
- if (happiness > 0.7)
|
|
|
- migration = currentPop * (happiness - 0.7);
|
|
|
+ let change = 0;
|
|
|
+ if (happiness > 0.7 && ['pio', 'set', 'tec'].includes(pop))
|
|
|
+ change = currentPop * (happiness - 0.7);
|
|
|
else if (happiness < 0.5)
|
|
|
- migration = 0.8 * currentPop * (happiness - 0.5);
|
|
|
- // TODO: education
|
|
|
+ change = 0.8 * currentPop * (happiness - 0.5);
|
|
|
+
|
|
|
+ if (pop !== 'pio')
|
|
|
+ change += lowerPop * education * happiness;
|
|
|
+ // TODO: education out
|
|
|
|
|
|
let cost = calcCost(config, prices);
|
|
|
// is any result better than this one?
|
|
|
- if (results.some((result) => (result.migration >= migration && result.cost < cost) ||
|
|
|
- (result.migration > migration && result.cost <= cost)))
|
|
|
+ if (results.some((result) => (result.change >= change && result.cost < cost) ||
|
|
|
+ (result.change > change && result.cost <= cost)))
|
|
|
continue;
|
|
|
// are any results worse than this one?
|
|
|
for (let i = results.length - 1; i >= 0; i--)
|
|
|
- if ((results[i].migration <= migration && results[i].cost > cost) ||
|
|
|
- (results[i].migration < migration && results[i].cost >= cost))
|
|
|
+ if ((results[i].change <= change && results[i].cost > cost) ||
|
|
|
+ (results[i].change < change && results[i].cost >= cost))
|
|
|
results.splice(i, 1);
|
|
|
- results.push({config, happiness, migration, cost});
|
|
|
+ results.push({config, happiness, change, cost});
|
|
|
}
|
|
|
return results;
|
|
|
}
|
|
|
@@ -413,6 +438,12 @@ const POPI: Record<POPIBuilding, {needs: {need: Need, supplied: number}[], mats:
|
|
|
mats: {'COF': 10, 'REA': 10, 'TUB': 10, 'BID': 0.33, 'HD': 0.67, 'IDC': 0.2},
|
|
|
},
|
|
|
};
|
|
|
+const EDUCATION: Record<Exclude<Pop, 'pio'>, number> = {
|
|
|
+ 'set': 0.025,
|
|
|
+ 'tec': 0.02,
|
|
|
+ 'eng': 0.0125,
|
|
|
+ 'sci': 0.0075,
|
|
|
+}
|
|
|
|
|
|
type Pop = 'pio' | 'set' | 'tec' | 'eng' | 'sci';
|
|
|
type Need = 'safety' | 'health' | 'comfort' | 'culture' | 'education';
|