feat(multi): award points based on both clicks and time
This commit is contained in:
@@ -54,11 +54,27 @@ function prunePlayers(players: Player[]): Player[] {
|
|||||||
return players.filter((p) => now - p.lastSeen < PLAYER_TIMEOUT_MS);
|
return players.filter((p) => now - p.lastSeen < PLAYER_TIMEOUT_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// En mode all_finish : calcule les points selon le rang d'arrivée
|
// Calcule les points d'un joueur selon ses clics et son temps
|
||||||
// 1er = 10pts, 2ème = 7pts, 3ème = 5pts, reste = 3pts, forfait = 0pts
|
// Points clics : max 10, -1 par tranche de 5 clics
|
||||||
const RANK_POINTS = [10, 7, 5, 3];
|
// Points temps : max 10, selon le temps restant (avec timeLimit) ou -1 par 30s (sans limite)
|
||||||
function getRankPoints(rank: number): number {
|
// Total max : 20 pts
|
||||||
return RANK_POINTS[rank] ?? RANK_POINTS[RANK_POINTS.length - 1];
|
function calcPlayerPoints(player: Player, room: Room): number {
|
||||||
|
const clicks = Math.max(0, (player.path?.length ?? 1) - 1);
|
||||||
|
const clickPts = Math.max(0, 10 - Math.floor(clicks / 5));
|
||||||
|
|
||||||
|
const wonAt = player.wonAt ?? Date.now();
|
||||||
|
const roundStart = room.roundStart ?? wonAt;
|
||||||
|
const elapsed = wonAt - roundStart;
|
||||||
|
|
||||||
|
let timePts: number;
|
||||||
|
if (room.timeLimit > 0) {
|
||||||
|
const timeLeft = room.timeLimit * 1000 - elapsed;
|
||||||
|
timePts = Math.max(0, Math.floor((timeLeft / (room.timeLimit * 1000)) * 10));
|
||||||
|
} else {
|
||||||
|
timePts = Math.max(0, 10 - Math.floor(elapsed / 30000));
|
||||||
|
}
|
||||||
|
|
||||||
|
return clickPts + timePts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vérifie si la manche doit se terminer en mode all_finish
|
// Vérifie si la manche doit se terminer en mode all_finish
|
||||||
@@ -67,14 +83,12 @@ function checkAllFinished(room: Room): boolean {
|
|||||||
return room.players.every((p) => p.hasWon || p.hasSurrendered);
|
return room.players.every((p) => p.hasWon || p.hasSurrendered);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attribue les points en mode all_finish selon l'ordre d'arrivée
|
// Attribue les points aux gagnants selon clics + temps
|
||||||
function assignAllFinishPoints(room: Room) {
|
function assignAllFinishPoints(room: Room) {
|
||||||
const winners = room.players.filter((p) => p.hasWon);
|
const winners = room.players.filter((p) => p.hasWon);
|
||||||
// On leur attribue les points selon leur rang (ordre d'arrivée = ordre dans le tableau, déjà fixé au moment où ils ont gagné)
|
winners.forEach((p) => {
|
||||||
winners.forEach((p, i) => {
|
p.score += calcPlayerPoints(p, room);
|
||||||
p.score += getRankPoints(i);
|
|
||||||
});
|
});
|
||||||
// roundWinner = premier arrivé
|
|
||||||
if (winners.length > 0) {
|
if (winners.length > 0) {
|
||||||
room.roundWinner = winners[0].id;
|
room.roundWinner = winners[0].id;
|
||||||
}
|
}
|
||||||
@@ -156,6 +170,7 @@ export async function PATCH(
|
|||||||
hasSurrendered: false,
|
hasSurrendered: false,
|
||||||
isHost: false,
|
isHost: false,
|
||||||
lastSeen: Date.now(),
|
lastSeen: Date.now(),
|
||||||
|
wonAt: null,
|
||||||
};
|
};
|
||||||
room.players.push(player);
|
room.players.push(player);
|
||||||
await saveRoom(room);
|
await saveRoom(room);
|
||||||
@@ -212,6 +227,7 @@ export async function PATCH(
|
|||||||
p.path = [startArticle];
|
p.path = [startArticle];
|
||||||
p.hasWon = false;
|
p.hasWon = false;
|
||||||
p.hasSurrendered = false;
|
p.hasSurrendered = false;
|
||||||
|
p.wonAt = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveRoom(room);
|
await saveRoom(room);
|
||||||
@@ -259,6 +275,7 @@ export async function PATCH(
|
|||||||
normalize(player.currentArticle) === normalize(room.targetArticle)
|
normalize(player.currentArticle) === normalize(room.targetArticle)
|
||||||
) {
|
) {
|
||||||
player.hasWon = true;
|
player.hasWon = true;
|
||||||
|
player.wonAt = Date.now();
|
||||||
|
|
||||||
if (room.gameMode === "race") {
|
if (room.gameMode === "race") {
|
||||||
// Mode course : 1er arrivé gagne la manche immédiatement
|
// Mode course : 1er arrivé gagne la manche immédiatement
|
||||||
@@ -266,7 +283,7 @@ export async function PATCH(
|
|||||||
(p) => p.hasWon && p.id !== player.id,
|
(p) => p.hasWon && p.id !== player.id,
|
||||||
);
|
);
|
||||||
if (!alreadyWon) {
|
if (!alreadyWon) {
|
||||||
player.score += 10;
|
player.score += calcPlayerPoints(player, room);
|
||||||
room.roundWinner = player.id;
|
room.roundWinner = player.id;
|
||||||
room.phase = "results";
|
room.phase = "results";
|
||||||
}
|
}
|
||||||
@@ -386,6 +403,7 @@ export async function PATCH(
|
|||||||
p.hasWon = false;
|
p.hasWon = false;
|
||||||
p.hasSurrendered = false;
|
p.hasSurrendered = false;
|
||||||
p.currentArticle = "";
|
p.currentArticle = "";
|
||||||
|
p.wonAt = null;
|
||||||
}
|
}
|
||||||
await saveRoom(room);
|
await saveRoom(room);
|
||||||
return Response.json({ room });
|
return Response.json({ room });
|
||||||
@@ -410,6 +428,7 @@ export async function PATCH(
|
|||||||
p.hasWon = false;
|
p.hasWon = false;
|
||||||
p.hasSurrendered = false;
|
p.hasSurrendered = false;
|
||||||
p.currentArticle = "";
|
p.currentArticle = "";
|
||||||
|
p.wonAt = null;
|
||||||
}
|
}
|
||||||
await saveRoom(room);
|
await saveRoom(room);
|
||||||
return Response.json({ room });
|
return Response.json({ room });
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type Player = {
|
|||||||
hasSurrendered: boolean;
|
hasSurrendered: boolean;
|
||||||
isHost: boolean;
|
isHost: boolean;
|
||||||
lastSeen: number; // timestamp ms
|
lastSeen: number; // timestamp ms
|
||||||
|
wonAt: number | null; // timestamp ms quand le joueur a trouvé l'article cible
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Room = {
|
export type Room = {
|
||||||
@@ -152,6 +153,7 @@ export async function POST(request: NextRequest) {
|
|||||||
hasSurrendered: false,
|
hasSurrendered: false,
|
||||||
isHost: true,
|
isHost: true,
|
||||||
lastSeen: Date.now(),
|
lastSeen: Date.now(),
|
||||||
|
wonAt: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user