Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<div id="mp-balance" class="hidden">
<div id="mp-balance-circle"></div>
<div id="mp-balance-pip"></div>
<div id="mp-balance-grade" class="hidden"></div>
</div>

<!-- MULTIPLAYER OVERLAY (opened by clicking balance indicator) -->
Expand All @@ -57,6 +58,15 @@
<button id="mp-overlay-logout">Déconnexion</button>
</div>

<!-- Login prompt for unauthenticated players -->
<div id="mp-overlay-login" class="hidden">
<div id="mp-overlay-login-text">Connectez-vous pour contribuer</div>
<div id="mp-overlay-login-btns">
<a href="/auth/google" class="mp-login-btn mp-login-google">Google</a>
<a href="/auth/discord" class="mp-login-btn mp-login-discord">Discord</a>
</div>
</div>

<div id="mp-overlay-war">
<div id="mp-overlay-gauge">
<div id="mp-overlay-light-bar"></div>
Expand All @@ -74,6 +84,7 @@
<div id="mp-overlay-player-title">Ma contribution</div>
<div id="mp-overlay-player-contrib">0</div>
<div id="mp-overlay-player-streak"></div>
<div id="mp-overlay-player-prestige" class="hidden"></div>

<!-- Contribution rate selector -->
<div id="mp-overlay-rate">
Expand Down
2 changes: 2 additions & 0 deletions js/click.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
import { checkMilestones } from './upgrades.js';
import { updateUI } from './ui.js';
import { save } from './save.js';
import { reportLumens } from './multiplayer.js';

