This commit is contained in:
slweeb 2023-09-01 11:21:05 -04:00
commit ae0c0d4687
4 changed files with 1254 additions and 105 deletions

291
mods/betterSettings.js Normal file
View File

@ -0,0 +1,291 @@
const settingType = {
COLOR: [0, "#ff0000"],
TEXT: [1, ""],
NUMBER: [2, 0],
BOOLEAN: [3, false],
SELECT: [4, null]
}
class Setting {
constructor (name, storageName, type, disabled = false, defaultValue = null) {
this.tabName = null;
this.name = name;
this.storageName = storageName;
this.type = type[0];
this.disabled = disabled;
this.defaultValue = defaultValue ?? type[1];
}
set(value) {
this.value = value;
const settings = JSON.parse(localStorage.getItem(`${this.tabName}/settings`)) ?? {};
settings[this.name] = value;
localStorage.setItem(`${this.tabName}/settings`, JSON.stringify(settings));
}
update() {
this.value = (JSON.parse(localStorage.getItem(`${this.tabName}/settings`)) ?? {})[this.name] ?? this.defaultValue;
}
get() {
this.update();
return this.value;
}
enable() {
this.disabled = false;
}
disable() {
this.disabled = true;
}
#parseColor(colorString) {
if (colorString instanceof Array) return parseColor(colorString[0]);
if (typeof colorString != "string") return "#ffffff";
if (colorString.startsWith("rgb(")) {
const color = colorString.replace("rgb(", "").replace(")", "");
return `#${color.split(",").map(a => parseInt(a).toString(16)).join("")}`;
} else {
if (colorString.startsWith("#")) {
const color = colorString.slice(1);
if (color.length == 3) return `#${color.repeat(2)}`;
else if (color.length == 2) return `#${color.repeat(3)}`;
else if (color.length >= 6) return `#${color.slice(0, 6)}`;
else return `#${color}`;
}
}
}
build() {
const value = this.get();
const id = "betterSettings/" + this.modName + "/" + this.storageName;
const span = document.createElement("span");
span.className = "setting-span";
span.title = 'Default: "' + this.defaultValue + '"' + (this.disabled ? ". This setting is disabled." : "");
span.innerText = this.name + " ";
const element = document.createElement("input");
switch (this.type) {
case 0: {
element.type = "color";
element.disabled = this.disabled;
element.id = id;
element.value = value;
element.onchange = (ev) => {
this.set(this.#parseColor(ev.target.value));
}
break;
}
case 1: {
element.type = "text";
element.disabled = this.disabled;
element.id = id;
element.value = value;
element.onchange = (ev) => {
this.set(ev.target.value);
}
break;
}
case 2: {
element.type = "number";
element.disabled = this.disabled;
element.id = id;
element.value = value;
element.onchange = (ev) => {
this.set(parseFloat(ev.target.value));
}
break;
}
case 3: {
element.type = "input";
element.className = "toggleInput";
element.disabled = this.disabled;
element.id = id;
element.value = value ? "ON" : "OFF";
element.setAttribute("state", value ? "1" : "0");
element.onclick = (ev) => {
ev.target.value = ev.target.value == "ON" ? "OFF" : "ON";
ev.target.setAttribute("state", ev.target.getAttribute("state") == "1" ? "0" : "1");
this.set(ev.target.value == "ON");
}
break;
}
}
span.appendChild(element);
return span;
}
}
class SelectSetting extends Setting {
constructor (name, storageName, values, disabled = false, defaultValue = null) {
super(name, storageName, settingType.SELECT, disabled, defaultValue ?? values[0][1]);
this.values = values;
}
build() {
const value = this.get();
const id = "betterSettings/" + this.modName + "/" + this.storageName;
let selected = false;
const span = document.createElement("span");
span.className = "setting-span";
span.title = "Default: " + this.defaultValue;
span.innerText = this.name;
const element = document.createElement("select");
element.id = id;
for (const val of this.values) {
const option = document.createElement("option");
option.value = val[0];
option.innerText = val[1];
if (val[0] == value && !selected) {
option.selected = true;
selected = true;
}
element.appendChild(option);
}
element.onchange = (ev) => {
this.set(ev.target.value);
}
span.appendChild(element);
return span;
}
}
class SettingsTab {
constructor (tabName) {
this.categories = new Map();
this.registry = new Map();
this.tabName = tabName;
}
registerSetting(setting, category = "General") {
setting.tabName = this.tabName.toLowerCase().replace(/ /, "_");
setting.update();
if (this.categories.has(category)) this.categories.get(category).push(setting);
else this.categories.set(category, [setting]);
this.registry.set(setting.storageName, setting);
}
registerSettings(category = "General", ...settings) {
for (const setting of settings) {
this.registerSetting(setting, category);
}
}
set(name, value) {
this.registry.get(name)?.set(value);
}
get(name) {
return this.registry.get(name)?.get();
}
build() {
const result = document.createElement("div");
for (const key of this.categories.keys()) {
const category = document.createElement("div");
const title = document.createElement("span");
title.innerText = key;
title.className = "betterSettings-categoryTitle";
category.appendChild(title);
for (const setting of this.categories.get(key)) {
if (setting instanceof Setting) category.appendChild(setting.build());
}
result.append(category, document.createElement("br"));
}
return result;
}
}
class SettingsManager {
constructor () {
this.settings = new Map();
}
registerTab(settingsTab) {
this.settings.set(settingsTab.tabName, settingsTab);
}
getSettings() {
return this.settings;
}
}
const settingsManager = new SettingsManager();
{
const injectCss = () => {
const css = `.modSelectSettingsButton {
padding: 10px;
cursor: pointer;
}
.modSelectSettingsButton[current=true] {
background-color: rgb(71, 71, 71);
}
.modSelectSettingsButton:hover {
background-color: rgb(51, 51, 51);
}
#modSelectControls {
margin-bottom: 10px;
position: relative;
display: flex;
overflow-x: scroll;
}
.betterSettings-categoryTitle {
font-size: 1.25em;
}`;
const style = document.createElement("style");
style.innerHTML = css;
document.head.appendChild(style);
}
const inject = () => {
const settingsMenu = document.getElementById("settingsMenu");
const menuText = settingsMenu.querySelector(".menuText");
const menuTextChildren = menuText.children;
const generalDiv = document.createElement("div");
generalDiv.id = "betterSettings/div/general";
while (menuTextChildren.length > 0) {
generalDiv.appendChild(menuTextChildren[0]);
}
menuText.appendChild(generalDiv);
const controls = document.createElement("div");
controls.id = "modSelectControls";
const generalButton = document.createElement("button");
generalButton.setAttribute("current", true);
generalButton.id = "betterSettings/button/general";
generalButton.className = "modSelectSettingsButton";
generalButton.innerText = "General";
generalButton.onclick = (ev) => {
for (const element of controls.children) {
element.setAttribute("current", false);
document.getElementById(element.id.replace("button", "div")).style.display = "none";
}
ev.target.setAttribute("current", true);
document.getElementById("betterSettings/div/general").style.display = "";
}
controls.appendChild(generalButton);
const wrapper = document.createElement("div");
wrapper.appendChild(generalDiv);
for (const mod of settingsManager.getSettings().keys()) {
const modButton = document.createElement("button");
modButton.setAttribute("current", false);
modButton.id = "betterSettings/button/" + mod;
modButton.className = "modSelectSettingsButton";
modButton.innerText = mod;
modButton.onclick = (ev) => {
for (const element of controls.children) {
element.setAttribute("current", false);
document.getElementById(element.id.replace("button", "div")).style.display = "none";
}
ev.target.setAttribute("current", true);
document.getElementById("betterSettings/div/" + mod).style.display = "";
}
controls.appendChild(modButton);
const modDiv = document.createElement("div");
modDiv.style.display = "none";
modDiv.id = "betterSettings/div/" + mod;
modDiv.appendChild(settingsManager.getSettings().get(mod).build());
wrapper.appendChild(modDiv);
}
menuText.append(controls, wrapper);
}
runAfterLoadList.push(inject, injectCss);
}

