MatchMaster/assets/Script/RainbowCut.ts
WIN-GKKD951VVMJ\Administrator c18d47f4b0 更新配置
2026-09-16 14:55:47 +08:00

264 lines
13 KiB
TypeScript

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();
}