function handleClick(e) {
if (state.victoryReached || state.sunPurchased || shared.sunCinematicActive) return;
Expand Down Expand Up @@ -75,6 +76,7 @@ function handleClick(e) {

state.lumens += gain;
state.totalLumens += gain;
reportLumens(gain);

addHalo(x, y);
if (multiplier > 1) {
Expand Down
6 changes: 4 additions & 2 deletions js/game-loop.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// === Game Loop — Main loop and passive income tick ===
'use strict';

import { state, getTotalPrestigeMultiplier } from './state.js';
import { state, shared, getTotalPrestigeMultiplier } from './state.js';
import { _raf } from './utils.js';
import { reportLumens } from './multiplayer.js';
import { canvas, ctx } from './canvas.js';

// Effects
Expand Down Expand Up @@ -60,11 +61,12 @@ export function gameLoop() {
}

export function passiveTick() {
if (state.victoryReached || state.sunPurchased) return;
if (state.victoryReached || state.sunPurchased || shared.seasonEndActive) return;
if (state.lumensPerSecond > 0) {
const gain = (state.lumensPerSecond * getTotalPrestigeMultiplier()) / 10; // called 10x per sec
state.lumens += gain;
state.totalLumens += gain;
reportLumens(gain);
checkMilestones();
updateUI();
}
Expand Down
2 changes: 2 additions & 0 deletions js/interaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
'use strict';

import { state, gameMode, getUpgradeCount, shared, getSaveKey, getTotalPrestigeMultiplier } from './state.js';
import { _st, _now } from './utils.js';

Check warning on line 5 in js/interaction.js

View workflow job for this annotation

GitHub Actions / Lint

'_st' is defined but never used
import { canvas, ctx } from './canvas.js';
import { halos } from './effects/halos.js';
import { reportLumens } from './multiplayer.js';

// --- Rubbing/swiping mechanic ---
let isRubbing = false;
Expand Down Expand Up @@ -36,6 +37,7 @@
const rubPower = Math.max(1, Math.floor(state.clickPower * 0.3 * getTotalPrestigeMultiplier()));
state.lumens += rubPower;
state.totalLumens += rubPower;
reportLumens(rubPower);
rubDistance -= RUB_THRESHOLD;

if (gameMode === 'off') {
Expand Down
88 changes: 68 additions & 20 deletions js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
setGameStarted,
getSaveKey,
prestigeLevel,
prestigeMultiplier,

Check warning on line 12 in js/main.js

View workflow job for this annotation

GitHub Actions / Lint

'prestigeMultiplier' is defined but never used
getTotalPrestigeMultiplier,
setMpPrestigeBonus,
} from './state.js';
Expand Down Expand Up @@ -39,10 +39,12 @@
onMultiplayerUpdate,
onSeasonEnd,
onRewardReceived,
onStreakChange,
notifySideChange,
setContributionRate,
reportLumens,
} from './multiplayer.js';
import { setServerAvailable, isOnboardingDone, isMultiplayerActive, checkOnboarding } from './onboarding.js';

Check warning on line 47 in js/main.js

View workflow job for this annotation

GitHub Actions / Lint

'isOnboardingDone' is defined but never used
import { showSeasonEnd, showSeasonEndBroadcast } from './season-end.js';

function startGame(mode) {
Expand Down Expand Up @@ -84,6 +86,8 @@
gameArea.classList.remove('hidden');
resizeCanvas();
initGame();
// Notify server of side choice after game is initialized
notifySideChange(mode);
}, 700);
}

Expand Down Expand Up @@ -112,6 +116,7 @@
if (offlineGain > 0) {
state.lumens += offlineGain;
state.totalLumens += offlineGain;
reportLumens(offlineGain);
updateUI();
var offlinePopup = document.getElementById('offline-popup');
var offlineText = document.getElementById('offline-text');
Expand Down Expand Up @@ -172,11 +177,9 @@
// --- Mode selection ---
modeOn.addEventListener('click', function () {
startGame('on');
notifySideChange('on');
});
modeOff.addEventListener('click', function () {
startGame('off');
notifySideChange('off');
});

// Check for saved game — skip landing if save exists
Expand Down Expand Up @@ -220,8 +223,11 @@
var mpOverlayPlayer = document.getElementById('mp-overlay-player');
var mpOverlayPlayerContrib = document.getElementById('mp-overlay-player-contrib');
var mpOverlayPlayerStreak = document.getElementById('mp-overlay-player-streak');
var mpOverlayPlayerPrestige = document.getElementById('mp-overlay-player-prestige');
var mpOverlayRateBtns = document.querySelectorAll('.mp-rate-btn');
var mpOverlayLeaderboardBtn = document.getElementById('mp-overlay-leaderboard-btn');
var mpBalanceGrade = document.getElementById('mp-balance-grade');
var mpOverlayLogin = document.getElementById('mp-overlay-login');

// Leaderboard DOM
var mpLeaderboard = document.getElementById('mp-leaderboard');
Expand All @@ -232,14 +238,6 @@
var mpLeaderboardList = document.getElementById('mp-leaderboard-list');
var mpLeaderboardPlayerRank = document.getElementById('mp-leaderboard-player-rank');

function formatShort(n) {
if (n >= 1e12) return (n / 1e12).toFixed(1) + 'T';
if (n >= 1e9) return (n / 1e9).toFixed(1) + 'B';
if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
return String(n);
}

// --- Update balance indicator circle proportions ---
function updateBalanceCircle(light, dark) {
var total = light + dark;
Expand All @@ -256,10 +254,16 @@

// --- Update the full overlay with current multiplayer state ---
function updateMultiplayerUI(mpState) {
// Balance indicator: only show if onboarding is done and user connected
if (isMultiplayerActive() && mpState.connected) {
// Balance indicator: only show if onboarding is done
if (isMultiplayerActive()) {
mpBalance.classList.remove('hidden');
mpBalance.classList.add('online');
if (mpState.connected) {
mpBalance.classList.add('online');
mpBalance.classList.remove('disconnected');
} else {
mpBalance.classList.remove('online');
mpBalance.classList.add('disconnected');
}
}

// Update balance proportions
Expand All @@ -277,10 +281,12 @@
mpOverlayAvatar.style.display = 'none';
}
mpOverlayLogout.style.display = '';
mpOverlayLogin.classList.add('hidden');
} else {
mpOverlayName.textContent = '';
mpOverlayAvatar.style.display = 'none';
mpOverlayLogout.style.display = 'none';
mpOverlayLogin.classList.remove('hidden');
}

// Grade badge
Expand All @@ -300,8 +306,8 @@
mpOverlayLightBar.style.width = '50%';
mpOverlayDarkBar.style.width = '50%';
}
mpOverlayLightTotal.textContent = formatShort(light);
mpOverlayDarkTotal.textContent = formatShort(dark);
mpOverlayLightTotal.textContent = formatNumber(light);
mpOverlayDarkTotal.textContent = formatNumber(dark);
mpOverlayOnline.textContent = mpState.online.total + ' en ligne';

// Season info
Expand All @@ -323,7 +329,7 @@
if (mpState.user && mpState.profile) {
mpOverlayPlayer.classList.remove('hidden');
mpOverlayPlayerContrib.textContent =
formatShort(mpState.profile.contribution) + (gameMode === 'off' ? ' ob' : ' lm');
formatNumber(mpState.profile.contribution) + (gameMode === 'off' ? ' ob' : ' lm');

if (mpState.profile.streakDays > 0) {
var streakText = mpState.profile.streakDays + 'j consécutifs';
Expand All @@ -335,6 +341,15 @@
mpOverlayPlayerStreak.textContent = '';
}

// Prestige bonus display
if (mpState.profile.mpPrestigeBonus > 0) {
mpOverlayPlayerPrestige.textContent =
'Bonus prestige : +' + mpState.profile.mpPrestigeBonus.toFixed(2) + ' (saisons passées)';
mpOverlayPlayerPrestige.classList.remove('hidden');
} else {
mpOverlayPlayerPrestige.classList.add('hidden');
}

// Update rate buttons
updateRateButtons(mpState.contributionRate);

Expand All @@ -345,6 +360,14 @@
} else {
mpOverlayPlayer.classList.add('hidden');
}

// Grade badge on balance indicator
if (mpState.profile && mpState.profile.grade && mpState.profile.grade !== 'none') {
mpBalanceGrade.textContent = mpState.profile.grade;
mpBalanceGrade.classList.remove('hidden');
} else {
mpBalanceGrade.classList.add('hidden');
}
}

function updateRateButtons(activeRate) {
Expand Down Expand Up @@ -441,7 +464,9 @@
});
});

