Compare commits
No commits in common. "a06795c694382679a02df84d048aba8f4469d345" and "7fd7ba2e76b70dd8259d6f2a3416093847cc1f0a" have entirely different histories.
a06795c694
...
7fd7ba2e76
8 changed files with 46 additions and 100 deletions
|
|
@ -1 +0,0 @@
|
|||
AddType font/ttf .ttf
|
||||
|
|
@ -63,6 +63,7 @@ Open `index.html` in a modern browser. No build step or third-party dependency i
|
|||
- Dynamic sprites now pause intermittently instead of walking continuously.
|
||||
- Night rendering is darker.
|
||||
- Object picking now uses sprite-sized, per-opaque-pixel hit testing, so transparent pixels are not clickable.
|
||||
- The CSS font stack now tries `3x5 MT Pixel Font` first. The font file itself is not bundled.
|
||||
|
||||
## Phase 5 changes
|
||||
|
||||
|
|
|
|||
|
|
@ -446,7 +446,7 @@ window.addEventListener('load', () => {
|
|||
});
|
||||
</script>
|
||||
<style>
|
||||
@font-face{font-family:PixelIslandServiceFont;src:url("../assets/fonts/PixelMplus10-Regular.ttf?v=20260605-2") format("truetype");font-weight:100 900;font-style:normal;font-display:swap}body{font-family:PixelIslandServiceFont,sans-serif;margin:24px;background:#f7f4ea;color:#2b2b2b}h1{margin-bottom:4px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(380px,1fr));gap:12px}.card{background:#fff;border:1px solid #ddd;border-radius:10px;padding:14px;margin:10px 0}.media{display:flex;gap:14px;align-items:flex-start}.muted{color:#666;font-size:13px}.error{background:#fff1f1;border:1px solid #e3aaaa;padding:12px;border-radius:8px}code{background:#eee;padding:1px 4px;border-radius:4px}input{max-width:210px;padding:6px;margin:2px 4px 2px 0}button{margin:2px 4px 2px 0;padding:6px 9px;border:1px solid #bbb;border-radius:6px;background:#f5f5f5}.danger{background:#b92323;color:white;border:0}.ok{background:#267a3e;color:white;border:0}.warn{background:#8b5b00;color:white;border:0}.pill{display:inline-block;padding:2px 7px;border-radius:999px;background:#eee;font-size:12px}.nav a{margin-right:10px}.thumb{position:relative;flex:0 0 auto;width:128px;height:128px;background:#f2eadb;border:1px solid #ddd;image-rendering:pixelated;overflow:hidden}.thumb i{position:absolute;transform:scale(1.02);transform-origin:0 0}.thumb.empty:after{content:'no image';position:absolute;inset:0;display:grid;place-items:center;color:#777;font-size:11px}@media(max-width:520px){.grid{grid-template-columns:1fr}.media{flex-direction:column}.thumb{width:112px;height:112px}}
|
||||
body{font-family:system-ui,sans-serif;margin:24px;background:#f7f4ea;color:#2b2b2b}h1{margin-bottom:4px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(380px,1fr));gap:12px}.card{background:#fff;border:1px solid #ddd;border-radius:10px;padding:14px;margin:10px 0}.media{display:flex;gap:14px;align-items:flex-start}.muted{color:#666;font-size:13px}.error{background:#fff1f1;border:1px solid #e3aaaa;padding:12px;border-radius:8px}code{background:#eee;padding:1px 4px;border-radius:4px}input{max-width:210px;padding:6px;margin:2px 4px 2px 0}button{margin:2px 4px 2px 0;padding:6px 9px;border:1px solid #bbb;border-radius:6px;background:#f5f5f5}.danger{background:#b92323;color:white;border:0}.ok{background:#267a3e;color:white;border:0}.warn{background:#8b5b00;color:white;border:0}.pill{display:inline-block;padding:2px 7px;border-radius:999px;background:#eee;font-size:12px}.nav a{margin-right:10px}.thumb{position:relative;flex:0 0 auto;width:128px;height:128px;background:#f2eadb;border:1px solid #ddd;image-rendering:pixelated;overflow:hidden}.thumb i{position:absolute;transform:scale(1.02);transform-origin:0 0}.thumb.empty:after{content:'no image';position:absolute;inset:0;display:grid;place-items:center;color:#777;font-size:11px}@media(max-width:520px){.grid{grid-template-columns:1fr}.media{flex-direction:column}.thumb{width:112px;height:112px}}
|
||||
</style>
|
||||
<h1>Pixel Island Admin</h1>
|
||||
<p class="muted">Store: <?=h($storeLabel)?> / Users: <?=count($accounts)?> / Pictures: <?=count($assets)?> / Reports: <?=count($reports)?></p>
|
||||
|
|
|
|||
80
app.js
80
app.js
|
|
@ -58,28 +58,37 @@
|
|||
const uid = () => Math.random().toString(36).slice(2, 9) + Date.now().toString(36).slice(-5);
|
||||
const mod = (n, m) => ((n % m) + m) % m;
|
||||
const lerp = (a, b, t) => a + (b - a) * t;
|
||||
|
||||
|
||||
function clampDimension(value, fallback = 8) {
|
||||
return PixelCodec.clampDimension ? PixelCodec.clampDimension(value, fallback) : Math.max(1, Math.min(MAX_EDITOR_DIMENSION, Math.round(Number(value) || fallback)));
|
||||
}
|
||||
|
||||
function rectArea(width, height = width) {
|
||||
return Math.max(1, Math.floor(Number(width) || 1)) * Math.max(1, Math.floor(Number(height) || width || 1));
|
||||
}
|
||||
|
||||
function lightBudgetForArea(width, height = width) {
|
||||
return Math.max(1, Math.ceil(rectArea(width, height) / 100));
|
||||
}
|
||||
|
||||
function assetWidth(asset) {
|
||||
return PixelCodec.assetWidth ? PixelCodec.assetWidth(asset) : clampDimension(asset?.width ?? asset?.w ?? asset?.size, 16);
|
||||
}
|
||||
|
||||
function assetHeight(asset) {
|
||||
return PixelCodec.assetHeight ? PixelCodec.assetHeight(asset) : clampDimension(asset?.height ?? asset?.ht ?? asset?.size, assetWidth(asset));
|
||||
}
|
||||
|
||||
function assetMaxSize(asset) {
|
||||
return PixelCodec.assetMaxSize ? PixelCodec.assetMaxSize(asset) : Math.max(assetWidth(asset), assetHeight(asset));
|
||||
}
|
||||
|
||||
function clampInt(value, min, max, fallback = min) {
|
||||
const n = Math.round(Number(value));
|
||||
return PixelCodec.clampInt ? PixelCodec.clampInt(value, min, max, fallback) : (Number.isFinite(n) ? clamp(n, min, max) : fallback);
|
||||
}
|
||||
|
||||
function buildPixelBlob(encodedPixels, width, height = width) {
|
||||
return PixelCodec.buildPixelBlob(encodedPixels, width, height, PALETTE, DEFAULT_SELECTED_COLOR_CODE);
|
||||
}
|
||||
|
|
@ -274,8 +283,8 @@
|
|||
staticKindWrap: $('staticKindWrap'),
|
||||
dynamicKindWrap: $('dynamicKindWrap'),
|
||||
roleHint: $('roleHint'),
|
||||
sideSwitcher: $('sideSwitcher'), frontSideSwitcher: $('frontSideSwitcher'),
|
||||
editRight: $('editRight'), editLeft: $('editLeft'), frontRight: $('frontRight'), frontLeft: $('frontLeft'),
|
||||
sideSwitcher: $('sideSwitcher'),
|
||||
editRight: $('editRight'), editLeft: $('editLeft'),
|
||||
paintColor: $('paintColor'), toolBrush: $('toolBrush'), toolErase: $('toolErase'), toolFill: $('toolFill'), toolPick: $('toolPick'), toolLine: $('toolLine'), toolRect: $('toolRect'), toolSelect: $('toolSelect'), undoPaint: $('undoPaint'), redoPaint: $('redoPaint'),
|
||||
toolLight: $('toolLight'), toolParticle: $('toolParticle'), toolDoor: $('toolDoor'), toolDepth: $('toolDepth'), depthHigh: $('depthHigh'), depthLow: $('depthLow'), toggleAdvanced: $('toggleAdvanced'), advancedHint: $('advancedHint'), advancedToolGroup: $('advancedToolGroup'), particleDirectionWrap: $('particleDirectionWrap'), particleDirection: $('particleDirection'), particleUseSelection: $('particleUseSelection'), particleClearRange: $('particleClearRange'), particleRangeStatus: $('particleRangeStatus'), clearPaint: $('clearPaint'), flipHorizontal: $('flipHorizontal'), flipVertical: $('flipVertical'), outlinePaint: $('outlinePaint'), clearSelection: $('clearSelection'), nudgeLeft: $('nudgeLeft'), nudgeRight: $('nudgeRight'), nudgeUp: $('nudgeUp'), nudgeDown: $('nudgeDown'),
|
||||
paintCanvas: $('paintCanvas'), editHint: $('editHint'), paletteGrid: $('paletteGrid'),
|
||||
|
|
@ -323,7 +332,6 @@
|
|||
let editorPixels = blankPixels(8);
|
||||
let editorLeftPixels = blankPixels(8);
|
||||
let editingSide = 'right';
|
||||
let frontSide = 'right';
|
||||
let paintTool = 'brush';
|
||||
let selectedColorCode = DEFAULT_SELECTED_COLOR_CODE;
|
||||
let advancedDraw = false;
|
||||
|
|
@ -456,8 +464,7 @@
|
|||
});
|
||||
|
||||
els.createAccount?.addEventListener('click', () => createLocalAccount(false));
|
||||
els.placeHereButton?.addEventListener('pointerdown', (event) => { event.preventDefault(); event.stopPropagation(); confirmPreviewPlacement(); });
|
||||
els.placeHereButton?.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); if (placementPreview?.asset) confirmPreviewPlacement(); });
|
||||
els.placeHereButton?.addEventListener('click', confirmPreviewPlacement);
|
||||
|
||||
els.settingLights?.addEventListener('change', () => {
|
||||
visualSettings().enableLights = !!els.settingLights.checked;
|
||||
|
|
@ -545,8 +552,6 @@
|
|||
|
||||
els.editRight.addEventListener('click', () => setEditingSide('right'));
|
||||
els.editLeft.addEventListener('click', () => setEditingSide('left'));
|
||||
els.frontRight?.addEventListener('click', () => setFrontSide('right'));
|
||||
els.frontLeft?.addEventListener('click', () => setFrontSide('left'));
|
||||
|
||||
els.toolBrush.addEventListener('click', () => setPaintTool('brush'));
|
||||
els.toolErase.addEventListener('click', () => setPaintTool('erase'));
|
||||
|
|
@ -729,7 +734,6 @@
|
|||
els.panels.forEach((panel) => panel.classList.toggle('active', panel.id === `tab-${name}`));
|
||||
if (name !== 'draw' && advancedDraw) setAdvancedDraw(false, { redraw: false });
|
||||
else els.drawer?.classList.toggle('advancedTools', name === 'draw' && advancedDraw);
|
||||
refreshCategoryUI();
|
||||
if (keepScroll) restoreLibraryScrollState(scrollState);
|
||||
}
|
||||
|
||||
|
|
@ -801,8 +805,7 @@
|
|||
|
||||
toggleHidden(els.staticKindWrap, true);
|
||||
toggleHidden(els.dynamicKindWrap, true);
|
||||
const showDynamicDrawControls = activeTabName === 'draw' && !isStatic;
|
||||
toggleHidden(els.sideSwitcher, true); toggleHidden(els.frontSideSwitcher, !showDynamicDrawControls);
|
||||
toggleHidden(els.sideSwitcher, isStatic);
|
||||
toggleHidden(els.staticSettingsPanel, !isStatic);
|
||||
toggleHidden(els.dynamicSettingsPanel, true);
|
||||
toggleHidden(els.advancedToolGroup, !advancedDraw);
|
||||
|
|
@ -819,7 +822,6 @@
|
|||
if (!isStatic && paintTool === 'door') setPaintTool('brush');
|
||||
if (isStatic && role !== 'building' && paintTool === 'door') setPaintTool('brush');
|
||||
if (isStatic) editingSide = 'right';
|
||||
if (isStatic) frontSide = 'right';
|
||||
|
||||
updateRoleHint();
|
||||
updateSideButtons();
|
||||
|
|
@ -858,7 +860,7 @@
|
|||
} else {
|
||||
const particleText = particlePixels.length ? ` / particles ${particlePixels.length} cell${particlePixels.length === 1 ? '' : 's'}` : '';
|
||||
const sideText = editingSide === 'left' ? 'canvas marked Left; Save mirrors it into canonical Right' : 'canvas marked canonical Right';
|
||||
els.settingsSummary.textContent = `${cap(role)} / ${editorWidth}x${editorHeight} canvas / ${sideText} / ${frontSide === 'left' ? 'front starts Left' : 'front starts Right'}${particleText}.`;
|
||||
els.settingsSummary.textContent = `${cap(role)} / ${editorWidth}x${editorHeight} canvas / ${sideText}${particleText}.`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -907,23 +909,14 @@
|
|||
drawEditor();
|
||||
}
|
||||
|
||||
function setFrontSide(side) {
|
||||
if (roleToCategory(currentRole()) !== 'dynamic') return;
|
||||
frontSide = side === 'left' ? 'left' : 'right'; updateSideButtons();
|
||||
if (els.editHint) els.editHint.textContent = `Movable sprite will appear facing ${frontSide} when first placed.`;
|
||||
spriteCache.clear(); drawEditor(); render();
|
||||
}
|
||||
|
||||
function updateSideButtons() {
|
||||
if (els.editRight) els.editRight.classList.toggle('active', editingSide === 'right');
|
||||
if (els.editLeft) els.editLeft.classList.toggle('active', editingSide === 'left');
|
||||
if (els.frontRight) els.frontRight.classList.toggle('active', frontSide === 'right'); if (els.frontLeft) els.frontLeft.classList.toggle('active', frontSide === 'left');
|
||||
updateSettingsSummary();
|
||||
}
|
||||
|
||||
function onWorldPointerDown(event) {
|
||||
event.preventDefault();
|
||||
if (isPlaceHereEvent(event)) { confirmPreviewPlacement(); return; }
|
||||
const downPos = getCanvasPoint(event);
|
||||
cursorScreen = { x: downPos.x, y: downPos.y, active: true };
|
||||
if (event.button === 2) {
|
||||
|
|
@ -977,7 +970,6 @@
|
|||
}
|
||||
|
||||
function onWorldPointerUp(event) {
|
||||
if (isPlaceHereEvent(event)) { confirmPreviewPlacement(); return; }
|
||||
if (!pointer.down || pointer.id !== event.pointerId) return;
|
||||
els.canvas.classList.remove('dragging');
|
||||
const total = Math.hypot(event.clientX - pointer.startX, event.clientY - pointer.startY);
|
||||
|
|
@ -1003,11 +995,6 @@
|
|||
pointer.dragging = false;
|
||||
}
|
||||
|
||||
function isPlaceHereEvent(event) {
|
||||
if (mode !== 'place' || !placementPreview?.asset || !els.placeHereButton || els.placeHereButton.hidden) return false;
|
||||
const rect = els.placeHereButton.getBoundingClientRect(); return event.clientX >= rect.left - 8 && event.clientX <= rect.right + 8 && event.clientY >= rect.top - 8 && event.clientY <= rect.bottom + 8;
|
||||
}
|
||||
|
||||
function onWorldWheel(event) {
|
||||
event.preventDefault();
|
||||
cameraFollowSelected = false;
|
||||
|
|
@ -1697,7 +1684,7 @@
|
|||
}
|
||||
if (placementPreview?.asset && placementPreview.x != null && placementPreview.y != null) {
|
||||
const asset = placementPreview.asset;
|
||||
items.push({ kind: asset.category === 'dynamic' ? 'dynamic' : 'static', asset, x: placementPreview.x + .5, y: placementPreview.y + .5, source: { id: 'placement-preview', preview: true, selectedAt: placementPreview.selectedAt || performance.now() }, preview: true });
|
||||
items.push({ kind: asset.category === 'dynamic' ? 'dynamic' : 'static', asset, x: placementPreview.x + .5, y: placementPreview.y + .5, source: { id: 'placement-preview', preview: true }, preview: true });
|
||||
}
|
||||
items.sort(drawOrderCompare);
|
||||
return items;
|
||||
|
|
@ -1875,7 +1862,7 @@
|
|||
const tile = world.get(x, y);
|
||||
if (!canPlace(asset, tile)) return;
|
||||
const savedAssetId = placementPreview?.savedAssetId || selectedAssetId || asset.id;
|
||||
placementPreview = { asset, savedAssetId, x, y, selectedAt: performance.now() };
|
||||
placementPreview = { asset, savedAssetId, x, y };
|
||||
clearWorldSelection(false);
|
||||
syncPlacementUi();
|
||||
if (visualSettings().enableParticles) spawnEffects.push({ x: x + .5, y: y + .5, started: performance.now(), preview: true });
|
||||
|
|
@ -1898,7 +1885,6 @@
|
|||
}
|
||||
placementPreview.x = x;
|
||||
placementPreview.y = y;
|
||||
placementPreview.selectedAt = performance.now();
|
||||
if (visualSettings().enableParticles) spawnEffects.push({ x: x + .5, y: y + .5, started: performance.now(), preview: true });
|
||||
render();
|
||||
toast(adjusted.moved ? 'Preview nudged away from the remix source. Right-click or DRAW to exit.' : 'Preview set. Right-click or DRAW to exit.');
|
||||
|
|
@ -3515,7 +3501,7 @@
|
|||
} : null,
|
||||
meta: buildAssetMeta(category, subtype, aligned.depthPixels, aligned.lightPixels, selectedColorCode, aligned.door, savedWidth, aligned.particlePixels, aligned.rightPixels, savedHeight)
|
||||
};
|
||||
if (asset.meta && category === 'dynamic') asset.meta.frontSide = frontSide; asset.contentHash = computeAssetContentHash(asset);
|
||||
asset.contentHash = computeAssetContentHash(asset);
|
||||
return { asset, pixelBlob, existing, aligned };
|
||||
}
|
||||
|
||||
|
|
@ -3621,7 +3607,7 @@
|
|||
faces: category === 'dynamic' ? { right: encodedRight, left: 'mirror' } : null,
|
||||
meta: buildAssetMeta(category, subtype, aligned.depthPixels, aligned.lightPixels, selectedColorCode, aligned.door, savedWidth, aligned.particlePixels, aligned.rightPixels, savedHeight)
|
||||
};
|
||||
if (asset.meta && category === 'dynamic') asset.meta.frontSide = frontSide; return asset;
|
||||
return asset;
|
||||
}
|
||||
|
||||
function checkCurrentEditorOnIsland() {
|
||||
|
|
@ -3648,11 +3634,9 @@
|
|||
toast('Click a tile first.');
|
||||
return;
|
||||
}
|
||||
let assetId = placementPreview.savedAssetId || placementPreview.asset.id;
|
||||
if (!findAsset(assetId) && String(assetId).startsWith('preview:')) {
|
||||
const saved = saveAssetFromEditor(); if (saved) assetId = saved.id;
|
||||
}
|
||||
if (!findAsset(assetId)) { toast('Saved asset is missing. Back to canvas and save again.');
|
||||
const assetId = placementPreview.savedAssetId || placementPreview.asset.id;
|
||||
if (!findAsset(assetId)) {
|
||||
toast('Saved asset is missing. Back to canvas and save again.');
|
||||
return;
|
||||
}
|
||||
selectedAssetId = assetId;
|
||||
|
|
@ -3672,7 +3656,7 @@
|
|||
staticKind = 'nature';
|
||||
dynamicKind = 'human';
|
||||
editingSide = 'right';
|
||||
frontSide = 'right'; lightPixels = [];
|
||||
lightPixels = [];
|
||||
particlePixels = [];
|
||||
particleConfig = { enabled: false, c: selectedColorCode, dir: 'up' };
|
||||
depthPixels = blankPixels(8).map(() => 0);
|
||||
|
|
@ -4308,7 +4292,7 @@
|
|||
els.assetCategory.value = subtypeToRole(asset);
|
||||
staticKind = asset.category === 'static' ? asset.subtype : staticKind;
|
||||
dynamicKind = asset.category === 'dynamic' ? asset.subtype : dynamicKind;
|
||||
editingSide = 'right'; frontSide = asset.meta?.frontSide === 'left' ? 'left' : 'right';
|
||||
editingSide = 'right';
|
||||
const right = getAssetPixels(asset, 'right');
|
||||
lightPixels = (asset.meta?.lightPixels || []).map((p) => ({ x: p.x, y: p.y, c: p.c || asset.meta?.lightColor || selectedColorCode }));
|
||||
particlePixels = (asset.meta?.particlePixels || []).map((p) => ({ x: p.x, y: p.y, c: p.c || selectedColorCode, dir: p.dir || 'up' }));
|
||||
|
|
@ -4465,7 +4449,7 @@
|
|||
const startY = Number.isFinite(Number(synced.y)) ? Number(synced.y) : (previous ? previous.y : spawn.y + .5);
|
||||
const targetX = Number.isFinite(Number(synced.targetX)) ? Number(synced.targetX) : (previous ? previous.targetX : startX);
|
||||
const targetY = Number.isFinite(Number(synced.targetY)) ? Number(synced.targetY) : (previous ? previous.targetY : startY);
|
||||
const facing = Number.isFinite(Number(synced.facing)) && Number(synced.facing) !== 0 ? Math.sign(Number(synced.facing)) : (previous?.facing || (asset.meta?.frontSide === 'left' ? -1 : 1));
|
||||
const facing = Number.isFinite(Number(synced.facing)) && Number(synced.facing) !== 0 ? Math.sign(Number(synced.facing)) : (previous?.facing || 1);
|
||||
return {
|
||||
id: summon.id,
|
||||
assetId: summon.assetId,
|
||||
|
|
@ -5141,8 +5125,8 @@
|
|||
const runtime = item.source || {};
|
||||
const seed = Number.isFinite(runtime.seed) ? runtime.seed : 0;
|
||||
const isPreview = Boolean(item.preview || runtime.preview);
|
||||
const moveFacing = Math.abs(runtime.lastMoveX || 0) > 0.003 ? Math.sign(runtime.lastMoveX) : (runtime.facing || runtime.vx || (asset.meta?.frontSide === 'left' ? -1 : 1));
|
||||
side = isPreview ? (asset.meta?.frontSide === 'left' ? 'left' : 'right') : (moveFacing < 0 ? 'left' : 'right');
|
||||
const moveFacing = Math.abs(runtime.lastMoveX || 0) > 0.003 ? Math.sign(runtime.lastMoveX) : (runtime.facing || runtime.vx || 1);
|
||||
side = isPreview ? 'right' : (moveFacing < 0 ? 'left' : 'right');
|
||||
if (asset.subtype === 'human' || asset.subtype === 'animal') {
|
||||
const idle = isPreview || time < (runtime.idleUntil || 0);
|
||||
if (!idle) {
|
||||
|
|
@ -5182,14 +5166,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (item.preview) {
|
||||
const previewElapsed = time - (item.source?.selectedAt || item.source?.createdAt || time);
|
||||
if (previewElapsed < 720) {
|
||||
const hop = Math.sin(clamp(previewElapsed / 720, 0, 1) * Math.PI);
|
||||
const settle = Math.exp(-Math.pow((previewElapsed - 470) / 120, 2)); bob += -hop * 12; stretchX += settle * 0.28; stretchY -= settle * 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
const sprite = getSpriteCanvas(asset, side);
|
||||
const drawX = pos.x - sprite.width / 2;
|
||||
const anchorY = asset.category === 'static' ? 0 : TILE_H / 2;
|
||||
|
|
@ -6736,7 +6712,6 @@ function drawShipRipples(pos, time, seedValue = '', asset = null) {
|
|||
.filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y))
|
||||
: [];
|
||||
const normalized = buildAssetMeta(category, subtype, normalizedDepth, lightPixels, meta.lightColor || '#ffd86a', meta.door || null, width, legacyParticlePixels, sourcePixels, height);
|
||||
if (category === 'dynamic') normalized.frontSide = meta.frontSide === 'left' ? 'left' : 'right';
|
||||
return normalized;
|
||||
}
|
||||
|
||||
|
|
@ -7125,7 +7100,6 @@ function drawShipRipples(pos, time, seedValue = '', asset = null) {
|
|||
JSON.stringify(asset.meta?.lightPixels || []),
|
||||
JSON.stringify(asset.meta?.particlePixels || []),
|
||||
JSON.stringify(asset.meta?.particleConfig || null),
|
||||
asset.meta?.frontSide || '',
|
||||
asset.meta?.door ? `${asset.meta.door.x},${asset.meta.door.y}` : ''
|
||||
].join('|');
|
||||
return `fnv1a:${fnv1a(payload)}`;
|
||||
|
|
@ -7697,4 +7671,4 @@ function drawShipRipples(pos, time, seedValue = '', asset = null) {
|
|||
// Pixel Island default-art drawing code was moved out of app.js.
|
||||
window.PixelIslandDebug = { ...(window.PixelIslandDebug || {}), applySyncEvent };
|
||||
bootstrap();
|
||||
})();
|
||||
})();
|
||||
Binary file not shown.
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Pixel Island</title>
|
||||
<link rel="stylesheet" href="./styles.css?v=20260605-2" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
|
|
@ -71,11 +71,6 @@
|
|||
<button id="editLeft" title="Left-facing sprite">◀ Left</button>
|
||||
<button id="editRight" class="active" title="Right-facing sprite is the main/front direction">▶ Right</button>
|
||||
</div>
|
||||
<div id="frontSideSwitcher" class="sideSwitcher frontSideSwitcher" hidden aria-label="Front-facing side">
|
||||
<span class="sideSwitcherLabel">Front</span>
|
||||
<button id="frontLeft" title="Show left side first">◀ Left</button>
|
||||
<button id="frontRight" class="active" title="Show right side first">▶ Right</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="paintLayout">
|
||||
|
|
|
|||
46
styles.css
46
styles.css
|
|
@ -1,11 +1,3 @@
|
|||
@font-face {
|
||||
font-family: "PixelIslandServiceFont";
|
||||
src: url("./assets/fonts/PixelMplus10-Regular.ttf?v=20260605-2") format("truetype");
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
:root {
|
||||
--ink: #243044;
|
||||
--muted: #6f7b91;
|
||||
|
|
@ -33,7 +25,7 @@ body {
|
|||
margin: 0;
|
||||
color: var(--ink);
|
||||
background: #92d8ff;
|
||||
font-family: "PixelIslandServiceFont", sans-serif;
|
||||
font-family: "3x5 MT Pixel Font", "Press Start 2P", "PixelMplus10", "DotGothic16", "Courier New", "Monaco", "Lucida Console", ui-monospace, monospace;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
|
@ -423,18 +415,6 @@ input[type="checkbox"] { width: auto; margin: 0 8px 0 0; }
|
|||
align-items: center;
|
||||
}
|
||||
.sideSwitcher button { padding: 6px 7px; font-size: 11px; }
|
||||
.compactEditorTop {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.sideSwitcherLabel {
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
color: #334155;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.toolRow {
|
||||
display: grid;
|
||||
|
|
@ -750,6 +730,7 @@ button:disabled, .tool:disabled { opacity: .45; cursor: not-allowed; transform:
|
|||
.heroEditor .hint { line-height: 1.35; }
|
||||
|
||||
.compactEditorTop { margin-bottom: 4px; min-height: 0; }
|
||||
body, button, input, select, textarea { font-smooth: never; -webkit-font-smoothing: none; }
|
||||
|
||||
.studioDrawer { width: min(600px, calc(100vw - 24px)); }
|
||||
.paintLayout {
|
||||
|
|
@ -865,6 +846,10 @@ button:disabled, .tool:disabled { opacity: .45; cursor: not-allowed; transform:
|
|||
background: var(--sun);
|
||||
}
|
||||
|
||||
body, button, input, select, textarea {
|
||||
font-family: "Courier New", "Lucida Console", Monaco, monospace;
|
||||
letter-spacing: .02em;
|
||||
}
|
||||
button, .tab, .tool, .iconButton, .cardTitle, .logo {
|
||||
text-transform: uppercase;
|
||||
font-weight: 900;
|
||||
|
|
@ -903,6 +888,12 @@ button, .tab, .tool, .iconButton, .cardTitle, .logo {
|
|||
color: var(--muted);
|
||||
}
|
||||
|
||||
body, button, input, select, textarea {
|
||||
font-family: "MS Gothic", "Osaka-Mono", "DotumChe", "Courier New", ui-monospace, monospace;
|
||||
letter-spacing: .02em;
|
||||
-webkit-font-smoothing: none;
|
||||
text-rendering: geometricPrecision;
|
||||
}
|
||||
.toolRow { grid-template-columns: repeat(6, minmax(0, 1fr)); }
|
||||
.editorHistoryRow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.advancedRow {
|
||||
|
|
@ -991,6 +982,9 @@ button, .tab, .tool, .iconButton, .cardTitle, .logo {
|
|||
grid-column: span 2;
|
||||
}
|
||||
|
||||
body, button, input, select, textarea {
|
||||
font-family: "3x5 MT Pixel Font", "Press Start 2P", "PixelMplus10", "DotGothic16", "MS Gothic", "Osaka-Mono", "Courier New", ui-monospace, monospace;
|
||||
}
|
||||
.hiddenSectionTitle {
|
||||
margin: 8px 0 6px;
|
||||
padding: 5px 7px;
|
||||
|
|
@ -2937,8 +2931,6 @@ body, button, input, select, textarea { font-size: 15px; }
|
|||
font-weight: 900 !important;
|
||||
letter-spacing: .04em;
|
||||
cursor: pointer;
|
||||
pointer-events: auto !important;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
.placeHereButton[hidden] { display: none !important; }
|
||||
#confirmDialog.reportDialog { position: fixed !important; z-index: 10000 !important; }
|
||||
|
|
@ -2951,7 +2943,7 @@ body, button, input, select, textarea { font-size: 15px; }
|
|||
.basicEditorTools,
|
||||
.studioDrawer.advancedTools .editorToolRow,
|
||||
.studioDrawer.advancedTools .basicEditorTools {
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr)) !important;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr)) !important;
|
||||
}
|
||||
.basicEditorTools #toolBrush {
|
||||
background: linear-gradient(180deg, #dcfce7, #86efac) !important;
|
||||
|
|
@ -2978,9 +2970,3 @@ body, button, input, select, textarea { font-size: 15px; }
|
|||
outline-offset: -6px !important;
|
||||
color: #111827 !important;
|
||||
}
|
||||
.basicEditorTools #clearPaint {
|
||||
border-right: 2px solid var(--line, #243044) !important;
|
||||
}
|
||||
.studioDrawer .compactEditorTop {
|
||||
display: flex !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<remove fileExtension=".ttf" />
|
||||
<mimeMap fileExtension=".ttf" mimeType="font/ttf" />
|
||||
</staticContent>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
Loading…
Add table
Add a link
Reference in a new issue