deadhead-space-freight-tycoon/packages/content/test/upgrades.test.ts

95 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it } from "vitest";
import {
content,
gameConfig,
shipModels,
upgradeMaxLevel,
upgradePercentPerLevel,
upgradeSchema,
upgrades,
type ShipStat,
} from "../src/index";
const model = (id: string) => shipModels.find((m) => m.id === id)!;
const upgradeFor = (stat: ShipStat) => upgrades.find((u) => u.stat === stat)!;
/** A stat at the upgrade's max level for this model (decision 0.5.63: linear % of base). */
const maxed = (modelId: string, stat: Exclude<ShipStat, "handlingGameHoursPerTonne">) => {
const upgrade = upgradeFor(stat);
const level = upgradeMaxLevel(upgrade, modelId);
return model(modelId)[stat] * (1 + (upgradePercentPerLevel(upgrade, modelId) * level) / 100);
};
describe("ship upgrades content (CR-009)", () => {
it("has the five upgrades, all valid, three levels each", () => {
for (const upgrade of upgrades) expect(upgradeSchema.safeParse(upgrade).success).toBe(true);
expect(upgrades.map((u) => u.id)).toEqual([
"engines",
"cargo-bay",
"fuel-tanks",
"landing-thrusters",
"handling-gear",
]);
for (const upgrade of upgrades) expect(upgrade.maxLevel).toBe(3);
expect(content.upgrade("engines")?.name).toBe("Engine Tuning");
});
it("only changes stats ships have", () => {
for (const upgrade of upgrades) {
if (upgrade.stat === "handlingGameHoursPerTonne") {
// A game-wide base until upgraded.
expect(gameConfig.handlingGameHoursPerTonne).toBeGreaterThan(0);
} else {
for (const m of shipModels)
expect(m, `${m.id}.${upgrade.stat}`).toHaveProperty(upgrade.stat);
}
for (const id of Object.keys(upgrade.modelOverrides ?? {})) {
expect(content.shipModel(id), id).toBeDefined();
}
}
});
it("prices levels at 10/20/35 % of the ship and 10/20/30 minutes in the shop", () => {
expect(gameConfig.upgradeLevels).toEqual([
{ costPercentOfPrice: 10, shopRealMinutes: 10 },
{ costPercentOfPrice: 20, shopRealMinutes: 20 },
{ costPercentOfPrice: 35, shopRealMinutes: 30 },
]);
for (const upgrade of upgrades) {
for (const m of shipModels) {
expect(upgradeMaxLevel(upgrade, m.id)).toBeLessThanOrEqual(gameConfig.upgradeLevels.length);
}
}
});
it("keeps models distinct (decision 0.5.67)", () => {
expect(maxed("courier", "cargoCapacityTonnes")).toBeLessThan(
model("freighter").cargoCapacityTonnes,
);
expect(maxed("bulk-hauler", "cruiseSpeedKmPerGameHour")).toBeLessThan(
model("freighter").cruiseSpeedKmPerGameHour,
);
// Reach grows with tank ÷ burn (decision 0.5.95), so compare that.
expect(
maxed("freighter", "tankCapacityFu") / model("freighter").burnFuPerMillionKm,
).toBeLessThan(model("bulk-hauler").tankCapacityFu / model("bulk-hauler").burnFuPerMillionKm);
// Amended by CR-012 (the Lighter's Mars cap is a sim test: upgrade-caps.test.ts).
expect(maxed("clipper", "cargoCapacityTonnes")).toBeLessThan(
model("freighter").cargoCapacityTonnes,
);
expect(maxed("courier", "cruiseSpeedKmPerGameHour")).toBeLessThan(
model("clipper").cruiseSpeedKmPerGameHour,
);
});
});
describe("shop time counts as rest (Step 23.1, decision 0.5.135)", () => {
it("keeps every upgrade level in the shop for at least a full rest, in game hours", () => {
const { upgradeLevels, timeCompression, restGameHours } = gameConfig;
for (const level of upgradeLevels) {
// Real minutes × compression = game minutes.
const shopGameHours = (level.shopRealMinutes * timeCompression) / 60;
expect(shopGameHours).toBeGreaterThanOrEqual(restGameHours);
}
});
});