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.
2319 lines
92 KiB
2319 lines
92 KiB
|
|
//*********************
|
|
// create by 流云
|
|
// time: Wed Apr 15 2026
|
|
// desc:
|
|
//*********************
|
|
import { _decorator, Camera, Collider2D, Component, Node, find, Label, UITransform, v3, Vec3, Color, EventTouch, Layers, Tween, tween, sp, Sprite, instantiate, PhysicsSystem2D } from 'cc';
|
|
import { Auto_tetriGame } from './Auto_tetriGame';
|
|
import { GBundle, UIID } from '../../game/ConfigRes';
|
|
import { BattleCore } from '../BattleGame/BattleCore';
|
|
import { tetriCardItem } from '../../tetriCard/tetriCardItem';
|
|
import { tetriMap } from '../../tetriMap/tetriMap';
|
|
import { tetriWeaponSystem } from '../../tetriMap/tetriWeaponSystem';
|
|
import { ERigidBody2DType, PolygonCollider2D, RigidBody2D, Vec2 } from 'cc';
|
|
import { tetriNode } from '../../tetriCard/tetriNode';
|
|
import { tetriFloorNode } from '../../tetriCard/tetriFloorNode';
|
|
import { GEvent } from 'db://assets/mx/module/event/GEvent';
|
|
import { CoinFly } from '../BattleGame/CoinFly';
|
|
import { ConstNumType, TetriType } from '../../game/ConfigProjectData';
|
|
import { tetriAbility } from '../../tetriCard/tetriAbility';
|
|
import { tetriWeaponCarrier } from '../../tetriCard/tetriWeaponCarrier';
|
|
import { TetriBlockSpineState, playTetriBlockSpine } from '../../tetriCard/tetriBlockSpine';
|
|
import { MonsterSpawner } from '../BattleGame/MonsterSpawner';
|
|
import { SkillController } from '../BattleGame/WeaponSkill/SkillController';
|
|
import MTools from 'db://assets/mx/tools/MTools';
|
|
import { DiamondTreasure } from '../BattleGame/DiamondTreasure';
|
|
import { StatisticsType, StatusType } from '../../game/GameData';
|
|
import { TableNames } from '../../game/ConfigTableData';
|
|
import { TweenBreathe } from 'db://assets/mx/components/tween/TweenBreathe';
|
|
import { BattleType } from '../../manager/ChapterDataManager';
|
|
import { BattlePerformance } from '../BattleGame/BattlePerformance';
|
|
import { BattleResLoader } from '../../game/BattleResLoader';
|
|
const { ccclass, property } = _decorator;
|
|
|
|
/**俄罗斯方块游戏UI */
|
|
@ccclass('UItetraGame')
|
|
export class UItetraGame extends Auto_tetriGame {
|
|
|
|
private CurentBattle: BattleCore = null;
|
|
private _cards: tetriCardItem[] = [];
|
|
/**本轮已成功放置(consume)的卡牌数量 */
|
|
private _placedCardCount: number = 0;
|
|
/**第一块落稳后是否已触发 start(只触发一次) */
|
|
private _startedAfterFirstPlacedSettled: boolean = false;
|
|
/**预警倒计时 UI:每秒刷新一次 */
|
|
private _warnUiAccSec: number = 0;
|
|
private _warnUiLastShownSec: number = -1;
|
|
/**宝箱飞行动画:起点缓存(world) */
|
|
private _boxJumpOriginWorld: Vec3 | null = null;
|
|
private _dealScheduled: boolean = false;
|
|
/**发牌次数(每次调用 dealCards +1) */
|
|
private _dealCardsCount: number = 0;
|
|
/** 雷电副本:场上统计方块数达到该值后不再发牌(须在 clearCards 之前判断,否则会清空手牌) */
|
|
private readonly _lightningMapBlockDealCap = 1000;
|
|
|
|
/** 猫头鹰飞行动画的 tween 目标(用于退出时停止) */
|
|
private _owlTweenTarget: { t: number } | null = null;
|
|
|
|
pauseUpdateTime: boolean = false;
|
|
private _speedupRateNormal: number = 1;
|
|
private _speedupRateFast: number = 1.5;
|
|
/**放置方块数量 */
|
|
private fangkuaiNum = 0;
|
|
|
|
private maxCountCoin = 6
|
|
private _getOwlChild(name: string): Node | null {
|
|
if (!this.btnOwl?.isValid) return null;
|
|
return this.btnOwl.getChildByName(name);
|
|
}
|
|
/** 延迟发牌用:与 scheduleOnce 同一引用以便 unschedule */
|
|
private _pendingDealCardsIsAd = false;
|
|
private readonly _deferDealCardsCb = (): void => {
|
|
void this._dealCardsImpl(this._pendingDealCardsIsAd);
|
|
};
|
|
|
|
/**高度尺光标的初始 y(作为 0m 起点) */
|
|
private _tagHeightStartY: number | null = null;
|
|
|
|
/**刷新按钮的金币 */
|
|
lb_Goid_num = 25
|
|
isShow10Anim: boolean = false;
|
|
isShow30Anim: boolean = false;
|
|
isShow60Anim: boolean = false;
|
|
isShow100Anim: boolean = false;
|
|
|
|
chapter1FreeCZ = false
|
|
chapter1FreeSQ = false
|
|
|
|
aixinIndex = 0
|
|
/** 单帧物理子步上限,防止超长卡顿一帧算过多子步把主线程打满 */
|
|
private readonly _physMaxSubStepsCap = 10;
|
|
|
|
private _applyPhysicsMaxSubStepsForFrame(dt: number): void {
|
|
const ph = PhysicsSystem2D.instance;
|
|
if (!ph.enable) return;
|
|
const step = ph.fixedTimeStep > 0 ? ph.fixedTimeStep : 1 / 60;
|
|
// 广告/切后台恢复后 dt 可能极大,限制参与子步计算的 dt,避免叠方块穿模
|
|
const clampedDt = Math.min(Math.max(0, dt), step * this._physMaxSubStepsCap);
|
|
const need = Math.max(1, Math.ceil(clampedDt / step));
|
|
ph.maxSubSteps = Math.min(this._physMaxSubStepsCap, need);
|
|
}
|
|
|
|
onLoad(): void {
|
|
super.onLoad();
|
|
}
|
|
|
|
onEnable(): void {
|
|
super.onEnable();
|
|
GEvent.Ins.on(GEvent.UpdateGameCoinNum, this.refreshCoinNum, this);
|
|
GEvent.Ins.on(GEvent.UpdateCoin, this.refreshCoinNum, this);
|
|
GEvent.Ins.on(GEvent.showCoinFly, this.showCoinFly, this);
|
|
GEvent.Ins.on(GEvent.playerHit, this.onPlayerHit, this);
|
|
GEvent.Ins.on(GEvent.UpdateWallHp, this.refreshWallHp, this);
|
|
GEvent.Ins.on(GEvent.TetriGameCancelUseShovel, this.cancelUseShovel, this);
|
|
GEvent.Ins.on(GEvent.GameReborn, this.gameReborn, this);
|
|
GEvent.Ins.on(GEvent.UpdateDps, this.updateDps, this);
|
|
GEvent.Ins.on(GEvent.TetriGameTouchStart, this.showChoosezhezhao, this);
|
|
GEvent.Ins.on(GEvent.TetriGameTouchEnd, this.hideChoosezhezhao, this);
|
|
GEvent.Ins.on(GEvent.PlayingGuideStoryEnd, this.onPlayingGuideStoryEnd, this);
|
|
GEvent.Ins.on(GEvent.tetriGameStart, this.onTetriGameStart, this);
|
|
GEvent.Ins.on(GEvent.BattleWarnTimeStart, this.onBattleWarnTimeStart, this);
|
|
this.node.on(Node.EventType.TOUCH_START, this._onGameplayTouchStart, this);
|
|
this.node.on(Node.EventType.TOUCH_END, this._onGameplayTouchEnd, this);
|
|
GEvent.Ins.on(GEvent.BattleWaveRefresh, this.onBattleWaveRefresh, this);
|
|
GEvent.Ins.on(GEvent.LightningLoveDrop, this.loveHpDrop, this);
|
|
GEvent.Ins.on(GEvent.TetriStackHeightNeedRefresh, this._applyTetriStackHeightRulerUI, this);
|
|
this.ui_Userchuizi.on(Node.EventType.TOUCH_START, this.click_btnUserchuziStart, this);
|
|
this.ui_Userchuizi.on(Node.EventType.TOUCH_END, this.click_btnUserchuziEnd, this);
|
|
|
|
|
|
}
|
|
|
|
onDisable(): void {
|
|
super.onDisable();
|
|
GEvent.Ins.off(GEvent.UpdateGameCoinNum, this.refreshCoinNum, this);
|
|
GEvent.Ins.off(GEvent.UpdateCoin, this.refreshCoinNum, this);
|
|
GEvent.Ins.off(GEvent.showCoinFly, this.showCoinFly, this);
|
|
GEvent.Ins.off(GEvent.playerHit, this.onPlayerHit, this);
|
|
GEvent.Ins.off(GEvent.UpdateWallHp, this.refreshWallHp, this);
|
|
GEvent.Ins.off(GEvent.TetriGameCancelUseShovel, this.cancelUseShovel, this);
|
|
GEvent.Ins.off(GEvent.GameReborn, this.gameReborn, this);
|
|
GEvent.Ins.off(GEvent.UpdateDps, this.updateDps, this);
|
|
GEvent.Ins.off(GEvent.TetriGameTouchStart, this.showChoosezhezhao, this);
|
|
GEvent.Ins.off(GEvent.TetriGameTouchEnd, this.hideChoosezhezhao, this);
|
|
GEvent.Ins.off(GEvent.PlayingGuideStoryEnd, this.onPlayingGuideStoryEnd, this);
|
|
GEvent.Ins.off(GEvent.tetriGameStart, this.onTetriGameStart, this);
|
|
GEvent.Ins.off(GEvent.BattleWarnTimeStart, this.onBattleWarnTimeStart, this);
|
|
this.node.off(Node.EventType.TOUCH_START, this._onGameplayTouchStart, this);
|
|
this.node.off(Node.EventType.TOUCH_END, this._onGameplayTouchEnd, this);
|
|
GEvent.Ins.off(GEvent.BattleWaveRefresh, this.onBattleWaveRefresh, this);
|
|
GEvent.Ins.off(GEvent.LightningLoveDrop, this.loveHpDrop, this);
|
|
GEvent.Ins.off(GEvent.TetriStackHeightNeedRefresh, this._applyTetriStackHeightRulerUI, this);
|
|
this.ui_Userchuizi.off(Node.EventType.TOUCH_START, this.click_btnUserchuziStart, this);
|
|
this.ui_Userchuizi.off(Node.EventType.TOUCH_END, this.click_btnUserchuziEnd, this);
|
|
|
|
this.unschedule(this.showOwl);
|
|
// 退出/切场景时停止猫头鹰相关 tween,避免回调访问已销毁节点导致报错
|
|
if (this._owlTweenTarget) {
|
|
Tween.stopAllByTarget(this._owlTweenTarget);
|
|
this._owlTweenTarget = null;
|
|
}
|
|
if (this.btnOwl?.isValid) {
|
|
Tween.stopAllByTarget(this.btnOwl);
|
|
}
|
|
}
|
|
|
|
/** 战斗场景与 BattleCore 引用是否仍可用于 update */
|
|
private _isBattleSceneActive(): boolean {
|
|
return gg.game.isBattleContextActive() && !!this.CurentBattle && this.node?.isValid;
|
|
}
|
|
|
|
onInit(): void {
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum;
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-显示画面`)
|
|
|
|
if(gg.game.tetriGameSpeed>1.5){
|
|
this._speedupRateFast = gg.game.tetriGameSpeed;
|
|
}
|
|
|
|
console.log('欢迎来到俄罗斯方块游戏');
|
|
gg.audio.playMusic({ name: "战斗背景音", path: "bgm/" });
|
|
// 地图异步加载完成前保持 loading,避免空地图黑屏,且防止 dealCards 早于 TetraMap 绑定
|
|
this.setProgressUIactive();
|
|
//给battlecore的对象赋值
|
|
|
|
|
|
//加载map1
|
|
this.CurentBattle = gg.game.CurentBattle;
|
|
// 橙武单通:须先于 guideHide/dealCards 生成本局可选武器形状,供发牌与三选一展示共用
|
|
if (gg.game.CurentBattle.BattleType == BattleType.OrangeWeaponMode_Dan) {
|
|
gg.game.CurentBattle.ModechooseTetriType = MTools.getRandomElements([1, 2, 3, 4, 5, 6], 3);
|
|
}
|
|
|
|
void this._setupBattleScene();
|
|
}
|
|
|
|
/** 异步挂载地图与战斗子系统,避免分包未就绪时 onInit 提前 return */
|
|
private async _setupBattleScene(): Promise<void> {
|
|
const battle = this.CurentBattle;
|
|
if (!battle) return;
|
|
|
|
const mapName = `tetriMap${battle.MapId}`;
|
|
let mapNode = gg.res.getNode('prefab/', mapName, GBundle.tetriMap);
|
|
if (!mapNode) {
|
|
await gg.res.ensurePrefab(mapName, 'prefab/', GBundle.tetriMap);
|
|
mapNode = gg.res.getNode('prefab/', mapName, GBundle.tetriMap);
|
|
}
|
|
if (!mapNode?.isValid || !this.node?.isValid) {
|
|
console.error(`${mapName} prefab load failed`);
|
|
gg.sdk?.pfLogKey?.('BATTLE:MAP_FAIL', mapName);
|
|
gg.game.hideLoading();
|
|
return;
|
|
}
|
|
mapNode.parent = this.ui_mapParentNode
|
|
this.setLayerMask(mapNode, this.ui_mapParentNode.layer);
|
|
|
|
battle.MonsterSpawner = this.ui_monsterParent.getComponent(MonsterSpawner);
|
|
battle.MonsterSpawner.initMonsterConfigs();
|
|
|
|
battle.SkillController = this.ui_skilllayer.getComponent(SkillController);
|
|
battle.SkillController.initAll();
|
|
|
|
battle.TetraMap = mapNode.getComponent(tetriMap);
|
|
battle.TetriWeaponSystem = mapNode.getComponentInChildren(tetriWeaponSystem);
|
|
|
|
battle.ScoreLayer = this.ui_scoreLayer;
|
|
battle.TopSpineLayer = this.ui_topSpineLayer;
|
|
battle.TempMonsterParent = this.ui_tempMonsterParent;
|
|
|
|
battle.ui_skillTraillayer = this.ui_skillTraillayer;
|
|
battle.ui_roleLayer = this.ui_roleLayer;
|
|
battle.ui_roleWeaponLayer = this.ui_roleWeaponLayer;
|
|
|
|
if (!battle.TetriWeaponSystem) {
|
|
console.error('tetriWeaponSystem not found');
|
|
}
|
|
if (!battle.TetraMap) {
|
|
console.error('tetriMap not found');
|
|
}
|
|
battle.TetraMap?.refreshHighLineFromStack();
|
|
let coin = gg.data.table.getConst(ConstNumType.CoinRefreshCost);
|
|
this.lb_Goid_num = coin;
|
|
this.refreshCoinNum()
|
|
|
|
this._startedAfterFirstPlacedSettled = false;
|
|
if (this.ui_boxJump?.isValid) {
|
|
this._boxJumpOriginWorld = this.ui_boxJump.worldPosition.clone();
|
|
}
|
|
|
|
this.setFreeBtnCoinNum()
|
|
this.refreshMagicalBtn()
|
|
this.onBattleWaveRefresh()
|
|
this._refreshSpeedUpBtnView();
|
|
|
|
this.hideBtn()
|
|
this.refreshWallHp();
|
|
this.updateDps();
|
|
|
|
// 地图就绪后再关 loading 并发牌,保证卡牌 bindMap 能拿到 highLine / layer
|
|
gg.game.hideLoading();
|
|
this.guideHide();
|
|
this._rebindDealtCardsToMap();
|
|
|
|
if (battle.BattleType == BattleType.SpeedMode || battle.BattleType == BattleType.OrangeWeaponMode_Dan) {
|
|
gg.ui.openPanel(UIID.SkillSelect, {data:true});
|
|
}
|
|
|
|
if (battle.BattleType == BattleType.OrangeWeaponMode_Dian && battle.loveHpNum > 0) {
|
|
this.ui_aixinNodes.active = true
|
|
this.showHideLoveHpUI()
|
|
this.createLoveHp()
|
|
} else {
|
|
this.ui_aixinNodes.active = false
|
|
}
|
|
}
|
|
|
|
/** 地图晚于卡牌就绪时补绑(防御异步竞态) */
|
|
private _rebindDealtCardsToMap() {
|
|
const map = this.CurentBattle?.TetraMap;
|
|
if (!map) return;
|
|
for (let i = 0; i < this._cards.length; i++) {
|
|
this._cards[i]?.bindMap(map);
|
|
}
|
|
}
|
|
|
|
/**波次倒计时结束,开始战斗*/
|
|
onTetriGameStart() {
|
|
if (this.ui_timeNode?.isValid && this.ui_timeNode.active) {
|
|
tween(this.ui_timeNode)
|
|
.to(0.1, { scale: v3(0.1, 0.1, 0.1) })
|
|
.call(() => {
|
|
this.ui_timeNode.active = false;
|
|
})
|
|
.start();
|
|
}
|
|
}
|
|
|
|
|
|
onBattleWarnTimeStart() {
|
|
//增加波次银币
|
|
if(gg.game.CurentBattle.BattleType == BattleType.OrangeWeaponMode_Dian){
|
|
return
|
|
}
|
|
if (!this.CurentBattle.IsGameOverBoxJump) {
|
|
return
|
|
}
|
|
if (gg.game.CurentBattle.waveGetCoin > 0 && gg.game.CurentBattle.CurentWaveNum > 1) {
|
|
gg.game.CurentBattle.addCoinNum(gg.game.CurentBattle.waveGetCoin);
|
|
GEvent.Ins.emit(GEvent.showCoinFly, this.node.worldPosition.clone(), 15);
|
|
gg.ui.showToast(gg.lang.get('波次结束,获得{1}银币', [gg.game.CurentBattle.waveGetCoin]), false, 1, 200)
|
|
}
|
|
|
|
|
|
if (gg.data.doc.chapter == 1) {
|
|
let boci = [6, 9, 12, 15]
|
|
if (boci.indexOf(gg.game.CurentBattle.CurentWaveNum) != -1) {
|
|
|
|
this.showDescGuide2('方块下落时,点击左右屏幕,调整方块掉落位置')
|
|
} else {
|
|
this.hideDescGuide2()
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
onPlayingGuideStoryEnd() {
|
|
this.guideShow()
|
|
}
|
|
|
|
guideHide() {
|
|
if (gg.data.doc.chapter == 1) {
|
|
gg.game.IsPlayingGuideStory = true
|
|
this.ui_xuanxiangKuang.active = false
|
|
this.ui_card.active = false
|
|
this.ui_Gold.active = false
|
|
this.ui_playHp.active = false
|
|
this.ui_bottom.active = false
|
|
|
|
const tryStartWarn = () => {
|
|
if (!this.CurentBattle?.SkillController) {
|
|
this.scheduleOnce(tryStartWarn, 0.2);
|
|
return;
|
|
}
|
|
this.CurentBattle.startWarnTime();
|
|
};
|
|
this.scheduleOnce(() => {
|
|
GEvent.Ins.emit(GEvent.PlayingGuideStoryEnd);
|
|
tryStartWarn();
|
|
}, 4.0);
|
|
} else {
|
|
this.dealCards()
|
|
}
|
|
|
|
}
|
|
|
|
guideShow() {
|
|
gg.game.IsPlayingGuideStory = false
|
|
|
|
const cam = gg.ui.GameCamera;
|
|
const zoomInH = gg.ui.height / 2;
|
|
tween(cam)
|
|
.to(1, { orthoHeight: zoomInH }, { easing: 'quadInOut' })
|
|
.delay(0.1)
|
|
.call(() => {
|
|
//开启引导
|
|
if (this.node.isValid) {
|
|
if (this.ui_xuanxiangKuang.isValid) {
|
|
this.ui_xuanxiangKuang.active = false
|
|
}
|
|
|
|
if (this.ui_card.isValid) {
|
|
this.ui_card.active = true
|
|
}
|
|
if (this.ui_Gold.isValid) {
|
|
this.ui_Gold.active = true
|
|
}
|
|
if (this.ui_playHp.isValid) {
|
|
this.ui_playHp.active = true
|
|
}
|
|
if (this.ui_bottom.isValid) {
|
|
this.ui_bottom.active = true
|
|
}
|
|
|
|
this.dealCards()
|
|
if (gg.data.getStatus(StatusType.IsShowPlayGuidePop) == 0) {
|
|
this.ui_guideHuadong.active = true;
|
|
this.showDescGuide("在绿色地基上搭建方块");
|
|
}
|
|
}
|
|
|
|
})
|
|
.start();
|
|
|
|
|
|
}
|
|
|
|
showDescGuide(str: string) {
|
|
this.ui_guideLab.active = true;
|
|
console.log('showDescGuide', str);
|
|
this.ui_guideLab.getComponent(Label).string = gg.lang.get(str);
|
|
}
|
|
|
|
hideDescGuide() {
|
|
//
|
|
this.ui_guideLab.active = false;
|
|
}
|
|
|
|
|
|
showDescGuide2(str: string) {
|
|
this.ui_guideLab2.active = true;
|
|
this.scheduleOnce(() => {
|
|
this.hideDescGuide2()
|
|
}, 5);
|
|
// console.log('showDescGuide2', str);
|
|
// this.ui_guideLab2.getComponent(Label).string = gg.lang.get(str);
|
|
}
|
|
|
|
hideDescGuide2() {
|
|
//
|
|
this.ui_guideLab2.active = false;
|
|
}
|
|
|
|
hideBtn() {
|
|
if (gg.data.doc.chapter == 1) {
|
|
this.btnShenqiBox.active = false
|
|
this.btnCoin.active = false
|
|
this.btnchuzi.active = false
|
|
this.btnMaotouying.active = false
|
|
this.btnRefreshBox.active = false
|
|
|
|
this.chapter1FreeCZ = true
|
|
this.chapter1FreeSQ = true
|
|
|
|
this.ui_sqfkAd.active = false
|
|
this.lb_sqnum.node.active = false
|
|
this.lb_freeSqLab.node.active = true
|
|
this.ui_sqbxshouzhi.active = true
|
|
|
|
|
|
this.ui_czAd.active = false
|
|
|
|
this.lb_freeCzLab.node.active = false
|
|
this.ui_chuizishouzhi.active = false
|
|
|
|
this.ui_mfybAd.active = false
|
|
this.lb_mfybCount.node.active = false
|
|
this.ui_mfybshouzhi.active = true
|
|
|
|
|
|
} else {
|
|
this.refreshShovelBtn()
|
|
|
|
if (gg.game.CurentBattle.BattleType == BattleType.SpeedMode) {
|
|
this.updateBtnActive();
|
|
}
|
|
}
|
|
}
|
|
clearCards() {
|
|
if (!this.ui_card?.isValid) {
|
|
this._cards = [];
|
|
return;
|
|
}
|
|
this.ui_card.children.forEach(uiboxBg => {
|
|
if (!uiboxBg?.isValid) return;
|
|
uiboxBg.children.forEach(child => {
|
|
if (child?.isValid) child.destroy();
|
|
});
|
|
});
|
|
this._cards = [];
|
|
}
|
|
|
|
/** 三个卡槽父节点(优先用 Auto 里绑定的 ui_box1/2/3,避免 children 顺序与 destroy 中间态问题) */
|
|
private _getCardParentSlots(): Node[] {
|
|
const slots = [this.ui_box1, this.ui_box2, this.ui_box3].filter((n) => !!n?.isValid);
|
|
if (slots.length > 0) return slots;
|
|
return (this.ui_card?.children ?? []).filter((n) => !!n?.isValid);
|
|
}
|
|
|
|
/**发牌
|
|
* 刷新按钮发牌
|
|
* 所有card已经放置自动发牌
|
|
*/
|
|
|
|
async dealCards(isAd: boolean = false) {
|
|
// 雷电副本:地图场上方块已达上限则不再发新牌;不 clearCards,保留当前手牌
|
|
const battle = this.CurentBattle;
|
|
if (
|
|
battle?.BattleType === BattleType.OrangeWeaponMode_Dian &&
|
|
battle?.TetraMap?.getTetriNodeCount?.() >= this._lightningMapBlockDealCap && !isAd
|
|
) {
|
|
this._dealScheduled = false;
|
|
this.ui_maxCard.active = true;
|
|
this.unschedule(this._deferDealCardsCb);
|
|
return;
|
|
}
|
|
this.ui_maxCard.active = false;
|
|
//发牌
|
|
//加载刷新方块
|
|
this._dealCardsCount++
|
|
this.clearCards()
|
|
this._placedCardCount = 0
|
|
this._dealScheduled = false
|
|
this._pendingDealCardsIsAd = isAd
|
|
// clearCards 里 destroy 是延迟到本帧结束的:同一帧立刻 parent 新节点会触发引擎 walk 对旧子树 null.length 报错
|
|
this.unschedule(this._deferDealCardsCb)
|
|
this.scheduleOnce(this._deferDealCardsCb, 0)
|
|
}
|
|
|
|
private async _dealCardsImpl(isAd: boolean): Promise<void> {
|
|
if (isAd) {
|
|
await BattleResLoader.loadBundle(GBundle.tetriCards);
|
|
}
|
|
const waveNum = this.CurentBattle.CurentWaveNum
|
|
let tetriConfigs = isAd
|
|
? gg.data.project.getTetriConfigsAd(waveNum, 3, this.CurentBattle.CurentConfigChapter)
|
|
: gg.data.project.getTetriConfigsByWave(waveNum, 3, this.CurentBattle.CurentConfigChapter)
|
|
|
|
|
|
if (this._dealCardsCount == 1 && this.CurentBattle.ChapterId == 1) {
|
|
tetriConfigs = [];
|
|
let ids = gg.data.table.getConstArray(ConstNumType.FirstWaveTetri);
|
|
for (let i = 0; i < ids.length; i++) {
|
|
const id = ids[i];
|
|
const tetriConfig = gg.data.table.getTableData<ITableBattleCube>(TableNames.BattleCube, id);
|
|
if (tetriConfig) {
|
|
tetriConfigs.push(tetriConfig);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this._dealCardsCount == 2 && this.CurentBattle.ChapterId == 1) {
|
|
tetriConfigs = [];
|
|
let ids = gg.data.table.getConstArray(ConstNumType.FirstWaveTetri2);
|
|
for (let i = 0; i < ids.length; i++) {
|
|
const id = ids[i];
|
|
const tetriConfig = gg.data.table.getTableData<ITableBattleCube>(TableNames.BattleCube, id);
|
|
if (tetriConfig) {
|
|
tetriConfigs.push(tetriConfig);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (this._dealCardsCount == 1 && this.CurentBattle.ChapterId == 2) {
|
|
let ids = gg.data.table.getConstArray(ConstNumType.SecondWaveTetri);
|
|
tetriConfigs = [];
|
|
for (let i = 0; i < ids.length; i++) {
|
|
const id = ids[i];
|
|
const tetriConfig = gg.data.table.getTableData<ITableBattleCube>(TableNames.BattleCube, id);
|
|
if (tetriConfig) {
|
|
tetriConfigs.push(tetriConfig);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!tetriConfigs?.length) {
|
|
console.error('[UItetraGame] dealCards: tetriConfigs 为空')
|
|
return
|
|
}
|
|
const slots = this._getCardParentSlots()
|
|
if (!slots.length) {
|
|
console.error('[UItetraGame] dealCards: 无卡槽父节点 ui_box1/2/3 或 ui_card.children')
|
|
return
|
|
}
|
|
|
|
for (let i = 0; i < tetriConfigs.length; i++) {
|
|
const parentNode = slots[i]
|
|
if (!parentNode?.isValid) {
|
|
console.error(`[UItetraGame] dealCards: 卡槽 ${i} 无效`)
|
|
continue
|
|
}
|
|
const cardItem = await gg.res.getNodeAsync(`prefab/`, 'cardItem', GBundle.tetriCards)
|
|
if (!cardItem?.isValid) {
|
|
console.error('[UItetraGame] dealCards: cardItem 加载失败')
|
|
gg.sdk?.pfLogKey?.('BATTLE:PREFAB_FAIL', 'cardItem');
|
|
continue
|
|
}
|
|
if (!parentNode?.isValid) {
|
|
cardItem.destroy();
|
|
continue
|
|
}
|
|
cardItem.setParent(parentNode, false)
|
|
cardItem.setPosition(0, 0)
|
|
const cardComp = cardItem.getComponent(tetriCardItem)
|
|
if (cardComp) this._cards.push(cardComp)
|
|
|
|
if (gg.data.getStatus(StatusType.IsShowPlayGuidePop) == 0 && cardComp && i == 1) {
|
|
let liangkuangNode = cardComp.liangkuangNode;
|
|
if (liangkuangNode?.isValid) {
|
|
liangkuangNode.active = true;
|
|
}
|
|
}
|
|
const tetriConfig = tetriConfigs[i]
|
|
|
|
|
|
|
|
cardComp?.setConfig(tetriConfig)
|
|
let cardNode: Node | null = null
|
|
if (tetriConfig.type == TetriType.Tetris_Basefloor) {
|
|
cardNode = await gg.res.getNodeAsync(`prefabFloor/`, tetriConfig.cubename, GBundle.tetriCards)
|
|
} else {
|
|
cardNode = await gg.res.getNodeAsync(`prefab/`, tetriConfig.cubename, GBundle.tetriCards)
|
|
}
|
|
if (!cardNode?.isValid) {
|
|
console.error('[UItetraGame] dealCards: 方块预制体加载失败', tetriConfig.cubename)
|
|
gg.sdk?.pfLogKey?.('BATTLE:PREFAB_FAIL', tetriConfig.cubename);
|
|
continue
|
|
}
|
|
if (!cardItem?.isValid) {
|
|
cardNode.destroy();
|
|
continue
|
|
}
|
|
cardNode.setParent(cardItem, false)
|
|
cardNode.opacity = 255
|
|
cardNode.setPosition(0, 30)
|
|
playTetriBlockSpine(cardNode, TetriBlockSpineState.Shop)
|
|
this.setLayerMask(cardNode, Layers.Enum.UI_2D);
|
|
if (tetriConfig.type == TetriType.Tetris_Basefloor) {
|
|
cardNode.getOrAddComponent(tetriFloorNode).setConfig(tetriConfig)
|
|
} else {
|
|
cardNode.angle = 0
|
|
cardNode.getOrAddComponent(tetriNode).setConfig(tetriConfig)
|
|
if (tetriConfig.type == TetriType.Tetris_BaseGold || tetriConfig.type == TetriType.Tetris_Baseheal) {
|
|
cardNode.getOrAddComponent(tetriAbility).setConfig(tetriConfig)
|
|
}
|
|
if (tetriConfig.type == TetriType.Tetris_Baseattk || tetriConfig.type == TetriType.Tetris_Basevine) {
|
|
cardNode.getOrAddComponent(tetriWeaponCarrier).setConfig(tetriConfig)
|
|
}
|
|
}
|
|
cardComp?.setPieceNode(cardNode)
|
|
|
|
const maybePieceNode =
|
|
cardItem.children?.length > 0 ? cardItem.children[cardItem.children.length - 1] : null
|
|
if (maybePieceNode?.isValid) {
|
|
for (const rb of maybePieceNode.getComponentsInChildren(RigidBody2D)) {
|
|
rb.enabled = true
|
|
rb.linearVelocity = new Vec2(0, 0)
|
|
rb.angularVelocity = 0
|
|
rb.gravityScale = 0
|
|
rb.type = ERigidBody2DType.Static
|
|
rb.fixedRotation = true
|
|
}
|
|
}
|
|
|
|
cardComp?.setChooseMask(this.ui_choosezhezhao)
|
|
cardComp?.setCancleNode(this.ui_cancleNode)
|
|
cardComp?.bindMap(this.CurentBattle?.TetraMap)
|
|
if (!this.CurentBattle?.TetraMap) {
|
|
console.warn('[UItetraGame] dealCards: TetraMap 尚未就绪,卡牌将无法正确预览/掉落')
|
|
}
|
|
if (cardComp) {
|
|
cardComp.onConsumed = () => {
|
|
this._placedCardCount++
|
|
const need = this._getCardParentSlots().length || this.ui_card?.children?.length || 3
|
|
if (this._placedCardCount >= need && !this._dealScheduled) {
|
|
this._dealScheduled = true
|
|
this.scheduleOnce(() => {
|
|
this.dealCards()
|
|
}, 0)
|
|
}
|
|
}
|
|
}
|
|
if (cardComp) {
|
|
cardComp.onPlaceRequested = (self, battleNode, worldPos) => {
|
|
|
|
this.fangkuaiNum++;
|
|
const preview = battleNode;
|
|
this.ui_guideHuadong.active = false;
|
|
|
|
if (gg.data.getStatus(StatusType.IsShowPlayGuidePop) == 0) {
|
|
gg.data.setStatus(StatusType.IsShowPlayGuidePop, 1);
|
|
//打点
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${gg.game.CurentBattle.CurentWaveNum}-首次拖动方块放置`);
|
|
if (gg.game.CurentBattle.TetraMap.node.getChildByName('地基区域')) {
|
|
gg.game.CurentBattle.TetraMap.node.getChildByName('地基区域').active = false;
|
|
}
|
|
this.ui_guideHuadong2.active = true;
|
|
this.showDescGuide("非常棒!继续摆放方块,保护她吧");
|
|
} else {
|
|
this.ui_guideHuadong2.active = false;
|
|
this.hideDescGuide()
|
|
}
|
|
|
|
for (let i = 0; i < this._cards.length; i++) {
|
|
let liangkuangNode = this._cards[i].liangkuangNode;
|
|
if (liangkuangNode?.isValid) {
|
|
liangkuangNode.active = false;
|
|
}
|
|
}
|
|
|
|
if (!preview?.isValid) {
|
|
self.cancelPlace()
|
|
return false
|
|
}
|
|
const gameRoot = this.CurentBattle?.TetraMap?._gameRoot;
|
|
if (!gameRoot?.isValid) {
|
|
self.cancelPlace()
|
|
return false
|
|
}
|
|
const map = this.CurentBattle?.TetraMap
|
|
// 先入树前写配置/物理,避免 addChild 触发 start 时 _config 仍为空
|
|
preview.active = false
|
|
this._applyBattlePieceConfig(preview, tetriConfig)
|
|
this._primeBattlePieceRigidbodies(preview, map)
|
|
gameRoot.addChild(preview)
|
|
this.setLayerMask(preview, gameRoot.layer)
|
|
this._enableBattlePieceColliders(preview)
|
|
preview.setWorldPosition(worldPos)
|
|
preview.active = true
|
|
const floorNodes = preview.getComponentsInChildren(tetriFloorNode) ?? []
|
|
for (const f of floorNodes) {
|
|
f.beginPlacementFall()
|
|
}
|
|
this._primeBattlePieceRigidbodies(preview, map)
|
|
for (const wc of preview.getComponentsInChildren(tetriWeaponCarrier)) {
|
|
wc.ensureBattleReady()
|
|
}
|
|
for (const ab of preview.getComponentsInChildren(tetriAbility)) {
|
|
ab.ensureBattleReady()
|
|
}
|
|
for (const t of preview.getComponentsInChildren(tetriNode)) {
|
|
t.playBlockSpine(TetriBlockSpineState.Falling);
|
|
}
|
|
this.CurentBattle?.TetraMap?.setLatestFallingNode(preview)
|
|
|
|
// 第一块方块:落地企稳后再开始 battle(只触发一次)
|
|
if (!this._startedAfterFirstPlacedSettled) {
|
|
const startOnce = () => {
|
|
if (this._startedAfterFirstPlacedSettled) return;
|
|
this._startedAfterFirstPlacedSettled = true;
|
|
if (gg.data.doc.chapter != 1 && !gg.game.noMonsterShow)
|
|
this.CurentBattle?.startWarnTime();
|
|
this.showWarnTimeUI()
|
|
|
|
};
|
|
const tetriNodes = preview.getComponentsInChildren(tetriNode) ?? [];
|
|
const floorNodes = preview.getComponentsInChildren(tetriFloorNode) ?? [];
|
|
if (tetriNodes.length > 0) {
|
|
// 只需挂一个:任意一个 tetriNode 落稳即可认为整块“企稳”
|
|
const t0 = tetriNodes[0];
|
|
const prev = t0.onSettled;
|
|
t0.onSettled = () => {
|
|
try { prev?.(); } catch (e) { console.error(e); }
|
|
startOnce();
|
|
};
|
|
} else if (floorNodes.length > 0) {
|
|
const f0: any = floorNodes[0] as any;
|
|
const prev = f0.onSettled as (() => void) | null | undefined;
|
|
f0.onSettled = () => {
|
|
try { prev?.(); } catch (e: any) { console.error(e); }
|
|
startOnce();
|
|
};
|
|
} else {
|
|
// 兜底:没有识别到脚本时,仍然延迟一帧开始(避免卡死不开始)
|
|
this.scheduleOnce(() => startOnce(), 0);
|
|
}
|
|
}
|
|
|
|
self.consume()
|
|
this.ui_choosezhezhao.active = false
|
|
this.ui_cancleNode.active = false
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
showWarnTimeUI() {
|
|
//播放一次动画
|
|
|
|
|
|
const shouldPlayAnim = !this.ui_timeNode.active;
|
|
if (shouldPlayAnim && this.CurentBattle.tetriWarnTimeFix > 0) {
|
|
this.CurentBattle.TetraMap.showQiPaoEffect()
|
|
console.log('showWarnTimeUI 播放一次动画');
|
|
this.ui_timeNode.active = true;
|
|
if(gg.game.noMonsterShow) {
|
|
this.ui_timeNode.active = false;
|
|
};
|
|
let posiy = this.CurentBattle.TetraMap.getHeightLinePosition();
|
|
//this.ui_timeNode.y = posiy+100;
|
|
this.ui_timeNode.scale = v3(0.1, 0.1, 0.1);
|
|
Tween.stopAllByTarget(this.ui_timeNode);
|
|
tween(this.ui_timeNode)
|
|
.to(0.2, { scale: v3(1.1, 1.1, 1.1) }, { easing: 'quadOut' })
|
|
.to(0.2, { scale: v3(1, 1, 1) }, { easing: 'quadInOut' })
|
|
.start();
|
|
}
|
|
|
|
|
|
if (this.CurentBattle.BattleType == BattleType.InfiniteMode) {
|
|
this.lb_waveDesc.string = `第${this.CurentBattle.InfiniteModeCurentWaveNum}波怪物来袭`;
|
|
} else {
|
|
this.lb_waveDesc.string = `第${this.CurentBattle.CurentWaveNum}波怪物来袭`;
|
|
}
|
|
this.lb_dTime.string = MTools.formatTimeString(this.CurentBattle.tetriWarnTimeFix, "mm:ss");
|
|
}
|
|
|
|
//获取gameroot的TetrisTable整体高度需要考虑缩放比例
|
|
//blockHeight 整体高度除以blockHeight就是当前的多少米了
|
|
update(dt: number) {
|
|
if (!this._isBattleSceneActive()) return;
|
|
this._applyPhysicsMaxSubStepsForFrame(dt);
|
|
//引导中暂停一下
|
|
if (gg.game.IsPlayingGuideStory) {
|
|
this.CurentBattle._dequeueWeaponLaunchEvents(this.CurentBattle.WeaponLaunchMaxPerFrame, this.CurentBattle.WeaponLaunchDispatchIntervalFrames);
|
|
return
|
|
}
|
|
if (this.pauseUpdateTime) {
|
|
return
|
|
}
|
|
this.CurentBattle.update(dt);
|
|
this.showHpWarn(false)
|
|
// 波次预警倒计时 UI(倒计时期间战斗暂停,但 UI 需要刷新)
|
|
if (this.CurentBattle?.IsWarnTimeActive) {
|
|
if (this.ui_timeNode?.isValid) {
|
|
this.showWarnTimeUI()
|
|
|
|
}
|
|
// 每 1 秒刷新一次文本(或秒数变化时刷新)
|
|
this._warnUiAccSec += dt;
|
|
const secLeft = Math.max(0, Math.ceil(this.CurentBattle.tetriWarnTimeFix));
|
|
if (this._warnUiAccSec >= 1 || secLeft !== this._warnUiLastShownSec) {
|
|
this._warnUiAccSec = 0;
|
|
this._warnUiLastShownSec = secLeft;
|
|
if (this.lb_dTime?.isValid) {
|
|
this.lb_dTime.string = MTools.formatTimeString(secLeft, "mm:ss");
|
|
}
|
|
}
|
|
} else {
|
|
|
|
this._warnUiAccSec = 0;
|
|
this._warnUiLastShownSec = -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 堆叠米数由 tetriMap._updateHighLine 写入 BattleCore;此处只更新侧栏高度尺与宝箱/竞速段 UI。
|
|
* 由 GEvent.TetriStackHeightNeedRefresh 触发(落稳、白线周期重算、地面闪红后延迟一帧等)。
|
|
*/
|
|
private _applyTetriStackHeightRulerUI(): void {
|
|
if (this.pauseUpdateTime || !this.CurentBattle) return;
|
|
const meters = this.CurentBattle.TetriStackHeightMeters;
|
|
if (!this.ui_tagHeight?.isValid || !this.ui_heightNode?.isValid) return;
|
|
|
|
if (this._tagHeightStartY === null) this._tagHeightStartY = this.ui_tagHeight.position.y;
|
|
const y0 = this._tagHeightStartY ?? this.ui_tagHeight.position.y;
|
|
const y15 = this.ui_10m?.position.y ?? 0;
|
|
const y30 = this.ui_30m?.position.y ?? y15;
|
|
const y60 = this.ui_60m?.position.y ?? y30;
|
|
|
|
const mapMeterToY = (m: number): number => {
|
|
if (m <= 15) {
|
|
const k = y15 !== y0 ? (y15 - y0) / 15 : 0;
|
|
return y0 + k * m;
|
|
}
|
|
if (y30 !== y15 && m <= 30) {
|
|
return y15 + (y30 - y15) * ((m - 15) / 15);
|
|
}
|
|
if (y60 !== y30 && m <= 60) {
|
|
return y30 + (y60 - y30) * ((m - 30) / 30);
|
|
}
|
|
return y60;
|
|
};
|
|
|
|
const newY = mapMeterToY(meters);
|
|
const p = this.ui_tagHeight.position;
|
|
this.ui_tagHeight.setPosition(p.x, newY, p.z);
|
|
this.showBoxUI(meters);
|
|
this.updateProgressUI();
|
|
if (meters >= 30) {
|
|
this.CurentBattle.pruneDetachedBattleLayers();
|
|
}
|
|
}
|
|
|
|
setProgressUIactive() {
|
|
if (gg.game.CurentBattle.BattleType == BattleType.SpeedMode) {
|
|
this.ui_fenduan2.active = true;
|
|
this.ui_fenduan.active = false;
|
|
this.ui_10m.active = false;
|
|
this.ui_30m.active = false;
|
|
this.ui_60m.active = false;
|
|
this.ui_100m.active = false;
|
|
this.ui_Baoxiang10m.active = false;
|
|
this.ui_Baoxiang30m.active = false;
|
|
this.ui_Baoxiang60m.active = false;
|
|
this.ui_Baoxiang100m.active = false;
|
|
} else {
|
|
this.ui_fenduan2.active = false;
|
|
this.ui_fenduan.active = true;
|
|
this.ui_10m.active = true;
|
|
this.ui_30m.active = true;
|
|
this.ui_60m.active = true;
|
|
this.ui_100m.active = false;
|
|
this.ui_Baoxiang10m.active = true;
|
|
this.ui_Baoxiang30m.active = true;
|
|
this.ui_Baoxiang60m.active = true;
|
|
this.ui_Baoxiang100m.active = false;
|
|
}
|
|
}
|
|
|
|
updateProgressUI() {
|
|
if (!this.ui_fenduan2?.active || !this.CurentBattle?.CurentConfigReplica) return;
|
|
const nextIdx = this.CurentBattle.getNextSpeedModeHeightMilestoneIndex();
|
|
const heightArr = this.CurentBattle.CurentConfigReplica.hight;
|
|
const atkArr = this.CurentBattle.CurentConfigReplica.atk;
|
|
for (let i = 0; i < this.ui_fenduan2.children.length; i++) {
|
|
const child = this.ui_fenduan2.children[i];
|
|
if (!child?.isValid) continue;
|
|
let isActive=child.active;
|
|
child.active = i === nextIdx || i == this.ui_fenduan2.children.length - 1;
|
|
if(!isActive&&child.active){
|
|
GEvent.Ins.emit(GEvent.UpdateDps, 2);
|
|
}
|
|
if (i < heightArr.length && i < atkArr.length) {
|
|
const atk = Math.floor(atkArr[i] / 10000 * 100);
|
|
const lb = child.getComponentInChildren(Label);
|
|
if (lb) lb.string = `${heightArr[i]}m 攻击+${atk}%`;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**显示宝箱UI */
|
|
showBoxUI(meters: number) {
|
|
if (!this.CurentBattle) return;
|
|
const m = meters ?? 0;
|
|
|
|
if(this.ui_fenduan.active){
|
|
this.ui_fenduan.getComponent(Sprite).fillRange = m / 60;
|
|
}
|
|
if(this.ui_fenduan2.active){
|
|
this.ui_fenduan2.getComponent(Sprite).fillRange = m / 60;
|
|
}
|
|
if (this.ui_Baoxiang10m?.isValid && !this.isShow10Anim) {
|
|
// 历史原因:字段仍叫 10m,但该宝箱现在在 15m 解锁
|
|
let show = m >= 15 && !this.CurentBattle.is10mBoxOpen;
|
|
this.ui_Baoxiang10m.children[0].active = show;
|
|
this.ui_Baoxiang10m.getComponent(Sprite).grayscale = !show;
|
|
this.ui_Baoxiang10m.getComponent(TweenBreathe).enabled = show;
|
|
this.ui_Baoxiang10m.setScale(show ? v3(2, 2, 2) : v3(0.8, 0.8, 0.8));
|
|
if (show && !this.isShow10Anim) {
|
|
this.isShow10Anim = true;
|
|
this.showGetTip('获得技能宝箱')
|
|
if (gg.data.doc.chapter == 1) {
|
|
this.ui_10mshouzhi.active = true;
|
|
this.showDescGuide("恭喜,达到15米了,获得武器技能转盘!")
|
|
|
|
}
|
|
}
|
|
}
|
|
if (this.ui_Baoxiang30m?.isValid && !this.isShow30Anim) {
|
|
let show = m >= 30 && !this.CurentBattle.is30mBoxOpen;
|
|
this.ui_Baoxiang30m.children[0].active = show;
|
|
this.ui_Baoxiang30m.getComponent(Sprite).grayscale = !show;
|
|
this.ui_Baoxiang30m.getComponent(TweenBreathe).enabled = show;
|
|
this.ui_Baoxiang30m.setScale(show ? v3(2, 2, 2) : v3(0.8, 0.8, 0.8));
|
|
if (show && !this.isShow30Anim) {
|
|
this.isShow30Anim = true;
|
|
this.showGetTip('获得技能宝箱')
|
|
}
|
|
}
|
|
if (this.ui_Baoxiang60m?.isValid && !this.isShow60Anim) {
|
|
let show = m >= 60 && !this.CurentBattle.is60mBoxOpen;
|
|
this.ui_Baoxiang60m.children[0].active = show;
|
|
this.ui_Baoxiang60m.getComponent(Sprite).grayscale = !show;
|
|
this.ui_Baoxiang60m.getComponent(TweenBreathe).enabled = show;
|
|
this.ui_Baoxiang60m.setScale(show ? v3(2, 2, 2) : v3(0.8, 0.8, 0.8));
|
|
if (show && !this.isShow60Anim) {
|
|
this.isShow60Anim = true;
|
|
this.showGetTip('获得技能宝箱')
|
|
}
|
|
}
|
|
// if (this.ui_Baoxiang100m?.isValid && !this.isShow100Anim) {
|
|
// let show = m >= 100 && !this.CurentBattle.is100mBoxOpen;
|
|
// this.ui_Baoxiang100m.children[0].active = show;
|
|
// this.ui_Baoxiang100m.getComponent(Sprite).grayscale = !show;
|
|
// this.ui_Baoxiang100m.getComponent(TweenBreathe).enabled = show;
|
|
// this.ui_Baoxiang100m.setScale(show ? v3(2, 2, 2) : v3(0.8, 0.8, 0.8));
|
|
// if (show && !this.isShow100Anim) {
|
|
// this.isShow100Anim = true;
|
|
// this.showGetTip('获得技能宝箱')
|
|
// }
|
|
// }
|
|
}
|
|
|
|
click_btnPause() {
|
|
this.CurentBattle.pause('BattlePause');
|
|
gg.ui.openPanel(UIID.BattlePause);
|
|
}
|
|
click_btnSpeedUp() {
|
|
if (!this.CurentBattle) return;
|
|
//第二章解锁
|
|
if (gg.data.doc.chapter == 1) {
|
|
gg.ui.showToast(gg.lang.get('通关第{1}章解锁', [1]), false)
|
|
return
|
|
}
|
|
//打点
|
|
|
|
const cur = this.CurentBattle.TimeScale > this._speedupRateNormal ? this._speedupRateFast : this._speedupRateNormal;
|
|
this.CurentBattle.TimeScale = (cur >= this._speedupRateFast) ? this._speedupRateNormal : this._speedupRateFast;
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-加速点击` + this.CurentBattle.TimeScale)
|
|
|
|
|
|
this._refreshSpeedUpBtnView();
|
|
}
|
|
click_btn_tip() {
|
|
const now = Date.now();
|
|
if (now - this._lastTipClickMs < 400) {
|
|
return;
|
|
}
|
|
this._lastTipClickMs = now;
|
|
if (gg.ui.panelIsShow(UIID.TetriDoc) || gg.ui.panelIsOpening(UIID.TetriDoc)) {
|
|
return;
|
|
}
|
|
gg.ui.removeWaitPop(UIID.TetriDoc);
|
|
gg.ui.openPanel(UIID.TetriDoc);
|
|
}
|
|
|
|
private _refreshSpeedUpBtnView() {
|
|
if (!this.btnSpeedUp?.isValid || !this.CurentBattle) return;
|
|
const isFast = this.CurentBattle.TimeScale > this._speedupRateNormal;
|
|
const sp1 = this.btnSpeedUp.getChildByName("1");
|
|
const sp2 = this.btnSpeedUp.getChildByName("2");
|
|
if (sp1?.isValid) sp1.active = !isFast;
|
|
if (sp2?.isValid) sp2.active = isFast;
|
|
}
|
|
|
|
useChuizi(){
|
|
|
|
this.ui_heightNode.active = false
|
|
this.ui_bottom.active = false
|
|
this.ui_top.active = false
|
|
this.ui_playHp.active = false
|
|
this.ui_Gold.active = false
|
|
this.ui_card.active = false
|
|
this.ui_choosezhezhao.active = false
|
|
this.ui_hpWarn.active = false
|
|
this.ui_Userchuizi.active = true
|
|
//显示使用锤子层级
|
|
this.CurentBattle.pause('useShovel')
|
|
this.CurentBattle.IsUseShovel = true;
|
|
//注册使用this.ui_Userchuizi界面点击触摸事件
|
|
//发消息使用锤子
|
|
GEvent.Ins.emit(GEvent.TetriGameUseShovel, this.CurentBattle.gameShovelNum)
|
|
|
|
}
|
|
|
|
click_btnchuzi() {
|
|
|
|
if(gg.game.CurentBattle.BattleType == BattleType.OrangeWeaponMode_Dian && this.ui_czAd.active == true){
|
|
//广告获取锤子
|
|
gg.sdk.showVideoAd((res) => {
|
|
if (res) {
|
|
//白银宝箱
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${this.CurentBattle.getReportDYChapterId()}_${curWaveNum}-AD-获取锤子`)
|
|
|
|
this.CurentBattle.gameShovelNum++
|
|
this.ui_czAd.active = false
|
|
this.useChuizi()
|
|
return
|
|
} else {
|
|
gg.ui.showToast("观看视频失败");
|
|
}
|
|
});
|
|
return
|
|
}
|
|
if (this.CurentBattle.GameShovelCoin > this.CurentBattle.CurentCoinNum) {
|
|
gg.ui.showToast('银币不足')
|
|
return
|
|
}
|
|
if (this.CurentBattle.GameShovelCoin <= this.CurentBattle.CurentCoinNum || (gg.data.doc.chapter == 1 && this.chapter1FreeCZ)) {
|
|
//使用锤子
|
|
this.useChuizi()
|
|
return
|
|
}
|
|
this._isClickChuiziBubble = true;
|
|
this.btnchuzi.getChildByName("paopao").active = false;
|
|
}
|
|
click_btnShenqiBox() {
|
|
if (this.CurentBattle.onRefreshMagicalNum <= 0) {
|
|
gg.ui.showToast('神奇方块次数不足')
|
|
return
|
|
}
|
|
//
|
|
if (gg.data.doc.chapter == 1 && this.chapter1FreeSQ) {
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-神奇方块免费刷新`)
|
|
|
|
this.lb_freeSqLab.node.active = false
|
|
this.ui_sqbxshouzhi.active = false
|
|
this.ui_sqfkAd.active = true
|
|
this.lb_sqnum.node.active = true
|
|
//刷新出三个卡牌,固定2类型的卡牌一张免费使用,剩下的还是走的随机
|
|
this.dealCards(true)
|
|
this.chapter1FreeSQ = false;
|
|
if (gg.data.getStatus(StatusType.IsShowPlayGuidePopNoGravity) == 0) {
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-新玩家玩法说明主动弹出`);
|
|
|
|
|
|
gg.ui.openPanel(UIID.TetriDoc, { data: 3 });
|
|
gg.data.setStatus(StatusType.IsShowPlayGuidePopNoGravity, 1);
|
|
}
|
|
|
|
return
|
|
}
|
|
gg.sdk.showVideoAd((res) => {
|
|
if (res) {
|
|
//白银宝箱
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-AD-神奇方块刷新`)
|
|
gg.game.CurentBattle.IsWatchAd = true
|
|
gg.PRR.AdClickNum.push(7)
|
|
this.CurentBattle.onRefreshMagicalNum--;
|
|
this.refreshMagicalBtn()
|
|
//刷新出三个卡牌,固定2类型的卡牌一张免费使用,剩下的还是走的随机
|
|
this.dealCards(true)
|
|
} else {
|
|
gg.ui.showToast("观看视频失败");
|
|
}
|
|
});
|
|
}
|
|
|
|
click_btnRefreshBox() {
|
|
|
|
if(this.ui_brboxAd.active){
|
|
gg.sdk.showVideoAd((res) => {
|
|
if (res) {
|
|
//换一批
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-换一批`)
|
|
this._isClickRefreshBubble = true;
|
|
this.btnRefreshBox.getChildByName("paopao").active = false;
|
|
//this.CurentBattle.subCoinNum(this.lb_Goid_num);
|
|
this.dealCards()
|
|
this._dealScheduled = false
|
|
this._placedCardCount = 0
|
|
this.ui_choosezhezhao.active = false
|
|
} else {
|
|
gg.ui.showToast("观看视频失败");
|
|
}
|
|
});
|
|
return
|
|
}
|
|
|
|
|
|
if (this.CurentBattle.CurentCoinNum < this.lb_Goid_num) {
|
|
gg.ui.showToast('银币不足')
|
|
return
|
|
}
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-换一批`)
|
|
this._isClickRefreshBubble = true;
|
|
this.btnRefreshBox.getChildByName("paopao").active = false;
|
|
this.CurentBattle.subCoinNum(this.lb_Goid_num);
|
|
this.dealCards()
|
|
this._dealScheduled = false
|
|
this._placedCardCount = 0
|
|
this.ui_choosezhezhao.active = false
|
|
//
|
|
}
|
|
|
|
click_ui_Baoxiang10m() {
|
|
if (this.CurentBattle.is10mBoxOpen || this.ui_Baoxiang10m.getComponent(Sprite).grayscale) {
|
|
return;
|
|
}
|
|
|
|
this.CurentBattle.is10mBoxOpen = true;
|
|
this.ui_Baoxiang10m.active = false;
|
|
this.ui_10mshouzhi.active = false;
|
|
this.hideDescGuide()
|
|
gg.ui.popPanel(UIID.TurnTable, { data: 0 });
|
|
}
|
|
click_ui_Baoxiang30m() {
|
|
if (this.CurentBattle.is30mBoxOpen || this.ui_Baoxiang30m.getComponent(Sprite).grayscale) {
|
|
return;
|
|
}
|
|
//只有一个武器不给开宝箱
|
|
if (this.CurentBattle.curRogueWeaponTimes == 1) {
|
|
gg.ui.showToast('请开启对局内武器宝箱获取第二把武器')
|
|
return
|
|
}
|
|
this.CurentBattle.is30mBoxOpen = true;
|
|
this.ui_Baoxiang30m.active = false;
|
|
gg.ui.popPanel(UIID.TurnTable, { data: 1 });
|
|
}
|
|
click_ui_Baoxiang60m() {
|
|
if (this.CurentBattle.is60mBoxOpen || this.ui_Baoxiang60m.getComponent(Sprite).grayscale) {
|
|
return;
|
|
}
|
|
if (this.CurentBattle.curRogueWeaponTimes == 1) {
|
|
gg.ui.showToast('请开启对局内武器宝箱获取第二把武器')
|
|
return
|
|
}
|
|
this.CurentBattle.is60mBoxOpen = true;
|
|
this.ui_Baoxiang60m.active = false;
|
|
gg.ui.popPanel(UIID.TurnTable, { data: 2 });
|
|
}
|
|
click_ui_Baoxiang100m() {
|
|
if (this.CurentBattle.is100mBoxOpen || this.ui_Baoxiang100m.getComponent(Sprite).grayscale) {
|
|
return;
|
|
}
|
|
if (this.CurentBattle.curRogueWeaponTimes == 1) {
|
|
gg.ui.showToast('请开启对局内武器宝箱获取第二把武器')
|
|
return
|
|
}
|
|
this.CurentBattle.is100mBoxOpen = true;
|
|
this.ui_Baoxiang100m.active = false;
|
|
gg.ui.popPanel(UIID.TurnTable, { data: 3 });
|
|
}
|
|
|
|
/** 战场方块:在 addChild 之前写入玩法配置(含武器/技能),与 dealCards 一致 */
|
|
private _applyBattlePieceConfig(preview: Node, tetriConfig: ITableBattleCube): void {
|
|
if (tetriConfig.type === TetriType.Tetris_Basefloor) {
|
|
preview.getOrAddComponent(tetriFloorNode).setConfig(tetriConfig)
|
|
for (const c of preview.getComponentsInChildren(tetriFloorNode)) {
|
|
c.setConfig(tetriConfig)
|
|
}
|
|
return
|
|
}
|
|
preview.angle = 0
|
|
preview.getOrAddComponent(tetriNode).setConfig(tetriConfig)
|
|
for (const c of preview.getComponentsInChildren(tetriNode)) {
|
|
c.setConfig(tetriConfig)
|
|
}
|
|
if (tetriConfig.type === TetriType.Tetris_BaseGold || tetriConfig.type === TetriType.Tetris_Baseheal) {
|
|
preview.getOrAddComponent(tetriAbility).setConfig(tetriConfig)
|
|
for (const c of preview.getComponentsInChildren(tetriAbility)) {
|
|
c.setConfig(tetriConfig)
|
|
}
|
|
}
|
|
if (tetriConfig.type === TetriType.Tetris_Baseattk || tetriConfig.type === TetriType.Tetris_Basevine) {
|
|
preview.getOrAddComponent(tetriWeaponCarrier).setConfig(tetriConfig, true, true)
|
|
for (const c of preview.getComponentsInChildren(tetriWeaponCarrier)) {
|
|
c.setConfig(tetriConfig, true, true)
|
|
}
|
|
}
|
|
}
|
|
|
|
/** floor 子树碰撞体落稳前保持关闭,普通块全开 */
|
|
private _enableBattlePieceColliders(preview: Node): void {
|
|
for (const col of preview.getComponentsInChildren(PolygonCollider2D)) {
|
|
let isFloorCollider = false
|
|
let cur: Node | null = col.node
|
|
while (cur) {
|
|
if (cur.getComponent(tetriFloorNode)) { isFloorCollider = true; break }
|
|
if (cur === preview) break
|
|
cur = cur.parent
|
|
}
|
|
col.enabled = !isFloorCollider
|
|
}
|
|
}
|
|
|
|
/** 卡牌模板上的 Static/零重力须在进入物理世界前改回 Dynamic */
|
|
private _primeBattlePieceRigidbodies(preview: Node, map: tetriMap | null): void {
|
|
const floorComps = preview.getComponentsInChildren(tetriFloorNode) ?? []
|
|
for (const rb of preview.getComponentsInChildren(RigidBody2D)) {
|
|
rb.enabled = true
|
|
const isFloor = floorComps.some(f => {
|
|
const fn = f?.node
|
|
if (!fn?.isValid) return false
|
|
let cur: Node | null = rb.node
|
|
while (cur) {
|
|
if (cur === fn) return true
|
|
cur = cur.parent
|
|
}
|
|
return false
|
|
})
|
|
if (isFloor) continue
|
|
rb.type = ERigidBody2DType.Dynamic
|
|
if (map) {
|
|
rb.gravityScale = map.fallGravityScale
|
|
rb.linearDamping = map.fallLinearDamping
|
|
} else if (rb.gravityScale === 0) {
|
|
rb.gravityScale = 1
|
|
}
|
|
rb.fixedRotation = false
|
|
rb.enabledContactListener = true
|
|
rb.allowSleep = false
|
|
rb.wakeUp()
|
|
}
|
|
}
|
|
|
|
setLayerMask(n: Node, layerMask: number) {
|
|
n.layer = layerMask;
|
|
for (let i = 0; i < n.children.length; i++) {
|
|
if (n.children[i].isValid) {
|
|
this.setLayerMask(n.children[i], layerMask);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//金币
|
|
refreshCoinNum() {
|
|
this.lb_money.string = `${this.CurentBattle.CurentCoinNum}`
|
|
|
|
|
|
this.lb_num.string = `${this.lb_Goid_num}`
|
|
if (this.CurentBattle.CurentCoinNum < this.lb_Goid_num) {
|
|
this.lb_num.color = new Color(255, 0, 0)
|
|
} else {
|
|
this.lb_num.color = new Color(255, 255, 255)
|
|
}
|
|
|
|
this.refreshCoinNumColor()
|
|
}
|
|
|
|
/**是否点击过锤子泡泡 */
|
|
private _isClickChuiziBubble: boolean = false;
|
|
/**是否点击过刷新泡泡 */
|
|
private _isClickRefreshBubble: boolean = false;
|
|
private _lastTipClickMs = 0;
|
|
|
|
onBattleWaveRefresh() {
|
|
|
|
if (gg.data.doc.chapter == 1) {
|
|
if (this.CurentBattle.CurentWaveNum == 3) {
|
|
|
|
} else if (this.CurentBattle.CurentWaveNum == 2) {
|
|
this.updateBtnActive();
|
|
}
|
|
|
|
if ([6, 9].includes(this.CurentBattle.CurentWaveNum) && !this._isClickChuiziBubble) {
|
|
let paopao = this.btnchuzi.getChildByName("paopao")
|
|
if (paopao?.isValid) {
|
|
paopao.active = true;
|
|
paopao.scale = Vec3.ZERO;
|
|
tween(paopao)
|
|
.to(0.5, { scale: Vec3.ONE }, { easing: 'quadInOut' })
|
|
.delay(3)
|
|
.call(() => {
|
|
paopao.active = false;
|
|
})
|
|
.start();
|
|
}
|
|
}
|
|
if ([4, 7, 10].includes(this.CurentBattle.CurentWaveNum) && !this._isClickRefreshBubble) {
|
|
let paopao = this.btnRefreshBox.getChildByName("paopao")
|
|
if (paopao?.isValid) {
|
|
paopao.active = true;
|
|
paopao.scale = Vec3.ZERO;
|
|
tween(paopao)
|
|
.to(0.5, { scale: Vec3.ONE }, { easing: 'quadInOut' })
|
|
.delay(3)
|
|
.call(() => {
|
|
paopao.active = false;
|
|
})
|
|
.start();
|
|
}
|
|
}
|
|
}
|
|
|
|
//如果是无尽模式不显示后面的TotalWaveNum
|
|
if (this.CurentBattle.BattleType == BattleType.InfiniteMode) {
|
|
this.lb_boci.string = `第${this.CurentBattle.InfiniteModeCurentWaveNum}波`
|
|
|
|
} else {
|
|
this.lb_boci.string = `第${this.CurentBattle.CurentWaveNum}/${this.CurentBattle.TotalWaveNum}波`
|
|
}
|
|
|
|
if (this.CurentBattle.CurentWaveNum == 1 || this.CurentBattle.CurentWaveNum > 20) {
|
|
return
|
|
}
|
|
if (this.CurentBattle.getOpenSkillSelectType() == 0) {
|
|
return
|
|
}
|
|
|
|
|
|
|
|
this.CurentBattle.tetriSaveBoxNum++
|
|
this._playBoxJumpToBoxAnim();
|
|
this.showBoxAccumulateBubble();
|
|
}
|
|
|
|
updateBtnActive() {
|
|
this.btnShenqiBox.active = gg.game.CurentBattle.BattleType == BattleType.SpeedMode;
|
|
this.btnCoin.active = true
|
|
this.btnMaotouying.active = gg.game.CurentBattle.BattleType != BattleType.SpeedMode;;
|
|
this.btnRefreshBox.active = true;
|
|
this.btnchuzi.active = true
|
|
}
|
|
|
|
async showCoinFly(pos: Vec3, count: number = 1) {
|
|
if (!this.ui_topSpineLayer?.isValid) return;
|
|
|
|
if (count == 1) {
|
|
const coinFly = await gg.res.getNodeAsync('prefab/', 'coinFly', GBundle.tetriGame);
|
|
if (!coinFly?.isValid) return;
|
|
coinFly.setParent(this.ui_topSpineLayer);
|
|
coinFly.worldPosition = pos.clone();
|
|
coinFly.getComponent(CoinFly).flyAni(this.ui_coinEndPos.worldPosition.clone(), this.refreshCoinNum.bind(this));
|
|
this.scheduleOnce(() => {
|
|
gg.audio.playEffect({ name: "获得金币" });
|
|
}, 1.2);
|
|
return;
|
|
}
|
|
for (let i = 0; i < count; i++) {
|
|
this.scheduleOnce(() => {
|
|
void (async () => {
|
|
const randomPos = new Vec3(pos.x + Math.random() * 50, pos.y + Math.random() * 50, 0);
|
|
const coinFly = await gg.res.getNodeAsync('prefab/', 'coinFly', GBundle.tetriGame);
|
|
if (!coinFly?.isValid || !this.ui_topSpineLayer?.isValid) return;
|
|
coinFly.setParent(this.ui_topSpineLayer);
|
|
coinFly.worldPosition = randomPos;
|
|
coinFly.getComponent(CoinFly).flyAni(this.ui_coinEndPos.worldPosition.clone(), this.refreshCoinNum.bind(this));
|
|
})();
|
|
}, i * 0.01);
|
|
}
|
|
this.scheduleOnce(() => {
|
|
gg.audio.playEffect({ name: "获得金币" });
|
|
}, 1.2);
|
|
}
|
|
|
|
onPlayerHit(target: Node) {
|
|
this.showHpWarn(true)
|
|
this.refreshWallHp();
|
|
}
|
|
showHpWarn(bShow: boolean) {
|
|
if (this.ui_hpWarn.active && MTools.beforeTimes(1500)) return;
|
|
this.ui_hpWarn.active = bShow;
|
|
}
|
|
|
|
refreshWallHp() {
|
|
let curWallHp = this.CurentBattle.CurentWallHp
|
|
let maxWallHp = this.CurentBattle.WallMaxHp
|
|
this.lb_hpNum.string = `${curWallHp}`
|
|
//console.log('refreshWallHp', curWallHp, maxWallHp)
|
|
this.sp_hpProgressTop.fillRange = curWallHp / maxWallHp
|
|
this.scheduleOnce(() => {
|
|
this.sp_hpProgressMid.fillRange = curWallHp / maxWallHp
|
|
}, 0.1)
|
|
this.checkbtnAdHp()
|
|
// this.checkbtnAdHp()
|
|
|
|
}
|
|
|
|
checkbtnAdHp() {
|
|
let limitNum = gg.data.table.getConst(ConstNumType.AdAddHpLimit);
|
|
let AdAddHp = gg.data.table.getConst(ConstNumType.AdAddHp);
|
|
let maxWallHp = this.CurentBattle.WallMaxHp
|
|
let curWallHp = this.CurentBattle.CurentWallHp
|
|
let chufaHp = maxWallHp * AdAddHp
|
|
if (this.CurentBattle.curAdAddHpLeftNum >= limitNum) {
|
|
this.btnRecoveyHp.active = false
|
|
} else {
|
|
if (curWallHp <= chufaHp) {
|
|
this.btnRecoveyHp.active = true
|
|
} else {
|
|
this.btnRecoveyHp.active = false
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
refreshShovelBtn() {
|
|
if(gg.game.CurentBattle.BattleType == BattleType.OrangeWeaponMode_Dian){
|
|
if (this.CurentBattle.gameShovelNum <= 0) {
|
|
this.ui_czAd.active = true
|
|
} else {
|
|
this.ui_czAd.active = false
|
|
}
|
|
}
|
|
|
|
this.lb_cznum.string = `${this.CurentBattle.GameShovelCoin}`;
|
|
this.lb_cznum.color = Color.WHITE;
|
|
if (this.CurentBattle.CurentCoinNum < this.CurentBattle.GameShovelCoin) {
|
|
this.lb_cznum.color = Color.RED
|
|
}
|
|
|
|
|
|
//this.ui_bottom_btnchuzi_lb_num.string = `${this.CurentBattle.gameShovelNum}`
|
|
}
|
|
|
|
|
|
refreshMagicalBtn() {
|
|
this.lb_sqnum.string = `${this.CurentBattle.onRefreshMagicalNum}/3`
|
|
}
|
|
|
|
private _isTouchInNode(event: EventTouch, node: Node | null): boolean {
|
|
if (!node?.isValid) return false;
|
|
const ut = node.getComponent(UITransform);
|
|
if (!ut) return false;
|
|
const rect = ut.getBoundingBoxToWorld();
|
|
const p = event.getUILocation();
|
|
return p.x >= rect.xMin && p.x <= rect.xMax && p.y >= rect.yMin && p.y <= rect.yMax;
|
|
}
|
|
|
|
/** 仅当触摸在游戏区,且没有在拖拽新卡时,才推动正在下落的方块 */
|
|
private _canControlFallingByTouch(event: EventTouch): boolean {
|
|
if (this.CurentBattle?.IsUseShovel) return false;
|
|
// 拖拽下一张卡(预览/引导层激活)期间,不允许推动已下落块
|
|
if (this.ui_choosezhezhao?.active) return false;
|
|
// 底部 UI 区域(卡槽、按钮)点击不触发推动
|
|
if (this._isTouchInNode(event, this.ui_bottom)) return false;
|
|
if (this._isTouchInNode(event, this.ui_card)) return false;
|
|
return true;
|
|
}
|
|
|
|
private _onGameplayTouchStart(event: EventTouch): void {
|
|
if (!this._canControlFallingByTouch(event)) return;
|
|
this.CurentBattle?.TetraMap?.handleExternalTouchStart(event);
|
|
}
|
|
|
|
private _onGameplayTouchEnd(event: EventTouch): void {
|
|
if (!this._canControlFallingByTouch(event)) return;
|
|
this.CurentBattle?.TetraMap?.handleExternalTouchEnd(event);
|
|
}
|
|
|
|
|
|
click_btnUserchuziStart(event: EventTouch) {
|
|
if (!this.CurentBattle.IsUseShovel) {
|
|
return
|
|
}
|
|
// 必须吞掉触摸:禁止穿透到方块节点(节点用 UI 命中,堆高镜头缩放后会点偏)
|
|
event.propagationStopped = true;
|
|
}
|
|
|
|
/**
|
|
* 锤子落点:UI 触点 → 双相机桥接到 GameCamera 世界(与画面一致)。
|
|
* 堆高后 GameCamera 会拉远/抬 Y,不能拿 UI 坐标直接对碰撞体,也不能用「以 GameCam 为中心 /zoom」的错误公式。
|
|
*/
|
|
private _getShovelTouchWorldPos(event: EventTouch): Vec3 {
|
|
const map = this.CurentBattle?.TetraMap;
|
|
const p = event.getUILocation();
|
|
if (map?.uiWorldToGameWorld) {
|
|
return map.uiWorldToGameWorld(p.x, p.y);
|
|
}
|
|
if (map) {
|
|
return new Vec3(map.uiWorldXToGameWorldX(p.x), map.uiWorldYToGameWorldY(p.y), 0);
|
|
}
|
|
return new Vec3(p.x, p.y, 0);
|
|
}
|
|
|
|
private _shovelPointInPolygon(ptX: number, ptY: number, poly: Vec2[]): boolean {
|
|
let inside = false;
|
|
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
const xi = poly[i].x;
|
|
const yi = poly[i].y;
|
|
const xj = poly[j].x;
|
|
const yj = poly[j].y;
|
|
const intersect = ((yi > ptY) !== (yj > ptY))
|
|
&& (ptX < (xj - xi) * (ptY - yi) / ((yj - yi) || 1e-6) + xi);
|
|
if (intersect) inside = !inside;
|
|
}
|
|
return inside;
|
|
}
|
|
|
|
/**
|
|
* 精确命中:优先 Polygon 点内判定(避免 L/T 形 AABB 空白角误点邻块);
|
|
* 返回越小越好的分数(中心距² + 面积微偏置)。
|
|
*/
|
|
private _shovelHitScore(root: Node | null, worldX: number, worldY: number): number {
|
|
if (!root?.isValid) return Number.POSITIVE_INFINITY;
|
|
const cols = root.getComponentsInChildren(Collider2D) ?? [];
|
|
let best = Number.POSITIVE_INFINITY;
|
|
const lv = new Vec3();
|
|
const wv = new Vec3();
|
|
for (let i = 0; i < cols.length; i++) {
|
|
const c = cols[i];
|
|
if (!c?.node?.isValid || !c.enabled) continue;
|
|
|
|
if (c instanceof PolygonCollider2D) {
|
|
const pts = c.points ?? [];
|
|
if (pts.length < 3) continue;
|
|
const off = (c as any).offset as Vec2 | undefined;
|
|
const ox = off?.x ?? 0;
|
|
const oy = off?.y ?? 0;
|
|
const m = c.node.getWorldMatrix();
|
|
const worldPts: Vec2[] = [];
|
|
let cx = 0;
|
|
let cy = 0;
|
|
for (let k = 0; k < pts.length; k++) {
|
|
lv.set(pts[k].x + ox, pts[k].y + oy, 0);
|
|
Vec3.transformMat4(wv, lv, m);
|
|
worldPts.push(new Vec2(wv.x, wv.y));
|
|
cx += wv.x;
|
|
cy += wv.y;
|
|
}
|
|
if (!this._shovelPointInPolygon(worldX, worldY, worldPts)) continue;
|
|
cx /= worldPts.length;
|
|
cy /= worldPts.length;
|
|
const dx = worldX - cx;
|
|
const dy = worldY - cy;
|
|
const aabb = c.worldAABB;
|
|
const area = aabb
|
|
? Math.max(1, (aabb.xMax - aabb.xMin) * (aabb.yMax - aabb.yMin))
|
|
: worldPts.length;
|
|
const score = dx * dx + dy * dy + area * 1e-6;
|
|
if (score < best) best = score;
|
|
continue;
|
|
}
|
|
|
|
const aabb = c.worldAABB;
|
|
if (!aabb) continue;
|
|
if (worldX < aabb.xMin || worldX > aabb.xMax || worldY < aabb.yMin || worldY > aabb.yMax) {
|
|
continue;
|
|
}
|
|
const cx = (aabb.xMin + aabb.xMax) * 0.5;
|
|
const cy = (aabb.yMin + aabb.yMax) * 0.5;
|
|
const dx = worldX - cx;
|
|
const dy = worldY - cy;
|
|
const area = Math.max(1, (aabb.xMax - aabb.xMin) * (aabb.yMax - aabb.yMin));
|
|
const score = dx * dx + dy * dy + area * 1e-6;
|
|
if (score < best) best = score;
|
|
}
|
|
return best;
|
|
}
|
|
|
|
private _tryUseShovelByOverlayTouch(event: EventTouch): boolean {
|
|
const p = this._getShovelTouchWorldPos(event);
|
|
const table = this.CurentBattle?.TetraMap?._tetrisTable;
|
|
const mapNode = this.CurentBattle?.TetraMap?.node;
|
|
const searchRoot = table?.isValid ? table : mapNode;
|
|
if (!searchRoot?.isValid) return false;
|
|
|
|
let bestClick: (() => void) | null = null;
|
|
let bestScore = Number.POSITIVE_INFINITY;
|
|
|
|
const floors = searchRoot.getComponentsInChildren(tetriFloorNode) ?? [];
|
|
for (let i = 0; i < floors.length; i++) {
|
|
const f = floors[i];
|
|
if (!f?.node?.isValid) continue;
|
|
const score = this._shovelHitScore(f.node, p.x, p.y);
|
|
if (score < bestScore) {
|
|
bestScore = score;
|
|
bestClick = () => f.click_btnEnd();
|
|
}
|
|
}
|
|
const normals = searchRoot.getComponentsInChildren(tetriNode) ?? [];
|
|
for (let i = 0; i < normals.length; i++) {
|
|
const n = normals[i];
|
|
if (!n?.node?.isValid) continue;
|
|
const score = this._shovelHitScore(n.node, p.x, p.y);
|
|
if (score < bestScore) {
|
|
bestScore = score;
|
|
bestClick = () => n.click_btnEnd();
|
|
}
|
|
}
|
|
if (!bestClick || !Number.isFinite(bestScore)) return false;
|
|
// 命中用游戏世界坐标;特效 anim 层=GameCamera(524288),必须也用游戏世界坐标
|
|
this.showChuiZiEffect(p, bestClick);
|
|
return true;
|
|
}
|
|
|
|
click_btnUserchuziEnd(event: EventTouch) {
|
|
if (!this.CurentBattle.IsUseShovel) {
|
|
return
|
|
}
|
|
event.propagationStopped = true;
|
|
if (this._tryUseShovelByOverlayTouch(event)) {
|
|
return;
|
|
}
|
|
|
|
if (gg.data.doc.chapter == 1 && this.chapter1FreeCZ) {
|
|
this.chapter1FreeCZ = false;
|
|
this.ui_czAd.active = false
|
|
|
|
this.lb_freeCzLab.node.active = false
|
|
this.ui_chuizishouzhi.active = false
|
|
this.refreshShovelBtn()
|
|
}
|
|
|
|
//取消使用锤子发消息
|
|
GEvent.Ins.emit(GEvent.TetriGameCancelUseShovel)
|
|
}
|
|
cancelUseShovel() {
|
|
|
|
this.ui_heightNode.active = true
|
|
this.ui_bottom.active = true
|
|
this.ui_top.active = true
|
|
this.ui_playHp.active = true
|
|
this.ui_Gold.active = true
|
|
this.ui_card.active = true
|
|
// this.ui_choosezhezhao.active = true
|
|
// this.ui_hpWarn.active = true
|
|
|
|
this.ui_Userchuizi.active = false
|
|
//显示使用锤子层级
|
|
|
|
this.CurentBattle.resume()
|
|
this.CurentBattle.IsUseShovel = false;
|
|
//刷新一下锤子按钮
|
|
this.refreshShovelBtn()
|
|
}
|
|
|
|
gameReborn() {
|
|
this.scheduleOnce(() => {
|
|
this.CurentBattle.startGame()
|
|
this.resetLoveHp()
|
|
this.CurentBattle.resetLightningLoveState()
|
|
}, 1.0)
|
|
}
|
|
|
|
/**
|
|
* 播放锤子动画。
|
|
* pos 必须是 GameCamera 世界坐标:预制体里 anim 子节点 layer=524288,只被 GameCamera 看见;
|
|
* 若误用 UI 坐标,堆高拉远后锤子会画在屏幕下方。
|
|
*/
|
|
showChuiZiEffect(pos: Vec3, callBack: Function = null) {
|
|
this.ui_chuiziEff.active = true;
|
|
this.ui_chuiziEff.setWorldPosition(pos.x, pos.y, pos.z);
|
|
// 确保 Spine 走游戏相机层(与叠塔同层)
|
|
const anim = this.ui_chuiziEff.getChildByName('anim');
|
|
const gameLayer = (gg.ui?.GameCamera as Camera | null)?.visibility ?? 524288;
|
|
if (anim?.isValid && gameLayer) {
|
|
anim.layer = gameLayer;
|
|
}
|
|
this.ui_chuiziEff.getComponentInChildren(sp.Skeleton).setAnimation(0, 'animation', false);
|
|
gg.audio.playEffect({ name: "锤子" });
|
|
this.scheduleOnce(() => {
|
|
this.ui_chuiziEff.active = false;
|
|
if (callBack) {
|
|
callBack();
|
|
}
|
|
}, 1.2)
|
|
}
|
|
|
|
private _playBoxJumpToBoxAnim(): void {
|
|
//游戏结束不需要播放宝箱跳转动画
|
|
if (!this.CurentBattle.IsGameOverBoxJump) {
|
|
return;
|
|
}
|
|
if (gg.game.CurentBattle.BattleType == BattleType.SpeedMode) {
|
|
return;
|
|
}
|
|
const from = this.ui_boxJump;
|
|
const to = this.ui_box;
|
|
if (!from?.isValid || !to?.isValid) return;
|
|
|
|
// 起点(world)缓存
|
|
if (!this._boxJumpOriginWorld) {
|
|
this._boxJumpOriginWorld = from.worldPosition.clone();
|
|
}
|
|
const start = this._boxJumpOriginWorld.clone();
|
|
const end = to.worldPosition.clone();
|
|
|
|
|
|
// 若两者几乎重合则直接切换显示
|
|
const dx = end.x - start.x;
|
|
const dy = end.y - start.y;
|
|
if (dx * dx + dy * dy < 1) {
|
|
to.active = true;
|
|
from.active = false;
|
|
from.worldPosition = start;
|
|
return;
|
|
}
|
|
|
|
// 预设:飞行中先显示 jump、隐藏 box,结束后反过来
|
|
from.active = true;
|
|
//to.active = false;
|
|
from.worldPosition = start;
|
|
|
|
// 控制点:中点上抬(根据距离动态调整)
|
|
const mid = new Vec3((start.x + end.x) * 0.5, (start.y + end.y) * 0.5, start.z);
|
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
// 抛物线高度:调高一点(距离越远抛得越高)
|
|
const lift = Math.max(220, Math.min(800, dist * 1));
|
|
const ctrl = new Vec3(mid.x, mid.y + lift, start.z);
|
|
|
|
this.showGetTip('获得技能宝箱')
|
|
|
|
const duration = Math.max(0.35, Math.min(0.8, dist / 900));
|
|
Tween.stopAllByTarget(from);
|
|
tween({ t: 0 })
|
|
.to(duration, { t: 1 }, {
|
|
easing: 'quadOut',
|
|
onUpdate: (obj: any) => {
|
|
const t = Math.max(0, Math.min(1, obj.t ?? 0));
|
|
const it = 1 - t;
|
|
const x = it * it * start.x + 2 * it * t * ctrl.x + t * t * end.x;
|
|
const y = it * it * start.y + 2 * it * t * ctrl.y + t * t * end.y;
|
|
from.setWorldPosition(x, y, start.z);
|
|
}
|
|
})
|
|
.call(() => {
|
|
if (!from?.isValid || !to?.isValid) return;
|
|
to.active = true;
|
|
to.getComponent(DiamondTreasure).refreshBoxNum()
|
|
to.getComponent(DiamondTreasure).playSpine('idle')
|
|
// 宝箱 UI 复用/切换显示时,确保可点击恢复
|
|
to.getComponent(DiamondTreasure).isCanClick = true;
|
|
to.getComponent(DiamondTreasure).lableTip = this.ui_guideLab;
|
|
from.active = false;
|
|
from.worldPosition = start;
|
|
this.refreshTreasureCostState(to);
|
|
//
|
|
if (this.CurentBattle.ChapterId == 1 && this.CurentBattle.CurentWaveNum == 3) {
|
|
//开启引导
|
|
this.ui_boxShouzhi.active = true;
|
|
// this.showDescGuide("波次结束,获得宝箱,解锁武器和技能");
|
|
|
|
let paopao = to.getChildByName('paopao')
|
|
|
|
if (paopao?.isValid) {
|
|
paopao.active = true;
|
|
paopao.scale = Vec3.ZERO;
|
|
tween(paopao)
|
|
.to(0.5, { scale: v3(2, 2, 2) }, { easing: 'quadInOut' })
|
|
.start();
|
|
}
|
|
|
|
// gg.ui.openPanel(UIID.NewGuideMask, {
|
|
// data: [
|
|
// [this.ui_box],
|
|
// ['<b><size=26><outline color=#000000 width=2><color=#ffffff>波次结束,获得</color><color=#45ff64>宝箱,解锁武器和技能</color></outline></size></b>'],
|
|
// () => {
|
|
// this.pauseUpdateTime = false
|
|
// // gg.data.setStatistics(StatisticsType.firstChapterGuideIndex, 1)
|
|
// },
|
|
// true,
|
|
// 255
|
|
// ]
|
|
// });
|
|
} else {
|
|
this.ui_boxShouzhi.active = false;
|
|
}
|
|
})
|
|
.start();
|
|
}
|
|
|
|
/**刷新银币lable颜色 */
|
|
refreshCoinNumColor() {
|
|
this.refreshTreasureCostState(this.ui_box);
|
|
|
|
//锤子
|
|
this.lb_cznum.string = `${this.CurentBattle.GameShovelCoin}`;
|
|
this.lb_cznum.color = Color.WHITE;
|
|
if (gg.game.CurentBattle.CurentCoinNum < gg.game.CurentBattle.GameShovelCoin) {
|
|
this.lb_cznum.color = Color.RED;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/**刷新宝箱免费/价格显示(含显隐、文案和颜色) */
|
|
private refreshTreasureCostState(boxNode: Node) {
|
|
if (!boxNode?.isValid) return;
|
|
let lbNum = boxNode.getChildByName("lbNum")?.getComponent(Label);
|
|
let lbfree = boxNode.getChildByName("lbfree")?.getComponent(Label);
|
|
if (!lbNum || !lbfree) return;
|
|
const costArr = gg.game?.CurentBattle?.OpenSkillSelectCoinArr ?? [];
|
|
if (costArr.length <= 0) {
|
|
lbNum.node.active = false;
|
|
lbfree.node.active = true;
|
|
return;
|
|
}
|
|
let index = gg.game.CurentBattle.IsOpenSkillSelectTimes;
|
|
if (index >= costArr.length) {
|
|
index = costArr.length - 1;
|
|
}
|
|
let num = Number(costArr[index] ?? 0);
|
|
lbNum.node.active = num > 0;
|
|
lbfree.node.active = num <= 0;
|
|
lbNum.string = String(num);
|
|
lbNum.color = (gg.game.CurentBattle.CurentCoinNum < num) ? Color.RED : Color.WHITE;
|
|
if(gg.game.CurentBattle.BattleType == BattleType.OrangeWeaponMode_Dian){
|
|
lbfree.node.active = true
|
|
lbNum.node.active = false
|
|
}
|
|
}
|
|
|
|
showOwl() {
|
|
if (this.btnOwl == null || !this.btnOwl.isValid) {
|
|
return;
|
|
}
|
|
if (this.btnOwl.active) return;
|
|
this.btnOwl.active = true;
|
|
const anim = this._getOwlChild("anim");
|
|
const spt = this._getOwlChild("spt");
|
|
if (!anim || !spt) {
|
|
// prefab 结构不匹配或节点已失效:直接隐藏,避免报错
|
|
this.btnOwl.active = false;
|
|
return;
|
|
}
|
|
if (gg.data.getStatistics(StatisticsType.OwlFreeGetCoinCount) == 0) {
|
|
this.btnOwl.getChildByName('ad').active = false
|
|
|
|
} else {
|
|
this.btnOwl.getChildByName('ad').active = true
|
|
}
|
|
anim.active = true;
|
|
spt.active = false;
|
|
const owlCoin = gg.game?.CurentBattle?.VideoAddCoin ?? 0;
|
|
this.btnOwl.getComponentInChildren(Label).string = String(owlCoin);
|
|
let spos = this.ui_owlStartPos.worldPosition.clone();
|
|
let epos = this.ui_owl.worldPosition.clone();
|
|
this.btnOwl.setWorldPosition(spos.x, spos.y, 0);
|
|
Tween.stopAllByTarget(this.btnOwl);
|
|
// 停掉上一段 _owlTweenTarget,否则旧 onUpdate 仍会持续改 btnOwl 世界坐标
|
|
if (this._owlTweenTarget) {
|
|
Tween.stopAllByTarget(this._owlTweenTarget);
|
|
this._owlTweenTarget = null;
|
|
}
|
|
const dx = epos.x - spos.x;
|
|
const dy = epos.y - spos.y;
|
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
const mid = new Vec3((spos.x + epos.x) * 0.5, (spos.y + epos.y) * 0.5, 0);
|
|
const lift = Math.max(120, Math.min(420, dist * 0.45));
|
|
const ctrl = new Vec3(mid.x, mid.y + lift, 0);
|
|
this._owlTweenTarget = { t: 0 };
|
|
tween(this._owlTweenTarget)
|
|
.to(10, { t: 1 }, {
|
|
easing: 'quadOut',
|
|
onUpdate: (obj: { t: number }) => {
|
|
if (!this.btnOwl?.isValid) return;
|
|
const t = Math.max(0, Math.min(1, obj.t ?? 0));
|
|
const it = 1 - t;
|
|
const x = it * it * spos.x + 2 * it * t * ctrl.x + t * t * epos.x;
|
|
const y = it * it * spos.y + 2 * it * t * ctrl.y + t * t * epos.y;
|
|
this.btnOwl.setWorldPosition(x, y, 0);
|
|
}
|
|
})
|
|
.call(() => {
|
|
if (!this.btnOwl?.isValid) return;
|
|
const anim2 = this._getOwlChild("anim");
|
|
const spt2 = this._getOwlChild("spt");
|
|
if (!anim2 || !spt2) return;
|
|
anim2.active = false;
|
|
spt2.active = true;
|
|
|
|
if (gg.data.getStatistics(StatisticsType.OwlFreeGetCoinCount) == 0) {
|
|
this.ui_guidedianjiMty.active = true
|
|
|
|
} else {
|
|
this.ui_guidedianjiMty.active = false
|
|
}
|
|
|
|
})
|
|
.start();
|
|
// .delay(18)
|
|
// .call(() => {
|
|
// if (!this.btnOwl?.isValid) return;
|
|
// const anim3 = this._getOwlChild("anim");
|
|
// const spt3 = this._getOwlChild("spt");
|
|
// if (!anim3 || !spt3) return;
|
|
// anim3.active = true;
|
|
// spt3.active = false;
|
|
// this.ui_guidedianjiMty.active = false
|
|
// tween(this.btnOwl)
|
|
// .by(1, { x: 200, y: 200 })
|
|
// .call(() => {
|
|
// if (!this.btnOwl?.isValid) return;
|
|
// this.btnOwl.active = false;
|
|
// this.unschedule(this.showOwl);
|
|
// this.schedule(this.showOwl, this.CurentBattle.AdFrequency);
|
|
// })
|
|
// .start();
|
|
// })
|
|
// .start();
|
|
}
|
|
|
|
click_btnOwl(): void {
|
|
let ad = this.btnOwl.getChildByName('ad')
|
|
if (ad.active == false) {
|
|
//不需要看广告
|
|
this.btnOwl.getChildByName('ad').active = true
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-猫头鹰硬币免费`)
|
|
GEvent.Ins.emit(GEvent.showCoinFly, this.btnOwl.worldPosition.clone(), 15);
|
|
gg.data.setStatistics(StatisticsType.OwlFreeGetCoinCount, 1)
|
|
this.ui_guidedianjiMty.active = false
|
|
|
|
this.CurentBattle.addCoinNum(this.CurentBattle.VideoAddCoin);
|
|
if (this._owlTweenTarget) {
|
|
Tween.stopAllByTarget(this._owlTweenTarget);
|
|
this._owlTweenTarget = null;
|
|
}
|
|
Tween.stopAllByTarget(this.btnOwl);
|
|
this.btnOwl.active = false;
|
|
this.unschedule(this.showOwl);
|
|
this.schedule(this.showOwl, this.CurentBattle.AdFrequency);
|
|
return;
|
|
|
|
}
|
|
gg.sdk.showVideoAd((res) => {
|
|
if (res) {
|
|
gg.game.CurentBattle.IsWatchAd = true
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-AD-猫头鹰硬币`)
|
|
GEvent.Ins.emit(GEvent.showCoinFly, this.btnOwl.worldPosition.clone(), 15);
|
|
|
|
this.CurentBattle.addCoinNum(this.CurentBattle.VideoAddCoin);
|
|
if (this._owlTweenTarget) {
|
|
Tween.stopAllByTarget(this._owlTweenTarget);
|
|
this._owlTweenTarget = null;
|
|
}
|
|
Tween.stopAllByTarget(this.btnOwl);
|
|
this.btnOwl.active = false;
|
|
this.unschedule(this.showOwl);
|
|
this.schedule(this.showOwl, this.CurentBattle.AdFrequency);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
/**弹提示 */
|
|
showGetTip(tip: string) {
|
|
Tween.stopAllByTarget(this.ui_tip);
|
|
this.ui_tip.active = true;
|
|
this.lb_getTip.string = tip;
|
|
this.ui_tip.x = -800;
|
|
tween(this.ui_tip)
|
|
.to(0.5, { x: -375 })
|
|
.delay(2.0)
|
|
.to(1.0, { x: -800 })
|
|
.call(() => {
|
|
this.ui_tip.active = false;
|
|
})
|
|
.start();
|
|
}
|
|
|
|
updateDps(delay: number = 0) {
|
|
if(gg.game.noMonsterShow) return;
|
|
const dpsThrottle = BattlePerformance.dpsUiMinIntervalMs();
|
|
// beforeTimes:冷却内返回 true → 应跳过;勿取反(取反会导致该刷时不刷)
|
|
if (dpsThrottle > 0 && MTools.beforeTimes(dpsThrottle, 'battle_dps_ui')) {
|
|
return;
|
|
}
|
|
|
|
let dps = this.CurentBattle.dps();
|
|
dps = Math.floor(dps);
|
|
if (this.CurentBattle?.skipNextDpsFlyAnim) {
|
|
this.CurentBattle.skipNextDpsFlyAnim = false;
|
|
this._lastDps = dps;
|
|
this.lb_dps.string = MTools.formatChineseUnit({ num: dps });
|
|
return;
|
|
}
|
|
if (dps == this._lastDps || dps == 0) {
|
|
if (dps == 0) {
|
|
this.lb_dps.string = '0';
|
|
this._lastDps = dps;
|
|
}
|
|
return;
|
|
}
|
|
// console.log('updateDps',dps,this._lastDps)
|
|
//如果dps比this._lastDps大播放动画
|
|
if (dps > this._lastDps || dps < this._lastDps) {
|
|
//播放动画
|
|
let addDps = 0;
|
|
let isAdd = false
|
|
if (dps > this._lastDps) {
|
|
addDps = dps - this._lastDps;
|
|
isAdd = true;
|
|
} else {
|
|
addDps = this._lastDps - dps;
|
|
isAdd = false;
|
|
}
|
|
this._lastDps = dps;
|
|
this.scheduleOnce(() => {
|
|
|
|
console.log('播放动画', dps, this._lastDps)
|
|
let copyNode = instantiate(this.ui_dpsMove);
|
|
copyNode.parent = this.ui_dpsMove.parent;
|
|
copyNode.active = true
|
|
let posiy = this.CurentBattle.TetraMap.getHeightLinePosition();
|
|
copyNode.y = posiy - 100;
|
|
|
|
let lb_moveDps = copyNode.getChildByName('lb_moveDps')?.getComponent(Label);
|
|
lb_moveDps.string = isAdd ? '+' + addDps : '-' + addDps;
|
|
|
|
let movePosy = copyNode.position.y + 50;
|
|
let endPos = this.ui_dps.position.clone();
|
|
|
|
|
|
tween(copyNode)
|
|
.to(1, { y: movePosy })
|
|
.to(0.5, { position: endPos, scale: v3(0.3, 0.3, 0.3) })
|
|
.call(() => {
|
|
copyNode.destroy();
|
|
this.lb_dps.string = MTools.formatChineseUnit({ num: dps });
|
|
Tween.stopAllByTarget(this.ui_dps);
|
|
tween(this.ui_dps)
|
|
.to(0.2, { scale: v3(1.2, 1.2, 1.2) })
|
|
.to(0.2, { scale: v3(1, 1, 1) })
|
|
.start();
|
|
|
|
})
|
|
.start();
|
|
|
|
}, delay)
|
|
|
|
} else {
|
|
this._lastDps = dps;
|
|
this.lb_dps.string = MTools.formatChineseUnit({ num: dps });
|
|
Tween.stopAllByTarget(this.ui_dps);
|
|
tween(this.ui_dps)
|
|
.to(0.2, { scale: v3(1.2, 1.2, 1.2) })
|
|
.to(0.2, { scale: v3(1, 1, 1) })
|
|
.start();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
private _lastDps: number = -1;
|
|
|
|
|
|
|
|
showChoosezhezhao() {
|
|
if (gg.data.doc.chapter > 1) {
|
|
return
|
|
}
|
|
if (this.fangkuaiNum >= 3) {
|
|
return
|
|
}
|
|
this.ui_choosezhezhao1.active = true;
|
|
this.ui_choosezhezhao2.active = true;
|
|
}
|
|
|
|
hideChoosezhezhao() {
|
|
if (gg.data.doc.chapter > 1) {
|
|
return
|
|
}
|
|
|
|
this.ui_choosezhezhao1.active = false;
|
|
this.ui_choosezhezhao2.active = false;
|
|
}
|
|
|
|
|
|
click_btnCoin() {
|
|
let maxCount = this.maxCountCoin
|
|
if (gg.game.CurentBattle.IsReplica) {
|
|
maxCount = 10000000;
|
|
}
|
|
if (gg.game.CurentBattle.getFreeCoinNum >= maxCount) {
|
|
gg.ui.showToast('免费次数已用完')
|
|
return
|
|
}
|
|
if (this.ui_mfybAd.active) {
|
|
//看广告
|
|
gg.sdk.showVideoAd((res) => {
|
|
if (res) {
|
|
gg.game.CurentBattle.IsWatchAd = true
|
|
gg.game.CurentBattle.getFreeCoinNum++;
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-AD-免费银币`)
|
|
this.CurentBattle.CurentCoinNum += this.CurentBattle.VideoAddCoin;
|
|
GEvent.Ins.emit(GEvent.showCoinFly, this.btnCoin.worldPosition.clone(), 15);
|
|
this.ui_mfybAd.active = true
|
|
this.lb_mfybCount.node.active = true
|
|
this.ui_mfybshouzhi.active = false
|
|
this.setFreeBtnCoinCount()
|
|
|
|
if (gg.game.CurentBattle.getFreeCoinNum >= maxCount) {
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}-免费银币次数用完`)
|
|
}
|
|
|
|
|
|
}
|
|
});
|
|
} else {
|
|
//直接获取不用看广告
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${curWaveNum}-免费银币`)
|
|
this.CurentBattle.CurentCoinNum += this.CurentBattle.VideoAddCoin;
|
|
|
|
GEvent.Ins.emit(GEvent.showCoinFly, this.btnCoin.worldPosition.clone(), 15);
|
|
|
|
this.ui_mfybAd.active = true
|
|
this.lb_mfybCount.node.active = true
|
|
this.ui_mfybshouzhi.active = false
|
|
this.setFreeBtnCoinCount()
|
|
if (gg.game.CurentBattle.getFreeCoinNum >= maxCount) {
|
|
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}-免费银币次数用完`)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
setFreeBtnCoinNum() {
|
|
const owlCoin = gg.game?.CurentBattle?.VideoAddCoin ?? 0;
|
|
this.lb_coinNum.string = `+${owlCoin}`;
|
|
this.setFreeBtnCoinCount()
|
|
}
|
|
|
|
setFreeBtnCoinCount() {
|
|
let maxCount = this.maxCountCoin
|
|
this.lb_mfybCount.string = `${maxCount - gg.game.CurentBattle.getFreeCoinNum}/${maxCount}`;
|
|
this.lb_mfybCount.node.active = gg.game.CurentBattle.BattleType != BattleType.SpeedMode;
|
|
if(gg.game.CurentBattle.IsReplica){
|
|
this.lb_mfybCount.node.active = false
|
|
}
|
|
}
|
|
|
|
/**宝箱累计气泡框提示:当宝箱大于等于2 第十波次开始出现*/
|
|
showBoxAccumulateBubble() {
|
|
if (!this.CurentBattle.IsGameOverBoxJump) {
|
|
return
|
|
}
|
|
if (gg.game.CurentBattle.CurentWaveNum < 10) {
|
|
return
|
|
}
|
|
if (gg.game.CurentBattle.tetriSaveBoxNum < 2) {
|
|
return
|
|
}
|
|
//宝箱来了显示气泡
|
|
|
|
let paopao = this.ui_box.getChildByName('paopao')
|
|
|
|
if (paopao?.isValid) {
|
|
paopao.active = true;
|
|
paopao.scale = Vec3.ZERO;
|
|
tween(paopao)
|
|
.to(0.5, { scale: v3(2, 2, 2) }, { easing: 'quadInOut' })
|
|
.delay(3.0)
|
|
.call(() => {
|
|
paopao.active = false;
|
|
})
|
|
.start();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
click_btnTiqian() {
|
|
this.CurentBattle.tryFinishWarnTimeEarly();
|
|
this.onTetriGameStart();
|
|
}
|
|
|
|
|
|
click_btnRecoveyHp(){
|
|
// 战斗暂停由 showVideoAd → _enter/_leaveVideoAdPause 对称处理,勿在此先 pause
|
|
gg.sdk.showVideoAd((res) => {
|
|
if (res) {
|
|
let curWaveNum = gg.game.CurentBattle.CurentWaveNum
|
|
gg.sdk.reportDY("inLevel", `章节${this.CurentBattle.getReportDYChapterId()}_${curWaveNum}-AD-满血`)
|
|
this.CurentBattle.setCurentWallHp(this.CurentBattle.WallMaxHp)
|
|
this.CurentBattle.curAdAddHpLeftNum++
|
|
this.refreshWallHp()
|
|
} else {
|
|
gg.ui.showToast('观看视频失败');
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
//雷电副本ui显示与隐藏
|
|
showHideLoveHpUI(){
|
|
this.btnShenqiBox.active = true
|
|
this.btnCoin.active = false
|
|
this.btnShenqiBox.position = this.btnCoin.position.clone()
|
|
|
|
this.ui_czAd.active = true
|
|
this.lb_cznum.node.active = false
|
|
this.btnchuzi.getChildByName('jinbi').active = false
|
|
this.ui_brboxAd.active = true
|
|
this.lb_num.node.active = false
|
|
this.btnRefreshBox.getChildByName('jinbi').active = false
|
|
|
|
this.ui_Gold.active = false
|
|
|
|
let posiai = this.ui_aixinNodes.position.clone()
|
|
let posi = this.ui_timeNode.position.clone()
|
|
this.ui_aixinNodes.setPosition(posi.x,posi.y)
|
|
this.ui_timeNode.setPosition(posiai.x,posiai.y)
|
|
}
|
|
//雷电副本爱心重置
|
|
resetLoveHp(){
|
|
this.aixinIndex = 0
|
|
//爱心还原位置0,0;透明度设置为255,显示可见
|
|
this.ui_aixinNodes.children.forEach(child => {
|
|
let aixin_liang = child.getChildByName('aixin_liang')
|
|
aixin_liang.setPosition(0,0)
|
|
aixin_liang.opacity = 255
|
|
aixin_liang.active = true
|
|
})
|
|
}
|
|
//雷电副本创建爱心血量
|
|
createLoveHp(){
|
|
let hpNum = gg.game.CurentBattle.loveHpNum
|
|
//创建hpNum-1个爱心
|
|
let aixinNode_hei = this.ui_aixinNodes.getChildByName('aixinNode_hei')
|
|
aixinNode_hei.active = true
|
|
for(let i = 0; i < hpNum - 1; i++){
|
|
let node = instantiate(aixinNode_hei)
|
|
node.parent = this.ui_aixinNodes
|
|
node.setPosition(0,0)
|
|
node.active = true
|
|
}
|
|
}
|
|
|
|
//雷电副本爱心掉落
|
|
loveHpDrop(){
|
|
if (gg.game.CurentBattle.BattleType !== BattleType.OrangeWeaponMode_Dian || gg.game.CurentBattle.loveHpNum <= 0) return;
|
|
if (!this.ui_aixinNodes?.isValid) return;
|
|
|
|
const hpNum = gg.game.CurentBattle.loveHpNum;
|
|
const idx = this.aixinIndex;
|
|
if (idx >= hpNum) {
|
|
gg.game.CurentBattle.failGame();
|
|
return;
|
|
}
|
|
|
|
const node = this.ui_aixinNodes.children[idx];
|
|
if (node) {
|
|
this.aixinIndex = idx + 1;
|
|
const aixin_liang = node.getChildByName('aixin_liang');
|
|
tween(aixin_liang)
|
|
.to(1, { y: - 30 , opacity: 0})
|
|
.call(() => {
|
|
aixin_liang.active = false;
|
|
gg.game.CurentBattle.failGame();
|
|
})
|
|
.start();
|
|
} else {
|
|
if(this.aixinIndex >= gg.game.CurentBattle.loveHpNum){
|
|
gg.game.CurentBattle.failGame();
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|