function fetchLeaderboard() {
function fetchLeaderboard(retries) {
if (retries === undefined) retries = 2;
mpLeaderboardList.innerHTML = '<div style="opacity:0.3;text-align:center;padding:20px;">Chargement\u2026</div>';
fetch('/api/leaderboard')
.then(function (res) {
return res.json();
Expand All @@ -452,7 +477,14 @@
renderLeaderboard();
})
.catch(function () {
mpLeaderboardList.innerHTML = '<div style="opacity:0.3;text-align:center;padding:20px;">Indisponible</div>';
if (retries > 0) {
_st(function () {
fetchLeaderboard(retries - 1);
}, 2000);
} else {
mpLeaderboardList.innerHTML =
'<div style="opacity:0.3;text-align:center;padding:20px;">Indisponible — réessayez plus tard</div>';
}
});
}

Expand All @@ -479,7 +511,7 @@

var totalEl = document.createElement('span');
totalEl.className = 'mp-lb-total';
totalEl.textContent = formatShort(entry.total);
totalEl.textContent = formatNumber(entry.total);

el.appendChild(rankEl);

Expand All @@ -500,7 +532,7 @@
// Player's own rank
if (leaderboardData.playerRank && leaderboardData.playerRank.side === currentLeaderboardSide) {
mpLeaderboardPlayerRank.textContent =
'Votre rang : #' + leaderboardData.playerRank.rank + ' (' + formatShort(leaderboardData.playerRank.total) + ')';
'Votre rang : #' + leaderboardData.playerRank.rank + ' (' + formatNumber(leaderboardData.playerRank.total) + ')';
} else {
mpLeaderboardPlayerRank.textContent = '';
}
Expand All @@ -519,6 +551,22 @@
}
});

// --- Streak loss notification ---
onStreakChange(function (info) {
if (!info.reset) return;
// Show a brief toast notification about streak loss
var toast = document.createElement('div');
toast.className = 'mp-streak-toast';
toast.textContent = 'Série perdue (' + info.oldStreak + 'j) — multiplieur réinitialisé';
document.body.appendChild(toast);
_st(function () {
toast.classList.add('fade-out');
_st(function () {
toast.remove();
}, 600);
}, 4000);
});

// --- Onboarding completion callback ---
window._onOnboardingDone = function (choice) {
if (choice === 'connected') {
Expand Down
Loading
Loading