import BlockAssetManager from "./module/Tool/BlockAssetManager"; import { blockTexturePath } from "./module/Tool/BlockTexturePaths"; import { RainbowCut, RAINBOW_COLOR } from "./RainbowRules"; const SIDES = ["top", "down", "left", "right"]; function discard(node: cc.Node): void { node.active = false; node.removeFromParent(); node.destroy(); } /** 保留原组件和正在进行的倒计时,只换形状对应的显示。 */ function reshapeProps(map: any, block: any, cut: RainbowCut): void { const node: cc.Node = block.node; const shape = cut.shape; const config = cc.fx.GameConfig.PROP_INFO[shape]; const icon = node.getChildByName("icon"); icon.getComponent(cc.Sprite).spriteFrame = BlockAssetManager.instance.getFrame(blockTexturePath(block.color, shape)); const skinX = config.pos6.x - node.width * (node.anchorX - 0.5); const skinY = config.pos6.y + node.height / 2; for (const child of node.children.slice()) { const floor = child.getComponent("Floor"); if (floor) { const x = Math.round((child.x + 65) / 120); const y = Math.round((child.y - 60) / 120); if (x === cut.cell.x && y === cut.cell.y) { discard(child); continue; } child.x -= cut.offset.x * 120; child.y -= cut.offset.y * 120; floor.posX = block.posX + x - cut.offset.x; floor.posY = block.posY + y - cut.offset.y; continue; } if (child.name === "freeze" || child.name === "question" || child.name === "switchs") { child.setPosition(skinX + (shape === 2 ? 4 : 0), skinY - (shape === 2 ? 10 : 0)); if (child.name === "switchs") { child.getChildByName("open").getComponent(cc.Sprite).spriteFrame = BlockAssetManager.instance.getFrame("12color" + shape); child.getChildByName("lock").getComponent(cc.Sprite).spriteFrame = BlockAssetManager.instance.getFrame("11color" + shape); } else { child.getChildByName("icon").getComponent(cc.Sprite).spriteFrame = child.name === "freeze" ? block.ice_SpriteFrame.getSpriteFrame("ice_" + shape) : BlockAssetManager.instance.getFrame("question" + shape); child.getChildByName("time").setPosition(config.pos5.x - 10 - skinX, config.pos5.y - 2 - skinY); // 问号壳内的方向提示也要跟随新形状,不能初始化问号计数。 child.children.forEach(hint => { const horizontal = hint.getComponent("Heng"); const vertical = hint.getComponent("Shu"); if (!horizontal && !vertical) return; const prefix = horizontal ? "heng" : "shu"; const name = prefix + (horizontal ? block.heng : block.shu); hint.children.forEach(part => { if (part.name.indexOf(prefix) === 0) part.active = part.name === name; }); const component = horizontal || vertical; component._name = name; hint.setPosition((horizontal ? config.pos2.x - 3 : config.pos3.x) - child.x, (horizontal ? config.pos2.y : config.pos3.y) - child.y); }); } continue; } const horizontal = child.getComponent("Heng"); const vertical = child.getComponent("Shu"); if (horizontal || vertical) { const component = horizontal || vertical; const prefix = horizontal ? "heng" : "shu"; const name = prefix + (horizontal ? block.heng : block.shu); child.children.forEach(part => { if (part.name.indexOf(prefix) === 0) part.active = part.name === name; }); component.init(name, component.time); child.setPosition(horizontal ? config.pos2.x - 3 : config.pos3.x, horizontal ? config.pos2.y : config.pos3.y); continue; } const lock = child.getComponent("Lock"); if (lock) { // 从原预制体取全新的锁形状,避免重复 init 累加翻转和偏移。 const template = cc.instantiate(map.Block_Prop[3]); const visual = cc.instantiate(template.getChildByName("lock")); discard(child.getChildByName("lock")); child.addChild(visual); lock.init(lock.time, lock.type, shape); child.setPosition(config.pos1.x, config.pos1.y); template.destroy(); continue; } const star = child.getComponent("Star"); if (star) { child.setAnchorPoint(node.getAnchorPoint()); child.getComponent(cc.Sprite).spriteFrame = star.star_SpriteFrame.getSpriteFrame("star_" + shape); child.setPosition(config.pos4.x - 10, config.pos4.y); continue; } if (child.name === "flowerFloor") { child.setPosition(icon.position); child.setAnchorPoint(icon.getAnchorPoint()); child.getComponent(cc.Sprite).spriteFrame = block.flower_SpriteFrame.getSpriteFrame("flower" + shape); continue; } if (["key", "boom", "boom2", "addTime", "flowerSend", "flowerReceive"].indexOf(child.name) >= 0) { child.setPosition(config.pos1.x, config.pos1.y); } } if (block.hit && cc.isValid(block.hit)) { block.hit.setAnchorPoint(node.getAnchorPoint()); block.hit.getComponent(cc.Sprite).spriteFrame = block.ice_SpriteFrame.getSpriteFrame("xz_" + shape); block.setHitPosition(); } } function reshape(map: any, block: any, cut: RainbowCut, template: cc.Node, upper: boolean): void { const node: cc.Node = block.node; if (!upper) { // 只清理由自己占用的格子,不调用会触发机关判定的接口。 block.allBlocks.forEach(p => { const cell = map.mapBlocksWall[block.posX + p.x][block.posY + p.y].getComponent("MapBlock"); if (cell.block_Id === node.uuid) { cell.block_Id = ""; cell.nowColor = -1; } }); } const geometry = template.getComponent("Block"); block.block_Info.block = cut.shape; node.name = "block" + cut.shape; node.setContentSize(template.getContentSize()); node.setAnchorPoint(template.getAnchorPoint()); block.heng = geometry.heng; block.shu = geometry.shu; block.initBlocks(); block.posX += cut.offset.x; block.posY += cut.offset.y; if (!upper) node.setPosition(node.x + cut.offset.x * 120, node.y + cut.offset.y * 120); const collider = node.getComponent(cc.PolygonCollider); const sampleCollider = template.getComponent(cc.PolygonCollider); collider.points = sampleCollider.points.map(p => cc.v2(p.x, p.y)); collider.offset = cc.v2(sampleCollider.offset.x, sampleCollider.offset.y); block.collider = collider; node.children.slice().forEach(child => { if (child.getComponent("lq_collide")) discard(child); }); template.children.forEach((child, index) => { if (child.getComponent("lq_collide")) { const copy = cc.instantiate(child); if (upper && SIDES.indexOf(copy.name) >= 0) copy.active = false; node.addChild(copy); // restoreNomal 依赖基础碰撞节点位于 hit 之前。 copy.setSiblingIndex(index); } else if (child.name === "icon" || child.name === "change_color") { const current = node.getChildByName(child.name); current.setPosition(child.position); current.setContentSize(child.getContentSize()); current.setAnchorPoint(child.getAnchorPoint()); } }); reshapeProps(map, block, cut); block.startPos = node.position.clone(); if (!upper) { block.level = 50 + block.posX - block.posY * 3; node.zIndex = block.level; block.setMapBlock(); } } /** 原节点身份不变,重新生成联动碰撞代理,不解除组合或重置连接计数。 */ function refreshLinks(map: any, changed: any[]): void { const linksOf = (block: any): cc.Node[] => { const nodes = (block.teamBlocks || []).slice(); if ((block.type === 9 || block.type === 1 || block.type === 10) && block.block_Info.node) { nodes.push(block.block_Info.node); } return nodes.filter(node => cc.isValid(node, true) && node.getComponent("Block")); }; const members = map.node.children.filter(node => cc.isValid(node, true)) .map(node => node.getComponent("Block")).filter(Boolean) .map(block => ({ block, links: linksOf(block) })); const linked = changed.slice(); for (let i = 0; i < linked.length; i++) { const links = linksOf(linked[i]); members.forEach(member => { // 部分地板解除后只留下队友对自己的引用,队友的偏移和碰撞代理仍需更新。 if (linked.indexOf(member.block) < 0 && (links.indexOf(member.block.node) >= 0 || member.links.indexOf(linked[i].node) >= 0)) linked.push(member.block); }); } linked.forEach(block => block.node.children.slice().forEach(child => { const collide = child.getComponent("lq_collide"); if (collide && (collide.data_string === "-1" || collide.data_string === "-2")) discard(child); })); linked.forEach(block => { if (block.type === 9 && block.block_Info.node) block.createNianHe(false); if (block.block_Info.floorMove === true) block.setMoveFloor(); if (changed.indexOf(block) < 0) return; (block.adhesiveNode || []).forEach(link => { if (!cc.isValid(link)) return; const component = link.getComponent("Adhesive"); if (!component || !component.target) return; // 将连接显示移到剩余块上最近的一格;保留原连接及 lockTime。 const point = block.allBlocks.slice().sort((a, b) => { const distance = p => Math.abs(block.node.x - 65 + p.x * 120 - link.x) + Math.abs(block.node.y + 60 + p.y * 120 - link.y); return distance(a) - distance(b); })[0]; link.setPosition(block.node.x - 65 + point.x * 120, block.node.y + 60 + point.y * 120); component.pos = { x: block.node.x - link.x, y: block.node.y - link.y }; }); }); } export function applyRainbowCut(map: any, block: any, cut: RainbowCut): cc.Node { const template = cc.instantiate(map.Block_Array[cut.shape]); const rainbow = cc.instantiate(map.Block_Array[0]); const x = block.posX + cut.cell.x; const y = block.posY + cut.cell.y; const upper = block.type === 1 && block.block_Info.node ? block.block_Info.node.getComponent("Block") : null; const changed = upper ? [block, upper] : [block]; // 上层使用下层的逻辑原点,显示仍使用原来的 0.7 缩放与叠加偏移。 if (upper) { upper.posX = block.posX; upper.posY = block.posY; } reshape(map, block, cut, template, false); if (upper) { reshape(map, upper, cut, template, true); const offset = upper.getStackingPos(); upper.node.setPosition(block.node.x + offset.x, block.node.y + offset.y); upper.stacking = cc.v2(offset.x, offset.y); upper.startPos = upper.node.position.clone(); } refreshLinks(map, changed); template.destroy(); const cell = map.mapBlocksWall[x][y]; rainbow.parent = map.node; rainbow.setPosition(cell.x + 65, cell.y - 60); const ids = map.node.children.map(node => node.getComponent("Block")).filter(Boolean).map(b => Number(b.blockId) || 0); rainbow.getComponent("Block").init({ id: Math.max(0, ...ids) + 1, block: 0, type: 0, color: RAINBOW_COLOR, position: { x: rainbow.x, y: rainbow.y } }, null, null, null, null); map.blocks.push(rainbow); map.blockNum = map.blocks.length; block.cmupdate(); return rainbow; } /** 彩虹只推进消除计数与藤蔓,不走普通消除的颜色、开关和旋转分支。 */ export function applyRainbowElimination(map: any, node: cc.Node): void { const remaining = map.blocks.filter(n => n !== node); map.blocks = remaining; map.shrinkVine(); // 灰色障碍和单色地块不计入清场数组,但其数字仍要随彩虹消除递减。 for (const target of map.node.children.slice()) { if (target === node || !cc.isValid(target, true)) continue; const block = target.getComponent("Block"); if (!block || block.spawnLocked || block.over) continue; for (const child of target.children.slice()) { if (!cc.isValid(child, true)) continue; for (const name of ["Freeze", "Question"]) { const counter = child.getComponent(name); if (counter && counter.time > 0) counter.reduce(1, true); } const floor = child.getComponent("Floor"); if (floor && floor.time > 0) floor.reduce(1, floor.color); for (const name of ["Lock", "Heng", "Shu"]) { const counter = child.getComponent(name); if (counter && counter.time > 0) counter.reduce(); } } } map.changeFreeze(); map.changeLock(); map.changeAdhesive(); }