181404010226 4 mesi fa
parent
commit
c3639eccc2

+ 1 - 0
assets/Scenes/GameLevel.scene

@@ -31557,6 +31557,7 @@
     "cameraNode": {
       "__id__": 3
     },
+    "debugDrawSnapRange": true,
     "_id": "ealiXZigZIgrXLo2NL22Ns"
   },
   {

+ 107 - 263
assets/scripts/CombatSystem/BlockManager.ts

@@ -1,4 +1,4 @@
-import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2, UITransform, find, Rect, Label, Color, Size, Sprite, SpriteFrame, resources, Button, Collider2D, Material } from 'cc';
+import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Color, Vec2, UITransform, find, Rect, Label, Size, Sprite, SpriteFrame, resources, Button, Collider2D, Material } from 'cc';
 import { ConfigManager, WeaponConfig } from '../Core/ConfigManager';
 import { SaveDataManager } from '../LevelSystem/SaveDataManager';
 import { LevelSessionManager } from '../Core/LevelSessionManager';
@@ -58,12 +58,6 @@ export class BlockManager extends Component {
     
     // 已经生成的块
     private blocks: Node[] = [];
-    // 当前拖拽的块
-    private currentDragBlock: Node | null = null;
-    // 拖拽起始位置
-    private startPos = new Vec2();
-    // 块的起始位置
-    private blockStartPos: Vec3 = new Vec3();
     // 网格占用情况,用于控制台输出
     private gridOccupationMap: number[][] = [];
     // 网格行数和列数
@@ -80,9 +74,9 @@ export class BlockManager extends Component {
     // 临时保存方块的原始占用格子
     private tempRemovedOccupiedGrids: { block: Node, occupiedGrids: { row: number, col: number }[] }[] = [];
     // 方块原始位置(在kuang中的位置)
-    private originalPositions: Map<Node, Vec3> = new Map();
+    public originalPositions: Map<Node, Vec3> = new Map();
     // 方块当前所在的区域
-    private blockLocations: Map<Node, string> = new Map();
+    public blockLocations: Map<Node, string> = new Map();
     
     // 配置管理器
     private configManager: ConfigManager = null;
@@ -93,8 +87,11 @@ export class BlockManager extends Component {
     // 方块武器配置映射
     private blockWeaponConfigs: Map<Node, WeaponConfig> = new Map();
     
+    // 调试绘制相关
+    // 调试绘制功能已迁移到GameBlockSelection
+    
     /** 冷却机制已废弃,方块随时可移动 */
-    private canMoveBlock(block: Node): boolean { return true; }
+
 
 
     // 清除所有冷却(游戏重置时调用)
@@ -237,6 +234,8 @@ export class BlockManager extends Component {
         // 设置事件监听器
         this.setupEventListeners();
         
+        // 调试绘制功能已迁移到GameBlockSelection
+        
         // 移除自动生成方块逻辑,改为事件触发
         // 方块生成现在通过以下事件触发:
         // - GAME_START: 游戏开始时
@@ -432,13 +431,18 @@ export class BlockManager extends Component {
             // 设置方块的武器外观
             this.setupBlockWeaponVisual(block, weaponConfig);
             
-            this.setupDragEvents(block);
-            
             // 为新生成的方块添加标签
             BlockTag.addTag(block);
             console.log(`[BlockManager] 为方块 ${block.name} 添加标签,UUID: ${block.uuid}`);
         }
         
+        // 通过事件机制通知GameBlockSelection为新生成的方块设置拖拽事件
+        if (this.blocks.length > 0) {
+            const eventBus = EventBus.getInstance();
+            eventBus.emit(GameEvents.SETUP_BLOCK_DRAG_EVENTS, this.blocks.slice());
+            console.log(`[BlockManager] 发送方块拖拽事件设置请求,方块数量: ${this.blocks.length}`);
+        }
+        
         this.updateCoinDisplay();
     }
     
@@ -522,239 +526,17 @@ export class BlockManager extends Component {
         this.updateCoinDisplay();
     }
     
-    // 设置拖拽事件
-    setupDragEvents(block: Node) {
-        block.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
-            if (this.gameStarted && this.blockLocations.get(block) === 'grid') {
-                if (!this.canMoveBlock(block)) {
-                    return;
-                }
-            }
-            
-            this.currentDragBlock = block;
-            this.startPos = event.getUILocation();
-            this.blockStartPos.set(block.position);
-            this.currentDragBlock['startLocation'] = this.blockLocations.get(block);
-            
-            block.setSiblingIndex(block.parent.children.length - 1);
-            this.tempStoreBlockOccupiedGrids(block);
-            // 拖拽开始时禁用碰撞体
-            const collider = block.getComponent(Collider2D);
-            if (collider) collider.enabled = false;
-        }, this);
-        
-        block.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
-            if (this.gameStarted && this.blockLocations.get(block) === 'grid') {
-                if (!this.canMoveBlock(block)) {
-                    return;
-                }
-            }
-            
-            if (!this.currentDragBlock) return;
-            
-            const location = event.getUILocation();
-            const deltaX = location.x - this.startPos.x;
-            const deltaY = location.y - this.startPos.y;
-            
-            this.currentDragBlock.position = new Vec3(
-                this.blockStartPos.x + deltaX,
-                this.blockStartPos.y + deltaY,
-                this.blockStartPos.z
-            );
-        }, this);
-        
-        block.on(Node.EventType.TOUCH_END, async (event: EventTouch) => {
-            if (this.gameStarted && this.blockLocations.get(block) === 'grid') {
-                if (!this.canMoveBlock(block)) {
-                    return;
-                }
-            }
-            
-            if (this.currentDragBlock) {
-                await this.handleBlockDrop(event);
-                
-          
-                this.currentDragBlock = null;
-                // 拖拽结束时恢复碰撞体
-                const collider = block.getComponent(Collider2D);
-                if (collider) collider.enabled = true;
-            }
-        }, this);
-        
-        block.on(Node.EventType.TOUCH_CANCEL, () => {
-            if (this.currentDragBlock) {
-                this.returnBlockToOriginalPosition();
-                this.currentDragBlock = null;
-                // 拖拽取消时恢复碰撞体
-                const collider = block.getComponent(Collider2D);
-                if (collider) collider.enabled = true;
-            }
-        }, this);
-    }
+    // 设置拖拽事件方法已迁移到GameBlockSelection
     
