Add initial Pico-8 cartridges for testing

- Created test_cart.p8 with basic drawing functionality and a print statement.
- Created test_unix.p8 with a simple initialization function.
This commit is contained in:
2026-03-21 18:38:43 +11:00
parent 71354ad463
commit f917cf6ad5
56 changed files with 5200 additions and 688 deletions

View File

@@ -0,0 +1,205 @@
import { SpriteEditor } from './SpriteEditor.js';
import { SpriteSheet } from '../core/SpriteSheet.js';
/**
* Manages the sprite editor modal integration.
*/
export class SpriteEditorManager {
/**
* @param {HTMLElement} modal Modal element.
* @param {HTMLElement} container Container for sprite editor.
*/
constructor(modal, container) {
this.modal = modal;
this.container = container;
this.spriteSheet = new SpriteSheet();
this.editor = null;
this.isOpen = false;
this.#setupModal();
}
/**
* Sets up modal event listeners.
*/
#setupModal() {
// Close modal on backdrop click
const backdrop = this.modal.querySelector('.modal__backdrop');
backdrop?.addEventListener('click', () => this.close());
// Close modal on close button click
const closeButtons = this.modal.querySelectorAll('[data-modal-close]');
closeButtons.forEach((btn) => {
btn.addEventListener('click', () => this.close());
});
// Export to .p8
const exportBtn = document.getElementById('exportP8Button');
exportBtn?.addEventListener('click', () => this.#exportToP8());
// Import from .p8
const importBtn = document.getElementById('importP8Button');
importBtn?.addEventListener('click', () => this.#importFromP8());
// Initialize editor on first open
this.modal.addEventListener('transitionend', () => {
if (this.isOpen && !this.editor) {
this.#initializeEditor();
}
}, { once: true });
}
/**
* Initializes the sprite editor.
*/
#initializeEditor() {
if (this.editor) return;
this.editor = new SpriteEditor(this.container, this.spriteSheet);
console.log('Sprite editor initialized');
}
/**
* Opens the sprite editor modal.
*/
open() {
if (this.isOpen) return;
this.isOpen = true;
this.modal.removeAttribute('hidden');
this.modal.setAttribute('aria-hidden', 'false');
document.body.classList.add('modal-open');
// Initialize editor immediately if not already done
if (!this.editor) {
this.#initializeEditor();
}
// Focus trap
requestAnimationFrame(() => {
const closeBtn = this.modal.querySelector('[data-modal-close]');
closeBtn?.focus();
});
}
/**
* Closes the sprite editor modal.
*/
close() {
if (!this.isOpen) return;
this.isOpen = false;
this.modal.setAttribute('hidden', '');
this.modal.setAttribute('aria-hidden', 'true');
document.body.classList.remove('modal-open');
}
/**
* Exports sprite data to .p8 format.
*/
#exportToP8() {
if (!this.editor) return;
const gfxData = this.editor.getSpriteSheet().toP8Gfx();
const flagData = this.editor.getSpriteSheet().toP8Flags();
const p8Content = `pico-8 cartridge // http://www.pico-8.com
version 41
__lua__
-- generated by picoGraph
-- paste your blueprinted code here
__gfx__
${gfxData}
__gff__
${flagData}
`;
const blob = new Blob([p8Content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'sprites.p8';
a.click();
URL.revokeObjectURL(url);
// Show success feedback
const exportBtn = document.getElementById('exportP8Button');
if (exportBtn) {
exportBtn.classList.add('is-success');
setTimeout(() => {
exportBtn.classList.remove('is-success');
}, 1500);
}
}
/**
* Imports sprite data from .p8 format.
*/
#importFromP8() {
if (!this.editor) return;
const input = document.createElement('input');
input.type = 'file';
input.accept = '.p8';
input.addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
const text = await file.text();
// Extract __gfx__ section
const gfxMatch = text.match(/__gfx__\s*\n([\s\S]*?)(?=\n__|$)/);
if (gfxMatch) {
this.editor.getSpriteSheet().fromP8Gfx(gfxMatch[1]);
}
// Extract __gff__ section
const gffMatch = text.match(/__gff__\s*\n([\s\S]*?)(?=\n__|$)/);
if (gffMatch) {
this.editor.getSpriteSheet().fromP8Flags(gffMatch[1]);
}
// Re-render
this.editor.setSpriteSheet(this.editor.getSpriteSheet());
// Show success feedback
const importBtn = document.getElementById('importP8Button');
if (importBtn) {
importBtn.classList.add('is-success');
setTimeout(() => {
importBtn.classList.remove('is-success');
}, 1500);
}
} catch (error) {
console.error('Failed to import sprite data:', error);
alert('Failed to import sprite data. Please ensure the file is a valid .p8 file.');
}
});
input.click();
}
/**
* Gets the current sprite sheet data.
*
* @returns {SpriteSheet}
*/
getSpriteSheet() {
return this.spriteSheet;
}
/**
* Sets sprite sheet data.
*
* @param {SpriteSheet} spriteSheet New sprite sheet.
*/
setSpriteSheet(spriteSheet) {
this.spriteSheet = spriteSheet;
if (this.editor) {
this.editor.setSpriteSheet(spriteSheet);
}
}
}