mirror of
https://github.com/NohamR/Pocket-Bird.git
synced 2026-05-24 19:59:36 +00:00
Add history for species changes
This commit is contained in:
BIN
dist/extension.zip
vendored
BIN
dist/extension.zip
vendored
Binary file not shown.
7
dist/extension/birb.js
vendored
7
dist/extension/birb.js
vendored
@@ -226,6 +226,13 @@
|
|||||||
return document.documentElement.clientHeight;
|
return document.documentElement.clientHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @typedef {Object} Species
|
||||||
|
* @property {string} name
|
||||||
|
* @property {string} description
|
||||||
|
* @property {Record<string, string>} colors
|
||||||
|
* @property {string[]} [tags]
|
||||||
|
*/
|
||||||
|
|
||||||
var species = {
|
var species = {
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"name": "Eastern Bluebird",
|
"name": "Eastern Bluebird",
|
||||||
|
|||||||
7
dist/obsidian/main.js
vendored
7
dist/obsidian/main.js
vendored
@@ -231,6 +231,13 @@ module.exports = class PocketBird extends Plugin {
|
|||||||
return document.documentElement.clientHeight;
|
return document.documentElement.clientHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @typedef {Object} Species
|
||||||
|
* @property {string} name
|
||||||
|
* @property {string} description
|
||||||
|
* @property {Record<string, string>} colors
|
||||||
|
* @property {string[]} [tags]
|
||||||
|
*/
|
||||||
|
|
||||||
var species = {
|
var species = {
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"name": "Eastern Bluebird",
|
"name": "Eastern Bluebird",
|
||||||
|
|||||||
7
dist/userscript/birb.user.js
vendored
7
dist/userscript/birb.user.js
vendored
@@ -240,6 +240,13 @@
|
|||||||
return document.documentElement.clientHeight;
|
return document.documentElement.clientHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @typedef {Object} Species
|
||||||
|
* @property {string} name
|
||||||
|
* @property {string} description
|
||||||
|
* @property {Record<string, string>} colors
|
||||||
|
* @property {string[]} [tags]
|
||||||
|
*/
|
||||||
|
|
||||||
var species = {
|
var species = {
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"name": "Eastern Bluebird",
|
"name": "Eastern Bluebird",
|
||||||
|
|||||||
7
dist/web/birb.embed.js
vendored
7
dist/web/birb.embed.js
vendored
@@ -226,6 +226,13 @@
|
|||||||
return document.documentElement.clientHeight;
|
return document.documentElement.clientHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @typedef {Object} Species
|
||||||
|
* @property {string} name
|
||||||
|
* @property {string} description
|
||||||
|
* @property {Record<string, string>} colors
|
||||||
|
* @property {string[]} [tags]
|
||||||
|
*/
|
||||||
|
|
||||||
var species = {
|
var species = {
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"name": "Eastern Bluebird",
|
"name": "Eastern Bluebird",
|
||||||
|
|||||||
7
dist/web/birb.js
vendored
7
dist/web/birb.js
vendored
@@ -226,6 +226,13 @@
|
|||||||
return document.documentElement.clientHeight;
|
return document.documentElement.clientHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @typedef {Object} Species
|
||||||
|
* @property {string} name
|
||||||
|
* @property {string} description
|
||||||
|
* @property {Record<string, string>} colors
|
||||||
|
* @property {string[]} [tags]
|
||||||
|
*/
|
||||||
|
|
||||||
var species = {
|
var species = {
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"name": "Eastern Bluebird",
|
"name": "Eastern Bluebird",
|
||||||
|
|||||||
145
editor/editor.js
145
editor/editor.js
@@ -5,13 +5,11 @@ import Frame from '../src/animation/frame.js';
|
|||||||
import { Directions, getLayerPixels } from '../src/shared.js';
|
import { Directions, getLayerPixels } from '../src/shared.js';
|
||||||
import species from '../src/species.js';
|
import species from '../src/species.js';
|
||||||
|
|
||||||
|
/** @typedef {import('../src/species.js').Species} Species */
|
||||||
|
|
||||||
const COLOR_MAP = SPRITE_SHEET_COLOR_MAP;
|
const COLOR_MAP = SPRITE_SHEET_COLOR_MAP;
|
||||||
const SPRITE_PATH = "../sprites/birb.png";
|
const SPRITE_PATH = "../sprites/birb.png";
|
||||||
const SPRITE_SIZE = 32;
|
const SPRITE_SIZE = 32;
|
||||||
/** @type {Array<{tag: string, label: string}>} */
|
|
||||||
const AVAILABLE_TAGS = [
|
|
||||||
{ tag: TAG.TUFT, label: "Tuft" },
|
|
||||||
];
|
|
||||||
|
|
||||||
/** @type {Record<string, string>} */
|
/** @type {Record<string, string>} */
|
||||||
const DEFAULT_OVERRIDES = {
|
const DEFAULT_OVERRIDES = {
|
||||||
@@ -46,15 +44,11 @@ let selectedColorElement = null;
|
|||||||
const colorElements = {};
|
const colorElements = {};
|
||||||
|
|
||||||
|
|
||||||
/** @type {Record<string, string>} */
|
/** @type {Species} */
|
||||||
let currentSpecies = { ...species.bluebird.colors };
|
let currentSpecies = JSON.parse(JSON.stringify(species.bluebird));
|
||||||
let colorHistory = [{ ...currentSpecies }];
|
let speciesHistory = [JSON.parse(JSON.stringify(currentSpecies))];
|
||||||
let historyIndex = 0;
|
let historyIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
/** @type {Set<string>} */
|
|
||||||
const currentTags = new Set();
|
|
||||||
|
|
||||||
/** @type {Frame|null} */
|
/** @type {Frame|null} */
|
||||||
let baseFrame = null;
|
let baseFrame = null;
|
||||||
const spriteCanvas = document.createElement('canvas');
|
const spriteCanvas = document.createElement('canvas');
|
||||||
@@ -93,30 +87,50 @@ function draw() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
drawBackground();
|
drawBackground();
|
||||||
baseFrame.draw(spriteCtx, Directions.RIGHT, 1, buildColorScheme(), [...currentTags]);
|
baseFrame.draw(spriteCtx, Directions.RIGHT, 1, buildColorScheme(), currentSpecies.tags || []);
|
||||||
ctx.drawImage(spriteCanvas, 0, 0);
|
ctx.drawImage(spriteCanvas, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateColors() {
|
function updateSpecies() {
|
||||||
const lastColors = colorHistory[historyIndex];
|
const previousSpecies = speciesHistory[historyIndex];
|
||||||
let changed = false;
|
let changed = false;
|
||||||
for (const part of Object.keys(currentSpecies)) {
|
// Check for changes in colors
|
||||||
if (currentSpecies[part] !== lastColors[part]) {
|
for (const part of Object.keys(currentSpecies.colors)) {
|
||||||
|
if (currentSpecies.colors[part] !== previousSpecies.colors[part]) {
|
||||||
changed = true;
|
changed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!changed) {
|
if (!changed) {
|
||||||
for (const part of Object.keys(lastColors)) {
|
for (const part of Object.keys(previousSpecies.colors)) {
|
||||||
if (!(part in currentSpecies)) {
|
if (!(part in currentSpecies.colors)) {
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for changes in tags
|
||||||
|
if (!changed) {
|
||||||
|
const prevTags = new Set(previousSpecies.tags || []);
|
||||||
|
const currTags = new Set(currentSpecies.tags || []);
|
||||||
|
for (const tag of prevTags) {
|
||||||
|
if (!currTags.has(tag)) {
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!changed) {
|
||||||
|
for (const tag of currentSpecies.tags || []) {
|
||||||
|
if (!previousSpecies.tags || !previousSpecies.tags.includes(tag)) {
|
||||||
changed = true;
|
changed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
colorHistory = colorHistory.slice(0, historyIndex + 1);
|
speciesHistory = speciesHistory.slice(0, historyIndex + 1);
|
||||||
colorHistory.push({ ...currentSpecies });
|
speciesHistory.push(JSON.parse(JSON.stringify(currentSpecies)));
|
||||||
historyIndex++;
|
historyIndex++;
|
||||||
}
|
}
|
||||||
updateJson();
|
updateJson();
|
||||||
@@ -131,8 +145,11 @@ function loadEditor() {
|
|||||||
const item = createColorSwatch(part, getColor(part) || color);
|
const item = createColorSwatch(part, getColor(part) || color);
|
||||||
editor.appendChild(item);
|
editor.appendChild(item);
|
||||||
}
|
}
|
||||||
for (const { tag, label } of AVAILABLE_TAGS) {
|
for (const value of Object.values(TAG)) {
|
||||||
editor.appendChild(createTagToggle(tag, label));
|
if (value === TAG.DEFAULT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
editor.appendChild(createTagToggle(value, getTag(value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,8 +158,8 @@ function loadEditor() {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function getColor(part) {
|
function getColor(part) {
|
||||||
if (currentSpecies[part]) {
|
if (currentSpecies.colors[part]) {
|
||||||
return currentSpecies[part];
|
return currentSpecies.colors[part];
|
||||||
}
|
}
|
||||||
if (DEFAULT_OVERRIDES[part]) {
|
if (DEFAULT_OVERRIDES[part]) {
|
||||||
return getColor(DEFAULT_OVERRIDES[part]);
|
return getColor(DEFAULT_OVERRIDES[part]);
|
||||||
@@ -155,6 +172,31 @@ function getColor(part) {
|
|||||||
return "transparent";
|
return "transparent";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} tag
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function getTag(tag) {
|
||||||
|
return currentSpecies.tags ? currentSpecies.tags.includes(tag) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} tag
|
||||||
|
* @param {boolean} enabled
|
||||||
|
*/
|
||||||
|
function setTag(tag, enabled) {
|
||||||
|
if (!currentSpecies.tags) {
|
||||||
|
currentSpecies.tags = [];
|
||||||
|
}
|
||||||
|
if (enabled) {
|
||||||
|
if (!currentSpecies.tags.includes(tag)) {
|
||||||
|
currentSpecies.tags.push(tag);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentSpecies.tags = currentSpecies.tags.filter(t => t !== tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createColorPicker() {
|
function createColorPicker() {
|
||||||
colorPickerInput.type = "text";
|
colorPickerInput.type = "text";
|
||||||
colorPickerInput.id = "color-picker-interceptor";
|
colorPickerInput.id = "color-picker-interceptor";
|
||||||
@@ -165,14 +207,14 @@ function createColorPicker() {
|
|||||||
if (selectedColorElement && selectedPart !== null) {
|
if (selectedColorElement && selectedPart !== null) {
|
||||||
const newColor = colorPickerInput.value;
|
const newColor = colorPickerInput.value;
|
||||||
selectedColorElement.style.backgroundColor = newColor;
|
selectedColorElement.style.backgroundColor = newColor;
|
||||||
currentSpecies[selectedPart] = newColor;
|
currentSpecies.colors[selectedPart] = newColor;
|
||||||
draw();
|
draw();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("mouseup", () => {
|
document.addEventListener("mouseup", () => {
|
||||||
if (selectedPart !== null && !jsonElement.contains(document.activeElement)) {
|
if (selectedPart !== null && !jsonElement.contains(document.activeElement)) {
|
||||||
updateColors();
|
updateSpecies();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -199,7 +241,7 @@ function createColorSwatch(label, color) {
|
|||||||
colorPickerInput.style.left = rect.left + "px";
|
colorPickerInput.style.left = rect.left + "px";
|
||||||
colorPickerInput.style.top = (rect.bottom + window.scrollY) + "px";
|
colorPickerInput.style.top = (rect.bottom + window.scrollY) + "px";
|
||||||
|
|
||||||
colorPickerInput.value = currentSpecies[label] || color;
|
colorPickerInput.value = currentSpecies.colors[label] || color;
|
||||||
colorPickerInput.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
colorPickerInput.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -211,10 +253,10 @@ function createColorSwatch(label, color) {
|
|||||||
labelElement.textContent = labelText;
|
labelElement.textContent = labelText;
|
||||||
labelElement.title = "Click to remove from species";
|
labelElement.title = "Click to remove from species";
|
||||||
labelElement.addEventListener("click", () => {
|
labelElement.addEventListener("click", () => {
|
||||||
delete currentSpecies[label];
|
delete currentSpecies.colors[label];
|
||||||
colorElement.style.backgroundColor = getColor(label);
|
colorElement.style.backgroundColor = getColor(label);
|
||||||
updateColors();
|
updateSpecies();
|
||||||
refreshEditorColors();
|
refreshEditor();
|
||||||
});
|
});
|
||||||
item.appendChild(labelElement);
|
item.appendChild(labelElement);
|
||||||
|
|
||||||
@@ -223,37 +265,34 @@ function createColorSwatch(label, color) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} tag
|
* @param {string} tag
|
||||||
* @param {string} label
|
* @param {boolean} enabled
|
||||||
* @returns {HTMLDivElement}
|
* @returns {HTMLDivElement}
|
||||||
*/
|
*/
|
||||||
function createTagToggle(tag, label) {
|
function createTagToggle(tag, enabled) {
|
||||||
const item = document.createElement("div");
|
const item = document.createElement("div");
|
||||||
item.classList.add("editor-item");
|
item.classList.add("editor-item");
|
||||||
|
|
||||||
const toggle = document.createElement("button");
|
const toggle = document.createElement("button");
|
||||||
|
toggle.id = `tag-toggle-${tag}`;
|
||||||
toggle.classList.add("tag-toggle");
|
toggle.classList.add("tag-toggle");
|
||||||
toggle.textContent = "✓";
|
toggle.textContent = "✓";
|
||||||
toggle.addEventListener("click", () => {
|
toggle.addEventListener("click", () => {
|
||||||
if (currentTags.has(tag)) {
|
setTag(tag, !getTag(tag));
|
||||||
currentTags.delete(tag);
|
toggle.classList.toggle("tag-toggle--active", getTag(tag));
|
||||||
toggle.classList.remove("tag-toggle--active");
|
updateSpecies();
|
||||||
} else {
|
|
||||||
currentTags.add(tag);
|
|
||||||
toggle.classList.add("tag-toggle--active");
|
|
||||||
}
|
|
||||||
draw();
|
draw();
|
||||||
});
|
});
|
||||||
item.appendChild(toggle);
|
item.appendChild(toggle);
|
||||||
|
|
||||||
const labelElement = document.createElement("div");
|
const labelElement = document.createElement("div");
|
||||||
labelElement.classList.add("label");
|
labelElement.classList.add("label");
|
||||||
labelElement.textContent = label.toUpperCase();
|
labelElement.textContent = tag.toUpperCase();
|
||||||
item.appendChild(labelElement);
|
item.appendChild(labelElement);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshEditorColors() {
|
function refreshEditor() {
|
||||||
for (const [, part] of Object.entries(COLOR_MAP)) {
|
for (const [, part] of Object.entries(COLOR_MAP)) {
|
||||||
const el = colorElements[part];
|
const el = colorElements[part];
|
||||||
if (el && !el.classList.contains("color--transparent")) {
|
if (el && !el.classList.contains("color--transparent")) {
|
||||||
@@ -261,7 +300,13 @@ function refreshEditorColors() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selectedColorElement && selectedPart !== null) {
|
if (selectedColorElement && selectedPart !== null) {
|
||||||
colorPickerInput.value = currentSpecies[selectedPart] || "";
|
colorPickerInput.value = currentSpecies.colors[selectedPart] || "";
|
||||||
|
}
|
||||||
|
for (const value of Object.values(TAG)) {
|
||||||
|
const toggle = editor.querySelector(`#tag-toggle-${value}`);
|
||||||
|
if (toggle && toggle instanceof HTMLElement) {
|
||||||
|
toggle.classList.toggle("tag-toggle--active", getTag(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,17 +321,17 @@ document.addEventListener("keydown", (e) => {
|
|||||||
if (e.key === "z" && !e.shiftKey) {
|
if (e.key === "z" && !e.shiftKey) {
|
||||||
if (historyIndex > 0) {
|
if (historyIndex > 0) {
|
||||||
historyIndex--;
|
historyIndex--;
|
||||||
currentSpecies = { ...colorHistory[historyIndex] };
|
currentSpecies = JSON.parse(JSON.stringify(speciesHistory[historyIndex]));
|
||||||
refreshEditorColors();
|
refreshEditor();
|
||||||
updateJson();
|
updateJson();
|
||||||
draw();
|
draw();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
} else if ((e.key === "z" && e.shiftKey) || e.key === "y") {
|
} else if ((e.key === "z" && e.shiftKey) || e.key === "y") {
|
||||||
if (historyIndex < colorHistory.length - 1) {
|
if (historyIndex < speciesHistory.length - 1) {
|
||||||
historyIndex++;
|
historyIndex++;
|
||||||
currentSpecies = { ...colorHistory[historyIndex] };
|
currentSpecies = JSON.parse(JSON.stringify(speciesHistory[historyIndex]));
|
||||||
refreshEditorColors();
|
refreshEditor();
|
||||||
updateJson();
|
updateJson();
|
||||||
draw();
|
draw();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -299,7 +344,7 @@ jsonElement.addEventListener("input", () => {
|
|||||||
const parsed = JSON.parse(jsonElement.textContent || "");
|
const parsed = JSON.parse(jsonElement.textContent || "");
|
||||||
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
||||||
currentSpecies = parsed;
|
currentSpecies = parsed;
|
||||||
refreshEditorColors();
|
refreshEditor();
|
||||||
draw();
|
draw();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -307,7 +352,7 @@ jsonElement.addEventListener("input", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
jsonElement.addEventListener("blur", () => {
|
jsonElement.addEventListener("blur", () => {
|
||||||
updateColors();
|
updateSpecies();
|
||||||
});
|
});
|
||||||
|
|
||||||
createColorPicker();
|
createColorPicker();
|
||||||
@@ -319,5 +364,5 @@ loadEditor();
|
|||||||
new Layer(getLayerPixels(pixels, 0, SPRITE_SIZE)),
|
new Layer(getLayerPixels(pixels, 0, SPRITE_SIZE)),
|
||||||
new Layer(getLayerPixels(pixels, 5, SPRITE_SIZE), TAG.TUFT),
|
new Layer(getLayerPixels(pixels, 5, SPRITE_SIZE), TAG.TUFT),
|
||||||
]);
|
]);
|
||||||
updateColors();
|
updateSpecies();
|
||||||
})();
|
})();
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/** @typedef {Object} Species
|
||||||
|
* @property {string} name
|
||||||
|
* @property {string} description
|
||||||
|
* @property {Record<string, string>} colors
|
||||||
|
* @property {string[]} [tags]
|
||||||
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"name": "Eastern Bluebird",
|
"name": "Eastern Bluebird",
|
||||||
|
|||||||
Reference in New Issue
Block a user