- Change VITE_API_URL in .env from localhost:3000 to localhost:3001 for backend access. - Update index.html to replace favicon with logo.png and ensure proper HTML structure. - Add new dependencies for WangEditor in package.json and package-lock.json to support rich text editing features.
111 lines
3.2 KiB
JavaScript
111 lines
3.2 KiB
JavaScript
const { POWER_CALC } = require('../config/constants');
|
||
|
||
/**
|
||
* 战力值计算服务
|
||
*/
|
||
class PowerCalculator {
|
||
/**
|
||
* 计算单场比赛战力值变动
|
||
* @param {Object} winner - 胜者信息 { powerScore, level, protectedMatches }
|
||
* @param {Object} loser - 败者信息 { powerScore, level, protectedMatches }
|
||
* @param {number} matchWeight - 比赛权重
|
||
* @returns {Object} { winnerChange, loserChange }
|
||
*/
|
||
static calculate(winner, loser, matchWeight = 1.0) {
|
||
const { BASE_WIN, BASE_LOSE, UNDERDOG_THRESHOLD, UNDERDOG_RATE, MAX_CHANGE, NEWBIE_PROTECTION, NEWBIE_LOSE_RATE } = POWER_CALC;
|
||
|
||
// 计算分差
|
||
const diff = loser.powerScore - winner.powerScore;
|
||
|
||
// 基础得分
|
||
let winnerChange = BASE_WIN;
|
||
let loserChange = BASE_LOSE;
|
||
|
||
// 以下克上奖励
|
||
if (diff >= UNDERDOG_THRESHOLD) {
|
||
const bonus = Math.ceil(diff * UNDERDOG_RATE);
|
||
winnerChange += bonus;
|
||
}
|
||
|
||
// 应用比赛权重
|
||
winnerChange = Math.round(winnerChange * matchWeight);
|
||
loserChange = Math.round(loserChange * matchWeight);
|
||
|
||
// 新手保护(Lv1和Lv2,前5场)
|
||
if (loser.level <= 2 && loser.protectedMatches < NEWBIE_PROTECTION) {
|
||
loserChange = Math.round(loserChange * NEWBIE_LOSE_RATE);
|
||
}
|
||
|
||
// 封顶限制
|
||
winnerChange = Math.min(winnerChange, MAX_CHANGE);
|
||
loserChange = Math.max(loserChange, -MAX_CHANGE);
|
||
|
||
return {
|
||
winnerChange,
|
||
loserChange
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 检查挑战赛冷却期
|
||
* @param {Date} lastMatchTime - 上次比赛时间
|
||
* @returns {boolean} 是否可以挑战
|
||
*/
|
||
static canChallenge(lastMatchTime) {
|
||
if (!lastMatchTime) return true;
|
||
|
||
const cooldownDays = POWER_CALC.CHALLENGE_COOLDOWN;
|
||
const cooldownMs = cooldownDays * 24 * 60 * 60 * 1000;
|
||
const timeDiff = Date.now() - new Date(lastMatchTime).getTime();
|
||
|
||
return timeDiff >= cooldownMs;
|
||
}
|
||
|
||
/**
|
||
* 获取冷却剩余天数
|
||
* @param {Date} lastMatchTime - 上次比赛时间
|
||
* @returns {number} 剩余天数
|
||
*/
|
||
static getCooldownDays(lastMatchTime) {
|
||
if (!lastMatchTime) return 0;
|
||
|
||
const cooldownDays = POWER_CALC.CHALLENGE_COOLDOWN;
|
||
const cooldownMs = cooldownDays * 24 * 60 * 60 * 1000;
|
||
const timeDiff = Date.now() - new Date(lastMatchTime).getTime();
|
||
|
||
if (timeDiff >= cooldownMs) return 0;
|
||
|
||
return Math.ceil((cooldownMs - timeDiff) / (24 * 60 * 60 * 1000));
|
||
}
|
||
|
||
/**
|
||
* 判断升降级(排位赛结束后)
|
||
* 规则:
|
||
* - 只有冠军晋级(+1 段位,最高不超过 5 段)
|
||
* - 只有最后一名且当前段位为 4 或 5 时降级(-1 段位)
|
||
* @param {number} rank - 排名 (1-based)
|
||
* @param {number} totalPlayers - 总人数
|
||
* @returns {string} 'promote' | 'demote' | 'stay'
|
||
*/
|
||
static determinePromotion(rank, totalPlayers) {
|
||
if (rank === 1) {
|
||
return 'promote'; // 仅冠军晋级
|
||
}
|
||
if (rank === totalPlayers) {
|
||
return 'demote'; // 仅最后一名有降级资格(具体是否降,看段位)
|
||
}
|
||
return 'stay'; // 其它名次保持不变
|
||
}
|
||
|
||
/**
|
||
* 计算消费积分
|
||
* @param {number} amount - 消费金额
|
||
* @returns {number} 积分
|
||
*/
|
||
static calculateConsumePoints(amount) {
|
||
return Math.floor(amount / 10);
|
||
}
|
||
}
|
||
|
||
module.exports = PowerCalculator;
|