608
mods/editTools.js Normal file
View File

@ -0,0 +1,608 @@
if (!enabledMods.includes("mods/betterSettings.js")) { enabledMods.unshift("mods/betterSettings.js"); localStorage.setItem("enabledMods", JSON.stringify(enabledMods)); window.location.reload() };
const settingsTab = new SettingsTab("Edit tools");
const element = new Setting("Element", "element", settingType.TEXT, false, "wall");
const replace = new Setting("Replace pixels", "replace", settingType.BOOLEAN, false, true);
const filter = new Setting("Filter", "filter", settingType.BOOLEAN, false, false);
const filteredElement = new Setting("Filtered Element", "filterElement", settingType.TEXT, false, "");
const transparentSelection = new Setting("Transparent selection", "transparentSelection", settingType.BOOLEAN, false, true);
settingsTab.registerSettings("Box tools", element, replace);
settingsTab.registerSettings("Filter settings", filter, filteredElement);
settingsTab.registerSettings("Selection settings", transparentSelection);
settingsManager.registerTab(settingsTab);
// current selection
let selection_ = {
start: {},
end: {}
}
// selection position, used for moveSelection
let selectionPosition = {};
// copy of the selection that is being moved (pixels), used for moveSelection
let selectionMoved = [];
// offsets of the mouse relative to selection position, used for moveSelection
let selectionOffsets = {};
// current box, used in box and rectangle
let box = {
start: {},
end: {}
}
// whether the next mouseUp even should trigger
let skip = false;
// whether user is currently holding
let holding = false;
// mobile device shift equivalent
let lockSelection = false;
// current clipboard, used for cutting, copying and pasting
let clipboard = [];
// createPixel but with color argument
const createPixelColor = (element, x, y, color) => {
const pixel = new Pixel(x, y, element);
pixel.color = color;
currentPixels.push(pixel);
checkUnlock(element);
}
// replaces a pixel at x, y position with replacement element specified in the settings
const replacePixel = (x, y) => {
if (outOfBounds(x, y)) return;
if (pixelMap[x][y]) {
if (!replace.get()) return;
deletePixel(x, y);
}
createPixel(element.get(), x, y);
}
// checks whether position pos is in bounds
const inBounds = (bounds, pos) => {
bounds = {
start: {
x: Math.min(bounds.start.x, bounds.end.x),
y: Math.min(bounds.start.y, bounds.end.y)
},
end: {
x: Math.max(bounds.start.x, bounds.end.x),
y: Math.max(bounds.start.y, bounds.end.y)
}
}
return pos.x >= bounds.start.x && pos.x <= bounds.end.x && pos.y >= bounds.start.y && pos.y <= bounds.end.y;
}
// generates a selection based on start and end positions
const select = (start, end) => {
const res = [];
for (let i = 0; i <= end.x - start.x; i++) {
res[i] = [];
for (let j = 0; j <= end.y - start.y; j++) {
res[i][j] = pixelMap[i + start.x][j + start.y];
}
}
return res;
}
elements.select = {
name: "Select",
category: "editTools",
maxSize: 1,
onMouseDown: () => {
if (outOfBounds(mousePos.x, mousePos.y)) {
skip = true;
return;
}
skip = false;
holding = true;
selection_.start = mousePos;
},
onMouseUp: () => {
if (skip) return;
selection_.end = mousePos;
if (selection_.start == selection_.end) selection_ = {};
holding = false;
},
tool: (_) => {},
perTick: () => {
if (!selection_ || !selection_.start || !selection_.end) return;
if (holding) {
selection_.end = mousePos;
}
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const start = {
x: Math.round(selection_.start.x * pixelSize),
y: Math.round(selection_.start.y * pixelSize)
}
const end = {
x: Math.round(selection_.end.x * pixelSize),
y: Math.round((selection_.end.y + 1) * pixelSize)
}
const {strokeStyle, lineWidth} = ctx;
ctx.setLineDash([8, 8]);
ctx.lineWidth = 2;
ctx.strokeStyle = "white";
ctx.globalCompositeOperation = "destination-over";
ctx.strokeRect(start.x, start.y, end.x - start.x, end.y - start.y);
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.setLineDash([]);
}
}
elements.box = {
name: "Box",
category: "editTools",
onMouseDown: () => {
if (outOfBounds(mousePos.x, mousePos.y) || showingMenu) {
skip = true;
return;
}
skip = false;
holding = true;
box.start = mousePos;
},
onMouseUp: () => {
if (skip) return;
box.end = mousePos;
holding = false;
for (let i = -Math.floor(mouseSize / 2); i < Math.abs(box.start.x - box.end.x) + Math.floor(mouseSize / 2); i++) {
const x = Math.min(box.start.x, box.end.x) + i;
for (let j = -Math.floor(mouseSize / 2); j <= Math.floor(mouseSize / 2); j++) {
replacePixel(x, box.start.y + (box.start.y > box.end.y ? -j : j));
replacePixel(x, box.end.y + (box.end.y > box.start.y ? -j : j));
}
}
for (let i = -Math.floor(mouseSize / 2); i <= Math.abs(box.start.y - box.end.y) + Math.floor(mouseSize / 2); i++) {
const y = Math.min(box.start.y, box.end.y) + i;
for (let j = -Math.floor(mouseSize / 2); j <= Math.floor(mouseSize / 2); j++) {
replacePixel(box.start.x + (box.start.x > box.end.x ? -j : j), y);
replacePixel(box.end.x + (box.end.x > box.start.x ? -j : j), y);
}
}
},
tool: (_) => {},
perTick: () => {
if (holding) {
if (shiftDown || lockSelection) {
box.end = {
y: mousePos.y,
x: box.start.x + Math.abs(mousePos.y - box.start.y) * (box.start.x > mousePos.x ? -1 : 1)
};
mousePos = box.end;
} else {
box.end = mousePos;
}
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const start = {
x: Math.round(box.start.x * pixelSize),
y: Math.round(box.start.y * pixelSize)
}
const end = {
x: Math.round(box.end.x * pixelSize),
y: Math.round((box.end.y + 1) * pixelSize)
}
const {strokeStyle, lineWidth} = ctx;
ctx.setLineDash([8, 8]);
ctx.lineWidth = 2;
ctx.strokeStyle = "yellow";
ctx.strokeRect(start.x, start.y, end.x - start.x, end.y - start.y);
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.setLineDash([]);
}
}
}
elements.cut = {
name: "Cut",
category: "editTools",
maxSize: 0,
tool: (_) => {},
onSelect: () => {
if (!selection_.start || !selection_.end) return alert("No selection made");
const selected = select({
x: Math.min(selection_.start.x, selection_.end.x),
y: Math.min(selection_.start.y, selection_.end.y)
}, {
x: Math.max(selection_.start.x, selection_.end.x),
y: Math.max(selection_.start.y, selection_.end.y)
})
clipboard = selected;
for (const i of selected) {
for (const pixel of i) {
if (!pixel) continue;
deletePixel(pixel.x, pixel.y);
}
}
selectElement("unknown");
}
}
elements.copy = {
name: "Copy",
category: "editTools",
maxSize: 0,
tool: (_) => {},
onSelect: () => {
if (!selection_.start || !selection_.end) return alert("No selection made");
const selected = select({
x: Math.min(selection_.start.x, selection_.end.x),
y: Math.min(selection_.start.y, selection_.end.y)
}, {
x: Math.max(selection_.start.x, selection_.end.x),
y: Math.max(selection_.start.y, selection_.end.y)
})
clipboard = selected;
selectElement("unknown");
}
}
elements.selectionMove = {
name: "Move selection",
category: "editTools",
maxSize: 0,
tool: (_) => {},
onMouseDown: () => {
if (outOfBounds(mousePos.x, mousePos.y)) return;
if (!selection_.start || !selection_.end) return;
if (!inBounds(selection_, mousePos)) return;
selectionOffsets = {
x: mousePos.x - Math.min(selection_.start.x, selection_.end.x),
y: mousePos.y - Math.min(selection_.start.y, selection_.end.y)
}
const selected = select({
x: Math.min(selection_.start.x, selection_.end.x),
y: Math.min(selection_.start.y, selection_.end.y)
}, {
x: Math.max(selection_.start.x, selection_.end.x),
y: Math.max(selection_.start.y, selection_.end.y)
})
selectionPosition = {
x: selection_.start.x,
y: selection_.start.y
}
for (const i of selected) {
for (const pixel of i) {
if (!pixel) continue;
deletePixel(pixel.x, pixel.y);
}
}
selectionMoved = selected;
},
perTick: () => {
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
ctx.globalAlpha = 1;
const start = {
x: Math.round(selection_.start.x * pixelSize),
y: Math.round(selection_.start.y * pixelSize)
}
const end = {
x: Math.round(selection_.end.x * pixelSize),
y: Math.round((selection_.end.y + 1) * pixelSize)
}
const {strokeStyle, lineWidth} = ctx;
ctx.setLineDash([8, 8]);
ctx.lineWidth = 2;
ctx.strokeStyle = "white";
ctx.strokeRect(start.x, start.y, end.x - start.x, end.y - start.y);
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.setLineDash([]);
if (!selectionMoved || selectionMoved.length == 0) return;
selectionPosition = {
x: mousePos.x - selectionOffsets.x,
y: mousePos.y - selectionOffsets.y
}
selection_ = {
start: {
x: selectionPosition.x,
y: selectionPosition.y
},
end: {
x: selectionPosition.x + selectionMoved.length - 1,
y: selectionPosition.y + selectionMoved[0].length - 1
}
}
ctx.globalAlpha = 0.5;
for (let i = 0; i < selectionMoved.length; i++) {
for (let j = 0; j < selectionMoved[i].length; j++) {
const x = selectionPosition.x + i;
const y = selectionPosition.y + j;
if (!selectionMoved[i][j]) continue;
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = selectionMoved[i][j].color;
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
}
}
},
onMouseUp: () => {
if (outOfBounds(mousePos.x, mousePos.y)) return;
for (let i = 0; i < selectionMoved.length; i++) {
for (let j = 0; j < selectionMoved[i].length; j++) {
const x = selectionPosition.x + i;
const y = selectionPosition.y + j;
if (!selectionMoved[i][j] && transparentSelection.get()) continue;
if (pixelMap[x][y]) deletePixel(x, y);
if (selectionMoved[i][j]) createPixelColor(selectionMoved[i][j].element, x, y, selectionMoved[i][j].color);
}
}
selectionMoved = [];
}
}
elements.paste = {
name: "Paste",
category: "editTools",
tool: (_) => {},
maxSize: 1,
onMouseDown: () => {
if (!clipboard) return alert("Nothing left to paste");
for (let i = 0; i < clipboard.length; i++) {
for (let j = 0; j < clipboard[i].length; j++) {
const x = mousePos.x + i;
const y = mousePos.y + j;
if (outOfBounds(x, y) || (!clipboard[i][j] && transparentSelection.get())) continue;
if (!clipboard[i][j]) {
if (pixelMap[x][y]) deletePixel(x, y);
continue;
}
if (!pixelMap[x][y]) createPixelColor(clipboard[i][j].element, x, y, clipboard[i][j].color);
else {
deletePixel(x, y);
createPixelColor(clipboard[i][j].element, x, y, clipboard[i][j].color);
}
}
}
}
}
elements.rectangle = {
name: "Rectangle",
category: "editTools",
onMouseDown: () => {
if (outOfBounds(mousePos.x, mousePos.y)) {
skip = true;
return;
}
skip = false;
holding = true;
box.start = mousePos;
},
onMouseUp: () => {
if (skip) return;
box.end = mousePos;
holding = false;
for (let i = -Math.floor(mouseSize / 2); i <= Math.abs(box.start.x - box.end.x) + Math.floor(mouseSize / 2); i++) {
for (let j = -Math.floor(mouseSize / 2); j <= Math.abs(box.start.y - box.end.y) + Math.floor(mouseSize / 2); j++) {
replacePixel(Math.min(box.start.x, box.end.x) + i, Math.min(box.start.y, box.end.y) + j);
}
}
},
tool: (_) => {},
perTick: () => {
if (holding) {
if (shiftDown || lockSelection) {
box.end = {
y: mousePos.y,
x: box.start.x + Math.abs(mousePos.y - box.start.y) * (box.start.x > mousePos.x ? -1 : 1)
};
mousePos = box.end;
} else {
box.end = mousePos;
}
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const start = {
x: Math.round(box.start.x * pixelSize),
y: Math.round(box.start.y * pixelSize)
}
const end = {
x: Math.round(box.end.x * pixelSize),
y: Math.round((box.end.y + 1) * pixelSize)
}
const {strokeStyle, lineWidth} = ctx;
ctx.setLineDash([8, 8]);
ctx.lineWidth = 2;
ctx.strokeStyle = "yellow";
ctx.strokeRect(start.x, start.y, end.x - start.x, end.y - start.y);
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.setLineDash([]);
}
}
}
// Ellipse Midpoint Algorithm
// https://stackoverflow.com/questions/15474122/is-there-a-midpoint-ellipse-algorithm
// https://web.archive.org/web/20160305234351/http://geofhagopian.net/sablog/Slog-october/slog-10-25-05.htm
function ellipsePlotPoints(xc, yc, x, y) {
replacePixel(xc + x, yc + y);
replacePixel(xc - x, yc + y);
replacePixel(xc + x, yc - y);
replacePixel(xc - x, yc - y);
}
function ellipse(xc, yc, w, h) {
const a2 = Math.pow(w, 2);
const b2 = Math.pow(h, 2);
const twoa2 = 2 * a2;
const twob2 = 2 * b2;
let p;
let x = 0;
let y = h;
let px = 0;
let py = twoa2 * y;
/* Plot the initial point in each quadrant. */
ellipsePlotPoints(xc, yc, x, y);
p = Math.round(b2 - (a2 * b) + (0.25 * a2));
while (px < py) {
x++;
px += twob2;
if (p < 0) p += b2 + px;
else {
y--;
py -= twoa2;
p += b2 + px - py;
}
ellipsePlotPoints(xc, yc, x, y);
}
p = Math.round(b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2);
while (y > 0) {
y--;
py -= twoa2;
if (p > 0) p += a2 - py;
else {
x++;
px += twob2;
p += a2 - py + px;
}
ellipsePlotPoints(xc, yc, x, y);
}
}
elements.ellipse = {
name: "Ellipse",
category: "editTools",
tool: (_) => {},
maxSize: 1,
onMouseDown: () => {
if (outOfBounds(mousePos.x, mousePos.y)) {
skip = true;
return;
}
skip = false;
holding = true;
box.start = mousePos;
},
onMouseUp: () => {
if (skip) return;
box.end = mousePos;
holding = false;
const w = Math.abs(box.end.x - box.start.x);
const h = Math.abs(box.end.y - box.start.y);
const x = Math.min(box.start.x, box.end.x) + Math.floor(w / 2);
const y = Math.min(box.start.y, box.end.y) + Math.floor(h / 2);
ellipse(x, y, Math.floor(w / 2), Math.floor(h / 2));
},
tool: (_) => {},
perTick: () => {
if (holding) {
if (shiftDown || lockSelection) {
box.end = {
y: mousePos.y,
x: box.start.x + Math.abs(mousePos.y - box.start.y) * (box.start.x > mousePos.x ? -1 : 1)
};
mousePos = box.end;
} else {
box.end = mousePos;
}
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const start = {
x: Math.round(box.start.x * pixelSize),
y: Math.round(box.start.y * pixelSize)
}
const end = {
x: Math.round(box.end.x * pixelSize),
y: Math.round((box.end.y + 1) * pixelSize)
}
const {strokeStyle, lineWidth} = ctx;
ctx.setLineDash([8, 8]);
ctx.lineWidth = 2;
ctx.strokeStyle = "yellow";
ctx.strokeRect(start.x, start.y, end.x - start.x, end.y - start.y);
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.setLineDash([]);
}
}
}
document.addEventListener("mousedown", (ev) => {
if (elements[currentElement].onMouseDown) {
elements[currentElement].onMouseDown();
}
})
// mouse2 overwrite for delete filter
// no mouse1 overwrite for replace functionality, I don't think I should be overwriting such big functions
// maybe in a future update
mouse2Action = (e,mouseX=undefined,mouseY=undefined,startPos) => {
// Erase pixel at mouse position
if (mouseX == undefined && mouseY == undefined) {
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
lastPos = mousePos;
mousePos = getMousePos(canvas, e);
var mouseX = mousePos.x;
var mouseY = mousePos.y;
}
// If the current element is "pick" or "lookup", coords = [mouseX,mouseY]
if (currentElement == "pick" || currentElement == "lookup") {
var coords = [[mouseX,mouseY]];
}
else if (!isMobile) {
startPos = startPos || lastPos
var coords = lineCoords(startPos.x,startPos.y,mouseX,mouseY);
}
else {
var coords = mouseRange(mouseX,mouseY);
}
// For each x,y in coords
for (var i = 0; i < coords.length; i++) {
var x = coords[i][0];
var y = coords[i][1];
if (!isEmpty(x, y)) {
if (outOfBounds(x,y)) {
continue
}
var pixel = pixelMap[x][y];
// filter
if (filter.get() && pixel.element != filteredElement.get()) continue;
delete pixelMap[x][y];
// Remove pixel from currentPixels
for (var j = 0; j < currentPixels.length; j++) {
if (currentPixels[j].x == x && currentPixels[j].y == y) {
currentPixels.splice(j, 1);
break;
}
}
}
}
}
// Mobile check
// https://stackoverflow.com/questions/11381673/detecting-a-mobile-browser
// http://detectmobilebrowsers.com/
window.mobileAndTabletCheck = () => {
let check = false;
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
return check;
};
// if user is on mobile, add lock selection tool
if (window.mobileAndTabletCheck()) {
elements.lockSelection = {
name: "Lock selection",
category: "editTools",
tool: (_) => {},
onSelect: () => {
// unselect so you can click it multiple times
selectElement("unknown");
document.getElementById("elementButton-lockSelection").innerText = lockSelection ? "Lock selection" : "Unlock selection";
lockSelection = !lockSelection;
}
}
}

