2025-04-14 13:39:30 +02:00
|
|
|
import { GameState, Level, PerkId, RawLevel, Upgrade } from "./types";
|
|
|
|
import { allLevels, icons, transformRawLevel, upgrades } from "./loadGameData";
|
2025-04-01 13:35:33 +02:00
|
|
|
import { t } from "./i18n/i18n";
|
|
|
|
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
|
2025-04-06 15:38:30 +02:00
|
|
|
import {
|
|
|
|
confirmRestart,
|
|
|
|
creativeModeThreshold,
|
|
|
|
gameState,
|
|
|
|
restart,
|
|
|
|
} from "./game";
|
|
|
|
import { asyncAlert, requiredAsyncAlert } from "./asyncAlert";
|
2025-04-06 18:21:53 +02:00
|
|
|
import {
|
|
|
|
describeLevel,
|
|
|
|
highScoreText,
|
|
|
|
reasonLevelIsLocked,
|
|
|
|
sumOfValues,
|
|
|
|
} from "./game_utils";
|
|
|
|
import { getHistory } from "./gameOver";
|
2025-04-11 20:34:51 +02:00
|
|
|
import { noCreative } from "./upgrades";
|
2025-04-14 13:39:30 +02:00
|
|
|
import { levelIconHTML } from "./levelIcon";
|
2025-04-01 13:35:33 +02:00
|
|
|
|
|
|
|
export function creativeMode(gameState: GameState) {
|
|
|
|
return {
|
2025-04-06 10:20:09 +02:00
|
|
|
icon: icons["icon:creative"],
|
2025-04-01 13:35:33 +02:00
|
|
|
text: t("lab.menu_entry"),
|
2025-04-01 13:39:09 +02:00
|
|
|
help:
|
|
|
|
(getTotalScore() < creativeModeThreshold &&
|
|
|
|
t("lab.unlocks_at", { score: creativeModeThreshold })) ||
|
|
|
|
t("lab.help"),
|
2025-04-01 13:35:33 +02:00
|
|
|
disabled: getTotalScore() < creativeModeThreshold,
|
|
|
|
async value() {
|
2025-04-06 15:38:30 +02:00
|
|
|
openCreativeModePerksPicker();
|
2025-04-01 13:35:33 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2025-04-06 10:13:10 +02:00
|
|
|
export async function openCreativeModePerksPicker() {
|
2025-04-01 13:35:33 +02:00
|
|
|
let creativeModePerks: Partial<{ [id in PerkId]: number }> = getSettingValue(
|
2025-04-06 15:38:30 +02:00
|
|
|
"creativeModePerks",
|
2025-04-01 13:35:33 +02:00
|
|
|
{},
|
2025-04-20 18:40:41 +02:00
|
|
|
);
|
2025-04-14 13:39:30 +02:00
|
|
|
const customLevels = (getSettingValue("custom_levels", []) as RawLevel[]).map(
|
|
|
|
transformRawLevel,
|
|
|
|
);
|
2025-04-18 21:17:32 +02:00
|
|
|
|
2025-04-20 18:40:41 +02:00
|
|
|
while (true ) {
|
|
|
|
|
|
|
|
const levelOptions= [
|
|
|
|
...allLevels.map((l, li) => {
|
|
|
|
const problem =
|
|
|
|
reasonLevelIsLocked(li, getHistory(), true)?.text || "";
|
|
|
|
return {
|
|
|
|
icon: icons[l.name],
|
|
|
|
text: l.name,
|
|
|
|
value: l,
|
|
|
|
disabled: !!problem,
|
|
|
|
tooltip: problem || describeLevel(l),
|
|
|
|
className:''
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
...customLevels.map((l) => ({
|
|
|
|
icon: levelIconHTML(l.bricks, l.size, l.color),
|
|
|
|
text: l.name,
|
|
|
|
value: l,
|
|
|
|
disabled: !l.bricks.filter((b) => b !== "_").length,
|
|
|
|
tooltip: describeLevel(l),
|
|
|
|
className:''
|
|
|
|
}))
|
|
|
|
]
|
|
|
|
|
|
|
|
const selectedLeveOption= levelOptions.find(l=>l.text===getSettingValue("creativeModeLevel", '')) || levelOptions[0]
|
|
|
|
selectedLeveOption.className= 'highlight'
|
|
|
|
|
|
|
|
|
|
|
|
const choice=await asyncAlert<Upgrade | Level | "reset" | "play">({
|
2025-04-06 10:13:10 +02:00
|
|
|
title: t("lab.menu_entry"),
|
2025-04-04 12:07:51 +02:00
|
|
|
className: "actionsAsGrid",
|
2025-04-01 13:35:33 +02:00
|
|
|
content: [
|
2025-04-01 21:37:07 +02:00
|
|
|
{
|
2025-04-20 18:40:41 +02:00
|
|
|
icon: icons['icon:reset'],
|
2025-04-01 21:43:36 +02:00
|
|
|
value: "reset",
|
|
|
|
text: t("lab.reset"),
|
|
|
|
disabled: !sumOfValues(creativeModePerks),
|
2025-04-01 21:37:07 +02:00
|
|
|
},
|
2025-04-20 18:40:41 +02:00
|
|
|
{
|
|
|
|
icon: icons['icon:new_run'],
|
|
|
|
value: "play",
|
|
|
|
text: t("lab.play"),
|
|
|
|
disabled: !sumOfValues(creativeModePerks),
|
|
|
|
},
|
|
|
|
|
|
|
|
t("lab.instructions"),
|
2025-04-01 13:35:33 +02:00
|
|
|
...upgrades
|
|
|
|
.filter((u) => !noCreative.includes(u.id))
|
|
|
|
.map((u) => ({
|
|
|
|
icon: u.icon,
|
|
|
|
text: u.name,
|
2025-04-06 18:21:53 +02:00
|
|
|
help:
|
|
|
|
(creativeModePerks[u.id] || 0) +
|
|
|
|
"/" +
|
|
|
|
(u.max + (creativeModePerks.limitless || 0)),
|
2025-04-01 13:35:33 +02:00
|
|
|
value: u,
|
|
|
|
className: creativeModePerks[u.id]
|
2025-04-18 21:17:32 +02:00
|
|
|
? "sandbox highlight"
|
2025-04-20 18:40:41 +02:00
|
|
|
: "sandbox not-highlighed",
|
2025-04-01 13:39:09 +02:00
|
|
|
tooltip: u.help(creativeModePerks[u.id] || 1),
|
2025-04-01 13:35:33 +02:00
|
|
|
})),
|
|
|
|
t("lab.select_level"),
|
2025-04-20 18:40:41 +02:00
|
|
|
...levelOptions
|
2025-04-01 13:35:33 +02:00
|
|
|
],
|
2025-04-20 18:40:41 +02:00
|
|
|
})
|
|
|
|
if(!choice)return
|
2025-04-01 21:43:36 +02:00
|
|
|
if (choice === "reset") {
|
|
|
|
upgrades.forEach((u) => {
|
|
|
|
creativeModePerks[u.id] = 0;
|
2025-04-01 21:37:07 +02:00
|
|
|
});
|
2025-04-06 15:38:30 +02:00
|
|
|
setSettingValue("creativeModePerks", creativeModePerks);
|
2025-04-20 18:40:41 +02:00
|
|
|
setSettingValue("creativeModeLevel", '')
|
2025-04-20 21:14:35 +02:00
|
|
|
} else if (choice === "play" || ("bricks" in choice && choice.name==getSettingValue("creativeModeLevel", ''))) {
|
2025-04-06 15:38:30 +02:00
|
|
|
if (await confirmRestart(gameState)) {
|
2025-04-18 17:15:47 +02:00
|
|
|
restart({
|
|
|
|
perks: creativeModePerks,
|
2025-04-20 18:40:41 +02:00
|
|
|
level: selectedLeveOption.value,
|
2025-04-18 17:15:47 +02:00
|
|
|
isCreativeRun: true,
|
|
|
|
});
|
2025-04-20 18:40:41 +02:00
|
|
|
return
|
2025-04-06 10:13:10 +02:00
|
|
|
}
|
2025-04-20 18:40:41 +02:00
|
|
|
} else if ("bricks" in choice) {
|
|
|
|
setSettingValue("creativeModeLevel", choice.name);
|
2025-04-01 13:35:33 +02:00
|
|
|
} else if (choice) {
|
|
|
|
creativeModePerks[choice.id] =
|
2025-04-06 18:21:53 +02:00
|
|
|
((creativeModePerks[choice.id] || 0) + 1) %
|
|
|
|
(choice.max + 1 + (creativeModePerks.limitless || 0));
|
2025-04-20 21:14:35 +02:00
|
|
|
|
|
|
|
setSettingValue("creativeModePerks", creativeModePerks);
|
2025-04-01 13:35:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|