-    // 处理方块放下
-    async handleBlockDrop(event: EventTouch) {
-        const touchPos = event.getLocation();
-        const startLocation = this.currentDragBlock['startLocation'];
-        
-        if (this.isInKuangArea(touchPos)) {
-            // 检查是否有标签,只有有标签的方块才能放回kuang
-            if (BlockTag.hasTag(this.currentDragBlock)) {
-                this.returnBlockToKuang(startLocation);
-            } else {
-                // 没有标签的方块不能放回kuang,返回原位置
-                console.log(`[BlockManager] 方块 ${this.currentDragBlock.name} 没有标签,不能放回kuang区域`);
-                this.returnBlockToOriginalPosition();
-            }
-        } else if (this.tryPlaceBlockToGrid(this.currentDragBlock)) {
-            await this.handleSuccessfulPlacement(startLocation);
-        } else {
-            console.log(`[BlockManager] 方块 ${this.currentDragBlock.name} 放置到网格失败`);
-            console.log(`[BlockManager] 方块标签状态:`, BlockTag.hasTag(this.currentDragBlock));
-            console.log(`[BlockManager] 方块UUID:`, this.currentDragBlock.uuid);
-            
-            // 放置失败,尝试直接与重叠方块合成
-            if (this.tryMergeOnOverlap(this.currentDragBlock)) {
-                // 合成成功时,若来自 kuang 则扣费
-                if (startLocation !== 'grid') {
-                    const price = this.getBlockPrice(this.currentDragBlock);
-                    this.deductPlayerCoins(price);
-                }
-                // 当前拖拽块已被销毁(合并时),无需复位
-            } else {
-                console.log(`[BlockManager] 方块 ${this.currentDragBlock.name} 合成也失败,返回原位置`);
-                this.returnBlockToOriginalPosition();
-            }
-        }
-    }
-    
-    // 返回方块到kuang区域
-    returnBlockToKuang(startLocation: string) {
-        const originalPos = this.originalPositions.get(this.currentDragBlock);
-        if (originalPos) {
-            const kuangNode = this.kuangContainer;
-            if (kuangNode && this.currentDragBlock.parent !== kuangNode) {
-                this.currentDragBlock.removeFromParent();
-                kuangNode.addChild(this.currentDragBlock);
-            }
-            
-            this.currentDragBlock.position = originalPos.clone();
-        }
-        
-        this.restoreBlockOccupiedGrids(this.currentDragBlock);
-        this.blockLocations.set(this.currentDragBlock, 'kuang');
-        this.showPriceLabel(this.currentDragBlock);
-        
-        if (startLocation === 'grid') {
-            const price = this.getBlockPrice(this.currentDragBlock);
-            this.refundPlayerCoins(price);
-            this.currentDragBlock['placedBefore'] = false;
-        }
-        
-        const dbNode = this.currentDragBlock['dbNode'];
-        if (dbNode) {
-            dbNode.active = true;
-            this.currentDragBlock.emit(Node.EventType.TRANSFORM_CHANGED);
-        }
-    }
+
     
-    // 处理成功放置
-    async handleSuccessfulPlacement(startLocation: string) {
-        const price = this.getBlockPrice(this.currentDragBlock);
-        
-        if (startLocation === 'grid') {
-            this.clearTempStoredOccupiedGrids(this.currentDragBlock);
-            this.blockLocations.set(this.currentDragBlock, 'grid');
-            this.hidePriceLabel(this.currentDragBlock);
-            
-            const dbNode = this.currentDragBlock['dbNode'];
-            if (dbNode) {
-                dbNode.active = false;
-            }
-            
-            // 立即将方块移动到PlacedBlocks节点下,不等游戏开始
-            this.moveBlockToPlacedBlocks(this.currentDragBlock);
-            
-            // 如果游戏已开始,添加锁定视觉提示
-            if (this.gameStarted) {
-                this.addLockedVisualHint(this.currentDragBlock);
-                
-                // 游戏开始后放置的方块移除标签,不能再放回kuang
-                BlockTag.removeTag(this.currentDragBlock);
-            }
 
-            // 检查并执行合成
-            await this.tryMergeBlock(this.currentDragBlock);
-        } else {
-            if (this.deductPlayerCoins(price)) {
-                this.clearTempStoredOccupiedGrids(this.currentDragBlock);
-                this.blockLocations.set(this.currentDragBlock, 'grid');
-                this.hidePriceLabel(this.currentDragBlock);
-                
-                const dbNode = this.currentDragBlock['dbNode'];
-                if (dbNode) {
-                    dbNode.active = false;
-                }
-                
-                this.currentDragBlock['placedBefore'] = true;
-                
-                // 立即将方块移动到PlacedBlocks节点下,不等游戏开始
-                this.moveBlockToPlacedBlocks(this.currentDragBlock);
-                
-                // 如果游戏已开始,添加锁定视觉提示
-                if (this.gameStarted) {
-                    this.addLockedVisualHint(this.currentDragBlock);
-                    
-                    // 游戏开始后放置的方块移除标签,不能再放回kuang
-                    BlockTag.removeTag(this.currentDragBlock);
-                }
+    
 
-                // 检查并执行合成
-                await this.tryMergeBlock(this.currentDragBlock);
-            } else {
-                this.returnBlockToOriginalPosition();
-            }
-        }
-    }
     
