mirror of
https://github.com/NohamR/Pocket-Bird.git
synced 2026-05-24 19:59:36 +00:00
Clean up code
This commit is contained in:
278
dist/birb.js
vendored
278
dist/birb.js
vendored
@@ -1,6 +1,5 @@
|
||||
// @ts-check
|
||||
|
||||
// @ts-ignore
|
||||
const SHARED_CONFIG = {
|
||||
birbCssScale: 1,
|
||||
uiCssScale: 1,
|
||||
@@ -9,7 +8,6 @@ const SHARED_CONFIG = {
|
||||
hopDistance: 45,
|
||||
};
|
||||
|
||||
|
||||
const DESKTOP_CONFIG = {
|
||||
flySpeed: 0.25
|
||||
};
|
||||
@@ -28,17 +26,6 @@ const BIRB_CSS_SCALE = CONFIG.birbCssScale;
|
||||
const UI_CSS_SCALE = CONFIG.uiCssScale;
|
||||
const CANVAS_PIXEL_SIZE = CONFIG.canvasPixelSize;
|
||||
const WINDOW_PIXEL_SIZE = CANVAS_PIXEL_SIZE * BIRB_CSS_SCALE;
|
||||
const HOP_SPEED = CONFIG.hopSpeed;
|
||||
const FLY_SPEED = CONFIG.flySpeed;
|
||||
const HOP_DISTANCE = CONFIG.hopDistance;
|
||||
// Time in milliseconds until the user is considered AFK
|
||||
const AFK_TIME = debugMode ? 0 : 1000 * 30;
|
||||
const SPRITE_HEIGHT = 32;
|
||||
const MENU_ID = "birb-menu";
|
||||
const MENU_EXIT_ID = "birb-menu-exit";
|
||||
const FIELD_GUIDE_ID = "birb-field-guide";
|
||||
const FEATHER_ID = "birb-feather";
|
||||
|
||||
|
||||
const DEFAULT_SETTINGS = {
|
||||
birbMode: false
|
||||
@@ -48,6 +35,23 @@ const DEFAULT_SETTINGS = {
|
||||
* @typedef {typeof DEFAULT_SETTINGS} Settings
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SavedStickyNote
|
||||
* @property {string} id
|
||||
* @property {string} site
|
||||
* @property {string} content
|
||||
* @property {number} top
|
||||
* @property {number} left
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} BirbSaveData
|
||||
* @property {string[]} unlockedSpecies
|
||||
* @property {string} currentSpecies
|
||||
* @property {Partial<Settings>} settings
|
||||
* @property {SavedStickyNote[]} [stickyNotes]
|
||||
*/
|
||||
|
||||
/** @type {Partial<Settings>} */
|
||||
let userSettings = {};
|
||||
|
||||
@@ -723,6 +727,7 @@ const Directions = {
|
||||
};
|
||||
|
||||
const SPRITE_WIDTH = 32;
|
||||
const SPRITE_HEIGHT = 32;
|
||||
const DECORATIONS_SPRITE_WIDTH = 48;
|
||||
const FEATHER_SPRITE_WIDTH = 32;
|
||||
|
||||
@@ -730,6 +735,34 @@ const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYA
|
||||
const DECORATIONS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAPNJREFUaIHtmTESgzAMBHWZDC+gp0vP/x9Bn44+L6BRmrhJA4csM05uGzfY1s1JxggzIYQQQgghxEnATnB3zwikAICKiXq4BE/uwaxvn/UPb3BnNwFg27Ky0w6vzRp8S4mkIbQD3wzzFJofdTMkYJgn89czFADGKSSiSgphfFBjTaoIKC4cHWvSxIFMmjiQSYoDLUlxoCVywOwHHWjpROop1IL/vsxty2oYO77M1QggSvcpJAFXE66BPfa+2C4v4j2yi7z7FJKAq6FrwN3TO3MMlAAAKO3F2sVZTiu2N9p9CnUv4FR7PbMG2BQ69SJL/kVA8QauAnHUj36BVwAAAABJRU5ErkJggg==";
|
||||
const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII=";
|
||||
|
||||
const MENU_ID = "birb-menu";
|
||||
const MENU_EXIT_ID = "birb-menu-exit";
|
||||
const FIELD_GUIDE_ID = "birb-field-guide";
|
||||
const FEATHER_ID = "birb-feather";
|
||||
|
||||
const HOP_SPEED = CONFIG.hopSpeed;
|
||||
const FLY_SPEED = CONFIG.flySpeed;
|
||||
const HOP_DISTANCE = CONFIG.hopDistance;
|
||||
/** Speed at which the feather falls per tick */
|
||||
const FEATHER_FALL_SPEED = 1;
|
||||
/** Time in milliseconds until the user is considered AFK */
|
||||
const AFK_TIME = debugMode ? 0 : 1000 * 30;
|
||||
const UPDATE_INTERVAL = 1000 / 60; // 60 FPS
|
||||
// Per-frame chances
|
||||
const HOP_CHANCE = 1 / (60 * 3); // 3 seconds
|
||||
const FOCUS_SWITCH_CHANCE = 1 / (60 * 20); // 20 seconds
|
||||
const FEATHER_CHANCE = 1 / (60 * 60 * 60 * 2); // 2 hours
|
||||
/** Multiplier after petting that increases the feather drop chance */
|
||||
const PET_FEATHER_BOOST = 2;
|
||||
/** How long the pet boost lasts in milliseconds */
|
||||
const PET_BOOST_DURATION = 1000 * 60 * 5;
|
||||
const MIN_FOCUS_ELEMENT_WIDTH = 100;
|
||||
const MIN_FOCUS_ELEMENT_TOP = 80;
|
||||
/** Time between checking whether the URL has changed */
|
||||
const URL_CHECK_INTERVAL = 500;
|
||||
/** Time after petting before the menu can be opened */
|
||||
const PET_MENU_COOLDOWN = 1000;
|
||||
|
||||
/**
|
||||
* Load the sprite sheet and return the pixel-map template
|
||||
* @param {string} dataUri
|
||||
@@ -788,7 +821,7 @@ function loadSpriteSheetPixels(dataUri, templateColors = true) {
|
||||
|
||||
log("Loading sprite sheets...");
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATIONS_SPRITE_SHEET, false), loadSpriteSheetPixels(FEATHER_SPRITE_SHEET)]).then(([birbPixels, decorationPixels, featherPixels]) => {
|
||||
const SPRITE_SHEET = birbPixels;
|
||||
const DECORATIONS_SPRITE_SHEET = decorationPixels;
|
||||
@@ -974,7 +1007,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
const canvas = document.createElement("canvas");
|
||||
|
||||
/** @type {CanvasRenderingContext2D} */
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const States = {
|
||||
@@ -1015,7 +1048,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
* @returns {boolean} Whether the script is running in a userscript extension context
|
||||
*/
|
||||
function isUserScript() {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
return typeof GM_getValue === "function";
|
||||
}
|
||||
|
||||
@@ -1028,9 +1061,10 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
function load() {
|
||||
/** @type {Record<string, any>} */
|
||||
let saveData = {};
|
||||
|
||||
if (isUserScript()) {
|
||||
log("Loading save data from UserScript storage");
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
saveData = GM_getValue("birbSaveData", {}) ?? {};
|
||||
} else if (isTestEnvironment()) {
|
||||
log("Test environment detected, loading save data from localStorage");
|
||||
@@ -1038,14 +1072,18 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
} else {
|
||||
log("Not a UserScript");
|
||||
}
|
||||
|
||||
debug("Loaded data: " + JSON.stringify(saveData));
|
||||
|
||||
if (!saveData.settings) {
|
||||
log("No user settings found in save data, starting fresh");
|
||||
}
|
||||
|
||||
userSettings = saveData.settings ?? {};
|
||||
unlockedSpecies = saveData.unlockedSpecies ?? [DEFAULT_BIRD];
|
||||
currentSpecies = saveData.currentSpecies ?? DEFAULT_BIRD;
|
||||
stickyNotes = [];
|
||||
|
||||
if (saveData.stickyNotes) {
|
||||
for (let note of saveData.stickyNotes) {
|
||||
if (note.id) {
|
||||
@@ -1053,17 +1091,19 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log(stickyNotes.length + " sticky notes loaded");
|
||||
switchSpecies(currentSpecies);
|
||||
}
|
||||
|
||||
function save() {
|
||||
/** @type {Record<string, any>} */
|
||||
let saveData = {
|
||||
unlockedSpecies: unlockedSpecies,
|
||||
currentSpecies: currentSpecies,
|
||||
/** @type {BirbSaveData} */
|
||||
const saveData = {
|
||||
unlockedSpecies,
|
||||
currentSpecies,
|
||||
settings: userSettings
|
||||
};
|
||||
|
||||
if (stickyNotes.length > 0) {
|
||||
saveData.stickyNotes = stickyNotes.map(note => ({
|
||||
id: note.id,
|
||||
@@ -1073,9 +1113,10 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
left: note.left
|
||||
}));
|
||||
}
|
||||
|
||||
if (isUserScript()) {
|
||||
log("Saving data to UserScript storage");
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
GM_setValue("birbSaveData", saveData);
|
||||
} else if (isTestEnvironment()) {
|
||||
log("Test environment detected, saving data to localStorage");
|
||||
@@ -1088,7 +1129,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
function resetSaveData() {
|
||||
if (isUserScript()) {
|
||||
log("Resetting save data in UserScript storage");
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
GM_deleteValue("birbSaveData");
|
||||
} else if (isTestEnvironment()) {
|
||||
log("Test environment detected, resetting save data in localStorage");
|
||||
@@ -1214,17 +1255,8 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const stickyNoteParams = stickyNoteUrl.split("?")[1]?.split("&").reduce((params, param) => {
|
||||
const [key, value] = param.split("=");
|
||||
return { ...params, [key]: value };
|
||||
}, {});
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const currentParams = currentUrl.split("?")[1]?.split("&").reduce((params, param) => {
|
||||
const [key, value] = param.split("=");
|
||||
return { ...params, [key]: value };
|
||||
}, {});
|
||||
const stickyNoteParams = parseUrlParams(stickyNoteUrl);
|
||||
const currentParams = parseUrlParams(currentUrl);
|
||||
|
||||
debug("Comparing params: ", stickyNoteParams, currentParams);
|
||||
|
||||
@@ -1286,7 +1318,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
});
|
||||
|
||||
onClick(canvas, () => {
|
||||
if (currentAnimation === Animations.HEART && (Date.now() - lastPetTimestamp < 1000)) {
|
||||
if (currentAnimation === Animations.HEART && (Date.now() - lastPetTimestamp < PET_MENU_COOLDOWN)) {
|
||||
// Currently being pet, don't open menu
|
||||
return;
|
||||
}
|
||||
@@ -1323,9 +1355,9 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
lastUrl = currentUrl;
|
||||
drawStickyNotes();
|
||||
}
|
||||
}, 500);
|
||||
}, URL_CHECK_INTERVAL);
|
||||
|
||||
setInterval(update, 1000 / 60);
|
||||
setInterval(update, UPDATE_INTERVAL);
|
||||
}
|
||||
|
||||
function drawStickyNotes() {
|
||||
@@ -1350,7 +1382,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
}
|
||||
|
||||
if (currentState === States.IDLE && !frozen && !isMenuOpen()) {
|
||||
if (Math.random() < 1 / (60 * 3) && currentAnimation !== Animations.HEART) {
|
||||
if (Math.random() < HOP_CHANCE && currentAnimation !== Animations.HEART) {
|
||||
hop();
|
||||
} else if (Date.now() - lastActionTimestamp > AFK_TIME) {
|
||||
// Idle for a while, do something
|
||||
@@ -1358,7 +1390,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
// Fly to an element
|
||||
focusOnElement();
|
||||
lastActionTimestamp = Date.now();
|
||||
} else if (Math.random() < 1 / (60 * 20)) {
|
||||
} else if (Math.random() < FOCUS_SWITCH_CHANCE) {
|
||||
// Fly to another element if idle for a longer while
|
||||
focusOnElement();
|
||||
lastActionTimestamp = Date.now();
|
||||
@@ -1369,9 +1401,9 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
setState(States.IDLE);
|
||||
}
|
||||
}
|
||||
const FEATHER_CHANCE = 1 / (60 * 60 * 60 * 2); // 1 every 2 hours (ticks * seconds * minutes * hours)
|
||||
|
||||
// Double the chance of a feather if recently pet
|
||||
let petMod = Date.now() - lastPetTimestamp < 1000 * 60 * 5 ? 2 : 1;
|
||||
const petMod = Date.now() - lastPetTimestamp < PET_BOOST_DURATION ? PET_FEATHER_BOOST : 1;
|
||||
if (visible && Math.random() < FEATHER_CHANCE * petMod) {
|
||||
lastPetTimestamp = 0;
|
||||
activateFeather();
|
||||
@@ -1442,6 +1474,42 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a window element with header and content
|
||||
* @param {string} id
|
||||
* @param {string} title
|
||||
* @param {string} contentHtml
|
||||
* @param {() => void} [onClose]
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
function createWindow(id, title, contentHtml, onClose) {
|
||||
const window = makeElement("birb-window", undefined, id);
|
||||
window.innerHTML = `
|
||||
<div class="birb-window-header">
|
||||
<div class="birb-window-title">${title}</div>
|
||||
<div class="birb-window-close">x</div>
|
||||
</div>
|
||||
<div class="birb-window-content">
|
||||
${contentHtml}
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(window);
|
||||
makeDraggable(window.querySelector(".birb-window-header"));
|
||||
|
||||
const closeButton = window.querySelector(".birb-window-close");
|
||||
if (closeButton) {
|
||||
makeClosable(() => {
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
window.remove();
|
||||
}, closeButton);
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
function insertDecoration() {
|
||||
// Create a canvas element for the decoration
|
||||
const decorationCanvas = document.createElement("canvas");
|
||||
@@ -1521,21 +1589,16 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
|
||||
function updateFeather() {
|
||||
const feather = document.querySelector("#birb-feather");
|
||||
const featherGravity = 1;
|
||||
if (!feather || !(feather instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
const y = parseInt(feather.style.top || "0") + featherGravity;
|
||||
const y = parseInt(feather.style.top || "0") + FEATHER_FALL_SPEED;
|
||||
feather.style.top = `${Math.min(y, window.innerHeight - feather.offsetHeight)}px`;
|
||||
if (y < window.innerHeight - feather.offsetHeight) {
|
||||
feather.style.left = `${Math.sin(3.14 * 2 * (ticks / 120)) * 25}px`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// insertDecoration();
|
||||
// insertFieldGuide();
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
@@ -1552,28 +1615,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
if (document.querySelector("#" + FIELD_GUIDE_ID)) {
|
||||
return;
|
||||
}
|
||||
let html = `
|
||||
<div class="birb-window-header">
|
||||
<div class="birb-window-title">${title}</div>
|
||||
<div class="birb-window-close">x</div>
|
||||
</div>
|
||||
<div class="birb-window-content">
|
||||
<div class="birb-message-content">
|
||||
${message}
|
||||
</div>
|
||||
</div>`
|
||||
const modal = makeElement("birb-window");
|
||||
modal.style.width = "270px";
|
||||
modal.innerHTML = html;
|
||||
document.body.appendChild(modal);
|
||||
makeDraggable(modal.querySelector(".birb-window-header"));
|
||||
|
||||
const closeButton = modal.querySelector(".birb-window-close");
|
||||
if (closeButton) {
|
||||
makeClosable(() => {
|
||||
modal.remove();
|
||||
}, closeButton);
|
||||
}
|
||||
const modal = createWindow("birb-modal", title, `
|
||||
<div class="birb-message-content">
|
||||
${message}
|
||||
</div>
|
||||
`);
|
||||
|
||||
modal.style.width = "270px";
|
||||
centerElement(modal);
|
||||
}
|
||||
|
||||
@@ -1990,12 +2039,11 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
const elements = document.querySelectorAll("img, video");
|
||||
const inWindow = Array.from(elements).filter((img) => {
|
||||
const rect = img.getBoundingClientRect();
|
||||
return rect.left >= 0 && rect.top >= 80 && rect.right <= window.innerWidth && rect.top <= window.innerHeight;
|
||||
return rect.left >= 0 && rect.top >= MIN_FOCUS_ELEMENT_TOP && rect.right <= window.innerWidth && rect.top <= window.innerHeight;
|
||||
});
|
||||
const MIN_WIDTH = 100;
|
||||
/** @type {HTMLElement[]} */
|
||||
// @ts-ignore
|
||||
const largeElements = Array.from(inWindow).filter((img) => img instanceof HTMLElement && img !== focusedElement && img.offsetWidth >= MIN_WIDTH);
|
||||
// @ts-expect-error
|
||||
const largeElements = Array.from(inWindow).filter((img) => img instanceof HTMLElement && img !== focusedElement && img.offsetWidth >= MIN_FOCUS_ELEMENT_WIDTH);
|
||||
if (largeElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -2012,12 +2060,8 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getFullWindowHeight() };
|
||||
return;
|
||||
}
|
||||
const rect = focusedElement.getBoundingClientRect();
|
||||
focusedBounds = {
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
top: rect.top
|
||||
};
|
||||
const { left, right, top } = focusedElement.getBoundingClientRect();
|
||||
focusedBounds = { left, right, top };
|
||||
}
|
||||
|
||||
function getCanvasWidth() {
|
||||
@@ -2125,49 +2169,49 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
||||
}
|
||||
canvas.style.bottom = `${bottom}px`;
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
/**
|
||||
* @param {number} startX
|
||||
* @param {number} startY
|
||||
* @param {number} endX
|
||||
* @param {number} endY
|
||||
* @param {number} amount
|
||||
* @param {number} [intensity]
|
||||
* @returns {{x: number, y: number}}
|
||||
*/
|
||||
function parabolicLerp(startX, startY, endX, endY, amount, intensity = 1.2) {
|
||||
const dx = endX - startX;
|
||||
const dy = endY - startY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const angle = Math.atan2(dy, dx);
|
||||
const midX = startX + Math.cos(angle) * distance / 2;
|
||||
const midY = startY + Math.sin(angle) * distance / 2 + distance / 4 * intensity;
|
||||
const t = amount;
|
||||
const x = (1 - t) ** 2 * startX + 2 * (1 - t) * t * midX + t ** 2 * endX;
|
||||
const y = (1 - t) ** 2 * startY + 2 * (1 - t) * t * midY + t ** 2 * endY;
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse URL parameters into a key-value map
|
||||
* @param {string} url
|
||||
* @returns {Record<string, string>}
|
||||
*/
|
||||
function parseUrlParams(url) {
|
||||
const queryString = url.split("?")[1];
|
||||
if (!queryString) return {};
|
||||
|
||||
return queryString.split("&").reduce((params, param) => {
|
||||
const [key, value] = param.split("=");
|
||||
return { ...params, [key]: value };
|
||||
}, {});
|
||||
}
|
||||
}).catch((e) => {
|
||||
error("Error while loading sprite sheets: ", e);
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {number} amount
|
||||
* @returns {number}
|
||||
*/
|
||||
function linearLerp(start, end, amount) {
|
||||
return start + (end - start) * amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} startX
|
||||
* @param {number} startY
|
||||
* @param {number} endX
|
||||
* @param {number} endY
|
||||
* @param {number} amount
|
||||
* @param {number} [intensity]
|
||||
* @returns {{x: number, y: number}}
|
||||
*/
|
||||
function parabolicLerp(startX, startY, endX, endY, amount, intensity = 1.2) {
|
||||
const dx = endX - startX;
|
||||
const dy = endY - startY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const angle = Math.atan2(dy, dx);
|
||||
const midX = startX + Math.cos(angle) * distance / 2;
|
||||
const midY = startY + Math.sin(angle) * distance / 2 + distance / 4 * intensity;
|
||||
const t = amount;
|
||||
const x = (1 - t) ** 2 * startX + 2 * (1 - t) * t * midX + t ** 2 * endX;
|
||||
const y = (1 - t) ** 2 * startY + 2 * (1 - t) * t * midY + t ** 2 * endY;
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
*/
|
||||
function roundToPixel(value) {
|
||||
return Math.round(value / WINDOW_PIXEL_SIZE) * WINDOW_PIXEL_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} Whether the user is on a mobile device
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user