Initial commit — Meridian OS browser desktop demo

This commit is contained in:
2026-04-19 23:18:55 +00:00
commit 8e30dd3597
30 changed files with 6277 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
// Achievement definitions for Meridian OS
const catalog = [
{
id: 'first_grace',
title: 'First Grace',
desc: 'Welcome to the Lands Between.',
game: 'ER',
trigger: { event: 'user:login' }
},
{
id: 'getting_tactical',
title: 'Getting Tactical',
desc: 'Run 5 terminal commands.',
game: 'CoD',
trigger: { event: 'term:cmd', count: 5 }
},
{
id: 'career_mode',
title: 'Career Mode',
desc: 'Open 3 different apps.',
game: 'FIFA',
trigger: { event: 'app:open', uniqueField: 'app', count: 3 }
},
{
id: 'tarnished_typist',
title: 'Tarnished Typist',
desc: 'Type 500 characters in Notepad.',
game: 'ER',
trigger: { event: 'text:typed', accumulate: 'chars', threshold: 500 }
},
{
id: 'dj',
title: 'DJ',
desc: 'Play music for 30 seconds.',
game: 'CoD',
trigger: { event: 'music:played', accumulate: 'seconds', threshold: 30 }
},
{
id: 'speedrun',
title: 'Speedrun',
desc: 'Open and close a window in under 2 seconds.',
game: 'FIFA',
trigger: { custom: 'speedrun' }
},
{
id: 'dataminer',
title: 'Dataminer',
desc: 'Discover 3 hidden terminal commands.',
game: 'ER',
trigger: { event: 'easter:found', count: 3, unique: true }
},
{
id: 'grace_bestowed',
title: 'Touched by Grace',
desc: 'Praise the Sun.',
game: 'ER',
trigger: { event: 'easter:found', id: 'er_grace' }
},
{
id: 'siuuu_master',
title: 'SIUUU Master',
desc: 'Celebrate in the terminal.',
game: 'FIFA',
trigger: { event: 'easter:found', id: 'er_siuuu' }
},
{
id: 'cheater',
title: 'Cheat Code Activated',
desc: 'Enter the Konami code.',
game: 'CoD',
trigger: { event: 'easter:found', id: 'er_konami' }
}
];
export default catalog;

128
js/achievements/engine.js Normal file
View File

@@ -0,0 +1,128 @@
// Achievements engine — subscribes to bus events, unlocks achievements.
// On unlock: pushes id into State, emits 'achievement:unlocked' for the widget.
import Bus from '../core/bus.js';
import State from '../core/state.js';
import catalog from './catalog.js';
// In-memory counters per achievement id
const counters = {};
// For uniqueField tracking: Set of seen values per achievement id
const uniqueSets = {};
// For speedrun: map of windowId -> open timestamp
const openTimestamps = {};
let initialized = false;
function hasUnlocked(id) {
const unlocked = State.get('achievements');
return unlocked && unlocked.includes(id);
}
function unlock(ach) {
if (hasUnlocked(ach.id)) return;
State.addAchievement(ach.id);
Bus.emit('achievement:unlocked', {
id: ach.id,
title: ach.title,
desc: ach.desc,
game: ach.game
});
}
function handleEvent(ach, data) {
const trigger = ach.trigger;
// Event+id specific trigger (easter egg matches)
if (trigger.id) {
if (data && data.id === trigger.id) {
unlock(ach);
}
return;
}
// Simple event trigger (no count, no accumulate) — unlock on first fire
if (!trigger.count && !trigger.accumulate && !trigger.uniqueField) {
unlock(ach);
return;
}
// Count-based trigger
if (trigger.count) {
counters[ach.id] = (counters[ach.id] || 0) + 1;
if (counters[ach.id] >= trigger.count) {
unlock(ach);
}
return;
}
// UniqueField trigger
if (trigger.uniqueField) {
if (!uniqueSets[ach.id]) {
uniqueSets[ach.id] = new Set();
}
const val = data && data[trigger.uniqueField];
if (val) {
uniqueSets[ach.id].add(val);
if (uniqueSets[ach.id].size >= trigger.count) {
unlock(ach);
}
}
return;
}
// Accumulate trigger
if (trigger.accumulate) {
const key = trigger.accumulate;
const value = data && data[key];
if (typeof value === 'number') {
counters[ach.id] = (counters[ach.id] || 0) + value;
if (counters[ach.id] >= trigger.threshold) {
unlock(ach);
}
}
return;
}
}
function init() {
if (initialized) return;
initialized = true;
// Pre-initialize counters for all achievements
catalog.forEach(ach => {
counters[ach.id] = 0;
uniqueSets[ach.id] = ach.trigger.uniqueField ? new Set() : undefined;
});
// Subscribe to each achievement's trigger event
catalog.forEach(ach => {
const trigger = ach.trigger;
if (trigger.custom === 'speedrun') {
// Speedrun: listen to app:open and app:close on wm.js
Bus.on('app:open', (data) => {
if (data && data.id) {
openTimestamps[data.id] = Date.now();
}
});
Bus.on('app:close', (data) => {
if (data && data.id && openTimestamps[data.id]) {
const delta = Date.now() - openTimestamps[data.id];
if (delta < 2000) {
unlock(ach);
}
delete openTimestamps[data.id];
}
});
return;
}
if (trigger.event) {
Bus.on(trigger.event, (data) => {
handleEvent(ach, data);
});
}
});
}
export default { init };