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/BattleFail/UIBattleFail.ts

412 lines
15 KiB

//*********************
// create by 流云
// time: Thu Sep 25 2025
// desc:
//*********************
import { _decorator, Label, Layout, Node, Sprite, tween, Tween, Vec3 } from 'cc';
import { Auto_BattleFail } from './Auto_BattleFail';
import { BattleSuccessCoinFlyPresenter } from '../BattleSuccess/BattleSuccessCoinFlyPresenter';
import { OtherDataType, StatisticsType, ItemNumType } from '../../game/GameData';
import { ItemData } from '../../game/ConfigProjectData';
import { UIID, GAudioPath } from '../../game/ConfigRes';
import { MatchWinStats } from '../../manager/MatchWinStats';
import { RankDataManager } from '../../manager/RankDataManager';
import { VivoAd } from '../../sdk/functions/VivoAd';
import { WeaponDropView } from '../BattleGame/BattleWeaponDropPresenter';
import { WeaponDropService, type WeaponDropRecord } from '../../battle/combat/WeaponDropService';
const { ccclass } = _decorator;
/** 降星 fillRange 动画时长(秒) */
const STAR_DROP_FILL_SEC = 1.5;
/** 展示变化前星级后再播动画的延迟(秒) */
const STAR_DROP_ANIM_DELAY_SEC = 0.45;
@ccclass('UIBattleFail')
export class UIBattleFail extends Auto_BattleFail {
/** 领取倍率(广告双倍为 2) */
addNum = 1;
/** 本局待领金币(不含倍率) */
private _pendingCoin = 0;
/** 结算奖励列表(领取时入账,不含金币) */
private _dataList: ItemData[] = [];
/** ui_content 宝石展示节点 */
private readonly _lootWeaponNodes: Node[] = [];
/** 金币飞行 */
private _coinFly: BattleSuccessCoinFlyPresenter | null = null;
/** 领取流程中 */
private _claiming = false;
/** 降星动画目标 index(star1~star5) */
private _starDropIndex = 0;
/** 降星动画结束后星条点亮数 */
private _starDropAfterCount = 0;
onLoad(): void {
super.onLoad();
this._hideGemTemp();
this._coinFly = new BattleSuccessCoinFlyPresenter();
this._coinFly.bind(this.ui_center);
}
/** 隐藏结算宝石模板,避免占位节点显示在面板上 */
private _hideGemTemp(): void {
if (!this.ui_gemTemp?.isValid) {
console.log('[UIBattleFail] ui_gemTemp 缺失');
return;
}
this.ui_gemTemp.active = false;
}
onEnable(): void {
super.onEnable();
gg.sdk.pfTriggerGC();
this._coinFly?.bind(this.ui_center);
}
onDisable(): void {
this._stopStarDropAnim();
this.unschedule(this._onStarDropAnimDelay);
this._recycleWeaponLoot();
this._coinFly?.unbind();
VivoAd.hideNative();
super.onDisable();
}
onInit(): void {
this.addNum = 1;
this._claiming = false;
this.btnGet.active = true;
this.btnADGet.active = true;
gg.audio.playEffect({ name: '游戏失败', path: GAudioPath.SfxUi });
const starsBefore = RankDataManager.getPlayerRankStars();
MatchWinStats.record(false, gg.game.CurentBattle?.opponentRankId ?? 0);
gg.sdk.reportDY('inLevel', `${gg.game.getReportDYRankLabel()}-失败`);
void this._refreshRankUi(starsBefore);
if (gg.game.CurentBattle.LevelConfigId == gg.game.CurentSelectLevelId) {
gg.data.setStatistics(StatisticsType.SuccessContinuousCount, 0);
}
// let value = gg.data.getkeyKeyValue(OtherDataType.FailReportData, gg.game.CurentBattle.getReportDYLevelId());
// let times = 1;
// if (value != '' && value != '0') {
// times = Number(value) + 1;
// }
//gg.data.setkeyKeyValue(OtherDataType.FailReportData, gg.game.CurentBattle.getReportDYLevelId(), times);
const curWaveNum = gg.game.CurentBattle.CurentWaveNum;
gg.sdk.reportDY("inLevel", `${gg.game.getReportDYBattleCountLabel(curWaveNum)}-过关失败`);
const levelId = gg.sdk.get30rLevelId();
gg.sdk.report30r('LEVEL_LOSE', {
level_id: levelId,
stage_type: gg.sdk.get30rStageType(),
ad_cnt: gg.sdk.get30rAdCount(),
action_param: levelId,
});
this.showReward();
this._refreshMoneyDisplay();
this._refreshCoinRewardDisplay();
if (!gg.game.CurentBattle.IsWatchAd && gg.game.CurentSelectLevelId >= 3) {
gg.sdk.showInterstitialAd(() => { }, () => { }, true);
}
}
/** 结算段位与星条:先展示降星前,再播当前星 fillRange 1→0 */
private _refreshRankUi(starsBefore: number): void {
void RankDataManager.ensureTable().then(() => {
if (!this.isValid) return;
const starsAfter = RankDataManager.getPlayerRankStars();
const beforeCount = RankDataManager.getStarModCount(starsBefore);
const afterCount = RankDataManager.getStarModCount(starsAfter);
const starDropped = starsAfter < starsBefore;
RankDataManager.applyRankLevelIconToNode(
this.ui_rankLevel,
RankDataManager.resolveTierByStars(starsAfter)?.id,
);
this._stopStarDropAnim();
this.unschedule(this._onStarDropAnimDelay);
if (!starDropped) {
this._applyStarDisplay(afterCount);
return;
}
this._starDropIndex = beforeCount;
this._starDropAfterCount = afterCount;
this._applyStarDisplay(beforeCount);
this.scheduleOnce(this._onStarDropAnimDelay, STAR_DROP_ANIM_DELAY_SEC);
});
}
private _onStarDropAnimDelay = (): void => {
this._playStarDropAnim(this._starDropIndex, this._starDropAfterCount);
};
/** ui_star:star1~star5,内层 star 控制点亮与 fillRange */
private _applyStarDisplay(litCount: number): void {
if (!this.ui_star?.isValid) {
console.log('[BattleFail] ui_star 缺失');
return;
}
const count = Math.max(0, Math.min(5, Math.floor(Number(litCount) || 0)));
for (let i = 1; i <= 5; i++) {
const slot = this.ui_star.getChildByName(`star${i}`);
if (!slot?.isValid) continue;
slot.active = true;
const icon = slot.getChildByName('star');
if (!icon?.isValid) continue;
Tween.stopAllByTarget(icon);
const sprite = icon.getComponent(Sprite);
if (sprite) {
Tween.stopAllByTarget(sprite);
sprite.fillRange = 1;
}
icon.active = i <= count;
}
}
/** 当前降掉的那颗星:fillRange 1→0,结束后刷新为降星后星条 */
private _playStarDropAnim(loseStarIndex: number, afterCount: number): void {
if (!this.isValid) return;
const slot = this.ui_star?.getChildByName(`star${loseStarIndex}`);
const icon = slot?.getChildByName('star');
const sprite = icon?.getComponent(Sprite);
if (!slot?.isValid || !icon?.isValid || !sprite) {
console.log('[BattleFail] 降星动画节点缺失', loseStarIndex);
this._applyStarDisplay(afterCount);
return;
}
icon.active = true;
Tween.stopAllByTarget(sprite);
sprite.fillRange = 1;
tween(sprite)
.to(STAR_DROP_FILL_SEC, { fillRange: 0 })
.call(() => {
if (!this.isValid) return;
this._applyStarDisplay(afterCount);
})
.start();
}
/** 停止降星 fillRange 动画 */
private _stopStarDropAnim(): void {
this.unschedule(this._onStarDropAnimDelay);
if (!this.ui_star?.isValid) return;
for (let i = 1; i <= 5; i++) {
const slot = this.ui_star.getChildByName(`star${i}`);
const icon = slot?.getChildByName('star');
if (icon?.isValid) Tween.stopAllByTarget(icon);
const sprite = icon?.getComponent(Sprite);
if (sprite) Tween.stopAllByTarget(sprite);
}
}
showReward(): void {
const levelId = gg.game.CurentBattle.LevelConfigId;
const rewardStr = gg.data.project.getLevelRewardStr(levelId, false);
if (!rewardStr) {
console.log('[BattleFail] Level 无 loserreward', levelId);
}
const pairs = gg.data.project.parseItemPipeNumList(rewardStr);
const mt = 1;
let addRatio = 1;
addRatio += gg.data.getFightRewardRatio();
const itemsArray: ItemData[] = [];
for (let i = 0; i < pairs.length; i++) {
const goodId = pairs[i].id;
const itemNum = pairs[i].num;
if (goodId === 104 || goodId === 105 || goodId === 106 || goodId === 107) {
continue;
}
if (gg.equip.getItemEquipId(goodId) > 0) {
continue;
}
let num = Math.floor(itemNum * addRatio);
if (goodId != 4) {
num = num * mt;
}
if (num <= 0) continue;
itemsArray.push(new ItemData(goodId, num));
}
this._appendLootRewards(itemsArray);
this._pendingCoin = this._extractCoinAmount(itemsArray);
this._dataList = itemsArray;
this._spawnWeaponLoot();
}
/** ui_content:本局掉落宝石(克隆 ui_gemTemp) */
private _spawnWeaponLoot(): void {
this._recycleWeaponLoot();
const root = this.ui_content;
if (!root?.isValid) return;
const drops = gg.game.CurentBattle?.weaponDrop?.allDrops ?? [];
const aggregated = WeaponDropService.aggregateByWeaponId(drops);
for (let i = 0; i < aggregated.length; i++) {
this._addWeaponLootNode(root, aggregated[i].record);
}
root.getComponent(Layout)?.updateLayout();
}
/**
* 在 ui_content 追加一颗宝石。模板只有 icon,不显示数量。
* @param root ui_content
* @param record 掉落记录
*/
private _addWeaponLootNode(root: Node, record: WeaponDropRecord): void {
const node = WeaponDropView.takeSettlePrefab(this.ui_gemTemp);
if (!node) return;
root.addChild(node);
node.setScale(Vec3.ZERO);
node.angle = 0;
WeaponDropView.applySettleVisual(node, record);
const view = node.getComponent(WeaponDropView) ?? node.addComponent(WeaponDropView);
view.playDisplay();
this._lootWeaponNodes.push(node);
}
/** 回收 ui_content 宝石节点 */
private _recycleWeaponLoot(): void {
for (let i = 0; i < this._lootWeaponNodes.length; i++) {
WeaponDropView.recycleSettleNode(this._lootWeaponNodes[i]);
}
this._lootWeaponNodes.length = 0;
}
/**
* 失败时把双方掉落碎片并入奖励列表(按道具 id 堆叠)。
* @param items 已解析的关卡失败奖励
*/
private _appendLootRewards(items: ItemData[]): void {
const drops = gg.game.CurentBattle?.weaponDrop?.collectRewardItems() ?? [];
for (let i = 0; i < drops.length; i++) {
const itemId = drops[i].itemId;
const num = drops[i].num;
let found = false;
for (let j = 0; j < items.length; j++) {
if (items[j].id !== itemId) continue;
items[j].num += num;
found = true;
break;
}
if (!found) items.push(new ItemData(itemId, num));
}
}
/** lb_money:当前拥有金币 */
private _refreshMoneyDisplay(): void {
if (!this.lb_money?.isValid) return;
this.lb_money.string = `${gg.data.getMoney()}`;
}
/** ui_coin/num:本局待领金币(含倍率) */
private _refreshCoinRewardDisplay(): void {
const numLb = this._coinNumLabel();
if (!numLb?.isValid) return;
const gain = Math.floor(this._pendingCoin * this.addNum);
numLb.string = gain > 0 ? `${gain}` : '0';
if (this.ui_coin?.isValid) this.ui_coin.active = gain > 0;
}
/** ui_coin 数量 Label */
private _coinNumLabel(): Label | null {
return this.ui_coin?.getChildByName('num')?.getComponent(Label) ?? null;
}
/** ui_coin 金币 icon */
private _coinIconNode(): Node | null {
return this.ui_coin?.getChildByName('icon') ?? null;
}
/** ui_money 金币 icon */
private _moneyCoinNode(): Node | null {
return this.ui_money?.getChildByName('金币') ?? null;
}
/** 从奖励列表汇总金币并移除(仅 ui_coin 展示) */
private _extractCoinAmount(items: ItemData[]): number {
let total = 0;
for (let i = items.length - 1; i >= 0; i--) {
if (items[i].id !== ItemNumType.money) continue;
total += Math.floor(Number(items[i].num) || 0);
items.splice(i, 1);
}
return total;
}
click_btnGet(): void {
if (this._claiming) return;
const curWaveNum = gg.game.CurentBattle.CurentWaveNum;
gg.sdk.reportDY("inLevel", `${gg.game.getReportDYBattleCountLabel(curWaveNum)}-失败领取奖励`);
this._claimWithCoinFly();
}
/** 金币影子飞行 → 入账 → 刷新 lb_money → 回大厅 */
private _claimWithCoinFly(): void {
if (this._claiming) return;
this._claiming = true;
this.btnGet.active = false;
this.btnADGet.active = false;
const coinGain = Math.floor(this._pendingCoin * this.addNum);
const icon = this._coinIconNode();
const target = this._moneyCoinNode();
const fly = this._coinFly;
if (!fly || coinGain <= 0 || !icon?.isValid || !target?.isValid) {
this._applyRewards();
this._pendingCoin = 0;
this._refreshMoneyDisplay();
this._refreshCoinRewardDisplay();
this._finishClaimNavigate();
return;
}
fly.play(icon, target, coinGain, () => {
if (!this.isValid) return;
this._applyRewards();
this._pendingCoin = 0;
this._refreshMoneyDisplay();
this._refreshCoinRewardDisplay();
this._finishClaimNavigate();
});
}
/** 发放除金币外的奖励并入账金币 */
private _applyRewards(): void {
for (let i = 0; i < this._dataList.length; i++) {
const value = this._dataList[i];
gg.data.addItemNum(Number(value.id), Number(value.num) * this.addNum);
}
const coinGain = Math.floor(this._pendingCoin * this.addNum);
if (coinGain > 0) gg.data.addItemNum(ItemNumType.money, coinGain);
gg.game.IsPlayedGame = true;
}
private _finishClaimNavigate(): void {
if (!this.isValid) return;
this._claiming = false;
gg.game.returnedFromBattleFail = true;
gg.game.returnedFromBattleFailChapterId = gg.game.CurentBattle.LevelConfigId;
gg.game.enterMain(UIID.Home);
}
click_btnADGet(): void {
if (this._claiming) return;
gg.sdk.showVideoAd((res) => {
if (res) {
const curWaveNum = gg.game.CurentBattle.CurentWaveNum;
gg.sdk.reportDY("inLevel", `${gg.game.getReportDYBattleCountLabel(curWaveNum)}-AD-失败双倍领取奖励`);
this.addNum = 2;
this._refreshCoinRewardDisplay();
gg.game.CurentBattle.IsWatchAd = true;
this._claimWithCoinFly();
} else {
gg.ui.showToast('观看视频失败');
}
});
}
}