-    // 返回方块到原位置
-    returnBlockToOriginalPosition() {
-        const currentLocation = this.blockLocations.get(this.currentDragBlock);
-        if (currentLocation === 'kuang') {
-            const originalPos = this.originalPositions.get(this.currentDragBlock);
-            if (originalPos) {
-                this.currentDragBlock.position = originalPos.clone();
-            }
-        } else {
-            this.currentDragBlock.position = this.blockStartPos.clone();
-        }
-        
-        this.restoreBlockOccupiedGrids(this.currentDragBlock);
-        this.showPriceLabel(this.currentDragBlock);
-        
-        const dbNode = this.currentDragBlock['dbNode'];
-        if (dbNode) {
-            dbNode.active = true;
-            this.currentDragBlock.emit(Node.EventType.TRANSFORM_CHANGED);
-        }
-    }
+
     
-    // 检查是否在kuang区域内
-    isInKuangArea(touchPos: Vec2): boolean {
-        if (!this.kuangContainer) return false;
-        
-        const kuangTransform = this.kuangContainer.getComponent(UITransform);
-        if (!kuangTransform) return false;
-        
-        const kuangBoundingBox = new Rect(
-            this.kuangContainer.worldPosition.x - kuangTransform.width * kuangTransform.anchorX,
-            this.kuangContainer.worldPosition.y - kuangTransform.height * kuangTransform.anchorY,
-            kuangTransform.width,
-            kuangTransform.height
-        );
-        
-        return kuangBoundingBox.contains(new Vec2(touchPos.x, touchPos.y));
-    }
+
     
     // 临时保存方块占用的网格
     tempStoreBlockOccupiedGrids(block: Node) {
@@ -848,7 +630,7 @@ export class BlockManager extends Component {
     }
     
     // 找到最近的网格节点
-    findNearestGridNode(position: Vec3): Node {
+    public findNearestGridNode(position: Vec3): Node {
         if (!this.gridContainer || !this.gridInitialized) return null;
         
         let nearestNode: Node = null;
@@ -965,6 +747,16 @@ export class BlockManager extends Component {
         return null;
     }
     
+    // 获取指定行列的网格世界坐标(用于调试绘制)
+    public getGridWorldPosition(row: number, col: number): Vec3 | null {
+        if (!this.gridInitialized || !this.gridNodes[row] || !this.gridNodes[row][col]) {
+            return null;
+        }
+        
+        const gridNode = this.gridNodes[row][col];
+        return this.gridContainer.getComponent(UITransform).convertToWorldSpaceAR(gridNode.position);
+    }
+    
     // 获取方块的所有部分节点及其相对坐标
     getBlockParts(block: Node): { node: Node, x: number, y: number }[] {
         const parts: { node: Node, x: number, y: number }[] = [];
@@ -1016,7 +808,7 @@ export class BlockManager extends Component {
     }
     
     // 标记方块占用的格子
-    markOccupiedPositions(block: Node, targetGrid: Node) {
+    public markOccupiedPositions(block: Node, targetGrid: Node) {
         if (!this.gridInitialized) return;
         
         const targetRowCol = this.getGridRowCol(targetGrid);
@@ -1184,7 +976,7 @@ export class BlockManager extends Component {
     }
     
     // 重置网格占用状态
-    private resetGridOccupation() {
+    public resetGridOccupation() {
         console.log('[BlockManager] 重置网格占用状态');
         
         // 重新初始化网格占用地图
@@ -1239,8 +1031,10 @@ export class BlockManager extends Component {
         this.placedBlocksContainer.addChild(block);
         block.setWorldPosition(worldPosition);
         
-        // 重新设置触摸事件监听器
-        this.setupDragEvents(block);
+        // 通过事件机制重新设置拖拽事件
+        const eventBus = EventBus.getInstance();
+        eventBus.emit(GameEvents.SETUP_BLOCK_DRAG_EVENTS, [block]);
+        console.log(`[BlockManager] 为移动到PlacedBlocks的方块 ${block.name} 重新设置拖拽事件`);
     }
     
     // 根据武器配置选择合适的预制体
@@ -1446,7 +1240,7 @@ export class BlockManager extends Component {
         'sharp_carrot': {
             'I': { x: 0, y: -1 },      // 尖胡萝卜竖条形状,轻微向下偏移
             'H-I': { x: 0, y: 0 },     // 尖胡萝卜横条形状,居中
-            'L': { x: -1, y: -5 },     // 尖胡萝卜L型,轻微向左下偏移
+            'L': { x: -1, y: -7 },     // 尖胡萝卜L型,轻微向左下偏移
             'S': { x: 1, y: -1 },      // 尖胡萝卜S型,轻微向右下偏移
             'D-T': { x: 0, y: -1 }     // 尖胡萝卜倒T型,轻微向下偏移
         },
@@ -1472,32 +1266,32 @@ export class BlockManager extends Component {
             'D-T': { x: -10, y: -13 }     // 回旋镖植物倒T型,轻微向下偏移
         },
         'hot_pepper': {
-            'I': { x: 0, y: -1 },      // 辣椒竖条形状,轻微向下偏移
+            'I': { x: 0, y: 5 },      // 辣椒竖条形状,轻微向下偏移
             'H-I': { x: 0, y: 0 },     // 辣椒横条形状,居中
             'L': { x: -1, y: -1 },     // 辣椒L型,轻微向左下偏移
             'S': { x: 1, y: -1 },      // 辣椒S型,轻微向右下偏移
-            'D-T': { x: 0, y: -1 }     // 辣椒倒T型,轻微向下偏移
+            'D-T': { x: 0, y: 0 }     // 辣椒倒T型,轻微向下偏移
         },
         'cactus_shotgun': {
             'I': { x: 0, y: -1 },      // 仙人掌霰弹枪竖条形状,轻微向下偏移
             'H-I': { x: 0, y: 0 },     // 仙人掌霰弹枪横条形状,居中
-            'L': { x: -1, y: -1 },     // 仙人掌霰弹枪L型,轻微向左下偏移
+            'L': { x: -1, y: -3 },     // 仙人掌霰弹枪L型,轻微向左下偏移
             'S': { x: 1, y: -1 },      // 仙人掌霰弹枪S型,轻微向右下偏移
             'D-T': { x: 0, y: -1 }     // 仙人掌霰弹枪倒T型,轻微向下偏移
         },
         'okra_missile': {
-            'I': { x: 0, y: -1 },      // 秋葵导弹竖条形状,轻微向下偏移
-            'H-I': { x: 0, y: 0 },     // 秋葵导弹横条形状,居中
-            'L': { x: -1, y: -1 },     // 秋葵导弹L型,轻微向左下偏移
-            'S': { x: 1, y: -1 },      // 秋葵导弹S型,轻微向右下偏移
-            'D-T': { x: 0, y: -1 }     // 秋葵导弹倒T型,轻微向下偏移
+            'I': { x: -4, y:3 },      // 秋葵导弹竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: -3 },     // 秋葵导弹横条形状,居中
+            'L': { x: -1, y: -9 },     // 秋葵导弹L型,轻微向左下偏移
+            'S': { x: -6, y: 0 },      // 秋葵导弹S型,轻微向右下偏移
+            'D-T': { x: -3, y: 3 }     // 秋葵导弹倒T型,轻微向下偏移
         },
         'mace_club': {
             'I': { x: 0, y: -1 },      // 狼牙棒竖条形状,轻微向下偏移
-            'H-I': { x: 0, y: 0 },     // 狼牙棒横条形状,居中
+            'H-I': { x: 1, y: 0 },     // 狼牙棒横条形状,居中
             'L': { x: -1, y: -1 },     // 狼牙棒L型,轻微向左下偏移
             'S': { x: 1, y: -1 },      // 狼牙棒S型,轻微向右下偏移
-            'D-T': { x: 0, y: -1 }     // 狼牙棒倒T型,轻微向下偏移
+            'D-T': { x: 0, y: -5 }     // 狼牙棒倒T型,轻微向下偏移
         }
     };
     
@@ -1521,14 +1315,14 @@ export class BlockManager extends Component {
         },
         'sharp_carrot': {
             'I': 0,      // 尖胡萝卜竖条形状
-            'H-I': 0,   // 尖胡萝卜横条形状,旋转适配水平方向
+            'H-I': 90,   // 尖胡萝卜横条形状,旋转适配水平方向
             'L': -15,    // 尖胡萝卜L型,轻微调整
             'S': 15,     // 尖胡萝卜S型,轻微调整
             'D-T': 0     // 尖胡萝卜倒T型
         },
         'saw_grass': {
             'I': 0,      // 锯齿草竖条形状
-            'H-I': 90,   // 锯齿草横条形状
+            'H-I': 0,   // 锯齿草横条形状
             'L': -45,    // 锯齿草L型,更大角度适配锯齿形状
             'S': 30,     // 锯齿草S型,适配锯齿弯曲
             'D-T': 0     // 锯齿草倒T型
@@ -1569,10 +1363,10 @@ export class BlockManager extends Component {
             'D-T': 0     // 秋葵导弹倒T型
         },
         'mace_club': {
-            'I': 0,      // 狼牙棒竖条形状
-            'H-I': 90,   // 狼牙棒横条形状
-            'L': -25,    // 狼牙棒L型,适配棒状武器
-            'S': 25,     // 狼牙棒S型
+            'I': -45,      // 狼牙棒竖条形状
+            'H-I': 0,   // 狼牙棒横条形状
+            'L': -90,    // 狼牙棒L型,适配棒状武器
+            'S': -90,     // 狼牙棒S型
             'D-T': 0     // 狼牙棒倒T型
         }
     };
@@ -1890,8 +1684,37 @@ export class BlockManager extends Component {
             BlockTag.removeTagsInContainer(this.placedBlocksContainer);
         }
         
+        // 保存已放置方块的占用信息
+        const placedBlocksOccupation: { block: Node, occupiedGrids: { row: number, col: number }[] }[] = [];
+        if (this.placedBlocksContainer && this.placedBlocksContainer.isValid) {
+            for (let i = 0; i < this.placedBlocksContainer.children.length; i++) {
+                const block = this.placedBlocksContainer.children[i];
+                const occupiedGrids = block['occupiedGrids'];
+                if (occupiedGrids && occupiedGrids.length > 0) {
+                    placedBlocksOccupation.push({
+                        block: block,
+                        occupiedGrids: [...occupiedGrids]
+                    });
+                }
+            }
+        }
+        
         // 生成新的方块
         this.generateRandomBlocksInKuang();
+        
+        // 恢复已放置方块的占用信息
+        for (const item of placedBlocksOccupation) {
+            if (item.block && item.block.isValid) {
+                item.block['occupiedGrids'] = [...item.occupiedGrids];
+                // 重新标记网格占用状态
+                for (const grid of item.occupiedGrids) {
+                    if (grid.row >= 0 && grid.row < this.GRID_ROWS && 
+                        grid.col >= 0 && grid.col < this.GRID_COLS) {
+                        this.gridOccupationMap[grid.row][grid.col] = 1;
+                    }
+                }
+            }
+        }
     }
 
     // 检查是否有已放置的方块
@@ -1913,7 +1736,7 @@ export class BlockManager extends Component {
     private readonly rarityOrder: string[] = ['common','uncommon','rare','epic','legendary'];
 
     /** 检查当前放置的方块能否与相同稀有度的方块合成 */
-    private async tryMergeBlock(block: Node) {
+    public async tryMergeBlock(block: Node) {
         const rarity = this.getBlockRarity(block);
         if (!rarity) return;
 
@@ -1995,6 +1818,8 @@ export class BlockManager extends Component {
         if (nextRarity) {
             await this.tryMergeBlock(target);
         }
+        
+        console.log(`[BlockManager] 合成完成`);
     }
 
     private getBlockRarity(block: Node): string | null {
@@ -2048,7 +1873,7 @@ export class BlockManager extends Component {
     }
 
     /** 在放置失败时尝试与现有方块进行合成 */
-    private tryMergeOnOverlap(draggedBlock: Node): boolean {
+    public tryMergeOnOverlap(draggedBlock: Node): boolean {
         if (!this.placedBlocksContainer) return false;
         const rarity = this.getBlockRarity(draggedBlock);
         if (!rarity) return false;
@@ -2070,7 +1895,26 @@ export class BlockManager extends Component {
         return false;
     }
     
+    // 调试绘制功能已迁移到GameBlockSelection
+    
+    // 输出格子占用情况矩阵
+    public printGridOccupationMatrix() {
+        console.log('[BlockManager] 格子占用情况矩阵 (6行11列):');
+        for (let row = 0; row < this.GRID_ROWS; row++) {
+            let rowStr = '';
+            for (let col = 0; col < this.GRID_COLS; col++) {
+                rowStr += this.gridOccupationMap[row][col] + ' ';
+            }
+            console.log(`第${row + 1}行: ${rowStr.trim()}`);
+        }
+    }
+    
+    // 通过GameBlockSelection刷新方块占用情况
+
+    
     onDestroy() {
+        // 调试绘制功能已迁移到GameBlockSelection
+        
         // 清理事件监听
         const eventBus = EventBus.getInstance();
         eventBus.off(GameEvents.RESET_BLOCK_MANAGER, this.onResetBlockManagerEvent, this);

+ 456 - 3
assets/scripts/CombatSystem/BlockSelection/GameBlockSelection.ts

@@ -1,4 +1,4 @@
-import { _decorator, Component, Node, Button, Label, find } from 'cc';
+import { _decorator, Component, Node, Button, Label, find, EventTouch, Vec2, Vec3, UITransform, Rect, Collider2D, Graphics, Color } from 'cc';
 import { LevelSessionManager } from '../../Core/LevelSessionManager';
 import { BallController } from '../BallController';
 import { BlockManager } from '../BlockManager';
@@ -96,6 +96,20 @@ export class GameBlockSelection extends Component {
     
     // 标记是否应该生成方块(只有在onBattle触发后才为true)
     private shouldGenerateBlocks: boolean = false;
+    
+    // 用户操作方块相关属性
+    private currentDragBlock: Node | null = null;
+    private startPos = new Vec2();
+    private blockStartPos: Vec3 = new Vec3();
+    
+    // 调试绘制相关属性
+    @property({
+        tooltip: '是否启用吸附检测范围调试绘制'
+    })
+    public debugDrawSnapRange: boolean = false;
+    
+    private debugDrawNode: Node = null;
+    private debugGraphics: Graphics = null;
 
     onEnable() {
         console.log('[GameBlockSelection] onEnable() 节点被激活');
@@ -103,6 +117,7 @@ export class GameBlockSelection extends Component {
         if (!this.isInitialized) {
             this.initializeComponent();
         }
+        this.initDebugDraw();
     }
 
     start() {
@@ -176,6 +191,10 @@ export class GameBlockSelection extends Component {
         eventBus.on(GameEvents.GAME_START, this.onGameStartEvent, this);
         console.log('[GameBlockSelection] GAME_START事件监听器已设置');
         
+        // 监听方块拖拽事件设置请求
+        eventBus.on(GameEvents.SETUP_BLOCK_DRAG_EVENTS, this.onSetupBlockDragEventsEvent, this);
+        console.log('[GameBlockSelection] SETUP_BLOCK_DRAG_EVENTS事件监听器已设置');
+        
         console.log('[GameBlockSelection] 事件监听器设置完成,组件节点:', this.node.name);
         console.log('[GameBlockSelection] 当前节点状态:', this.node.active);
     }
@@ -194,6 +213,15 @@ export class GameBlockSelection extends Component {
         this.shouldGenerateBlocks = true;
     }
     
+    // 处理方块拖拽事件设置请求
+    private onSetupBlockDragEventsEvent(blocks: Node[]) {
+        console.log('[GameBlockSelection] 接收到方块拖拽事件设置请求,方块数量:', blocks.length);
+        for (const block of blocks) {
+            this.setupBlockDragEvents(block);
+            console.log(`[GameBlockSelection] 为方块 ${block.name} 设置拖拽事件`);
+        }
+    }
+    
     // 绑定按钮事件
     private bindButtonEvents() {
         // 绑定新增小球按钮
@@ -433,6 +461,33 @@ export class GameBlockSelection extends Component {
         this.onConfirmCallback = callback;
     }
 
+    // 统一的方块占用情况刷新方法
+    public refreshGridOccupation() {
+        console.log('[GameBlockSelection] 刷新方块占用情况');
+        
+        if (this.blockManager) {
+            // 不重置网格占用状态,只重新计算已放置方块的占用情况
+            // 这样可以保留refreshBlocks中恢复的占用信息
+            
+            // 重新计算所有已放置方块的占用情况
+            const placedBlocksContainer = find('Canvas/GameLevelUI/PlacedBlocks');
+            if (placedBlocksContainer) {
+                for (let i = 0; i < placedBlocksContainer.children.length; i++) {
+                    const block = placedBlocksContainer.children[i];
+                    // 重新标记方块占用的网格位置
+                    const gridNode = this.blockManager.findNearestGridNode(block.worldPosition);
+                    if (gridNode) {
+                        this.blockManager.markOccupiedPositions(block, gridNode);
+                    }
+                }
+            }
+            
+            console.log('[GameBlockSelection] 方块占用情况刷新完成');
+        } else {
+            console.warn('[GameBlockSelection] BlockManager未找到,无法刷新占用情况');
+        }
+    }
+
     // 重置方块选择状态
     public resetSelection() {
         console.log('[GameBlockSelection] 重置方块选择状态');
@@ -449,6 +504,9 @@ export class GameBlockSelection extends Component {
         BlockTag.clearAllTags();
         console.log('[GameBlockSelection] 方块标签已清理');
         
+        // 刷新方块占用情况
+        this.refreshGridOccupation();
+        
         // 更新金币显示
         this.updateCoinDisplay();
         console.log('[GameBlockSelection] 金币显示已更新');
@@ -485,11 +543,406 @@ export class GameBlockSelection extends Component {
         console.log('[GameBlockSelection] 请至少上阵一个植物!');
     }
     
+    // 设置方块拖拽事件
+    public setupBlockDragEvents(block: Node) {
+        block.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
+            if (this.blockManager.gameStarted && this.blockManager.blockLocations.get(block) === 'grid') {
+                if (!this.canMoveBlock(block)) {
+                    return;
+                }
+            }
+            
+            this.currentDragBlock = block;
+            this.startPos = event.getUILocation();
+            this.blockStartPos.set(block.position);
+            this.currentDragBlock['startLocation'] = this.blockManager.blockLocations.get(block);
+            
+            block.setSiblingIndex(block.parent.children.length - 1);
+            this.blockManager.tempStoreBlockOccupiedGrids(block);
+            // 拖拽开始时禁用碰撞体
+            const collider = block.getComponent(Collider2D);
+            if (collider) collider.enabled = false;
+        }, this);
+        
+        block.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
+            if (this.blockManager.gameStarted && this.blockManager.blockLocations.get(block) === 'grid') {
+                if (!this.canMoveBlock(block)) {
+                    return;
+                }
+            }
+            
+            if (!this.currentDragBlock) return;
+            
+            const location = event.getUILocation();
+            const deltaX = location.x - this.startPos.x;
+            const deltaY = location.y - this.startPos.y;
+            
+            this.currentDragBlock.position = new Vec3(
+                this.blockStartPos.x + deltaX,
+                this.blockStartPos.y + deltaY,
+                this.blockStartPos.z
+            );
+            
+            // 更新调试绘制
+            this.updateDebugDraw();
+        }, this);
+        
+        block.on(Node.EventType.TOUCH_END, async (event: EventTouch) => {
+            if (this.blockManager.gameStarted && this.blockManager.blockLocations.get(block) === 'grid') {
+                if (!this.canMoveBlock(block)) {
+                    return;
+                }
+            }
+            
+            if (this.currentDragBlock) {
+                await this.handleBlockDrop(event);
+                
+                this.currentDragBlock = null;
+                // 拖拽结束时恢复碰撞体
+                const collider = block.getComponent(Collider2D);
+                if (collider) collider.enabled = true;
+                
+                // 更新调试绘制
+                this.updateDebugDraw();
+            }
+        }, this);
+        
+        block.on(Node.EventType.TOUCH_CANCEL, () => {
+            if (this.currentDragBlock) {
+                this.returnBlockToOriginalPosition();
+                this.currentDragBlock = null;
+                // 拖拽取消时恢复碰撞体
+                const collider = block.getComponent(Collider2D);
+                if (collider) collider.enabled = true;
+                
+                // 更新调试绘制
+                this.updateDebugDraw();
+            }
+        }, this);
+    }
+    
+    // 检查方块是否可以移动
+    private canMoveBlock(block: Node): boolean {
+        return true;
+    }
+    
+    // 处理方块放下
+    private async handleBlockDrop(event: EventTouch) {
+        const touchPos = event.getLocation();
+        const startLocation = this.currentDragBlock['startLocation'];
+        
+        if (this.isInKuangArea(touchPos)) {
+            // 检查是否有标签,只有有标签的方块才能放回kuang
+            if (BlockTag.hasTag(this.currentDragBlock)) {
+                this.returnBlockToKuang(startLocation);
+                // 返回kuang后输出格子占用情况
+                this.blockManager.printGridOccupationMatrix();
+                // 刷新方块占用情况
+                this.refreshGridOccupation();
+            } else {
+                // 没有标签的方块不能放回kuang,返回原位置
+                console.log(`[GameBlockSelection] 方块 ${this.currentDragBlock.name} 没有标签,不能放回kuang区域`);
+                this.returnBlockToOriginalPosition();
+                // 返回原位置后输出格子占用情况
+                this.blockManager.printGridOccupationMatrix();
+                // 刷新方块占用情况
+                this.refreshGridOccupation();
+            }
+        } else if (this.blockManager.tryPlaceBlockToGrid(this.currentDragBlock)) {
+            await this.handleSuccessfulPlacement(startLocation);
+            // 放置成功后输出格子占用情况
+            this.blockManager.printGridOccupationMatrix();
+            // 刷新方块占用情况
+            this.refreshGridOccupation();
+        } else {
+            console.log(`[GameBlockSelection] 方块 ${this.currentDragBlock.name} 放置到网格失败`);
+            console.log(`[GameBlockSelection] 方块标签状态:`, BlockTag.hasTag(this.currentDragBlock));
+            console.log(`[GameBlockSelection] 方块UUID:`, this.currentDragBlock.uuid);
+            
+            // 放置失败,尝试直接与重叠方块合成
+            if (this.blockManager.tryMergeOnOverlap(this.currentDragBlock)) {
+                // 合成成功时,若来自 kuang 则扣费
+                if (startLocation !== 'grid') {
+                    const price = this.blockManager.getBlockPrice(this.currentDragBlock);
+                    this.blockManager.deductPlayerCoins(price);
+                }
+                // 当前拖拽块已被销毁(合并时),无需复位
+                // 合成成功后输出格子占用情况
+                this.blockManager.printGridOccupationMatrix();
+                // 刷新方块占用情况
+                this.refreshGridOccupation();
+            } else {
+                console.log(`[GameBlockSelection] 方块 ${this.currentDragBlock.name} 合成也失败,返回原位置`);
+                this.returnBlockToOriginalPosition();
+                // 放置失败后输出格子占用情况
+                this.blockManager.printGridOccupationMatrix();
+                // 刷新方块占用情况
+                this.refreshGridOccupation();
+            }
+        }
+    }
+    
+    // 检查是否在kuang区域内
+    private isInKuangArea(touchPos: Vec2): boolean {
+        if (!this.blockManager.kuangContainer) return false;
+        
+        const kuangTransform = this.blockManager.kuangContainer.getComponent(UITransform);
+        if (!kuangTransform) return false;
+        
+        const kuangBoundingBox = new Rect(
+            this.blockManager.kuangContainer.worldPosition.x - kuangTransform.width * kuangTransform.anchorX,
+            this.blockManager.kuangContainer.worldPosition.y - kuangTransform.height * kuangTransform.anchorY,
+            kuangTransform.width,
+            kuangTransform.height
+        );
+        
+        return kuangBoundingBox.contains(new Vec2(touchPos.x, touchPos.y));
+    }
+    
+    // 返回方块到kuang区域
+    private returnBlockToKuang(startLocation: string) {
+        const originalPos = this.blockManager.originalPositions.get(this.currentDragBlock);
+        if (originalPos) {
+            const kuangNode = this.blockManager.kuangContainer;
+            if (kuangNode && this.currentDragBlock.parent !== kuangNode) {
+                this.currentDragBlock.removeFromParent();
+                kuangNode.addChild(this.currentDragBlock);
+            }
+            
+            this.currentDragBlock.position = originalPos.clone();
+        }
+        
+        this.blockManager.restoreBlockOccupiedGrids(this.currentDragBlock);
+        this.blockManager.blockLocations.set(this.currentDragBlock, 'kuang');
+        this.blockManager.showPriceLabel(this.currentDragBlock);
+        
+        if (startLocation === 'grid') {
+            const price = this.blockManager.getBlockPrice(this.currentDragBlock);
+            this.blockManager.refundPlayerCoins(price);
+            this.currentDragBlock['placedBefore'] = false;
+        }
+        
+        const dbNode = this.currentDragBlock['dbNode'];
+        if (dbNode) {
+            dbNode.active = true;
+            this.currentDragBlock.emit(Node.EventType.TRANSFORM_CHANGED);
+        }
+    }
+    
+    // 处理成功放置
+    private async handleSuccessfulPlacement(startLocation: string) {
+        const price = this.blockManager.getBlockPrice(this.currentDragBlock);
+        
+        if (startLocation === 'grid') {
+            this.blockManager.clearTempStoredOccupiedGrids(this.currentDragBlock);
+            this.blockManager.blockLocations.set(this.currentDragBlock, 'grid');
+            this.blockManager.hidePriceLabel(this.currentDragBlock);
+            
+            const dbNode = this.currentDragBlock['dbNode'];
+            if (dbNode) {
+                dbNode.active = false;
+            }
+            
+            // 立即将方块移动到PlacedBlocks节点下,不等游戏开始
+            this.blockManager.moveBlockToPlacedBlocks(this.currentDragBlock);
+            
+            // 如果游戏已开始,添加锁定视觉提示
+            if (this.blockManager.gameStarted) {
+                this.blockManager.addLockedVisualHint(this.currentDragBlock);
+                
+                // 游戏开始后放置的方块移除标签,不能再放回kuang
+                BlockTag.removeTag(this.currentDragBlock);
+            }
+
+            // 检查并执行合成
+            await this.blockManager.tryMergeBlock(this.currentDragBlock);
+        } else {
+            if (this.blockManager.deductPlayerCoins(price)) {
+                this.blockManager.clearTempStoredOccupiedGrids(this.currentDragBlock);
+                this.blockManager.blockLocations.set(this.currentDragBlock, 'grid');
+                this.blockManager.hidePriceLabel(this.currentDragBlock);
+                
+                const dbNode = this.currentDragBlock['dbNode'];
+                if (dbNode) {
+                    dbNode.active = false;
+                }
+                
+                this.currentDragBlock['placedBefore'] = true;
+                
+                // 立即将方块移动到PlacedBlocks节点下,不等游戏开始
+                this.blockManager.moveBlockToPlacedBlocks(this.currentDragBlock);
+                
+                // 如果游戏已开始,添加锁定视觉提示
+                if (this.blockManager.gameStarted) {
+                    this.blockManager.addLockedVisualHint(this.currentDragBlock);
+                    
+                    // 游戏开始后放置的方块移除标签,不能再放回kuang
+                    BlockTag.removeTag(this.currentDragBlock);
+                }
+
+                // 检查并执行合成
+                await this.blockManager.tryMergeBlock(this.currentDragBlock);
+            } else {
+                this.returnBlockToOriginalPosition();
+            }
+        }
+    }
+    
+    // 返回方块到原位置
+    private returnBlockToOriginalPosition() {
+        const currentLocation = this.blockManager.blockLocations.get(this.currentDragBlock);
+        if (currentLocation === 'kuang') {
+            const originalPos = this.blockManager.originalPositions.get(this.currentDragBlock);
+            if (originalPos) {
+                this.currentDragBlock.position = originalPos.clone();
+            }
+        } else {
+            this.currentDragBlock.position = this.blockStartPos.clone();
+        }
+        
+        this.blockManager.restoreBlockOccupiedGrids(this.currentDragBlock);
+        this.blockManager.showPriceLabel(this.currentDragBlock);
+        
+        const dbNode = this.currentDragBlock['dbNode'];
+        if (dbNode) {
+            dbNode.active = true;
+            this.currentDragBlock.emit(Node.EventType.TRANSFORM_CHANGED);
+        }
+        
+        // 刷新方块占用情况
+        this.refreshGridOccupation();
+    }
+
+    // === 调试绘制相关方法 ===
+    
+    // 初始化调试绘制
+    private initDebugDraw() {
+        if (!this.debugDrawSnapRange) return;
+        
+        // 创建调试绘制节点
+        this.debugDrawNode = new Node('DebugDraw');
+        this.debugGraphics = this.debugDrawNode.addComponent(Graphics);
+        
+        // 将调试绘制节点添加到场景中
+        if (this.gridContainer && this.gridContainer.parent) {
+            this.gridContainer.parent.addChild(this.debugDrawNode);
+        }
+        
+        console.log('[GameBlockSelection] 调试绘制初始化完成');
+    }
+    
+    // 绘制网格吸附范围
+    private drawGridSnapRanges() {
+        if (!this.debugDrawSnapRange || !this.debugGraphics || !this.blockManager) return;
+        
+        this.debugGraphics.strokeColor = Color.GREEN;
+        this.debugGraphics.lineWidth = 2;
+        
+        // 通过BlockManager获取网格信息来绘制吸附范围
+        const gridSpacing = 54; // 网格间距
+        const snapRange = gridSpacing * 0.8; // 吸附范围
+        
+        // 遍历所有网格位置,绘制吸附范围
+        for (let row = 0; row < 6; row++) {
+            for (let col = 0; col < 11; col++) {
+                // 计算网格世界坐标
+                const gridWorldPos = this.blockManager.getGridWorldPosition(row, col);
+                if (!gridWorldPos) continue;
+                
+                // 转换为调试绘制节点的本地坐标
+                const localPos = new Vec3();
+                this.debugDrawNode.getComponent(UITransform).convertToNodeSpaceAR(gridWorldPos, localPos);
+                
+                // 绘制网格吸附范围圆圈
+                this.debugGraphics.circle(localPos.x, localPos.y, snapRange);
+                this.debugGraphics.stroke();
+            }
+        }
+    }
+    
+    // 绘制方块吸附范围
+    private drawBlockSnapRange(block: Node) {
+        if (!this.debugDrawSnapRange || !this.debugGraphics || !block || !this.blockManager) return;
+        
+        // 绘制方块吸附点
+        this.debugGraphics.strokeColor = Color.RED;
+        this.debugGraphics.fillColor = Color.RED;
+        this.debugGraphics.lineWidth = 3;
+        
+        const blockParts = this.blockManager.getBlockParts(block);
+        const gridSpacing = 54; // 网格间距
+        const snapRange = gridSpacing * 0.6; // 吸附范围
+        
+        blockParts.forEach(part => {
+            const worldPos = part.node.getWorldPosition();
+            const localPos = new Vec3();
+            this.debugDrawNode.getComponent(UITransform).convertToNodeSpaceAR(worldPos, localPos);
+            
+            // 绘制红色十字标记吸附点
+            const crossSize = 10;
+            this.debugGraphics.moveTo(localPos.x - crossSize, localPos.y);
+            this.debugGraphics.lineTo(localPos.x + crossSize, localPos.y);
+            this.debugGraphics.moveTo(localPos.x, localPos.y - crossSize);
+            this.debugGraphics.lineTo(localPos.x, localPos.y + crossSize);
+            this.debugGraphics.stroke();
+            
+            // 绘制吸附范围圆圈
+            this.debugGraphics.circle(localPos.x, localPos.y, snapRange);
+            this.debugGraphics.stroke();
+        });
+    }
+    
+    // 更新调试绘制
+    private updateDebugDraw() {
+        if (!this.debugDrawSnapRange || !this.debugGraphics) return;
+        
+        this.debugGraphics.clear();
+        
+        // 绘制网格吸附范围
+        this.drawGridSnapRanges();
+        
+        // 如果有正在拖拽的方块,绘制其吸附范围
+        if (this.currentDragBlock) {
+            this.drawBlockSnapRange(this.currentDragBlock);
+        }
+    }
+    
+    // 清理调试绘制
+    private cleanupDebugDraw() {
+        if (this.debugDrawNode && this.debugDrawNode.isValid) {
+            this.debugDrawNode.destroy();
+            this.debugDrawNode = null;
+            this.debugGraphics = null;
+        }
+    }
+    
+    // 设置调试绘制开关
+    public setDebugDrawSnapRange(enabled: boolean) {
+        this.debugDrawSnapRange = enabled;
+        
+        if (enabled) {
+            this.initDebugDraw();
+        } else {
+            this.cleanupDebugDraw();
+        }
+        
+        console.log(`[GameBlockSelection] 调试绘制已${enabled ? '开启' : '关闭'}`);
+    }
+
+    onDisable() {
+        this.removeEventListeners();
+        this.cleanupDebugDraw();
+    }
+
     onDestroy() {
-        // 清理事件监听
+        this.removeEventListeners();
+        this.cleanupDebugDraw();
+    }
+
+    // 移除事件监听器
+    private removeEventListeners() {
         const eventBus = EventBus.getInstance();
         eventBus.off(GameEvents.RESET_BLOCK_SELECTION, this.onResetBlockSelectionEvent, this);
-
         eventBus.off(GameEvents.GAME_START, this.onGameStartEvent, this);
     }
 }

+ 1 - 0
assets/scripts/Core/EventBus.ts

@@ -78,6 +78,7 @@ export enum GameEvents {
     // 方块生成事件
     GENERATE_BLOCKS = 'GENERATE_BLOCKS',
     WAVE_COMPLETED = 'WAVE_COMPLETED',
+    SETUP_BLOCK_DRAG_EVENTS = 'SETUP_BLOCK_DRAG_EVENTS',
     
     // 子弹事件
     BULLET_CREATE_REQUEST = 'BULLET_CREATE_REQUEST',