| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- import { _decorator, Button, Color, EditBox, instantiate, Label, Node, NodePool, Prefab, ProgressBar, RichText, Sprite, SpriteFrame, UITransform } from 'cc';
- import { UIView } from '@scripts/framework/ui/UIView';
- import { AppKernel } from '@scripts/app/AppKernel';
- import { UIViewID } from '@scripts/define/UIDefine';
- import { UICloseReason } from '@scripts/framework/ui/UILayer';
- import { formatCurrency } from '@scripts/utils/Helper';
- import { addBtnClick } from '@scripts/utils/UIHelper';
- import { RewardItem, RewardType } from './Tasks/RewardItem';
- import { NodeItem } from './Prize/NodeItem';
- const { ccclass, property } = _decorator;
- interface CDTask {
- targetTs: number;
- label: Label;
- format?: (timeStr: string) => string; // 改成接收已经格式化好的时间字符串,可选
- timerFunc: () => void;
- }
- @ccclass('SeasonPassView')
- export class SeasonPassView extends UIView {
- readonly viewId = UIViewID.SeasonPassView;
- @property(Node)
- btn_close: Node = null;
- @property(Node)
- btn_tasks: Node = null;
- @property(Node)
- btn_prize: Node = null;
- @property(Node)
- btn_buyPass: Node = null;
- @property(Node)
- tasks: Node = null;
- @property(Node)
- prize: Node = null;
- @property(Label)
- season_end: Label = null;
- @property(Label)
- tasks_daily_done: Label = null;
- @property(Label)
- tasks_daily_reset: Label = null;
- @property(Node)
- tasks_daily_content: Node = null;
- @property(Label)
- tasks_seasonal_done: Label = null;
- @property(Label)
- tasks_seasonal_reset: Label = null;
- @property(Node)
- tasks_seasonal_content: Node = null;
- @property(Prefab)
- tasks_reward_item: Prefab = null;
- @property(Node)
- prize_content: Node = null;
- @property(Prefab)
- prize_node_item: Prefab = null;
- @property(Node)
- prize_fixed_dlx: Node = null;
- @property(Node)
- redPoint_tasks: Node = null;
- @property(Node)
- redPoint_prize: Node = null;
- @property(ProgressBar)
- progressBar: ProgressBar = null;
- @property(Label)
- progress_label: Label = null;
- @property(Label)
- next_need_exp: Label = null;
- @property(Node)
- scrollView_content: Node = null;
- private _seasonPassData: any = null;
- private _dailyItemPool: NodePool = new NodePool();
- private _dailyUsedItems: Node[] = [];
- private _seasonalItemPool: NodePool = new NodePool();
- private _seasonalUsedItems: Node[] = [];
- private _prizeItemPool: NodePool = new NodePool();
- private _prizeUsedItems: Node[] = [];
- protected async onBeforeOpen(_params: unknown): Promise<boolean> {
- const seasonPassStore = AppKernel.stores.seasonPassStore;
- const res = await seasonPassStore.reqSeasonPassInfo();
- return res;
- }
- protected onCreate(): void | Promise<void> {
- AppKernel.events.on('seasonPass:update', this.updateSeasonPassView, this);
- AppKernel.events.on('checkIn:update', this.handleCheckInUpdate, this);
- addBtnClick(this.btn_close, this.onBtnCloseClick, this);
- addBtnClick(this.btn_tasks, this.onBtnTasksClick, this);
- addBtnClick(this.btn_prize, this.onBtnPrizeClick, this);
- addBtnClick(this.btn_buyPass, this.onBtnBuyPassClick, this);
- this.initView();
- }
- // protected onBeforeClose(_reason: UICloseReason): boolean | Promise<boolean> {
- // return true;
- // }
- private handleCheckInUpdate() {
- AppKernel.stores.seasonPassStore.reqSeasonPassInfo();
- }
- private updateSeasonPassView() {
- this._seasonPassData = AppKernel.stores.seasonPassStore.seasonPassData;
- this.updateTasks();
- this.updatePrize();
- this.updateRedPoint();
- this.updateProgress();
- }
- private updateTasks() {
- const { season_tasks } = this._seasonPassData ?? {};
- this.updateDailyTasks();
- this.updateSeasonalTasks();
- const seasonal_targetTs = season_tasks?.end_at * 1000;
- this.startTimer(seasonal_targetTs, this.season_end);
- }
- private updateDailyTasks() {
- const { tasks, task_end_timestamp } = this._seasonPassData ?? {};
- // 将当前正在使用的item全部回收进对象池
- for (const node of this._dailyUsedItems) {
- this._dailyItemPool.put(node);
- }
- this._dailyUsedItems.length = 0;
- for (const task of tasks ?? []) {
- let itemNode: Node;
- if (this._dailyItemPool.size() > 0) {
- itemNode = this._dailyItemPool.get();
- } else {
- itemNode = instantiate(this.tasks_reward_item);
- }
- itemNode.active = true;
- itemNode.parent = this.tasks_daily_content;
- const itemCmp = itemNode.getComponent(RewardItem);
- if (itemCmp) itemCmp.initItem(task, RewardType.Daily);
- this._dailyUsedItems.push(itemNode);
- }
- const daily_done = tasks?.filter(task => task.status > 0)?.length ?? 0;
- this.tasks_daily_done.string = `${daily_done}/${tasks?.length ?? 0} DONE`;
- const daily_targetTs = task_end_timestamp * 1000;
- this.startTimer(daily_targetTs, this.tasks_daily_reset, (cd) => "RESET IN " + cd);
- }
- private updateSeasonalTasks() {
- const { season_tasks } = this._seasonPassData ?? {};
- for (const node of this._seasonalUsedItems) {
- this._seasonalItemPool.put(node);
- }
- this._seasonalUsedItems.length = 0;
- for (const task of season_tasks?.tasks ?? []) {
- let itemNode: Node;
- if (this._seasonalItemPool.size() > 0) {
- itemNode = this._seasonalItemPool.get();
- } else {
- itemNode = instantiate(this.tasks_reward_item);
- }
- itemNode.active = true;
- itemNode.parent = this.tasks_seasonal_content;
- const itemCmp = itemNode.getComponent(RewardItem);
- if (itemCmp) itemCmp.initItem(task, RewardType.Seasonal);
- this._seasonalUsedItems.push(itemNode);
- }
- const seasonal_done = season_tasks?.tasks.filter(task => task.status > 0)?.length ?? 0;
- this.tasks_seasonal_done.string = `${seasonal_done}/${season_tasks?.tasks.length ?? 0} DONE`;
- const seasonal_targetTs = season_tasks?.end_at * 1000;
- this.startTimer(seasonal_targetTs, this.tasks_seasonal_reset, (cd) => "RESET IN " + cd);
- }
- private updatePrize() {
- const { pass } = this._seasonPassData ?? {};
- const { current_xp, is_deluxe } = pass ?? {};
- // 将当前正在使用的item全部回收进对象池
- for (const node of this._prizeUsedItems) {
- this._prizeItemPool.put(node);
- }
- this._prizeUsedItems.length = 0;
- for (const node of pass?.nodes ?? []) {
- let itemNode: Node;
- if (this._prizeItemPool.size() > 0) {
- itemNode = this._prizeItemPool.get();
- } else {
- itemNode = instantiate(this.prize_node_item);
- }
- itemNode.active = true;
- itemNode.parent = this.prize_content;
- const itemCmp = itemNode.getComponent(NodeItem);
- if (itemCmp) itemCmp.initItem({ node, current_xp, is_deluxe });
- this._prizeUsedItems.push(itemNode);
- }
- const activeLabel = this.prize_fixed_dlx.getChildByName('label');
- const btn_unlock = this.prize_fixed_dlx.getChildByName('btn_unlock');
- const lock = this.prize_fixed_dlx.getChildByName('lock');
- activeLabel.active = is_deluxe;
- btn_unlock.active = !is_deluxe;
- lock.active = !is_deluxe;
- }
- private updateRedPoint() {
- const { tasks, season_tasks, pass } = this._seasonPassData ?? {};
- const hasTaskReward = tasks?.some(task => task.status === 1) ?? false;
- const hasSeasonalReward = season_tasks?.tasks.some(task => task.status === 1) ?? false;
- const { is_deluxe, nodes } = pass ?? {};
- const hasFreePrizeReward = nodes?.some(node => {
- const unlocked = node.unlocked === 1;
- const canClaim = node.free.claimed === 0;
- const hasReward = Object.values(node.free.reward)?.some(reward => reward != 0);
- return unlocked && canClaim && hasReward;
- });
- const hasDeluxePrizeReward = nodes?.some(node => {
- const unlocked = node.unlocked === 1;
- const canClaim = node.deluxe.claimed === 0;
- const hasReward = Object.values(node.deluxe.reward)?.some(reward => reward != 0);
- return unlocked && canClaim && hasReward && is_deluxe;
- })
- console.log("hasTaskReward", hasTaskReward, "hasSeasonalReward", hasSeasonalReward, "hasFreePrizeReward", hasFreePrizeReward, "hasDeluxePrizeReward", hasDeluxePrizeReward);
- this.redPoint_tasks.active = hasTaskReward || hasSeasonalReward;
- this.redPoint_prize.active = hasFreePrizeReward || hasDeluxePrizeReward;
- }
- private updateProgress() {
- const { pass } = this._seasonPassData ?? {};
- const { current_xp, nodes } = pass ?? {};
- const next_need_exp = nodes?.find(node => node.required_xp > current_xp)?.required_xp;
- if (!next_need_exp) {
- this.progress_label.string = 'MAX';
- this.progressBar.progress = 1;
- this.next_need_exp.string = 'MAX LEVEL';
- } else {
- this.progress_label.string = `${current_xp}/${next_need_exp ?? 0} XP`;
- this.progressBar.progress = current_xp / next_need_exp;
- this.next_need_exp.string = `${next_need_exp - current_xp} XP TO\nNEXT PRIZE`;
- }
- }
- // 弹窗关闭,回收节点
- protected async onAfterClose() {
- for (const node of this._dailyUsedItems) {
- this._dailyItemPool.put(node);
- }
- this._dailyUsedItems.length = 0;
- for (const node of this._seasonalUsedItems) {
- this._seasonalItemPool.put(node);
- }
- this._seasonalUsedItems.length = 0;
- for (const node of this._prizeUsedItems) {
- this._prizeItemPool.put(node);
- }
- this._prizeUsedItems.length = 0;
- }
- // 弹窗销毁,清空池子释放资源
- protected onDestroy(): void {
- this._dailyItemPool.clear();
- this._seasonalItemPool.clear();
- this._prizeItemPool.clear();
- this.stopAllTimer();
- AppKernel.events.off('seasonPass:update', this.updateSeasonPassView, this);
- AppKernel.events.off('checkIn:update', this.handleCheckInUpdate, this);
- super.onDestroy();
- }
- private initView() {
- this.onToggleSidebar("tasks");
- // const items_data = new Array(10).fill(0);
- // items_data.forEach((item, index) => {
- // const item_DLX = instantiate(this.item_DLX);
- // item_DLX.parent = this.item_DLX.parent;
- // item_DLX.active = true;
- // const item_free = instantiate(this.item_free);
- // item_free.parent = this.item_free.parent;
- // item_free.active = true;
- // const item_progress = instantiate(this.item_progress);
- // item_progress.parent = this.item_progress.parent;
- // item_progress.active = true;
- // })
- // this.scheduleOnce(() => {
- // console.log("item_DLX.parent.width", this.item_DLX.parent.getComponent(UITransform).width)
- // console.log("item_free.parent.width", this.item_free.parent.getComponent(UITransform).width)
- // console.log("item_progress.parent.width", this.item_progress.parent.getComponent(UITransform).width)
- // this.scrollView_content.getComponent(UITransform).width = this.item_free.parent.getComponent(UITransform).width;
- // })
- }
- private onBtnCloseClick() {
- AppKernel.ui.close(this.viewId);
- }
- private onToggleSidebar(type: string) {
- const selected_tasks = this.btn_tasks.getChildByName('selected');
- const unselected_tasks = this.btn_tasks.getChildByName('unselected');
- const selected_prize = this.btn_prize.getChildByName('selected');
- const unselected_prize = this.btn_prize.getChildByName('unselected');
- selected_tasks.active = type === "tasks";
- unselected_tasks.active = type !== "tasks";
- selected_prize.active = type === "prize";
- unselected_prize.active = type !== "prize";
- this.tasks.active = type === "tasks";
- this.prize.active = type === "prize";
- }
- private onBtnTasksClick() {
- this.onToggleSidebar("tasks");
- }
- private onBtnPrizeClick() {
- this.onToggleSidebar("prize");
- }
- private onBtnBuyPassClick() {
- const { pass } = this._seasonPassData ?? {};
- const gear = pass?.purchase_gear;
- if (!gear) return;
- AppKernel.ui.open(UIViewID.SeasonPassPurchase, gear);
- }
- private _cdTaskList: CDTask[] = [];
- /**
- * 开启倒计时
- * @param targetTs 结束时间戳(ms)
- * @param label 显示文字的Label
- * @param format 格式化回调,入参剩余秒数,返回要展示的字符串
- */
- private startTimer(targetTs: number, label: Label, format?: (timeStr: string) => string) {
- if (targetTs == null || isNaN(targetTs)) return;
- // 先查找这个label有没有正在跑的倒计时,存在就先停止旧的
- const existIdx = this._cdTaskList.findIndex(item => item.label === label);
- if (existIdx >= 0) {
- const oldTask = this._cdTaskList[existIdx];
- this.unschedule(oldTask.timerFunc);
- this._cdTaskList.splice(existIdx, 1);
- }
- // 计算当前剩余秒数
- const diff = targetTs - Date.now();
- const remainSec = Math.max(0, Math.ceil(diff / 1000));
- const timeStr = this.formatHMS(remainSec);
- // 有format就调用,没有直接用timeStr
- label.string = format ? format(timeStr) : timeStr;
- if (remainSec <= 0) {
- return;
- }
- // 新建任务
- const task: CDTask = {
- targetTs,
- label,
- format,
- timerFunc: () => this.onUpdateCDTask(task)
- };
- this._cdTaskList.push(task);
- this.onUpdateCDTask(task);
- this.schedule(task.timerFunc, 1);
- }
- private stopTimerByLabel(label: Label) {
- const idx = this._cdTaskList.findIndex(item => item.label === label);
- if (idx >= 0) {
- const task = this._cdTaskList[idx];
- this.unschedule(task.timerFunc);
- this._cdTaskList.splice(idx, 1);
- }
- }
- private stopAllTimer() {
- for (const task of this._cdTaskList) {
- this.unschedule(task.timerFunc);
- }
- this._cdTaskList.length = 0;
- }
- private onUpdateCDTask(task: CDTask) {
- const now = Date.now();
- const diff = task.targetTs - now;
- const remainSec = Math.max(0, Math.ceil(diff / 1000));
- const timeStr = this.formatHMS(remainSec);
- // 存在format回调就调用,没有直接使用时间字符串
- task.label.string = task.format ? task.format(timeStr) : timeStr;
- if (remainSec <= 0) {
- this.unschedule(task.timerFunc);
- const idx = this._cdTaskList.indexOf(task);
- if (idx > -1) {
- this._cdTaskList.splice(idx, 1);
- }
- }
- }
- private formatHMS(totalSec: number): string {
- const sec = Math.max(0, Math.floor(totalSec));
- const d = Math.floor(sec / (3600 * 24));
- const h = Math.floor((sec % (3600 * 24)) / 3600);
- const m = Math.floor((sec % 3600) / 60);
- const s = sec % 60;
- const pad = (n: number) => n.toString().padStart(2, '0');
- const hhmmss = `${pad(h)}:${pad(m)}:${pad(s)}`;
- if (d > 0) {
- return `${d}:${hhmmss}`;
- }
- return hhmmss;
- }
- }
|