You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AdLike2048/assets/script/ui/Rank/RankItemView.ts

175 lines
7.2 KiB

3 weeks ago
import { Color, Label, Node, Sprite } from 'cc';
import { RankBoardEntry, RankBoardScope } from '../../manager/RankBoardManager';
import { RankDataManager } from '../../manager/RankDataManager';
import { HeadIconManager } from '../../manager/HeadIconManager';
import { TableNames } from '../../game/ConfigTableData';
import { SpriteLoad } from '../../../mx/components/SpriteLoad';
/** 排行条目:head / region / name / level / lay(num+星星) / winNum,以及名次背景 */
export class RankItemView {
/**
* 绑定排行条目
* @param scope 榜单类型,决定 num / winNum / level / 星星 展示
*/
static bind(root: Node, data: RankBoardEntry, scope: RankBoardScope, skipBg = false): void {
if (!root?.isValid) return;
const daily = scope === RankBoardScope.DailyWins;
this._bindRankDecor(root, data.rank, skipBg);
const regionNode = root.find('region');
this._setActive(regionNode, true);
this._setLabelAutoWidth(regionNode, this._formatRegion(data.region));
this._setLabelAutoWidth(root.find('name'), this._formatNickname(data.nickname));
const lay = root.find('lay');
const numNode = root.find('lay/num') ?? root.find('num');
const starNode = root.find('lay/星星') ?? root.find('星星');
const winNumNode = root.find('winNum');
this._setActive(root.find('level'), !daily);
this._setActive(lay, !daily);
this._setActive(numNode, !daily);
this._setActive(starNode, !daily);
this._setActive(winNumNode, daily);
if (!daily) {
const totalStars = RankDataManager.clampRankStars(data.score);
const intraStars = RankDataManager.getIntraTierStars(totalStars);
this._setLabel(root.find('level'), this._rankNameByScore(totalStars));
this._setLabel(numNode, String(intraStars));
} else {
this._setLabel(winNumNode, String(data.score ?? 0));
}
this._bindAvatar(root, data.avatarUrl);
}
/**
* rank1/2/3 前三名,otherRank 其他名次(rank 写入数字)。
* ui_rankItem 另切 bg1~bg4;ui_selfRank 不改背景。
*/
private static _bindRankDecor(root: Node, rank: number, skipBg: boolean): void {
const r = Math.max(0, Math.floor(Number(rank) || 0));
const top = r === 1 || r === 2 || r === 3 ? r : 0;
if (!skipBg) {
this._setActive(root.find('bg1'), top === 1);
this._setActive(root.find('bg2'), top === 2);
this._setActive(root.find('bg3'), top === 3);
this._setActive(root.find('bg4'), top === 0);
}
this._setActive(root.find('rank1'), top === 1);
this._setActive(root.find('rank2'), top === 2);
this._setActive(root.find('rank3'), top === 3);
const other = root.find('otherRank');
this._setActive(other, top === 0);
if (other?.isValid && top === 0) {
if(r > 100){
//100名以上未上榜
other.find('rank').active = false;
other.find('noRank').active = true;
this._setLabel(other.find('noRank'), "未上榜");
}else{
other.find('rank').active = true;
other.find('noRank').active = false;
this._setLabel(other.find('rank'), String(r));
}
}
}
private static _setActive(node: Node | null, active: boolean): void {
if (node?.isValid) node.active = active;
}
/** 地区展示 */
private static _formatRegion(region: string): string {
return String(region ?? '').trim();
}
/** 昵称展示最多 7 字 */
private static _formatNickname(nickname: string): string {
const text = String(nickname ?? '').trim() || '棋手';
return text.length > 7 ? text.slice(0, 7) : text;
}
/** 用总星数反查 Rank 表段位名 */
private static _rankNameByScore(score: number): string {
return (RankDataManager.resolveByStars(score)?.name ?? '').trim() || '初出茅庐';
}
private static _setLabel(node: Node | null, text: string): void {
if (!node?.isValid) return;
const label = node.getComponent(Label);
if (label) label.string = text;
}
/** region / name 等左对齐文案:按内容撑开宽度,避免 prefab 默认宽度过窄只显示一字 */
private static _setLabelAutoWidth(node: Node | null, text: string): void {
if (!node?.isValid) return;
const label = node.getComponent(Label);
if (!label) return;
label.overflow = Label.Overflow.NONE;
label.string = text;
label.updateRenderData(true);
}
/** headMask:远程头像用 head,本地头像用 localHead */
private static _bindAvatar(root: Node, url: string): void {
const headMask = root.find('headMask');
const headNode = headMask?.find('head') ?? root.find('head');
const localHeadNode = headMask?.find('localHead') ?? null;
const avatar = (url ?? '').trim();
if (!avatar) {
this._clearAvatarNode(headNode);
this._clearAvatarNode(localHeadNode);
this._setActive(headNode, false);
this._setActive(localHeadNode, false);
if (headMask?.isValid) headMask.active = false;
return;
}
if (headMask?.isValid) headMask.active = true;
const localId = this._parseLocalHeadIconId(avatar);
if (localId > 0) {
const row = gg.data.table.getTableData<ITableHeadicon>(TableNames.Headicon, localId);
if (!row) {
console.error('[RankItemView] Headicon 不存在', localId);
this._clearAvatarNode(headNode);
this._clearAvatarNode(localHeadNode);
this._setActive(headNode, false);
this._setActive(localHeadNode, false);
if (headMask?.isValid) headMask.active = false;
return;
}
this._setActive(headNode, false);
this._clearAvatarNode(headNode);
this._setActive(localHeadNode, true);
if (!localHeadNode?.isValid) {
console.error('[RankItemView] localHead 节点缺失', localId);
return;
}
localHeadNode.getOrAddComponent(SpriteLoad);
HeadIconManager.applyRowToNode(localHeadNode, row);
return;
}
this._setActive(localHeadNode, false);
this._clearAvatarNode(localHeadNode);
this._setActive(headNode, true);
if (!headNode?.isValid) return;
const sp = headNode.getComponent(Sprite);
if (sp) sp.color = Color.WHITE;
headNode.getOrAddComponent(SpriteLoad).loadRemote(avatar);
}
/** 清空头像节点 SpriteLoad */
private static _clearAvatarNode(node: Node | null): void {
if (!node?.isValid) return;
node.getComponent(SpriteLoad)?.clear();
}
/** 纯数字头像字段视为本地 Headicon.id */
private static _parseLocalHeadIconId(avatar: string): number {
const text = avatar.trim();
if (!/^\d+$/.test(text)) return 0;
const id = Math.floor(Number(text));
return id > 0 ? id : 0;
}
}