View File

@ -30,54 +30,51 @@ elements.ketchup.reactions = {
"mayonnaise": { "elem1": null, "elem2": "fry_sauce" },
"plague": { "elem1": "poisoned_ketchup", "elem2": null},
"infection": { "elem1": "poisoned_ketchup", "elem2": null},
"radiation": { "elem1": "poisoned_ketchup", chance:025},
"fallout": { "elem1": "poisoned_ketchup", chance:025},
"fallout": { "elem1": "poisoned_ketchup", "chance":25},
"gloomwind": { "elem1": "poisoned_ketchup", "elem2": null},
};
};
// making ketchup dirty
elements.dirt.reactions = {
"ketchup": { "elem1": null, "elem2": "dirty_ketchup", "oneway":true},
};
elements.ash.reactions = {
"ketchup": { "elem1": null, "elem2": "dirty_ketchup", "oneway":true},
"steam": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] },
"rain_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] },
"snow_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] },
"acid_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,15] },
"pyrocumulus": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] },
};
elements.ash.reactions.ketchup = { "elem1": null, "elem2": "dirty_ketchup", "oneway":true},
elements.dust.reactions = {
"ketchup": { "elem1": null, "elem2": "dirty_ketchup", "oneway":true},
};
// making it so ketchup clouds can react with smoke to make pyrocumulus
elements.smoke.reactions = {
"steam": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] },
"rain_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] },
"snow_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] },
"acid_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,15] },
"fire_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,15] },
"pyrocumulus": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] },
"ketchup_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0.15] },
"poisoned_ketchup_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0.15] },
};
// pyrocumulus reactions
elements.smoke.reactions.ketchup_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.smoke.reactions.poisoned_ketchup_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.smoke.reactions.ketchup_snow_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.smoke.reactions.poisoned_ketchup_snow_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.smoke.reactions.ketchup_rain_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.smoke.reactions.poisoned_ketchup_rain_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.ash.reactions.ketchup_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.ash.reactions.poisoned_ketchup_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.ash.reactions.ketchup_snow_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.ash.reactions.poisoned_ketchup_snow_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.ash.reactions.ketchup_rain_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
elements.ash.reactions.poisoned_ketchup_rain_cloud = { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15], "setting":"clouds" },
// fixing radiation reactions
elements.radiation.reactions.ketchup = { "elem1": null, "elem2": "poisoned_ketchup", "chance":25}
// elements
elements.frozen_ketchup = {
color: "#d44737",
behavior: behaviors.WALL,
temp: 0,
temp: -5,
category:"solids",
tempHigh: -3,
tempHigh: 5,
stateHigh: "ketchup",
state: "solid",
density: 917,
reactions: {
"plague": { "elem1": "frozen_poisoned_ketchup", "elem2": null},
"infection": { "elem1": "frozen_poisoned_ketchup", "elem2": null},
"radiation": { "elem1": "frozen_poisoned_ketchup", chance:025},
"fallout": { "elem1": "frozen_poisoned_ketchup", chance:025},
"radiation": { "elem1": "frozen_poisoned_ketchup", "chance":25},
"fallout": { "elem1": "frozen_poisoned_ketchup", "chance":25},
"gloomwind": { "elem1": "frozen_poisoned_ketchup", "elem2": null},
},
};
@ -92,13 +89,14 @@ elements.poisoned_ketchup = {
category:"liquids",
state: "liquid",
density: 1140,
stain: 0.05,
};
elements.frozen_poisoned_ketchup = {
color: "#d43754",
behavior: behaviors.POISONED_WALL,
temp: 0,
temp: -5,
category:"solids",
tempHigh: 3,
tempHigh: 5,
stateHigh: "poisoned_ketchup",
state: "solid",
density: 917,
@ -113,56 +111,98 @@ elements.ketchup_spout = {
category:"special",
};
elements.ketchup_cloud = {
color: "#6e413b",
color: "#ad655c",
behavior: [
"XX|XX|XX",
"M1%5|XX|M1%5",
"XX|CR:ketchup%1|XX",
"XX|CO:1%5|M1%2.5 AND BO",
"XX|XX|XX",
],
category:"gases",
temp: 80,
tempLow: 0,
stateLow: "ketchup_snow_cloud",
temp: 110,
tempLow: 100,
stateLow: "ketchup_rain_cloud",
state: "gas",
density: 1,
density: 0.5,
reactions: {
"plague": { "elem1": "poisoned_ketchup_cloud", "elem2": null},
"infection": { "elem1": "poisoned_ketchup_cloud"},
"radiation": { "elem1": "poisoned_ketchup_cloud", chance:025},
"fallout": { "elem1": "poisoned_ketchup_cloud", chance:025},
"radiation": { "elem1": "poisoned_ketchup_cloud", "chance":25},
"fallout": { "elem1": "poisoned_ketchup_cloud", "chance":25},
"gloomwind": { "elem1": "poisoned_ketchup_cloud", "elem2": null},
"ketchup_rain_cloud": { "elem1":"ketchup_rain_cloud", "temp1":-20 },
},
conduct: 0.03,
ignoreAir: true,
};
elements.ketchup_rain_cloud = {
color: "#6e413b",
behavior: [
"XX|XX|XX",
"XX|CH:ketchup%0.05|M1%2.5 AND BO",
"XX|XX|XX|",
],
category: "gases",
temp: 70,
tempHigh: 100,
stateHigh: "ketchup_cloud",
tempLow: 0,
stateLow: "ketchup_snow_cloud",
state: "gas",
density: "0.5",
ignoreAir: true,
conduct: 0.03,
};
elements.poisoned_ketchup_cloud = {
color: "#a8596b",
behavior: [
"XX|XX|XX",
"XX|CO:1%5|M1%2.5 AND BO",
"XX|XX|XX",
],
reactions: {
"poisoned_ketchup_rain_cloud": { "elem1":"poisoned_ketchup_rain_cloud", "temp1": -20 },
},
category: "gases",
temp: 110,
tempLow: 100,
stateLow: "poisoned_ketchup_rain_cloud",
state: "gas",
density: 0.5,
conduct: 0.03,
ignoreAir: true,
};
elements.poisoned_ketchup_rain_cloud = {
color: "#633640",
behavior: [
"XX|XX|XX",
"M1%5|XX|M1%5",
"XX|CR:poisoned_ketchup%1|XX",
"XX|CH:poisoned_ketchup%0.05|M1%2.5 AND BO",
"XX|XX|XX",
],
category:"gases",
temp: 80,
category: "gases",
temp: 70,
tempHigh: 100,
stateHigh: "poisoned_ketchup_cloud",
tempLow: 0,
stateLow: "poisoned_ketchup_snow_cloud",
state: "gas",
density: 1,
density: 0.5,
ignoreAir: true,
conduct: 0.03,
};
elements.ketchup_snow = {
color: "#ed7a6d",
behavior: behaviors.POWDER,
temp: 0,
tempHigh: 5,
temp: -5,
tempHigh: 18,
stateHigh: "ketchup",
category: "land",
category: "land",
state: "solid",
density: "100",
density: 100,
reactions: {
"plague": { "elem1": "poisoned_ketchup_snow", "elem2": null},
"infection": { "elem1": "poisoned_ketchup_snow", "elem2": null},
"radiation": { "elem1": "poisoned_ketchup_snow", chance:025},
"fallout": { "elem1": "poisoned_ketchup_snow", chance:025},
"radiation": { "elem1": "poisoned_ketchup_snow", chance:25},
"fallout": { "elem1": "poisoned_ketchup_snow", chance:25},
"gloomwind": { "elem1": "poisoned_ketchup_snow", "elem2": null},
},
};
@ -170,7 +210,7 @@ elements.ketchup_snow_cloud = {
color: "#755652",
behavior: [
"XX|XX|XX",
"M1%5|CH:ketchup_snow%0.05|M1%5",
"XX|CH:ketchup_snow%0.05|M1%2.5 AND BO",
"XX|XX|XX",
],
category:"gases",
@ -178,30 +218,31 @@ elements.ketchup_snow_cloud = {
tempHigh: 30,
stateHigh: "ketchup_cloud",
state: "gas",
density: 2,
density: 0.55,
reactions: {
"plague": { "elem1": "poisoned_ketchup_snow_cloud", "elem2": null},
"infection": { "elem1": "poisoned_ketchup_snow_cloud"},
"radiation": { "elem1": "poisoned_ketchup_snow_cloud", chance:025},
"fallout": { "elem1": "poisoned_ketchup_snow_cloud", chance:025},
"radiation": { "elem1": "poisoned_ketchup_snow_cloud", chance:25},
"fallout": { "elem1": "poisoned_ketchup_snow_cloud", chance:25},
"gloomwind": { "elem1": "poisoned_ketchup_snow_cloud", "elem2": null},
},
ignoreAir: true,
};
elements.poisoned_ketchup_snow = {
color: "#d1697f",
behavior: behaviors.POISONED_POWDER,
temp: 0,
tempHigh: 5,
temp: -5,
tempHigh: 18,
stateHigh: "poisoned_ketchup",
category: "land",
state: "solid",
density: "100",
density: 100,
};
elements.poisoned_ketchup_snow_cloud = {
color: "#6e4e55",
behavior: [
"XX|XX|XX",
"M1%5|CH:poisoned_ketchup_snow%0.05|M1%5",
"XX|CH:poisoned_ketchup_snow%0.05|M1%2.5 AND BO",
"XX|XX|XX",
],
category:"gases",
@ -209,7 +250,8 @@ elements.poisoned_ketchup_snow_cloud = {
tempHigh: 30,
stateHigh: "poisoned_ketchup_cloud",
state: "gas",
density: 2,
density: 0.55,
ignoreAir: true,
};
elements.mayonnaise = {
color: "#F2EEE9",
@ -218,6 +260,8 @@ elements.mayonnaise = {
category:"liquids",
state: "liquid",
density: 1000,
stain: 0.05,
isFood: true,
};
elements.mustard = {
color: "#D8AD01",
@ -226,34 +270,42 @@ elements.mustard = {
category:"liquids",
state: "liquid",
density: 1052,
stain: 0.05,
isFood: true,
};
elements.ketchup_gas = {
color: "#ffb5ad",
behavior: behaviors.GAS,
temp: 150,
density: 0.6,
state: "gas",
tempLow: 100,
tempLow: 95,
stateLow: "ketchup",
category: "gases",
reactions: {
"plague": { "elem1": "poisoned_ketchup_gas", "elem2": null},
"ketchup_gas": { "elem1": null, "elem2": "ketchup_cloud", "chance":0.3, "y":[0,15] },
"ketchup_gas": { "elem1": null, "elem2": "ketchup_cloud", "chance":0.3, "y":[0,15], "setting":"clouds" },
"ketchup_cloud": { "elem1": "ketchup_cloud", "chance":0.4, "y":[0, 12], "setting":"clouds" },
"ketchup_rain_cloud": { "elem1": "ketchup_rain_cloud", "chance":0.4, "y":[0, 12], "setting":"clouds" },
"infection": { "elem1": "poisoned_ketchup_gas"},
"radiation": { "elem1": "poisoned_ketchup_gas", chance:025},
"fallout": { "elem1": "poisoned_ketchup_gas", chance:025},
"radiation": { "elem1": "poisoned_ketchup_gas", chance:25},
"fallout": { "elem1": "poisoned_ketchup_gas", chance:25},
"gloomwind": { "elem1": "poisoned_ketchup_gas", "elem2": null},
},
};
elements.poisoned_ketchup_gas = {
color: "#e096a6",
behavior: behaviors.POISONED_GAS,
temp: 150,
density: 0.6,
state: "gas",
tempLow: 100,
tempLow: 95,
stateLow: "poisoned_ketchup",
category: "gases",
reactions: {
"poisoned_ketchup_gas": { "elem1": null, "elem2": "poisoned_ketchup_cloud", "chance":0.3, "y":[0,15] },
"poisoned_ketchup_gas": { "elem1": null, "elem2": "poisoned_ketchup_cloud", "chance":0.3, "y":[0,15], "setting":"clouds" },
"poisoned_ketchup_cloud": { "elem1": "poisoned_ketchup_cloud", "chance":0.4, "y":[0, 12], "setting":"clouds" },
"ketchup_rain_cloud": { "elem1": "poisoned_ketchup_rain_cloud", "chance":0.4, "y":[0, 12], "setting":"clouds" },
},
};
elements.fry_sauce = {
@ -263,6 +315,8 @@ elements.fry_sauce = {
category: "liquids",
state: "liquid",
density: 1149,
stain: 0.05,
isFood: true,
};
elements.ketchup_powder = {
color: "#E06320",
@ -271,12 +325,13 @@ elements.ketchup_powder = {
reactions: {
"plague": { "elem1": "poisoned_ketchup_powder", "elem2": null},
"infection": { "elem1": "poisoned_ketchup_powder", "elem2": null},
"radiation": { "elem1": "poisoned_ketchup_powder", chance:025},
"fallout": { "elem1": "poisoned_ketchup_powder", chance:025},
"radiation": { "elem1": "poisoned_ketchup_powder", chance:25},
"fallout": { "elem1": "poisoned_ketchup_powder", chance:25},
"gloomwind": { "elem1": "poisoned_ketchup_powder", "elem2": null},
},
state: "solid",
category: "powders",
isFood: true,
};
elements.poisoned_ketchup_powder = {
color: "#e0204a",
@ -285,44 +340,6 @@ elements.poisoned_ketchup_powder = {
state: "solid",
category: "powders",
};
elements.tomato = {
color: "#B11E0C",
behavior: behaviors.STURDYPOWDER,
category: "food",
density: 470,
state: "solid",
tempHigh: 400,
stateHigh: "ash",
reactions: {
"rock": { "elem1": "tomato_sauce", "elem2": "rock" },
},
burn: 40,
burnTime: 30,
burnInto: "ash",
};
elements.tomato_sauce = {
color: "#B72003",
behavior: behaviors.LIQUID,
category: "liquids",
density: 1031,
state: "liquid",
reactions: {
"sugar": { "elem1": "sugary_tomato_sauce", "elem2": null },
},
viscosity: 25000,
};
elements.sugary_tomato_sauce = {
color: "#b53921",
behavior: behaviors.LIQUID,
category: "liquids",
density: 1031,
state: "liquid",
reactions: {
"vinegar": { "elem1": "ketchup", "elem2": null },
},
viscosity: 25000,
hidden: true,
};
elements.cumin = {
color: "#8B7778",
behavior: behaviors.POWDER,
@ -334,6 +351,7 @@ elements.cumin = {
burn: 40,
burnTime: 40,
burnInto: "ash",
isFood: true,
};
elements.eketchup_spout = {
name: "E-Ketchup Spout",
@ -364,6 +382,7 @@ elements.antiketchup = {
category:"special",
state: "liquid",
density: 1092,
stain: 0.05,
};
elements.dirty_ketchup = {
color: "#851a0d",
@ -377,6 +396,7 @@ elements.dirty_ketchup = {
stateLow: "frozen_ketchup",
density: 1140,
hidden: true,
stain: 0.05,
};
elements.ketchup_gold = {
color: ["#eb8a8a", "#bf3939", "#ff6161"],
@ -444,8 +464,24 @@ runAfterLoad(function() {
/*
Changelog
Mod made primarily by Nubo318. Contributors include deviantEquinox and Lily129.
Version 1.3.1
Mod made by Nubo318. Contributors include DeviantEquinox and An Orbit.
Version 1.3.3
Version 1.3.3 (23rd of August 2023)
+ All liquids added on this mod can now stain stuff, with the exception of molten metals
+ Certain elements can now be mixed with dough and batter
~ Fixed reactions that turned clouds into pyrocumulus when in contact with with smoke or ash
~ Optimized the way in which new reactions of vanilla elements are coded
~ Changed the initial temperature of multiple elements
~ Fixed a bug that caused Ketchup Snow and its poisoned variant to not display their info properly
~ Ketchup clouds now work more similarly to vanilla clouds
Version 1.3.2 (22nd of August 2023)
- Removed some elements due to their inclusion or some form of it in the vanilla game, including:
- Tomato
- Tomato Sauce
- Sugary Tomato Sauce
- Removed a vanilla reaction which turned ketchup into sauce when exposed to radiation
Version 1.3.1 (20th of January 2022)
~ Ketchup fairies are now killed by iron and silver

214
mods/peta.js Normal file
View File

@ -0,0 +1,214 @@
{
const inject = () => {
const gameDiv = document.getElementById("gameDiv");
const parent = document.createElement("div");
parent.id = "popUpParent";
parent.style.display = "none";
const inner = document.createElement("div");
inner.className = "popUp";
inner.id = "petaPopup";
const title = document.createElement("span");
title.id = "popUpTitle";
title.style.fontSize = "1.5em";
title.innerText = "title";
inner.appendChild(title);
const closeButton = document.createElement("button");
closeButton.innerText = "-";
closeButton.id = "popUpCloseButton";
closeButton.className = "XButton";
closeButton.onclick = () => {
closePopUp();
}
inner.appendChild(closeButton);
const text = document.createElement("div");
text.id = "popUpText";
text.style.marginTop = "20px";
text.style.lineHeight = "1.5em";
inner.appendChild(text);
const progress = document.createElement("div");
progress.className = "progressBar";
const progressInner = document.createElement("div");
progressInner.className = "progressBarInside";
progressInner.id = "popUpProgressBar";
progress.appendChild(progressInner);
inner.appendChild(progress);
parent.appendChild(inner);
gameDiv.appendChild(parent);
}
const injectCss = () => {
const style = `.popUp {
position: absolute;
border: 1px solid #fff;
left: 50%;
top: 20%;
transform: translate(-50%, 0%);
width: 70%;
height: 30%;
padding: 10px;
background-color: rgb(31, 31, 31);
overflow-x: hidden;
z-index: 12;
}
.progressBar {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
height: 20px;
}
.progressBarInside {
width: 100%;
height: 20px;
background-color: white;
}`;
const styleElement = document.createElement("style");
styleElement.innerHTML = style;
document.head.appendChild(styleElement);
}
function closePopUp() {
const popUp = document.getElementById("popUpParent");
let alpha = 100;
const interval = setInterval(frame, 5);
function frame() {
if (alpha <= 0) {
clearInterval(interval);
popUp.style.display = "none";
popUp.style.opacity = 1;
} else {
popUp.style.opacity = alpha / 100;
alpha -= 5;
}
}
showingPopUp = false;
}
let intervalTime = 30;
function destroyGame() {
selectElement("unknown");
for (const i of pixelMap) {
for (const pixel of i) {
if (pixel) {
deletePixel(pixel.x, pixel.y);
}
}
}
for (const element of Object.keys(elements)) {
delete elements[element];
const elem = document.getElementById("elementButton-" + element);
if (elem) elem.remove();
}
for (let j = 0; j < document.getElementById("categoryControls").children.length; j++) {
const i = parseInt(j);
document.getElementById("categoryControls").children.item(i).innerText = ["DONT", "MESS", "WITH", "PETA"][i % 4];
}
document.getElementById("toolControls").remove();
// breaks stuff
mouse1Action = () => {};
mouseAction = () => {};
updateStats = () => {};
pixelMap = [];
document.getElementById('stats').innerHTML = "<span>xNONE,yNONE Pxls:-8 -Infinity tps NONE NO_VIEW THAT'S WHAT YOU GET";
}
let showingPopUp = false;
function showPopUp(text, title) {
const titleElement = document.getElementById("popUpTitle");
titleElement.innerText = title;
const textElement = document.getElementById("popUpText");
textElement.innerText = text;
const popUp = document.getElementById("popUpParent");
popUp.style.display = "block";
let width = 100;
showingPopUp = true;
const progressBar = document.getElementById("popUpProgressBar");
const interval = setInterval(frame, intervalTime);
function frame() {
if (width <= 0 || !showingPopUp) {
clearInterval(interval);
setTimeout(() => {
closePopUp();
}, intervalTime);
progressBar.style.width = "0%";
} else {
width--;
progressBar.style.width = width + "%";
}
}
}
let warnings = 0;
let happened = false;
const forbiddenElements = ["meat", "rotten_meat", "cooked_meat", "frozen_meat", "milk", "cream", "fruit_milk", "pilk", "yogurt", "frozen_yogurt", "ice_cream", "egg", "yolk", "hard_yolk", "chocolate_milk", "eggnog"]
const messages = ["What are you trying to do?! That behavior is unnacceptable. Those animals didn't do anything to deserve this. You have {warning} warnings left before we delete half of your elements.",
"You really think we can't do anything huh? Fuck around and find out. {warning} warnings left",
"We have no words for you, how dare you?! {warning} warnings left",
"That is completely unbelievable. {warning} warnings left",
"That is your last chance. If you try it one more time, we WILL remove half of your elements. Do you really want to do it?",
"You already lost half of your elements. Can't you just leave animals alone and use vegan products instead? We are left with no choice, we have to give you a warning. {warning} warnings remaining.",
"You are leaving us with no choice. If you continue this behavior we will make your game completely unplayable. Do you really want to do this? {warning} warnings remaing.",
"Are you even listening? Or are you just skipping our messages like there's nothing there. We are increasing the pop-up length and removing the close button, maybe now you will listen to us. {warning} warnings remaining.",
"You only ever think about yourself, don't you? Do you really think that what you're doing currently is morally justifiable? You are just a coward, you can't admit that what you are doing is evil. Disgusting behavior. {warning} warnings remaining.",
"That is your last chance. It's completely impossible to educate you, so if you try it one more time WE WILL TAKE ACTION! DO YOU UNDERSTAND THAT?!"];
selectElement = (element) => {
if (showingPopUp) return;
if (forbiddenElements.includes(element)) {
console.log(warnings)
if (warnings == 5 && !happened) {
showPopUp("As you wish.", "From PETA");
happened = true;
let possibleElements = Object.keys(elements).filter(e => !forbiddenElements.includes(e) && currentElement != e);
const initialLength = Math.floor(possibleElements.length / 2);
while (possibleElements.length > initialLength) {
const max = possibleElements.length;
const randomElement = Math.floor(Math.random() * max);
const element_ = possibleElements[randomElement];
const elem = document.getElementById("elementButton-" + element_);
if (elem) elem.remove();
possibleElements = possibleElements.filter(e => e != element_);
}
} else if (warnings == 10) {
destroyGame();
} else {
if (warnings == 7) {
document.getElementById("popUpCloseButton").remove();
intervalTime = 60;
}
showPopUp(messages[warnings].replace("{warning}", happened ? 10 - (warnings) : 5 - warnings), "From PETA");
warnings++;
}
return;
}
if (elements[currentElement].onUnselect) {
elements[currentElement].onUnselect();
}
var e1 = document.getElementById("elementButton-"+currentElement);
if (e1 != null) { e1.setAttribute("current","false"); }
currentElement = element;
if (elements[element].customColor) {
// show the colorSelector
document.getElementById("colorSelector").style.display = "block";
}
else {
// hide the colorSelector
document.getElementById("colorSelector").style.display = "none";
}
if (elements[element].onSelect) {
elements[element].onSelect();
}
var e2 = document.getElementById("elementButton-"+element);
if (!e2) { return; }
e2.setAttribute("current","true");
// if e2 has the class "notify", remove it
if (e2.classList.contains("notify")) {
e2.classList.remove("notify");
}
}
runAfterLoadList.push(inject, injectCss);
}