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.
// 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();
},
};
