diff --git a/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte b/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte
index a4295c6..c6362de 100644
--- a/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte
+++ b/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte
@@ -1,18 +1,18 @@
diff --git a/client/src/lib/Apps/appList.ts b/client/src/lib/Apps/appList.ts
index 2a8da94..dcf9be5 100644
--- a/client/src/lib/Apps/appList.ts
+++ b/client/src/lib/Apps/appList.ts
@@ -1,46 +1,53 @@
-import Camera from './Camera/Camera.svelte'
-import MusicBrowser from './MusicBrowser/MusicBrowser.svelte'
-import Settings from './Settings/Settings.svelte'
+import Camera from "./Camera/Camera.svelte";
+import MusicBrowser from "./MusicBrowser/MusicBrowser.svelte";
+import Settings from "./Settings/Settings.svelte";
-type Format = 'png' | 'jpg' | 'webp'
+type Format = "png" | "jpg" | "webp";
const resolveIconPath = (slug: keyof typeof appList, format: Format) => {
- return `/static/app-icons/${slug}.${format}`
-}
+ return `/static/app-icons/${slug}.${format}`;
+};
-import GBAEmulator from './GBAEmulator/GBAEmulator.svelte'
+import GBAEmulator from "./GBAEmulator/GBAEmulator.svelte";
+import JsDoom from "./JSDoom/JSDoom.svelte";
interface AppList {
[key: string]: {
- name: string
- component: any
- icon: string
- external: boolean
- }
+ name: string;
+ component: any;
+ icon: string;
+ external: boolean;
+ };
}
export const appList: AppList = {
- 'camera': {
- name: 'Camera',
+ camera: {
+ name: "Camera",
component: Camera,
- icon: resolveIconPath('camera', 'png'),
+ icon: resolveIconPath("camera", "png"),
external: false,
},
- 'media-player': {
- name: 'Media Player',
+ "media-player": {
+ name: "Media Player",
component: MusicBrowser,
- icon: resolveIconPath('media-player', 'png'),
+ icon: resolveIconPath("media-player", "png"),
external: false,
},
- 'settings': {
- name: 'Settings',
- component: Settings,
- icon: resolveIconPath('settings', 'webp'),
- external: false,
- },
- 'gba-emulator': {
- name: 'GBA Emulator',
+ "gba-emulator": {
+ name: "GBA Emulator",
component: GBAEmulator,
- icon: resolveIconPath('gba-emulator', 'png'),
+ icon: resolveIconPath("gba-emulator", "png"),
external: true,
},
-}
+ jsdoom: {
+ name: "Doom",
+ component: JsDoom,
+ icon: resolveIconPath("jsdoom", "png"),
+ external: true,
+ },
+ settings: {
+ name: "Settings",
+ component: Settings,
+ icon: resolveIconPath("settings", "webp"),
+ external: false,
+ },
+};
diff --git a/client/src/lib/Sequences/sequences.ts b/client/src/lib/Sequences/sequences.ts
index e07712a..72840de 100644
--- a/client/src/lib/Sequences/sequences.ts
+++ b/client/src/lib/Sequences/sequences.ts
@@ -14,42 +14,47 @@ Sequences should be either event-driven or periodic. In the case of periodic
sequences, invoke them in the periodicSequence function
*/
-import { Notifications } from '../Notifications/notifications'
-import { sequenceStore } from '../stores/sequenceStore'
-import { settingsStore } from '../stores/settingsStore'
-import { get } from 'svelte/store'
-import getVoicePath from '../utils/getVoicePath'
-import { tick } from 'svelte'
+import { Notifications } from "../Notifications/notifications";
+import { sequenceStore } from "../stores/sequenceStore";
+import { settingsStore } from "../stores/settingsStore";
+import { get } from "svelte/store";
+import getVoicePath from "../utils/getVoicePath";
+import { tick } from "svelte";
// await a "tick" (a svelte update frame) at the start of every sequence so that
// state is synced and no weird side effects occur
export const initializationSequence = async () => {
- await tick()
- Notifications.info('Jankboard initialized!', {
+ await tick();
+ Notifications.info("Jankboard initialized!", {
withAudio: true,
- src: getVoicePath('jankboard-initialized', 'en'),
- })
+ src: getVoicePath("jankboard-initialized", "en"),
+ });
setTimeout(() => {
- if (get(settingsStore).goWoke) return
- Notifications.success('LittenOS is online', {
+ if (get(settingsStore).goWoke) return;
+ Notifications.success("LittenOS is online", {
withAudio: true,
- src: getVoicePath('littenos-is-online', 'en'),
- })
+ src: getVoicePath("littenos-is-online", "en"),
+ });
setTimeout(() => {
- Notifications.warn('Breaching Monte Vista codebase', {
+ Notifications.warn("Breaching Monte Vista codebase", {
withAudio: true,
- src: getVoicePath('breaching-monte-vista', 'en'),
- })
+ src: getVoicePath("breaching-monte-vista", "en"),
+ });
setTimeout(() => {
- Notifications.playAudio(getVoicePath('hello-virtual-assistant', 'en'))
- periodicSequence()
- }, 3000)
- }, 3000)
- }, 3000)
-}
+ Notifications.playAudio(
+ getVoicePath("hello-virtual-assistant", "en"),
+ () => {
+ sequenceStore.update("initializationComplete", true);
+ periodicSequence();
+ }
+ );
+ }, 3000);
+ }, 3000);
+ }, 3000);
+};
-let counter = 1
+let counter = 1;
/**
* Special sequence that plays invokes itself periodically, started automatically
* at the end of the initializationSequence
@@ -59,7 +64,7 @@ let counter = 1
* @return void
*/
const periodicSequence = async () => {
- await tick()
+ await tick();
/**
* Returns either true or false based on the provided probability
@@ -69,11 +74,11 @@ const periodicSequence = async () => {
*/
const chance = (probability: number) => {
if (probability < 0 || probability > 1) {
- throw new Error('Probability must be between 0 and 1')
+ throw new Error("Probability must be between 0 and 1");
}
- return Math.random() < probability
- }
+ return Math.random() < probability;
+ };
/**
* Calls a callback function at regular intervals.
@@ -82,119 +87,134 @@ const periodicSequence = async () => {
* @param callback - the function to call
*/
const every = (seconds: number, callback: () => void) => {
- if (counter % seconds === 0) callback()
- }
+ if (counter % seconds === 0) callback();
+ };
// add your periodic sequences here
- every(10, () => {
- if (chance(0.2)) breaching1323Sequence()
- else if (chance(0.2)) breaching254Sequence()
- else if (chance(0.05)) bullyingRohanSequence()
- })
+ every(15, () => {
+ if (chance(0.2)) breaching1323Sequence();
+ else if (chance(0.2)) breaching254Sequence();
+ else if (chance(0.05)) bullyingRohanSequence();
+ else if (chance(0.1)) bypassCoprocessorRestrictionsSequence();
+ });
// Dont touch
- counter++
- setTimeout(periodicSequence, 1000)
-}
+ counter++;
+ setTimeout(periodicSequence, 1000);
+};
export const criticalFailureIminentSequence = async () => {
- await tick()
- Notifications.error('Critical robot failure imminent', {
+ await tick();
+ Notifications.error("Critical robot failure imminent", {
withAudio: true,
- src: getVoicePath('critical-robot-failure', 'en'),
- })
-}
+ src: getVoicePath("critical-robot-failure", "en"),
+ });
+};
export const collisionDetectedSequence = async () => {
- await tick()
- Notifications.error('Collision detected', {
+ await tick();
+ Notifications.error("Collision detected", {
withAudio: true,
- src: getVoicePath('collision-detected', 'en'),
- })
-}
+ src: getVoicePath("collision-detected", "en"),
+ });
+};
export const collisionImminentSequence = async () => {
- await tick()
- Notifications.error('Collision imminent', {
+ await tick();
+ Notifications.error("Collision imminent", {
withAudio: true,
- src: getVoicePath('collision-imminent', 'en'),
- })
-}
+ src: getVoicePath("collision-imminent", "en"),
+ });
+};
export const cruiseControlEngagedSequence = async () => {
- if (get(settingsStore).disableAnnoyances) return
- await tick()
- Notifications.success('Cruise control engaged', {
+ if (get(settingsStore).disableAnnoyances) return;
+ await tick();
+ Notifications.success("Cruise control engaged", {
withAudio: true,
- src: getVoicePath('cruise-control-engaged', 'en'),
- })
-}
+ src: getVoicePath("cruise-control-engaged", "en"),
+ });
+};
export const retardSequence = async () => {
- if (get(settingsStore).goWoke) return
- await tick()
- Notifications.warn('Retard', {
+ if (get(settingsStore).goWoke) return;
+ await tick();
+ Notifications.warn("Retard", {
withAudio: true,
- src: getVoicePath('retard', 'en'),
- })
-}
+ src: getVoicePath("retard", "en"),
+ });
+};
const breaching254Sequence = async () => {
- if (get(settingsStore).disableAnnoyances) return
- await tick()
- Notifications.warn('Breaching 254 mainframe', {
+ if (get(settingsStore).disableAnnoyances) return;
+ await tick();
+ Notifications.warn("Breaching 254 mainframe", {
withAudio: true,
- src: getVoicePath('breaching-254-mainframe', 'en'),
- })
-}
+ src: getVoicePath("breaching-254-mainframe", "en"),
+ });
+};
const breaching1323Sequence = async () => {
- if (get(settingsStore).disableAnnoyances) return
- await tick()
- Notifications.warn('Breaching 1323 mainframe', {
+ if (get(settingsStore).disableAnnoyances) return;
+ await tick();
+ Notifications.warn("Breaching 1323 mainframe", {
withAudio: true,
- src: getVoicePath('breaching-1323-mainframe', 'en'),
- })
-}
+ src: getVoicePath("breaching-1323-mainframe", "en"),
+ });
+};
const bullyingRohanSequence = async () => {
- if (get(settingsStore).disableAnnoyances) return
- await tick()
- Notifications.info('Bullying Rohan', {
+ if (get(settingsStore).disableAnnoyances) return;
+ await tick();
+ Notifications.info("Bullying Rohan", {
withAudio: true,
- src: getVoicePath('bullying-rohan', 'en'),
- })
-}
+ src: getVoicePath("bullying-rohan", "en"),
+ });
+};
export const userErrorDetectedSequence = async () => {
- await tick()
- Notifications.error('User error detected', {
+ await tick();
+ Notifications.error("User error detected", {
withAudio: true,
- src: getVoicePath('user-error-detected', 'en'),
- })
-}
+ src: getVoicePath("user-error-detected", "en"),
+ });
+};
export const infotainmentBootupSequence = async () => {
if (
get(sequenceStore).infotainmentStartedFirstTime ||
get(settingsStore).disableAnnoyances
)
- return
+ return;
- await tick()
+ await tick();
- sequenceStore.update('infotainmentStartedFirstTime', true)
-
- Notifications.info('Infotainment system buffering', {
- withAudio: true,
- src: getVoicePath('infotainment-system-buffering', 'en'),
- })
- setTimeout(() => {
- Notifications.success('Infotainment system online', {
+ const sequence = () => {
+ Notifications.info("Infotainment system buffering", {
withAudio: true,
- src: getVoicePath('infotainment-system-online', 'en'),
- })
- }, 3000)
-}
+ src: getVoicePath("infotainment-system-buffering", "en"),
+ });
+ setTimeout(() => {
+ Notifications.success("Infotainment system online", {
+ withAudio: true,
+ src: getVoicePath("infotainment-system-online", "en"),
+ onComplete: () => {
+ sequenceStore.update("infotainmentStartedFirstTime", true);
+ },
+ });
+ }, 3000);
+ };
+
+ if (!get(sequenceStore).initializationComplete) {
+ const unsubscribe = sequenceStore.subscribe((data) => {
+ if (data.initializationComplete) {
+ sequence();
+ unsubscribe();
+ }
+ });
+ } else {
+ sequence();
+ }
+};
/**
* Waits for the infotainment system to boot up before executing the given sequence.
@@ -202,52 +222,80 @@ export const infotainmentBootupSequence = async () => {
* If it's already booted, the sequence will be executed immediately.
*
* @param sequence - The sequence to execute after infotainment bootup, or immediately it already booted.
- * @param delay? - The delay in milliseconds to wait if infotainment system is currently booting. Defaults to 5000ms
*/
-const waitForInfotainmentBootup = (
- sequence: () => void,
- delay: number = 5000
-) => {
+const waitForInfotainmentBootup = (sequence: () => void) => {
if (!get(sequenceStore).infotainmentStartedFirstTime) {
- setTimeout(sequence, delay)
+ const unsubscribe = sequenceStore.subscribe((data) => {
+ if (data.infotainmentStartedFirstTime) {
+ sequence();
+ unsubscribe();
+ }
+ });
} else {
- sequence()
+ sequence();
}
-}
+};
export const musicPlayerBootupSequence = async () => {
if (
get(sequenceStore).musicStartedFirstTime ||
get(settingsStore).disableAnnoyances
)
- return
+ return;
- await tick()
+ await tick();
- sequenceStore.update('musicStartedFirstTime', true)
+ sequenceStore.update("musicStartedFirstTime", true);
waitForInfotainmentBootup(() => {
- Notifications.info('Downloading copyrighted music...', {
+ Notifications.info("Downloading copyrighted music...", {
withAudio: true,
- src: getVoicePath('downloading-copyrighted-music', 'en'),
- })
- })
-}
+ src: getVoicePath("downloading-copyrighted-music", "en"),
+ });
+ });
+};
export const gbaEmulatorBootupSequence = async () => {
if (
get(sequenceStore).gbaEmulatorStartedFirstTime ||
get(settingsStore).disableAnnoyances
)
- return
+ return;
- await tick()
- sequenceStore.update('gbaEmulatorStartedFirstTime', true)
+ await tick();
+ sequenceStore.update("gbaEmulatorStartedFirstTime", true);
waitForInfotainmentBootup(() => {
- Notifications.info('Loading pirated Nintendo ROMs', {
+ Notifications.info("Loading pirated Nintendo ROMs", {
withAudio: true,
- src: getVoicePath('loading-pirated-nintendo', 'en'),
- })
- })
-}
+ src: getVoicePath("loading-pirated-nintendo", "en"),
+ });
+ });
+};
+
+export const doomBootupSequence = async () => {
+ if (
+ get(sequenceStore).doomStartedFirstTime ||
+ get(settingsStore).disableAnnoyances
+ )
+ return;
+
+ await tick();
+ sequenceStore.update("doomStartedFirstTime", true);
+
+ waitForInfotainmentBootup(() => {
+ Notifications.success("Doom Engaged", {
+ withAudio: true,
+ src: getVoicePath("doom-engaged", "en"),
+ });
+ });
+};
+
+const bypassCoprocessorRestrictionsSequence = async () => {
+ if (get(settingsStore).disableAnnoyances) return;
+ await tick();
+ Notifications.warn("Bypassing coprocessor restrictions", {
+ withAudio: true,
+ src: getVoicePath("bypassing-coprocessor-restrictions", "en"),
+ });
+};
diff --git a/client/src/lib/stores/sequenceStore.ts b/client/src/lib/stores/sequenceStore.ts
index fd17ad3..60f6adb 100644
--- a/client/src/lib/stores/sequenceStore.ts
+++ b/client/src/lib/stores/sequenceStore.ts
@@ -1,34 +1,38 @@
/* in this store, put stateful variables that sequences need to access */
-import { writable } from 'svelte/store'
+import { writable } from "svelte/store";
interface SequenceStoreData {
- infotainmentStartedFirstTime: boolean
- musicStartedFirstTime: boolean
- gbaEmulatorStartedFirstTime: boolean
+ initializationComplete: boolean;
+ infotainmentStartedFirstTime: boolean;
+ musicStartedFirstTime: boolean;
+ gbaEmulatorStartedFirstTime: boolean;
+ doomStartedFirstTime: boolean;
}
let defaults: SequenceStoreData = {
+ initializationComplete: false,
infotainmentStartedFirstTime: false, // for infotainment bootup sequence
musicStartedFirstTime: false,
gbaEmulatorStartedFirstTime: false,
-}
+ doomStartedFirstTime: false,
+};
const createSequenceStore = () => {
- const { subscribe, set, update } = writable(defaults)
+ const { subscribe, set, update } = writable(defaults);
return {
subscribe,
update: (
data: keyof SequenceStoreData,
newValue: SequenceStoreData[typeof data]
) => {
- update(store => {
- store[data] = newValue
- return store
- })
+ update((store) => {
+ store[data] = newValue;
+ return store;
+ });
},
reset: () => set(defaults),
- }
-}
+ };
+};
-export const sequenceStore = createSequenceStore()
+export const sequenceStore = createSequenceStore();
diff --git a/client/src/lib/stores/settingsStore.ts b/client/src/lib/stores/settingsStore.ts
index 7599db7..917bcfd 100644
--- a/client/src/lib/stores/settingsStore.ts
+++ b/client/src/lib/stores/settingsStore.ts
@@ -1,35 +1,35 @@
/* stores global app wide settings */
-import { writable } from 'svelte/store'
+import { writable } from "svelte/store";
export interface SettingsStoreData {
- disableAnnoyances: boolean
- goWoke: boolean
- fastStartup: boolean
+ disableAnnoyances: boolean;
+ goWoke: boolean;
+ fastStartup: boolean;
}
export const defaults: SettingsStoreData = {
disableAnnoyances: false, // disable non-critical notifications
goWoke: false, // go woke (for showing parents or other officials where DEI has taken over), disables "offensive" sequences
- fastStartup: true, // skip the loading splash screen (for development purposes. Setting this from within the app has no effect.)
-}
+ fastStartup: false, // skip the loading splash screen (for development purposes. Setting this from within the app has no effect.)
+};
const createSequenceStore = () => {
- const { subscribe, set, update } = writable(defaults)
+ const { subscribe, set, update } = writable(defaults);
return {
subscribe,
update: (
data: keyof SettingsStoreData,
newValue: SettingsStoreData[typeof data]
) => {
- update(store => {
- store[data] = newValue
- return store
- })
+ update((store) => {
+ store[data] = newValue;
+ return store;
+ });
},
reset: () => set(defaults),
set: (data: SettingsStoreData) => set(data),
- }
-}
+ };
+};
-export const settingsStore = createSequenceStore()
+export const settingsStore = createSequenceStore();