refacto: Changing click cooldown to daily actions for users and teams
This commit is contained in:
@@ -158,14 +158,19 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="infoRow" id="countdownWrap" aria-live="polite">
|
||||
<span class="infoKey countdownLabel">Prochain clic</span>
|
||||
<span class="infoKey countdownLabel">Actions restantes</span>
|
||||
<span class="infoVal countdownVal">
|
||||
<span id="countdown" class="countdown">0</span>
|
||||
<span class="countdownUnit">s</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="infoRow" aria-live="polite">
|
||||
<span class="infoKey countdownLabel">Actions équipe restantes</span>
|
||||
<span class="infoVal countdownVal">
|
||||
<span id="teamActionsRemaining" class="countdown">—</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="infoRow">
|
||||
<span class="infoKey muted">Délai entre deux clics</span>
|
||||
<span class="infoKey muted">Actions par jour</span>
|
||||
<code class="infoVal" id="cooldownConfig">—</code>
|
||||
</div>
|
||||
<div class="infoRow">
|
||||
@@ -216,7 +221,7 @@
|
||||
<span class="elemBonusLabel">Premier Ordre</span>
|
||||
</span>
|
||||
<span class="elemBonusEffective">
|
||||
<span class="elemBonusDetailLabel">Recharge :</span>
|
||||
<span class="elemBonusDetailLabel">Quota effectif/j :</span>
|
||||
<span class="elemBonusDetailVal" id="effectiveCooldown">—</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@ const COLOR_OPPONENT_GREY = "rgba(95, 98, 110, 0.72)";
|
||||
// ── Shared game state ─────────────────────────────────────────────────────────
|
||||
|
||||
export const GAME_CONFIG = {
|
||||
clickCooldownSeconds: 5,
|
||||
dailyActionQuota: 100,
|
||||
databaseWipeoutIntervalSeconds: 21600,
|
||||
debugModeForTeams: false,
|
||||
configReloadIntervalSeconds: 30,
|
||||
@@ -30,6 +30,7 @@ export const GAME_CONFIG = {
|
||||
elementWorth: {},
|
||||
resourceWorth: { common: {}, rare: {} },
|
||||
militaryPower: {},
|
||||
teamActionsRemaining: null,
|
||||
};
|
||||
window.GAME_CONFIG = GAME_CONFIG;
|
||||
|
||||
@@ -46,8 +47,10 @@ export function applySeed(str) {
|
||||
seedU32 = fnv1a32(str || "fallback");
|
||||
}
|
||||
|
||||
export const teamCooldownEndMs = { blue: 0, red: 0 };
|
||||
let rafId = 0;
|
||||
/** Remaining actions for the current user in today's quota window. */
|
||||
export let actionsRemaining = 0;
|
||||
/** Remaining actions for the current team in today's team quota window. */
|
||||
export let teamActionsRemaining = null;
|
||||
let lastPointerEvent = null;
|
||||
|
||||
// ── Tile fade-in animation ────────────────────────────────────────────────────
|
||||
@@ -228,6 +231,7 @@ const attackOverlayEl = document.getElementById("attackOverlay");
|
||||
const attackModalBodyEl = document.getElementById("attackModalBody");
|
||||
const attackModalYesEl = document.getElementById("attackModalYes");
|
||||
const attackModalNoEl = document.getElementById("attackModalNo");
|
||||
const teamQuotaEl = document.getElementById("teamActionsRemaining");
|
||||
// ── Cell helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
export function cellKey(x, y) { return `${x},${y}`; }
|
||||
@@ -249,7 +253,7 @@ export function isOwnTile(key) { const m = cellMeta(key); return m !== nul
|
||||
// ── Config display ────────────────────────────────────────────────────────────
|
||||
|
||||
export function applyConfigPayload(data) {
|
||||
GAME_CONFIG.clickCooldownSeconds = Number(data.clickCooldownSeconds) || 0;
|
||||
GAME_CONFIG.dailyActionQuota = Number(data.dailyActionQuota) || 100;
|
||||
GAME_CONFIG.databaseWipeoutIntervalSeconds = Number(data.databaseWipeoutIntervalSeconds) || 21600;
|
||||
GAME_CONFIG.debugModeForTeams = Boolean(data.debugModeForTeams);
|
||||
GAME_CONFIG.configReloadIntervalSeconds = Math.max(5, Number(data.configReloadIntervalSeconds) || 30);
|
||||
@@ -264,8 +268,16 @@ export function applyConfigPayload(data) {
|
||||
if (data.militaryPower && typeof data.militaryPower === "object") {
|
||||
GAME_CONFIG.militaryPower = data.militaryPower;
|
||||
}
|
||||
if (data.actionsRemaining !== null && data.actionsRemaining !== undefined) {
|
||||
actionsRemaining = Number(data.actionsRemaining);
|
||||
updateActionsDisplay();
|
||||
}
|
||||
if (data.teamActionsRemaining !== null && data.teamActionsRemaining !== undefined) {
|
||||
teamActionsRemaining = Number(data.teamActionsRemaining);
|
||||
updateTeamQuotaDisplay();
|
||||
}
|
||||
|
||||
cooldownCfgEl.textContent = String(GAME_CONFIG.clickCooldownSeconds);
|
||||
cooldownCfgEl.textContent = String(GAME_CONFIG.dailyActionQuota);
|
||||
seedDisplayEl.textContent = GAME_CONFIG.worldSeed || "—";
|
||||
nextPeriodEl.textContent = GAME_CONFIG.seedPeriodEndsAtUtc
|
||||
? new Date(GAME_CONFIG.seedPeriodEndsAtUtc).toISOString().replace("T", " ").slice(0, 19) + "Z"
|
||||
@@ -340,15 +352,13 @@ export async function loadMilitaryDeductions() {
|
||||
}
|
||||
|
||||
function updateEffectiveCooldownDisplay() {
|
||||
const base = GAME_CONFIG.clickCooldownSeconds;
|
||||
const base = GAME_CONFIG.dailyActionQuota;
|
||||
const bonus = currentTeam === "blue" ? elemBonusBlue : elemBonusRed;
|
||||
const effective = base / (1 + bonus / 100);
|
||||
const effective = Math.floor(base * (1 + bonus / 100));
|
||||
if (elemBonusBlueEl) elemBonusBlueEl.textContent = elemBonusBlue.toFixed(2);
|
||||
if (elemBonusRedEl) elemBonusRedEl.textContent = elemBonusRed.toFixed(2);
|
||||
if (effectiveCooldownEl) {
|
||||
effectiveCooldownEl.textContent = effective < 1
|
||||
? `${(effective * 1000).toFixed(0)}ms`
|
||||
: `${effective.toFixed(2)}s`;
|
||||
effectiveCooldownEl.textContent = String(effective);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,10 +386,10 @@ export async function tickElementBonus() {
|
||||
updateEffectiveCooldownDisplay();
|
||||
}
|
||||
|
||||
export function getEffectiveCooldown() {
|
||||
const base = GAME_CONFIG.clickCooldownSeconds;
|
||||
export function getEffectiveQuota() {
|
||||
const base = GAME_CONFIG.dailyActionQuota;
|
||||
const bonus = currentTeam === "blue" ? elemBonusBlue : elemBonusRed;
|
||||
return base / (1 + bonus / 100);
|
||||
return Math.floor(base * (1 + bonus / 100));
|
||||
}
|
||||
|
||||
export async function loadDbInfo() {
|
||||
@@ -534,56 +544,40 @@ export async function fetchGridForSeed(seed, depth = 0) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Cooldown ──────────────────────────────────────────────────────────────────
|
||||
// ── Action quota ──────────────────────────────────────────────────────────────
|
||||
|
||||
/** Updates the actions remaining display in the sidebar. */
|
||||
function updateActionsDisplay() {
|
||||
countdownEl.textContent = String(Math.max(0, actionsRemaining));
|
||||
refreshCursorFromLast();
|
||||
}
|
||||
|
||||
/** Updates the team quota display in the sidebar. */
|
||||
function updateTeamQuotaDisplay() {
|
||||
if (teamQuotaEl) {
|
||||
teamQuotaEl.textContent = teamActionsRemaining !== null ? String(Math.max(0, teamActionsRemaining)) : "—";
|
||||
}
|
||||
refreshCursorFromLast();
|
||||
}
|
||||
|
||||
/** Returns true when the user has no actions left for today's quota window. */
|
||||
export function cooldownActive() {
|
||||
if (GAME_CONFIG.clickCooldownSeconds <= 0) return false;
|
||||
return Date.now() < teamCooldownEndMs[currentTeam];
|
||||
}
|
||||
|
||||
function remainingSecs() {
|
||||
return Math.max(0, (teamCooldownEndMs[currentTeam] - Date.now()) / 1000);
|
||||
}
|
||||
|
||||
function tickCooldown() {
|
||||
if (GAME_CONFIG.clickCooldownSeconds <= 0) { countdownWrap.classList.add("hidden"); return; }
|
||||
const left = remainingSecs();
|
||||
if (left <= 0) {
|
||||
countdownWrap.classList.add("hidden");
|
||||
countdownEl.textContent = "0";
|
||||
teamCooldownEndMs[currentTeam] = 0;
|
||||
refreshCursorFromLast();
|
||||
// Auto-refresh from server when cooldown expires
|
||||
refreshFromServer();
|
||||
return;
|
||||
}
|
||||
countdownWrap.classList.remove("hidden");
|
||||
countdownEl.textContent = String(Math.ceil(left));
|
||||
refreshCursorFromLast();
|
||||
rafId = requestAnimationFrame(tickCooldown);
|
||||
return actionsRemaining <= 0;
|
||||
}
|
||||
|
||||
/** Decrements the local actions counter by 1 after a successful reveal. */
|
||||
export function startCooldown() {
|
||||
const secs = getEffectiveCooldown();
|
||||
if (secs <= 0) {
|
||||
teamCooldownEndMs[currentTeam] = 0;
|
||||
countdownWrap.classList.add("hidden");
|
||||
refreshCursorFromLast();
|
||||
return;
|
||||
}
|
||||
teamCooldownEndMs[currentTeam] = Date.now() + secs * 1000;
|
||||
countdownWrap.classList.remove("hidden");
|
||||
countdownEl.textContent = String(secs);
|
||||
cancelAnimationFrame(rafId);
|
||||
rafId = requestAnimationFrame(tickCooldown);
|
||||
actionsRemaining = Math.max(0, actionsRemaining - 1);
|
||||
updateActionsDisplay();
|
||||
refreshCursorFromLast();
|
||||
}
|
||||
|
||||
/** Resets local action state (called on world seed change or re-login). */
|
||||
export function clearCooldown() {
|
||||
teamCooldownEndMs.blue = 0;
|
||||
teamCooldownEndMs.red = 0;
|
||||
cancelAnimationFrame(rafId);
|
||||
countdownWrap.classList.add("hidden");
|
||||
actionsRemaining = 0;
|
||||
teamActionsRemaining = null;
|
||||
updateActionsDisplay();
|
||||
updateTeamQuotaDisplay();
|
||||
}
|
||||
|
||||
// ── Draw ──────────────────────────────────────────────────────────────────────
|
||||
@@ -829,7 +823,7 @@ async function onCanvasClick(ev) {
|
||||
if (isOwnTile(key)) { showLocalSelection(cell.x, cell.y); return; }
|
||||
|
||||
if (cooldownActive()) {
|
||||
hint.textContent = "Clic indisponible — attendez la fin du délai ou cliquez sur une tuile déjà découverte.";
|
||||
hint.textContent = "Quota d'actions épuisé pour aujourd'hui — revenez après 12h00 UTC.";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -841,6 +835,12 @@ async function onCanvasClick(ev) {
|
||||
draw();
|
||||
return;
|
||||
}
|
||||
if (res.status === 429) {
|
||||
actionsRemaining = 0;
|
||||
updateActionsDisplay();
|
||||
hint.textContent = "Quota d'actions épuisé pour aujourd'hui — revenez après 12h00 UTC.";
|
||||
return;
|
||||
}
|
||||
if (res.status === 410) {
|
||||
hint.textContent = "Le serveur change sa base de planètes — synchronisation...";
|
||||
await refreshFromServer();
|
||||
@@ -880,7 +880,6 @@ export async function refreshFromServer() {
|
||||
try {
|
||||
const seedChanged = await fetchConfig();
|
||||
if (seedChanged) {
|
||||
clearCooldown();
|
||||
resetEconScores();
|
||||
loadEconScores();
|
||||
details.textContent = "Les stats sont vides jusqu'au clic sur une tuile.";
|
||||
|
||||
Reference in New Issue
Block a user