MatchMaster/assets/jungle_treasure/script/jungle_Manager.ts
2026-08-03 20:04:40 +08:00

315 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import JungleAnimator from "./JungleAnimator";
import { createJungleMockConfig } from "./JungleConfig";
import JungleProgress from "./JungleProgress";
import JungleRewardService from "./JungleRewardService";
import {
JungleActivityConfig,
JungleRenderState,
JungleTierConfig,
JungleViewAssets,
} from "./JungleTypes";
import JungleView from "./JungleView";
const { ccclass, property } = cc._decorator;
/**
* Jungle Treasure 流程控制器。
* 只管理活动状态和模块协作UI、动画、配置、存档与发奖均由独立模块负责。
*/
@ccclass
export default class JungleManager extends cc.Component {
@property(cc.SpriteFrame)
cardFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
trackFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
titleFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
buttonFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
lockFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
timerFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
closeFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
coinFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
freezeFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
hammerFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
magicWandFrame: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
infiniteHealthFrame: cc.SpriteFrame = null;
@property({ tooltip: "服务器支付尚未接入时,点击付费档位自动模拟支付成功" })
useMockPayment: boolean = true;
@property({ tooltip: "只预览领取和轨道动画,不发道具、不支付、不保存进度" })
previewAnimationOnly: boolean = true;
private activityConfig: JungleActivityConfig = null;
private currentTierIndex: number = 0;
private cards: cc.Node[] = [];
private slotPositions: cc.Vec2[] = [];
private isMoving: boolean = false;
private isPurchasing: boolean = false;
private view: JungleView = null;
private animator: JungleAnimator = null;
private progress: JungleProgress = null;
private rewardService: JungleRewardService = null;
protected onLoad(): void {
this.progress = new JungleProgress();
this.rewardService = new JungleRewardService();
this.activityConfig = createJungleMockConfig();
this.currentTierIndex = this.progress.read(this.activityConfig, this.previewAnimationOnly);
this.view = new JungleView(
this.node,
this.getViewAssets(),
this,
this.closeActivity,
this.onCardClicked,
);
const refs = this.view.build();
this.cards = refs.cards;
this.slotPositions = refs.slotPositions;
this.animator = new JungleAnimator(this, refs.contentNode, this.cardFrame);
this.renderAllCards();
this.updateTimer();
this.schedule(this.updateTimer, 1);
}
protected onDestroy(): void {
this.unschedule(this.updateTimer);
}
/** 后续服务器配置到达后调用此方法即可替换模拟表。 */
public setServerConfig(config: JungleActivityConfig, claimedTierIndex?: number): void {
if (!config || !config.tiers || config.tiers.length === 0) {
cc.warn("[JungleTreasure] ignored empty server config");
return;
}
this.activityConfig = config;
this.currentTierIndex = typeof claimedTierIndex === "number"
? Math.max(0, Math.min(claimedTierIndex, config.tiers.length))
: this.progress.read(config, this.previewAnimationOnly);
this.renderAllCards();
this.updateTimer();
}
/** 正式支付层完成后回调。支付失败或取消不会移动轨道。 */
public completePurchase(success: boolean): void {
if (!this.isPurchasing) {
return;
}
this.isPurchasing = false;
if (success) {
this.completeCurrentTier();
} else {
this.renderAllCards();
}
}
public openActivity(): void {
this.node.active = true;
this.renderAllCards();
this.updateTimer();
}
public closeActivity(): void {
if (this.isMoving || this.isPurchasing) {
return;
}
this.node.emit("jungle-close");
this.node.active = false;
}
/** 仅用于当前模拟配置的联调。 */
public resetMockProgress(): void {
if (!this.activityConfig) {
return;
}
this.progress.reset(this.activityConfig);
this.currentTierIndex = 0;
this.renderAllCards();
}
public getCurrentTierIndex(): number {
return this.currentTierIndex;
}
private onCardClicked(buttonComponent: cc.Button): void {
if (this.isMoving || this.isPurchasing || !this.activityConfig) {
return;
}
const button = buttonComponent && buttonComponent.node;
if (!button) {
return;
}
const card = button.parent;
if (card !== this.cards[0]) {
this.animator.playLockedFeedback(card);
return;
}
const tier = this.activityConfig.tiers[this.currentTierIndex];
if (!tier || Date.now() >= this.activityConfig.endAt) {
return;
}
if (this.previewAnimationOnly) {
this.completeCurrentTier();
} else if (tier.price > 0) {
this.requestPurchase(tier);
} else {
this.completeCurrentTier();
}
}
private requestPurchase(tier: JungleTierConfig): void {
this.isPurchasing = true;
this.renderAllCards();
if (this.useMockPayment) {
this.scheduleOnce(() => this.completePurchase(true), 0.45);
return;
}
this.node.emit("jungle-purchase-request", {
tierId: tier.id,
price: tier.price,
productId: tier.productId || "",
rewards: tier.rewards,
});
}
private completeCurrentTier(): void {
const tier = this.activityConfig.tiers[this.currentTierIndex];
if (!tier || this.isMoving) {
return;
}
this.isMoving = true;
if (!this.previewAnimationOnly) {
this.rewardService.grant(this.node, tier.rewards);
}
const leavingCard = this.cards[0];
this.animator.playClaimEffect(
leavingCard,
() => this.animator.playCardExit(leavingCard, () => this.shiftCards()),
);
}
private shiftCards(): void {
const oldCards = this.cards.slice();
this.currentTierIndex++;
if (!this.previewAnimationOnly) {
this.progress.save(this.activityConfig, this.currentTierIndex);
}
const recycled = oldCards[0];
recycled.stopAllActions();
recycled.active = false;
recycled.scale = 1;
recycled.opacity = 0;
recycled.setAnchorPoint(0.5, 0.5);
this.animator.moveCardsWithOverlap(oldCards, this.slotPositions, 1, () => {
this.cards.splice(
0,
this.cards.length,
oldCards[1],
oldCards[2],
oldCards[3],
oldCards[4],
oldCards[5],
recycled,
);
this.showNewSixthCard(recycled);
});
}
private showNewSixthCard(card: cc.Node): void {
const sixthTierIndex = this.currentTierIndex + 5;
const targetCenter = this.slotPositions[5];
this.view.renderCard(card, this.activityConfig, this.getRenderState(), sixthTierIndex, 5);
if (sixthTierIndex >= this.activityConfig.tiers.length) {
card.setAnchorPoint(0.5, 0.5);
card.setPosition(targetCenter);
this.finishShift();
return;
}
this.animator.showNewCard(card, targetCenter, () => this.finishShift());
}
private finishShift(): void {
this.isMoving = false;
this.renderAllCards();
if (this.currentTierIndex >= this.activityConfig.tiers.length) {
this.node.emit("jungle-complete");
}
}
private renderAllCards(): void {
if (this.view) {
this.view.renderAllCards(this.activityConfig, this.getRenderState());
}
}
private updateTimer(): void {
if (!this.view || !this.activityConfig) {
return;
}
if (this.view.updateTimer(this.activityConfig) === 0) {
this.renderAllCards();
this.node.emit("jungle-expired");
}
}
private getRenderState(): JungleRenderState {
return {
currentTierIndex: this.currentTierIndex,
isMoving: this.isMoving,
isPurchasing: this.isPurchasing,
};
}
private getViewAssets(): JungleViewAssets {
return {
cardFrame: this.cardFrame,
trackFrame: this.trackFrame,
titleFrame: this.titleFrame,
buttonFrame: this.buttonFrame,
lockFrame: this.lockFrame,
timerFrame: this.timerFrame,
closeFrame: this.closeFrame,
coinFrame: this.coinFrame,
freezeFrame: this.freezeFrame,
hammerFrame: this.hammerFrame,
magicWandFrame: this.magicWandFrame,
infiniteHealthFrame: this.infiniteHealthFrame,
};
}
}