消消方块阵换皮表情
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.
 
 
 

1283 lines
52 KiB

import { _decorator, Collider2D, Component, ERigidBody2DType, EventTouch, Graphics, instantiate, Intersection2D, Node, PolygonCollider2D, RigidBody2D, tween, Tween, UIOpacity, UITransform, Vec2, Vec3, view, Color, Rect, Label, Sprite } from 'cc';
import { tetriMap } from '../tetriMap/tetriMap';
import { GEvent } from '../../mx/module/event/GEvent';
import { TetriType } from '../game/ConfigProjectData';
import { tetriNode } from './tetriNode';
import { tetriWeaponCarrier } from './tetriWeaponCarrier';
import { tetriAbility } from './tetriAbility';
import { tetriFloorNode } from './tetriFloorNode';
import { UIID } from '../game/ConfigRes';
import { StatusType } from '../game/GameData';
import MTools from '../../mx/tools/MTools';
import { BattleType } from '../manager/ChapterDataManager';
import { playTetriBlockSpine, TetriBlockSpineState } from './tetriBlockSpine';
const { ccclass, property } = _decorator;
/***方块摆放的item节点, 放在tetraGame上的ui_card的子节点上,会自动根据方块的类型创建对应的方块节点
* 可以进行触摸移动
*
* 点击item节点0秒后item上创建的方块的父节点到_gameRoot的ui_card的子节点上,位置更新一下然后设置item节点opacity==0保证触摸事件继续
* 根据类型不同,所以方块有两个脚本
* tetriCardItem: 自由下落的方块节点
* tetraFloorNode: 无重力方块,墙体。接触到其他方块会立马停住做为底板使用
* 方块会绑定武器和技能根据武器不同,调度进行发射子弹
* TetriType == 3||TetriType == 4 添加脚本tetriAbility脚本 自带脚本tetriNode
* TetriType == 1 || TetriType == 5 添加脚本tetriWeaponCarrier脚本 自带脚本tetriNode
* TetriType == 2 自带脚本tetriFloorNode
*/
@ccclass('tetriCardItem')
export class tetriCardItem extends Component {
/**本卡牌对应的方块配置(BattleCube 表) */
public config: ITableBattleCube | null = null;
public setConfig(cfg: ITableBattleCube | null) {
this.config = cfg;
this.refreshCoinNum()
}
wenhaoBtnNode: Node | null = null;
tetriNameLabel: Label | null = null;
/**放置区域下边界节点(默认从 CurentBattle.TetraMap 获取,也可外部注入) */
highLine: Node | null = null;
chooseMask: Node | null = null;
liangkuangNode: Node | null = null;
/**拖到该节点上松手则取消(chooseMask 的子节点:cancleNode) */
cancleNode: Node | null = null;
/**真实下落/稳定的方块父节点(地图 GameRoot) */
physicsRoot: Node | null = null;
/**绑定地图脚本(用于读取镜头缩放,做 x 坐标换算) */
private _boundMap: tetriMap | null = null;
/**外部回调:松手且满足放置条件时触发 */
onPlaceRequested: ((self: tetriCardItem, worldPos: Vec3) => void) | null = null;
/**外部回调:卡牌被成功消耗(放置成功) */
onConsumed: (() => void) | null = null;
// 原始状态(用于回弹复位)
private _originPos: Vec3 = new Vec3();
private _originSiblingIdx: number = 0;
// 方块子节点及其原始本地坐标(用于预览创建/销毁)
private _pieceNode: Node | null = null;
/***预览二号 */
private _pieceNodeCopyMove: Node | null = null;
private _pieceNodeCopyMoveFixedWorldY: number | null = null;
private _pieceOriginLocalPos: Vec3 = new Vec3();
/** 拿起预览时 schedule 的「下一帧关物理」令牌;放置后递增以作废,避免松手后把下落块又设回 Static */
private _previewPhysicsDisableToken = 0;
private _holdScheduled: boolean = false;
private _lastTouchEvent: EventTouch | null = null;
private _startPos: Vec2 = new Vec2();
private _startTime: number = 0;
private static readonly HOLD_SECONDS = 0;
private _isDragging: boolean = false;
private _snapTween: Tween<Node> | null = null;
private _previewNode: Node | null = null;
/** 预览二号:仅视觉对齐,跟手点上方偏移;不参与物理/掉落/角色武器 */
private _previewAlignNode: Node | null = null;
/** 拿起方块时记录的 scale,拖拽期内乘镜头缩放;交还卡牌/下落前恢复 */
private _previewDragBaseScale: Vec3 | null = null;
private _guideLine: Node | null = null;
private _guideLineParent: Node | null = null;
private _isOverCancel: boolean = false;
private _cancelNodeBaseScale: Vec3 | null = null;
/**钳制安全边距(像素),避免边缘出现少量溢出;可在编辑器里调 */
@property({ tooltip: '拖拽钳制的安全边距(px),越大越不容易“露出一点点”' })
public clampPaddingPx: number = 8;
/**
* 预览二号(对齐用):相对手指触点沿 UI 坐标 Y 轴向上偏移。
* 需求约 30cm 时请在编辑器按设计分辨率/DPI 换算为像素后填写。
*/
@property({ tooltip: '预览二号:手指触点正上方偏移(UI 像素,与 getUILocation 同坐标系)' })
public alignPreviewOffsetAboveFingerPx: number = 400;
public enableAlignPreview: boolean = false;
// 位置钳制缓存(mask 本地坐标系):AABB 相对 previewRoot.position 的偏移量
private _clampRelAABB: { minX: number; minY: number; maxX: number; maxY: number } | null = null;
// 引导区域边界缓存(基于 tetriMap 里的 GameRoot 子节点 UITransform 计算)
private _chooseMaskTopY: number = 0;
/**银币是否够放置方块 */
_affordable: boolean = true;
/** world -> local */
private _worldToLocal(parent: Node, world: Vec3): Vec3 {
const ut = parent.getComponent(UITransform);
if (!ut) return world.clone();
const local = new Vec3();
ut.convertToNodeSpaceAR(world, local);
return local;
}
private _setLayerRecursively(node: Node, layer: number) {
node.layer = layer;
for (let i = 0; i < node.children.length; i++) {
this._setLayerRecursively(node.children[i], layer);
}
}
/** 预览态彻底退出物理世界:禁用刚体与全部碰撞体 */
private _disablePhysicsForPreview(node: Node | null): void {
if (!node?.isValid) return;
// 已交给战场下落(挂在 GameRoot / Tetris 地图下)时禁止再关物理
if (this._isNodeReleasedToBattlefield(node)) return;
for (const rb of node.getComponentsInChildren(RigidBody2D)) {
rb.linearVelocity = new Vec2(0, 0);
rb.angularVelocity = 0;
rb.gravityScale = 0;
rb.type = ERigidBody2DType.Static;
rb.fixedRotation = true;
rb.enabledContactListener = false;
rb.enabled = false;
}
for (const col of node.getComponentsInChildren(Collider2D)) {
col.enabled = false;
}
}
/** 某些脚本会在 next tick 反开 collider,这里做一帧后的兜底再禁用 */
private _disablePhysicsForPreviewNextFrame(node: Node | null): void {
const token = this._previewPhysicsDisableToken;
this.scheduleOnce(() => {
if (token !== this._previewPhysicsDisableToken) return;
this._disablePhysicsForPreview(node);
}, 0);
}
/** 放置瞬间作废所有「预览关物理」延迟任务 */
private _invalidatePreviewPhysicsDisable(): void {
this._previewPhysicsDisableToken++;
}
/** 节点是否已移交战场(不再是 chooseMask 上的拖拽预览) */
private _isNodeReleasedToBattlefield(node: Node | null): boolean {
if (!node?.isValid) return false;
const map = this._boundMap ?? (gg as any)?.game?.CurentBattle?.TetraMap;
const gameRoot = map?._gameRoot as Node | null | undefined;
if (gameRoot?.isValid) {
let cur: Node | null = node;
while (cur) {
if (cur === gameRoot) return true;
cur = cur.parent;
}
}
// 已不在卡牌/遮罩下,也视为已释放
if (this.chooseMask?.isValid) {
let cur: Node | null = node;
while (cur) {
if (cur === this.chooseMask || cur === this.node) return false;
cur = cur.parent;
}
return true;
}
return false;
}
/** 递归销毁名为「角色」的子树(预览二号禁止挂角色/武器) */
private _destroyRoleNamedSubtrees(node: Node | null): void {
if (!node?.isValid) return;
const kids = node.children.slice();
for (const ch of kids) {
if (ch.name === '角色') {
ch.destroy();
} else {
this._destroyRoleNamedSubtrees(ch);
}
}
}
/**
* 预览二号:在进入场景树之前剥掉玩法脚本,避免 start/onEnable 里创建角色、武器或注册战斗逻辑。
*/
private _stripAlignPreviewGameplay(root: Node | null): void {
if (!root?.isValid) return;
for (const c of root.getComponentsInChildren(tetriAbility)) {
c.destroy();
}
for (const c of root.getComponentsInChildren(tetriWeaponCarrier)) {
c.destroy();
}
this._destroyRoleNamedSubtrees(root);
for (const c of root.getComponentsInChildren(tetriNode)) {
c.destroy();
}
for (const c of root.getComponentsInChildren(tetriFloorNode)) {
c.destroy();
}
}
private _destroyAlignPreview(): void {
if (this._previewAlignNode?.isValid) {
this._previewAlignNode.destroy();
}
this._previewAlignNode = null;
}
private _setCardVisible(visible: boolean): void {
const op = this.node.getComponent(UIOpacity) ?? this.node.addComponent(UIOpacity);
op.opacity = visible ? 255 : 0;
}
/**外部可拿当前预览节点(用于放置成功时接管) */
public get previewNode(): Node | null { return this._previewNode; }
public setChooseMask(mask: Node | null) {
this.chooseMask = mask;
}
public setCancleNode(node: Node | null) {
this.cancleNode = node;
this._cancelNodeBaseScale = node ? node.scale.clone() : null;
}
/**允许外部注入 map(推荐在发牌后立刻调用一次) */
public bindMap(map: tetriMap | null) {
if (!map) return;
this._boundMap = map;
this.physicsRoot = map._gameRoot ?? null;
this.highLine = map._highLine ?? null;
}
/**
* 拖拽主预览在 UI 坐标下跟手;白线副本/最终下落在游戏相机下渲染。
* 镜头拉远后,两者 x 需要按 zoom 做换算,否则会出现“偏离引导线”。
*/
private _toGameWorldXFromUiWorldX(uiWorldX: number): number {
const map = this._boundMap;
if (map?.uiWorldXToGameWorldX) {
return map.uiWorldXToGameWorldX(uiWorldX);
}
const zoom = map?.getVisualZoomScale?.() ?? 1;
if (!Number.isFinite(zoom) || zoom <= 0 || Math.abs(zoom - 1) < 0.0001) return uiWorldX;
const camX = (globalThis as any)?.gg?.ui?.GameCamera?.node?.worldPosition.x ?? 0;
return camX + (uiWorldX - camX) / zoom;
}
private _toGameWorldYFromUiWorldY(uiWorldY: number): number {
const map = this._boundMap;
if (map?.uiWorldYToGameWorldY) {
return map.uiWorldYToGameWorldY(uiWorldY);
}
const zoom = map?.getVisualZoomScale?.() ?? 1;
if (!Number.isFinite(zoom) || zoom <= 0 || Math.abs(zoom - 1) < 0.0001) return uiWorldY;
const camY = (globalThis as any)?.gg?.ui?.GameCamera?.node?.worldPosition.y ?? 0;
return camY + (uiWorldY - camY) / zoom;
}
/**外部显式指定“卡牌里哪一个节点是方块”(强烈推荐在发牌时调用) */
public setPieceNode(node: Node | null) {
this._pieceNode = node;
if (this._pieceNode) {
this._pieceOriginLocalPos = this._pieceNode.position.clone();
}
}
protected onLoad(): void {
this._originPos = this.node.position.clone();
this._originSiblingIdx = this.node.getSiblingIndex();
this.liangkuangNode = this.node.getChildByName('亮框');
this.wenhaoBtnNode = this.node.getChildByName('btnwenhao');
this.tetriNameLabel = this.node.getChildByName('tetriName').getComponent(Label);
// 兜底:如果外部没调用 setPieceNode,则尝试自动抓取
if (!this._pieceNode) {
this._pieceNode = this.node.getChildByName('Tetris') ?? this.node.children[0] ?? null;
if (this._pieceNode) {
this._pieceOriginLocalPos = this._pieceNode.position.clone();
}
}
// 如果没有外部注入,尝试从当前战斗里取 tetriMap 绑定
try {
const ggAny = (globalThis as any).gg;
const map = ggAny?.game?.CurentBattle?.TetraMap as tetriMap | null;
if (map) this.bindMap(map);
} catch { }
this.node.on(Node.EventType.TOUCH_START, this._onTouchStart, this);
this.node.on(Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
this.node.on(Node.EventType.TOUCH_END, this._onTouchEnd, this);
this.node.on(Node.EventType.TOUCH_CANCEL, this._onTouchEnd, this);
GEvent.Ins.on(GEvent.showCoinFly, this.refreshCoinNum2, this);
GEvent.Ins.on(GEvent.UpdateGameCoinNum, this.refreshCoinNum, this);
GEvent.Ins.on(GEvent.UpdateCoin, this.refreshCoinNum, this);
this.refreshCoinNum()
}
protected onDestroy(): void {
this.node.off(Node.EventType.TOUCH_START, this._onTouchStart, this);
this.node.off(Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
this.node.off(Node.EventType.TOUCH_END, this._onTouchEnd, this);
this.node.off(Node.EventType.TOUCH_CANCEL, this._onTouchEnd, this);
GEvent.Ins.off(GEvent.showCoinFly, this.refreshCoinNum2, this);
GEvent.Ins.off(GEvent.UpdateGameCoinNum, this.refreshCoinNum, this);
GEvent.Ins.off(GEvent.UpdateCoin, this.refreshCoinNum, this);
this._destroyAlignPreview();
}
private _onTouchStart(event: EventTouch): void {
// 地图异步晚到时补绑,避免 highLine/layer 为空导致无 copy、无法掉落
if (!this._boundMap || !this.highLine?.isValid) {
try {
const map = (globalThis as any).gg?.game?.CurentBattle?.TetraMap as tetriMap | null;
if (map) this.bindMap(map);
} catch { }
}
if (!this._affordable) {
//
if (!MTools.beforeTimes(1000, "goldNotEnoughTimeStamp")) {
gg.ui.showToast(gg.lang.get('银币不足'))
}
return
}
//触摸穿透 为了maplayer的可以监听到点击事件给下落的方块左右移动
//如果是使用锤子阶段就触摸穿透
if (gg.game.CurentBattle.IsUseShovel) {
event.preventSwallow = true;
event.propagationStopped = true;
}
this._snapTween?.stop();
this._snapTween = null;
this._isDragging = true;
this._startPos = event.getUILocation();
this._startTime = Date.now();
this._lastTouchEvent = event;
this.scheduleOnce(() => {
if (this._previewNode) {
this._syncPreviewToTouch(event);
}
}, gg.game.CurentBattle.cancelTimeMs / 1000);
// 置顶,避免被其他 UI 遮挡
if (this.node.parent) {
this.node.setSiblingIndex(this.node.parent.children.length - 1);
}
if(gg.data.getStatus(StatusType.IsShowPlayGuidePop) == 0){
//打点
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${gg.game.CurentBattle.CurentWaveNum}-首次拖动方块1`);
}
// 按住 0.5s 后才真正“拿起方块”:卡牌透明 + 方块移到 GameRoot
if (!this._holdScheduled) {
this._holdScheduled = true;
this.scheduleOnce(() => {
this._holdScheduled = false;
if (!this._isDragging) return;
if (this._previewNode) return;
this._createPreview();
if (!this._previewNode) return;
this._setCardVisible(false);
if (this._lastTouchEvent) this._syncPreviewToTouch(this._lastTouchEvent);
if(gg.data.doc.chapter == 1){
GEvent.Ins.emit(GEvent.TetriGameTouchStart);
}
if(gg.data.getStatus(StatusType.IsShowPlayGuidePop) == 0){
//打点
gg.sdk.reportDY("inLevel", `章节${gg.game.CurentBattle.getReportDYChapterId()}_${gg.game.CurentBattle.CurentWaveNum}-首次拖动方块2`);
}
}, tetriCardItem.HOLD_SECONDS);
}
}
//移动添加区域保护不能超过chooseMask包围的区域
private _onTouchMove(event: EventTouch): void {
if (!this._isDragging) return;
this._lastTouchEvent = event;
if (this._previewNode) {
this._syncPreviewToTouch(event);
}
}
private _onTouchEnd(event: EventTouch): void {
if (gg.game.CurentBattle.IsUseShovel) {
event.preventSwallow = true;
event.propagationStopped = true;
}
if (!this._isDragging) return;
this._isDragging = false;
this._lastTouchEvent = null;
// 如果还没到“按住 0.5s 拿起”的时机,直接结束(不消耗、不改变卡牌)
if (!this._previewNode) {
this._setCardVisible(true);
this.snapBack();
return;
}
// 拖到取消区松手:直接取消
if (this._isOverCancel) {
this.cancelPlace();
return;
}
// 新需求:任何地方松手都放置;默认 x 取当前位置,y 固定为白线处开始掉落;isShowBlockDrop 时在手指释放处落点并做 Polygon 重叠校验
const pos = this._getDropStartWorldPos();
if (!pos) {
this.cancelPlace();
return;
}
if (gg.game.isShowBlockDrop && this._previewPolygonsOverlapGameplayBlocksAt(pos)) {
this.cancelPlace();
return;
}
if (this.onPlaceRequested) {
// 交给战场前恢复预制 scale,避免下落块带拖拽期的镜头缩放系数
this._restorePreviewPieceBaseScale();
// 作废「下一帧关物理」:HOLD_SECONDS=0 时 createPreview 与松手常隔 1 帧,否则会把已下落块又冻成 Static 悬在白线
this._invalidatePreviewPhysicsDisable();
//注册的回调
this.onPlaceRequested(this, pos);
if (this._pieceNodeCopyMove && this._pieceNodeCopyMove.isValid) {
this._pieceNodeCopyMove.destroy();
console.log('回收拖拽预览副 this._pieceNodeCopyMove333');
//gg.res.putNode(this._pieceNodeCopyMove);
}
this._pieceNodeCopyMove = null;
this._pieceNodeCopyMoveFixedWorldY = null;
this._destroyAlignPreview();
if(gg.data.doc.chapter == 1 ){
GEvent.Ins.emit(GEvent.TetriGameTouchEnd);
}
//扣钱(雷电副本不消耗银币)
if (gg.game.CurentBattle.BattleType !== BattleType.OrangeWeaponMode_Dian) {
gg.game.CurentBattle.subCoinNum(this.config.price);
}
} else {
this.consume();
}
}
/**放置成功:预览所有权移交游戏逻辑,卡牌隐藏 */
public consume(): void {
this._snapTween?.stop();
this._destroyGuideLine();
this._destroyAlignPreview();
this._restorePreviewPieceBaseScale();
this._previewDragBaseScale = null;
this._previewNode = null;
this.node.active = false;
this.onConsumed?.();
if(gg.data.doc.chapter == 1){
GEvent.Ins.emit(GEvent.TetriGameTouchEnd);
}
}
/**放置失败:销毁预览,卡牌回弹 */
public cancelPlace(): void {
this._destroyPreview();
this._setCardVisible(true);
this.snapBack();
if(gg.data.doc.chapter == 1){
GEvent.Ins.emit(GEvent.TetriGameTouchEnd);
}
}
/**弹回原始位置 */
public snapBack(): void {
if (this.node.parent) {
this.node.setSiblingIndex(this._originSiblingIdx);
}
this._setCardVisible(true);
this._snapTween = tween(this.node)
.to(0.3, { position: this._originPos }, { easing: 'backOut' })
.start();
if(gg.data.doc.chapter == 1){
GEvent.Ins.emit(GEvent.TetriGameTouchEnd);
}
}
private _isAboveLine(_event: EventTouch): boolean {
if (!this.highLine || !this._previewNode) return false;
const ut = this._previewNode.getComponent(UITransform);
const halfH = ut ? ut.getBoundingBoxToWorld().height / 2 : 0;
const bottomY = this._previewNode.worldPosition.y - halfH;
return bottomY > this.highLine.worldPosition.y;
}
/** 预览二号:底边高于高度线(白线)时隐藏,移回线下方再显示 */
private _syncAlignPreviewVisibilityByHighLine(): void {
if (!this.enableAlignPreview) return;
if (!this._previewAlignNode?.isValid) return;
if (!this.highLine?.isValid) {
this._previewAlignNode.active = true;
return;
}
const ut = this._previewAlignNode.getComponent(UITransform);
const halfH = ut ? ut.getBoundingBoxToWorld().height / 2 : 0;
const bottomY = this._previewAlignNode.worldPosition.y - halfH;
const overHighLine = bottomY > this.highLine.worldPosition.y;
this._previewAlignNode.active = !overHighLine;
}
private _isOverCancleNode(event: EventTouch): boolean {
if (!this.cancleNode) return false;
const ut = this.cancleNode.getComponent(UITransform);
if (!ut) return false;
const rect = ut.getBoundingBoxToWorld();
const p = event.getUILocation();
// 仅在 cancleNode 的矩形区域内才视为“取消”
return p.x >= rect.xMin && p.x <= rect.xMax && p.y >= rect.yMin && p.y <= rect.yMax;
}
private _getDropStartWorldPos(): Vec3 | null {
if (!this._previewNode) return null;
const footprintNode =
this._pieceNodeCopyMove?.isValid ? this._pieceNodeCopyMove : this._previewNode;
const centerGameX = this._pieceNodeCopyMove?.isValid
? this._pieceNodeCopyMove.worldPosition.x
: this._toGameWorldXFromUiWorldX(this._previewNode.worldPosition.x);
let x = centerGameX;
if (footprintNode?.isValid && this._boundMap?.snapPlaceCenterWorldX) {
x = this._boundMap.snapPlaceCenterWorldX(centerGameX, footprintNode);
}
const z = this._previewNode.worldPosition.z;
if (gg.game.isShowBlockDrop) {
const y = this._toGameWorldYFromUiWorldY(this._previewNode.worldPosition.y);
return new Vec3(x, y, z);
}
if (!this.highLine) return null;
const ut = this._previewNode.getComponent(UITransform);
const halfH = ut ? ut.getBoundingBoxToWorld().height / 2 : 0;
// 下落起点固定为白线处(底边贴线)。即使拖拽到很高也只影响 X,不影响下落起点 Y。
const minCenterY = this.highLine.worldPosition.y + halfH;
return new Vec3(x, minCenterY, z);
}
/**白线预览块固定 Y:白线 worldY + 方块半高(让底边贴线) */
private _getWhiteLineFixedWorldYFor(node: Node): number | null {
if (!this.highLine) return null;
const rect = this._getWorldRect(node);
const halfH = rect ? rect.height / 2 : 0;
return this.highLine.worldPosition.y + halfH;
}
/** PolygonCollider2D 顶点变换到世界 XY(与 enabled 无关,用于精确重叠判定) */
private _getPolygonColliderWorldPoints2D(poly: PolygonCollider2D): Vec2[] {
const owner = poly.node;
const m = owner.getWorldMatrix();
const offset = (poly as any).offset as Vec2 | undefined;
const offX = offset?.x ?? 0;
const offY = offset?.y ?? 0;
const pts = poly.points ?? [];
const wv = new Vec3();
const lv = new Vec3();
const out: Vec2[] = [];
for (const p of pts) {
lv.set(p.x + offX, p.y + offY, 0);
Vec3.transformMat4(wv, lv, m);
out.push(new Vec2(wv.x, wv.y));
}
return out;
}
private _polygonHasEnoughVerts(pts: readonly Vec2[]): boolean {
return (pts?.length ?? 0) >= 3;
}
/** 节点是否属于场上玩法方块(tetriNode / tetriFloorNode 子树) */
private _isGameplayBlockColliderNode(n: Node | null): boolean {
let cur: Node | null = n;
while (cur) {
if (cur.getComponent(tetriNode) || cur.getComponent(tetriFloorNode)) return true;
cur = cur.parent;
}
return false;
}
/**
* 在指定世界落点下,预览块 Polygon 是否与场上已存在方块的启用 PolygonCollider2D 相交。
* 预览仍在 chooseMask 下,通过平移当前世界多边形到落点做判定。
*/
private _previewPolygonsOverlapGameplayBlocksAt(dropWorldPos: Vec3): boolean {
if (!this._previewNode?.isValid) return true;
const gameRoot = this._boundMap?._gameRoot;
if (!gameRoot?.isValid) return false;
const root = this._previewNode;
const dx = dropWorldPos.x - root.worldPosition.x;
const dy = dropWorldPos.y - root.worldPosition.y;
const previewPolys = root.getComponentsInChildren(PolygonCollider2D) ?? [];
const shiftedPreview: Vec2[][] = [];
for (const pp of previewPolys) {
const w = this._getPolygonColliderWorldPoints2D(pp);
if (!this._polygonHasEnoughVerts(w)) continue;
const shifted: Vec2[] = [];
for (const p of w) {
shifted.push(new Vec2(p.x + dx, p.y + dy));
}
shiftedPreview.push(shifted);
}
if (shiftedPreview.length === 0) return false;
const others = gameRoot.getComponentsInChildren(PolygonCollider2D) ?? [];
for (const op of others) {
if (!op?.node?.isValid || !op.enabled) continue;
if (!this._isGameplayBlockColliderNode(op.node)) continue;
const ow = this._getPolygonColliderWorldPoints2D(op);
if (!this._polygonHasEnoughVerts(ow)) continue;
for (const sp of shiftedPreview) {
if (Intersection2D.polygonPolygon(sp, ow)) {
return true;
}
}
}
return false;
}
/**创建预览 */
private _createPreview(): void {
if (!this._pieceNode || !this.chooseMask) return;
// 将方块子节点移入 chooseMask,成为独立预览(最上层)
this.chooseMask.active = true
this.cancleNode.active = true
this.chooseMask.addChild(this._pieceNode);
// 确保挪到 GameRoot 后仍会被正确相机渲染(layer 必须与 GameRoot 一致)
this._setLayerRecursively(this._pieceNode, this.chooseMask.layer);
this._pieceNode.active = true;
// 预览阶段:彻底禁用物理,防止任何接触/推挤
this._disablePhysicsForPreview(this._pieceNode);
this._disablePhysicsForPreviewNextFrame(this._pieceNode);
this._previewDragBaseScale = this._pieceNode.scale.clone();
//this.config.cubename 在创建一个方块节点,位置放在当前方块的白线正上方,保持一致,用于拖动预览,禁用碰撞器,禁用重力,禁用刚体
//赋值到this._pieceNodeCopyMove上,用于拖动预览结束的时候回收,原来的逻辑保留
// 额外的“拖拽预览副本”(不影响原有 _pieceNode 逻辑);isShowBlockDrop 时不在白线展示副本,落点跟手
if (this._pieceNodeCopyMove && this._pieceNodeCopyMove.isValid) {
console.log('回收拖拽预览副 this._pieceNodeCopyMove1111');
//gg.res.putNode(this._pieceNodeCopyMove);
this._pieceNodeCopyMove.destroy();
this._pieceNodeCopyMove = null;
}
if (!gg.game.isShowBlockDrop) {
this._pieceNodeCopyMove = instantiate(this._pieceNode);
if (this._pieceNode.getComponent(tetriNode)) {
this._pieceNodeCopyMove.getOrAddComponent(tetriNode).setConfig(this.config);
}
if (this._pieceNode.getComponent(tetriWeaponCarrier)) {
this._pieceNodeCopyMove.getOrAddComponent(tetriWeaponCarrier).setConfig(this.config);
}
//this._pieceNodeCopyMove.scale = this._pieceNode.scale
this._pieceNodeCopyMove.angle = 0
this.chooseMask.addChild(this._pieceNodeCopyMove);
//this._pieceNodeCopyMove.scale = this._pieceNode.scale.clone();
const mapLayer = gg.game.CurentBattle.TetraMap?.node?.layer ?? this.chooseMask.layer;
this._setLayerRecursively(this._pieceNodeCopyMove, mapLayer);
this._pieceNodeCopyMove.active = true;
// 固定在白线处(底边贴线),X 跟随主方块
const fixedY = this._getWhiteLineFixedWorldYFor(this._pieceNodeCopyMove);
const startX = this._pieceNode.worldPosition.x;
const startZ = this._pieceNodeCopyMove.worldPosition.z;
this._pieceNodeCopyMoveFixedWorldY = fixedY ?? this._pieceNode.worldPosition.y;
this._pieceNodeCopyMove.setWorldPosition(new Vec3(startX, this._pieceNodeCopyMoveFixedWorldY, startZ));
// 白线副本同样彻底禁用物理,保证仅作为视觉引导
this._disablePhysicsForPreview(this._pieceNodeCopyMove);
this._disablePhysicsForPreviewNextFrame(this._pieceNodeCopyMove);
} else {
this._pieceNodeCopyMove = null;
this._pieceNodeCopyMoveFixedWorldY = null;
}
this._previewNode = this._pieceNode;
this._createAlignPreview();
this._playPreviewBlockSpine(TetriBlockSpineState.Falling);
// 主预览叠在最上层,避免被预览二号挡住
if (this._pieceNode?.isValid && this._pieceNode.parent === this.chooseMask) {
this._pieceNode.setSiblingIndex(this.chooseMask.children.length - 1);
}
this._createGuideLine();
}
/** 预览二号:与主预览同形,位置为手指触点正上方(可配置像素偏移),仅对齐用 */
private _createAlignPreview(): void {
this._destroyAlignPreview();
if (!this.enableAlignPreview) return;
if (!this._pieceNode?.isValid || !this.chooseMask) return;
const n = instantiate(this._pieceNode);
this._stripAlignPreviewGameplay(n);
this.chooseMask.addChild(n);
this._setLayerRecursively(n, this.chooseMask.layer);
n.active = true;
n.angle = this._pieceNode.angle;
this._disablePhysicsForPreview(n);
this._disablePhysicsForPreviewNextFrame(n);
this._previewAlignNode = n;
}
/** 堆叠升高镜头拉远时与 tetriMap 一致,主预览与预览二号同比例缩小,贴近场上/白线副本观感 */
private _syncPreviewDragScaleForMapZoom(): void {
if (!this._previewDragBaseScale || !this._pieceNode?.isValid) return;
const z = this._boundMap?.getVisualZoomScale?.() ?? 1;
const b = this._previewDragBaseScale;
const sx = b.x * z;
const sy = b.y * z;
const sz = b.z * z;
this._pieceNode.setScale(sx, sy, sz);
if (this._previewAlignNode?.isValid) {
this._previewAlignNode.setScale(sx, sy, sz);
}
}
private _restorePreviewPieceBaseScale(): void {
if (this._pieceNode?.isValid && this._previewDragBaseScale) {
const b = this._previewDragBaseScale;
this._pieceNode.setScale(b.x, b.y, b.z);
}
}
private _destroyPreview(): void {
this._invalidatePreviewPhysicsDisable();
this._destroyGuideLine();
this._destroyAlignPreview();
if (this._pieceNodeCopyMove && this._pieceNodeCopyMove.isValid) {
this._pieceNodeCopyMove.destroy();
console.log('回收拖拽预览副 this._pieceNodeCopyMove1111');
//gg.res.putNode(this._pieceNodeCopyMove);
}
this._pieceNodeCopyMove = null;
this._pieceNodeCopyMoveFixedWorldY = null;
if (this._pieceNode && this._previewNode === this._pieceNode) {
this._restorePreviewPieceBaseScale();
// 将方块归还给卡牌
this.node.addChild(this._pieceNode);
this._pieceNode.setPosition(this._pieceOriginLocalPos);
playTetriBlockSpine(this._pieceNode, TetriBlockSpineState.Shop);
}
this._previewNode = null;
this._previewDragBaseScale = null;
this.chooseMask.active = false;
this.cancleNode.active = false
this._restoreCancelNodeScale();
this._clampRelAABB = null;
}
/**拖入取消区时做一次轻微放大反馈 */
private _playCancelHoverFeedback(): void {
if (!this.cancleNode?.isValid) return;
if (!this._cancelNodeBaseScale) {
this._cancelNodeBaseScale = this.cancleNode.scale.clone();
}
const base = this._cancelNodeBaseScale;
const bump = new Vec3(base.x * 1.08, base.y * 1.08, base.z);
Tween.stopAllByTarget(this.cancleNode);
this.cancleNode.setScale(base);
tween(this.cancleNode)
.to(0.08, { scale: bump }, { easing: 'quadOut' })
.to(0.12, { scale: base }, { easing: 'quadIn' })
.start();
}
private _restoreCancelNodeScale(): void {
if (!this.cancleNode?.isValid) return;
if (!this._cancelNodeBaseScale) return;
Tween.stopAllByTarget(this.cancleNode);
this.cancleNode.setScale(this._cancelNodeBaseScale);
}
/**获取节点(含子节点)在世界坐标的合并包围盒(基于 UITransform) */
private _getWorldRectByUI(node: Node): Rect | null {
const uts = node.getComponentsInChildren(UITransform);
let out: Rect | null = null;
for (const ut of uts) {
const r = ut.getBoundingBoxToWorld();
if (!out) {
out = new Rect(r.x, r.y, r.width, r.height);
} else {
const xMin = Math.min(out.xMin, r.xMin);
const yMin = Math.min(out.yMin, r.yMin);
const xMax = Math.max(out.xMax, r.xMax);
const yMax = Math.max(out.yMax, r.yMax);
out.x = xMin;
out.y = yMin;
out.width = xMax - xMin;
out.height = yMax - yMin;
}
}
return out;
}
/**获取节点(含子节点)在世界坐标的合并包围盒(优先 UITransform,否则用 2D 物理 Collider) */
private _getWorldRect(node: Node): Rect | null {
const uiRect = this._getWorldRectByUI(node);
if (uiRect) return uiRect;
// 预览阶段我们会禁用 PolygonCollider2D,导致 worldAABB 可能不可用。
// 这里用 collider 的点位手算 AABB(即使 collider disabled 也能取到 points)。
const polyRect = this._getWorldRectByPolygonCollider(node);
if (polyRect) return polyRect;
const cols = node.getComponentsInChildren(Collider2D);
let out: Rect | null = null;
for (const col of cols) {
const r: any = (col as any).worldAABB;
if (!r) continue;
// worldAABB 是 math.Rect(字段是 x/y/width/height + xMin/xMax/yMin/yMax getter)
if (!out) {
out = new Rect(r.x, r.y, r.width, r.height);
} else {
const xMin = Math.min(out.xMin, r.xMin);
const yMin = Math.min(out.yMin, r.yMin);
const xMax = Math.max(out.xMax, r.xMax);
const yMax = Math.max(out.yMax, r.yMax);
out.x = xMin;
out.y = yMin;
out.width = xMax - xMin;
out.height = yMax - yMin;
}
}
return out;
}
/**基于 PolygonCollider2D.points 手算世界 AABB(忽略 enabled 状态) */
private _getWorldRectByPolygonCollider(node: Node): Rect | null {
const polys = node.getComponentsInChildren(PolygonCollider2D);
if (!polys?.length) return null;
let minX = Number.POSITIVE_INFINITY;
let minY = Number.POSITIVE_INFINITY;
let maxX = Number.NEGATIVE_INFINITY;
let maxY = Number.NEGATIVE_INFINITY;
const wv = new Vec3();
const lv = new Vec3();
for (const poly of polys) {
const owner = poly.node;
const m = owner.getWorldMatrix();
const offset = (poly as any).offset as Vec2 | undefined;
const offX = offset?.x ?? 0;
const offY = offset?.y ?? 0;
const pts = poly.points ?? [];
for (const p of pts) {
lv.set(p.x + offX, p.y + offY, 0);
Vec3.transformMat4(wv, lv, m);
if (wv.x < minX) minX = wv.x;
if (wv.y < minY) minY = wv.y;
if (wv.x > maxX) maxX = wv.x;
if (wv.y > maxY) maxY = wv.y;
}
}
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {
return null;
}
return new Rect(minX, minY, maxX - minX, maxY - minY);
}
/**按 PolygonCollider2D 点位计算“横向占用格子列数”(不使用包围盒 API) */
private _getGridColumnCountByPolygon(node: Node, cellWorldWidth: number): number {
const polys = node.getComponentsInChildren(PolygonCollider2D);
if (!polys?.length) return 1;
const cell = Math.max(1, cellWorldWidth);
let minX = Number.POSITIVE_INFINITY;
let maxX = Number.NEGATIVE_INFINITY;
const wv = new Vec3();
const lv = new Vec3();
for (const poly of polys) {
const owner = poly.node;
const m = owner.getWorldMatrix();
const offset = (poly as any).offset as Vec2 | undefined;
const offX = offset?.x ?? 0;
const offY = offset?.y ?? 0;
const pts = poly.points ?? [];
for (const p of pts) {
lv.set(p.x + offX, p.y + offY, 0);
Vec3.transformMat4(wv, lv, m);
if (wv.x < minX) minX = wv.x;
if (wv.x > maxX) maxX = wv.x;
}
}
if (!Number.isFinite(minX) || !Number.isFinite(maxX)) return 1;
const cols = Math.round((maxX - minX) / cell);
return Math.max(1, cols);
}
/**把“预览整体大小”合并成 mask 本地 AABB(UITransform + PolygonCollider2D) */
private _getPreviewLocalAABBInMask(maskUT: UITransform, previewRoot: Node): { minX: number; minY: number; maxX: number; maxY: number } | null {
let minX = Number.POSITIVE_INFINITY;
let minY = Number.POSITIVE_INFINITY;
let maxX = Number.NEGATIVE_INFINITY;
let maxY = Number.NEGATIVE_INFINITY;
const tmp = new Vec3();
const toLocal = (w: Vec3) => {
maskUT.convertToNodeSpaceAR(w, tmp);
if (tmp.x < minX) minX = tmp.x;
if (tmp.y < minY) minY = tmp.y;
if (tmp.x > maxX) maxX = tmp.x;
if (tmp.y > maxY) maxY = tmp.y;
};
// 1) UITransform 的 worldRect 四角投影到 mask 本地
const uts = previewRoot.getComponentsInChildren(UITransform);
for (const ut of uts) {
const r = ut.getBoundingBoxToWorld();
toLocal(new Vec3(r.xMin, r.yMin, 0));
toLocal(new Vec3(r.xMin, r.yMax, 0));
toLocal(new Vec3(r.xMax, r.yMin, 0));
toLocal(new Vec3(r.xMax, r.yMax, 0));
}
// 2) PolygonCollider2D 的点(即使 disabled 也能取 points)投影到 mask 本地
const polys = previewRoot.getComponentsInChildren(PolygonCollider2D);
const wv = new Vec3();
const lv = new Vec3();
for (const poly of polys) {
const owner = poly.node;
const m = owner.getWorldMatrix();
const offset = (poly as any).offset as Vec2 | undefined;
const offX = offset?.x ?? 0;
const offY = offset?.y ?? 0;
const pts = poly.points ?? [];
for (const p of pts) {
lv.set(p.x + offX, p.y + offY, 0);
Vec3.transformMat4(wv, lv, m);
toLocal(wv);
}
}
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) return null;
return { minX, minY, maxX, maxY };
}
/**初始化位置钳制缓存:把当前预览整体 AABB 转成“相对 previewRoot.position 的偏移” */
private _ensureClampCache(maskUT: UITransform, previewRoot: Node): void {
if (this._clampRelAABB) return;
const aabb = this._getPreviewLocalAABBInMask(maskUT, previewRoot);
if (!aabb) return;
const p = previewRoot.position; // previewRoot 本身就在 chooseMask 下,本地坐标系一致
this._clampRelAABB = {
minX: aabb.minX - p.x,
minY: aabb.minY - p.y,
maxX: aabb.maxX - p.x,
maxY: aabb.maxY - p.y,
};
}
/**使用 position + 缓存 AABB 做钳制(不做每帧遍历/矩阵计算) */
private _clampPreviewByPosition(maskUT: UITransform, previewRoot: Node): void {
this._ensureClampCache(maskUT, previewRoot);
if (!this._clampRelAABB) return;
const maskW = maskUT.contentSize.width;
const maskH = maskUT.contentSize.height;
const ax = maskUT.anchorPoint.x;
const ay = maskUT.anchorPoint.y;
const maskLocalMinX = -maskW * ax;
const maskLocalMaxX = maskW * (1 - ax);
const maskLocalMinY = -maskH * ay;
const maskLocalMaxY = maskH * (1 - ay);
const pad = this.clampPaddingPx ?? 0;
const rel = this._clampRelAABB;
const p = previewRoot.position;
// 约束:p.x + rel.minX - pad >= maskMinX 且 p.x + rel.maxX + pad <= maskMaxX
const minPX = maskLocalMinX - rel.minX + pad;
const maxPX = maskLocalMaxX - rel.maxX - pad;
const minPY = maskLocalMinY - rel.minY + pad;
const maxPY = maskLocalMaxY - rel.maxY - pad;
const clampedX = Math.min(Math.max(p.x, minPX), maxPX);
const clampedY = Math.min(Math.max(p.y, minPY), maxPY);
if (clampedX !== p.x || clampedY !== p.y) {
previewRoot.setPosition(clampedX, clampedY, p.z);
}
}
private _syncPreviewToTouch(event: EventTouch): void {
if (!this._previewNode || !this.chooseMask) return;
const uiPos = event.getUILocation();
const worldPos = new Vec3(uiPos.x, uiPos.y, 0);
// 先按触摸点移动,再做 chooseMask 区域边界钳制(保证预览整体不超过 mask)
const maskUT = this.chooseMask.getComponent(UITransform);
if (maskUT) {
const localPos = new Vec3();
maskUT.convertToNodeSpaceAR(worldPos, localPos);
this._previewNode.setPosition(localPos);
if (this._previewAlignNode?.isValid) {
const alignWorld = new Vec3(
uiPos.x,
uiPos.y + (this.alignPreviewOffsetAboveFingerPx ?? 0),
0,
);
const alignLocal = new Vec3();
maskUT.convertToNodeSpaceAR(alignWorld, alignLocal);
this._previewAlignNode.setPosition(alignLocal);
}
} else {
this._previewNode.setWorldPosition(worldPos);
if (this._previewAlignNode?.isValid) {
this._previewAlignNode.setWorldPosition(
new Vec3(
uiPos.x,
uiPos.y + (this.alignPreviewOffsetAboveFingerPx ?? 0),
this._previewAlignNode.worldPosition.z,
),
);
}
}
// 边界保护:在 chooseMask 的本地坐标系中钳制(避免 UI 坐标/世界坐标不一致导致钳制失效)
// if (maskUT) {
// this._clampPreviewByPosition(maskUT, this._previewNode);
// }
// 白线预览块:Y 固定在白线上,X 跟随主拖拽方块(受钳制后的最终位置)
if (!gg.game.isShowBlockDrop && this._pieceNodeCopyMove && this._pieceNodeCopyMove.isValid && this._pieceNodeCopyMoveFixedWorldY != null) {
const x = this._toGameWorldXFromUiWorldX(this._previewNode.worldPosition.x);
const z = this._pieceNodeCopyMove.worldPosition.z;
this._pieceNodeCopyMove.setWorldPosition(new Vec3(x, this._pieceNodeCopyMoveFixedWorldY, z));
}
this._syncPreviewDragScaleForMapZoom();
let time = Date.now() - this._startTime;
const wasOverCancel = this._isOverCancel;
if (time < gg.game.CurentBattle.cancelTimeMs) {
this._isOverCancel = true;
} else {
this._isOverCancel = this._isOverCancleNode(event);
}
if (!wasOverCancel && this._isOverCancel) {
this._playCancelHoverFeedback();
}
if (this._guideLine) {
// 拖拽时始终显示引导区域(避免“没超过高线就完全不显示”看起来像没画出来)
this._guideLine.active = true;
this._updateGuideLine();
}
this._syncAlignPreviewVisibilityByHighLine();
}
private _createGuideLine(): void {
if (!this.chooseMask) return;
// guideLine 放在 chooseMask 的父节点(通常是 Canvas/UI 根),避免被 chooseMask 裁剪
this._guideLineParent = this.chooseMask.parent ?? this.chooseMask;
// 垂直覆盖使用超大范围,保证镜头缩放后仍能完整贯穿屏幕
this._chooseMaskTopY = 10000;
const node = new Node('GuideLine');
// 初始 layer 先跟 chooseMask,绘制时会再同步到目标预览层
node.layer = this.chooseMask.layer;
node.addComponent(UITransform);
node.addComponent(Graphics);
this._guideLineParent.addChild(node);
node.setSiblingIndex(this._guideLineParent.children.length - 1);
this._guideLine = node;
}
private _updateGuideLine(): void {
if (!this._guideLine || !this._previewNode) return;
const g = this._guideLine.getComponent(Graphics)!;
g.clear();
const parent = this._guideLineParent ?? this.chooseMask ?? this._previewNode.parent;
if (!parent) return;
// 引导线完全使用“白线副本”的世界包围盒(与真实落点同源)。
const targetNode = (this._pieceNodeCopyMove && this._pieceNodeCopyMove.isValid) ? this._pieceNodeCopyMove : this._previewNode;
// 与目标节点保持同一 layer/camera,避免镜头缩放后屏幕投影不一致
if (this._guideLine.layer !== targetNode.layer) {
this._guideLine.layer = targetNode.layer;
}
const centerWorld = targetNode.worldPosition;
// 宽度按“方块实际占用格子列数”计算(列数 * 固定格宽),不使用包围盒。
const cellWorldWidth = this._boundMap?.blockHeight ?? 36;
const rectCenterX = centerWorld.x;
const cols = this._getGridColumnCountByPolygon(targetNode, cellWorldWidth);
const snappedWorldWidth = cols * cellWorldWidth;
const halfW = snappedWorldWidth * 0.5;
let left = -halfW;
let right = halfW;
const leftWorldX = rectCenterX - halfW;
const rightWorldX = rectCenterX + halfW;
left = this._worldToLocal(parent, new Vec3(leftWorldX, centerWorld.y, 0)).x;
right = this._worldToLocal(parent, new Vec3(rightWorldX, centerWorld.y, 0)).x;
// 画线区域左右各 +1 像素(按 parent 的本地坐标系单位,即 UI 像素)
// left -= 1;
// right += 1;
const topY = this._chooseMaskTopY || (view.getDesignResolutionSize().height / 2);
// 新需求:线要很长,直接贯穿整个可视区域
const bottomY = -topY;
// 拖到取消区:引导区域变红
g.fillColor = this._isOverCancel ? new Color(255, 0, 0, 50) : new Color(0, 255, 0, 50);
g.rect(left, bottomY, right - left, topY - bottomY);
g.fill();
g.strokeColor = this._isOverCancel ? new Color(255, 0, 0, 200) : new Color(0, 255, 0, 200);
g.lineWidth = 3;
g.moveTo(left, topY);
g.lineTo(left, bottomY);
g.stroke();
g.moveTo(right, topY);
g.lineTo(right, bottomY);
g.stroke();
}
private _destroyGuideLine(): void {
if (this._guideLine) {
const line = this._guideLine;
this._guideLine = null;
line.destroy();
}
}
refreshCoinNum2(){
this.scheduleOnce(()=>{
if (gg.game.CurentBattle.BattleType === BattleType.OrangeWeaponMode_Dian) {
this._affordable = true
return;
}
let cost = this.node.getChildByName('cost').getComponent(Label)
if (gg.game.CurentBattle.CurentCoinNum < this.config.price) {
this._affordable = false
cost.color = new Color(255, 0, 0)
} else {
this._affordable = true
cost.color = new Color(255, 255, 255)
}
},1.5)
}
/**刷新金币*/
refreshCoinNum() {
if (!this.config) {
return
}
const lightning = gg.game.CurentBattle.BattleType === BattleType.OrangeWeaponMode_Dian;
const yinbi = this.node.getChildByName('jinbi');
const costNode = this.node.getChildByName('cost');
if (lightning) {
if (yinbi) yinbi.active = false;
//if (costNode) costNode.active = false;
const cost = costNode?.getComponent(Label);
if (costNode && cost) {
costNode.x = -30
cost.string = '免费';
}
this._affordable = true;
} else {
if (yinbi) yinbi.active = true;
if (costNode) costNode.active = true;
const cost = costNode?.getComponent(Label);
if (cost) {
cost.string = `${this.config.price}`;
if (gg.game.CurentBattle.CurentCoinNum < this.config.price) {
this._affordable = false;
cost.color = new Color(255, 0, 0);
} else {
this._affordable = true;
cost.color = new Color(255, 255, 255);
}
}
}
if(!this.wenhaoBtnNode){
return
}
if(gg.game.CurentBattle.ChapterId == 1 || gg.game.CurentBattle.ChapterId == 2){
if(this.config.type != TetriType.Tetris_Baseattk){
this.wenhaoBtnNode.active = true
this.tetriNameLabel.node.active = true
this.setNameType(this.config.type)
}else{
this.wenhaoBtnNode.active = false
this.tetriNameLabel.node.active = false
}
}else{
this.wenhaoBtnNode.active = false
this.tetriNameLabel.node.active = false
}
}
click_wenhaoBtn(){
//
let type = this.config.type
if(type == TetriType.Tetris_Basefloor){
gg.ui.openPanel(UIID.TetriDoc, { data: 3});
}else{
gg.ui.openPanel(UIID.TetriDoc, { data: 4 });
}
}
setNameType(type: TetriType){
// Tetris_Basefloor
// Tetris_BaseGold
// Tetris_Baseheal
// Tetris_Basevine
if(type == TetriType.Tetris_Basefloor){
this.tetriNameLabel.string = '悬浮'
}else if(type == TetriType.Tetris_BaseGold){
this.tetriNameLabel.string = '产币'
}else if(type == TetriType.Tetris_Baseheal){
this.tetriNameLabel.string = '回血'
}else if(type == TetriType.Tetris_Basevine){
this.tetriNameLabel.string = '绑定'
}
}
/** 同步主预览、白线副本、对齐预览的表情 Spine */
private _playPreviewBlockSpine(state: TetriBlockSpineState): void {
playTetriBlockSpine(this._pieceNode, state);
playTetriBlockSpine(this._pieceNodeCopyMove, state);
playTetriBlockSpine(this._previewAlignNode, state);
}
}