mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-26 15:06:16 -04:00
wip
This commit is contained in:
parent
b8daf018b1
commit
181e156f60
6 changed files with 1061 additions and 1132 deletions
|
@ -17,7 +17,8 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
||||||
|
|
||||||
## Done
|
## Done
|
||||||
|
|
||||||
- categorize and color perks
|
-
|
||||||
|
- color coded perks (green = noob friendly, red = combo with reset condition)
|
||||||
- removed : instant_upgrade
|
- removed : instant_upgrade
|
||||||
- nerfed : helium : now need to be level 3 to have the same effect of keeping coins up
|
- nerfed : helium : now need to be level 3 to have the same effect of keeping coins up
|
||||||
|
|
||||||
|
|
1574
dist/index.html
vendored
1574
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -1,297 +1,270 @@
|
||||||
import { icons, transformRawLevel } from "./loadGameData";
|
import {icons, transformRawLevel} from "./loadGameData";
|
||||||
import { t } from "./i18n/i18n";
|
import {t} from "./i18n/i18n";
|
||||||
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
|
import {getSettingValue, getTotalScore, setSettingValue} from "./settings";
|
||||||
import { asyncAlert } from "./asyncAlert";
|
import {asyncAlert} from "./asyncAlert";
|
||||||
import { Palette, RawLevel } from "./types";
|
import {Palette, RawLevel} from "./types";
|
||||||
import { levelIconHTML } from "./levelIcon";
|
import {levelIconHTML} from "./levelIcon";
|
||||||
|
|
||||||
import _palette from "./data/palette.json";
|
import _palette from "./data/palette.json";
|
||||||
import { restart } from "./game";
|
import {restart} from "./game";
|
||||||
import { describeLevel } from "./game_utils";
|
import {describeLevel} from "./game_utils";
|
||||||
|
import {automaticBackgroundColor, levelCodeToRawLevel, MAX_LEVEL_SIZE, MIN_LEVEL_SIZE} from "./pure_functions";
|
||||||
|
|
||||||
const palette = _palette as Palette;
|
const palette = _palette as Palette;
|
||||||
const MAX_LEVEL_SIZE = 21;
|
|
||||||
const MIN_LEVEL_SIZE = 2;
|
|
||||||
|
|
||||||
export function levelEditorMenuEntry() {
|
export function levelEditorMenuEntry() {
|
||||||
const min = 10000;
|
const min = 10000;
|
||||||
const disabled = getTotalScore() < min;
|
const disabled = getTotalScore() < min;
|
||||||
return {
|
return {
|
||||||
icon: icons["icon:editor"],
|
icon: icons["icon:editor"],
|
||||||
text: t("editor.title"),
|
text: t("editor.title"),
|
||||||
disabled,
|
disabled,
|
||||||
help: disabled ? t("editor.locked", { min }) : t("editor.help"),
|
help: disabled ? t("editor.locked", {min}) : t("editor.help"),
|
||||||
async value() {
|
async value() {
|
||||||
openLevelEditorLevelsList().then();
|
openLevelEditorLevelsList().then();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openLevelEditorLevelsList() {
|
async function openLevelEditorLevelsList() {
|
||||||
const rawList = getSettingValue("custom_levels", []) as RawLevel[];
|
const rawList = getSettingValue("custom_levels", []) as RawLevel[];
|
||||||
const customLevels = rawList.map(transformRawLevel);
|
const customLevels = rawList.map(transformRawLevel);
|
||||||
|
|
||||||
let choice = await asyncAlert({
|
let choice = await asyncAlert({
|
||||||
title: t("editor.title"),
|
title: t("editor.title"),
|
||||||
content: [
|
content: [
|
||||||
...customLevels.map((l, li) => ({
|
...customLevels.map((l, li) => ({
|
||||||
text: l.name,
|
text: l.name,
|
||||||
icon: levelIconHTML(l.bricks, l.size, l.color),
|
icon: levelIconHTML(l.bricks, l.size, l.color),
|
||||||
value() {
|
value() {
|
||||||
editRawLevelList(li);
|
editRawLevelList(li);
|
||||||
},
|
},
|
||||||
help: l.credit || describeLevel(l),
|
help: l.credit || describeLevel(l),
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
text: t("editor.new_level"),
|
text: t("editor.new_level"),
|
||||||
icon: icons["icon:editor"],
|
icon: icons["icon:editor"],
|
||||||
value() {
|
value() {
|
||||||
rawList.push({
|
rawList.push({
|
||||||
color: "",
|
color: "",
|
||||||
size: 6,
|
size: 6,
|
||||||
bricks: "____________________________________",
|
bricks: "____________________________________",
|
||||||
name: "custom level" + (rawList.length + 1),
|
name: "custom level" + (rawList.length + 1),
|
||||||
credit: "",
|
credit: "",
|
||||||
});
|
});
|
||||||
setSettingValue("custom_levels", rawList);
|
setSettingValue("custom_levels", rawList);
|
||||||
editRawLevelList(rawList.length - 1);
|
editRawLevelList(rawList.length - 1);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t("editor.import"),
|
text: t("editor.import"),
|
||||||
help: t("editor.import_instruction"),
|
help: t("editor.import_instruction"),
|
||||||
value() {
|
value() {
|
||||||
const code = prompt(t("editor.import_instruction"))?.trim();
|
const code = prompt(t("editor.import_instruction"))?.trim();
|
||||||
if (code) {
|
if (code) {
|
||||||
let [name, credit] = code.match(/\[([^\]]+)]/gi);
|
const lvl = levelCodeToRawLevel(code)
|
||||||
|
if (lvl) {
|
||||||
let bricks = code
|
rawList.push(lvl);
|
||||||
.split(name)[1]
|
setSettingValue("custom_levels", rawList);
|
||||||
.split(credit)[0]
|
}
|
||||||
.replace(/\s/gi, "");
|
}
|
||||||
name = name.slice(1, -1);
|
openLevelEditorLevelsList();
|
||||||
credit = credit.slice(1, -1);
|
},
|
||||||
name ||= "Imported on " + new Date().toISOString().slice(0, 10);
|
},
|
||||||
credit ||= "";
|
],
|
||||||
const size = Math.sqrt(bricks.length);
|
});
|
||||||
if (
|
if (typeof choice == "function") choice();
|
||||||
Math.floor(size) === size &&
|
|
||||||
size >= MIN_LEVEL_SIZE &&
|
|
||||||
size <= MAX_LEVEL_SIZE
|
|
||||||
) {
|
|
||||||
rawList.push({
|
|
||||||
color: automaticBackgroundColor(bricks.split("")),
|
|
||||||
size,
|
|
||||||
bricks,
|
|
||||||
name,
|
|
||||||
credit,
|
|
||||||
});
|
|
||||||
setSettingValue("custom_levels", rawList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
openLevelEditorLevelsList();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (typeof choice == "function") choice();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function editRawLevelList(nth: number, color = "W") {
|
export async function editRawLevelList(nth: number, color = "W") {
|
||||||
let rawList = getSettingValue("custom_levels", []) as RawLevel[];
|
let rawList = getSettingValue("custom_levels", []) as RawLevel[];
|
||||||
const level = rawList[nth];
|
const level = rawList[nth];
|
||||||
const bricks = level.bricks.split("");
|
const bricks = level.bricks.split("");
|
||||||
let grid = "";
|
let grid = "";
|
||||||
for (let y = 0; y < level.size; y++) {
|
for (let y = 0; y < level.size; y++) {
|
||||||
grid += '<div style="background: ' + (level.color || "black") + ';">';
|
grid += '<div style="background: ' + (level.color || "black") + ';">';
|
||||||
for (let x = 0; x < level.size; x++) {
|
|
||||||
const c = bricks[y * level.size + x];
|
|
||||||
grid += `<span data-resolve-to="paint_brick:${x}:${y}" style="background: ${palette[c]}">${c == "B" ? "💣" : ""}</span>`;
|
|
||||||
}
|
|
||||||
grid += "</div>";
|
|
||||||
}
|
|
||||||
|
|
||||||
const levelColors = new Set(bricks);
|
|
||||||
levelColors.delete("_");
|
|
||||||
levelColors.delete("B");
|
|
||||||
|
|
||||||
let colorList =
|
|
||||||
'<div class="palette">' +
|
|
||||||
Object.entries(palette)
|
|
||||||
.filter(([key, value]) => key !== "_")
|
|
||||||
.filter(
|
|
||||||
([key, value]) =>
|
|
||||||
levelColors.size < 5 || levelColors.has(key) || key === "B",
|
|
||||||
)
|
|
||||||
.map(
|
|
||||||
([key, value]) =>
|
|
||||||
`<span data-resolve-to="set_color:${key}" data-selected="${key == color}" style="background: ${value}">${key == "B" ? "💣" : ""}</span>`,
|
|
||||||
)
|
|
||||||
.join("") +
|
|
||||||
"</div>";
|
|
||||||
|
|
||||||
const clicked = await asyncAlert<string | null>({
|
|
||||||
title: t("editor.editing.title", { name: level.name }),
|
|
||||||
content: [
|
|
||||||
t("editor.editing.color"),
|
|
||||||
colorList,
|
|
||||||
t("editor.editing.help"),
|
|
||||||
`<div class="gridEdit" style="--grid-size:${level.size};">${grid}</div>`,
|
|
||||||
|
|
||||||
{
|
|
||||||
icon: icons["icon:new_run"],
|
|
||||||
text: t("editor.editing.play"),
|
|
||||||
value: "play",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.rename"),
|
|
||||||
value: "rename",
|
|
||||||
help: level.name,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.credit"),
|
|
||||||
value: "credit",
|
|
||||||
help: level.credit,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.delete"),
|
|
||||||
value: "delete",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.copy"),
|
|
||||||
value: "copy",
|
|
||||||
help: t("editor.editing.copy_help"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.bigger"),
|
|
||||||
value: "size:+1",
|
|
||||||
disabled: level.size >= MAX_LEVEL_SIZE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.smaller"),
|
|
||||||
value: "size:-1",
|
|
||||||
disabled: level.size <= MIN_LEVEL_SIZE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.left"),
|
|
||||||
value: "move:-1:0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.right"),
|
|
||||||
value: "move:1:0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.up"),
|
|
||||||
value: "move:0:-1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("editor.editing.down"),
|
|
||||||
value: "move:0:1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (!clicked) return;
|
|
||||||
if (typeof clicked === "string") {
|
|
||||||
const [action, a, b] = clicked.split(":");
|
|
||||||
if (action == "paint_brick") {
|
|
||||||
const x = parseInt(a),
|
|
||||||
y = parseInt(b);
|
|
||||||
bricks[y * level.size + x] =
|
|
||||||
bricks[y * level.size + x] === color ? "_" : color;
|
|
||||||
level.bricks = bricks.join("");
|
|
||||||
}
|
|
||||||
if (action == "set_color") {
|
|
||||||
color = a;
|
|
||||||
}
|
|
||||||
if (action == "size") {
|
|
||||||
const newSize = level.size + parseInt(a);
|
|
||||||
const newBricks = [];
|
|
||||||
for (let y = 0; y < newSize; y++) {
|
|
||||||
for (let x = 0; x < newSize; x++) {
|
|
||||||
newBricks.push(
|
|
||||||
(x < level.size && y < level.size && bricks[y * level.size + x]) ||
|
|
||||||
"_",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
level.size = newSize;
|
|
||||||
level.bricks = newBricks.join("");
|
|
||||||
}
|
|
||||||
if (action == "move") {
|
|
||||||
const dx = parseInt(a),
|
|
||||||
dy = parseInt(b);
|
|
||||||
const newBricks = [];
|
|
||||||
for (let y = 0; y < level.size; y++) {
|
|
||||||
for (let x = 0; x < level.size; x++) {
|
for (let x = 0; x < level.size; x++) {
|
||||||
const tx = x - dx;
|
const c = bricks[y * level.size + x];
|
||||||
const ty = y - dy;
|
grid += `<span data-resolve-to="paint_brick:${x}:${y}" style="background: ${palette[c]}">${c == "B" ? "💣" : ""}</span>`;
|
||||||
if (tx < 0 || tx >= level.size || ty < 0 || ty >= level.size) {
|
|
||||||
newBricks.push("_");
|
|
||||||
} else {
|
|
||||||
newBricks.push(bricks[ty * level.size + tx]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
grid += "</div>";
|
||||||
level.bricks = newBricks.join("");
|
|
||||||
}
|
}
|
||||||
if (action === "play") {
|
|
||||||
restart({
|
|
||||||
level: transformRawLevel(level),
|
|
||||||
isEditorTrialRun: nth,
|
|
||||||
perks: {
|
|
||||||
base_combo: 7,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (action === "copy") {
|
|
||||||
let text =
|
|
||||||
"```\n[" +
|
|
||||||
(level.name || "unnamed level")?.replace(/\[|\]/gi, " ") +
|
|
||||||
"]";
|
|
||||||
bricks.forEach((b, bi) => {
|
|
||||||
if (!(bi % level.size)) text += "\n";
|
|
||||||
text += b;
|
|
||||||
});
|
|
||||||
text +=
|
|
||||||
"\n[" +
|
|
||||||
(level.credit?.replace(/\[|\]/gi, " ") || "Missing credits") +
|
|
||||||
"]\n```";
|
|
||||||
navigator.clipboard.writeText(text);
|
|
||||||
// return
|
|
||||||
}
|
|
||||||
if (action === "rename") {
|
|
||||||
const name = prompt(t("editor.editing.rename_prompt"), level.name);
|
|
||||||
if (name) {
|
|
||||||
level.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (action === "credit") {
|
|
||||||
const credit = prompt(
|
|
||||||
t("editor.editing.credit_prompt"),
|
|
||||||
level.credit || "",
|
|
||||||
);
|
|
||||||
if (credit !== "null") {
|
|
||||||
level.credit = credit || "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (action === "delete") {
|
|
||||||
rawList = rawList.filter((l, li) => li !== nth);
|
|
||||||
setSettingValue("custom_levels", rawList);
|
|
||||||
openLevelEditorLevelsList();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
level.color = automaticBackgroundColor(bricks);
|
const levelColors = new Set(bricks);
|
||||||
|
levelColors.delete("_");
|
||||||
|
levelColors.delete("B");
|
||||||
|
|
||||||
setSettingValue("custom_levels", rawList);
|
let colorList =
|
||||||
editRawLevelList(nth, color);
|
'<div class="palette">' +
|
||||||
|
Object.entries(palette)
|
||||||
|
.filter(([key, value]) => key !== "_")
|
||||||
|
.filter(
|
||||||
|
([key, value]) =>
|
||||||
|
levelColors.size < 5 || levelColors.has(key) || key === "B",
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
([key, value]) =>
|
||||||
|
`<span data-resolve-to="set_color:${key}" data-selected="${key == color}" style="background: ${value}">${key == "B" ? "💣" : ""}</span>`,
|
||||||
|
)
|
||||||
|
.join("") +
|
||||||
|
"</div>";
|
||||||
|
|
||||||
|
const clicked = await asyncAlert<string | null>({
|
||||||
|
title: t("editor.editing.title", {name: level.name}),
|
||||||
|
content: [
|
||||||
|
t("editor.editing.color"),
|
||||||
|
colorList,
|
||||||
|
t("editor.editing.help"),
|
||||||
|
`<div class="gridEdit" style="--grid-size:${level.size};">${grid}</div>`,
|
||||||
|
|
||||||
|
{
|
||||||
|
icon: icons["icon:new_run"],
|
||||||
|
text: t("editor.editing.play"),
|
||||||
|
value: "play",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.rename"),
|
||||||
|
value: "rename",
|
||||||
|
help: level.name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.credit"),
|
||||||
|
value: "credit",
|
||||||
|
help: level.credit,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.delete"),
|
||||||
|
value: "delete",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.copy"),
|
||||||
|
value: "copy",
|
||||||
|
help: t("editor.editing.copy_help"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.bigger"),
|
||||||
|
value: "size:+1",
|
||||||
|
disabled: level.size >= MAX_LEVEL_SIZE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.smaller"),
|
||||||
|
value: "size:-1",
|
||||||
|
disabled: level.size <= MIN_LEVEL_SIZE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.left"),
|
||||||
|
value: "move:-1:0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.right"),
|
||||||
|
value: "move:1:0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.up"),
|
||||||
|
value: "move:0:-1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("editor.editing.down"),
|
||||||
|
value: "move:0:1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!clicked) return;
|
||||||
|
if (typeof clicked === "string") {
|
||||||
|
const [action, a, b] = clicked.split(":");
|
||||||
|
if (action == "paint_brick") {
|
||||||
|
const x = parseInt(a),
|
||||||
|
y = parseInt(b);
|
||||||
|
bricks[y * level.size + x] =
|
||||||
|
bricks[y * level.size + x] === color ? "_" : color;
|
||||||
|
level.bricks = bricks.join("");
|
||||||
|
}
|
||||||
|
if (action == "set_color") {
|
||||||
|
color = a;
|
||||||
|
}
|
||||||
|
if (action == "size") {
|
||||||
|
const newSize = level.size + parseInt(a);
|
||||||
|
const newBricks = [];
|
||||||
|
for (let y = 0; y < newSize; y++) {
|
||||||
|
for (let x = 0; x < newSize; x++) {
|
||||||
|
newBricks.push(
|
||||||
|
(x < level.size && y < level.size && bricks[y * level.size + x]) ||
|
||||||
|
"_",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
level.size = newSize;
|
||||||
|
level.bricks = newBricks.join("");
|
||||||
|
}
|
||||||
|
if (action == "move") {
|
||||||
|
const dx = parseInt(a),
|
||||||
|
dy = parseInt(b);
|
||||||
|
const newBricks = [];
|
||||||
|
for (let y = 0; y < level.size; y++) {
|
||||||
|
for (let x = 0; x < level.size; x++) {
|
||||||
|
const tx = x - dx;
|
||||||
|
const ty = y - dy;
|
||||||
|
if (tx < 0 || tx >= level.size || ty < 0 || ty >= level.size) {
|
||||||
|
newBricks.push("_");
|
||||||
|
} else {
|
||||||
|
newBricks.push(bricks[ty * level.size + tx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
level.bricks = newBricks.join("");
|
||||||
|
}
|
||||||
|
if (action === "play") {
|
||||||
|
restart({
|
||||||
|
level: transformRawLevel(level),
|
||||||
|
isEditorTrialRun: nth,
|
||||||
|
perks: {
|
||||||
|
base_combo: 7,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (action === "copy") {
|
||||||
|
let text =
|
||||||
|
"```\n[" +
|
||||||
|
(level.name || "unnamed level")?.replace(/\[|\]/gi, " ") +
|
||||||
|
"]";
|
||||||
|
bricks.forEach((b, bi) => {
|
||||||
|
if (!(bi % level.size)) text += "\n";
|
||||||
|
text += b;
|
||||||
|
});
|
||||||
|
text +=
|
||||||
|
"\n[" +
|
||||||
|
(level.credit?.replace(/\[|\]/gi, " ") || "Missing credits") +
|
||||||
|
"]\n```";
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
// return
|
||||||
|
}
|
||||||
|
if (action === "rename") {
|
||||||
|
const name = prompt(t("editor.editing.rename_prompt"), level.name);
|
||||||
|
if (name) {
|
||||||
|
level.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (action === "credit") {
|
||||||
|
const credit = prompt(
|
||||||
|
t("editor.editing.credit_prompt"),
|
||||||
|
level.credit || "",
|
||||||
|
);
|
||||||
|
if (credit !== "null") {
|
||||||
|
level.credit = credit || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (action === "delete") {
|
||||||
|
rawList = rawList.filter((l, li) => li !== nth);
|
||||||
|
setSettingValue("custom_levels", rawList);
|
||||||
|
openLevelEditorLevelsList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
level.color = automaticBackgroundColor(bricks);
|
||||||
|
|
||||||
|
setSettingValue("custom_levels", rawList);
|
||||||
|
editRawLevelList(nth, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function automaticBackgroundColor(bricks: string[]) {
|
|
||||||
return bricks.filter((b) => b === "g").length >
|
|
||||||
bricks.filter((b) => b !== "_").length * 0.05
|
|
||||||
? "#115988"
|
|
||||||
: "";
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { getLevelBackground, hashCode } from "../getLevelBackground";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { moveLevel, resizeLevel, setBrick } from "./levels_editor_util";
|
import { moveLevel, resizeLevel, setBrick } from "./levels_editor_util";
|
||||||
|
import {levelCodeToRawLevel} from "../pure_functions";
|
||||||
|
|
||||||
const backgrounds = _backgrounds as string[];
|
const backgrounds = _backgrounds as string[];
|
||||||
|
|
||||||
|
@ -90,17 +91,11 @@ function App() {
|
||||||
height: 40,
|
height: 40,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
}}
|
}}
|
||||||
></button>,
|
>{ (color=="black" && '💣')||' '}</button>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const background = color
|
|
||||||
? { backgroundImage: "none", backgroundColor: color }
|
|
||||||
: {
|
|
||||||
backgroundImage: `url("data:image/svg+xml;UTF8,${encodeURIComponent(getLevelBackground(level) as string)}")`,
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={li}>
|
<div key={li}>
|
||||||
|
@ -141,31 +136,12 @@ function App() {
|
||||||
<button onClick={() => updateLevel(li, moveLevel(level, 0, 1))}>
|
<button onClick={() => updateLevel(li, moveLevel(level, 0, 1))}>
|
||||||
D
|
D
|
||||||
</button>
|
</button>
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
value={level.color || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
e.target.value && updateLevel(li, { color: e.target.value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={level.svg || hashCode(level.name) % backgrounds.length}
|
|
||||||
onChange={(e) =>
|
|
||||||
!isNaN(parseFloat(e.target.value)) &&
|
|
||||||
updateLevel(li, {
|
|
||||||
color: "",
|
|
||||||
svg: parseFloat(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="level-bricks-preview"
|
className="level-bricks-preview"
|
||||||
style={{
|
style={{
|
||||||
width: size * 40,
|
width: size * 40,
|
||||||
height: size * 40,
|
height: size * 40
|
||||||
...background,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{brickButtons}
|
{brickButtons}
|
||||||
|
@ -180,14 +156,14 @@ function App() {
|
||||||
key={code}
|
key={code}
|
||||||
className={code === selected ? "active" : ""}
|
className={code === selected ? "active" : ""}
|
||||||
style={{
|
style={{
|
||||||
background: color || "linear-gradient(45deg,black,white)",
|
background: color || "",
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
width: "40px",
|
width: "40px",
|
||||||
height: "40px",
|
height: "40px",
|
||||||
border: "1px solid black",
|
border: "1px solid black",
|
||||||
}}
|
}}
|
||||||
onClick={() => setSelected(code)}
|
onClick={() => setSelected(code)}
|
||||||
></button>
|
>{(color=='' && 'x') || (color=="black" && '💣')||' '}</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
@ -211,6 +187,21 @@ function App() {
|
||||||
>
|
>
|
||||||
new
|
new
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
id="import-level"
|
||||||
|
onClick={() => {
|
||||||
|
const code = prompt("Level Code ? ");
|
||||||
|
if(!code) return;
|
||||||
|
const l=levelCodeToRawLevel(code)
|
||||||
|
if(!l)return;
|
||||||
|
setLevels((list) => [
|
||||||
|
...list,
|
||||||
|
l
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
import
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import _appVersion from "./data/version.json";
|
||||||
import { rawUpgrades } from "./upgrades";
|
import { rawUpgrades } from "./upgrades";
|
||||||
import { getLevelBackground } from "./getLevelBackground";
|
import { getLevelBackground } from "./getLevelBackground";
|
||||||
import { levelIconHTML } from "./levelIcon";
|
import { levelIconHTML } from "./levelIcon";
|
||||||
import {automaticBackgroundColor} from "./levelEditor";
|
|
||||||
|
import {automaticBackgroundColor} from "./pure_functions";
|
||||||
|
|
||||||
const palette = _palette as Palette;
|
const palette = _palette as Palette;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Ball, GameState } from "./types";
|
import {Ball, GameState} from "./types";
|
||||||
|
|
||||||
export function clamp(value: number, min: number, max: number) {
|
export function clamp(value: number, min: number, max: number) {
|
||||||
return Math.max(min, Math.min(value, max));
|
return Math.max(min, Math.min(value, max));
|
||||||
|
@ -102,3 +102,38 @@ export const wallBouncedBest = 3,
|
||||||
catchRateGood = 90,
|
catchRateGood = 90,
|
||||||
missesBest = 3,
|
missesBest = 3,
|
||||||
missesGood = 6;
|
missesGood = 6;
|
||||||
|
|
||||||
|
export const MAX_LEVEL_SIZE = 21;
|
||||||
|
export const MIN_LEVEL_SIZE = 2;
|
||||||
|
|
||||||
|
export function automaticBackgroundColor(bricks: string[]) {
|
||||||
|
return bricks.filter((b) => b === "g").length >
|
||||||
|
bricks.filter((b) => b !== "_").length * 0.05
|
||||||
|
? "#115988"
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function levelCodeToRawLevel(code: string) {
|
||||||
|
|
||||||
|
let [name, credit] = code.match(/\[([^\]]+)]/gi);
|
||||||
|
|
||||||
|
let bricks = code
|
||||||
|
.split(name)[1]
|
||||||
|
.split(credit)[0]
|
||||||
|
.replace(/\s/gi, "");
|
||||||
|
name = name.slice(1, -1);
|
||||||
|
credit = credit.slice(1, -1);
|
||||||
|
name ||= "Imported on " + new Date().toISOString().slice(0, 10);
|
||||||
|
credit ||= "";
|
||||||
|
const size = Math.sqrt(bricks.length);
|
||||||
|
if (Math.floor(size) === size &&
|
||||||
|
size >= MIN_LEVEL_SIZE &&
|
||||||
|
size <= MAX_LEVEL_SIZE)
|
||||||
|
return {
|
||||||
|
color: automaticBackgroundColor(bricks.split("")),
|
||||||
|
size,
|
||||||
|
bricks,
|
||||||
|
name,
|
||||||
|
credit,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue