mirror of
https://github.com/gradenGnostic/LegacyLauncher.git
synced 2026-04-29 10:25:45 +00:00
v2.2.0
This commit is contained in:
parent
69a0ebbfce
commit
46a484d5df
25
index.html
25
index.html
|
|
@ -35,7 +35,10 @@
|
|||
</div>
|
||||
|
||||
<div class="content-area">
|
||||
<img src="minecraftlogo.png" class="mc-logo-img" alt="Minecraft Logo">
|
||||
<div class="relative">
|
||||
<img src="minecraftlogo.png" class="mc-logo-img" alt="Minecraft Logo">
|
||||
<div id="splash-text" class="splash-text">Splash!</div>
|
||||
</div>
|
||||
|
||||
<div id="btn-play-main" class="btn-mc btn-play nav-item" onclick="launchGame()" tabindex="0">PLAY</div>
|
||||
|
||||
|
|
@ -69,7 +72,7 @@
|
|||
</div>
|
||||
|
||||
<div class="modal-overlay" id="servers-modal">
|
||||
<div class="modal-box" style="width: 850px;">
|
||||
<div class="modal-box">
|
||||
<div class="modal-title">CUSTOM SERVERS</div>
|
||||
|
||||
<div id="servers-list-container" class="w-full max-h-[300px] overflow-y-auto mb-6 border-2 border-[#555] bg-black p-2">
|
||||
|
|
@ -104,6 +107,15 @@
|
|||
<div class="modal-box">
|
||||
<div class="modal-title">LAUNCHER OPTIONS</div>
|
||||
|
||||
<div class="mc-input-group">
|
||||
<label class="mc-label">Installation Directory:</label>
|
||||
<div class="flex gap-2 mb-2">
|
||||
<input type="text" id="install-path-input" class="mc-input nav-item !mb-0" placeholder="Default: Documents/LegacyClient" tabindex="0">
|
||||
<div class="btn-mc btn-mini nav-item !w-[60px] !h-[48px]" onclick="browseInstallDir()" tabindex="0">...</div>
|
||||
</div>
|
||||
<div class="btn-mc w-full !h-[40px] !text-lg !mb-0 nav-item" onclick="openGameDir()" tabindex="0">OPEN FOLDER</div>
|
||||
</div>
|
||||
|
||||
<div class="mc-input-group">
|
||||
<label class="mc-label">GitHub Repository Source:</label>
|
||||
<input type="text" id="repo-input" class="mc-input nav-item" placeholder="user/repo" tabindex="0">
|
||||
|
|
@ -174,10 +186,7 @@
|
|||
<div class="modal-overlay" id="update-modal">
|
||||
<div class="modal-box relative">
|
||||
<div class="modal-close-btn" id="btn-close-update">×</div>
|
||||
<div class="modal-title">UPDATE AVAILABLE</div>
|
||||
<div id="update-modal-text" class="modal-body-text">
|
||||
A new version is available. Would you like to update now?
|
||||
</div>
|
||||
<div id="update-modal-text" class="modal-body-text" style="display: none; margin-bottom: 20px;"></div>
|
||||
<div class="flex gap-4 w-full">
|
||||
<div class="btn-mc flex-grow nav-item" id="btn-confirm-update">UPDATE NOW</div>
|
||||
<div class="btn-mc flex-grow nav-item" id="btn-skip-update">LATER</div>
|
||||
|
|
@ -187,6 +196,10 @@
|
|||
|
||||
<div id="toast">NOTIFICATION</div>
|
||||
|
||||
<div id="music-toggle" class="music-btn nav-item" onclick="toggleMusic()" title="Toggle Music" tabindex="0">
|
||||
<span id="music-icon">♪</span>
|
||||
</div>
|
||||
|
||||
<script src="renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
9
main.js
9
main.js
|
|
@ -1,4 +1,4 @@
|
|||
const { app, BrowserWindow, shell, ipcMain } = require('electron');
|
||||
const { app, BrowserWindow, shell, ipcMain, dialog } = require('electron');
|
||||
const path = require('path');
|
||||
const Store = require('electron-store');
|
||||
const fs = require('fs');
|
||||
|
|
@ -41,6 +41,13 @@ function createWindow() {
|
|||
|
||||
ipcMain.handle('store-get', (event, key) => store.get(key));
|
||||
ipcMain.handle('store-set', (event, key, value) => store.set(key, value));
|
||||
|
||||
ipcMain.handle('select-directory', async () => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
properties: ['openDirectory', 'createDirectory']
|
||||
});
|
||||
return result.filePaths[0];
|
||||
});
|
||||
|
||||
win.on('maximize', () => win.webContents.send('window-is-maximized', true));
|
||||
win.on('unmaximize', () => win.webContents.send('window-is-maximized', false));
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "legacylauncher",
|
||||
"version": "1.1.1",
|
||||
"version": "2.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "legacylauncher",
|
||||
"version": "1.1.1",
|
||||
"version": "2.0.2",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"electron-store": "^6.0.1",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "legacylauncher",
|
||||
"version": "2.0.2",
|
||||
"version": "2.2.0",
|
||||
"description": "",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
284
renderer.js
284
renderer.js
|
|
@ -24,6 +24,9 @@ const Store = {
|
|||
},
|
||||
async set(key, value) {
|
||||
return await ipcRenderer.invoke('store-set', key, value);
|
||||
},
|
||||
async selectDirectory() {
|
||||
return await ipcRenderer.invoke('select-directory');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -293,6 +296,110 @@ const GamepadManager = {
|
|||
}
|
||||
};
|
||||
|
||||
// Music Player logic
|
||||
const MusicManager = {
|
||||
audio: new Audio(),
|
||||
playlist: [],
|
||||
currentIndex: -1,
|
||||
enabled: false,
|
||||
|
||||
async init() {
|
||||
this.enabled = await Store.get('legacy_music_enabled', true);
|
||||
this.updateIcon();
|
||||
this.audio.volume = 1.0;
|
||||
this.audio.onended = () => this.playNext();
|
||||
if (this.enabled) {
|
||||
this.start();
|
||||
}
|
||||
},
|
||||
|
||||
async scan() {
|
||||
try {
|
||||
const installDir = await getInstallDir();
|
||||
// Path provided by user: root of games folder in music/music
|
||||
const musicPath = path.join(installDir, 'music', 'music');
|
||||
|
||||
if (fs.existsSync(musicPath)) {
|
||||
const files = fs.readdirSync(musicPath);
|
||||
this.playlist = files
|
||||
.filter(f => f.toLowerCase().endsWith('.ogg'))
|
||||
.map(f => path.join(musicPath, f));
|
||||
|
||||
// Shuffle playlist
|
||||
for (let i = this.playlist.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[this.playlist[i], this.playlist[j]] = [this.playlist[j], this.playlist[i]];
|
||||
}
|
||||
console.log(`MusicManager: Loaded ${this.playlist.length} tracks.`);
|
||||
return this.playlist.length > 0;
|
||||
} else {
|
||||
console.log("MusicManager: music/music folder not found yet.");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Music scan error:", e);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
async start() {
|
||||
if (this.playlist.length === 0) {
|
||||
const success = await this.scan();
|
||||
if (!success) return;
|
||||
}
|
||||
if (this.playlist.length > 0 && this.audio.paused) {
|
||||
this.playNext();
|
||||
}
|
||||
},
|
||||
|
||||
playNext() {
|
||||
if (!this.enabled || this.playlist.length === 0) return;
|
||||
|
||||
let nextIndex;
|
||||
if (this.playlist.length > 1) {
|
||||
// Keep picking until we get a different track
|
||||
do {
|
||||
nextIndex = Math.floor(Math.random() * this.playlist.length);
|
||||
} while (nextIndex === this.currentIndex);
|
||||
} else {
|
||||
nextIndex = 0;
|
||||
}
|
||||
|
||||
this.currentIndex = nextIndex;
|
||||
this.audio.src = `file://${this.playlist[this.currentIndex]}`;
|
||||
this.audio.play().catch(e => {
|
||||
console.error("Audio playback error:", e);
|
||||
// If failed, try next one
|
||||
setTimeout(() => this.playNext(), 1000);
|
||||
});
|
||||
},
|
||||
|
||||
stop() {
|
||||
this.audio.pause();
|
||||
this.audio.currentTime = 0;
|
||||
},
|
||||
|
||||
async toggle() {
|
||||
this.enabled = !this.enabled;
|
||||
await Store.set('legacy_music_enabled', this.enabled);
|
||||
this.updateIcon();
|
||||
if (this.enabled) {
|
||||
this.start();
|
||||
} else {
|
||||
this.stop();
|
||||
}
|
||||
},
|
||||
|
||||
updateIcon() {
|
||||
const btn = document.getElementById('music-toggle');
|
||||
if (!btn) return;
|
||||
if (this.enabled) {
|
||||
btn.classList.remove('muted');
|
||||
} else {
|
||||
btn.classList.add('muted');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = async () => {
|
||||
document.getElementById('repo-input').value = await Store.get('legacy_repo', DEFAULT_REPO);
|
||||
document.getElementById('exec-input').value = await Store.get('legacy_exec_path', DEFAULT_EXEC);
|
||||
|
|
@ -301,6 +408,9 @@ window.onload = async () => {
|
|||
document.getElementById('port-input').value = await Store.get('legacy_port', "");
|
||||
document.getElementById('server-checkbox').checked = await Store.get('legacy_is_server', false);
|
||||
|
||||
const installDir = await getInstallDir();
|
||||
document.getElementById('install-path-input').value = installDir;
|
||||
|
||||
if (process.platform === 'linux' || process.platform === 'darwin') {
|
||||
document.getElementById('compat-option-container').style.display = 'block';
|
||||
scanCompatibilityLayers();
|
||||
|
|
@ -315,46 +425,99 @@ window.onload = async () => {
|
|||
|
||||
fetchGitHubData();
|
||||
checkForLauncherUpdates();
|
||||
loadSplashText();
|
||||
MusicManager.init();
|
||||
GamepadManager.init();
|
||||
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'F9') {
|
||||
checkForLauncherUpdates(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async function checkForLauncherUpdates() {
|
||||
async function loadSplashText() {
|
||||
const splashEl = document.getElementById('splash-text');
|
||||
if (!splashEl) return;
|
||||
|
||||
try {
|
||||
const filePath = path.join(__dirname, 'strings.txt');
|
||||
if (fs.existsSync(filePath)) {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const lines = content.split('\n').map(l => l.trim()).filter(l => l !== '');
|
||||
if (lines.length > 0) {
|
||||
const randomSplash = lines[Math.floor(Math.random() * lines.length)];
|
||||
splashEl.textContent = randomSplash;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to load splash text:", e);
|
||||
splashEl.textContent = "Welcome!";
|
||||
}
|
||||
}
|
||||
|
||||
function isNewerVersion(latest, current) {
|
||||
const lParts = latest.split('.').map(Number);
|
||||
const cParts = current.split('.').map(Number);
|
||||
for (let i = 0; i < Math.max(lParts.length, cParts.length); i++) {
|
||||
const l = lParts[i] || 0;
|
||||
const c = cParts[i] || 0;
|
||||
if (l > c) return true;
|
||||
if (l < c) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function checkForLauncherUpdates(manual = false) {
|
||||
try {
|
||||
const currentVersion = require('./package.json').version;
|
||||
const res = await fetch(`https://api.github.com/repos/${LAUNCHER_REPO}/releases/latest`);
|
||||
if (!res.ok) return;
|
||||
if (!res.ok) {
|
||||
if (manual) showToast("Could not check for updates.");
|
||||
return;
|
||||
}
|
||||
|
||||
const latestRelease = await res.json();
|
||||
const latestVersion = latestRelease.tag_name.replace('v', '');
|
||||
|
||||
if (latestVersion !== currentVersion) {
|
||||
const updateConfirmed = await promptLauncherUpdate(latestRelease.tag_name);
|
||||
if (isNewerVersion(latestVersion, currentVersion)) {
|
||||
const updateConfirmed = await promptLauncherUpdate(latestRelease.tag_name, latestRelease.body);
|
||||
if (updateConfirmed) {
|
||||
downloadAndInstallLauncherUpdate(latestRelease);
|
||||
}
|
||||
} else if (manual) {
|
||||
showToast("Launcher is up to date!");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Launcher update check failed:", e);
|
||||
if (manual) showToast("Update check failed.");
|
||||
}
|
||||
}
|
||||
|
||||
async function promptLauncherUpdate(version) {
|
||||
async function promptLauncherUpdate(version, changelog) {
|
||||
return new Promise((resolve) => {
|
||||
const modal = document.getElementById('update-modal');
|
||||
const confirmBtn = document.getElementById('btn-confirm-update');
|
||||
const skipBtn = document.getElementById('btn-skip-update');
|
||||
const closeBtn = document.getElementById('btn-close-update');
|
||||
const modalText = document.getElementById('update-modal-text');
|
||||
|
||||
document.getElementById('update-modal-text').innerHTML =
|
||||
`A new Launcher version <b>${version}</b> is available.<br><br>` +
|
||||
`Would you like to download and install it now?`;
|
||||
if (modalText) {
|
||||
modalText.innerHTML = `<span class="update-tag">NEW UPDATE: v${version}</span><br>` +
|
||||
`<div class="pn-body" style="font-size: 16px; max-height: 200px; overflow-y: auto; background: rgba(0,0,0,0.2); padding: 10px; margin-top: 5px;">${changelog || "No changelog provided."}</div>`;
|
||||
modalText.style.display = 'block';
|
||||
}
|
||||
|
||||
document.activeElement?.blur();
|
||||
modal.style.display = 'flex';
|
||||
modal.style.opacity = '1';
|
||||
|
||||
const cleanup = (result) => {
|
||||
modal.style.opacity = '0';
|
||||
setTimeout(() => modal.style.display = 'none', 300);
|
||||
setTimeout(() => {
|
||||
modal.style.display = 'none';
|
||||
if (modalText) modalText.style.display = 'none';
|
||||
}, 300);
|
||||
confirmBtn.onclick = null;
|
||||
skipBtn.onclick = null;
|
||||
closeBtn.onclick = null;
|
||||
|
|
@ -474,10 +637,32 @@ function updateCompatDisplay() {
|
|||
}
|
||||
}
|
||||
|
||||
async function getInstalledPath() {
|
||||
async function getInstallDir() {
|
||||
const homeDir = require('os').homedir();
|
||||
const defaultPath = path.join(homeDir, 'Documents', 'LegacyClient');
|
||||
return await Store.get('legacy_install_path', defaultPath);
|
||||
}
|
||||
|
||||
async function browseInstallDir() {
|
||||
const dir = await Store.selectDirectory();
|
||||
if (dir) {
|
||||
document.getElementById('install-path-input').value = dir;
|
||||
}
|
||||
}
|
||||
|
||||
async function openGameDir() {
|
||||
const dir = await getInstallDir();
|
||||
if (fs.existsSync(dir)) {
|
||||
shell.openPath(dir);
|
||||
} else {
|
||||
showToast("Directory does not exist yet!");
|
||||
}
|
||||
}
|
||||
|
||||
async function getInstalledPath() {
|
||||
const installDir = await getInstallDir();
|
||||
const execPath = await Store.get('legacy_exec_path', DEFAULT_EXEC);
|
||||
return path.join(homeDir, 'Documents', 'LegacyClient', execPath);
|
||||
return path.join(installDir, execPath);
|
||||
}
|
||||
|
||||
async function checkIsInstalled(tag) {
|
||||
|
|
@ -525,16 +710,19 @@ async function monitorProcess(proc) {
|
|||
if (!proc) return;
|
||||
const sessionStart = Date.now();
|
||||
setGameRunning(true);
|
||||
MusicManager.stop();
|
||||
|
||||
proc.on('exit', async () => {
|
||||
const sessionDuration = Math.floor((Date.now() - sessionStart) / 1000);
|
||||
const playtime = await Store.get('legacy_playtime', 0);
|
||||
await Store.set('legacy_playtime', playtime + sessionDuration);
|
||||
setGameRunning(false);
|
||||
if (MusicManager.enabled) MusicManager.start();
|
||||
});
|
||||
proc.on('error', (err) => {
|
||||
console.error("Process error:", err);
|
||||
setGameRunning(false);
|
||||
if (MusicManager.enabled) MusicManager.start();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -691,19 +879,18 @@ async function promptUpdate(newTag) {
|
|||
const confirmBtn = document.getElementById('btn-confirm-update');
|
||||
const skipBtn = document.getElementById('btn-skip-update');
|
||||
const closeBtn = document.getElementById('btn-close-update');
|
||||
const installedTag = await Store.get('installed_version_tag', "Unknown");
|
||||
|
||||
document.getElementById('update-modal-text').innerHTML =
|
||||
`New version <b>${newTag}</b> is available.<br><br>` +
|
||||
`Currently installed: <b>${installedTag}</b>.<br><br>` +
|
||||
`Would you like to update now?`;
|
||||
const modalText = document.getElementById('update-modal-text');
|
||||
|
||||
document.activeElement?.blur();
|
||||
modal.style.display = 'flex';
|
||||
modal.style.opacity = '1';
|
||||
|
||||
const cleanup = (result) => {
|
||||
modal.style.opacity = '0';
|
||||
setTimeout(() => modal.style.display = 'none', 300);
|
||||
setTimeout(() => {
|
||||
modal.style.display = 'none';
|
||||
if (modalText) modalText.style.display = 'none';
|
||||
}, 300);
|
||||
confirmBtn.onclick = null;
|
||||
skipBtn.onclick = null;
|
||||
closeBtn.onclick = null;
|
||||
|
|
@ -836,10 +1023,10 @@ function updateProgress(percent, text) {
|
|||
async function handleElectronFlow(url) {
|
||||
try {
|
||||
const homeDir = require('os').homedir();
|
||||
const docDir = path.join(homeDir, 'Documents');
|
||||
const zipPath = path.join(docDir, TARGET_FILE);
|
||||
const extractDir = path.join(docDir, 'LegacyClient');
|
||||
const backupDir = path.join(docDir, 'LegacyClient_Backup');
|
||||
const extractDir = await getInstallDir();
|
||||
const parentDir = path.dirname(extractDir);
|
||||
const zipPath = path.join(parentDir, TARGET_FILE);
|
||||
const backupDir = path.join(parentDir, 'LegacyClient_Backup');
|
||||
|
||||
updateProgress(5, "Downloading " + TARGET_FILE + "...");
|
||||
await downloadFile(url, zipPath);
|
||||
|
|
@ -883,6 +1070,10 @@ async function handleElectronFlow(url) {
|
|||
|
||||
await extractZip(zipPath, { dir: extractDir });
|
||||
|
||||
// Refresh music playlist if it was empty
|
||||
await MusicManager.scan();
|
||||
if (MusicManager.enabled) MusicManager.start();
|
||||
|
||||
// Restore preserved files
|
||||
if (fs.existsSync(backupDir)) {
|
||||
for (const item of preserveList) {
|
||||
|
|
@ -1144,6 +1335,48 @@ async function saveOptions() {
|
|||
const ip = document.getElementById('ip-input').value.trim();
|
||||
const port = document.getElementById('port-input').value.trim();
|
||||
const isServer = document.getElementById('server-checkbox').checked;
|
||||
const newInstallPath = document.getElementById('install-path-input').value.trim();
|
||||
|
||||
const oldInstallPath = await getInstallDir();
|
||||
if (newInstallPath && newInstallPath !== oldInstallPath) {
|
||||
// Move preserved files to new directory if they exist in old but not in new
|
||||
if (fs.existsSync(oldInstallPath)) {
|
||||
const preserveList = [
|
||||
'options.txt',
|
||||
'servers.txt',
|
||||
'username.txt',
|
||||
'settings.dat',
|
||||
'UID.dat',
|
||||
path.join('Windows64', 'GameHDD')
|
||||
];
|
||||
|
||||
if (!fs.existsSync(newInstallPath)) {
|
||||
fs.mkdirSync(newInstallPath, { recursive: true });
|
||||
}
|
||||
|
||||
for (const item of preserveList) {
|
||||
const src = path.join(oldInstallPath, item);
|
||||
const dest = path.join(newInstallPath, item);
|
||||
|
||||
if (fs.existsSync(src)) {
|
||||
const destDir = path.dirname(dest);
|
||||
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
||||
|
||||
// Simple move; if cross-device it might fail, but let's try
|
||||
try {
|
||||
if (!fs.existsSync(dest)) {
|
||||
fs.renameSync(src, dest);
|
||||
} else {
|
||||
console.log("Dest already exists, skipping move for: " + item);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Migration error for " + item + ": " + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await Store.set('legacy_install_path', newInstallPath);
|
||||
}
|
||||
|
||||
if (newRepo) await Store.set('legacy_repo', newRepo);
|
||||
if (newExec) await Store.set('legacy_exec_path', newExec);
|
||||
|
|
@ -1180,6 +1413,10 @@ function showToast(msg) {
|
|||
}, 3000);
|
||||
}
|
||||
|
||||
async function toggleMusic() {
|
||||
await MusicManager.toggle();
|
||||
}
|
||||
|
||||
// Global functions for HTML onclick
|
||||
window.minimizeWindow = minimizeWindow;
|
||||
window.toggleMaximize = toggleMaximize;
|
||||
|
|
@ -1195,3 +1432,6 @@ window.saveOptions = saveOptions;
|
|||
window.saveProfile = saveProfile;
|
||||
window.updateCompatDisplay = updateCompatDisplay;
|
||||
window.checkForUpdatesManual = checkForUpdatesManual;
|
||||
window.browseInstallDir = browseInstallDir;
|
||||
window.openGameDir = openGameDir;
|
||||
window.toggleMusic = toggleMusic;
|
||||
|
|
|
|||
478
strings.txt
Normal file
478
strings.txt
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
Happy birthday, ez!
|
||||
Happy birthday, Notch!
|
||||
Let me tell you about Homestuck!
|
||||
Let me tell you about Homestuck!
|
||||
Let me tell you about Homestuck!
|
||||
Merry X-mas!
|
||||
Happy New Year!
|
||||
As seen on TV!
|
||||
A young man stands in his bedroom!
|
||||
A young man stands in his bedroom!
|
||||
A young woman stands in her bedroom!
|
||||
A young woman stands in her bedroom!
|
||||
Awesome!
|
||||
100% pure!
|
||||
Better than Prey!
|
||||
More polygons!
|
||||
Get the cruxite dowel!
|
||||
Get the cruxite dowel!
|
||||
Get the cruxite dowel!
|
||||
Flashing letters!
|
||||
Made by Notch!
|
||||
It's here!
|
||||
Best in class!
|
||||
It's finished!
|
||||
Kind of dragon free!
|
||||
Excitement!
|
||||
Where doing it man, where making this hapen!
|
||||
Where doing it man, where making this hapen!
|
||||
One of a kind!
|
||||
Heaps of hits on YouTube!
|
||||
Indev!
|
||||
Spiders everywhere!
|
||||
Check it out!
|
||||
Holy cow, man!
|
||||
It's a game!
|
||||
Made in Sweden!
|
||||
Uses LWJGL!
|
||||
Reticulating splines!
|
||||
Minecraft!
|
||||
Yaaay!
|
||||
413!
|
||||
413!
|
||||
413!
|
||||
413!
|
||||
Singleplayer!
|
||||
Keyboard compatible!
|
||||
Undocumented!
|
||||
Ingots!
|
||||
Exploding creepers!
|
||||
That's no moon!
|
||||
l33t!
|
||||
Create!
|
||||
Survive!
|
||||
Dungeon!
|
||||
I WARNED YOU ABOUT STAIRS BRO!!!!
|
||||
I WARNED YOU ABOUT STAIRS BRO!!!!
|
||||
I WARNED YOU ABOUT STAIRS BRO!!!!
|
||||
Exclusive!
|
||||
The bee's knees!
|
||||
Closed source!
|
||||
Classy!
|
||||
Wow!
|
||||
Not on steam!
|
||||
Oh man!
|
||||
Awesome community!
|
||||
Pixels!
|
||||
Teetsuuuuoooo!
|
||||
Kaaneeeedaaaa!
|
||||
Now with difficulty!
|
||||
Enhanced!
|
||||
90% bug free!
|
||||
Pretty!
|
||||
12 herbs and spices!
|
||||
Fat free!
|
||||
Absolutely no memes!
|
||||
Free dental!
|
||||
Cloud computing!
|
||||
Hard to label!
|
||||
Technically good!
|
||||
Bringing home the bacon!
|
||||
Indie!
|
||||
GOTY!
|
||||
Ceci n'est pas une title screen!
|
||||
Euclidian!
|
||||
Now in 3D!
|
||||
Inspirational!
|
||||
Herregud!
|
||||
Complex cellular automata!
|
||||
Yes, sir!
|
||||
Played by cowboys!
|
||||
Thousands of colors!
|
||||
Try it!
|
||||
Age of Wonders is better!
|
||||
Try the mushroom stew!
|
||||
Sensational!
|
||||
Hot tamale, hot hot tamale!
|
||||
Play him off, keyboard cat!
|
||||
Guaranteed!
|
||||
Alchemize ALL the things!
|
||||
Alchemize ALL the things!
|
||||
Macroscopic!
|
||||
Bring it on!
|
||||
Random splash!
|
||||
Call your mother!
|
||||
Monster infighting!
|
||||
Loved by millions!
|
||||
Ultimate edition!
|
||||
Freaky!
|
||||
Water proof!
|
||||
Uninflammable!
|
||||
Whoa, dude!
|
||||
Roxy is best hacker!
|
||||
Roxy is best hacker!
|
||||
Roxy is best hacker!
|
||||
All inclusive!
|
||||
Tell your friends!
|
||||
NP is not in P!
|
||||
Notch <3 ez!
|
||||
Music by C418!
|
||||
Livestreamed!
|
||||
Haunted!
|
||||
Polynomial!
|
||||
Terrestrial!
|
||||
All is full of love!
|
||||
Full of stars!
|
||||
Scientific!
|
||||
Cooler than Spock!
|
||||
Collaborate and listen!
|
||||
Never dig down!
|
||||
Take frequent breaks!
|
||||
Not linear!
|
||||
Han shot first!
|
||||
Nice to meet you!
|
||||
Buckets of lava!
|
||||
Ride the pig!
|
||||
Larger than Earth!
|
||||
sqrt(-1) love you!
|
||||
Phobos anomaly!
|
||||
Punching wood!
|
||||
Falling off cliffs!
|
||||
0% sugar!
|
||||
150% hyperbole!
|
||||
Synecdoche!
|
||||
Let's danec!
|
||||
Reference implementation!
|
||||
Lewd with two dudes with food!
|
||||
Kiss the sky!
|
||||
20 GOTO 10!
|
||||
Verlet intregration!
|
||||
Peter Griffin!
|
||||
Do not distribute!
|
||||
Cogito ergo sum!
|
||||
4815162342 lines of code!
|
||||
A skeleton popped out!
|
||||
The Work of Notch!
|
||||
The sum of its parts!
|
||||
BTAF used to be good!
|
||||
I miss ADOM!
|
||||
umop-apisdn!
|
||||
OICU812!
|
||||
Bring me Ray Cokes!
|
||||
Finger-licking!
|
||||
Thematic!
|
||||
Pneumatic!
|
||||
Sublime!
|
||||
Octagonal!
|
||||
Une baguette!
|
||||
Gargamel plays it!
|
||||
Rita is the new top dog!
|
||||
SWM forever!
|
||||
Representing Edsbyn!
|
||||
Matt Damon!
|
||||
Supercalifragilisticexpialidocious!
|
||||
Consummate V's!
|
||||
Cow Tools!
|
||||
Double buffered!
|
||||
Fan fiction!
|
||||
Flaxkikare!
|
||||
Jason! Jason! Jason!
|
||||
Hotter than the sun!
|
||||
Internet enabled!
|
||||
Autonomous!
|
||||
Engage!
|
||||
Fantasy!
|
||||
DRR! DRR! DRR!
|
||||
Kick it root down!
|
||||
Regional resources!
|
||||
Woo, facepunch!
|
||||
Woo, somethingawful!
|
||||
Woo, /v/!
|
||||
Woo, tigsource!
|
||||
Woo, minecraftforum!
|
||||
Woo, worldofminecraft!
|
||||
Woo, reddit!
|
||||
Woo, 2pp!
|
||||
Google anlyticsed!
|
||||
Now supports åäö!
|
||||
Give us Gordon!
|
||||
Tip your waiter!
|
||||
Very fun!
|
||||
12345 is a bad password!
|
||||
Vote for net neutrality!
|
||||
Lives in a pineapple under the sea!
|
||||
MAP11 has two names!
|
||||
Omnipotent!
|
||||
Gasp!
|
||||
...!
|
||||
Bees, bees, bees, bees!
|
||||
Jag känner en bot!
|
||||
This text is hard to read if you play the game at the default resolution, but at 1080p it's fine!
|
||||
Haha, LOL!
|
||||
Hampsterdance!
|
||||
Switches and ores!
|
||||
Menger sponge!
|
||||
idspispopd!
|
||||
Eple (original edit)!
|
||||
So fresh, so clean!
|
||||
Slow acting portals!
|
||||
Try the Nether!
|
||||
Don't look directly at the bugs!
|
||||
Oh, ok, Pigmen!
|
||||
Finally with ladders!
|
||||
Scary!
|
||||
Play Minecraft, Watch Topgear, Get Pig!
|
||||
Twittered about!
|
||||
Jump up, jump up, and get down!
|
||||
Joel is neat!
|
||||
A riddle, wrapped in a mystery!
|
||||
Huge tracts of land!
|
||||
Welcome to your Doom!
|
||||
Stay a while, stay forever!
|
||||
Stay a while and listen!
|
||||
Treatment for your rash!
|
||||
"Autological" is!
|
||||
Information wants to be free!
|
||||
"Almost never" is an interesting concept!
|
||||
Lots of truthiness!
|
||||
The creeper is a spy!
|
||||
Turing complete!
|
||||
It's groundbreaking!
|
||||
Let our battle's begin!
|
||||
The sky is the limit!
|
||||
Jeb has amazing hair!
|
||||
Casual gaming!
|
||||
Undefeated!
|
||||
Kinda like Lemmings!
|
||||
Follow the train, CJ!
|
||||
Leveraging synergy!
|
||||
This message will never appear on the splash screen, isn't that weird?
|
||||
DungeonQuest is unfair!
|
||||
110813!
|
||||
90210!
|
||||
Check out the far lands!
|
||||
Tyrion would love it!
|
||||
Also try VVVVVV!
|
||||
Also try Super Meat Boy!
|
||||
Also try Terraria!
|
||||
Also try Mount And Blade!
|
||||
Also try Project Zomboid!
|
||||
Also try World of Goo!
|
||||
Also try Limbo!
|
||||
Also try Pixeljunk Shooter!
|
||||
Also try Braid!
|
||||
Also try Sburb!
|
||||
Also try Sburb!
|
||||
Also try Sburb!
|
||||
That's super!
|
||||
Bread is pain!
|
||||
Read more books!
|
||||
Khaaaaaaaaan!
|
||||
Less addictive than TV Tropes!
|
||||
More addictive than lemonade!
|
||||
Bigger than a bread box!
|
||||
Millions of peaches!
|
||||
Fnord!
|
||||
This is my true form!
|
||||
Totally forgot about Dre!
|
||||
Don't bother with the clones!
|
||||
Pumpkinhead!
|
||||
Hobo humping slobo babe!
|
||||
Made by Jeb!
|
||||
Has an ending!
|
||||
Finally complete!
|
||||
Feature packed!
|
||||
Boots with the fur!
|
||||
Stop, hammertime!
|
||||
Testificates!
|
||||
Conventional!
|
||||
Homeomorphic to a 3-sphere!
|
||||
Doesn't avoid double negatives!
|
||||
Place ALL the blocks!
|
||||
Does barrel rolls!
|
||||
Meeting expectations!
|
||||
PC gaming since 1873!
|
||||
Ghoughpteighbteau tchoghs!
|
||||
Déjà vu!
|
||||
IT KEEPS HAPPENING!
|
||||
IT KEEPS HAPPENING!
|
||||
IT KEEPS HAPPENING!
|
||||
I TOLD YOU DOG!
|
||||
I TOLD YOU DOG!
|
||||
Déjà vu!
|
||||
Got your nose!
|
||||
Afraid of the big, black bat!
|
||||
Doesn't use the U-word!
|
||||
Child's play!
|
||||
See you next Friday or so!
|
||||
From the streets of Södermalm!
|
||||
150 bpm for 400000 minutes!
|
||||
Technologic!
|
||||
Funk soul brother!
|
||||
Pumpa kungen!
|
||||
Fetchstuck compatible!
|
||||
Sylladex full!
|
||||
Sylladex full!
|
||||
Honk. HONK.
|
||||
Honk. HONK.
|
||||
You can't fight the Homestuck!
|
||||
You can't fight the Homestuck!
|
||||
==> Enter.
|
||||
==> Enter.
|
||||
STRIFE!
|
||||
STRIFE!
|
||||
8^y
|
||||
8^y
|
||||
Sweet Bro and Hella Jeff!
|
||||
What pumpkin?
|
||||
What pumpkin?
|
||||
ABSCOND!
|
||||
ABSCOND!
|
||||
Check your Strife Specibus!
|
||||
Ascend to God Tier!
|
||||
Ascend to God Tier!
|
||||
Got tiger!
|
||||
Got tiger!
|
||||
Motherfucking miracles!
|
||||
Welcome to Skaia!
|
||||
Welcome to Skaia!
|
||||
Prospit or Derse?
|
||||
Upgrading the Alchemiter!
|
||||
Build Grist!
|
||||
Build Grist!
|
||||
Now with 100% more Faygo!
|
||||
Sopor slime pie!
|
||||
Grimdark!
|
||||
Trickster mode engaged!
|
||||
How HIGH do you even have to BE just to DO something like that........
|
||||
Mayor of Can Town!
|
||||
Spades Slick did nothing wrong!
|
||||
It's hard and nobody understands.
|
||||
Shenanigans!
|
||||
The Scratch is happening!
|
||||
612!
|
||||
111111!
|
||||
Jegus!
|
||||
All the luck!
|
||||
All the luck!
|
||||
Dunkass!
|
||||
Captchalogue this!
|
||||
Captchalogue this!
|
||||
Echeladder rung attained!
|
||||
Megalovania originally played here!
|
||||
Megalovania originally played here!
|
||||
Music by Toby Fox!
|
||||
Don't ask about the buckets!
|
||||
Don't ask about the buckets!
|
||||
Candy corn horns!
|
||||
Your name is JOHN EGBERT.
|
||||
Let me guess, your zodiac sign is a troll?
|
||||
Put the bunny back in the box!
|
||||
Do the windy thing!
|
||||
Do the windy thing!
|
||||
Betty Crocker is evil!
|
||||
Betty Crocker is evil!
|
||||
Beware the Batterwitch!
|
||||
He is already here.
|
||||
He is already here.
|
||||
Doc Scratch is watching.
|
||||
Con Air is a masterpiece!
|
||||
Karkalicious!
|
||||
SGRUB!
|
||||
Sburb Beta!
|
||||
Sburb Beta!
|
||||
Knight of Time!
|
||||
Seer of Light!
|
||||
Heir of Breath!
|
||||
Witch of Space!
|
||||
Flipping the frog switch!
|
||||
Forge the Bilious Sliver!
|
||||
Midnight Crew!
|
||||
We have the cue ball!
|
||||
th1s 1s r3d1culous.
|
||||
wweh...
|
||||
glub glub...
|
||||
DAVE: this is stupid
|
||||
WHAT THE FUCK IS A CREEPER.
|
||||
I AM GOING TO THROW A TANTRUM.
|
||||
H3H3H3! 1 C4N SM3LL TH3 D14MOND 0R3!
|
||||
T4ST3S L1K3 CH3RRY!
|
||||
uHH, pLEASE DONT MINE THAT,,, iTS MINE.
|
||||
ii jju2t want two play a game.
|
||||
two much iinformatiion.
|
||||
:33 < ac goes on a purrfect mining trip!
|
||||
:33 < furrealsies!
|
||||
I Cannot Believe You Just Mined That Dirt.
|
||||
Please Stop Being Ridiculous.
|
||||
All 8 of my 8lue diamonds! ::::::::)
|
||||
You're w8ing for something?
|
||||
D --> That block is insufficiently STRONG
|
||||
D --> e%cuse me
|
||||
tHaT sHiT iS mOtHeRfUcKiNg MiRaCuLoUs.
|
||||
mY mOtHeRfUcKiNg BrOtHeR.
|
||||
wwhats the point of minin anywway.
|
||||
38) This is so GLUBBING exciting!
|
||||
the dead p0pulati0n is b0thering me.
|
||||
0k. i am t0tally fine with this.
|
||||
ROXY: wonk ;)
|
||||
ROXY: obfuscation!!!
|
||||
ROXY: i am the best hacker
|
||||
ROXY: meow
|
||||
ROXY: dirk u there
|
||||
ROXY: i am going to save u
|
||||
ROXY: wtf is this block
|
||||
ROXY: where r the fucking cats
|
||||
ROXY: im kinda drunk
|
||||
ROXY: the magic is real
|
||||
ROXY: sweet catch!
|
||||
ROXY: frigglish!
|
||||
ROXY: where are all the wizards
|
||||
ROXY: im putting on my god tier pjs
|
||||
ROXY: pumpkins r legally outlawed
|
||||
ROXY: mutieeeee
|
||||
ROXY: cant wake up
|
||||
ROXY: sup
|
||||
ROXY: wonk ;)
|
||||
JOHN: wow, okay.
|
||||
JOHN: wow, okay.
|
||||
JOHN: i am a true prankster!
|
||||
JOHN: what are you even talking about?
|
||||
JOHN: let's go do some ghostbusting!
|
||||
JOHN: i'm not a homosexual!
|
||||
JOHN: i am a true prankster!
|
||||
Prankster's Gambit!
|
||||
Prankster's Gambit!
|
||||
Ectobiology!
|
||||
Ectobiology!
|
||||
JADE: hooray!!!
|
||||
JADE: <3 <3 <3
|
||||
JADE: beep beep meow
|
||||
JADE: im going to build a tall tower!
|
||||
JADE: hooray!!!
|
||||
JADE: <3 <3 <3
|
||||
Becquerel is a good boy!
|
||||
Becquerel is a good boy!
|
||||
Good dog. Best friend.
|
||||
Good dog. Best friend.
|
||||
Squiddleknit!
|
||||
Squiddles!
|
||||
DAVE: i am not a hero
|
||||
DAVE: ironically of course
|
||||
DAVE: time shenanigans
|
||||
DAVE: lets drop it like its hot
|
||||
DAVE: apple juice
|
||||
DAVE: sweet catch
|
||||
ROSE: Let the record state that I am not a fan of this.
|
||||
ROSE: I will tear this game apart.
|
||||
ROSE: The plot thickens.
|
||||
ROSE: We are playing a game.
|
||||
Pony Pals!
|
||||
DIRK: You can't escape the miles.
|
||||
DIRK: Sup.
|
||||
DIRK: im going to build a robot.
|
||||
Auto-Responder!
|
||||
JAKE: Tally ho!
|
||||
JAKE: Pip pip!
|
||||
JAKE: By jove!
|
||||
JAKE: Egads!
|
||||
JAKE: Jolly good show
|
||||
81
style.css
81
style.css
|
|
@ -255,11 +255,33 @@ body {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.splash-text {
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
left: 90%;
|
||||
width: fit-content;
|
||||
color: #ffff00;
|
||||
font-size: 20px;
|
||||
text-shadow: 2px 2px 0 #3f3f00;
|
||||
transform: translate(-50%, -50%) rotate(-20deg);
|
||||
animation: splashBounce 0.2s infinite alternate ease-in-out;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
z-index: 5;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@keyframes splashBounce {
|
||||
from { transform: translate(-50%, -50%) rotate(-20deg) scale(1); }
|
||||
to { transform: translate(-50%, -50%) rotate(-20deg) scale(1.1); }
|
||||
}
|
||||
|
||||
.mc-logo-img {
|
||||
width: 650px;
|
||||
max-width: 95%;
|
||||
filter: drop-shadow(6px 6px 0px rgba(0,0,0,0.6));
|
||||
margin-bottom: 50px;
|
||||
margin-top: -40px;
|
||||
margin-bottom: 70px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
|
|
@ -435,22 +457,38 @@ body {
|
|||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 150;
|
||||
z-index: 2000;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.modal-box {
|
||||
width: 750px;
|
||||
max-width: 95%;
|
||||
width: 60vw;
|
||||
max-width: 850px;
|
||||
min-width: 600px;
|
||||
max-height: 85vh;
|
||||
background: var(--mc-gui-bg);
|
||||
border: 4px solid #000;
|
||||
padding: 50px;
|
||||
padding: clamp(20px, 4vw, 50px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-shadow: inset 4px 4px 0 #fff;
|
||||
animation: modalPop 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-box::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.modal-box::-webkit-scrollbar-track {
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.modal-box::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
border: 2px solid var(--mc-gui-bg);
|
||||
}
|
||||
|
||||
.modal-close-btn {
|
||||
|
|
@ -571,6 +609,39 @@ body {
|
|||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.music-btn {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background: var(--mc-button-bg);
|
||||
border: 2px solid #000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-shadow: inset -2px -2px 0 #333, inset 2px 2px 0 #aaa;
|
||||
z-index: 1000;
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.music-btn:hover:not(.disabled) {
|
||||
background-color: var(--mc-button-hover);
|
||||
outline: 2px solid #fff;
|
||||
}
|
||||
|
||||
.music-btn.muted {
|
||||
opacity: 0.6;
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.music-btn.muted #music-icon {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
#toast {
|
||||
position: fixed;
|
||||
bottom: 80px;
|
||||
|
|
|
|||
Loading…
Reference in a new issue