264 lines
13 KiB
TypeScript
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();
|
|
}
|