Back to Black Site
SOURCE / PINNED RELEASE

Made of little things.

Black Site

Release
666bc7c74ebd…
Author-recorded commit
05e95af79676…
License
LICENSE
Author’s source reference
nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-d2225ddc391

Archive hash verified: 1544e072e245c4a8…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/game-bridge.js
// App-owned adapter appended inside bootGame's shared scope. Game source sync leaves it intact.
const progressSnapshot = () => JSON.stringify([PROFILE.unlocked, PROFILE.armours, PROFILE.levels, PROFILE.armourLevels]);
let lastProgress = progressSnapshot();
$('shop').addEventListener('click', () => {
  const progress = progressSnapshot();
  if (progress !== lastProgress) {
    lastProgress = progress;
    window.dispatchEvent(new Event('blacksite:armory'));
  }
});
let deferredArmor = null;
const choices = () => ({ character: CHAR_ID, loadout: { ...LOADOUT }, equippedArmor: deferredArmor || PROFILE.armour || null, difficulty: DIFF_ID, map: MAP_ID });
let lastChoices = JSON.stringify(choices());
const choicesChanged = e => {
  if (e.target.closest && e.target.closest('[data-armour]')) deferredArmor = null;
  const snapshot = JSON.stringify(choices());
  if (snapshot !== lastChoices) { lastChoices = snapshot; window.dispatchEvent(new Event('blacksite:customization')); }
};
for (const id of ['character', 'loadout', 'maps', 'shop']) $(id).addEventListener('click', choicesChanged);
$('diffSlider').addEventListener('input', choicesChanged);
const originalShow = UI.show;
UI.show = function(id) {
  originalShow.call(UI, id);
  window.dispatchEvent(new CustomEvent('blacksite:screen', { detail: id }));
};
const refreshMenu = () => {
  const visible = document.querySelector('.screen.on')?.id;
  UI.menu();
  if (visible && visible !== 'menu') originalShow.call(UI, visible);
};
return {
  isSafe: () => G === null,
  profile: () => clone(PROFILE),
  customization: () => clone(choices()),
  applyCustomization(value) {
    if (G !== null) throw new Error('game-busy');
    CHAR_ID = value.character;
    LOADOUT = { ...value.loadout };
    DIFF_ID = value.difficulty;
    MAP_ID = value.map;
    deferredArmor = value.equippedArmor && !PROFILE.armours.includes(value.equippedArmor) ? value.equippedArmor : null;
    PROFILE.armour = deferredArmor ? null : value.equippedArmor;
    store.set('blacksite.character', CHAR_ID);
    store.set('blacksite.loadout', LOADOUT);
    store.set('blacksite.difficulty', DIFF_ID);
    store.set('blacksite.map', MAP_ID);
    saveProfile();
    lastChoices = JSON.stringify(choices());
    if (MODE === 'menu') refreshMenu();
    else if (MODE === 'character') renderCharacters();
    else if (MODE === 'loadout') renderLoadout();
    else if (MODE === 'maps') renderMaps();
    else if (MODE === 'shop') renderShop();
  },
  applyArmory(armory) {
    if (G !== null) throw new Error('game-busy');
    PROFILE.unlocked = [...armory.unlockedThrowables];
    PROFILE.armours = [...armory.ownedArmors];
    PROFILE.levels = { ...armory.weaponLevels };
    PROFILE.armourLevels = { ...armory.armorLevels };
    if (deferredArmor && PROFILE.armours.includes(deferredArmor)) {
      PROFILE.armour = deferredArmor; deferredArmor = null;
    }
    lastProgress = progressSnapshot();
    lastChoices = JSON.stringify(choices());
    saveProfile();
    if (MODE === 'shop') renderShop();
    else if (MODE === 'loadout') renderLoadout();
    else if (MODE === 'menu') refreshMenu();
  },
  reloadSaves() {
    if (G !== null) throw new Error('game-busy');
    for (const key of Object.keys(PROFILE)) delete PROFILE[key];
    Object.assign(PROFILE, { bank: 0, levels: {}, armours: [], armour: null, unlocked: [], armourLevels: {}, armourBonus: {} }, store.get('blacksite.profile', {}));
    LOADOUT = store.get('blacksite.loadout', null) || { primary: 'm4a1', secondary: 'm1911', throwable: 'frag' };
    if (!PRIMARY_IDS.includes(LOADOUT.primary)) LOADOUT.primary = 'm4a1';
    if (!SECONDARY_IDS.includes(LOADOUT.secondary)) LOADOUT.secondary = 'm1911';
    if (!THROWABLE_IDS.includes(LOADOUT.throwable)) LOADOUT.throwable = null;
    MAP_ID = store.get('blacksite.map', 'town');
    if (!MAPS.some(map => map.id === MAP_ID)) MAP_ID = 'town';
    CAMPAIGN = store.get('blacksite.campaign', null);
    CHAR_ID = store.get('blacksite.character', 'assaulter');
    if (!CHARACTERS[CHAR_ID]) CHAR_ID = 'assaulter';
    DIFF_ID = store.get('blacksite.difficulty', 'normal');
    if (!DIFFICULTIES[DIFF_ID]) DIFF_ID = 'normal';
    chargeCampaignQuit();
    deferredArmor = null; lastChoices = JSON.stringify(choices());
    lastProgress = progressSnapshot();
    UI.menu();
  },
};