MatchMaster/assets/Script/SpawnGate.ts

246 lines
8.0 KiB
TypeScript

const { ccclass, property } = cc._decorator;
export interface SpawnGateConfig {
id?: string;
x: number;
y: number;
width: number;
height: number;
spawnQueue: Array<{ members: any[] }>;
}
export interface SpawnGateBatchResult {
nodes: cc.Node[];
opacities: number[];
}
enum SpawnGateState {
Waiting,
Opening,
Revealing,
Closing,
}
@ccclass
export default class SpawnGate extends cc.Component {
@property(cc.SpriteFrame)
left2Frame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
right2Frame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
left3Frame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
right3Frame: cc.SpriteFrame = null;
@property(sp.SkeletonData)
revealSmokeData: sp.SkeletonData = null;
private map: any = null;
private config: SpawnGateConfig = null;
private queueIndex = 0;
private state = SpawnGateState.Waiting;
private elapsed = 0;
private finished = false;
private batch: SpawnGateBatchResult = null;
private leftSprite: cc.Sprite = null;
private rightSprite: cc.Sprite = null;
private countLabel: cc.Label = null;
init(map: any, config: SpawnGateConfig) {
this.map = map;
this.config = config;
this.queueIndex = 0;
this.state = SpawnGateState.Waiting;
this.elapsed = 0;
this.finished = false;
this.batch = null;
const left = this.node.getChildByName("left");
const right = this.node.getChildByName("right");
this.leftSprite = left && left.getComponent(cc.Sprite);
this.rightSprite = right && right.getComponent(cc.Sprite);
const count = left && left.getChildByName("count");
this.countLabel = count && count.getComponent(cc.Label);
this.configureArtwork(left, right);
this.updateRemainingCount();
if (this.countLabel) this.countLabel.node.active = true;
const bottomLeft = map.mapBlocksWall[config.x][config.y];
this.node.setPosition(
bottomLeft.x + (config.width - 1) * 60,
bottomLeft.y + (config.height - 1) * 60,
);
const gateWidth = config.width * 120;
const gateHeight = config.height * 120;
this.node.setContentSize(gateWidth, gateHeight);
const bg = this.node.getChildByName("bg");
const kuang = this.node.getChildByName("kuang");
if (bg) bg.setContentSize(gateWidth, gateHeight);
if (kuang) kuang.setContentSize(gateWidth, gateHeight);
this.node.zIndex = -10;
const line = this.node.getChildByName("line");
if (line) line.active = false;
this.setClosedAmount(1);
}
hasOutstandingWork(): boolean {
return !this.finished;
}
private configureArtwork(left: cc.Node, right: cc.Node) {
const isTwo = this.config.width === 2;
const leftFrame = isTwo ? this.left2Frame : this.left3Frame;
const rightFrame = isTwo ? this.right2Frame : this.right3Frame;
this.configureDoorPart(left, this.leftSprite, leftFrame);
this.configureDoorPart(right, this.rightSprite, rightFrame);
left.x = -this.config.width * 60 + left.width / 2;
right.x = this.config.width * 60 - right.width / 2;
left.y = 0;
right.y = 0;
}
private configureDoorPart(node: cc.Node, sprite: cc.Sprite, frame: cc.SpriteFrame) {
sprite.spriteFrame = frame;
sprite.type = cc.Sprite.Type.FILLED;
sprite.fillType = cc.Sprite.FillType.HORIZONTAL;
sprite.sizeMode = cc.Sprite.SizeMode.RAW;
if (frame) node.setContentSize(frame.getOriginalSize());
}
update(dt: number) {
if (this.finished || !this.map || !cc.isValid(this.map, true)) return;
if (this.map.gameOver || this.map.gameWin) return;
if (this.state === SpawnGateState.Waiting) {
if (this.queueIndex >= this.config.spawnQueue.length) {
this.finishGate();
return;
}
if (this.map.pause || !this.map.isSpawnGateAreaEmpty(this.config)) return;
this.beginBatch();
return;
}
this.elapsed += dt;
if (this.state === SpawnGateState.Opening) {
const progress = Math.min(1, this.elapsed / 0.3);
this.setClosedAmount(1 - progress);
if (progress >= 1) this.enterState(SpawnGateState.Revealing);
}
else if (this.state === SpawnGateState.Revealing) {
const progress = Math.min(1, this.elapsed / 0.15);
this.setBatchOpacity(progress);
if (progress >= 1) this.enterState(SpawnGateState.Closing);
}
else if (this.state === SpawnGateState.Closing) {
const progress = Math.min(1, this.elapsed / 0.2);
this.setClosedAmount(progress);
if (progress >= 1) this.completeBatch();
}
}
private beginBatch() {
const queueItem = this.config.spawnQueue[this.queueIndex];
if (this.countLabel) this.countLabel.node.active = false;
this.batch = this.map.createSpawnGateBatch(
queueItem.members,
this.config,
this.queueIndex,
);
this.queueIndex++;
this.setBatchOpacity(0);
this.enterState(SpawnGateState.Opening);
}
private completeBatch() {
this.setClosedAmount(1);
this.setBatchOpacity(1);
this.map.unlockSpawnGateBatch(this.batch.nodes);
this.batch = null;
if (this.queueIndex >= this.config.spawnQueue.length) {
this.finishGate();
}
else {
if (this.countLabel) this.countLabel.node.active = true;
this.enterState(SpawnGateState.Waiting);
}
}
private finishGate() {
this.finished = true;
const map = this.map;
this.map = null;
this.node.destroy();
if (map && cc.isValid(map, true)) map.onSpawnGateFinished();
}
private enterState(state: SpawnGateState) {
this.state = state;
this.elapsed = 0;
if (state === SpawnGateState.Revealing) {
this.updateRemainingCount();
this.playRevealSmoke();
}
}
private updateRemainingCount() {
if (!this.countLabel) return;
const remaining = Math.max(0, this.config.spawnQueue.length - this.queueIndex);
this.countLabel.string = remaining.toString();
}
private playRevealSmoke() {
if (!this.batch || !this.revealSmokeData) return;
const smoke = new cc.Node("spawnGateSmoke");
smoke.parent = this.node.parent;
smoke.zIndex = 1100;
const gateWidth = this.config.width * 120;
const gateHeight = this.config.height * 120;
const scale = Math.max(gateWidth / 430.62, gateHeight / 373.6) * 0.8;
smoke.scale = scale;
smoke.setPosition(
this.node.x - 8.78 * scale,
this.node.y + 9.49 * scale,
);
const skeleton = smoke.addComponent(sp.Skeleton);
skeleton.skeletonData = this.revealSmokeData;
skeleton.premultipliedAlpha = true;
skeleton.loop = false;
skeleton.timeScale = 1.5625;
const track = skeleton.setAnimation(0, "animation", false);
skeleton.setAttachment("锤子", null);
if (track) track.trackTime = 0.78;
skeleton.setCompleteListener(() => smoke.destroy());
}
private setClosedAmount(value: number) {
if (!this.leftSprite || !this.rightSprite) return;
this.leftSprite.fillStart = 0;
this.leftSprite.fillRange = value;
this.rightSprite.fillStart = 1;
this.rightSprite.fillRange = -value;
}
private setBatchOpacity(progress: number) {
if (!this.batch) return;
for (let i = 0; i < this.batch.nodes.length; i++) {
const node = this.batch.nodes[i];
if (node && cc.isValid(node)) {
node.opacity = Math.round(this.batch.opacities[i] * progress);
}
}
}
onDestroy() {
this.map = null;
this.batch = null;
this.countLabel = null;
}
}